pax_global_header00006660000000000000000000000064126044637670014531gustar00rootroot0000000000000052 comment=95c9ccb2959abb46c553ec72731cd75358b80138 z3-z3-4.4.1/000077500000000000000000000000001260446376700124255ustar00rootroot00000000000000z3-z3-4.4.1/.gitattributes000066400000000000000000000002121260446376700153130ustar00rootroot00000000000000# Set default behaviour, in case users don't have core.autocrlf set. * text=auto src/api/dotnet/Properties/AssemblyInfo.cs text eol=crlf z3-z3-4.4.1/.gitignore000066400000000000000000000025061260446376700144200ustar00rootroot00000000000000*~ *.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-z3-4.4.1/LICENSE.txt000066400000000000000000000021101260446376700142420ustar00rootroot00000000000000Z3 Copyright (c) Microsoft Corporation All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. z3-z3-4.4.1/README000066400000000000000000000025501260446376700133070ustar00rootroot00000000000000Z3 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 32-bit builds, start with: python scripts/mk_make.py or instead, for a 64-bit build: python scripts/mk_make.py -x then: 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-z3-4.4.1/RELEASE_NOTES000066400000000000000000001031541260446376700144040ustar00rootroot00000000000000RELEASE NOTES Version 4.4.1 ============= - This release marks the transition to the new GitHub fork & pull model; the unstable and contrib branches will be retired with all new contributions going into the master branch directly. - A multitude of bugs has been fixed. 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-z3-4.4.1/configure000077500000000000000000000006401260446376700143340ustar00rootroot00000000000000#!/bin/sh if test -z $PYTHON; then PYTHON=python fi if ! which $PYTHON > /dev/null; then echo "'$PYTHON' not found. Try to set the environment variable PYTHON." exit 1 fi if ! $PYTHON -c "print('testing')" > /dev/null ; then echo "'$PYTHON' failed to execute basic test script. Try to set the environment variable PYTHON with a working Python interpreter." exit 1 fi $PYTHON scripts/mk_make.py $* z3-z3-4.4.1/doc/000077500000000000000000000000001260446376700131725ustar00rootroot00000000000000z3-z3-4.4.1/doc/README000066400000000000000000000011061260446376700140500ustar00rootroot00000000000000API 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-z3-4.4.1/doc/mk_api_doc.py000066400000000000000000000101721260446376700156320ustar00rootroot00000000000000# 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-z3-4.4.1/doc/update_api_website.cmd000066400000000000000000000004041260446376700175120ustar00rootroot00000000000000REM 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-z3-4.4.1/doc/update_code_website.cmd000066400000000000000000000004131260446376700176530ustar00rootroot00000000000000REM 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-z3-4.4.1/doc/website.dox000066400000000000000000000017141260446376700153530ustar00rootroot00000000000000/** \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-z3-4.4.1/doc/z3api.dox000066400000000000000000002324301260446376700147400ustar00rootroot00000000000000# 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-z3-4.4.1/doc/z3code.dox000066400000000000000000001264671260446376700151150ustar00rootroot00000000000000# 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-z3-4.4.1/examples/000077500000000000000000000000001260446376700142435ustar00rootroot00000000000000z3-z3-4.4.1/examples/c++/000077500000000000000000000000001260446376700146135ustar00rootroot00000000000000z3-z3-4.4.1/examples/c++/README000066400000000000000000000006351260446376700154770ustar00rootroot00000000000000Small 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-z3-4.4.1/examples/c++/example.cpp000066400000000000000000001000441260446376700167510ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/c/000077500000000000000000000000001260446376700144655ustar00rootroot00000000000000z3-z3-4.4.1/examples/c/README000066400000000000000000000006351260446376700153510ustar00rootroot00000000000000Small 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-z3-4.4.1/examples/c/test_capi.c000066400000000000000000002436371260446376700166230ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/dotnet/000077500000000000000000000000001260446376700155405ustar00rootroot00000000000000z3-z3-4.4.1/examples/dotnet/Program.cs000066400000000000000000002444371260446376700175140ustar00rootroot00000000000000/*++ 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-z3-4.4.1/examples/dotnet/README000066400000000000000000000003251260446376700164200ustar00rootroot00000000000000Small 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-z3-4.4.1/examples/interp/000077500000000000000000000000001260446376700155445ustar00rootroot00000000000000z3-z3-4.4.1/examples/interp/iz3.cpp000077500000000000000000000300431260446376700167600ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/java/000077500000000000000000000000001260446376700151645ustar00rootroot00000000000000z3-z3-4.4.1/examples/java/JavaExample.java000066400000000000000000002406241260446376700202340ustar00rootroot00000000000000/*++ 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 void optimizeExample(Context ctx) { System.out.println("Opt"); Optimize opt = ctx.mkOptimize(); // Set constraints. IntExpr xExp = ctx.mkIntConst("x"); IntExpr yExp = ctx.mkIntConst("y"); opt.Add(ctx.mkEq(ctx.mkAdd(xExp, yExp), ctx.mkInt(10)), ctx.mkGe(xExp, ctx.mkInt(0)), ctx.mkGe(yExp, ctx.mkInt(0))); // Set objectives. Optimize.Handle mx = opt.MkMaximize(xExp); Optimize.Handle my = opt.MkMaximize(yExp); System.out.println(opt.Check()); System.out.println(mx); System.out.println(my); } 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.optimizeExample(ctx); 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-z3-4.4.1/examples/java/README000066400000000000000000000010431260446376700160420ustar00rootroot00000000000000A 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-z3-4.4.1/examples/maxsat/000077500000000000000000000000001260446376700155405ustar00rootroot00000000000000z3-z3-4.4.1/examples/maxsat/README000066400000000000000000000010011260446376700164100ustar00rootroot00000000000000Small 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-z3-4.4.1/examples/maxsat/ex.smt000066400000000000000000000003751260446376700167060ustar00rootroot00000000000000(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-z3-4.4.1/examples/maxsat/maxsat.c000066400000000000000000000473241260446376700172130ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/ml/000077500000000000000000000000001260446376700146535ustar00rootroot00000000000000z3-z3-4.4.1/examples/ml/README000066400000000000000000000015051260446376700155340ustar00rootroot00000000000000Small 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-z3-4.4.1/examples/ml/ml_example.ml000066400000000000000000000313201260446376700173270ustar00rootroot00000000000000(* 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-z3-4.4.1/examples/msf/000077500000000000000000000000001260446376700150305ustar00rootroot00000000000000z3-z3-4.4.1/examples/msf/README000066400000000000000000000020251260446376700157070ustar00rootroot00000000000000In 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3.Tests/000077500000000000000000000000001260446376700231625ustar00rootroot00000000000000z3-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3.Tests/App.config000066400000000000000000000070131260446376700250720ustar00rootroot00000000000000
z3-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/000077500000000000000000000000001260446376700253165ustar00rootroot00000000000000z3-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/AssemblyInfo.cs000066400000000000000000000026401260446376700302420ustar00rootroot00000000000000using 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs000066400000000000000000000051501260446376700261350ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverFoundation.Plugin.Z3.Tests.csproj000066400000000000000000000062041260446376700325400ustar00rootroot00000000000000 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs000066400000000000000000000102661260446376700260130ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/000077500000000000000000000000001260446376700220615ustar00rootroot00000000000000z3-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs000066400000000000000000000053501260446376700246540ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/App.config000066400000000000000000000070131260446376700237710ustar00rootroot00000000000000
z3-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/SolverFoundation.Plugin.Z3.csproj000066400000000000000000000143071260446376700303410ustar00rootroot00000000000000 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/Utils.cs000066400000000000000000000073301260446376700235130ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs000066400000000000000000000047371260446376700253510ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs000066400000000000000000000055001260446376700246430ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs000066400000000000000000000340541260446376700247000ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs000066400000000000000000000003451260446376700252270ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Z3MILPDirective : Z3BaseDirective { } } z3-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs000066400000000000000000000011071260446376700245310ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs000066400000000000000000000171771260446376700245760ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs000066400000000000000000000003451260446376700253750ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Z3TermDirective : Z3BaseDirective { } } z3-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs000066400000000000000000000007761260446376700247120ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs000066400000000000000000000374601260446376700247410ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/Validator/000077500000000000000000000000001260446376700167555ustar00rootroot00000000000000z3-z3-4.4.1/examples/msf/Validator/App.config000066400000000000000000000070131260446376700206650ustar00rootroot00000000000000
z3-z3-4.4.1/examples/msf/Validator/MicrosoftSolverFoundationForExcel.dll.config000066400000000000000000000065471260446376700275510ustar00rootroot00000000000000
z3-z3-4.4.1/examples/msf/Validator/Program.cs000066400000000000000000000160371260446376700207220ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/msf/Validator/Properties/000077500000000000000000000000001260446376700211115ustar00rootroot00000000000000z3-z3-4.4.1/examples/msf/Validator/Properties/AssemblyInfo.cs000066400000000000000000000026061260446376700240370ustar00rootroot00000000000000using 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-z3-4.4.1/examples/msf/Validator/Validator.csproj000066400000000000000000000124011260446376700221220ustar00rootroot00000000000000 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-z3-4.4.1/examples/msf/Z3MSFPlugin.sln000066400000000000000000000215261260446376700175750ustar00rootroot00000000000000 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-z3-4.4.1/examples/python/000077500000000000000000000000001260446376700155645ustar00rootroot00000000000000z3-z3-4.4.1/examples/python/README000066400000000000000000000002341260446376700164430ustar00rootroot00000000000000The 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-z3-4.4.1/examples/python/complex/000077500000000000000000000000001260446376700172335ustar00rootroot00000000000000z3-z3-4.4.1/examples/python/complex/complex.py000066400000000000000000000063431260446376700212620ustar00rootroot00000000000000############################################ # 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-z3-4.4.1/examples/python/example.py000066400000000000000000000002501260446376700175660ustar00rootroot00000000000000# 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-z3-4.4.1/examples/python/hamiltonian/000077500000000000000000000000001260446376700200675ustar00rootroot00000000000000z3-z3-4.4.1/examples/python/hamiltonian/hamiltonian.py000066400000000000000000000055741260446376700227570ustar00rootroot00000000000000############################################ # Copyright (c) Microsoft Corporation. All Rights Reserved. # # 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-z3-4.4.1/examples/tptp/000077500000000000000000000000001260446376700152325ustar00rootroot00000000000000z3-z3-4.4.1/examples/tptp/README000066400000000000000000000011621260446376700161120ustar00rootroot00000000000000TPTP 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-z3-4.4.1/examples/tptp/tptp5.cpp000066400000000000000000002400061260446376700170140ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/tptp/tptp5.h000066400000000000000000000021041260446376700164540ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/tptp/tptp5.lex.cpp000066400000000000000000002050761260446376700176130ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/examples/tptp/tptp5.tab.c000066400000000000000000005035011260446376700172230ustar00rootroot00000000000000/* 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-z3-4.4.1/examples/tptp/tptp5.tab.h000066400000000000000000000074211260446376700172300ustar00rootroot00000000000000/* 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-z3-4.4.1/scripts/000077500000000000000000000000001260446376700141145ustar00rootroot00000000000000z3-z3-4.4.1/scripts/README000066400000000000000000000006071260446376700147770ustar00rootroot00000000000000Instructions 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-z3-4.4.1/scripts/mk_copyright.py000066400000000000000000000021331260446376700171640ustar00rootroot00000000000000# 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-z3-4.4.1/scripts/mk_exception.py000066400000000000000000000005001260446376700171460ustar00rootroot00000000000000############################################ # 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-z3-4.4.1/scripts/mk_make.py000066400000000000000000000007061260446376700160750ustar00rootroot00000000000000############################################ # 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-z3-4.4.1/scripts/mk_project.py000066400000000000000000000143351260446376700166310ustar00rootroot00000000000000############################################ # 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('dataflow', ['muz'], 'muz/dataflow') add_lib('transforms', ['muz', 'hilbert', 'dataflow'], '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('sat_solver', ['solver', 'core_tactics', 'aig_tactic', 'bv_tactics', 'arith_tactics', 'sat_tactic'], 'sat/sat_solver') add_lib('portfolio', ['smtlogic_tactics', 'sat_solver', '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', 'sat_solver'], 'opt') 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-z3-4.4.1/scripts/mk_unix_dist.py000066400000000000000000000146771260446376700172020ustar00rootroot00000000000000############################################ # 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-z3-4.4.1/scripts/mk_util.py000066400000000000000000003651161260446376700161460ustar00rootroot00000000000000############################################ # 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 SLOW_OPTIMIZE=False USE_OMP=True 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 not USE_OMP: return False 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(" --noomp disable OpenMP and all features that require it.") 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, SLOW_OPTIMIZE, USE_OMP 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', 'noomp']) 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'): SLOW_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 elif opt in ('', '--noomp'): USE_OMP = False 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 LTCG=' /LTCG' if SLOW_OPTIMIZE else '' GL = ' /GL' if SLOW_OPTIMIZE else '' config.write( 'AR_FLAGS=/nologo%s\n' 'LINK_FLAGS=/nologo /MD\n' 'SLINK_FLAGS=/nologo /LD\n' % LTCG) if TRACE: extra_opt = '%s /D _TRACE ' % extra_opt if not VS_X64: config.write( 'CXXFLAGS=/nologo /c%s /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' % (GL, extra_opt)) config.write( 'LINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' 'SLINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n' % (LTCG, LTCG)) else: config.write( 'CXXFLAGS=/c%s /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' % (GL, extra_opt)) config.write( 'LINK_EXTRA_FLAGS=/link%s /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608\n' 'SLINK_EXTRA_FLAGS=/link%s /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608\n' % (LTCG, LTCG)) # 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-z3-4.4.1/scripts/mk_win_dist.py000066400000000000000000000207501260446376700170010ustar00rootroot00000000000000############################################ # 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-z3-4.4.1/scripts/trackall.sh000077500000000000000000000005411260446376700162500ustar00rootroot00000000000000#!/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-z3-4.4.1/scripts/update_api.py000066400000000000000000001761001260446376700166060ustar00rootroot00000000000000 ############################################ # 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-z3-4.4.1/scripts/update_header_guards.py000066400000000000000000000027141260446376700206310ustar00rootroot00000000000000# Copyright (c) 2015 Microsoft Corporation import os import re ifndef = re.compile("#ifndef \_(.*)\_H\_") doubleu = re.compile("#(.*) (.*)\_\_H\_") defn = re.compile("#define \_(.*)\_H\_") endif = re.compile("#endif /\* \_(.*)\_H\_") def fix_hdr(file): print file tmp = "%s.tmp" % file ins = open(file) ous = open(tmp,'w') line = ins.readline() found = False while line: m = doubleu.search(line) if m: ous.write("#") ous.write(m.group(1)) ous.write(" ") ous.write(m.group(2)) ous.write("_H_\n") line = ins.readline() found = True continue m = ifndef.search(line) if m: print m.group(1) ous.write("#ifndef ") ous.write(m.group(1)) ous.write("_H_\n") line = ins.readline() found = True continue m = defn.search(line) if m: ous.write("#define ") ous.write(m.group(1)) ous.write("_H_\n") line = ins.readline() found = True continue m = endif.search(line) if m: ous.write("#endif /* ") ous.write(m.group(1)) ous.write("_H_ */\n") line = ins.readline() found = True continue ous.write(line) line = ins.readline() ins.close() ous.close() if found: os.system("move %s %s" % (tmp, file)) else: os.system("del %s" % tmp) def fixup(dir): for root, dirs, files in os.walk(dir): for f in files: if f.endswith('.h'): path = "%s\\%s" % (root, f) fix_hdr(path) fixup('src') z3-z3-4.4.1/src/000077500000000000000000000000001260446376700132145ustar00rootroot00000000000000z3-z3-4.4.1/src/api/000077500000000000000000000000001260446376700137655ustar00rootroot00000000000000z3-z3-4.4.1/src/api/api_algebraic.cpp000066400000000000000000000377771260446376700172600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_arith.cpp000066400000000000000000000152751260446376700164430ustar00rootroot00000000000000/*++ 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(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-z3-4.4.1/src/api/api_array.cpp000066400000000000000000000177351260446376700164550ustar00rootroot00000000000000/*++ 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(Z3_context c, Z3_func_decl f, unsigned n, 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(Z3_context c, Z3_sort domain, 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(Z3_context c, 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(Z3_context c, Z3_sort domain, 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(Z3_context c, 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(Z3_context c, 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(Z3_context c, 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(Z3_context c, Z3_ast elem, Z3_ast set) { return Z3_mk_select(c, set, elem); } Z3_ast Z3_mk_set_add(Z3_context c, Z3_ast set, Z3_ast elem) { return Z3_mk_store(c, set, elem, Z3_mk_true(c)); } Z3_ast Z3_mk_set_del(Z3_context c, Z3_ast set, 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-z3-4.4.1/src/api/api_ast.cpp000066400000000000000000001303101260446376700161070ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_ast.cpp Abstract: Basic API for ASTs Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include"api_log_macros.h" #include"api_context.h" #include"api_util.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"pb_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_safe_replace.h" #include"pp.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"scoped_timer.h" #include"pp_params.hpp" extern bool is_numeral_sort(Z3_context c, Z3_sort ty); extern "C" { Z3_symbol Z3_API Z3_mk_int_symbol(Z3_context c, int i) { Z3_TRY; LOG_Z3_mk_int_symbol(c, i); RESET_ERROR_CODE(); if (i < 0 || (size_t)i >= (SIZE_MAX >> PTR_ALIGNMENT)) { SET_ERROR_CODE(Z3_IOB); return 0; } Z3_symbol result = of_symbol(symbol(i)); return result; Z3_CATCH_RETURN(0); } Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, char const * str) { Z3_TRY; LOG_Z3_mk_string_symbol(c, str); RESET_ERROR_CODE(); symbol s; if (str == 0 || *str == 0) s = symbol::null; else s = symbol(str); Z3_symbol result = of_symbol(s); return result; Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_is_eq_sort(Z3_context c, Z3_sort s1, Z3_sort s2) { RESET_ERROR_CODE(); return s1 == s2; } Z3_sort Z3_API Z3_mk_uninterpreted_sort(Z3_context c, Z3_symbol name) { Z3_TRY; LOG_Z3_mk_uninterpreted_sort(c, name); RESET_ERROR_CODE(); sort* ty = mk_c(c)->m().mk_uninterpreted_sort(to_symbol(name)); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_is_eq_ast(Z3_context c, Z3_ast s1, Z3_ast s2) { RESET_ERROR_CODE(); return s1 == s2; } Z3_bool Z3_API Z3_is_eq_func_decl(Z3_context c, Z3_func_decl s1, Z3_func_decl s2) { RESET_ERROR_CODE(); return s1 == s2; } Z3_func_decl Z3_API Z3_mk_func_decl(Z3_context c, Z3_symbol s, unsigned domain_size, Z3_sort const* domain, Z3_sort range) { Z3_TRY; LOG_Z3_mk_func_decl(c, s, domain_size, domain, range); RESET_ERROR_CODE(); func_decl* d = mk_c(c)->m().mk_func_decl(to_symbol(s), domain_size, to_sorts(domain), to_sort(range)); mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_app(Z3_context c, Z3_func_decl d, unsigned num_args, Z3_ast const * args) { Z3_TRY; LOG_Z3_mk_app(c, d, num_args, args); RESET_ERROR_CODE(); ptr_buffer arg_list; for (unsigned i = 0; i < num_args; ++i) { arg_list.push_back(to_expr(args[i])); } func_decl* _d = reinterpret_cast(d); app* a = mk_c(c)->m().mk_app(_d, num_args, arg_list.c_ptr()); 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_const(Z3_context c, Z3_symbol s, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_const(c, s, ty); RESET_ERROR_CODE(); app* a = mk_c(c)->m().mk_const(mk_c(c)->m().mk_const_decl(to_symbol(s), to_sort(ty))); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_label(Z3_context c, Z3_symbol s, Z3_bool is_pos, Z3_ast f) { Z3_TRY; LOG_Z3_mk_label(c, s, is_pos, f); RESET_ERROR_CODE(); expr* _f = to_expr(f); if (!mk_c(c)->m().is_bool(_f)) { SET_ERROR_CODE(Z3_SORT_ERROR); return f; } expr* a = mk_c(c)->m().mk_label(is_pos != 0, to_symbol(s), _f); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_mk_fresh_func_decl(Z3_context c, const char * prefix, unsigned domain_size, Z3_sort const domain[], Z3_sort range) { Z3_TRY; LOG_Z3_mk_fresh_func_decl(c, prefix, domain_size, domain, range); RESET_ERROR_CODE(); if (prefix == 0) { prefix = ""; } func_decl* d = mk_c(c)->m().mk_fresh_func_decl(prefix, domain_size, reinterpret_cast(domain), to_sort(range)); mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fresh_const(Z3_context c, const char * prefix, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fresh_const(c, prefix, ty); RESET_ERROR_CODE(); if (prefix == 0) { prefix = ""; } app* a = mk_c(c)->m().mk_fresh_const(prefix, to_sort(ty)); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_true(Z3_context c) { Z3_TRY; LOG_Z3_mk_true(c); RESET_ERROR_CODE(); Z3_ast r = of_ast(mk_c(c)->m().mk_true()); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_false(Z3_context c) { Z3_TRY; LOG_Z3_mk_false(c); RESET_ERROR_CODE(); Z3_ast r = of_ast(mk_c(c)->m().mk_false()); RETURN_Z3(r); Z3_CATCH_RETURN(0); } MK_UNARY(Z3_mk_not, mk_c(c)->get_basic_fid(), OP_NOT, SKIP); MK_BINARY(Z3_mk_eq, mk_c(c)->get_basic_fid(), OP_EQ, SKIP); MK_NARY(Z3_mk_distinct, mk_c(c)->get_basic_fid(), OP_DISTINCT, SKIP); MK_BINARY(Z3_mk_iff, mk_c(c)->get_basic_fid(), OP_IFF, SKIP); MK_BINARY(Z3_mk_implies, mk_c(c)->get_basic_fid(), OP_IMPLIES, SKIP); MK_BINARY(Z3_mk_xor, mk_c(c)->get_basic_fid(), OP_XOR, SKIP); MK_NARY(Z3_mk_and, mk_c(c)->get_basic_fid(), OP_AND, SKIP); MK_NARY(Z3_mk_or, mk_c(c)->get_basic_fid(), OP_OR, SKIP); MK_UNARY(Z3_mk_interpolant, mk_c(c)->get_basic_fid(), OP_INTERP, SKIP); Z3_ast mk_ite_core(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3) { expr * result = mk_c(c)->m().mk_ite(to_expr(t1), to_expr(t2), to_expr(t3)); mk_c(c)->save_ast_trail(result); check_sorts(c, result); return of_ast(result); } Z3_ast Z3_API Z3_mk_ite(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3) { Z3_TRY; LOG_Z3_mk_ite(c, t1, t2, t3); RESET_ERROR_CODE(); Z3_ast r = mk_ite_core(c, t1, t2, t3); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_mk_bool_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_bool_sort(c); RESET_ERROR_CODE(); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->m().get_basic_family_id(), BOOL_SORT)); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_app_to_ast(Z3_context c, Z3_app a) { RESET_ERROR_CODE(); return (Z3_ast)(a); } Z3_ast Z3_API Z3_sort_to_ast(Z3_context c, Z3_sort s) { RESET_ERROR_CODE(); return (Z3_ast)(s); } Z3_ast Z3_API Z3_func_decl_to_ast(Z3_context c, Z3_func_decl f) { RESET_ERROR_CODE(); return (Z3_ast)(f); } // ------------------------ unsigned Z3_API Z3_get_ast_id(Z3_context c, Z3_ast t) { LOG_Z3_get_ast_id(c, t); RESET_ERROR_CODE(); return to_expr(t)->get_id(); } unsigned Z3_API Z3_get_func_decl_id(Z3_context c, Z3_func_decl f) { LOG_Z3_get_func_decl_id(c, f); RESET_ERROR_CODE(); return to_func_decl(f)->get_id(); } unsigned Z3_API Z3_get_sort_id(Z3_context c, Z3_sort s) { LOG_Z3_get_sort_id(c, s); RESET_ERROR_CODE(); return to_sort(s)->get_id(); } Z3_bool Z3_API Z3_is_well_sorted(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_is_well_sorted(c, t); RESET_ERROR_CODE(); return is_well_sorted(mk_c(c)->m(), to_expr(t)); Z3_CATCH_RETURN(Z3_FALSE); } Z3_symbol_kind Z3_API Z3_get_symbol_kind(Z3_context c, Z3_symbol s) { Z3_TRY; LOG_Z3_get_symbol_kind(c, s); RESET_ERROR_CODE(); symbol _s = to_symbol(s); return _s.is_numerical() ? Z3_INT_SYMBOL : Z3_STRING_SYMBOL; Z3_CATCH_RETURN(Z3_INT_SYMBOL); } int Z3_API Z3_get_symbol_int(Z3_context c, Z3_symbol s) { Z3_TRY; LOG_Z3_get_symbol_int(c, s); RESET_ERROR_CODE(); symbol _s = to_symbol(s); if (_s.is_numerical()) { return _s.get_num(); } SET_ERROR_CODE(Z3_INVALID_ARG); return -1; Z3_CATCH_RETURN(-1); } Z3_API char const * Z3_get_symbol_string(Z3_context c, Z3_symbol s) { Z3_TRY; LOG_Z3_get_symbol_string(c, s); RESET_ERROR_CODE(); symbol _s = to_symbol(s); if (_s.is_numerical()) { std::ostringstream buffer; buffer << _s.get_num(); return mk_c(c)->mk_external_string(buffer.str()); } else { return mk_c(c)->mk_external_string(_s.bare_str()); } Z3_CATCH_RETURN(""); } Z3_ast_kind Z3_API Z3_get_ast_kind(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_ast_kind(c, a); RESET_ERROR_CODE(); CHECK_VALID_AST(a, Z3_UNKNOWN_AST); ast * _a = to_expr(a); switch (_a->get_kind()) { case AST_APP: { expr * e = to_expr(_a); // Real algebraic numbers are not considered Z3_NUMERAL_AST if (is_numeral_sort(c, of_sort(mk_c(c)->m().get_sort(e))) && mk_c(c)->m().is_unique_value(e)) return Z3_NUMERAL_AST; return Z3_APP_AST; } case AST_VAR: return Z3_VAR_AST; case AST_QUANTIFIER: return Z3_QUANTIFIER_AST; case AST_SORT: return Z3_SORT_AST; case AST_FUNC_DECL: return Z3_FUNC_DECL_AST; default: return Z3_UNKNOWN_AST; } Z3_CATCH_RETURN(Z3_UNKNOWN_AST); } unsigned Z3_API Z3_get_ast_hash(Z3_context c, Z3_ast a) { LOG_Z3_get_ast_hash(c, a); RESET_ERROR_CODE(); return to_ast(a)->hash(); } Z3_bool Z3_API Z3_is_app(Z3_context c, Z3_ast a) { LOG_Z3_is_app(c, a); RESET_ERROR_CODE(); return is_app(reinterpret_cast(a)); } Z3_app Z3_API Z3_to_app(Z3_context c, Z3_ast a) { LOG_Z3_to_app(c, a); RESET_ERROR_CODE(); SASSERT(is_app(reinterpret_cast(a))); RETURN_Z3(of_app(reinterpret_cast(a))); } Z3_func_decl Z3_API Z3_to_func_decl(Z3_context c, Z3_ast a) { LOG_Z3_to_func_decl(c, a); RESET_ERROR_CODE(); SASSERT(is_func_decl(reinterpret_cast(a))); RETURN_Z3(of_func_decl(reinterpret_cast(a))); } Z3_func_decl Z3_API Z3_get_app_decl(Z3_context c, Z3_app a) { LOG_Z3_get_app_decl(c, a); RESET_ERROR_CODE(); if (!is_app(reinterpret_cast(a))) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } RETURN_Z3(of_func_decl(to_app(a)->get_decl())); } unsigned Z3_API Z3_get_app_num_args(Z3_context c, Z3_app a) { LOG_Z3_get_app_num_args(c, a); RESET_ERROR_CODE(); return to_app(a)->get_num_args(); } Z3_ast Z3_API Z3_get_app_arg(Z3_context c, Z3_app a, unsigned i) { LOG_Z3_get_app_arg(c, a, i); RESET_ERROR_CODE(); if (!is_app(reinterpret_cast(a))) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } if (i >= to_app(a)->get_num_args()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } RETURN_Z3(of_ast(to_app(a)->get_arg(i))); } Z3_symbol Z3_API Z3_get_decl_name(Z3_context c, Z3_func_decl d) { LOG_Z3_get_decl_name(c, d); RESET_ERROR_CODE(); return of_symbol(to_func_decl(d)->get_name()); } unsigned Z3_API Z3_get_decl_num_parameters(Z3_context c, Z3_func_decl d) { LOG_Z3_get_decl_num_parameters(c, d); RESET_ERROR_CODE(); return to_func_decl(d)->get_num_parameters(); } Z3_parameter_kind Z3_API Z3_get_decl_parameter_kind(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_parameter_kind(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); return Z3_PARAMETER_INT; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (p.is_int()) { return Z3_PARAMETER_INT; } if (p.is_double()) { return Z3_PARAMETER_DOUBLE; } if (p.is_symbol()) { return Z3_PARAMETER_SYMBOL; } if (p.is_rational()) { return Z3_PARAMETER_RATIONAL; } if (p.is_ast() && is_sort(p.get_ast())) { return Z3_PARAMETER_SORT; } if (p.is_ast() && is_expr(p.get_ast())) { return Z3_PARAMETER_AST; } SASSERT(p.is_ast() && is_func_decl(p.get_ast())); return Z3_PARAMETER_FUNC_DECL; Z3_CATCH_RETURN(Z3_PARAMETER_INT); } int Z3_API Z3_get_decl_int_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_int_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); return 0; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_int()) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } return p.get_int(); Z3_CATCH_RETURN(0); } double Z3_API Z3_get_decl_double_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_double_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); return 0; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_double()) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } return p.get_double(); Z3_CATCH_RETURN(0.0); } Z3_symbol Z3_API Z3_get_decl_symbol_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_symbol_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); return 0; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_symbol()) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } return of_symbol(p.get_symbol()); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_get_decl_sort_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_sort_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast() || !is_sort(p.get_ast())) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } RETURN_Z3(of_sort(to_sort(p.get_ast()))); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_decl_ast_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_ast_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast()) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } RETURN_Z3(of_ast(p.get_ast())); Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_get_decl_func_decl_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_func_decl_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast() || !is_func_decl(p.get_ast())) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } RETURN_Z3(of_func_decl(to_func_decl(p.get_ast()))); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_get_decl_rational_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_rational_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); return ""; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_rational()) { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } return mk_c(c)->mk_external_string(p.get_rational().to_string()); Z3_CATCH_RETURN(""); } Z3_symbol Z3_API Z3_get_sort_name(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_sort_name(c, t); RESET_ERROR_CODE(); return of_symbol(to_sort(t)->get_name()); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_get_sort(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_sort(c, a); RESET_ERROR_CODE(); Z3_sort r = of_sort(mk_c(c)->m().get_sort(to_expr(a))); RETURN_Z3(r); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_arity(Z3_context c, Z3_func_decl d) { Z3_TRY; LOG_Z3_get_arity(c, d); RESET_ERROR_CODE(); return to_func_decl(d)->get_arity(); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_domain_size(Z3_context c, Z3_func_decl d) { Z3_TRY; LOG_Z3_get_domain_size(c, d); RESET_ERROR_CODE(); return to_func_decl(d)->get_arity(); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_get_domain(Z3_context c, Z3_func_decl d, unsigned i) { Z3_TRY; LOG_Z3_get_domain(c, d, i); RESET_ERROR_CODE(); if (i >= to_func_decl(d)->get_arity()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } Z3_sort r = of_sort(to_func_decl(d)->get_domain(i)); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_get_range(Z3_context c, Z3_func_decl d) { Z3_TRY; LOG_Z3_get_range(c, d); RESET_ERROR_CODE(); CHECK_VALID_AST(d, 0); Z3_sort r = of_sort(to_func_decl(d)->get_range()); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_sort_kind Z3_get_sort_kind(Z3_context c, Z3_sort t) { LOG_Z3_get_sort_kind(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, Z3_UNKNOWN_SORT); family_id fid = to_sort(t)->get_family_id(); decl_kind k = to_sort(t)->get_decl_kind(); if (mk_c(c)->m().is_uninterp(to_sort(t))) { return Z3_UNINTERPRETED_SORT; } else if (fid == mk_c(c)->m().get_basic_family_id() && k == BOOL_SORT) { return Z3_BOOL_SORT; } else if (fid == mk_c(c)->get_arith_fid() && k == INT_SORT) { return Z3_INT_SORT; } else if (fid == mk_c(c)->get_arith_fid() && k == REAL_SORT) { return Z3_REAL_SORT; } else if (fid == mk_c(c)->get_bv_fid() && k == BV_SORT) { return Z3_BV_SORT; } else if (fid == mk_c(c)->get_array_fid() && k == ARRAY_SORT) { return Z3_ARRAY_SORT; } else if (fid == mk_c(c)->get_dt_fid() && k == DATATYPE_SORT) { return Z3_DATATYPE_SORT; } else if (fid == mk_c(c)->get_datalog_fid() && k == datalog::DL_RELATION_SORT) { return Z3_RELATION_SORT; } else if (fid == mk_c(c)->get_datalog_fid() && k == datalog::DL_FINITE_SORT) { return Z3_FINITE_DOMAIN_SORT; } else if (fid == mk_c(c)->get_fpa_fid() && k == FLOATING_POINT_SORT) { return Z3_FLOATING_POINT_SORT; } else if (fid == mk_c(c)->get_fpa_fid() && k == ROUNDING_MODE_SORT) { return Z3_ROUNDING_MODE_SORT; } else { return Z3_UNKNOWN_SORT; } } Z3_lbool Z3_API Z3_get_bool_value(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_bool_value(c, a); RESET_ERROR_CODE(); CHECK_IS_EXPR(a, Z3_L_UNDEF); ast_manager & m = mk_c(c)->m(); ast * n = to_ast(a); if (m.is_true(to_expr(n))) return Z3_L_TRUE; if (m.is_false(to_expr(n))) return Z3_L_FALSE; return Z3_L_UNDEF; Z3_CATCH_RETURN(Z3_L_UNDEF); } static Z3_ast simplify(Z3_context c, Z3_ast _a, Z3_params _p) { Z3_TRY; RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * a = to_expr(_a); params_ref p = to_param_ref(_p); unsigned timeout = p.get_uint("timeout", mk_c(c)->get_timeout()); bool use_ctrl_c = p.get_bool("ctrl_c", false); th_rewriter m_rw(m, p); expr_ref result(m); cancel_eh eh(m_rw); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); try { m_rw(a, result); } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); return 0; } } mk_c(c)->save_ast_trail(result); return of_ast(result.get()); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_simplify(Z3_context c, Z3_ast _a) { LOG_Z3_simplify(c, _a); RETURN_Z3(simplify(c, _a, 0)); } Z3_ast Z3_API Z3_simplify_ex(Z3_context c, Z3_ast _a, Z3_params p) { LOG_Z3_simplify_ex(c, _a, p); RETURN_Z3(simplify(c, _a, p)); } Z3_string Z3_API Z3_simplify_get_help(Z3_context c) { Z3_TRY; LOG_Z3_simplify_get_help(c); RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; th_rewriter::get_param_descrs(descrs); descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(Z3_context c) { Z3_TRY; LOG_Z3_simplify_get_param_descrs(c); RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); mk_c(c)->save_object(d); th_rewriter::get_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_update_term(Z3_context c, Z3_ast _a, unsigned num_args, Z3_ast const _args[]) { Z3_TRY; LOG_Z3_update_term(c, _a, num_args, _args); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); expr* a = to_expr(_a); expr* const* args = to_exprs(_args); switch(a->get_kind()) { case AST_APP: { app* e = to_app(a); if (e->get_num_args() != num_args) { SET_ERROR_CODE(Z3_IOB); } else { a = m.mk_app(e->get_decl(), num_args, args); } break; } case AST_QUANTIFIER: { if (num_args != 1) { SET_ERROR_CODE(Z3_IOB); } else { a = m.update_quantifier(to_quantifier(a), args[0]); } break; } default: break; } mk_c(c)->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_substitute(Z3_context c, Z3_ast _a, unsigned num_exprs, Z3_ast const _from[], Z3_ast const _to[]) { Z3_TRY; LOG_Z3_substitute(c, _a, num_exprs, _from, _to); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * a = to_expr(_a); expr * const * from = to_exprs(_from); expr * const * to = to_exprs(_to); expr * r = 0; for (unsigned i = 0; i < num_exprs; i++) { if (m.get_sort(from[i]) != m.get_sort(to[i])) { SET_ERROR_CODE(Z3_SORT_ERROR); RETURN_Z3(of_expr(0)); } } expr_safe_replace subst(m); for (unsigned i = 0; i < num_exprs; i++) { subst.insert(from[i], to[i]); } expr_ref new_a(m); subst(a, new_a); mk_c(c)->save_ast_trail(new_a); r = new_a.get(); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_substitute_vars(Z3_context c, Z3_ast _a, unsigned num_exprs, Z3_ast const _to[]) { Z3_TRY; LOG_Z3_substitute_vars(c, _a, num_exprs, _to); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * a = to_expr(_a); expr * const * to = to_exprs(_to); var_subst subst(m, false); expr_ref new_a(m); subst(a, num_exprs, to, new_a); mk_c(c)->save_ast_trail(new_a); RETURN_Z3(of_expr(new_a.get())); Z3_CATCH_RETURN(0); } Z3_API char const * Z3_ast_to_string(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_ast_to_string(c, a); RESET_ERROR_CODE(); std::ostringstream buffer; switch (mk_c(c)->get_print_mode()) { case Z3_PRINT_SMTLIB_FULL: buffer << mk_pp(to_ast(a), mk_c(c)->m()); break; case Z3_PRINT_LOW_LEVEL: buffer << mk_ll_pp(to_ast(a), mk_c(c)->m()); break; case Z3_PRINT_SMTLIB_COMPLIANT: { ast_smt_pp pp(mk_c(c)->m()); pp_params params; pp.set_simplify_implies(params.simplify_implies()); ast* a1 = to_ast(a); pp.set_logic(mk_c(c)->fparams().m_smtlib_logic.c_str()); if (!is_expr(a1)) { buffer << mk_pp(a1, mk_c(c)->m()); break; } if (mk_c(c)->get_print_mode() == Z3_PRINT_SMTLIB_COMPLIANT) { pp.display_expr(buffer, to_expr(a1)); break; } if (mk_c(c)->get_print_mode() == Z3_PRINT_SMTLIB2_COMPLIANT) { pp.display_expr_smt2(buffer, to_expr(a1)); break; } break; } case Z3_PRINT_SMTLIB2_COMPLIANT: { buffer << mk_ismt2_pp(to_ast(a), mk_c(c)->m()); break; } default: UNREACHABLE(); } return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(0); } Z3_API char const * Z3_sort_to_string(Z3_context c, Z3_sort s) { return Z3_ast_to_string(c, reinterpret_cast(s)); } Z3_API char const * Z3_func_decl_to_string(Z3_context c, Z3_func_decl f) { return Z3_ast_to_string(c, reinterpret_cast(f)); } Z3_string Z3_API Z3_benchmark_to_smtlib_string(Z3_context c, Z3_string name, Z3_string logic, Z3_string status, Z3_string attributes, unsigned num_assumptions, Z3_ast const assumptions[], Z3_ast formula) { Z3_TRY; LOG_Z3_benchmark_to_smtlib_string(c, name, logic, status, attributes, num_assumptions, assumptions, formula); RESET_ERROR_CODE(); std::ostringstream buffer; ast_smt_pp pp(mk_c(c)->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(to_expr(assumptions[i])); } if (mk_c(c)->get_print_mode() == Z3_PRINT_SMTLIB2_COMPLIANT) { pp.display_smt2(buffer, to_expr(formula)); } else { pp.display(buffer, to_expr(formula)); } return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_decl_kind Z3_API Z3_get_decl_kind(Z3_context c, Z3_func_decl d) { Z3_TRY; LOG_Z3_get_decl_kind(c, d); RESET_ERROR_CODE(); func_decl* _d = to_func_decl(d); if (null_family_id == _d->get_family_id()) { return Z3_OP_UNINTERPRETED; } if (mk_c(c)->get_basic_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_TRUE: return Z3_OP_TRUE; case OP_FALSE: return Z3_OP_FALSE; case OP_EQ: return Z3_OP_EQ; case OP_DISTINCT: return Z3_OP_DISTINCT; case OP_ITE: return Z3_OP_ITE; case OP_AND: return Z3_OP_AND; case OP_OR: return Z3_OP_OR; case OP_IFF: return Z3_OP_IFF; case OP_XOR: return Z3_OP_XOR; case OP_NOT: return Z3_OP_NOT; case OP_IMPLIES: return Z3_OP_IMPLIES; case OP_OEQ: return Z3_OP_OEQ; case OP_INTERP: return Z3_OP_INTERP; case PR_UNDEF: return Z3_OP_PR_UNDEF; case PR_TRUE: return Z3_OP_PR_TRUE; case PR_ASSERTED: return Z3_OP_PR_ASSERTED; case PR_GOAL: return Z3_OP_PR_GOAL; case PR_MODUS_PONENS: return Z3_OP_PR_MODUS_PONENS; case PR_REFLEXIVITY: return Z3_OP_PR_REFLEXIVITY; case PR_SYMMETRY: return Z3_OP_PR_SYMMETRY; case PR_TRANSITIVITY: return Z3_OP_PR_TRANSITIVITY; case PR_TRANSITIVITY_STAR: return Z3_OP_PR_TRANSITIVITY_STAR; case PR_MONOTONICITY: return Z3_OP_PR_MONOTONICITY; case PR_QUANT_INTRO: return Z3_OP_PR_QUANT_INTRO; case PR_DISTRIBUTIVITY: return Z3_OP_PR_DISTRIBUTIVITY; case PR_AND_ELIM: return Z3_OP_PR_AND_ELIM; case PR_NOT_OR_ELIM: return Z3_OP_PR_NOT_OR_ELIM; case PR_REWRITE: return Z3_OP_PR_REWRITE; case PR_REWRITE_STAR: return Z3_OP_PR_REWRITE_STAR; case PR_PULL_QUANT: return Z3_OP_PR_PULL_QUANT; case PR_PULL_QUANT_STAR: return Z3_OP_PR_PULL_QUANT_STAR; case PR_PUSH_QUANT: return Z3_OP_PR_PUSH_QUANT; case PR_ELIM_UNUSED_VARS: return Z3_OP_PR_ELIM_UNUSED_VARS; case PR_DER: return Z3_OP_PR_DER; case PR_QUANT_INST: return Z3_OP_PR_QUANT_INST; case PR_HYPOTHESIS: return Z3_OP_PR_HYPOTHESIS; case PR_LEMMA: return Z3_OP_PR_LEMMA; case PR_UNIT_RESOLUTION: return Z3_OP_PR_UNIT_RESOLUTION; case PR_IFF_TRUE: return Z3_OP_PR_IFF_TRUE; case PR_IFF_FALSE: return Z3_OP_PR_IFF_FALSE; case PR_COMMUTATIVITY: return Z3_OP_PR_COMMUTATIVITY; case PR_DEF_AXIOM: return Z3_OP_PR_DEF_AXIOM; case PR_DEF_INTRO: return Z3_OP_PR_DEF_INTRO; case PR_APPLY_DEF: return Z3_OP_PR_APPLY_DEF; case PR_IFF_OEQ: return Z3_OP_PR_IFF_OEQ; case PR_NNF_POS: return Z3_OP_PR_NNF_POS; case PR_NNF_NEG: return Z3_OP_PR_NNF_NEG; case PR_NNF_STAR: return Z3_OP_PR_NNF_STAR; case PR_SKOLEMIZE: return Z3_OP_PR_SKOLEMIZE; case PR_CNF_STAR: return Z3_OP_PR_CNF_STAR; case PR_MODUS_PONENS_OEQ: return Z3_OP_PR_MODUS_PONENS_OEQ; case PR_TH_LEMMA: return Z3_OP_PR_TH_LEMMA; case PR_HYPER_RESOLVE: return Z3_OP_PR_HYPER_RESOLVE; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; } } if (mk_c(c)->get_arith_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_NUM: return Z3_OP_ANUM; case OP_IRRATIONAL_ALGEBRAIC_NUM: return Z3_OP_AGNUM; case OP_LE: return Z3_OP_LE; case OP_GE: return Z3_OP_GE; case OP_LT: return Z3_OP_LT; case OP_GT: return Z3_OP_GT; case OP_ADD: return Z3_OP_ADD; case OP_SUB: return Z3_OP_SUB; case OP_UMINUS: return Z3_OP_UMINUS; case OP_MUL: return Z3_OP_MUL; case OP_DIV: return Z3_OP_DIV; case OP_IDIV: return Z3_OP_IDIV; case OP_REM: return Z3_OP_REM; case OP_MOD: return Z3_OP_MOD; case OP_POWER: return Z3_OP_POWER; case OP_TO_REAL: return Z3_OP_TO_REAL; case OP_TO_INT: return Z3_OP_TO_INT; case OP_IS_INT: return Z3_OP_IS_INT; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; } } if (mk_c(c)->get_array_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_STORE: return Z3_OP_STORE; case OP_SELECT: return Z3_OP_SELECT; case OP_CONST_ARRAY: return Z3_OP_CONST_ARRAY; case OP_ARRAY_DEFAULT: return Z3_OP_ARRAY_DEFAULT; case OP_ARRAY_MAP: return Z3_OP_ARRAY_MAP; case OP_SET_UNION: return Z3_OP_SET_UNION; case OP_SET_INTERSECT: return Z3_OP_SET_INTERSECT; case OP_SET_DIFFERENCE: return Z3_OP_SET_DIFFERENCE; case OP_SET_COMPLEMENT: return Z3_OP_SET_COMPLEMENT; case OP_SET_SUBSET: return Z3_OP_SET_SUBSET; case OP_AS_ARRAY: return Z3_OP_AS_ARRAY; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; } } if (mk_c(c)->get_bv_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_BV_NUM: return Z3_OP_BNUM; case OP_BIT1: return Z3_OP_BIT1; case OP_BIT0: return Z3_OP_BIT0; case OP_BNEG: return Z3_OP_BNEG; case OP_BADD: return Z3_OP_BADD; case OP_BSUB: return Z3_OP_BSUB; case OP_BMUL: return Z3_OP_BMUL; case OP_BSDIV: return Z3_OP_BSDIV; case OP_BUDIV: return Z3_OP_BUDIV; case OP_BSREM: return Z3_OP_BSREM; case OP_BUREM: return Z3_OP_BUREM; case OP_BSMOD: return Z3_OP_BSMOD; case OP_BSDIV0: return Z3_OP_BSDIV0; case OP_BUDIV0: return Z3_OP_BUDIV0; case OP_BSREM0: return Z3_OP_BUREM0; case OP_BUREM0: return Z3_OP_BUREM0; case OP_BSMOD0: return Z3_OP_BSMOD0; case OP_ULEQ: return Z3_OP_ULEQ; case OP_SLEQ: return Z3_OP_SLEQ; case OP_UGEQ: return Z3_OP_UGEQ; case OP_SGEQ: return Z3_OP_SGEQ; case OP_ULT: return Z3_OP_ULT; case OP_SLT: return Z3_OP_SLT; case OP_UGT: return Z3_OP_UGT; case OP_SGT: return Z3_OP_SGT; case OP_BAND: return Z3_OP_BAND; case OP_BOR: return Z3_OP_BOR; case OP_BNOT: return Z3_OP_BNOT; case OP_BXOR: return Z3_OP_BXOR; case OP_BNAND: return Z3_OP_BNAND; case OP_BNOR: return Z3_OP_BNOR; case OP_BXNOR: return Z3_OP_BXNOR; case OP_CONCAT: return Z3_OP_CONCAT; case OP_SIGN_EXT: return Z3_OP_SIGN_EXT; case OP_ZERO_EXT: return Z3_OP_ZERO_EXT; case OP_EXTRACT: return Z3_OP_EXTRACT; case OP_REPEAT: return Z3_OP_REPEAT; case OP_BREDOR: return Z3_OP_BREDOR; case OP_BREDAND: return Z3_OP_BREDAND; case OP_BCOMP: return Z3_OP_BCOMP; case OP_BSHL: return Z3_OP_BSHL; case OP_BLSHR: return Z3_OP_BLSHR; case OP_BASHR: return Z3_OP_BASHR; case OP_ROTATE_LEFT: return Z3_OP_ROTATE_LEFT; case OP_ROTATE_RIGHT: return Z3_OP_ROTATE_RIGHT; case OP_EXT_ROTATE_LEFT: return Z3_OP_EXT_ROTATE_LEFT; case OP_EXT_ROTATE_RIGHT: return Z3_OP_EXT_ROTATE_RIGHT; case OP_INT2BV: return Z3_OP_INT2BV; case OP_BV2INT: return Z3_OP_BV2INT; case OP_CARRY: return Z3_OP_CARRY; case OP_XOR3: return Z3_OP_XOR3; case OP_BSMUL_NO_OVFL: case OP_BUMUL_NO_OVFL: case OP_BSMUL_NO_UDFL: case OP_BSDIV_I: case OP_BUDIV_I: case OP_BSREM_I: case OP_BUREM_I: case OP_BSMOD_I: return Z3_OP_UNINTERPRETED; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; } } if (mk_c(c)->get_dt_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR; case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER; case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR; case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; } } if (mk_c(c)->get_datalog_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case datalog::OP_RA_STORE: return Z3_OP_RA_STORE; case datalog::OP_RA_EMPTY: return Z3_OP_RA_EMPTY; case datalog::OP_RA_IS_EMPTY: return Z3_OP_RA_IS_EMPTY; case datalog::OP_RA_JOIN: return Z3_OP_RA_JOIN; case datalog::OP_RA_UNION: return Z3_OP_RA_UNION; case datalog::OP_RA_WIDEN: return Z3_OP_RA_WIDEN; case datalog::OP_RA_PROJECT: return Z3_OP_RA_PROJECT; case datalog::OP_RA_FILTER: return Z3_OP_RA_FILTER; case datalog::OP_RA_NEGATION_FILTER: return Z3_OP_RA_NEGATION_FILTER; case datalog::OP_RA_RENAME: return Z3_OP_RA_RENAME; case datalog::OP_RA_COMPLEMENT: return Z3_OP_RA_COMPLEMENT; case datalog::OP_RA_SELECT: return Z3_OP_RA_SELECT; case datalog::OP_RA_CLONE: return Z3_OP_RA_CLONE; case datalog::OP_DL_LT: return Z3_OP_FD_LT; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; } } if (mk_c(c)->get_fpa_fid() == _d->get_family_id()) { switch (_d->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: return Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; case OP_FPA_RM_NEAREST_TIES_TO_AWAY: return Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; case OP_FPA_RM_TOWARD_POSITIVE: return Z3_OP_FPA_RM_TOWARD_POSITIVE; case OP_FPA_RM_TOWARD_NEGATIVE: return Z3_OP_FPA_RM_TOWARD_NEGATIVE; case OP_FPA_RM_TOWARD_ZERO: return Z3_OP_FPA_RM_TOWARD_ZERO; case OP_FPA_NUM: return Z3_OP_FPA_NUM; case OP_FPA_PLUS_INF: return Z3_OP_FPA_PLUS_INF; case OP_FPA_MINUS_INF: return Z3_OP_FPA_MINUS_INF; case OP_FPA_NAN: return Z3_OP_FPA_NAN; case OP_FPA_MINUS_ZERO: return Z3_OP_FPA_MINUS_ZERO; case OP_FPA_PLUS_ZERO: return Z3_OP_FPA_PLUS_ZERO; case OP_FPA_ADD: return Z3_OP_FPA_ADD; case OP_FPA_SUB: return Z3_OP_FPA_SUB; case OP_FPA_NEG: return Z3_OP_FPA_NEG; case OP_FPA_MUL: return Z3_OP_FPA_MUL; case OP_FPA_DIV: return Z3_OP_FPA_DIV; case OP_FPA_REM: return Z3_OP_FPA_REM; case OP_FPA_ABS: return Z3_OP_FPA_ABS; case OP_FPA_MIN: return Z3_OP_FPA_MIN; case OP_FPA_MAX: return Z3_OP_FPA_MAX; case OP_FPA_FMA: return Z3_OP_FPA_FMA; case OP_FPA_SQRT: return Z3_OP_FPA_SQRT; case OP_FPA_EQ: return Z3_OP_FPA_EQ; case OP_FPA_ROUND_TO_INTEGRAL: return Z3_OP_FPA_ROUND_TO_INTEGRAL; case OP_FPA_LT: return Z3_OP_FPA_LT; case OP_FPA_GT: return Z3_OP_FPA_GT; case OP_FPA_LE: return Z3_OP_FPA_LE; case OP_FPA_GE: return Z3_OP_FPA_GE; case OP_FPA_IS_NAN: return Z3_OP_FPA_IS_NAN; case OP_FPA_IS_INF: return Z3_OP_FPA_IS_INF; case OP_FPA_IS_ZERO: return Z3_OP_FPA_IS_ZERO; case OP_FPA_IS_NORMAL: return Z3_OP_FPA_IS_NORMAL; case OP_FPA_IS_SUBNORMAL: return Z3_OP_FPA_IS_SUBNORMAL; case OP_FPA_IS_NEGATIVE: return Z3_OP_FPA_IS_NEGATIVE; case OP_FPA_IS_POSITIVE: return Z3_OP_FPA_IS_POSITIVE; case OP_FPA_FP: return Z3_OP_FPA_FP; case OP_FPA_TO_FP: return Z3_OP_FPA_TO_FP; case OP_FPA_TO_FP_UNSIGNED: return Z3_OP_FPA_TO_FP_UNSIGNED; case OP_FPA_TO_UBV: return Z3_OP_FPA_TO_UBV; case OP_FPA_TO_SBV: return Z3_OP_FPA_TO_SBV; case OP_FPA_TO_REAL: return Z3_OP_FPA_TO_REAL; case OP_FPA_TO_IEEE_BV: return Z3_OP_FPA_TO_IEEE_BV; case OP_FPA_INTERNAL_BVWRAP: case OP_FPA_INTERNAL_BVUNWRAP: case OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED: case OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED: case OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED: return Z3_OP_UNINTERPRETED; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; } } if (mk_c(c)->m().get_label_family_id() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_LABEL: return Z3_OP_LABEL; case OP_LABEL_LIT: return Z3_OP_LABEL_LIT; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; } } if (mk_c(c)->get_pb_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_PB_LE: return Z3_OP_PB_LE; case OP_PB_GE: return Z3_OP_PB_GE; case OP_AT_MOST_K: return Z3_OP_PB_AT_MOST; default: UNREACHABLE(); } } return Z3_OP_UNINTERPRETED; Z3_CATCH_RETURN(Z3_OP_UNINTERPRETED); } unsigned Z3_API Z3_get_index_value(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_index_value(c, a); RESET_ERROR_CODE(); ast* _a = reinterpret_cast(a); if (!_a || _a->get_kind() != AST_VAR) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } var* va = to_var(_a); if (va) { return va->get_idx(); } SET_ERROR_CODE(Z3_INVALID_ARG); return 0; Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_translate(Z3_context c, Z3_ast a, Z3_context target) { Z3_TRY; LOG_Z3_translate(c, a, target); RESET_ERROR_CODE(); CHECK_VALID_AST(a, 0); if (c == target) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } SASSERT(mk_c(c)->m().contains(to_ast(a))); ast_translation translator(mk_c(c)->m(), mk_c(target)->m()); ast * _result = translator(to_ast(a)); mk_c(target)->save_ast_trail(_result); RETURN_Z3(of_ast(_result)); Z3_CATCH_RETURN(0); } }; z3-z3-4.4.1/src/api/api_ast_map.cpp000066400000000000000000000115231260446376700167500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_ast_map.h000066400000000000000000000014111260446376700164100ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_ast_map.h Abstract: API for creating AST maps Author: Leonardo de Moura (leonardo) 2012-03-09. Revision History: --*/ #ifndef API_AST_MAP_H_ #define API_AST_MAP_H_ #include"api_util.h" #include"obj_hashtable.h" struct Z3_ast_map_ref : public api::object { ast_manager & m; obj_map m_map; Z3_ast_map_ref(ast_manager & _m):m(_m) {} virtual ~Z3_ast_map_ref(); }; inline Z3_ast_map_ref * to_ast_map(Z3_ast_map v) { return reinterpret_cast(v); } inline Z3_ast_map of_ast_map(Z3_ast_map_ref * v) { return reinterpret_cast(v); } inline obj_map & to_ast_map_ref(Z3_ast_map v) { return to_ast_map(v)->m_map; } #endif z3-z3-4.4.1/src/api/api_ast_vector.cpp000066400000000000000000000076551260446376700175100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_ast_vector.h000066400000000000000000000014271260446376700171440ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_bv.cpp000066400000000000000000000337251260446376700157430ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_bv.cpp Abstract: API for bv 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"bv_decl_plugin.h" extern "C" { Z3_sort Z3_API Z3_mk_bv_sort(Z3_context c, unsigned sz) { Z3_TRY; LOG_Z3_mk_bv_sort(c, sz); RESET_ERROR_CODE(); if (sz == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); } parameter p(sz); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_bv_fid(), BV_SORT, 1, &p)); RETURN_Z3(r); Z3_CATCH_RETURN(0); } #define MK_BV_UNARY(NAME, OP) MK_UNARY(NAME, mk_c(c)->get_bv_fid(), OP, SKIP) #define MK_BV_BINARY(NAME, OP) MK_BINARY(NAME, mk_c(c)->get_bv_fid(), OP, SKIP) MK_BV_UNARY(Z3_mk_bvnot, OP_BNOT); MK_BV_UNARY(Z3_mk_bvredand, OP_BREDAND); MK_BV_UNARY(Z3_mk_bvredor, OP_BREDOR); MK_BV_BINARY(Z3_mk_bvand, OP_BAND); MK_BV_BINARY(Z3_mk_bvor, OP_BOR); MK_BV_BINARY(Z3_mk_bvxor, OP_BXOR); MK_BV_BINARY(Z3_mk_bvnand, OP_BNAND); MK_BV_BINARY(Z3_mk_bvnor, OP_BNOR); MK_BV_BINARY(Z3_mk_bvxnor, OP_BXNOR); MK_BV_BINARY(Z3_mk_bvadd, OP_BADD); MK_BV_BINARY(Z3_mk_bvmul, OP_BMUL); MK_BV_BINARY(Z3_mk_bvudiv, OP_BUDIV); MK_BV_BINARY(Z3_mk_bvsdiv, OP_BSDIV); MK_BV_BINARY(Z3_mk_bvurem, OP_BUREM); MK_BV_BINARY(Z3_mk_bvsrem, OP_BSREM); MK_BV_BINARY(Z3_mk_bvsmod, OP_BSMOD); MK_BV_BINARY(Z3_mk_bvule, OP_ULEQ); MK_BV_BINARY(Z3_mk_bvsle, OP_SLEQ); MK_BV_BINARY(Z3_mk_bvuge, OP_UGEQ); MK_BV_BINARY(Z3_mk_bvsge, OP_SGEQ); MK_BV_BINARY(Z3_mk_bvult, OP_ULT); MK_BV_BINARY(Z3_mk_bvslt, OP_SLT); MK_BV_BINARY(Z3_mk_bvugt, OP_UGT); MK_BV_BINARY(Z3_mk_bvsgt, OP_SGT); MK_BV_BINARY(Z3_mk_concat, OP_CONCAT); MK_BV_BINARY(Z3_mk_bvshl, OP_BSHL); MK_BV_BINARY(Z3_mk_bvlshr, OP_BLSHR); MK_BV_BINARY(Z3_mk_bvashr, OP_BASHR); MK_BV_BINARY(Z3_mk_ext_rotate_left, OP_EXT_ROTATE_LEFT); MK_BV_BINARY(Z3_mk_ext_rotate_right, OP_EXT_ROTATE_RIGHT); Z3_ast mk_extract_core(Z3_context c, unsigned high, unsigned low, Z3_ast n) { expr * _n = to_expr(n); parameter params[2] = { parameter(high), parameter(low) }; expr * a = mk_c(c)->m().mk_app(mk_c(c)->get_bv_fid(), OP_EXTRACT, 2, params, 1, &_n); mk_c(c)->save_ast_trail(a); check_sorts(c, a); return of_ast(a); } Z3_ast Z3_API Z3_mk_extract(Z3_context c, unsigned high, unsigned low, Z3_ast n) { Z3_TRY; LOG_Z3_mk_extract(c, high, low, n); RESET_ERROR_CODE(); Z3_ast r = mk_extract_core(c, high, low, n); RETURN_Z3(r); Z3_CATCH_RETURN(0); } #define MK_BV_PUNARY(NAME, OP) \ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_TRY; \ LOG_ ## NAME(c, i, n); \ RESET_ERROR_CODE(); \ expr * _n = to_expr(n); \ parameter p(i); \ ast* a = mk_c(c)->m().mk_app(mk_c(c)->get_bv_fid(), OP, 1, &p, 1, &_n); \ mk_c(c)->save_ast_trail(a); \ check_sorts(c, a); \ RETURN_Z3(of_ast(a)); \ Z3_CATCH_RETURN(0); \ } MK_BV_PUNARY(Z3_mk_sign_ext, OP_SIGN_EXT); MK_BV_PUNARY(Z3_mk_zero_ext, OP_ZERO_EXT); MK_BV_PUNARY(Z3_mk_repeat, OP_REPEAT); MK_BV_PUNARY(Z3_mk_rotate_left, OP_ROTATE_LEFT); MK_BV_PUNARY(Z3_mk_rotate_right, OP_ROTATE_RIGHT); MK_BV_PUNARY(Z3_mk_int2bv, OP_INT2BV); Z3_ast Z3_API Z3_mk_bv2int(Z3_context c, Z3_ast n, Z3_bool is_signed) { Z3_TRY; LOG_Z3_mk_bv2int(c, n, is_signed); RESET_ERROR_CODE(); Z3_sort int_s = Z3_mk_int_sort(c); if (is_signed) { Z3_ast r = Z3_mk_bv2int(c, n, false); Z3_inc_ref(c, r); Z3_sort s = Z3_get_sort(c, n); unsigned sz = Z3_get_bv_sort_size(c, s); rational max_bound = power(rational(2), sz); Z3_ast bound = Z3_mk_numeral(c, max_bound.to_string().c_str(), int_s); Z3_inc_ref(c, bound); Z3_ast zero = Z3_mk_int(c, 0, s); Z3_inc_ref(c, zero); Z3_ast pred = Z3_mk_bvslt(c, n, zero); Z3_inc_ref(c, pred); // if n <_sigend 0 then r - s^sz else r Z3_ast args[2] = { r, bound }; Z3_ast sub = Z3_mk_sub(c, 2, args); Z3_inc_ref(c, sub); Z3_ast res = Z3_mk_ite(c, pred, sub, r); Z3_dec_ref(c, bound); Z3_dec_ref(c, pred); Z3_dec_ref(c, sub); Z3_dec_ref(c, zero); Z3_dec_ref(c, r); RETURN_Z3(res); } else { expr * _n = to_expr(n); parameter p(to_sort(int_s)); ast* a = mk_c(c)->m().mk_app(mk_c(c)->get_bv_fid(), OP_BV2INT, 1, &p, 1, &_n); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); } Z3_CATCH_RETURN(0); } /** \brief \mlh mk_bvmsb c s \endmlh Create a bit-vector of sort \s with 1 in the most significant bit position. The sort \s must be a bit-vector sort. This function is a shorthand for shl(1, N-1) where N are the number of bits of \c s. */ Z3_ast Z3_API Z3_mk_bvmsb(Z3_context c, Z3_sort s) { Z3_TRY; RESET_ERROR_CODE(); // Not logging this one, since it is just syntax sugar. unsigned sz = Z3_get_bv_sort_size(c, s); if (sz == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } Z3_ast x = Z3_mk_int64(c, 1, s); Z3_inc_ref(c, x); Z3_ast y = Z3_mk_int64(c, sz - 1, s); Z3_inc_ref(c, y); Z3_ast result = Z3_mk_bvshl(c, x, y); Z3_dec_ref(c, x); Z3_dec_ref(c, y); return result; Z3_CATCH_RETURN(0); } Z3_ast Z3_mk_bvsmin(Z3_context c, Z3_sort s) { return Z3_mk_bvmsb(c, s); } Z3_ast Z3_mk_bvsmax(Z3_context c, Z3_sort s) { return Z3_mk_bvnot(c, Z3_mk_bvmsb(c, s)); } Z3_ast Z3_mk_bvumax(Z3_context c, Z3_sort s) { return Z3_mk_int(c, -1, s); } Z3_ast Z3_API Z3_mk_bvadd_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed) { Z3_TRY; RESET_ERROR_CODE(); if (is_signed) { Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); Z3_inc_ref(c, zero); Z3_ast r = Z3_mk_bvadd(c, t1, t2); Z3_inc_ref(c, r); Z3_ast l1 = Z3_mk_bvslt(c, zero, t1); Z3_inc_ref(c, l1); Z3_ast l2 = Z3_mk_bvslt(c, zero, t2); Z3_inc_ref(c, l2); Z3_ast args[2] = { l1, l2 }; Z3_ast args_pos = Z3_mk_and(c, 2, args); Z3_inc_ref(c, args_pos); Z3_ast result = Z3_mk_implies(c, args_pos, Z3_mk_bvslt(c, zero, r)); Z3_dec_ref(c, r); Z3_dec_ref(c, l1); Z3_dec_ref(c, l2); Z3_dec_ref(c, args_pos); Z3_dec_ref(c, zero); return result; } else { unsigned sz = Z3_get_bv_sort_size(c, Z3_get_sort(c, t1)); t1 = Z3_mk_zero_ext(c, 1, t1); Z3_inc_ref(c, t1); t2 = Z3_mk_zero_ext(c, 1, t2); Z3_inc_ref(c, t2); Z3_ast r = Z3_mk_bvadd(c, t1, t2); Z3_inc_ref(c, r); Z3_ast ex = Z3_mk_extract(c, sz, sz, r); Z3_inc_ref(c, ex); Z3_ast result = Z3_mk_eq(c, ex, Z3_mk_int(c, 0, Z3_mk_bv_sort(c, 1))); Z3_dec_ref(c, t1); Z3_dec_ref(c, t2); Z3_dec_ref(c, ex); Z3_dec_ref(c, r); return result; } Z3_CATCH_RETURN(0); } // only for signed machine integers Z3_ast Z3_API Z3_mk_bvadd_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); Z3_inc_ref(c, zero); Z3_ast r = Z3_mk_bvadd(c, t1, t2); Z3_inc_ref(c, r); Z3_ast l1 = Z3_mk_bvslt(c, t1, zero); Z3_inc_ref(c, l1); Z3_ast l2 = Z3_mk_bvslt(c, t2, zero); Z3_inc_ref(c, l2); Z3_ast args[2] = { l1, l2 }; Z3_ast args_neg = Z3_mk_and(c, 2, args); Z3_inc_ref(c, args_neg); Z3_ast lt = Z3_mk_bvslt(c, r, zero); Z3_inc_ref(c, lt); Z3_ast result = Z3_mk_implies(c, args_neg, lt); Z3_dec_ref(c, lt); Z3_dec_ref(c, l1); Z3_dec_ref(c, l2); Z3_dec_ref(c, r); Z3_dec_ref(c, args_neg); Z3_dec_ref(c, zero); return result; Z3_CATCH_RETURN(0); } // only for signed machine integers Z3_ast Z3_API Z3_mk_bvsub_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); Z3_inc_ref(c, minus_t2); Z3_sort s = Z3_get_sort(c, t2); Z3_ast min = Z3_mk_bvsmin(c, s); Z3_inc_ref(c, min); Z3_ast x = Z3_mk_eq(c, t2, min); Z3_inc_ref(c, x); Z3_ast zero = Z3_mk_int(c, 0, s); Z3_inc_ref(c, zero); Z3_ast y = Z3_mk_bvslt(c, t1, zero); Z3_inc_ref(c, y); Z3_ast z = Z3_mk_bvadd_no_overflow(c, t1, minus_t2, true); Z3_inc_ref(c, z); Z3_ast result = Z3_mk_ite(c, x, y, z); mk_c(c)->save_ast_trail(to_app(result)); Z3_dec_ref(c, minus_t2); Z3_dec_ref(c, min); Z3_dec_ref(c, x); Z3_dec_ref(c, y); Z3_dec_ref(c, z); Z3_dec_ref(c, zero); return result; Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_bvsub_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed) { Z3_TRY; RESET_ERROR_CODE(); if (is_signed) { Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); Z3_inc_ref(c, zero); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); Z3_inc_ref(c, minus_t2); Z3_ast x = Z3_mk_bvslt(c, zero, t2); Z3_inc_ref(c, x); Z3_ast y = Z3_mk_bvadd_no_underflow(c, t1, minus_t2); Z3_inc_ref(c, y); Z3_ast result = Z3_mk_implies(c, x, y); Z3_dec_ref(c, zero); Z3_dec_ref(c, minus_t2); Z3_dec_ref(c, x); Z3_dec_ref(c, y); return result; } else { return Z3_mk_bvule(c, t2, t1); } Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_bvmul_no_overflow(Z3_context c, Z3_ast n1, Z3_ast n2, Z3_bool is_signed) { LOG_Z3_mk_bvmul_no_overflow(c, n1, n2, is_signed); RESET_ERROR_CODE(); if (is_signed) { MK_BINARY_BODY(Z3_mk_bvmul_no_overflow, mk_c(c)->get_bv_fid(), OP_BSMUL_NO_OVFL, SKIP); } else { MK_BINARY_BODY(Z3_mk_bvmul_no_overflow, mk_c(c)->get_bv_fid(), OP_BUMUL_NO_OVFL, SKIP); } } // only for signed machine integers Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast n1, Z3_ast n2) { LOG_Z3_mk_bvmul_no_underflow(c, n1, n2); MK_BINARY_BODY(Z3_mk_bvmul_no_underflow, mk_c(c)->get_bv_fid(), OP_BSMUL_NO_UDFL, SKIP); } // only for signed machine integers Z3_ast Z3_API Z3_mk_bvneg_no_overflow(Z3_context c, Z3_ast t) { Z3_TRY; RESET_ERROR_CODE(); Z3_ast min = Z3_mk_bvsmin(c, Z3_get_sort(c, t)); if (Z3_get_error_code(c) != Z3_OK) return 0; Z3_ast eq = Z3_mk_eq(c, t, min); if (Z3_get_error_code(c) != Z3_OK) return 0; return Z3_mk_not(c, eq); Z3_CATCH_RETURN(0); } // only for signed machine integers Z3_ast Z3_API Z3_mk_bvsdiv_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); Z3_sort s = Z3_get_sort(c, t1); Z3_ast min = Z3_mk_bvmsb(c, s); Z3_inc_ref(c, min); Z3_ast x = Z3_mk_eq(c, t1, min); Z3_inc_ref(c, x); Z3_ast y = Z3_mk_int(c, -1, s); Z3_inc_ref(c, y); Z3_ast z = Z3_mk_eq(c, t2, y); Z3_inc_ref(c, z); Z3_ast args[2] = { x, z }; Z3_ast u = Z3_mk_and(c, 2, args); Z3_inc_ref(c, u); Z3_ast result = Z3_mk_not(c, u); Z3_dec_ref(c, min); Z3_dec_ref(c, x); Z3_dec_ref(c, y); Z3_dec_ref(c, z); Z3_dec_ref(c, u); return result; Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_bvsub(Z3_context c, Z3_ast n1, Z3_ast n2) { Z3_TRY; LOG_Z3_mk_bvsub(c, n1, n2); RESET_ERROR_CODE(); MK_BINARY_BODY(Z3_mk_bvsub, mk_c(c)->get_bv_fid(), OP_BSUB, SKIP); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_bvneg(Z3_context c, Z3_ast n) { Z3_TRY; LOG_Z3_mk_bvneg(c, n); RESET_ERROR_CODE(); MK_UNARY_BODY(Z3_mk_bvneg, mk_c(c)->get_bv_fid(), OP_BNEG, SKIP); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_bv_sort_size(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_bv_sort_size(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, 0); if (to_sort(t)->get_family_id() == mk_c(c)->get_bv_fid() && to_sort(t)->get_decl_kind() == BV_SORT) { return to_sort(t)->get_parameter(0).get_int(); } SET_ERROR_CODE(Z3_INVALID_ARG); return 0; Z3_CATCH_RETURN(0); } }; z3-z3-4.4.1/src/api/api_config_params.cpp000066400000000000000000000055331260446376700201400ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_config_params.cpp Abstract: Configuration parameters Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include"z3.h" #include"api_context.h" #include"pp.h" #include"api_log_macros.h" #include"api_util.h" #include"cmd_context.h" #include"symbol.h" #include"gparams.h" #include"env_params.h" #include"context_params.h" extern "C" { void Z3_API Z3_global_param_set(Z3_string param_id, Z3_string param_value) { memory::initialize(UINT_MAX); LOG_Z3_global_param_set(param_id, param_value); try { gparams::set(param_id, param_value); env_params::updt_params(); } catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. warning_msg(ex.msg()); } } void Z3_API Z3_global_param_reset_all(void) { memory::initialize(UINT_MAX); LOG_Z3_global_param_reset_all(); gparams::reset(); env_params::updt_params(); } std::string g_Z3_global_param_get_buffer; Z3_bool_opt Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value) { memory::initialize(UINT_MAX); LOG_Z3_global_param_get(param_id, param_value); *param_value = 0; try { g_Z3_global_param_get_buffer = gparams::get_value(param_id); *param_value = g_Z3_global_param_get_buffer.c_str(); return Z3_TRUE; } catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. warning_msg(ex.msg()); return Z3_FALSE; } } Z3_config Z3_API Z3_mk_config(void) { memory::initialize(UINT_MAX); LOG_Z3_mk_config(); Z3_config r = reinterpret_cast(alloc(context_params)); RETURN_Z3(r); } void Z3_API Z3_del_config(Z3_config c) { LOG_Z3_del_config(c); dealloc((reinterpret_cast(c))); } void Z3_API Z3_set_param_value(Z3_config c, char const * param_id, char const * param_value) { LOG_Z3_set_param_value(c, param_id, param_value); try { context_params * p = reinterpret_cast(c); p->set(param_id, param_value); } catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. warning_msg(ex.msg()); } } void Z3_API Z3_update_param_value(Z3_context c, Z3_string param_id, Z3_string param_value) { Z3_TRY; LOG_Z3_update_param_value(c, param_id, param_value); RESET_ERROR_CODE(); mk_c(c)->params().set(param_id, param_value); Z3_CATCH; } }; z3-z3-4.4.1/src/api/api_context.cpp000066400000000000000000000420771260446376700170200ustar00rootroot00000000000000/*++ 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(Z3_context c, 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(Z3_context c) { return mk_c(c)->m(); } z3-z3-4.4.1/src/api/api_context.h000066400000000000000000000226671260446376700164700ustar00rootroot00000000000000/*++ 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; } unsigned get_rlimit() const { return m_params.m_rlimit; } 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)); } #define CHECK_IS_EXPR(_p_, _ret_) { if (!is_expr(_p_)) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } 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-z3-4.4.1/src/api/api_datalog.cpp000066400000000000000000000512641260446376700167450ustar00rootroot00000000000000/*++ 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()); unsigned rlimit = to_fixedpoint(d)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); 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( Z3_context c,Z3_fixedpoint d, 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-z3-4.4.1/src/api/api_datalog.h000066400000000000000000000022421260446376700164020ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_datatype.cpp000066400000000000000000000600251260446376700171400ustar00rootroot00000000000000/*++ 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( Z3_context c, Z3_func_decl f, Z3_ast t, 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-z3-4.4.1/src/api/api_fpa.cpp000066400000000000000000000677111260446376700161040ustar00rootroot00000000000000/*++ 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(Z3_context c, 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(Z3_context c, Z3_ast t, __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(Z3_context c, 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(Z3_context c, Z3_ast t, __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_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-z3-4.4.1/src/api/api_goal.cpp000066400000000000000000000120431260446376700162440ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_goal.h000066400000000000000000000012031260446376700157050ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_goal.h Abstract: API for creating goals Author: Leonardo de Moura (leonardo) 2012-03-06. Revision History: --*/ #ifndef API_GOAL_H_ #define API_GOAL_H_ #include"api_util.h" #include"goal.h" struct Z3_goal_ref : public api::object { goal_ref m_goal; virtual ~Z3_goal_ref() {} }; inline Z3_goal_ref * to_goal(Z3_goal g) { return reinterpret_cast(g); } inline Z3_goal of_goal(Z3_goal_ref * g) { return reinterpret_cast(g); } inline goal_ref to_goal_ref(Z3_goal g) { return g == 0 ? goal_ref() : to_goal(g)->m_goal; } #endif z3-z3-4.4.1/src/api/api_interp.cpp000066400000000000000000000623111260446376700166260ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: api_interp.cpp Abstract: API for interpolation Author: Ken McMillan Revision History: --*/ #include #include #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" #include"iz3interp.h" #include"iz3profiling.h" #include"iz3hash.h" #include"iz3pp.h" #include"iz3checker.h" #include"scoped_proof.h" using namespace stl_ext; // WARNING: don't make a hash_map with this if the range type // has a destructor: you'll get an address dependency!!! namespace stl_ext { template <> class hash < Z3_ast > { public: size_t operator()(const Z3_ast p) const { return (size_t)p; } }; } typedef interpolation_options_struct *Z3_interpolation_options; extern "C" { Z3_context Z3_mk_interpolation_context(Z3_config cfg){ if (!cfg) cfg = Z3_mk_config(); Z3_set_param_value(cfg, "PROOF", "true"); Z3_set_param_value(cfg, "MODEL", "true"); // Z3_set_param_value(cfg, "PRE_SIMPLIFIER","false"); // Z3_set_param_value(cfg, "SIMPLIFY_CLAUSES","false"); Z3_context ctx = Z3_mk_context(cfg); return ctx; } void Z3_interpolate_proof(Z3_context ctx, Z3_ast proof, int num, Z3_ast *cnsts, unsigned *parents, Z3_params options, Z3_ast *interps, int num_theory, Z3_ast *theory) { if (num > 1){ // if we have interpolants to compute ptr_vector pre_cnsts_vec(num); // get constraints in a vector for (int i = 0; i < num; i++){ ast *a = to_ast(cnsts[i]); pre_cnsts_vec[i] = a; } ::vector pre_parents_vec; // get parents in a vector if (parents){ pre_parents_vec.resize(num); for (int i = 0; i < num; i++) pre_parents_vec[i] = parents[i]; } ptr_vector theory_vec; // get background theory in a vector if (theory){ theory_vec.resize(num_theory); for (int i = 0; i < num_theory; i++) theory_vec[i] = to_ast(theory[i]); } ptr_vector interpolants(num - 1); // make space for result ast_manager &_m = mk_c(ctx)->m(); iz3interpolate(_m, to_ast(proof), pre_cnsts_vec, pre_parents_vec, interpolants, theory_vec, 0); // ignore params for now FIXME // copy result back for (unsigned i = 0; i < interpolants.size(); i++){ mk_c(ctx)->save_ast_trail(interpolants[i]); interps[i] = of_ast(interpolants[i]); _m.dec_ref(interpolants[i]); } } } static std::ostringstream itp_err; int Z3_check_interpolant(Z3_context ctx, unsigned num, Z3_ast *cnsts, unsigned *parents, Z3_ast *itp, Z3_string *error, unsigned num_theory, Z3_ast *theory){ ast_manager &_m = mk_c(ctx)->m(); itp_err.clear(); // need a solver -- make one here, but how? params_ref p = params_ref::get_empty(); //FIXME scoped_ptr sf(mk_smt_solver_factory()); scoped_ptr sp((*(sf))(_m, p, false, true, false, symbol("AUFLIA"))); ptr_vector cnsts_vec(num); // get constraints in a vector for (unsigned i = 0; i < num; i++){ ast *a = to_ast(cnsts[i]); cnsts_vec[i] = a; } ptr_vector itp_vec(num); // get interpolants in a vector for (unsigned i = 0; i < num - 1; i++){ ast *a = to_ast(itp[i]); itp_vec[i] = a; } ::vector parents_vec; // get parents in a vector if (parents){ parents_vec.resize(num); for (unsigned i = 0; i < num; i++) parents_vec[i] = parents[i]; } ptr_vector theory_vec; // get background theory in a vector if (theory){ theory_vec.resize(num_theory); for (unsigned i = 0; i < num_theory; i++) theory_vec[i] = to_ast(theory[i]); } bool res = iz3check(_m, sp.get(), itp_err, cnsts_vec, parents_vec, itp_vec, theory_vec); *error = res ? 0 : itp_err.str().c_str(); return res; } static std::string Z3_profile_string; Z3_string Z3_interpolation_profile(Z3_context ctx){ std::ostringstream f; profiling::print(f); Z3_profile_string = f.str(); return Z3_profile_string.c_str(); } Z3_interpolation_options Z3_mk_interpolation_options(){ return (Z3_interpolation_options) new interpolation_options_struct; } void Z3_del_interpolation_options(Z3_interpolation_options opts){ delete opts; } void Z3_set_interpolation_option(Z3_interpolation_options opts, Z3_string name, Z3_string value){ opts->map[name] = value; } Z3_ast_vector Z3_API Z3_get_interpolant(Z3_context c, Z3_ast pf, Z3_ast pat, Z3_params p){ Z3_TRY; LOG_Z3_get_interpolant(c, pf, pat, p); RESET_ERROR_CODE(); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); mk_c(c)->save_object(v); ast *_pf = to_ast(pf); ast *_pat = to_ast(pat); ptr_vector interp; ptr_vector cnsts; // to throw away ast_manager &_m = mk_c(c)->m(); iz3interpolate(_m, _pf, cnsts, _pat, interp, (interpolation_options_struct *)0 // ignore params for now ); // copy result back for (unsigned i = 0; i < interp.size(); i++){ v->m_ast_vector.push_back(interp[i]); _m.dec_ref(interp[i]); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(0); } Z3_lbool Z3_API Z3_compute_interpolant(Z3_context c, Z3_ast pat, Z3_params p, Z3_ast_vector *out_interp, Z3_model *model){ Z3_TRY; LOG_Z3_compute_interpolant(c, pat, p, out_interp, model); RESET_ERROR_CODE(); // params_ref &_p = to_params(p)->m_params; params_ref _p; _p.set_bool("proof", true); // this is currently useless scoped_proof_mode spm(mk_c(c)->m(), PGM_FINE); scoped_ptr sf = mk_smt_solver_factory(); scoped_ptr m_solver((*sf)(mk_c(c)->m(), _p, true, true, true, ::symbol::null)); m_solver.get()->updt_params(_p); // why do we have to do this? ast *_pat = to_ast(pat); ptr_vector interp; ptr_vector cnsts; // to throw away ast_manager &_m = mk_c(c)->m(); model_ref m; lbool _status = iz3interpolate(_m, *(m_solver.get()), _pat, cnsts, interp, m, 0 // ignore params for now ); for (unsigned i = 0; i < cnsts.size(); i++) _m.dec_ref(cnsts[i]); Z3_lbool status = of_lbool(_status); Z3_ast_vector_ref *v = 0; *model = 0; if (_status == l_false){ // copy result back v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); mk_c(c)->save_object(v); for (unsigned i = 0; i < interp.size(); i++){ v->m_ast_vector.push_back(interp[i]); _m.dec_ref(interp[i]); } } else { model_ref mr; m_solver.get()->get_model(mr); Z3_model_ref *tmp_val = alloc(Z3_model_ref); tmp_val->m_model = mr.get(); mk_c(c)->save_object(tmp_val); *model = of_model(tmp_val); } *out_interp = of_ast_vector(v); return status; Z3_CATCH_RETURN(Z3_L_UNDEF); } }; static void tokenize(const std::string &str, std::vector &tokens){ for (unsigned i = 0; i < str.size();){ if (str[i] == ' '){ i++; continue; } unsigned beg = i; while (i < str.size() && str[i] != ' ')i++; if (i > beg) tokens.push_back(str.substr(beg, i - beg)); } } static void get_file_params(const char *filename, hash_map ¶ms){ std::ifstream f(filename); if (f){ std::string first_line; std::getline(f, first_line); // std::cout << "first line: '" << first_line << "'" << std::endl; if (first_line.size() >= 2 && first_line[0] == ';' && first_line[1] == '!'){ std::vector tokens; tokenize(first_line.substr(2, first_line.size() - 2), tokens); for (unsigned i = 0; i < tokens.size(); i++){ std::string &tok = tokens[i]; size_t eqpos = tok.find('='); if (eqpos != std::string::npos){ std::string left = tok.substr(0, eqpos); std::string right = tok.substr(eqpos + 1, tok.size() - eqpos - 1); params[left] = right; } } } f.close(); } } extern "C" { #if 0 static void iZ3_write_seq(Z3_context ctx, int num, Z3_ast *cnsts, const char *filename, int num_theory, Z3_ast *theory){ int num_fmlas = num+num_theory; std::vector fmlas(num_fmlas); if(num_theory) std::copy(theory,theory+num_theory,fmlas.begin()); for(int i = 0; i < num_theory; i++) fmlas[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),fmlas[i]); std::copy(cnsts,cnsts+num,fmlas.begin()+num_theory); Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"none","AUFLIA","unknown","",num_fmlas-1,&fmlas[0],fmlas[num_fmlas-1]); std::ofstream f(filename); if(num_theory) f << ";! THEORY=" << num_theory << "\n"; f << smt; f.close(); } void Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, unsigned *parents, const char *filename, int num_theory, Z3_ast *theory){ if(!parents){ iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); return; } std::vector tcnsts(num); hash_map syms; for(int j = 0; j < num - 1; j++){ std::ostringstream oss; oss << "$P" << j; std::string name = oss.str(); Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); syms[j] = symbol; tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); } tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); for(int j = num-2; j >= 0; j--){ int parent = parents[j]; // assert(parent >= 0 && parent < num); tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); } iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); } #else static Z3_ast and_vec(Z3_context ctx, svector &c){ return (c.size() > 1) ? Z3_mk_and(ctx, c.size(), &c[0]) : c[0]; } static Z3_ast parents_vector_to_tree(Z3_context ctx, int num, Z3_ast *cnsts, unsigned *parents){ Z3_ast res; if (!parents){ res = Z3_mk_interpolant(ctx, cnsts[0]); for (int i = 1; i < num - 1; i++){ Z3_ast bar[2] = { res, cnsts[i] }; res = Z3_mk_interpolant(ctx, Z3_mk_and(ctx, 2, bar)); } if (num > 1){ Z3_ast bar[2] = { res, cnsts[num - 1] }; res = Z3_mk_and(ctx, 2, bar); } } else { std::vector > chs(num); for (int i = 0; i < num - 1; i++){ svector &c = chs[i]; c.push_back(cnsts[i]); Z3_ast foo = Z3_mk_interpolant(ctx, and_vec(ctx, c)); chs[parents[i]].push_back(foo); } { svector &c = chs[num - 1]; c.push_back(cnsts[num - 1]); res = and_vec(ctx, c); } } Z3_inc_ref(ctx, res); return res; } void Z3_write_interpolation_problem(Z3_context ctx, unsigned num, Z3_ast *cnsts, unsigned *parents, const char *filename, unsigned num_theory, Z3_ast *theory){ std::ofstream f(filename); if (num > 0){ #if 0 // Suggested shorthand: ptr_vector cnsts_vec; cnsts_vec.append(num, to_exprs(cnsts)); cnsts_vec.append(num_theory, to_exprs(theory)); #endif ptr_vector cnsts_vec(num); // get constraints in a vector for (unsigned i = 0; i < num; i++){ expr *a = to_expr(cnsts[i]); cnsts_vec[i] = a; } for (unsigned i = 0; i < num_theory; i++){ expr *a = to_expr(theory[i]); cnsts_vec.push_back(a); } Z3_ast tree = parents_vector_to_tree(ctx, num, cnsts, parents); iz3pp(mk_c(ctx)->m(), cnsts_vec, to_expr(tree), f); Z3_dec_ref(ctx, tree); } f.close(); #if 0 if(!parents){ iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); return; } std::vector tcnsts(num); hash_map syms; for(int j = 0; j < num - 1; j++){ std::ostringstream oss; oss << "$P" << j; std::string name = oss.str(); Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); syms[j] = symbol; tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); } tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); for(int j = num-2; j >= 0; j--){ int parent = parents[j]; // assert(parent >= 0 && parent < num); tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); } iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); #endif } #endif static std::vector read_cnsts; static std::vector read_parents; static std::ostringstream read_error; static std::string read_msg; static std::vector read_theory; static bool iZ3_parse(Z3_context ctx, const char *filename, const char **error, svector &assertions){ read_error.clear(); try { std::string foo(filename); if (foo.size() >= 5 && foo.substr(foo.size() - 5) == ".smt2"){ Z3_ast assrts = Z3_parse_smtlib2_file(ctx, filename, 0, 0, 0, 0, 0, 0); Z3_app app = Z3_to_app(ctx, assrts); int nconjs = Z3_get_app_num_args(ctx, app); assertions.resize(nconjs); for (int k = 0; k < nconjs; k++) assertions[k] = Z3_get_app_arg(ctx, app, k); } else { Z3_parse_smtlib_file(ctx, filename, 0, 0, 0, 0, 0, 0); int numa = Z3_get_smtlib_num_assumptions(ctx); int numf = Z3_get_smtlib_num_formulas(ctx); int num = numa + numf; assertions.resize(num); for (int j = 0; j < num; j++){ if (j < numa) assertions[j] = Z3_get_smtlib_assumption(ctx, j); else assertions[j] = Z3_get_smtlib_formula(ctx, j - numa); } } } catch (...) { read_error << "SMTLIB parse error: " << Z3_get_smtlib_error(ctx); read_msg = read_error.str(); *error = read_msg.c_str(); return false; } Z3_set_error_handler(ctx, 0); return true; } int Z3_read_interpolation_problem(Z3_context ctx, unsigned *_num, Z3_ast *cnsts[], unsigned *parents[], const char *filename, Z3_string_ptr error, unsigned *ret_num_theory, Z3_ast *theory[]){ hash_map file_params; get_file_params(filename, file_params); unsigned num_theory = 0; if (file_params.find("THEORY") != file_params.end()) num_theory = atoi(file_params["THEORY"].c_str()); svector assertions; if (!iZ3_parse(ctx, filename, error, assertions)) return false; if (num_theory > assertions.size()) num_theory = assertions.size(); unsigned num = assertions.size() - num_theory; read_cnsts.resize(num); read_parents.resize(num); read_theory.resize(num_theory); for (unsigned j = 0; j < num_theory; j++) read_theory[j] = assertions[j]; for (unsigned j = 0; j < num; j++) read_cnsts[j] = assertions[j + num_theory]; if (ret_num_theory) *ret_num_theory = num_theory; if (theory) *theory = &read_theory[0]; if (!parents){ *_num = num; *cnsts = &read_cnsts[0]; return true; } for (unsigned j = 0; j < num; j++) read_parents[j] = SHRT_MAX; hash_map pred_map; for (unsigned j = 0; j < num; j++){ Z3_ast lhs = 0, rhs = read_cnsts[j]; if (Z3_get_decl_kind(ctx, Z3_get_app_decl(ctx, Z3_to_app(ctx, rhs))) == Z3_OP_IMPLIES){ Z3_app app1 = Z3_to_app(ctx, rhs); Z3_ast lhs1 = Z3_get_app_arg(ctx, app1, 0); Z3_ast rhs1 = Z3_get_app_arg(ctx, app1, 1); if (Z3_get_decl_kind(ctx, Z3_get_app_decl(ctx, Z3_to_app(ctx, lhs1))) == Z3_OP_AND){ Z3_app app2 = Z3_to_app(ctx, lhs1); int nconjs = Z3_get_app_num_args(ctx, app2); for (int k = nconjs - 1; k >= 0; --k) rhs1 = Z3_mk_implies(ctx, Z3_get_app_arg(ctx, app2, k), rhs1); rhs = rhs1; } } while (1){ Z3_app app = Z3_to_app(ctx, rhs); Z3_func_decl func = Z3_get_app_decl(ctx, app); Z3_decl_kind dk = Z3_get_decl_kind(ctx, func); if (dk == Z3_OP_IMPLIES){ if (lhs){ Z3_ast child = lhs; if (pred_map.find(child) == pred_map.end()){ read_error << "formula " << j + 1 << ": unknown: " << Z3_ast_to_string(ctx, child); goto fail; } int child_num = pred_map[child]; if (read_parents[child_num] != SHRT_MAX){ read_error << "formula " << j + 1 << ": multiple reference: " << Z3_ast_to_string(ctx, child); goto fail; } read_parents[child_num] = j; } lhs = Z3_get_app_arg(ctx, app, 0); rhs = Z3_get_app_arg(ctx, app, 1); } else { if (!lhs){ read_error << "formula " << j + 1 << ": should be (implies {children} fmla parent)"; goto fail; } read_cnsts[j] = lhs; Z3_ast name = rhs; if (pred_map.find(name) != pred_map.end()){ read_error << "formula " << j + 1 << ": duplicate symbol"; goto fail; } pred_map[name] = j; break; } } } for (unsigned j = 0; j < num - 1; j++) if (read_parents[j] == SHRT_MAX){ read_error << "formula " << j + 1 << ": unreferenced"; goto fail; } *_num = num; *cnsts = &read_cnsts[0]; *parents = &read_parents[0]; return true; fail: read_msg = read_error.str(); *error = read_msg.c_str(); return false; } } #if 0 /** Constant reprepresenting a root of a formula tree for tree interpolation */ #define IZ3_ROOT SHRT_MAX /** This function uses Z3 to determine satisfiability of a set of constraints. If UNSAT, an interpolant is returned, based on the refutation generated by Z3. If SAT, a model is returned. If "parents" is non-null, computes a tree interpolant. The tree is defined by the array "parents". This array maps each formula in the tree to its parent, where formulas are indicated by their integer index in "cnsts". The parent of formula n must have index greater than n. The last formula is the root of the tree. Its parent entry should be the constant IZ3_ROOT. If "parents" is null, computes a sequence interpolant. \param ctx The Z3 context. Must be generated by iz3_mk_context \param num The number of constraints in the sequence \param cnsts Array of constraints (AST's in context ctx) \param parents The parents vector defining the tree structure \param options Interpolation options (may be NULL) \param interps Array to return interpolants (size at least num-1, may be NULL) \param model Returns a Z3 model if constraints SAT (may be NULL) \param labels Returns relevant labels if SAT (may be NULL) \param incremental VERY IMPORTANT: All the Z3 formulas in cnsts must be in Z3 context ctx. The model and interpolants returned are also in this context. The return code is as in Z3_check_assumptions, that is, Z3_L_FALSE = constraints UNSAT (interpolants returned) Z3_L_TRUE = constraints SAT (model returned) Z3_L_UNDEF = Z3 produced no result, or interpolation not possible Currently, this function supports integer and boolean variables, as well as arrays over these types, with linear arithmetic, uninterpreted functions and quantifiers over integers (that is AUFLIA). Interpolants are produced in AULIA. However, some uses of array operations may cause quantifiers to appear in the interpolants even when there are no quantifiers in the input formulas. Although quantifiers may appear in the input formulas, Z3 may give up in this case, returning Z3_L_UNDEF. If "incremental" is true, cnsts must contain exactly the set of formulas that are currently asserted in the context. If false, there must be no formulas currently asserted in the context. Setting "incremental" to true makes it posisble to incrementally add and remove constraints from the context until the context becomes UNSAT, at which point an interpolant is computed. Caution must be used, however. Before popping the context, if you wish to keep the interolant formulas, you *must* preserve them by using Z3_persist_ast. Also, if you want to simplify the interpolant formulas using Z3_simplify, you must first pop all of the assertions in the context (or use a different context). Otherwise, the formulas will be simplified *relative* to these constraints, which is almost certainly not what you want. Current limitations on tree interpolants. In a tree interpolation problem, each constant (0-ary function symbol) must occur only along one path from root to leaf. Function symbols (of arity > 0) are considered to have global scope (i.e., may appear in any interpolant formula). def_API('Z3_interpolate', BOOL, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(PARAMS), _out_array(1, AST), _out(MODEL), _out(LITERALS), _in(UINT), _in(UINT), _in_array(9, AST))) */ Z3_lbool Z3_API Z3_interpolate(Z3_context ctx, unsigned num, Z3_ast *cnsts, unsigned *parents, Z3_params options, Z3_ast *interps, Z3_model *model, Z3_literals *labels, unsigned incremental, unsigned num_theory, Z3_ast *theory); #endif z3-z3-4.4.1/src/api/api_log.cpp000066400000000000000000000020611260446376700161020ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_log.cpp Abstract: API for creating logs Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include #include"z3.h" #include"api_log_macros.h" #include"util.h" std::ostream * g_z3_log = 0; bool g_z3_log_enabled = false; extern "C" { Z3_bool Z3_API Z3_open_log(Z3_string filename) { if (g_z3_log != 0) Z3_close_log(); g_z3_log = alloc(std::ofstream, filename); g_z3_log_enabled = true; if (g_z3_log->bad() || g_z3_log->fail()) { dealloc(g_z3_log); g_z3_log = 0; return Z3_FALSE; } return Z3_TRUE; } void Z3_API Z3_append_log(Z3_string str) { if (g_z3_log == 0) return; _Z3_append_log(static_cast(str)); } void Z3_API Z3_close_log(void) { if (g_z3_log != 0) { dealloc(g_z3_log); g_z3_log_enabled = false; g_z3_log = 0; } } } z3-z3-4.4.1/src/api/api_model.cpp000066400000000000000000000540051260446376700164260ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_quant.cpp Abstract: API for models 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_model.h" #include"api_ast_vector.h" #include"array_decl_plugin.h" #include"model.h" #include"model_v2_pp.h" #include"model_smt2_pp.h" #include"model_params.hpp" #include"model_evaluator_params.hpp" extern "C" { void Z3_API Z3_model_inc_ref(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_inc_ref(c, m); RESET_ERROR_CODE(); if (m) { to_model(m)->inc_ref(); } Z3_CATCH; } void Z3_API Z3_model_dec_ref(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_dec_ref(c, m); RESET_ERROR_CODE(); if (m) { to_model(m)->dec_ref(); } Z3_CATCH; } Z3_ast Z3_API Z3_model_get_const_interp(Z3_context c, Z3_model m, Z3_func_decl a) { Z3_TRY; LOG_Z3_model_get_const_interp(c, m, a); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); expr * r = to_model_ref(m)->get_const_interp(to_func_decl(a)); if (!r) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_model_has_interp(Z3_context c, Z3_model m, Z3_func_decl a) { Z3_TRY; LOG_Z3_model_has_interp(c, m, a); CHECK_NON_NULL(m, 0); if (to_model_ref(m)->has_interpretation(to_func_decl(a))) { return Z3_TRUE; } else { return Z3_FALSE; } Z3_CATCH_RETURN(Z3_FALSE); } Z3_func_interp Z3_API Z3_model_get_func_interp(Z3_context c, Z3_model m, Z3_func_decl f) { Z3_TRY; LOG_Z3_model_get_func_interp(c, m, f); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); func_interp * _fi = to_model_ref(m)->get_func_interp(to_func_decl(f)); if (!_fi) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } Z3_func_interp_ref * fi = alloc(Z3_func_interp_ref, to_model_ref(m)); fi->m_func_interp = _fi; mk_c(c)->save_object(fi); RETURN_Z3(of_func_interp(fi)); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_model_get_num_consts(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_get_num_consts(c, m); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); return to_model_ref(m)->get_num_constants(); Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_model_get_const_decl(Z3_context c, Z3_model m, unsigned i) { Z3_TRY; LOG_Z3_model_get_const_decl(c, m, i); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); model * _m = to_model_ref(m); if (i < _m->get_num_constants()) { RETURN_Z3(of_func_decl(_m->get_constant(i))); } else { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_model_get_num_funcs(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_get_num_funcs(c, m); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); return to_model_ref(m)->get_num_functions(); Z3_CATCH_RETURN(0); } Z3_func_decl get_model_func_decl_core(Z3_context c, Z3_model m, unsigned i) { CHECK_NON_NULL(m, 0); model * _m = to_model_ref(m); if (i >= _m->get_num_functions()) { SET_ERROR_CODE(Z3_IOB); return 0; } return of_func_decl(_m->get_function(i)); } Z3_func_decl Z3_API Z3_model_get_func_decl(Z3_context c, Z3_model m, unsigned i) { Z3_TRY; LOG_Z3_get_model_func_decl(c, m, i); RESET_ERROR_CODE(); Z3_func_decl r = get_model_func_decl_core(c, m, i); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, Z3_bool model_completion, Z3_ast * v) { Z3_TRY; LOG_Z3_model_eval(c, m, t, model_completion, v); if (v) *v = 0; RESET_ERROR_CODE(); CHECK_NON_NULL(m, Z3_FALSE); CHECK_IS_EXPR(t, Z3_FALSE); model * _m = to_model_ref(m); expr_ref result(mk_c(c)->m()); _m->eval(to_expr(t), result, model_completion == Z3_TRUE); mk_c(c)->save_ast_trail(result.get()); *v = of_ast(result.get()); RETURN_Z3_model_eval Z3_TRUE; Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_model_get_num_sorts(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_get_num_sorts(c, m); RESET_ERROR_CODE(); return to_model_ref(m)->get_num_uninterpreted_sorts(); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_model_get_sort(Z3_context c, Z3_model m, unsigned i) { Z3_TRY; LOG_Z3_model_get_sort(c, m, i); RESET_ERROR_CODE(); if (i >= to_model_ref(m)->get_num_uninterpreted_sorts()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } sort * s = to_model_ref(m)->get_uninterpreted_sort(i); RETURN_Z3(of_sort(s)); Z3_CATCH_RETURN(0); } Z3_ast_vector Z3_API Z3_model_get_sort_universe(Z3_context c, Z3_model m, Z3_sort s) { Z3_TRY; LOG_Z3_model_get_sort_universe(c, m, s); RESET_ERROR_CODE(); if (!to_model_ref(m)->has_uninterpreted_sort(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } ptr_vector const & universe = to_model_ref(m)->get_universe(to_sort(s)); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); mk_c(c)->save_object(v); unsigned sz = universe.size(); for (unsigned i = 0; i < sz; i++) { v->m_ast_vector.push_back(universe[i]); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_is_as_array(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_is_as_array(c, a); RESET_ERROR_CODE(); return is_expr(to_ast(a)) && is_app_of(to_expr(a), mk_c(c)->get_array_fid(), OP_AS_ARRAY); Z3_CATCH_RETURN(Z3_FALSE); } Z3_func_decl Z3_API Z3_get_as_array_func_decl(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_as_array_func_decl(c, a); RESET_ERROR_CODE(); if (is_expr(to_ast(a)) && is_app_of(to_expr(a), mk_c(c)->get_array_fid(), OP_AS_ARRAY)) { RETURN_Z3(of_func_decl(to_func_decl(to_app(a)->get_decl()->get_parameter(0).get_ast()))); } else { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } Z3_CATCH_RETURN(0); } void Z3_API Z3_func_interp_inc_ref(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_inc_ref(c, f); RESET_ERROR_CODE(); if (f) { to_func_interp(f)->inc_ref(); } Z3_CATCH; } void Z3_API Z3_func_interp_dec_ref(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_dec_ref(c, f); RESET_ERROR_CODE(); if (f) { to_func_interp(f)->dec_ref(); } Z3_CATCH; } unsigned Z3_API Z3_func_interp_get_num_entries(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_get_num_entries(c, f); RESET_ERROR_CODE(); CHECK_NON_NULL(f, 0); return to_func_interp_ref(f)->num_entries(); Z3_CATCH_RETURN(0); } Z3_func_entry Z3_API Z3_func_interp_get_entry(Z3_context c, Z3_func_interp f, unsigned i) { Z3_TRY; LOG_Z3_func_interp_get_entry(c, f, i); RESET_ERROR_CODE(); CHECK_NON_NULL(f, 0); if (i >= to_func_interp_ref(f)->num_entries()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } Z3_func_entry_ref * e = alloc(Z3_func_entry_ref, to_func_interp(f)->m_model.get()); e->m_func_interp = to_func_interp_ref(f); e->m_func_entry = to_func_interp_ref(f)->get_entry(i); mk_c(c)->save_object(e); RETURN_Z3(of_func_entry(e)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_func_interp_get_else(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_get_else(c, f); RESET_ERROR_CODE(); CHECK_NON_NULL(f, 0); expr * e = to_func_interp_ref(f)->get_else(); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_func_interp_get_arity(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_get_arity(c, f); RESET_ERROR_CODE(); CHECK_NON_NULL(f, 0); return to_func_interp_ref(f)->get_arity(); Z3_CATCH_RETURN(0); } void Z3_API Z3_func_entry_inc_ref(Z3_context c, Z3_func_entry e) { Z3_TRY; LOG_Z3_func_entry_inc_ref(c, e); RESET_ERROR_CODE(); if (e) { to_func_entry(e)->inc_ref(); } Z3_CATCH; } void Z3_API Z3_func_entry_dec_ref(Z3_context c, Z3_func_entry e) { Z3_TRY; LOG_Z3_func_entry_dec_ref(c, e); RESET_ERROR_CODE(); if (e) { to_func_entry(e)->dec_ref(); } Z3_CATCH; } Z3_ast Z3_API Z3_func_entry_get_value(Z3_context c, Z3_func_entry e) { Z3_TRY; LOG_Z3_func_entry_get_value(c, e); RESET_ERROR_CODE(); expr * v = to_func_entry_ref(e)->get_result(); mk_c(c)->save_ast_trail(v); RETURN_Z3(of_expr(v)); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_func_entry_get_num_args(Z3_context c, Z3_func_entry e) { Z3_TRY; LOG_Z3_func_entry_get_num_args(c, e); RESET_ERROR_CODE(); return to_func_entry(e)->m_func_interp->get_arity(); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_func_entry_get_arg(Z3_context c, Z3_func_entry e, unsigned i) { Z3_TRY; LOG_Z3_func_entry_get_arg(c, e, i); RESET_ERROR_CODE(); if (i >= to_func_entry(e)->m_func_interp->get_arity()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } expr * r = to_func_entry(e)->m_func_entry->get_arg(i); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } // ---------------------------- // // DEPRECATED API // // ---------------------------- void Z3_API Z3_del_model(Z3_context c, Z3_model m) { Z3_model_dec_ref(c, m); } unsigned Z3_API Z3_get_model_num_constants(Z3_context c, Z3_model m) { return Z3_model_get_num_consts(c, m); } Z3_func_decl Z3_API Z3_get_model_constant(Z3_context c, Z3_model m, unsigned i) { return Z3_model_get_const_decl(c, m, i); } unsigned Z3_API Z3_get_model_num_funcs(Z3_context c, Z3_model m) { return Z3_model_get_num_funcs(c, m); } Z3_func_decl Z3_API Z3_get_model_func_decl(Z3_context c, Z3_model m, unsigned i) { return Z3_model_get_func_decl(c, m, i); } Z3_ast Z3_API Z3_get_model_func_else(Z3_context c, Z3_model m, unsigned i) { Z3_TRY; LOG_Z3_get_model_func_else(c, m, i); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); Z3_func_decl d = get_model_func_decl_core(c, m, i); if (d) { model * _m = to_model_ref(m); func_interp * g = _m->get_func_interp(to_func_decl(d)); if (g) { expr * e = g->get_else(); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_ast(e)); } SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } RETURN_Z3(0); Z3_CATCH_RETURN(0); } unsigned get_model_func_num_entries_core(Z3_context c, Z3_model m, unsigned i) { RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); Z3_func_decl d = get_model_func_decl_core(c, m, i); if (d) { model * _m = to_model_ref(m); func_interp * g = _m->get_func_interp(to_func_decl(d)); if (g) { return g->num_entries(); } SET_ERROR_CODE(Z3_IOB); return 0; } return 0; } unsigned Z3_API Z3_get_model_func_num_entries(Z3_context c, Z3_model m, unsigned i) { Z3_TRY; LOG_Z3_get_model_func_num_entries(c, m, i); return get_model_func_num_entries_core(c, m, i); Z3_CATCH_RETURN(0); } unsigned get_model_func_entry_num_args_core(Z3_context c, Z3_model m, unsigned i, unsigned j) { RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); if (j >= get_model_func_num_entries_core(c, m, i)) { SET_ERROR_CODE(Z3_IOB); return 0; } Z3_func_decl d = get_model_func_decl_core(c, m, i); if (d) { model * _m = to_model_ref(m); func_interp * g = _m->get_func_interp(to_func_decl(d)); return g->get_arity(); } return 0; } unsigned Z3_API Z3_get_model_func_entry_num_args(Z3_context c, Z3_model m, unsigned i, unsigned j) { Z3_TRY; LOG_Z3_get_model_func_entry_num_args(c, m, i, j); return get_model_func_entry_num_args_core(c, m, i, j); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_model_func_entry_arg(Z3_context c, Z3_model m, unsigned i, unsigned j, unsigned k) { Z3_TRY; LOG_Z3_get_model_func_entry_arg(c, m, i, j, k); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); if (j >= get_model_func_num_entries_core(c, m, i) || k >= get_model_func_entry_num_args_core(c, m, i, j)) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } Z3_func_decl d = get_model_func_decl_core(c, m, i); if (d) { model * _m = to_model_ref(m); func_interp * g = _m->get_func_interp(to_func_decl(d)); if (g && j < g->num_entries()) { func_entry const * e = g->get_entry(j); if (k < g->get_arity()) { expr * a = e->get_arg(k); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); } } SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } RETURN_Z3(0); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_model_func_entry_value(Z3_context c, Z3_model m, unsigned i, unsigned j) { Z3_TRY; LOG_Z3_get_model_func_entry_value(c, m, i, j); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); if (j >= get_model_func_num_entries_core(c, m, i)) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } Z3_func_decl d = get_model_func_decl_core(c, m, i); if (d) { model * _m = to_model_ref(m); func_interp * g = _m->get_func_interp(to_func_decl(d)); if (g && j < g->num_entries()) { func_entry const* e = g->get_entry(j); expr* a = e->get_result(); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); } SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } RETURN_Z3(0); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_eval(Z3_context c, Z3_model m, Z3_ast t, Z3_ast * v) { model_evaluator_params p; return Z3_model_eval(c, m, t, p.completion(), v); } Z3_bool Z3_API Z3_eval_func_decl(Z3_context c, Z3_model m, Z3_func_decl decl, Z3_ast* v) { Z3_TRY; LOG_Z3_eval_func_decl(c, m, decl, v); RESET_ERROR_CODE(); CHECK_NON_NULL(m, Z3_FALSE); ast_manager & mgr = mk_c(c)->m(); model * _m = to_model_ref(m); expr_ref result(mgr); if( _m->eval(to_func_decl(decl), result)) { mk_c(c)->save_ast_trail(result.get()); *v = of_ast(result.get()); RETURN_Z3_eval_func_decl Z3_TRUE; } else { return Z3_FALSE; } Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_is_array_value(Z3_context c, Z3_model _m, Z3_ast _v, unsigned* size) { Z3_TRY; LOG_Z3_is_array_value(c, _m, _v, size); RESET_ERROR_CODE(); CHECK_NON_NULL(_v, Z3_FALSE); CHECK_NON_NULL(_m, Z3_FALSE); model * m = to_model_ref(_m); expr * v = to_expr(_v); ast_manager& mgr = mk_c(c)->m(); family_id afid = mk_c(c)->get_array_fid(); unsigned sz = 0; array_util pl(mgr); if (pl.is_as_array(v)) { func_decl* f = pl.get_as_array_func_decl(to_app(v)); func_interp* g = m->get_func_interp(f); sz = g->num_entries(); if (sz > 0 && g->get_arity() != 1) { return Z3_FALSE; } } else { while (pl.is_store(v)) { if (to_app(v)->get_num_args() != 3) { return Z3_FALSE; } v = to_app(v)->get_arg(0); ++sz; } if (!is_app_of(v, afid, OP_CONST_ARRAY)) { return Z3_FALSE; } } if (size) { *size = sz; } return Z3_TRUE; Z3_CATCH_RETURN(Z3_FALSE); } void Z3_API Z3_get_array_value(Z3_context c, Z3_model _m, Z3_ast _v, unsigned num_entries, Z3_ast indices[], Z3_ast values[], Z3_ast* else_value) { Z3_TRY; LOG_Z3_get_array_value(c, _m, _v, num_entries, indices, values, else_value); RESET_ERROR_CODE(); CHECK_NON_NULL(_m, ); model * m = to_model_ref(_m); expr* v = to_expr(_v); family_id afid = mk_c(c)->get_array_fid(); ast_manager& mgr = mk_c(c)->m(); array_util pl(mgr); // // note: _v is already reference counted. // saving the trail for the returned values // is redundant. // unsigned sz = 0; if (pl.is_as_array(v)) { func_decl* f = pl.get_as_array_func_decl(to_app(v)); func_interp* g = m->get_func_interp(f); sz = g->num_entries(); if (g->get_arity() != 1) { SET_ERROR_CODE(Z3_INVALID_ARG); return; } for (unsigned i = 0; i < sz && i < num_entries; ++i) { indices[i] = of_ast(g->get_entry(i)->get_arg(0)); values[i] = of_ast(g->get_entry(i)->get_result()); } if (else_value) { *else_value = of_ast(g->get_else()); } } else { while (sz <= num_entries && is_app_of(v, afid, OP_STORE)) { app* a = to_app(v); if (a->get_num_args() != 3) { SET_ERROR_CODE(Z3_INVALID_ARG); return; } expr* idx = a->get_arg(1); expr* val = a->get_arg(2); indices[sz] = of_ast(idx); values[sz] = of_ast(val); v = to_app(v)->get_arg(0); ++sz; } if (is_app_of(v, afid, OP_CONST_ARRAY)) { if (else_value) { *else_value = of_ast(to_app(v)->get_arg(0)); } } else { SET_ERROR_CODE(Z3_INVALID_ARG); return; } } RETURN_Z3_get_array_value; Z3_CATCH; } Z3_bool Z3_API Z3_eval_decl(Z3_context c, Z3_model m, Z3_func_decl d, unsigned num_args, Z3_ast const args[], Z3_ast* v) { Z3_TRY; LOG_Z3_eval_decl(c, m, d, num_args, args, v); RESET_ERROR_CODE(); CHECK_NON_NULL(m, Z3_FALSE); ast_manager & mgr = mk_c(c)->m(); model * _m = to_model_ref(m); app_ref app(mgr); app = mgr.mk_app(to_func_decl(d), num_args, to_exprs(args)); expr_ref result(mgr); _m->eval(app.get(), result); mk_c(c)->save_ast_trail(result.get()); *v = of_ast(result.get()); RETURN_Z3_eval_decl Z3_TRUE; Z3_CATCH_RETURN(Z3_FALSE); } Z3_API char const * Z3_model_to_string(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_to_string(c, m); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); std::ostringstream buffer; std::string result; if (mk_c(c)->get_print_mode() == Z3_PRINT_SMTLIB2_COMPLIANT) { model_smt2_pp(buffer, mk_c(c)->m(), *(to_model_ref(m)), 0); // Hack for removing the trailing '\n' result = buffer.str(); if (result.size() != 0) result.resize(result.size()-1); } else { model_params p; model_v2_pp(buffer, *(to_model_ref(m)), p.partial()); result = buffer.str(); } return mk_c(c)->mk_external_string(result); Z3_CATCH_RETURN(0); } }; z3-z3-4.4.1/src/api/api_model.h000066400000000000000000000036221260446376700160720ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_model.h Abstract: API for models Author: Leonardo de Moura (leonardo) 2012-03-08. Revision History: --*/ #ifndef API_MODEL_H_ #define API_MODEL_H_ #include"api_util.h" #include"model.h" struct Z3_model_ref : public api::object { model_ref m_model; Z3_model_ref() {} virtual ~Z3_model_ref() {} }; inline Z3_model_ref * to_model(Z3_model s) { return reinterpret_cast(s); } inline Z3_model of_model(Z3_model_ref * s) { return reinterpret_cast(s); } inline model * to_model_ref(Z3_model s) { return to_model(s)->m_model.get(); } struct Z3_func_interp_ref : public api::object { model_ref m_model; // must have it to prevent reference to m_func_interp to be killed. func_interp * m_func_interp; Z3_func_interp_ref(model * m):m_model(m), m_func_interp(0) {} virtual ~Z3_func_interp_ref() {} }; inline Z3_func_interp_ref * to_func_interp(Z3_func_interp s) { return reinterpret_cast(s); } inline Z3_func_interp of_func_interp(Z3_func_interp_ref * s) { return reinterpret_cast(s); } inline func_interp * to_func_interp_ref(Z3_func_interp s) { return to_func_interp(s)->m_func_interp; } struct Z3_func_entry_ref : public api::object { model_ref m_model; // must have it to prevent reference to m_func_entry to be killed. func_interp * m_func_interp; func_entry const * m_func_entry; Z3_func_entry_ref(model * m):m_model(m), m_func_interp(0), m_func_entry(0) {} virtual ~Z3_func_entry_ref() {} }; inline Z3_func_entry_ref * to_func_entry(Z3_func_entry s) { return reinterpret_cast(s); } inline Z3_func_entry of_func_entry(Z3_func_entry_ref * s) { return reinterpret_cast(s); } inline func_entry const * to_func_entry_ref(Z3_func_entry s) { return to_func_entry(s)->m_func_entry; } #endif z3-z3-4.4.1/src/api/api_numeral.cpp000066400000000000000000000310601260446376700167650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_opt.cpp000066400000000000000000000172171260446376700161340ustar00rootroot00000000000000/*++ 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()); unsigned rlimit = mk_c(c)->get_rlimit(); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); 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_string Z3_API Z3_optimize_get_reason_unknown(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)->reason_unknown()); Z3_CATCH_RETURN(""); } 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-z3-4.4.1/src/api/api_params.cpp000066400000000000000000000135461260446376700166160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_parsers.cpp000066400000000000000000000257711260446376700170150ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_pb.cpp000066400000000000000000000027121260446376700157250ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_polynomial.cpp000066400000000000000000000042271260446376700175120ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_polynomial.h000066400000000000000000000012121260446376700171460ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_quant.cpp000066400000000000000000000443111260446376700164550ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_rcf.cpp000066400000000000000000000210761260446376700161020ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_solver.cpp000066400000000000000000000314421260446376700166400ustar00rootroot00000000000000/*++ 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()); unsigned rlimit = to_solver(s)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); 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); scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); 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); get_memory_statistics(st->m_stats); get_rlimit_statistics(mk_c(c)->m().limit(), st->m_stats); 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-z3-4.4.1/src/api/api_solver.h000066400000000000000000000016021260446376700163000ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_solver.h Abstract: New solver API Author: Leonardo de Moura (leonardo) 2012-03-07. Revision History: --*/ #ifndef API_SOLVER_H_ #define API_SOLVER_H_ #include"api_util.h" #include"solver.h" struct Z3_solver_ref : public api::object { scoped_ptr m_solver_factory; ref m_solver; params_ref m_params; symbol m_logic; Z3_solver_ref(solver_factory * f):m_solver_factory(f), m_solver(0), m_logic(symbol::null) {} virtual ~Z3_solver_ref() {} }; inline Z3_solver_ref * to_solver(Z3_solver s) { return reinterpret_cast(s); } inline Z3_solver of_solver(Z3_solver_ref * s) { return reinterpret_cast(s); } inline solver * to_solver_ref(Z3_solver s) { return to_solver(s)->m_solver.get(); } #endif z3-z3-4.4.1/src/api/api_solver_old.cpp000066400000000000000000000264471260446376700175070ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_solver_old.cpp Abstract: OLD API for using solvers. This has been deprecated 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_model.h" #include"cancel_eh.h" extern "C" { void Z3_API Z3_push(Z3_context c) { Z3_TRY; LOG_Z3_push(c); RESET_ERROR_CODE(); CHECK_SEARCHING(c); mk_c(c)->push(); Z3_CATCH; } void Z3_API Z3_pop(Z3_context c, unsigned num_scopes) { Z3_TRY; LOG_Z3_pop(c, num_scopes); RESET_ERROR_CODE(); CHECK_SEARCHING(c); if (num_scopes > mk_c(c)->get_num_scopes()) { SET_ERROR_CODE(Z3_IOB); return; } if (num_scopes > 0) { mk_c(c)->pop(num_scopes); } Z3_CATCH; } unsigned Z3_API Z3_get_num_scopes(Z3_context c) { Z3_TRY; LOG_Z3_get_num_scopes(c); RESET_ERROR_CODE(); return mk_c(c)->get_num_scopes(); Z3_CATCH_RETURN(0); } void Z3_API Z3_assert_cnstr(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_assert_cnstr(c, a); RESET_ERROR_CODE(); CHECK_FORMULA(a,); mk_c(c)->assert_cnstr(to_expr(a)); Z3_CATCH; } Z3_lbool Z3_API Z3_check_and_get_model(Z3_context c, Z3_model * m) { Z3_TRY; LOG_Z3_check_and_get_model(c, m); RESET_ERROR_CODE(); CHECK_SEARCHING(c); cancel_eh eh(mk_c(c)->get_smt_kernel()); api::context::set_interruptable si(*(mk_c(c)), eh); flet _model(mk_c(c)->fparams().m_model, true); lbool result; try { model_ref _m; result = mk_c(c)->check(_m); if (m) { if (_m) { Z3_model_ref * m_ref = alloc(Z3_model_ref); m_ref->m_model = _m; // Must bump reference counter for backward compatibility reasons. // Don't need to invoke save_object, since the counter was bumped m_ref->inc_ref(); *m = of_model(m_ref); } else { *m = 0; } } } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); RETURN_Z3_check_and_get_model static_cast(l_undef); } RETURN_Z3_check_and_get_model static_cast(result); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_lbool Z3_API Z3_check(Z3_context c) { Z3_TRY; // This is just syntax sugar... RESET_ERROR_CODE(); CHECK_SEARCHING(c); Z3_lbool r = Z3_check_and_get_model(c, 0); return r; Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_lbool Z3_API Z3_check_assumptions(Z3_context c, unsigned num_assumptions, Z3_ast const assumptions[], Z3_model * m, Z3_ast* proof, unsigned* core_size, Z3_ast core[]) { Z3_TRY; LOG_Z3_check_assumptions(c, num_assumptions, assumptions, m, proof, core_size, core); RESET_ERROR_CODE(); CHECK_SEARCHING(c); expr * const* _assumptions = to_exprs(assumptions); flet _model(mk_c(c)->fparams().m_model, true); cancel_eh eh(mk_c(c)->get_smt_kernel()); api::context::set_interruptable si(*(mk_c(c)), eh); lbool result; result = mk_c(c)->get_smt_kernel().check(num_assumptions, _assumptions); if (result != l_false && m) { model_ref _m; mk_c(c)->get_smt_kernel().get_model(_m); if (_m) { Z3_model_ref * m_ref = alloc(Z3_model_ref); m_ref->m_model = _m; // Must bump reference counter for backward compatibility reasons. // Don't need to invoke save_object, since the counter was bumped m_ref->inc_ref(); *m = of_model(m_ref); } else { *m = 0; } } if (result == l_false && core_size) { *core_size = mk_c(c)->get_smt_kernel().get_unsat_core_size(); if (*core_size > num_assumptions) { SET_ERROR_CODE(Z3_INVALID_ARG); } for (unsigned i = 0; i < *core_size; ++i) { core[i] = of_ast(mk_c(c)->get_smt_kernel().get_unsat_core_expr(i)); } } else if (core_size) { *core_size = 0; } if (result == l_false && proof) { *proof = of_ast(mk_c(c)->get_smt_kernel().get_proof()); } else if (proof) { *proof = 0; // breaks abstraction. } RETURN_Z3_check_assumptions static_cast(result); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_search_failure Z3_API Z3_get_search_failure(Z3_context c) { Z3_TRY; LOG_Z3_get_search_failure(c); RESET_ERROR_CODE(); CHECK_SEARCHING(c); smt::failure f = mk_c(c)->get_smt_kernel().last_failure(); return api::mk_Z3_search_failure(f); Z3_CATCH_RETURN(Z3_UNKNOWN); } class labeled_literal { expr_ref m_literal; symbol m_label; bool m_enabled; public: labeled_literal(ast_manager& m, expr* l, symbol const& n) : m_literal(l,m), m_label(n), m_enabled(true) {} labeled_literal(ast_manager& m, expr* l) : m_literal(l,m), m_label(), m_enabled(true) {} bool is_enabled() const { return m_enabled; } void disable() { m_enabled = false; } symbol const& get_label() const { return m_label; } expr* get_literal() { return m_literal.get(); } }; typedef vector labels; Z3_literals Z3_API Z3_get_relevant_labels(Z3_context c) { Z3_TRY; LOG_Z3_get_relevant_labels(c); RESET_ERROR_CODE(); buffer labl_syms; ast_manager& m = mk_c(c)->m(); expr_ref_vector lits(m); mk_c(c)->get_smt_kernel().get_relevant_labels(0, labl_syms); mk_c(c)->get_smt_kernel().get_relevant_labeled_literals(mk_c(c)->fparams().m_at_labels_cex, lits); labels* lbls = alloc(labels); SASSERT(labl_syms.size() == lits.size()); for (unsigned i = 0; i < lits.size(); ++i) { lbls->push_back(labeled_literal(m,lits[i].get(), labl_syms[i])); } RETURN_Z3(reinterpret_cast(lbls)); Z3_CATCH_RETURN(0); } Z3_literals Z3_API Z3_get_relevant_literals(Z3_context c) { Z3_TRY; LOG_Z3_get_relevant_literals(c); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); expr_ref_vector lits(m); mk_c(c)->get_smt_kernel().get_relevant_literals(lits); labels* lbls = alloc(labels); for (unsigned i = 0; i < lits.size(); ++i) { lbls->push_back(labeled_literal(m,lits[i].get())); } RETURN_Z3(reinterpret_cast(lbls)); Z3_CATCH_RETURN(0); } Z3_literals Z3_API Z3_get_guessed_literals(Z3_context c) { Z3_TRY; LOG_Z3_get_guessed_literals(c); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); expr_ref_vector lits(m); mk_c(c)->get_smt_kernel().get_guessed_literals(lits); labels* lbls = alloc(labels); for (unsigned i = 0; i < lits.size(); ++i) { lbls->push_back(labeled_literal(m,lits[i].get())); } RETURN_Z3(reinterpret_cast(lbls)); Z3_CATCH_RETURN(0); } void Z3_API Z3_del_literals(Z3_context c, Z3_literals lbls) { Z3_TRY; LOG_Z3_del_literals(c, lbls); RESET_ERROR_CODE(); dealloc(reinterpret_cast(lbls)); Z3_CATCH; } unsigned Z3_API Z3_get_num_literals(Z3_context c,Z3_literals lbls) { Z3_TRY; LOG_Z3_get_num_literals(c, lbls); RESET_ERROR_CODE(); return reinterpret_cast(lbls)->size(); Z3_CATCH_RETURN(0); } Z3_symbol Z3_API Z3_get_label_symbol(Z3_context c,Z3_literals lbls, unsigned idx) { Z3_TRY; LOG_Z3_get_label_symbol(c, lbls, idx); RESET_ERROR_CODE(); return of_symbol((*reinterpret_cast(lbls))[idx].get_label()); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_literal(Z3_context c,Z3_literals lbls, unsigned idx) { Z3_TRY; LOG_Z3_get_literal(c, lbls, idx); RESET_ERROR_CODE(); expr* e = (*reinterpret_cast(lbls))[idx].get_literal(); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_ast(e)); Z3_CATCH_RETURN(0); } void Z3_API Z3_disable_literal(Z3_context c, Z3_literals lbls, unsigned idx) { Z3_TRY; LOG_Z3_disable_literal(c, lbls, idx); RESET_ERROR_CODE(); (*reinterpret_cast(lbls))[idx].disable(); Z3_CATCH; } void Z3_API Z3_block_literals(Z3_context c, Z3_literals lbls) { Z3_TRY; LOG_Z3_block_literals(c, lbls); RESET_ERROR_CODE(); labels* _lbls = reinterpret_cast(lbls); ast_manager& m = mk_c(c)->m(); expr_ref_vector lits(m); for (unsigned i = 0; i < _lbls->size(); ++i) { if ((*_lbls)[i].is_enabled()) { lits.push_back(m.mk_not((*_lbls)[i].get_literal())); } } expr_ref clause(m); clause = m.mk_or(lits.size(), lits.c_ptr()); mk_c(c)->save_ast_trail(clause.get()); mk_c(c)->assert_cnstr(clause.get()); Z3_CATCH; } Z3_API char const * Z3_context_to_string(Z3_context c) { Z3_TRY; LOG_Z3_context_to_string(c); RESET_ERROR_CODE(); std::ostringstream buffer; mk_c(c)->get_smt_kernel().display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_context_assignment(Z3_context c) { Z3_TRY; LOG_Z3_get_context_assignment(c); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); expr_ref result(m); expr_ref_vector assignment(m); mk_c(c)->get_smt_kernel().get_assignments(assignment); result = mk_c(c)->mk_and(assignment.size(), assignment.c_ptr()); RETURN_Z3(of_ast(result.get())); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_statistics_to_string(Z3_context c) { Z3_TRY; LOG_Z3_statistics_to_string(c); RESET_ERROR_CODE(); std::ostringstream buffer; mk_c(c)->get_smt_kernel().display_statistics(buffer); memory::display_max_usage(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(0); } void Z3_API Z3_soft_check_cancel(Z3_context c) { Z3_TRY; LOG_Z3_soft_check_cancel(c); RESET_ERROR_CODE(); mk_c(c)->interrupt(); Z3_CATCH; } }; void Z3_display_statistics(Z3_context c, std::ostream& s) { mk_c(c)->get_smt_kernel().display_statistics(s); } void Z3_display_istatistics(Z3_context c, std::ostream& s) { mk_c(c)->get_smt_kernel().display_istatistics(s); } z3-z3-4.4.1/src/api/api_stats.cpp000066400000000000000000000067551260446376700164750ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_stats.cpp Abstract: API for browsing statistics 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_stats.h" extern "C" { Z3_string Z3_API Z3_stats_to_string(Z3_context c, Z3_stats s) { Z3_TRY; LOG_Z3_stats_to_string(c, s); RESET_ERROR_CODE(); std::ostringstream buffer; to_stats_ref(s).display_smt2(buffer); std::string result = buffer.str(); // Hack for removing the trailing '\n' result = buffer.str(); SASSERT(result.size() > 0); result.resize(result.size()-1); return mk_c(c)->mk_external_string(result); Z3_CATCH_RETURN(""); } void Z3_API Z3_stats_inc_ref(Z3_context c, Z3_stats s) { Z3_TRY; LOG_Z3_stats_inc_ref(c, s); RESET_ERROR_CODE(); to_stats(s)->inc_ref(); Z3_CATCH; } void Z3_API Z3_stats_dec_ref(Z3_context c, Z3_stats s) { Z3_TRY; LOG_Z3_stats_dec_ref(c, s); RESET_ERROR_CODE(); to_stats(s)->dec_ref(); Z3_CATCH; } unsigned Z3_API Z3_stats_size(Z3_context c, Z3_stats s) { Z3_TRY; LOG_Z3_stats_size(c, s); RESET_ERROR_CODE(); return to_stats_ref(s).size(); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_stats_get_key(Z3_context c, Z3_stats s, unsigned idx) { Z3_TRY; LOG_Z3_stats_get_key(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { SET_ERROR_CODE(Z3_IOB); return ""; } return to_stats_ref(s).get_key(idx); Z3_CATCH_RETURN(""); } Z3_bool Z3_API Z3_stats_is_uint(Z3_context c, Z3_stats s, unsigned idx) { Z3_TRY; LOG_Z3_stats_is_uint(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { SET_ERROR_CODE(Z3_IOB); return Z3_FALSE; } return to_stats_ref(s).is_uint(idx); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_stats_is_double(Z3_context c, Z3_stats s, unsigned idx) { Z3_TRY; LOG_Z3_stats_is_double(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { SET_ERROR_CODE(Z3_IOB); return Z3_FALSE; } return !to_stats_ref(s).is_uint(idx); Z3_CATCH_RETURN(Z3_FALSE); } unsigned Z3_API Z3_stats_get_uint_value(Z3_context c, Z3_stats s, unsigned idx) { Z3_TRY; LOG_Z3_stats_get_uint_value(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { SET_ERROR_CODE(Z3_IOB); return 0; } if (!to_stats_ref(s).is_uint(idx)) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } return to_stats_ref(s).get_uint_value(idx); Z3_CATCH_RETURN(0); } double Z3_API Z3_stats_get_double_value(Z3_context c, Z3_stats s, unsigned idx) { Z3_TRY; LOG_Z3_stats_get_double_value(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { SET_ERROR_CODE(Z3_IOB); return 0.0; } if (to_stats_ref(s).is_uint(idx)) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0.0; } return to_stats_ref(s).get_double_value(idx); Z3_CATCH_RETURN(0.0); } }; z3-z3-4.4.1/src/api/api_stats.h000066400000000000000000000012121260446376700161210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_tactic.cpp000066400000000000000000000402501260446376700165720ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_tactic.cpp Abstract: API for creating tactics and probes 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_tactic.h" #include"api_model.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"scoped_timer.h" Z3_apply_result_ref::Z3_apply_result_ref(ast_manager & m):m_core(m) { } extern "C" { #define RETURN_TACTIC(_t_) { \ Z3_tactic_ref * _ref_ = alloc(Z3_tactic_ref); \ _ref_->m_tactic = _t_; \ mk_c(c)->save_object(_ref_); \ Z3_tactic _result_ = of_tactic(_ref_); \ RETURN_Z3(_result_); \ } #define RETURN_PROBE(_t_) { \ Z3_probe_ref * _ref_ = alloc(Z3_probe_ref); \ _ref_->m_probe = _t_; \ mk_c(c)->save_object(_ref_); \ Z3_probe _result_ = of_probe(_ref_); \ RETURN_Z3(_result_); \ } Z3_tactic Z3_API Z3_mk_tactic(Z3_context c, Z3_string name) { Z3_TRY; LOG_Z3_mk_tactic(c, name); RESET_ERROR_CODE(); tactic_cmd * t = mk_c(c)->find_tactic_cmd(symbol(name)); if (t == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } tactic * new_t = t->mk(mk_c(c)->m()); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } void Z3_API Z3_tactic_inc_ref(Z3_context c, Z3_tactic t) { Z3_TRY; LOG_Z3_tactic_inc_ref(c, t); RESET_ERROR_CODE(); to_tactic(t)->inc_ref(); Z3_CATCH; } void Z3_API Z3_tactic_dec_ref(Z3_context c, Z3_tactic t) { Z3_TRY; LOG_Z3_tactic_dec_ref(c, t); RESET_ERROR_CODE(); to_tactic(t)->dec_ref(); Z3_CATCH; } Z3_probe Z3_API Z3_mk_probe(Z3_context c, Z3_string name) { Z3_TRY; LOG_Z3_mk_probe(c, name); RESET_ERROR_CODE(); probe_info * p = mk_c(c)->find_probe(symbol(name)); if (p == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } probe * new_p = p->get(); RETURN_PROBE(new_p); Z3_CATCH_RETURN(0); } void Z3_API Z3_probe_inc_ref(Z3_context c, Z3_probe p) { Z3_TRY; LOG_Z3_probe_inc_ref(c, p); RESET_ERROR_CODE(); to_probe(p)->inc_ref(); Z3_CATCH; } void Z3_API Z3_probe_dec_ref(Z3_context c, Z3_probe p) { Z3_TRY; LOG_Z3_probe_dec_ref(c, p); RESET_ERROR_CODE(); to_probe(p)->dec_ref(); Z3_CATCH; } Z3_tactic Z3_API Z3_tactic_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2) { Z3_TRY; LOG_Z3_tactic_and_then(c, t1, t2); RESET_ERROR_CODE(); tactic * new_t = and_then(to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_or_else(Z3_context c, Z3_tactic t1, Z3_tactic t2) { Z3_TRY; LOG_Z3_tactic_or_else(c, t1, t2); RESET_ERROR_CODE(); tactic * new_t = or_else(to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_par_or(Z3_context c, unsigned num, Z3_tactic const ts[]) { Z3_TRY; LOG_Z3_tactic_par_or(c, num, ts); RESET_ERROR_CODE(); ptr_buffer _ts; for (unsigned i = 0; i < num; i++) { _ts.push_back(to_tactic_ref(ts[i])); } tactic * new_t = par(num, _ts.c_ptr()); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_par_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2) { Z3_TRY; LOG_Z3_tactic_par_and_then(c, t1, t2); RESET_ERROR_CODE(); tactic * new_t = par_and_then(to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_try_for(Z3_context c, Z3_tactic t, unsigned ms) { Z3_TRY; LOG_Z3_tactic_try_for(c, t, ms); RESET_ERROR_CODE(); tactic * new_t = try_for(to_tactic_ref(t), ms); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_when(Z3_context c, Z3_probe p, Z3_tactic t) { Z3_TRY; LOG_Z3_tactic_when(c, p, t); RESET_ERROR_CODE(); tactic * new_t = when(to_probe_ref(p), to_tactic_ref(t)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_cond(Z3_context c, Z3_probe p, Z3_tactic t1, Z3_tactic t2) { Z3_TRY; LOG_Z3_tactic_cond(c, p, t1, t2); RESET_ERROR_CODE(); tactic * new_t = cond(to_probe_ref(p), to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_repeat(Z3_context c, Z3_tactic t, unsigned max) { Z3_TRY; LOG_Z3_tactic_repeat(c, t, max); RESET_ERROR_CODE(); tactic * new_t = repeat(to_tactic_ref(t), max); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_skip(Z3_context c) { Z3_TRY; LOG_Z3_tactic_skip(c); RESET_ERROR_CODE(); tactic * new_t = mk_skip_tactic(); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_fail(Z3_context c) { Z3_TRY; LOG_Z3_tactic_fail(c); RESET_ERROR_CODE(); tactic * new_t = mk_fail_tactic(); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_fail_if(Z3_context c, Z3_probe p) { Z3_TRY; LOG_Z3_tactic_fail_if(c, p); RESET_ERROR_CODE(); tactic * new_t = fail_if(to_probe_ref(p)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_fail_if_not_decided(Z3_context c) { Z3_TRY; LOG_Z3_tactic_fail_if_not_decided(c); RESET_ERROR_CODE(); tactic * new_t = mk_fail_if_undecided_tactic(); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_tactic Z3_API Z3_tactic_using_params(Z3_context c, Z3_tactic t, Z3_params p) { Z3_TRY; LOG_Z3_tactic_using_params(c, t, p); RESET_ERROR_CODE(); param_descrs r; to_tactic_ref(t)->collect_param_descrs(r); to_param_ref(p).validate(r); tactic * new_t = using_params(to_tactic_ref(t), to_param_ref(p)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(0); } Z3_probe Z3_API Z3_probe_const(Z3_context c, double val) { Z3_TRY; LOG_Z3_probe_const(c, val); RESET_ERROR_CODE(); probe * new_p = mk_const_probe(val); RETURN_PROBE(new_p); Z3_CATCH_RETURN(0); } Z3_probe Z3_API Z3_probe_lt(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_lt(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_lt(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(0); } Z3_probe Z3_API Z3_probe_gt(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_gt(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_gt(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(0); } Z3_probe Z3_API Z3_probe_le(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_le(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_le(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(0); } Z3_probe Z3_API Z3_probe_ge(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_ge(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_ge(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(0); } Z3_probe Z3_API Z3_probe_eq(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_eq(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_eq(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(0); } Z3_probe Z3_API Z3_probe_and(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_and(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_and(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(0); } Z3_probe Z3_API Z3_probe_or(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_or(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_or(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(0); } Z3_probe Z3_API Z3_probe_not(Z3_context c, Z3_probe p) { Z3_TRY; LOG_Z3_probe_not(c, p); RESET_ERROR_CODE(); probe * new_p = mk_not(to_probe_ref(p)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_num_tactics(Z3_context c) { Z3_TRY; LOG_Z3_get_num_tactics(c); RESET_ERROR_CODE(); return mk_c(c)->num_tactics(); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_get_tactic_name(Z3_context c, unsigned idx) { Z3_TRY; LOG_Z3_get_tactic_name(c, idx); RESET_ERROR_CODE(); if (idx >= mk_c(c)->num_tactics()) { SET_ERROR_CODE(Z3_IOB); return ""; } return mk_c(c)->get_tactic(idx)->get_name().bare_str(); Z3_CATCH_RETURN(""); } unsigned Z3_API Z3_get_num_probes(Z3_context c) { Z3_TRY; LOG_Z3_get_num_probes(c); RESET_ERROR_CODE(); return mk_c(c)->num_probes(); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_get_probe_name(Z3_context c, unsigned idx) { Z3_TRY; LOG_Z3_get_probe_name(c, idx); RESET_ERROR_CODE(); if (idx >= mk_c(c)->num_probes()) { SET_ERROR_CODE(Z3_IOB); return ""; } return mk_c(c)->get_probe(idx)->get_name().bare_str(); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_tactic_get_help(Z3_context c, Z3_tactic t) { Z3_TRY; LOG_Z3_tactic_get_help(c, t); RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; to_tactic_ref(t)->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_tactic_get_param_descrs(Z3_context c, Z3_tactic t) { Z3_TRY; LOG_Z3_tactic_get_param_descrs(c, t); RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); mk_c(c)->save_object(d); to_tactic_ref(t)->collect_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_tactic_get_descr(Z3_context c, Z3_string name) { Z3_TRY; LOG_Z3_tactic_get_descr(c, name); RESET_ERROR_CODE(); tactic_cmd * t = mk_c(c)->find_tactic_cmd(symbol(name)); if (t == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } return t->get_descr(); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_probe_get_descr(Z3_context c, Z3_string name) { Z3_TRY; LOG_Z3_probe_get_descr(c, name); RESET_ERROR_CODE(); probe_info * p = mk_c(c)->find_probe(symbol(name)); if (p == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } return p->get_descr(); Z3_CATCH_RETURN(""); } static Z3_apply_result _tactic_apply(Z3_context c, Z3_tactic t, Z3_goal g, params_ref p) { goal_ref new_goal; new_goal = alloc(goal, *to_goal_ref(g)); Z3_apply_result_ref * ref = alloc(Z3_apply_result_ref, mk_c(c)->m()); mk_c(c)->save_object(ref); unsigned timeout = p.get_uint("timeout", UINT_MAX); bool use_ctrl_c = p.get_bool("ctrl_c", false); cancel_eh eh(*to_tactic_ref(t)); to_tactic_ref(t)->updt_params(p); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); try { exec(*to_tactic_ref(t), new_goal, ref->m_subgoals, ref->m_mc, ref->m_pc, ref->m_core); return of_apply_result(ref); } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); return 0; } } } double Z3_API Z3_probe_apply(Z3_context c, Z3_probe p, Z3_goal g) { Z3_TRY; LOG_Z3_probe_apply(c, p, g); RESET_ERROR_CODE(); return to_probe_ref(p)->operator()(*to_goal_ref(g)).get_value(); Z3_CATCH_RETURN(0); } Z3_apply_result Z3_API Z3_tactic_apply(Z3_context c, Z3_tactic t, Z3_goal g) { Z3_TRY; LOG_Z3_tactic_apply(c, t, g); RESET_ERROR_CODE(); params_ref p; Z3_apply_result r = _tactic_apply(c, t, g, p); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_apply_result Z3_API Z3_tactic_apply_ex(Z3_context c, Z3_tactic t, Z3_goal g, Z3_params p) { Z3_TRY; LOG_Z3_tactic_apply_ex(c, t, g, p); RESET_ERROR_CODE(); param_descrs pd; to_tactic_ref(t)->collect_param_descrs(pd); to_param_ref(p).validate(pd); Z3_apply_result r = _tactic_apply(c, t, g, to_param_ref(p)); RETURN_Z3(r); Z3_CATCH_RETURN(0); } void Z3_API Z3_apply_result_inc_ref(Z3_context c, Z3_apply_result r) { Z3_TRY; LOG_Z3_apply_result_inc_ref(c, r); RESET_ERROR_CODE(); to_apply_result(r)->inc_ref(); Z3_CATCH; } void Z3_API Z3_apply_result_dec_ref(Z3_context c, Z3_apply_result r) { Z3_TRY; LOG_Z3_apply_result_dec_ref(c, r); RESET_ERROR_CODE(); to_apply_result(r)->dec_ref(); Z3_CATCH; } Z3_string Z3_API Z3_apply_result_to_string(Z3_context c, Z3_apply_result r) { Z3_TRY; LOG_Z3_apply_result_to_string(c, r); RESET_ERROR_CODE(); std::ostringstream buffer; buffer << "(goals\n"; unsigned sz = to_apply_result(r)->m_subgoals.size(); for (unsigned i = 0; i < sz; i++) { to_apply_result(r)->m_subgoals[i]->display(buffer); } buffer << ")"; return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } unsigned Z3_API Z3_apply_result_get_num_subgoals(Z3_context c, Z3_apply_result r) { Z3_TRY; LOG_Z3_apply_result_get_num_subgoals(c, r); RESET_ERROR_CODE(); return to_apply_result(r)->m_subgoals.size(); Z3_CATCH_RETURN(0); } Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i) { Z3_TRY; LOG_Z3_apply_result_get_subgoal(c, r, i); RESET_ERROR_CODE(); if (i > to_apply_result(r)->m_subgoals.size()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } Z3_goal_ref * g = alloc(Z3_goal_ref); g->m_goal = to_apply_result(r)->m_subgoals[i]; mk_c(c)->save_object(g); Z3_goal result = of_goal(g); RETURN_Z3(result); Z3_CATCH_RETURN(0); } Z3_model Z3_API Z3_apply_result_convert_model(Z3_context c, Z3_apply_result r, unsigned i, Z3_model m) { Z3_TRY; LOG_Z3_apply_result_convert_model(c, r, i, m); RESET_ERROR_CODE(); if (i > to_apply_result(r)->m_subgoals.size()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } model_ref new_m = to_model_ref(m)->copy(); if (to_apply_result(r)->m_mc) to_apply_result(r)->m_mc->operator()(new_m, i); Z3_model_ref * m_ref = alloc(Z3_model_ref); m_ref->m_model = new_m; mk_c(c)->save_object(m_ref); RETURN_Z3(of_model(m_ref)); Z3_CATCH_RETURN(0); } }; z3-z3-4.4.1/src/api/api_tactic.h000066400000000000000000000030411260446376700162340ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/api_user_theory.cpp000066400000000000000000000245351260446376700177030ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_user_theory.cpp Abstract: API for external theories 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"user_smt_theory.h" smt::user_theory * mk_t(Z3_theory t) { return reinterpret_cast(t); } extern "C" { /////////////////////////////// // Theory plugin // No support for logging Z3_theory Z3_mk_theory(Z3_context c, Z3_string th_name, void * ext_data) { Z3_TRY; RESET_ERROR_CODE(); if (mk_c(c)->get_smt_kernel().get_scope_level() > 0) { SET_ERROR_CODE(Z3_INVALID_USAGE); return 0; } return reinterpret_cast(mk_user_theory(mk_c(c)->get_smt_kernel(), c, ext_data, th_name)); Z3_CATCH_RETURN(0); } void * Z3_theory_get_ext_data(Z3_theory t) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); void * r = mk_t(t)->get_ext_data(); return r; Z3_CATCH_RETURN(0); } Z3_sort Z3_theory_mk_sort(Z3_context c, Z3_theory t, Z3_symbol s) { Z3_TRY; RESET_ERROR_CODE(); sort * r = mk_t(t)->mk_sort(to_symbol(s)); mk_c(c)->save_ast_trail(r); return of_sort(r); Z3_CATCH_RETURN(0); } Z3_ast Z3_theory_mk_value(Z3_context c, Z3_theory t, Z3_symbol n, Z3_sort s) { Z3_TRY; RESET_ERROR_CODE(); func_decl * d = mk_t(t)->mk_value_decl(to_symbol(n), to_sort(s)); app * r = mk_c(c)->m().mk_const(d); mk_c(c)->save_ast_trail(r); return of_ast(r); Z3_CATCH_RETURN(0); } Z3_ast Z3_theory_mk_constant(Z3_context c, Z3_theory t, Z3_symbol n, Z3_sort s) { Z3_TRY; RESET_ERROR_CODE(); Z3_func_decl d = Z3_theory_mk_func_decl(c, t, n, 0, 0, s); app * r = mk_c(c)->m().mk_const(to_func_decl(d)); mk_c(c)->save_ast_trail(r); return of_ast(r); Z3_CATCH_RETURN(0); } Z3_func_decl Z3_theory_mk_func_decl(Z3_context c, Z3_theory t, Z3_symbol n, unsigned domain_size, Z3_sort const domain[], Z3_sort range) { Z3_TRY; RESET_ERROR_CODE(); func_decl * r = mk_t(t)->mk_func_decl(to_symbol(n), domain_size, to_sorts(domain), to_sort(range)); mk_c(c)->save_ast_trail(r); return of_func_decl(r); Z3_CATCH_RETURN(0); } Z3_context Z3_theory_get_context(Z3_theory t) { Z3_context c = reinterpret_cast(mk_t(t)->get_ext_context()); RESET_ERROR_CODE(); return c; } void Z3_set_delete_callback(Z3_theory t, Z3_theory_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_delete_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_reduce_app_callback(Z3_theory t, Z3_reduce_app_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_reduce_app_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_reduce_eq_callback(Z3_theory t, Z3_reduce_eq_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_reduce_eq_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_reduce_distinct_callback(Z3_theory t, Z3_reduce_distinct_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_reduce_distinct_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_new_app_callback(Z3_theory t, Z3_theory_ast_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_new_app_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_new_elem_callback(Z3_theory t, Z3_theory_ast_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_new_elem_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_init_search_callback(Z3_theory t, Z3_theory_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_init_search_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_push_callback(Z3_theory t, Z3_theory_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_push_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_pop_callback(Z3_theory t, Z3_theory_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_pop_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_restart_callback(Z3_theory t, Z3_theory_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_restart_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_reset_callback(Z3_theory t, Z3_theory_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_reset_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_final_check_callback(Z3_theory t, Z3_theory_final_check_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_final_check_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_new_eq_callback(Z3_theory t, Z3_theory_ast_ast_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_new_eq_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_new_diseq_callback(Z3_theory t, Z3_theory_ast_ast_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_new_diseq_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_new_assignment_callback(Z3_theory t, Z3_theory_ast_bool_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_new_assignment_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_set_new_relevant_callback(Z3_theory t, Z3_theory_ast_callback_fptr f) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->set_new_relevant_fptr(reinterpret_cast(f)); Z3_CATCH; } void Z3_theory_assert_axiom(Z3_theory t, Z3_ast ax) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->assert_axiom(to_ast(ax)); Z3_CATCH; } void Z3_theory_assume_eq(Z3_theory t, Z3_ast lhs, Z3_ast rhs) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->assume_eq(to_ast(lhs), to_ast(rhs)); Z3_CATCH; } void Z3_theory_enable_axiom_simplification(Z3_theory t, Z3_bool flag) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); mk_t(t)->enable_axiom_simplification(flag == Z3_TRUE); Z3_CATCH; } Z3_ast Z3_theory_get_eqc_root(Z3_theory t, Z3_ast n) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); return of_ast(mk_t(t)->get_root(to_ast(n))); Z3_CATCH_RETURN(0); } Z3_ast Z3_theory_get_eqc_next(Z3_theory t, Z3_ast n) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); return of_ast(mk_t(t)->get_next(to_ast(n))); Z3_CATCH_RETURN(0); } unsigned Z3_theory_get_num_parents(Z3_theory t, Z3_ast n) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); return mk_t(t)->get_num_parents(to_ast(n)); Z3_CATCH_RETURN(0); } Z3_ast Z3_theory_get_parent(Z3_theory t, Z3_ast n, unsigned i) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); return of_ast(mk_t(t)->get_parent(to_ast(n), i)); Z3_CATCH_RETURN(0); } unsigned Z3_theory_get_num_elems(Z3_theory t) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); return mk_t(t)->get_num_asts(); Z3_CATCH_RETURN(0); } Z3_ast Z3_theory_get_elem(Z3_theory t, unsigned i) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); return of_ast(mk_t(t)->get_ast(i)); Z3_CATCH_RETURN(0); } unsigned Z3_theory_get_num_apps(Z3_theory t) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); return mk_t(t)->get_num_parents(); Z3_CATCH_RETURN(0); } Z3_ast Z3_theory_get_app(Z3_theory t, unsigned i) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); return of_ast(mk_t(t)->get_parent(i)); Z3_CATCH_RETURN(0); } Z3_bool Z3_theory_is_value(Z3_theory t, Z3_ast n) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); return is_app(to_ast(n)) && mk_t(t)->get_family_id() == to_app(to_ast(n))->get_family_id(); Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_theory_is_decl(Z3_theory t, Z3_func_decl d) { Z3_context c = Z3_theory_get_context(t); Z3_TRY; RESET_ERROR_CODE(); return mk_t(t)->get_family_id() == to_func_decl(d)->get_family_id(); Z3_CATCH_RETURN(Z3_FALSE); } }; z3-z3-4.4.1/src/api/api_util.h000066400000000000000000000146311260446376700157510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/c++/000077500000000000000000000000001260446376700143355ustar00rootroot00000000000000z3-z3-4.4.1/src/api/c++/z3++.h000066400000000000000000002506671260446376700152100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dll/000077500000000000000000000000001260446376700145405ustar00rootroot00000000000000z3-z3-4.4.1/src/api/dll/dll.cpp000066400000000000000000000007661260446376700160300ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/api/dotnet/000077500000000000000000000000001260446376700152625ustar00rootroot00000000000000z3-z3-4.4.1/src/api/dotnet/AST.cs000066400000000000000000000177641260446376700162570ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/ASTMap.cs000066400000000000000000000103441260446376700167000ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/ASTVector.cs000066400000000000000000000176721260446376700174400ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/AlgebraicNum.cs000066400000000000000000000045271260446376700201520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/ApplyResult.cs000066400000000000000000000062421260446376700201010ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/ArithExpr.cs000066400000000000000000000013361260446376700175220ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/ArithSort.cs000066400000000000000000000010611260446376700175260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/ArrayExpr.cs000066400000000000000000000013161260446376700175270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/ArraySort.cs000066400000000000000000000030721260446376700175410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/BitVecExpr.cs000066400000000000000000000015731260446376700176320ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/BitVecNum.cs000066400000000000000000000053001260446376700174430ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/BitVecSort.cs000066400000000000000000000013731260446376700176410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/BoolExpr.cs000066400000000000000000000012461260446376700173460ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/BoolSort.cs000066400000000000000000000012171260446376700173550ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Constructor.cs000066400000000000000000000077241260446376700201500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/ConstructorList.cs000066400000000000000000000023161260446376700207740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Context.cs000066400000000000000000005431771260446376700172560ustar00rootroot00000000000000/*++ 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 ArrayExpr MkEmptySet(Sort domain) { Contract.Requires(domain != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_empty_set(nCtx, domain.NativeObject)); } /// /// Create the full set. /// public ArrayExpr MkFullSet(Sort domain) { Contract.Requires(domain != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_full_set(nCtx, domain.NativeObject)); } /// /// Add an element to the set. /// public ArrayExpr MkSetAdd(ArrayExpr set, Expr element) { Contract.Requires(set != null); Contract.Requires(element != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(set); CheckContextMatch(element); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_add(nCtx, set.NativeObject, element.NativeObject)); } /// /// Remove an element from a set. /// public ArrayExpr MkSetDel(ArrayExpr set, Expr element) { Contract.Requires(set != null); Contract.Requires(element != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(set); CheckContextMatch(element); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_del(nCtx, set.NativeObject, element.NativeObject)); } /// /// Take the union of a list of sets. /// public ArrayExpr MkSetUnion(params ArrayExpr[] args) { Contract.Requires(args != null); Contract.Requires(Contract.ForAll(args, a => a != null)); CheckContextMatch(args); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_union(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } /// /// Take the intersection of a list of sets. /// public ArrayExpr MkSetIntersection(params ArrayExpr[] args) { Contract.Requires(args != null); Contract.Requires(Contract.ForAll(args, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(args); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_intersect(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } /// /// Take the difference between two sets. /// public ArrayExpr MkSetDifference(ArrayExpr arg1, ArrayExpr arg2) { Contract.Requires(arg1 != null); Contract.Requires(arg2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(arg1); CheckContextMatch(arg2); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_difference(nCtx, arg1.NativeObject, arg2.NativeObject)); } /// /// Take the complement of a set. /// public ArrayExpr MkSetComplement(ArrayExpr arg) { Contract.Requires(arg != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(arg); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_complement(nCtx, arg.NativeObject)); } /// /// Check for set membership. /// public BoolExpr MkSetMembership(Expr elem, ArrayExpr set) { Contract.Requires(elem != null); Contract.Requires(set != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(elem); CheckContextMatch(set); return (BoolExpr) Expr.Create(this, Native.Z3_mk_set_member(nCtx, elem.NativeObject, set.NativeObject)); } /// /// Check for subsetness of sets. /// public BoolExpr MkSetSubset(ArrayExpr arg1, ArrayExpr arg2) { Contract.Requires(arg1 != null); Contract.Requires(arg2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(arg1); CheckContextMatch(arg2); return (BoolExpr) 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-z3-4.4.1/src/api/dotnet/DatatypeExpr.cs000066400000000000000000000013401260446376700202210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/DatatypeSort.cs000066400000000000000000000062341260446376700202410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Deprecated.cs000066400000000000000000000067641260446376700176660ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/EnumSort.cs000066400000000000000000000074621260446376700173760ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Expr.cs000066400000000000000000002344011260446376700165330ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/FPExpr.cs000066400000000000000000000020141260446376700167520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/FPNum.cs000066400000000000000000000066231260446376700166050ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/FPRMExpr.cs000066400000000000000000000013721260446376700172170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/FPRMNum.cs000066400000000000000000000072141260446376700170410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/FPRMSort.cs000066400000000000000000000014061260446376700172260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/FPSort.cs000066400000000000000000000022001260446376700167600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/FiniteDomainSort.cs000066400000000000000000000023451260446376700210330ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Fixedpoint.cs000066400000000000000000000306521260446376700177300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/FuncDecl.cs000066400000000000000000000305011260446376700172730ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/FuncInterp.cs000066400000000000000000000154021260446376700176700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Global.cs000066400000000000000000000100071260446376700170070ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Goal.cs000066400000000000000000000175331260446376700165040ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/IDecRefQueue.cs000066400000000000000000000045501260446376700200630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/IntExpr.cs000066400000000000000000000013051260446376700172010ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/IntNum.cs000066400000000000000000000053361260446376700170320ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/IntSort.cs000066400000000000000000000013361260446376700172160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/IntSymbol.cs000066400000000000000000000030771260446376700175400ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/InterpolationContext.cs000066400000000000000000000151421260446376700220100ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/api/dotnet/ListSort.cs000066400000000000000000000074611260446376700174040ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Log.cs000066400000000000000000000035701260446376700163370ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Microsoft.Z3.csproj000066400000000000000000000605661260446376700207610ustar00rootroot00000000000000 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-z3-4.4.1/src/api/dotnet/Model.cs000066400000000000000000000262761260446376700166660ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Optimize.cs000066400000000000000000000220511260446376700174110ustar00rootroot00000000000000/*++ 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)); } /// /// Return a string the describes why the last to check returned unknown /// public String getReasonUnknown() { Contract.Ensures(Contract.Result() != null); return Native.Z3_optimize_get_reason_unknown(Context.nCtx, NativeObject); } /// /// 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-z3-4.4.1/src/api/dotnet/ParamDescrs.cs000066400000000000000000000060121260446376700200140ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Params.cs000066400000000000000000000113331260446376700170350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Pattern.cs000066400000000000000000000033301260446376700172250ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Probe.cs000066400000000000000000000052311260446376700166610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Properties/000077500000000000000000000000001260446376700174165ustar00rootroot00000000000000z3-z3-4.4.1/src/api/dotnet/Properties/AssemblyInfo000066400000000000000000000027751260446376700217470ustar00rootroot00000000000000using 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-z3-4.4.1/src/api/dotnet/Quantifier.cs000066400000000000000000000222021260446376700177160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/RatNum.cs000066400000000000000000000051621260446376700170230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Readme.NET35000066400000000000000000000007441260446376700172040ustar00rootroot00000000000000The 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-z3-4.4.1/src/api/dotnet/RealExpr.cs000066400000000000000000000013151260446376700173330ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/RealSort.cs000066400000000000000000000013371260446376700173500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/RelationSort.cs000066400000000000000000000027441260446376700202450ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/SetSort.cs000066400000000000000000000014711260446376700172170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Solver.cs000066400000000000000000000262661260446376700170770ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Sort.cs000066400000000000000000000117651260446376700165520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Statistics.cs000066400000000000000000000147371260446376700177570ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Status.cs000066400000000000000000000011751260446376700171000ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/StringSymbol.cs000066400000000000000000000032631260446376700202510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Symbol.cs000066400000000000000000000067561260446376700170740ustar00rootroot00000000000000/*++ 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"); } /// /// Equality overloading. /// public static bool operator ==(Symbol s1, Symbol s2) { return Object.ReferenceEquals(s1, s2) || (!Object.ReferenceEquals(s1, null) && !Object.ReferenceEquals(s2, null) && s1.NativeObject == s2.NativeObject); } /// /// Equality overloading. /// public static bool operator !=(Symbol s1, Symbol s2) { return !(s1.NativeObject == s2.NativeObject); } /// /// Object comparison. /// public override bool Equals(object o) { Symbol casted = o as Symbol; if (casted == null) return false; return this == casted; } /// /// The Symbols's hash code. /// /// A hash code public override int GetHashCode() { return (int)NativeObject; } #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-z3-4.4.1/src/api/dotnet/Tactic.cs000066400000000000000000000076601260446376700170310ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/TupleSort.cs000066400000000000000000000042471260446376700175610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/UninterpretedSort.cs000066400000000000000000000015341260446376700213140ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Version.cs000066400000000000000000000046201260446376700172400ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Z3Exception.cs000066400000000000000000000013521260446376700177650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/dotnet/Z3Object.cs000066400000000000000000000066721260446376700172470ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/java/000077500000000000000000000000001260446376700147065ustar00rootroot00000000000000z3-z3-4.4.1/src/api/java/AST.java000066400000000000000000000133631260446376700162060ustar00rootroot00000000000000/** 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 implements Comparable { /** * 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 == casted) || (this != null) && (casted != null) && (getContext().nCtx() == casted.getContext().nCtx()) && (Native.isEqAst(getContext().nCtx(), 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-z3-4.4.1/src/api/java/ASTDecRefQueue.java000066400000000000000000000014411260446376700202560ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ASTDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ASTDecRefQueue extends IDecRefQueue { public ASTDecRefQueue() { super(); } public ASTDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.incRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.decRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-z3-4.4.1/src/api/java/ASTMap.java000066400000000000000000000054461260446376700166470ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ASTMap.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Map from AST to AST **/ class ASTMap extends Z3Object { /** * Checks whether the map contains the key {@code k}. * @param k An AST * * @return True if {@code k} is a key in the map, false * otherwise. **/ public boolean contains(AST k) { return Native.astMapContains(getContext().nCtx(), getNativeObject(), k.getNativeObject()); } /** * Finds the value associated with the key {@code k}. * Remarks: This function signs an error when {@code k} is not a key in * the map. * @param k An AST * * @throws Z3Exception **/ public AST find(AST k) { return new AST(getContext(), Native.astMapFind(getContext().nCtx(), getNativeObject(), k.getNativeObject())); } /** * Stores or replaces a new key/value pair in the map. * @param k The key AST * @param v The value AST **/ public void insert(AST k, AST v) { Native.astMapInsert(getContext().nCtx(), getNativeObject(), k.getNativeObject(), v.getNativeObject()); } /** * Erases the key {@code k} from the map. * @param k An AST **/ public void erase(AST k) { Native.astMapErase(getContext().nCtx(), getNativeObject(), k.getNativeObject()); } /** * Removes all keys from the map. **/ public void reset() { Native.astMapReset(getContext().nCtx(), getNativeObject()); } /** * The size of the map **/ public int size() { return Native.astMapSize(getContext().nCtx(), getNativeObject()); } /** * The keys stored in the map. * * @throws Z3Exception **/ public AST[] getKeys() { ASTVector av = new ASTVector(getContext(), Native.astMapKeys(getContext().nCtx(), getNativeObject())); return av.ToArray(); } /** * Retrieves a string representation of the map. **/ public String toString() { try { return Native.astMapToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } ASTMap(Context ctx, long obj) { super(ctx, obj); } ASTMap(Context ctx) { super(ctx, Native.mkAstMap(ctx.nCtx())); } void incRef(long o) { getContext().getASTMapDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getASTMapDRQ().add(o); super.decRef(o); } } z3-z3-4.4.1/src/api/java/ASTVector.java000066400000000000000000000141221260446376700173630ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/AlgebraicNum.java000066400000000000000000000032031260446376700201000ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/ApplyResult.java000066400000000000000000000040511260446376700200350ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ApplyResult.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * ApplyResult objects represent the result of an application of a tactic to a * goal. It contains the subgoals that were produced. **/ public class ApplyResult extends Z3Object { /** * The number of Subgoals. **/ public int getNumSubgoals() { return Native.applyResultGetNumSubgoals(getContext().nCtx(), getNativeObject()); } /** * Retrieves the subgoals from the ApplyResult. * * @throws Z3Exception **/ public Goal[] getSubgoals() { int n = getNumSubgoals(); Goal[] res = new Goal[n]; for (int i = 0; i < n; i++) res[i] = new Goal(getContext(), Native.applyResultGetSubgoal(getContext().nCtx(), getNativeObject(), i)); return res; } /** * Convert a model for the subgoal {@code i} into a model for the * original goal {@code g}, that the ApplyResult was obtained from. * * @return A model for {@code g} * @throws Z3Exception **/ public Model convertModel(int i, Model m) { return new Model(getContext(), Native.applyResultConvertModel(getContext().nCtx(), getNativeObject(), i, m.getNativeObject())); } /** * A string representation of the ApplyResult. **/ public String toString() { try { return Native.applyResultToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } ApplyResult(Context ctx, long obj) { super(ctx, obj); } void incRef(long o) { getContext().getApplyResultDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getApplyResultDRQ().add(o); super.decRef(o); } } z3-z3-4.4.1/src/api/java/ApplyResultDecRefQueue.java000066400000000000000000000015271260446376700221200ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ApplyResultDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ApplyResultDecRefQueue extends IDecRefQueue { public ApplyResultDecRefQueue() { super(); } public ApplyResultDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.applyResultIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.applyResultDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-z3-4.4.1/src/api/java/ArithExpr.java000066400000000000000000000006441260446376700174630ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/ArithSort.java000066400000000000000000000005701260446376700174720ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ArithSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * An arithmetic sort, i.e., Int or Real. **/ public class ArithSort extends Sort { ArithSort(Context ctx, long obj) { super(ctx, obj); } }; z3-z3-4.4.1/src/api/java/ArrayExpr.java000066400000000000000000000006251260446376700174710ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/ArraySort.java000066400000000000000000000021671260446376700175050ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ArraySort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Array sorts. **/ public class ArraySort extends Sort { /** * The domain of the array sort. * @throws Z3Exception * @throws Z3Exception on error * @return a sort **/ public Sort getDomain() { return Sort.create(getContext(), Native.getArraySortDomain(getContext().nCtx(), getNativeObject())); } /** * The range of the array sort. * @throws Z3Exception * @throws Z3Exception on error * @return a sort **/ public Sort getRange() { return Sort.create(getContext(), Native.getArraySortRange(getContext().nCtx(), getNativeObject())); } ArraySort(Context ctx, long obj) { super(ctx, obj); } ArraySort(Context ctx, Sort domain, Sort range) { super(ctx, Native.mkArraySort(ctx.nCtx(), domain.getNativeObject(), range.getNativeObject())); } }; z3-z3-4.4.1/src/api/java/AstMapDecRefQueue.java000066400000000000000000000014711260446376700210170ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: AstMapDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ASTMapDecRefQueue extends IDecRefQueue { public ASTMapDecRefQueue() { super(); } public ASTMapDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.astMapIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.astMapDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-z3-4.4.1/src/api/java/AstVectorDecRefQueue.java000066400000000000000000000015131260446376700215410ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/BitVecExpr.java000066400000000000000000000012241260446376700175630ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/BitVecNum.java000066400000000000000000000030361260446376700174070ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/BitVecSort.java000066400000000000000000000011141260446376700175720ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: BitVecSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Bit-vector sorts. **/ public class BitVecSort extends Sort { /** * The size of the bit-vector sort. * @throws Z3Exception on error * @return an int **/ public int getSize() { return Native.getBvSortSize(getContext().nCtx(), getNativeObject()); } BitVecSort(Context ctx, long obj) { super(ctx, obj); } }; z3-z3-4.4.1/src/api/java/BoolExpr.java000066400000000000000000000011101260446376700172740ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: BoolExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Boolean expressions **/ public class BoolExpr extends Expr { /** * Constructor for BoolExpr **/ protected BoolExpr(Context ctx) { super(ctx); } /** * Constructor for BoolExpr * @throws Z3Exception * @throws Z3Exception on error **/ BoolExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.4.1/src/api/java/BoolSort.java000066400000000000000000000006371260446376700173220ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: BoolSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * A Boolean sort. **/ public class BoolSort extends Sort { BoolSort(Context ctx, long obj) { super(ctx, obj); { }} BoolSort(Context ctx) { super(ctx, Native.mkBoolSort(ctx.nCtx())); { }} }; z3-z3-4.4.1/src/api/java/Constructor.java000066400000000000000000000061441260446376700201030ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Constructor.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Constructors are used for datatype sorts. **/ public class Constructor extends Z3Object { /** * The number of fields of the constructor. * @throws Z3Exception * @throws Z3Exception on error * @return an int **/ public int getNumFields() { return n; } /** * The function declaration of the constructor. * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl ConstructorDecl() { Native.LongPtr constructor = new Native.LongPtr(); Native.LongPtr tester = new Native.LongPtr(); long[] accessors = new long[n]; Native.queryConstructor(getContext().nCtx(), getNativeObject(), n, constructor, tester, accessors); return new FuncDecl(getContext(), constructor.value); } /** * The function declaration of the tester. * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl getTesterDecl() { Native.LongPtr constructor = new Native.LongPtr(); Native.LongPtr tester = new Native.LongPtr(); long[] accessors = new long[n]; Native.queryConstructor(getContext().nCtx(), getNativeObject(), n, constructor, tester, accessors); return new FuncDecl(getContext(), tester.value); } /** * The function declarations of the accessors * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl[] getAccessorDecls() { Native.LongPtr constructor = new Native.LongPtr(); Native.LongPtr tester = new Native.LongPtr(); long[] accessors = new long[n]; Native.queryConstructor(getContext().nCtx(), getNativeObject(), n, constructor, tester, accessors); FuncDecl[] t = new FuncDecl[n]; for (int i = 0; i < n; i++) t[i] = new FuncDecl(getContext(), accessors[i]); return t; } /** * Destructor. * @throws Z3Exception on error **/ protected void finalize() { Native.delConstructor(getContext().nCtx(), getNativeObject()); } private int n = 0; Constructor(Context ctx, Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, int[] sortRefs) { super(ctx); 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 int[n]; setNativeObject(Native.mkConstructor(ctx.nCtx(), name.getNativeObject(), recognizer.getNativeObject(), n, Symbol.arrayToNative(fieldNames), Sort.arrayToNative(sorts), sortRefs)); } } z3-z3-4.4.1/src/api/java/ConstructorList.java000066400000000000000000000015051260446376700207330ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ConstructorList.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Lists of constructors **/ public class ConstructorList extends Z3Object { /** * Destructor. * @throws Z3Exception on error **/ protected void finalize() { Native.delConstructorList(getContext().nCtx(), getNativeObject()); } ConstructorList(Context ctx, long obj) { super(ctx, obj); } ConstructorList(Context ctx, Constructor[] constructors) { super(ctx); setNativeObject(Native.mkConstructorList(getContext().nCtx(), (int) constructors.length, Constructor.arrayToNative(constructors))); } } z3-z3-4.4.1/src/api/java/Context.java000066400000000000000000003420121260446376700171770ustar00rootroot00000000000000/** 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 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-z3-4.4.1/src/api/java/FixedpointDecRefQueue.java000066400000000000000000000015221260446376700217400ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FixedpointDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class FixedpointDecRefQueue extends IDecRefQueue { public FixedpointDecRefQueue() { super(); } public FixedpointDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.fixedpointIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.fixedpointDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-z3-4.4.1/src/api/java/FuncDecl.java000066400000000000000000000236051260446376700172420ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FuncDecl.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_parameter_kind; /** * Function declarations. **/ public class FuncDecl extends AST { /** * Object comparison. **/ public boolean equals(Object o) { FuncDecl casted = null; try { casted = FuncDecl.class.cast(o); } catch (ClassCastException e) { return false; } return (this == casted) || (this != null) && (casted != null) && (getContext().nCtx() == casted.getContext().nCtx()) && (Native.isEqFuncDecl(getContext().nCtx(), getNativeObject(), casted.getNativeObject())); } /** * A hash code. **/ public int hashCode() { return super.hashCode(); } /** * A string representations of the function declaration. **/ public String toString() { try { return Native.funcDeclToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } /** * Returns a unique identifier for the function declaration. **/ public int getId() { return Native.getFuncDeclId(getContext().nCtx(), getNativeObject()); } /** * The arity of the function declaration **/ public int getArity() { return Native.getArity(getContext().nCtx(), getNativeObject()); } /** * The size of the domain of the function declaration * @see getArity **/ public int getDomainSize() { return Native.getDomainSize(getContext().nCtx(), getNativeObject()); } /** * The domain of the function declaration **/ public Sort[] getDomain() { int n = getDomainSize(); Sort[] res = new Sort[n]; for (int i = 0; i < n; i++) res[i] = Sort.create(getContext(), Native.getDomain(getContext().nCtx(), getNativeObject(), i)); return res; } /** * The range of the function declaration **/ public Sort getRange() { return Sort.create(getContext(), Native.getRange(getContext().nCtx(), getNativeObject())); } /** * The kind of the function declaration. **/ public Z3_decl_kind getDeclKind() { return Z3_decl_kind.fromInt(Native.getDeclKind(getContext().nCtx(), getNativeObject())); } /** * The name of the function declaration **/ public Symbol getName() { return Symbol.create(getContext(), Native.getDeclName(getContext().nCtx(), getNativeObject())); } /** * The number of parameters of the function declaration **/ public int getNumParameters() { return Native.getDeclNumParameters(getContext().nCtx(), getNativeObject()); } /** * The parameters of the function declaration **/ public Parameter[] getParameters() { int num = getNumParameters(); Parameter[] res = new Parameter[num]; for (int i = 0; i < num; i++) { Z3_parameter_kind k = Z3_parameter_kind.fromInt(Native .getDeclParameterKind(getContext().nCtx(), getNativeObject(), i)); switch (k) { case Z3_PARAMETER_INT: res[i] = new Parameter(k, Native.getDeclIntParameter(getContext() .nCtx(), getNativeObject(), i)); break; case Z3_PARAMETER_DOUBLE: res[i] = new Parameter(k, Native.getDeclDoubleParameter( getContext().nCtx(), getNativeObject(), i)); break; case Z3_PARAMETER_SYMBOL: res[i] = new Parameter(k, Symbol.create(getContext(), Native .getDeclSymbolParameter(getContext().nCtx(), getNativeObject(), i))); break; case Z3_PARAMETER_SORT: res[i] = new Parameter(k, Sort.create(getContext(), Native .getDeclSortParameter(getContext().nCtx(), getNativeObject(), i))); break; case Z3_PARAMETER_AST: res[i] = new Parameter(k, new AST(getContext(), Native.getDeclAstParameter(getContext().nCtx(), getNativeObject(), i))); break; case Z3_PARAMETER_FUNC_DECL: res[i] = new Parameter(k, new FuncDecl(getContext(), Native.getDeclFuncDeclParameter(getContext().nCtx(), getNativeObject(), i))); break; case Z3_PARAMETER_RATIONAL: res[i] = new Parameter(k, Native.getDeclRationalParameter( getContext().nCtx(), getNativeObject(), 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 { private Z3_parameter_kind kind; private int i; private double d; private Symbol sym; private Sort srt; private AST ast; private FuncDecl fd; private String r; /** * The int value of the parameter. **/ public int getInt() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_INT) throw new Z3Exception("parameter is not an int"); return i; } /** * The double value of the parameter. **/ public double getDouble() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_DOUBLE) throw new Z3Exception("parameter is not a double "); return d; } /** * The Symbol value of the parameter. **/ public Symbol getSymbol() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_SYMBOL) throw new Z3Exception("parameter is not a Symbol"); return sym; } /** * The Sort value of the parameter. **/ public Sort getSort() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_SORT) throw new Z3Exception("parameter is not a Sort"); return srt; } /** * The AST value of the parameter. **/ public AST getAST() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_AST) throw new Z3Exception("parameter is not an AST"); return ast; } /** * The FunctionDeclaration value of the parameter. **/ public FuncDecl getFuncDecl() { if (getParameterKind() != 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 getRational() { if (getParameterKind() != 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 getParameterKind() { return kind; } Parameter(Z3_parameter_kind k, int i) { this.kind = k; this.i = i; } Parameter(Z3_parameter_kind k, double d) { this.kind = k; this.d = d; } Parameter(Z3_parameter_kind k, Symbol s) { this.kind = k; this.sym = s; } Parameter(Z3_parameter_kind k, Sort s) { this.kind = k; this.srt = s; } Parameter(Z3_parameter_kind k, AST a) { this.kind = k; this.ast = a; } Parameter(Z3_parameter_kind k, FuncDecl fd) { this.kind = k; this.fd = fd; } Parameter(Z3_parameter_kind k, String r) { this.kind = k; this.r = r; } } FuncDecl(Context ctx, long obj) { super(ctx, obj); } FuncDecl(Context ctx, Symbol name, Sort[] domain, Sort range) { super(ctx, Native.mkFuncDecl(ctx.nCtx(), name.getNativeObject(), AST.arrayLength(domain), AST.arrayToNative(domain), range.getNativeObject())); } FuncDecl(Context ctx, String prefix, Sort[] domain, Sort range) { super(ctx, Native.mkFreshFuncDecl(ctx.nCtx(), prefix, AST.arrayLength(domain), AST.arrayToNative(domain), range.getNativeObject())); } void checkNativeObject(long obj) { if (Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_FUNC_DECL_AST .toInt()) throw new Z3Exception( "Underlying object is not a function declaration"); super.checkNativeObject(obj); } /** * Create expression that applies function to arguments. * @param args * * @return **/ public Expr apply(Expr ... args) { getContext().checkContextMatch(args); return Expr.create(getContext(), this, args); } } z3-z3-4.4.1/src/api/java/FuncInterp.java000066400000000000000000000120271260446376700176300ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FuncInterp.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.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. **/ public class FuncInterp extends Z3Object { /** * An Entry object represents an element in the finite map used to encode a * function interpretation. **/ public class Entry extends Z3Object { /** * Return the (symbolic) value of this entry. * * @throws Z3Exception * @throws Z3Exception on error **/ public Expr getValue() { return Expr.create(getContext(), Native.funcEntryGetValue(getContext().nCtx(), getNativeObject())); } /** * The number of arguments of the entry. * @throws Z3Exception on error **/ public int getNumArgs() { return Native.funcEntryGetNumArgs(getContext().nCtx(), getNativeObject()); } /** * The arguments of the function entry. * * @throws Z3Exception * @throws Z3Exception on error **/ public Expr[] getArgs() { int n = getNumArgs(); Expr[] res = new Expr[n]; for (int i = 0; i < n; i++) res[i] = Expr.create(getContext(), Native.funcEntryGetArg( getContext().nCtx(), getNativeObject(), i)); return res; } /** * A string representation of the function entry. **/ public String toString() { try { int n = getNumArgs(); String res = "["; Expr[] args = getArgs(); for (int i = 0; i < n; i++) res += args[i] + ", "; return res + getValue() + "]"; } catch (Z3Exception e) { return new String("Z3Exception: " + e.getMessage()); } } Entry(Context ctx, long obj) { super(ctx, obj); } void incRef(long o) { getContext().getFuncEntryDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getFuncEntryDRQ().add(o); super.decRef(o); } }; /** * The number of entries in the function interpretation. * @throws Z3Exception on error * @return an int **/ public int getNumEntries() { return Native.funcInterpGetNumEntries(getContext().nCtx(), getNativeObject()); } /** * The entries in the function interpretation * * @throws Z3Exception * @throws Z3Exception on error **/ public Entry[] getEntries() { int n = getNumEntries(); Entry[] res = new Entry[n]; for (int i = 0; i < n; i++) res[i] = new Entry(getContext(), Native.funcInterpGetEntry(getContext() .nCtx(), getNativeObject(), i)); return res; } /** * The (symbolic) `else' value of the function interpretation. * * @throws Z3Exception * @throws Z3Exception on error * @return an Expr **/ public Expr getElse() { return Expr.create(getContext(), Native.funcInterpGetElse(getContext().nCtx(), getNativeObject())); } /** * The arity of the function interpretation * @throws Z3Exception on error * @return an int **/ public int getArity() { return Native.funcInterpGetArity(getContext().nCtx(), getNativeObject()); } /** * A string representation of the function interpretation. **/ public String toString() { try { String res = ""; res += "["; for (Entry e : getEntries()) { int n = e.getNumArgs(); if (n > 1) res += "["; Expr[] args = e.getArgs(); for (int i = 0; i < n; i++) { if (i != 0) res += ", "; res += args[i]; } if (n > 1) res += "]"; res += " -> " + e.getValue() + ", "; } res += "else -> " + getElse(); res += "]"; return res; } catch (Z3Exception e) { return new String("Z3Exception: " + e.getMessage()); } } FuncInterp(Context ctx, long obj) { super(ctx, obj); } void incRef(long o) { getContext().getFuncInterpDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getFuncInterpDRQ().add(o); super.decRef(o); } } z3-z3-4.4.1/src/api/java/FuncInterpDecRefQueue.java000066400000000000000000000015221260446376700217040ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FuncInterpDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class FuncInterpDecRefQueue extends IDecRefQueue { public FuncInterpDecRefQueue() { super(); } public FuncInterpDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.funcInterpIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.funcInterpDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-z3-4.4.1/src/api/java/FuncInterpEntryDecRefQueue.java000066400000000000000000000015431260446376700227310ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/Global.java000066400000000000000000000061521260446376700167550ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Global.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Global functions for Z3. * Remarks: * This (static) class contains functions that effect the behaviour of Z3 * globally across contexts, etc. * **/ public final class Global { /** * Set a global (or module) parameter, which is shared by all Z3 contexts. * Remarks: * 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.globalParamSet(id, value); } /** * Get a global (or module) parameter. * Remarks: * This function cannot be invoked simultaneously from different threads without synchronization. * The result string stored in param_value is stored in a shared location. * @return null if the parameter {@code id} does not exist. **/ public static String getParameter(String id) { Native.StringPtr res = new Native.StringPtr(); if (!Native.globalParamGet(id, res)) return null; else return res.value; } /** * Restore the value of all global (and module) parameters. * Remarks: * This command will not affect already created objects (such as tactics and solvers) * @see setParameter **/ public static void resetParameters() { Native.globalParamResetAll(); } /** * Enable/disable printing of warning messages to the console. * Remarks: Note * that this function is static and effects the behaviour of all contexts * globally. **/ public static void ToggleWarningMessages(boolean enabled) { Native.toggleWarningMessages((enabled) ? true : false); } /** * Enable tracing messages tagged as `tag' when Z3 is compiled in debug mode. * * Remarks: It is a NOOP otherwise. **/ public static void enableTrace(String tag) { Native.enableTrace(tag); } /** * Disable tracing messages tagged as `tag' when Z3 is compiled in debug mode. * * Remarks: It is a NOOP otherwise. **/ public static void disableTrace(String tag) { Native.disableTrace(tag); } } z3-z3-4.4.1/src/api/java/Goal.java000066400000000000000000000144541260446376700164430ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/GoalDecRefQueue.java000066400000000000000000000014561260446376700205170ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/IDecRefQueue.java000066400000000000000000000023701260446376700200210ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: IDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import java.util.LinkedList; public abstract class IDecRefQueue { protected Object m_lock = new Object(); protected LinkedList m_queue = new LinkedList(); protected int m_move_limit; protected IDecRefQueue() { m_move_limit = 1024; } protected IDecRefQueue(int move_limit) { m_move_limit = move_limit; } public void setLimit(int l) { m_move_limit = l; } protected abstract void incRef(Context ctx, long obj); protected abstract void decRef(Context ctx, long obj); protected void incAndClear(Context ctx, long o) { incRef(ctx, o); if (m_queue.size() >= m_move_limit) clear(ctx); } protected void add(long o) { if (o == 0) return; synchronized (m_lock) { m_queue.add(o); } } protected void clear(Context ctx) { synchronized (m_lock) { for (Long o : m_queue) decRef(ctx, o); m_queue.clear(); } } } z3-z3-4.4.1/src/api/java/IDisposable.java000066400000000000000000000004631260446376700177520ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IDisposable.java Abstract: Compatability interface (C# -> Java) Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ package com.microsoft.z3; public class IDisposable { public void dispose() { } } z3-z3-4.4.1/src/api/java/IntExpr.java000066400000000000000000000006641260446376700171500ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/IntNum.java000066400000000000000000000027161260446376700167710ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: IntNum.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import java.math.BigInteger; /** * Integer Numerals **/ public class IntNum extends IntExpr { IntNum(Context ctx, long obj) { super(ctx, obj); } /** * Retrieve the int value. **/ 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. **/ public long getInt64() { Native.LongPtr res = new Native.LongPtr(); if (Native.getNumeralInt64(getContext().nCtx(), getNativeObject(), res) ^ true) throw new Z3Exception("Numeral is not an int64"); 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(); } } } z3-z3-4.4.1/src/api/java/IntSort.java000066400000000000000000000006701260446376700171560ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: IntSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * An Integer sort **/ public class IntSort extends ArithSort { IntSort(Context ctx, long obj) { super(ctx, obj); } IntSort(Context ctx) { super(ctx, Native.mkIntSort(ctx.nCtx())); } } z3-z3-4.4.1/src/api/java/IntSymbol.java000066400000000000000000000021511260446376700174700ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/InterpolationContext.java000066400000000000000000000156131260446376700217530ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/ListSort.java000066400000000000000000000051231260446376700173350ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ListSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * List sorts. **/ public class ListSort extends Sort { /** * The declaration of the nil function of this list sort. * @throws Z3Exception **/ public FuncDecl getNilDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), 0)); } /** * The empty list. * @throws Z3Exception **/ public Expr getNil() { return getContext().mkApp(getNilDecl()); } /** * The declaration of the isNil function of this list sort. * @throws Z3Exception **/ public FuncDecl getIsNilDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortRecognizer(getContext().nCtx(), getNativeObject(), 0)); } /** * The declaration of the cons function of this list sort. * @throws Z3Exception **/ public FuncDecl getConsDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), 1)); } /** * The declaration of the isCons function of this list sort. * @throws Z3Exception * **/ public FuncDecl getIsConsDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortRecognizer(getContext().nCtx(), getNativeObject(), 1)); } /** * The declaration of the head function of this list sort. * @throws Z3Exception **/ public FuncDecl getHeadDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortConstructorAccessor(getContext().nCtx(), getNativeObject(), 1, 0)); } /** * The declaration of the tail function of this list sort. * @throws Z3Exception **/ public FuncDecl getTailDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortConstructorAccessor(getContext().nCtx(), getNativeObject(), 1, 1)); } ListSort(Context ctx, Symbol name, Sort elemSort) { super(ctx, 0); Native.LongPtr inil = new Native.LongPtr(), iisnil = new Native.LongPtr(); Native.LongPtr icons = new Native.LongPtr(), iiscons = new Native.LongPtr(); Native.LongPtr ihead = new Native.LongPtr(), itail = new Native.LongPtr(); setNativeObject(Native.mkListSort(ctx.nCtx(), name.getNativeObject(), elemSort.getNativeObject(), inil, iisnil, icons, iiscons, ihead, itail)); } }; z3-z3-4.4.1/src/api/java/Log.java000066400000000000000000000026521260446376700162770ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/Model.java000066400000000000000000000222151260446376700166130ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Model.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_sort_kind; /** * A Model contains interpretations (assignments) of constants and functions. **/ public class Model extends Z3Object { /** * Retrieves the interpretation (the assignment) of {@code a} in * the model. * @param a A Constant * * @return An expression if the constant has an interpretation in the model, * null otherwise. * @throws Z3Exception **/ public Expr getConstInterp(Expr a) { getContext().checkContextMatch(a); return getConstInterp(a.getFuncDecl()); } /** * Retrieves the interpretation (the assignment) of {@code f} in * the model. * @param f A function declaration of zero arity * * @return An expression if the function has an interpretation in the model, * null otherwise. * @throws Z3Exception **/ public Expr getConstInterp(FuncDecl f) { getContext().checkContextMatch(f); if (f.getArity() != 0 || Native.getSortKind(getContext().nCtx(), Native.getRange(getContext().nCtx(), f.getNativeObject())) == Z3_sort_kind.Z3_ARRAY_SORT .toInt()) throw new Z3Exception( "Non-zero arity functions and arrays have FunctionInterpretations as a model. Use FuncInterp."); long n = Native.modelGetConstInterp(getContext().nCtx(), getNativeObject(), f.getNativeObject()); if (n == 0) return null; else return Expr.create(getContext(), n); } /** * Retrieves the interpretation (the assignment) of a non-constant {@code f} in the model. * @param f A function declaration of non-zero arity * * @return A FunctionInterpretation if the function has an interpretation in * the model, null otherwise. * @throws Z3Exception **/ public FuncInterp getFuncInterp(FuncDecl f) { getContext().checkContextMatch(f); Z3_sort_kind sk = Z3_sort_kind.fromInt(Native.getSortKind(getContext() .nCtx(), Native.getRange(getContext().nCtx(), f.getNativeObject()))); if (f.getArity() == 0) { long n = Native.modelGetConstInterp(getContext().nCtx(), getNativeObject(), f.getNativeObject()); if (sk == Z3_sort_kind.Z3_ARRAY_SORT) { if (n == 0) return null; else { if (Native.isAsArray(getContext().nCtx(), n) ^ true) throw new Z3Exception( "Argument was not an array constant"); long fd = Native.getAsArrayFuncDecl(getContext().nCtx(), n); return getFuncInterp(new FuncDecl(getContext(), fd)); } } else { throw new Z3Exception( "Constant functions do not have a function interpretation; use ConstInterp"); } } else { long n = Native.modelGetFuncInterp(getContext().nCtx(), getNativeObject(), f.getNativeObject()); if (n == 0) return null; else return new FuncInterp(getContext(), n); } } /** * The number of constants that have an interpretation in the model. **/ public int getNumConsts() { return Native.modelGetNumConsts(getContext().nCtx(), getNativeObject()); } /** * The function declarations of the constants in the model. * * @throws Z3Exception **/ public FuncDecl[] getConstDecls() { int n = getNumConsts(); FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < n; i++) res[i] = new FuncDecl(getContext(), Native.modelGetConstDecl(getContext() .nCtx(), getNativeObject(), i)); return res; } /** * The number of function interpretations in the model. **/ public int getNumFuncs() { return Native.modelGetNumFuncs(getContext().nCtx(), getNativeObject()); } /** * The function declarations of the function interpretations in the model. * * @throws Z3Exception **/ public FuncDecl[] getFuncDecls() { int n = getNumFuncs(); FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < n; i++) res[i] = new FuncDecl(getContext(), Native.modelGetFuncDecl(getContext() .nCtx(), getNativeObject(), i)); return res; } /** * All symbols that have an interpretation in the model. * * @throws Z3Exception **/ public FuncDecl[] getDecls() { int nFuncs = getNumFuncs(); int nConsts = getNumConsts(); int n = nFuncs + nConsts; FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < nConsts; i++) res[i] = new FuncDecl(getContext(), Native.modelGetConstDecl(getContext() .nCtx(), getNativeObject(), i)); for (int i = 0; i < nFuncs; i++) res[nConsts + i] = new FuncDecl(getContext(), Native.modelGetFuncDecl( getContext().nCtx(), getNativeObject(), i)); return res; } /** * A ModelEvaluationFailedException is thrown when an expression cannot be * evaluated by the model. **/ @SuppressWarnings("serial") public class ModelEvaluationFailedException extends Z3Exception { /** * An exception that is thrown when model evaluation fails. **/ public ModelEvaluationFailedException() { super(); } } /** * Evaluates the expression {@code t} in the current model. * Remarks: This function may fail if {@code t} contains * quantifiers, is partial (MODEL_PARTIAL enabled), or if {@code t} is not well-sorted. In this case a * {@code ModelEvaluationFailedException} is thrown. * @param t An expression {@code completion} 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. * * @return The evaluation of {@code t} in the model. * @throws Z3Exception **/ public Expr eval(Expr t, boolean completion) { Native.LongPtr v = new Native.LongPtr(); if (Native.modelEval(getContext().nCtx(), getNativeObject(), t.getNativeObject(), (completion) ? true : false, v) ^ true) throw new ModelEvaluationFailedException(); else return Expr.create(getContext(), v.value); } /** * Alias for {@code Eval}. * * @throws Z3Exception **/ public Expr evaluate(Expr t, boolean completion) { return eval(t, completion); } /** * The number of uninterpreted sorts that the model has an interpretation * for. **/ public int getNumSorts() { return Native.modelGetNumSorts(getContext().nCtx(), getNativeObject()); } /** * The uninterpreted sorts that the model has an interpretation for. * Remarks: 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. * * @see getNumSorts * @see getSortUniverse * * @throws Z3Exception **/ public Sort[] getSorts() { int n = getNumSorts(); Sort[] res = new Sort[n]; for (int i = 0; i < n; i++) res[i] = Sort.create(getContext(), Native.modelGetSort(getContext().nCtx(), getNativeObject(), i)); return res; } /** * The finite set of distinct values that represent the interpretation for * sort {@code s}. * @param s An uninterpreted sort * * @return An array of expressions, where each is an element of the universe * of {@code s} * @throws Z3Exception **/ public Expr[] getSortUniverse(Sort s) { ASTVector nUniv = new ASTVector(getContext(), Native.modelGetSortUniverse( getContext().nCtx(), getNativeObject(), s.getNativeObject())); return nUniv.ToExprArray(); } /** * Conversion of models to strings. * * @return A string representation of the model. **/ public String toString() { try { return Native.modelToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } Model(Context ctx, long obj) { super(ctx, obj); } void incRef(long o) { getContext().getModelDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getModelDRQ().add(o); super.decRef(o); } } z3-z3-4.4.1/src/api/java/ModelDecRefQueue.java000066400000000000000000000014641260446376700206740ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/Optimize.java000066400000000000000000000145311260446376700173550ustar00rootroot00000000000000/** Copyright (c) 2015 Microsoft Corporation Module Name: Optimize.java Abstract: Z3 Java API: Optimizes Author: Nikolaj Bjorner (nbjorner) 2015-07-16 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_lbool; /** * Object for managing optimizization context **/ public class Optimize extends Z3Object { /** * A string that describes all available optimize solver parameters. **/ public String getHelp() { return Native.optimizeGetHelp(getContext().nCtx(), getNativeObject()); } /** * Sets the optimize solver parameters. * * @throws Z3Exception **/ public void setParameters(Params value) { Native.optimizeSetParams(getContext().nCtx(), getNativeObject(), value.getNativeObject()); } /** * Retrieves parameter descriptions for Optimize solver. **/ public ParamDescrs getParameterDescriptions() { return new ParamDescrs(getContext(), Native.optimizeGetParamDescrs(getContext().nCtx(), getNativeObject())); } /** * Assert a constraint (or multiple) into the optimize solver. **/ public void Assert(BoolExpr ... constraints) { getContext().checkContextMatch(constraints); for (BoolExpr a : constraints) { Native.optimizeAssert(getContext().nCtx(), getNativeObject(), a.getNativeObject()); } } /** * Alias for Assert. **/ public void Add(BoolExpr ... constraints) { Assert(constraints); } /** * Handle to objectives returned by objective functions. **/ public class Handle { Optimize opt; int handle; Handle(Optimize opt, int h) { this.opt = opt; this.handle = h; } /** * Retrieve a lower bound for the objective handle. **/ public ArithExpr getLower() { return opt.GetLower(handle); } /** * Retrieve an upper bound for the objective handle. **/ public ArithExpr getUpper() { return opt.GetUpper(handle); } /** * Retrieve the value of an objective. **/ public ArithExpr getValue() { return getLower(); } /** * Print a string representation of the handle. **/ public String toString() { return getValue().toString(); } } /** * Assert soft constraint * * Return an objective which associates with the group of constraints. * **/ public Handle AssertSoft(BoolExpr constraint, int weight, String group) { getContext().checkContextMatch(constraint); Symbol s = getContext().mkSymbol(group); return new Handle(this, Native.optimizeAssertSoft(getContext().nCtx(), getNativeObject(), constraint.getNativeObject(), Integer.toString(weight), s.getNativeObject())); } /** * 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.fromInt(Native.optimizeCheck(getContext().nCtx(), getNativeObject())); switch (r) { case Z3_L_TRUE: return Status.SATISFIABLE; case Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /** * Creates a backtracking point. **/ public void Push() { Native.optimizePush(getContext().nCtx(), getNativeObject()); } /** * Backtrack one backtracking point. * * Note that an exception is thrown if Pop is called without a corresponding Push. **/ public void Pop() { Native.optimizePop(getContext().nCtx(), getNativeObject()); } /** * 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 getModel() { long x = Native.optimizeGetModel(getContext().nCtx(), getNativeObject()); if (x == 0) return null; else return new Model(getContext(), 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.optimizeMaximize(getContext().nCtx(), getNativeObject(), e.getNativeObject())); } /** * Declare an arithmetical minimization objective. * Similar to MkMaximize. **/ public Handle MkMinimize(ArithExpr e) { return new Handle(this, Native.optimizeMinimize(getContext().nCtx(), getNativeObject(), e.getNativeObject())); } /** * Retrieve a lower bound for the objective handle. **/ private ArithExpr GetLower(int index) { return (ArithExpr)Expr.create(getContext(), Native.optimizeGetLower(getContext().nCtx(), getNativeObject(), index)); } /** * Retrieve an upper bound for the objective handle. **/ private ArithExpr GetUpper(int index) { return (ArithExpr)Expr.create(getContext(), Native.optimizeGetUpper(getContext().nCtx(), getNativeObject(), index)); } /** * Return a string the describes why the last to check returned unknown **/ public String getReasonUnknown() { return Native.optimizeGetReasonUnknown(getContext().nCtx(), getNativeObject()); } /** * Print the context to a String (SMT-LIB parseable benchmark). **/ public String toString() { return Native.optimizeToString(getContext().nCtx(), getNativeObject()); } /** * Optimize statistics. **/ public Statistics getStatistics() { return new Statistics(getContext(), Native.optimizeGetStatistics(getContext().nCtx(), getNativeObject())); } Optimize(Context ctx, long obj) throws Z3Exception { super(ctx, obj); } Optimize(Context ctx) throws Z3Exception { super(ctx, Native.mkOptimize(ctx.nCtx())); } void incRef(long o) { getContext().getOptimizeDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getOptimizeDRQ().add(o); super.decRef(o); } } z3-z3-4.4.1/src/api/java/OptimizeDecRefQueue.java000066400000000000000000000015121260446376700214260ustar00rootroot00000000000000/** Copyright (c) 2012-2015 Microsoft Corporation Module Name: OptimizeDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class OptimizeDecRefQueue extends IDecRefQueue { public OptimizeDecRefQueue() { super(); } public OptimizeDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.fixedpointIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.fixedpointDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-z3-4.4.1/src/api/java/ParamDescrs.java000066400000000000000000000041341260446376700177570ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/ParamDescrsDecRefQueue.java000066400000000000000000000015301260446376700220320ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ParamDescrsDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ParamDescrsDecRefQueue extends IDecRefQueue { public ParamDescrsDecRefQueue() { super(); } public ParamDescrsDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.paramDescrsIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.paramDescrsDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-z3-4.4.1/src/api/java/Params.java000066400000000000000000000064031260446376700167770ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Params.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * A ParameterSet represents a configuration in the form of Symbol/value pairs. **/ public class Params extends Z3Object { /** * Adds a parameter setting. **/ public void add(Symbol name, boolean value) { Native.paramsSetBool(getContext().nCtx(), getNativeObject(), name.getNativeObject(), (value) ? true : false); } /** * Adds a parameter setting. **/ public void add(Symbol name, double value) { Native.paramsSetDouble(getContext().nCtx(), getNativeObject(), name.getNativeObject(), value); } /** * Adds a parameter setting. **/ public void add(Symbol name, String value) { Native.paramsSetSymbol(getContext().nCtx(), getNativeObject(), name.getNativeObject(), getContext().mkSymbol(value).getNativeObject()); } /** * Adds a parameter setting. **/ public void add(Symbol name, Symbol value) { Native.paramsSetSymbol(getContext().nCtx(), getNativeObject(), name.getNativeObject(), value.getNativeObject()); } /** * Adds a parameter setting. **/ public void add(String name, boolean value) { Native.paramsSetBool(getContext().nCtx(), getNativeObject(), getContext().mkSymbol(name).getNativeObject(), value); } /** * Adds a parameter setting. **/ public void add(String name, int value) { Native.paramsSetUint(getContext().nCtx(), getNativeObject(), getContext() .mkSymbol(name).getNativeObject(), value); } /** * Adds a parameter setting. **/ public void add(String name, double value) { Native.paramsSetDouble(getContext().nCtx(), getNativeObject(), getContext() .mkSymbol(name).getNativeObject(), value); } /** * Adds a parameter setting. **/ public void add(String name, Symbol value) { Native.paramsSetSymbol(getContext().nCtx(), getNativeObject(), getContext() .mkSymbol(name).getNativeObject(), value.getNativeObject()); } /** * Adds a parameter setting. **/ public void add(String name, String value) { Native.paramsSetSymbol(getContext().nCtx(), getNativeObject(), getContext().mkSymbol(name).getNativeObject(), getContext().mkSymbol(value).getNativeObject()); } /** * A string representation of the parameter set. **/ public String toString() { try { return Native.paramsToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } Params(Context ctx) { super(ctx, Native.mkParams(ctx.nCtx())); } void incRef(long o) { getContext().getParamsDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getParamsDRQ().add(o); super.decRef(o); } } z3-z3-4.4.1/src/api/java/ParamsDecRefQueue.java000066400000000000000000000014711260446376700210550ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ParamDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ParamsDecRefQueue extends IDecRefQueue { public ParamsDecRefQueue() { super(); } public ParamsDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.paramsIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.paramsDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-z3-4.4.1/src/api/java/Pattern.java000066400000000000000000000025151260446376700171710ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/Probe.java000066400000000000000000000027161260446376700166260ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Probe.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.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 * {@code Context.NumProbes} and {@code Context.ProbeNames}. It may * also be obtained using the command {@code (help-tactics)} in the SMT 2.0 * front-end. **/ public class Probe extends Z3Object { /** * Execute the probe over the goal. * * @return A probe always produce a double value. "Boolean" probes return * 0.0 for false, and a value different from 0.0 for true. * @throws Z3Exception **/ public double apply(Goal g) { getContext().checkContextMatch(g); return Native.probeApply(getContext().nCtx(), getNativeObject(), g.getNativeObject()); } Probe(Context ctx, long obj) { super(ctx, obj); } Probe(Context ctx, String name) { super(ctx, Native.mkProbe(ctx.nCtx(), name)); } void incRef(long o) { getContext().getProbeDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getProbeDRQ().add(o); super.decRef(o); } } z3-z3-4.4.1/src/api/java/ProbeDecRefQueue.java000066400000000000000000000014641260446376700207030ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/Quantifier.java000066400000000000000000000146641260446376700176730ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/README000066400000000000000000000003521260446376700155660ustar00rootroot00000000000000Java 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-z3-4.4.1/src/api/java/RatNum.java000066400000000000000000000036121260446376700167610ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: RatNum.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import java.math.BigInteger; /** * Rational Numerals **/ public class RatNum extends RealExpr { /** * The numerator of a rational numeral. **/ public IntNum getNumerator() { return new IntNum(getContext(), Native.getNumerator(getContext().nCtx(), getNativeObject())); } /** * The denominator of a rational numeral. **/ public IntNum getDenominator() { return new IntNum(getContext(), Native.getDenominator(getContext().nCtx(), getNativeObject())); } /** * Converts the numerator of the rational to a BigInteger **/ public BigInteger getBigIntNumerator() { IntNum n = getNumerator(); return new BigInteger(n.toString()); } /** * Converts the denominator of the rational to a BigInteger **/ public BigInteger getBigIntDenominator() { IntNum n = getDenominator(); return new BigInteger(n.toString()); } /** * Returns a string representation in decimal notation. * Remarks: The result * has at most {@code precision} decimal places. **/ public String toDecimalString(int precision) { return Native.getNumeralDecimalString(getContext().nCtx(), getNativeObject(), precision); } /** * Returns a string representation of the numeral. **/ public String toString() { try { return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } RatNum(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.4.1/src/api/java/RealExpr.java000066400000000000000000000006251260446376700172760ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/RealSort.java000066400000000000000000000006711260446376700173100ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/RelationSort.java000066400000000000000000000020351260446376700201760ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/SetSort.java000066400000000000000000000007151260446376700171570ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: SetSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Set sorts. **/ public class SetSort extends Sort { SetSort(Context ctx, long obj) { super(ctx, obj); } SetSort(Context ctx, Sort ty) { super(ctx, Native.mkSetSort(ctx.nCtx(), ty.getNativeObject())); } } z3-z3-4.4.1/src/api/java/Solver.java000066400000000000000000000213431260446376700170260ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/SolverDecRefQueue.java000066400000000000000000000014551260446376700211060ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: SolverDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class SolverDecRefQueue extends IDecRefQueue { public SolverDecRefQueue() { super(); } public SolverDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.solverIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.solverDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-z3-4.4.1/src/api/java/Sort.java000066400000000000000000000066371260446376700165140ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Sort.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_sort_kind; /** * The Sort class implements type information for ASTs. **/ public class Sort extends AST { /** * Equality operator for objects of type Sort. * @param o * @return **/ public boolean equals(Object o) { Sort casted = null; try { casted = Sort.class.cast(o); } catch (ClassCastException e) { return false; } return (this == casted) || (this != null) && (casted != null) && (getContext().nCtx() == casted.getContext().nCtx()) && (Native.isEqSort(getContext().nCtx(), getNativeObject(), casted.getNativeObject())); } /** * Hash code generation for Sorts * * @return A hash code **/ public int hashCode() { return super.hashCode(); } /** * Returns a unique identifier for the sort. **/ public int getId() { return Native.getSortId(getContext().nCtx(), getNativeObject()); } /** * The kind of the sort. **/ public Z3_sort_kind getSortKind() { return Z3_sort_kind.fromInt(Native.getSortKind(getContext().nCtx(), getNativeObject())); } /** * The name of the sort **/ public Symbol getName() { return Symbol.create(getContext(), Native.getSortName(getContext().nCtx(), getNativeObject())); } /** * A string representation of the sort. **/ public String toString() { try { return Native.sortToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } /** * Sort constructor **/ Sort(Context ctx, long obj) { super(ctx, obj); } void checkNativeObject(long obj) { if (Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_SORT_AST .toInt()) throw new Z3Exception("Underlying object is not a sort"); super.checkNativeObject(obj); } static Sort create(Context ctx, long obj) { Z3_sort_kind sk = Z3_sort_kind.fromInt(Native.getSortKind(ctx.nCtx(), obj)); switch (sk) { case Z3_ARRAY_SORT: return new ArraySort(ctx, obj); case Z3_BOOL_SORT: return new BoolSort(ctx, obj); case Z3_BV_SORT: return new BitVecSort(ctx, obj); case Z3_DATATYPE_SORT: return new DatatypeSort(ctx, obj); case Z3_INT_SORT: return new IntSort(ctx, obj); case Z3_REAL_SORT: return new RealSort(ctx, obj); case Z3_UNINTERPRETED_SORT: return new UninterpretedSort(ctx, obj); case Z3_FINITE_DOMAIN_SORT: return new FiniteDomainSort(ctx, obj); case Z3_RELATION_SORT: return new RelationSort(ctx, obj); case Z3_FLOATING_POINT_SORT: return new FPSort(ctx, obj); case Z3_ROUNDING_MODE_SORT: return new FPRMSort(ctx, obj); default: throw new Z3Exception("Unknown sort kind"); } } } z3-z3-4.4.1/src/api/java/Statistics.java000066400000000000000000000113231260446376700177030ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Statistics.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Objects of this class track statistical information about solvers. **/ public class Statistics extends Z3Object { /** * Statistical data is organized into pairs of [Key, Entry], where every * Entry is either a {@code DoubleEntry} or a {@code UIntEntry} **/ public class Entry { /** * The key of the entry. **/ public String Key; /** * The uint-value of the entry. **/ public int getUIntValue() { return m_int; } /** * The double-value of the entry. **/ public double getDoubleValue() { return m_double; } /** * True if the entry is uint-valued. **/ public boolean isUInt() { return m_is_int; } /** * True if the entry is double-valued. **/ public boolean isDouble() { return m_is_double; } /** * The string representation of the the entry's value. * * @throws Z3Exception **/ public String getValueString() { if (isUInt()) return Integer.toString(m_int); else if (isDouble()) return Double.toString(m_double); else throw new Z3Exception("Unknown statistical entry type"); } /** * The string representation of the Entry. **/ public String toString() { try { return Key + ": " + getValueString(); } catch (Z3Exception e) { return new String("Z3Exception: " + e.getMessage()); } } private boolean m_is_int = false; private boolean m_is_double = false; private int m_int = 0; private double m_double = 0.0; Entry(String k, int v) { Key = k; m_is_int = true; m_int = v; } Entry(String k, double v) { Key = k; m_is_double = true; m_double = v; } } /** * A string representation of the statistical data. **/ public String toString() { try { return Native.statsToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } /** * The number of statistical data. **/ public int size() { return Native.statsSize(getContext().nCtx(), getNativeObject()); } /** * The data entries. * * @throws Z3Exception **/ public Entry[] getEntries() { int n = size(); Entry[] res = new Entry[n]; for (int i = 0; i < n; i++) { Entry e; String k = Native.statsGetKey(getContext().nCtx(), getNativeObject(), i); if (Native.statsIsUint(getContext().nCtx(), getNativeObject(), i)) e = new Entry(k, Native.statsGetUintValue(getContext().nCtx(), getNativeObject(), i)); else if (Native.statsIsDouble(getContext().nCtx(), getNativeObject(), i)) e = new Entry(k, Native.statsGetDoubleValue(getContext().nCtx(), getNativeObject(), i)); else throw new Z3Exception("Unknown data entry value"); res[i] = e; } return res; } /** * The statistical counters. **/ public String[] getKeys() { int n = size(); String[] res = new String[n]; for (int i = 0; i < n; i++) res[i] = Native.statsGetKey(getContext().nCtx(), getNativeObject(), i); return res; } /** * The value of a particular statistical counter. * Remarks: Returns null if * the key is unknown. * * @throws Z3Exception **/ public Entry get(String key) { int n = size(); Entry[] es = getEntries(); for (int i = 0; i < n; i++) if (es[i].Key == key) return es[i]; return null; } Statistics(Context ctx, long obj) { super(ctx, obj); } void incRef(long o) { getContext().getStatisticsDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getStatisticsDRQ().add(o); super.decRef(o); } } z3-z3-4.4.1/src/api/java/StatisticsDecRefQueue.java000066400000000000000000000015101260446376700217560ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/Status.java000066400000000000000000000014431260446376700170360ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Status.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.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); private final int intValue; Status(int v) { this.intValue = v; } public static final Status fromInt(int v) { for (Status k : values()) if (k.intValue == v) return k; return values()[0]; } public final int toInt() { return this.intValue; } } z3-z3-4.4.1/src/api/java/StringSymbol.java000066400000000000000000000020451260446376700202060ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/Symbol.java000066400000000000000000000044371260446376700170260ustar00rootroot00000000000000/** 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; } public boolean equals(Object o) { Symbol casted = null; try { casted = Symbol.class.cast(o); } catch (ClassCastException e) { return false; } return this.getNativeObject() == casted.getNativeObject(); } /** * 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-z3-4.4.1/src/api/java/Tactic.java000066400000000000000000000047561260446376700167740ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Tactic.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.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 * {@code Context.NumTactics} and {@code Context.TacticNames}. It may * also be obtained using the command {@code (help-tactics)} in the SMT 2.0 * front-end. **/ public class Tactic extends Z3Object { /** * A string containing a description of parameters accepted by the tactic. **/ public String getHelp() { return Native.tacticGetHelp(getContext().nCtx(), getNativeObject()); } /** * Retrieves parameter descriptions for Tactics. * @throws Z3Exception **/ public ParamDescrs getParameterDescriptions() { return new ParamDescrs(getContext(), Native.tacticGetParamDescrs(getContext() .nCtx(), getNativeObject())); } /** * Execute the tactic over the goal. * @throws Z3Exception **/ public ApplyResult apply(Goal g) { return apply(g, null); } /** * Execute the tactic over the goal. * @throws Z3Exception **/ public ApplyResult apply(Goal g, Params p) { getContext().checkContextMatch(g); if (p == null) return new ApplyResult(getContext(), Native.tacticApply(getContext() .nCtx(), getNativeObject(), g.getNativeObject())); else { getContext().checkContextMatch(p); return new ApplyResult(getContext(), Native.tacticApplyEx(getContext().nCtx(), getNativeObject(), g.getNativeObject(), p.getNativeObject())); } } /** * Creates a solver that is implemented using the given tactic. * @see Context#mkSolver(Tactic) * @throws Z3Exception **/ public Solver getSolver() { return getContext().mkSolver(this); } Tactic(Context ctx, long obj) { super(ctx, obj); } Tactic(Context ctx, String name) { super(ctx, Native.mkTactic(ctx.nCtx(), name)); } void incRef(long o) { getContext().getTacticDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getTacticDRQ().add(o); super.decRef(o); } } z3-z3-4.4.1/src/api/java/TacticDecRefQueue.java000066400000000000000000000014721260446376700210420ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: TacticDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class TacticDecRefQueue extends IDecRefQueue { public TacticDecRefQueue() { super(); } public TacticDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.tacticIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.tacticDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-z3-4.4.1/src/api/java/TupleSort.java000066400000000000000000000027731260446376700175230ustar00rootroot00000000000000/** 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-z3-4.4.1/src/api/java/UninterpretedSort.java000066400000000000000000000010101260446376700212410ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: UninterpretedSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Uninterpreted Sorts **/ public class UninterpretedSort extends Sort { UninterpretedSort(Context ctx, long obj) { super(ctx, obj); } UninterpretedSort(Context ctx, Symbol s) { super(ctx, Native.mkUninterpretedSort(ctx.nCtx(), s.getNativeObject())); } } z3-z3-4.4.1/src/api/java/Version.java000066400000000000000000000040401260446376700171740ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Version.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Version information. * Remarks: Note that this class is static. **/ public class Version { /** * The major version **/ public static int getMajor() { Native.IntPtr major = new Native.IntPtr(), minor = new Native.IntPtr(), build = new Native.IntPtr(), revision = new Native.IntPtr(); Native.getVersion(major, minor, build, revision); return major.value; } /** * The minor version **/ public static int getMinor() { Native.IntPtr major = new Native.IntPtr(), minor = new Native.IntPtr(), build = new Native.IntPtr(), revision = new Native.IntPtr(); Native.getVersion(major, minor, build, revision); return minor.value; } /** * The build version **/ public static int getBuild() { Native.IntPtr major = new Native.IntPtr(), minor = new Native.IntPtr(), build = new Native.IntPtr(), revision = new Native.IntPtr(); Native.getVersion(major, minor, build, revision); return build.value; } /** * The revision **/ public static int getRevision() { Native.IntPtr major = new Native.IntPtr(), minor = new Native.IntPtr(), build = new Native.IntPtr(), revision = new Native.IntPtr(); Native.getVersion(major, minor, build, revision); return revision.value; } /** * A string representation of the version information. **/ public static String getString() { Native.IntPtr major = new Native.IntPtr(), minor = new Native.IntPtr(), build = new Native.IntPtr(), revision = new Native.IntPtr(); Native.getVersion(major, minor, build, revision); return Integer.toString(major.value) + "." + Integer.toString(minor.value) + "." + Integer.toString(build.value) + "." + Integer.toString(revision.value); } } z3-z3-4.4.1/src/api/java/Z3Exception.java000066400000000000000000000012761260446376700177320ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Z3Exception.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * The exception base class for error reporting from Z3 **/ @SuppressWarnings("serial") public class Z3Exception extends RuntimeException { /** * Constructor. **/ public Z3Exception() { super(); } /** * Constructor. **/ public Z3Exception(String message) { super(message); } /** * Constructor. **/ public Z3Exception(String message, Exception inner) { super(message, inner); } } z3-z3-4.4.1/src/api/java/Z3Object.java000066400000000000000000000040331260446376700171740ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Z3Object.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Internal base class for interfacing with native Z3 objects. Should not be * used externally. **/ public class Z3Object extends IDisposable { /** * Finalizer. **/ protected void finalize() { dispose(); } /** * Disposes of the underlying native Z3 object. **/ public void dispose() { if (m_n_obj != 0) { decRef(m_n_obj); m_n_obj = 0; } if (m_ctx != null) { m_ctx.m_refCount--; m_ctx = null; } } private Context m_ctx = null; private long m_n_obj = 0; Z3Object(Context ctx) { ctx.m_refCount++; m_ctx = ctx; } Z3Object(Context ctx, long obj) { ctx.m_refCount++; m_ctx = ctx; incRef(obj); m_n_obj = obj; } void incRef(long o) { } void decRef(long o) { } void checkNativeObject(long obj) { } long getNativeObject() { return m_n_obj; } void setNativeObject(long value) { if (value != 0) { checkNativeObject(value); incRef(value); } if (m_n_obj != 0) { decRef(m_n_obj); } m_n_obj = value; } static long getNativeObject(Z3Object s) { if (s == null) return 0; return s.getNativeObject(); } Context getContext() { return m_ctx; } static long[] arrayToNative(Z3Object[] a) { if (a == null) return null; long[] an = new long[a.length]; for (int i = 0; i < a.length; i++) an[i] = (a[i] == null) ? 0 : a[i].getNativeObject(); return an; } static int arrayLength(Z3Object[] a) { return (a == null) ? 0 : a.length; } } z3-z3-4.4.1/src/api/java/manifest000066400000000000000000000001021260446376700164300ustar00rootroot00000000000000Manifest-Version: 1.0 Created-By: 4.3.2 (Microsoft Research LTD.) z3-z3-4.4.1/src/api/ml/000077500000000000000000000000001260446376700143755ustar00rootroot00000000000000z3-z3-4.4.1/src/api/ml/META000066400000000000000000000005271260446376700150520ustar00rootroot00000000000000# 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-z3-4.4.1/src/api/ml/Makefile000066400000000000000000000004201260446376700160310ustar00rootroot00000000000000# 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-z3-4.4.1/src/api/ml/README000066400000000000000000000010321260446376700152510ustar00rootroot00000000000000This 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-z3-4.4.1/src/api/ml/z3.ml000066400000000000000000004432501260446376700152730ustar00rootroot00000000000000(** 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 -> ast 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_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_parameters ( 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 Optimize = struct type optimize = z3_native_object type opt = optimize type handle = { opt : opt; h : int } let mk_handle (x : opt) h = { opt = x; h = h } let mk_opt (ctx : context) = let res : opt = { m_ctx = ctx; m_n_obj = null ; inc_ref = Z3native.optimize_inc_ref ; dec_ref = Z3native.optimize_dec_ref } in (z3obj_sno res ctx (Z3native.mk_optimize (context_gno ctx))) ; (z3obj_create res) ; res let get_help ( x : opt ) = Z3native.optimize_get_help (z3obj_gnc x) (z3obj_gno x) let set_parameters ( x : opt ) ( p : Params.params )= Z3native.optimize_set_params (z3obj_gnc x) (z3obj_gno x) (z3obj_gno p) let get_param_descrs ( x : opt ) = Params.ParamDescrs.param_descrs_of_ptr (z3obj_gc x) (Z3native.optimize_get_param_descrs (z3obj_gnc x) (z3obj_gno x)) let add ( x : opt ) ( constraints : expr list ) = let f e = (Z3native.optimize_assert (z3obj_gnc x) (z3obj_gno x) (Expr.gno e)) in List.iter f constraints let add_soft ( x : opt ) ( e : Expr.expr) ( w : string ) ( s : Symbol.symbol ) = mk_handle x (Z3native.optimize_assert_soft (z3obj_gnc x) (z3obj_gno x) (Expr.gno e) w (Symbol.gno s)) let maximize ( x : opt ) ( e : Expr.expr ) = mk_handle x (Z3native.optimize_maximize (z3obj_gnc x) (z3obj_gno x) (Expr.gno e)) let minimize ( x : opt ) ( e : Expr.expr ) = mk_handle x (Z3native.optimize_minimize (z3obj_gnc x) (z3obj_gno x) (Expr.gno e)) let check ( x : opt ) = let r = lbool_of_int (Z3native.optimize_check (z3obj_gnc x) (z3obj_gno x)) in match r with | L_TRUE -> Solver.SATISFIABLE | L_FALSE -> Solver.UNSATISFIABLE | _ -> Solver.UNKNOWN let get_model ( x : opt ) = let q = Z3native.optimize_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_lower ( x : handle ) ( idx : int ) = expr_of_ptr (z3obj_gc x.opt) (Z3native.optimize_get_lower (z3obj_gnc x.opt) (z3obj_gno x.opt) idx) let get_upper ( x : handle ) ( idx : int ) = expr_of_ptr (z3obj_gc x.opt) (Z3native.optimize_get_upper (z3obj_gnc x.opt) (z3obj_gno x.opt) idx) let push ( x : opt ) = Z3native.optimize_push (z3obj_gnc x) (z3obj_gno x) let pop ( x : opt ) = Z3native.optimize_pop (z3obj_gnc x) (z3obj_gno x) let get_reason_unknown ( x : opt ) = Z3native.optimize_get_reason_unknown (z3obj_gnc x) (z3obj_gno x) let to_string ( x : opt ) = Z3native.optimize_to_string (z3obj_gnc x) (z3obj_gno x) let get_statistics ( x : opt ) = let s = Z3native.optimize_get_statistics (z3obj_gnc x) (z3obj_gno x) in (Statistics.create (z3obj_gc x) s) 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-z3-4.4.1/src/api/ml/z3.mli000066400000000000000000003734031260446376700154460ustar00rootroot00000000000000(** 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 -> ast 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_parameters : 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 (** Optimization *) module Optimize : sig type optimize type handle (** Create a Optimize context. *) val mk_opt : context -> optimize (** A string that describes all available optimize solver parameters. *) val get_help : optimize -> string (** Sets the optimize solver parameters. *) val set_parameters : optimize -> Params.params -> unit (** Retrieves parameter descriptions for Optimize solver. *) val get_param_descrs : optimize -> Params.ParamDescrs.param_descrs (** Assert a constraints into the optimize solver. *) val add : optimize -> Expr.expr list -> unit (** Asssert a soft constraint. Supply integer weight and string that identifies a group of soft constraints. *) val add_soft : optimize -> Expr.expr -> string -> Symbol.symbol -> handle (** Add maximization objective. *) val maximize : optimize -> Expr.expr -> handle (** Add minimization objective. *) val minimize : optimize -> Expr.expr -> handle (** Checks whether the assertions in the context are satisfiable and solves objectives. *) val check : optimize -> Solver.status (** Retrieve model from satisfiable context *) val get_model : optimize -> Model.model option (** Retrieve lower bound in current model for handle *) val get_lower : handle -> int -> Expr.expr (** Retrieve upper bound in current model for handle *) val get_upper : handle -> int -> Expr.expr (** Creates a backtracking point. {!pop} *) val push : optimize -> unit (** Backtrack one backtracking point. Note that an exception is thrown if Pop is called without a corresponding [Push] {!push} *) val pop : optimize -> unit (** Retrieve explanation why optimize engine returned status Unknown. *) val get_reason_unknown : optimize -> string (** Retrieve SMT-LIB string representation of optimize object. *) val to_string : optimize -> string (** Retrieve statistics information from the last call to check *) val get_statistics : optimize -> Statistics.statistics 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-z3-4.4.1/src/api/python/000077500000000000000000000000001260446376700153065ustar00rootroot00000000000000z3-z3-4.4.1/src/api/python/README.txt000066400000000000000000000011351260446376700170040ustar00rootroot00000000000000You can learn more about Z3Py at: http://rise4fun.com/Z3Py/tutorial/guide On Windows, you must build Z3 before using Z3Py. To build Z3, you should executed the following command in the Z3 root directory at the Visual Studio Command Prompt msbuild /p:configuration=external If you are using a 64-bit Python interpreter, you should use msbuild /p:configuration=external /p:platform=x64 On Linux and OSX, you must install Z3Py, before trying example.py. To install Z3Py on Linux and OSX, you should execute the following command in the Z3 root directory sudo make install-z3py z3-z3-4.4.1/src/api/python/z3.py000066400000000000000000007674141260446376700162370ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface # # Author: Leonardo de Moura (leonardo) ############################################ """Z3 is a high performance theorem prover developed at Microsoft Research. Z3 is used in many applications such as: software/hardware verification and testing, constraint solving, analysis of hybrid systems, security, biology (in silico analysis), and geometrical problems. Several online tutorials for Z3Py are available at: http://rise4fun.com/Z3Py/tutorial/guide Please send feedback, comments and/or corrections to leonardo@microsoft.com. Your comments are very valuable. Small example: >>> x = Int('x') >>> y = Int('y') >>> s = Solver() >>> s.add(x > 0) >>> s.add(x < 2) >>> s.add(y == x + 1) >>> s.check() sat >>> m = s.model() >>> m[x] 1 >>> m[y] 2 Z3 exceptions: >>> try: ... x = BitVec('x', 32) ... y = Bool('y') ... # the expression x + y is type incorrect ... n = x + y ... except Z3Exception as ex: ... print("failed: %s" % ex) failed: sort mismatch """ from z3core import * from z3types import * from z3consts import * from z3printer import * from fractions import Fraction import sys import io if sys.version < '3': def _is_int(v): return isinstance(v, int) or isinstance(v, long) else: def _is_int(v): return isinstance(v, int) def enable_trace(msg): Z3_enable_trace(msg) def disable_trace(msg): Z3_disable_trace(msg) def get_version_string(): major = ctypes.c_uint(0) minor = ctypes.c_uint(0) build = ctypes.c_uint(0) rev = ctypes.c_uint(0) Z3_get_version(major, minor, build, rev) return "%s.%s.%s" % (major.value, minor.value, build.value) def get_version(): major = ctypes.c_uint(0) minor = ctypes.c_uint(0) build = ctypes.c_uint(0) rev = ctypes.c_uint(0) Z3_get_version(major, minor, build, rev) return (major.value, minor.value, build.value, rev.value) # We use _z3_assert instead of the assert command because we want to # produce nice error messages in Z3Py at rise4fun.com def _z3_assert(cond, msg): if not cond: raise Z3Exception(msg) def open_log(fname): """Log interaction to a file. This function must be invoked immediately after init(). """ Z3_open_log(fname) def append_log(s): """Append user-defined string to interaction log. """ Z3_append_log(s) def to_symbol(s, ctx=None): """Convert an integer or string into a Z3 symbol.""" if isinstance(s, int): return Z3_mk_int_symbol(_get_ctx(ctx).ref(), s) else: return Z3_mk_string_symbol(_get_ctx(ctx).ref(), s) def _symbol2py(ctx, s): """Convert a Z3 symbol back into a Python object. """ if Z3_get_symbol_kind(ctx.ref(), s) == Z3_INT_SYMBOL: return "k!%s" % Z3_get_symbol_int(ctx.ref(), s) else: return Z3_get_symbol_string(ctx.ref(), s) _error_handler_fptr = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_uint) # Hack for having nary functions that can receive one argument that is the # list of arguments. def _get_args(args): try: if len(args) == 1 and (isinstance(args[0], tuple) or isinstance(args[0], list)): return args[0] else: return args except: # len is not necessarily defined when args is not a sequence (use reflection?) return args def _Z3python_error_handler_core(c, e): # Do nothing error handler, just avoid exit(0) # The wrappers in z3core.py will raise a Z3Exception if an error is detected return _Z3Python_error_handler = _error_handler_fptr(_Z3python_error_handler_core) def _to_param_value(val): if isinstance(val, bool): if val == True: return "true" else: return "false" else: return str(val) class Context: """A Context manages all other Z3 objects, global configuration options, etc. Z3Py uses a default global context. For most applications this is sufficient. An application may use multiple Z3 contexts. Objects created in one context cannot be used in another one. However, several objects may be "translated" from one context to another. It is not safe to access Z3 objects from multiple threads. The only exception is the method `interrupt()` that can be used to interrupt() a long computation. The initialization method receives global configuration options for the new context. """ def __init__(self, *args, **kws): if __debug__: _z3_assert(len(args) % 2 == 0, "Argument list must have an even number of elements.") conf = Z3_mk_config() for key in kws: value = kws[key] Z3_set_param_value(conf, str(key).upper(), _to_param_value(value)) prev = None for a in args: if prev == None: prev = a else: Z3_set_param_value(conf, str(prev), _to_param_value(a)) prev = None self.lib = lib() self.ctx = Z3_mk_context_rc(conf) Z3_set_ast_print_mode(self.ctx, Z3_PRINT_SMTLIB2_COMPLIANT) lib().Z3_set_error_handler.restype = None lib().Z3_set_error_handler.argtypes = [ContextObj, _error_handler_fptr] lib().Z3_set_error_handler(self.ctx, _Z3Python_error_handler) Z3_del_config(conf) def __del__(self): self.lib.Z3_del_context(self.ctx) def ref(self): """Return a reference to the actual C pointer to the Z3 context.""" return self.ctx def interrupt(self): """Interrupt a solver performing a satisfiability test, a tactic processing a goal, or simplify functions. This method can be invoked from a thread different from the one executing the interruptable procedure. """ Z3_interrupt(self.ref()) # Global Z3 context _main_ctx = None def main_ctx(): """Return a reference to the global Z3 context. >>> x = Real('x') >>> x.ctx == main_ctx() True >>> c = Context() >>> c == main_ctx() False >>> x2 = Real('x', c) >>> x2.ctx == c True >>> eq(x, x2) False """ global _main_ctx if _main_ctx == None: _main_ctx = Context() return _main_ctx def _get_ctx(ctx): if ctx == None: return main_ctx() else: return ctx def set_param(*args, **kws): """Set Z3 global (or module) parameters. >>> set_param(precision=10) """ if __debug__: _z3_assert(len(args) % 2 == 0, "Argument list must have an even number of elements.") new_kws = {} for k in kws: v = kws[k] if not set_pp_option(k, v): new_kws[k] = v for key in new_kws: value = new_kws[key] Z3_global_param_set(str(key).upper(), _to_param_value(value)) prev = None for a in args: if prev == None: prev = a else: Z3_global_param_set(str(prev), _to_param_value(a)) prev = None def reset_params(): """Reset all global (or module) parameters. """ Z3_global_param_reset_all() def set_option(*args, **kws): """Alias for 'set_param' for backward compatibility. """ return set_param(*args, **kws) def get_param(name): """Return the value of a Z3 global (or module) parameter >>> get_param('nlsat.reorder') 'true' """ ptr = (ctypes.c_char_p * 1)() if Z3_global_param_get(str(name), ptr): r = z3core._to_pystr(ptr[0]) return r raise Z3Exception("failed to retrieve value for '%s'" % name) ######################################### # # ASTs base class # ######################################### # Mark objects that use pretty printer class Z3PPObject: """Superclass for all Z3 objects that have support for pretty printing.""" def use_pp(self): return True class AstRef(Z3PPObject): """AST are Direct Acyclic Graphs (DAGs) used to represent sorts, declarations and expressions.""" def __init__(self, ast, ctx=None): self.ast = ast self.ctx = _get_ctx(ctx) Z3_inc_ref(self.ctx.ref(), self.as_ast()) def __del__(self): Z3_dec_ref(self.ctx.ref(), self.as_ast()) def __str__(self): return obj_to_string(self) def __repr__(self): return obj_to_string(self) def sexpr(self): """Return an string representing the AST node in s-expression notation. >>> x = Int('x') >>> ((x + 1)*x).sexpr() '(* (+ x 1) x)' """ return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) def as_ast(self): """Return a pointer to the corresponding C Z3_ast object.""" return self.ast def get_id(self): """Return unique identifier for object. It can be used for hash-tables and maps.""" return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def ctx_ref(self): """Return a reference to the C context where this AST node is stored.""" return self.ctx.ref() def eq(self, other): """Return `True` if `self` and `other` are structurally identical. >>> x = Int('x') >>> n1 = x + 1 >>> n2 = 1 + x >>> n1.eq(n2) False >>> n1 = simplify(n1) >>> n2 = simplify(n2) >>> n1.eq(n2) True """ if __debug__: _z3_assert(is_ast(other), "Z3 AST expected") return Z3_is_eq_ast(self.ctx_ref(), self.as_ast(), other.as_ast()) def translate(self, target): """Translate `self` to the context `target`. That is, return a copy of `self` in the context `target`. >>> c1 = Context() >>> c2 = Context() >>> x = Int('x', c1) >>> y = Int('y', c2) >>> # Nodes in different contexts can't be mixed. >>> # However, we can translate nodes from one context to another. >>> x.translate(c2) + y x + y """ if __debug__: _z3_assert(isinstance(target, Context), "argument must be a Z3 context") return _to_ast_ref(Z3_translate(self.ctx.ref(), self.as_ast(), target.ref()), target) def hash(self): """Return a hashcode for the `self`. >>> n1 = simplify(Int('x') + 1) >>> n2 = simplify(2 + Int('x') - 1) >>> n1.hash() == n2.hash() True """ return Z3_get_ast_hash(self.ctx_ref(), self.as_ast()) def is_ast(a): """Return `True` if `a` is an AST node. >>> is_ast(10) False >>> is_ast(IntVal(10)) True >>> is_ast(Int('x')) True >>> is_ast(BoolSort()) True >>> is_ast(Function('f', IntSort(), IntSort())) True >>> is_ast("x") False >>> is_ast(Solver()) False """ return isinstance(a, AstRef) def eq(a, b): """Return `True` if `a` and `b` are structurally identical AST nodes. >>> x = Int('x') >>> y = Int('y') >>> eq(x, y) False >>> eq(x + 1, x + 1) True >>> eq(x + 1, 1 + x) False >>> eq(simplify(x + 1), simplify(1 + x)) True """ if __debug__: _z3_assert(is_ast(a) and is_ast(b), "Z3 ASTs expected") return a.eq(b) def _ast_kind(ctx, a): if is_ast(a): a = a.as_ast() return Z3_get_ast_kind(ctx.ref(), a) def _ctx_from_ast_arg_list(args, default_ctx=None): ctx = None for a in args: if is_ast(a) or is_probe(a): if ctx == None: ctx = a.ctx else: if __debug__: _z3_assert(ctx == a.ctx, "Context mismatch") if ctx == None: ctx = default_ctx return ctx def _ctx_from_ast_args(*args): return _ctx_from_ast_arg_list(args) def _to_func_decl_array(args): sz = len(args) _args = (FuncDecl * sz)() for i in range(sz): _args[i] = args[i].as_func_decl() return _args, sz def _to_ast_array(args): sz = len(args) _args = (Ast * sz)() for i in range(sz): _args[i] = args[i].as_ast() return _args, sz def _to_ref_array(ref, args): sz = len(args) _args = (ref * sz)() for i in range(sz): _args[i] = args[i].as_ast() return _args, sz def _to_ast_ref(a, ctx): k = _ast_kind(ctx, a) if k == Z3_SORT_AST: return _to_sort_ref(a, ctx) elif k == Z3_FUNC_DECL_AST: return _to_func_decl_ref(a, ctx) else: return _to_expr_ref(a, ctx) ######################################### # # Sorts # ######################################### def _sort_kind(ctx, s): return Z3_get_sort_kind(ctx.ref(), s) class SortRef(AstRef): """A Sort is essentially a type. Every Z3 expression has a sort. A sort is an AST node.""" def as_ast(self): return Z3_sort_to_ast(self.ctx_ref(), self.ast) def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def kind(self): """Return the Z3 internal kind of a sort. This method can be used to test if `self` is one of the Z3 builtin sorts. >>> b = BoolSort() >>> b.kind() == Z3_BOOL_SORT True >>> b.kind() == Z3_INT_SORT False >>> A = ArraySort(IntSort(), IntSort()) >>> A.kind() == Z3_ARRAY_SORT True >>> A.kind() == Z3_INT_SORT False """ return _sort_kind(self.ctx, self.ast) def subsort(self, other): """Return `True` if `self` is a subsort of `other`. >>> IntSort().subsort(RealSort()) True """ return False def cast(self, val): """Try to cast `val` as an element of sort `self`. This method is used in Z3Py to convert Python objects such as integers, floats, longs and strings into Z3 expressions. >>> x = Int('x') >>> RealSort().cast(x) ToReal(x) """ if __debug__: _z3_assert(is_expr(val), "Z3 expression expected") _z3_assert(self.eq(val.sort()), "Sort mismatch") return val def name(self): """Return the name (string) of sort `self`. >>> BoolSort().name() 'Bool' >>> ArraySort(IntSort(), IntSort()).name() 'Array' """ return _symbol2py(self.ctx, Z3_get_sort_name(self.ctx_ref(), self.ast)) def __eq__(self, other): """Return `True` if `self` and `other` are the same Z3 sort. >>> p = Bool('p') >>> p.sort() == BoolSort() True >>> p.sort() == IntSort() False """ if other == None: return False return Z3_is_eq_sort(self.ctx_ref(), self.ast, other.ast) def __ne__(self, other): """Return `True` if `self` and `other` are not the same Z3 sort. >>> p = Bool('p') >>> p.sort() != BoolSort() False >>> p.sort() != IntSort() True """ return not Z3_is_eq_sort(self.ctx_ref(), self.ast, other.ast) def is_sort(s): """Return `True` if `s` is a Z3 sort. >>> is_sort(IntSort()) True >>> is_sort(Int('x')) False >>> is_expr(Int('x')) True """ return isinstance(s, SortRef) def _to_sort_ref(s, ctx): if __debug__: _z3_assert(isinstance(s, Sort), "Z3 Sort expected") k = _sort_kind(ctx, s) if k == Z3_BOOL_SORT: return BoolSortRef(s, ctx) elif k == Z3_INT_SORT or k == Z3_REAL_SORT: return ArithSortRef(s, ctx) elif k == Z3_BV_SORT: return BitVecSortRef(s, ctx) elif k == Z3_ARRAY_SORT: return ArraySortRef(s, ctx) elif k == Z3_DATATYPE_SORT: return DatatypeSortRef(s, ctx) elif k == Z3_FINITE_DOMAIN_SORT: return FiniteDomainSortRef(s, ctx) elif k == Z3_FLOATING_POINT_SORT: return FPSortRef(s, ctx) elif k == Z3_ROUNDING_MODE_SORT: return FPRMSortRef(s, ctx) return SortRef(s, ctx) def _sort(ctx, a): return _to_sort_ref(Z3_get_sort(ctx.ref(), a), ctx) def DeclareSort(name, ctx=None): """Create a new uninterpred sort named `name`. If `ctx=None`, then the new sort is declared in the global Z3Py context. >>> A = DeclareSort('A') >>> a = Const('a', A) >>> b = Const('b', A) >>> a.sort() == A True >>> b.sort() == A True >>> a == b a == b """ ctx = _get_ctx(ctx) return SortRef(Z3_mk_uninterpreted_sort(ctx.ref(), to_symbol(name, ctx)), ctx) ######################################### # # Function Declarations # ######################################### class FuncDeclRef(AstRef): """Function declaration. Every constant and function have an associated declaration. The declaration assigns a name, a sort (i.e., type), and for function the sort (i.e., type) of each of its arguments. Note that, in Z3, a constant is a function with 0 arguments. """ def as_ast(self): return Z3_func_decl_to_ast(self.ctx_ref(), self.ast) def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def as_func_decl(self): return self.ast def name(self): """Return the name of the function declaration `self`. >>> f = Function('f', IntSort(), IntSort()) >>> f.name() 'f' >>> isinstance(f.name(), str) True """ return _symbol2py(self.ctx, Z3_get_decl_name(self.ctx_ref(), self.ast)) def arity(self): """Return the number of arguments of a function declaration. If `self` is a constant, then `self.arity()` is 0. >>> f = Function('f', IntSort(), RealSort(), BoolSort()) >>> f.arity() 2 """ return int(Z3_get_arity(self.ctx_ref(), self.ast)) def domain(self, i): """Return the sort of the argument `i` of a function declaration. This method assumes that `0 <= i < self.arity()`. >>> f = Function('f', IntSort(), RealSort(), BoolSort()) >>> f.domain(0) Int >>> f.domain(1) Real """ if __debug__: _z3_assert(i < self.arity(), "Index out of bounds") return _to_sort_ref(Z3_get_domain(self.ctx_ref(), self.ast, i), self.ctx) def range(self): """Return the sort of the range of a function declaration. For constants, this is the sort of the constant. >>> f = Function('f', IntSort(), RealSort(), BoolSort()) >>> f.range() Bool """ return _to_sort_ref(Z3_get_range(self.ctx_ref(), self.ast), self.ctx) def kind(self): """Return the internal kind of a function declaration. It can be used to identify Z3 built-in functions such as addition, multiplication, etc. >>> x = Int('x') >>> d = (x + 1).decl() >>> d.kind() == Z3_OP_ADD True >>> d.kind() == Z3_OP_MUL False """ return Z3_get_decl_kind(self.ctx_ref(), self.ast) def __call__(self, *args): """Create a Z3 application expression using the function `self`, and the given arguments. The arguments must be Z3 expressions. This method assumes that the sorts of the elements in `args` match the sorts of the domain. Limited coersion is supported. For example, if args[0] is a Python integer, and the function expects a Z3 integer, then the argument is automatically converted into a Z3 integer. >>> f = Function('f', IntSort(), RealSort(), BoolSort()) >>> x = Int('x') >>> y = Real('y') >>> f(x, y) f(x, y) >>> f(x, x) f(x, ToReal(x)) """ args = _get_args(args) num = len(args) if __debug__: _z3_assert(num == self.arity(), "Incorrect number of arguments to %s" % self) _args = (Ast * num)() saved = [] for i in range(num): # self.domain(i).cast(args[i]) may create a new Z3 expression, # then we must save in 'saved' to prevent it from being garbage collected. tmp = self.domain(i).cast(args[i]) saved.append(tmp) _args[i] = tmp.as_ast() return _to_expr_ref(Z3_mk_app(self.ctx_ref(), self.ast, len(args), _args), self.ctx) def is_func_decl(a): """Return `True` if `a` is a Z3 function declaration. >>> f = Function('f', IntSort(), IntSort()) >>> is_func_decl(f) True >>> x = Real('x') >>> is_func_decl(x) False """ return isinstance(a, FuncDeclRef) def Function(name, *sig): """Create a new Z3 uninterpreted function with the given sorts. >>> f = Function('f', IntSort(), IntSort()) >>> f(f(0)) f(f(0)) """ sig = _get_args(sig) if __debug__: _z3_assert(len(sig) > 0, "At least two arguments expected") arity = len(sig) - 1 rng = sig[arity] if __debug__: _z3_assert(is_sort(rng), "Z3 sort expected") dom = (Sort * arity)() for i in range(arity): if __debug__: _z3_assert(is_sort(sig[i]), "Z3 sort expected") dom[i] = sig[i].ast ctx = rng.ctx return FuncDeclRef(Z3_mk_func_decl(ctx.ref(), to_symbol(name, ctx), arity, dom, rng.ast), ctx) def _to_func_decl_ref(a, ctx): return FuncDeclRef(a, ctx) ######################################### # # Expressions # ######################################### class ExprRef(AstRef): """Constraints, formulas and terms are expressions in Z3. Expressions are ASTs. Every expression has a sort. There are three main kinds of expressions: function applications, quantifiers and bounded variables. A constant is a function application with 0 arguments. For quantifier free problems, all expressions are function applications. """ def as_ast(self): return self.ast def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def sort(self): """Return the sort of expression `self`. >>> x = Int('x') >>> (x + 1).sort() Int >>> y = Real('y') >>> (x + y).sort() Real """ return _sort(self.ctx, self.as_ast()) def sort_kind(self): """Shorthand for `self.sort().kind()`. >>> a = Array('a', IntSort(), IntSort()) >>> a.sort_kind() == Z3_ARRAY_SORT True >>> a.sort_kind() == Z3_INT_SORT False """ return self.sort().kind() def __eq__(self, other): """Return a Z3 expression that represents the constraint `self == other`. If `other` is `None`, then this method simply returns `False`. >>> a = Int('a') >>> b = Int('b') >>> a == b a == b >>> a == None False """ if other == None: return False a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_eq(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __ne__(self, other): """Return a Z3 expression that represents the constraint `self != other`. If `other` is `None`, then this method simply returns `True`. >>> a = Int('a') >>> b = Int('b') >>> a != b a != b >>> a != None True """ if other == None: return True a, b = _coerce_exprs(self, other) _args, sz = _to_ast_array((a, b)) return BoolRef(Z3_mk_distinct(self.ctx_ref(), 2, _args), self.ctx) def decl(self): """Return the Z3 function declaration associated with a Z3 application. >>> f = Function('f', IntSort(), IntSort()) >>> a = Int('a') >>> t = f(a) >>> eq(t.decl(), f) True >>> (a + 1).decl() + """ if __debug__: _z3_assert(is_app(self), "Z3 application expected") return FuncDeclRef(Z3_get_app_decl(self.ctx_ref(), self.as_ast()), self.ctx) def num_args(self): """Return the number of arguments of a Z3 application. >>> a = Int('a') >>> b = Int('b') >>> (a + b).num_args() 2 >>> f = Function('f', IntSort(), IntSort(), IntSort(), IntSort()) >>> t = f(a, b, 0) >>> t.num_args() 3 """ if __debug__: _z3_assert(is_app(self), "Z3 application expected") return int(Z3_get_app_num_args(self.ctx_ref(), self.as_ast())) def arg(self, idx): """Return argument `idx` of the application `self`. This method assumes that `self` is a function application with at least `idx+1` arguments. >>> a = Int('a') >>> b = Int('b') >>> f = Function('f', IntSort(), IntSort(), IntSort(), IntSort()) >>> t = f(a, b, 0) >>> t.arg(0) a >>> t.arg(1) b >>> t.arg(2) 0 """ if __debug__: _z3_assert(is_app(self), "Z3 application expected") _z3_assert(idx < self.num_args(), "Invalid argument index") return _to_expr_ref(Z3_get_app_arg(self.ctx_ref(), self.as_ast(), idx), self.ctx) def children(self): """Return a list containing the children of the given expression >>> a = Int('a') >>> b = Int('b') >>> f = Function('f', IntSort(), IntSort(), IntSort(), IntSort()) >>> t = f(a, b, 0) >>> t.children() [a, b, 0] """ if is_app(self): return [self.arg(i) for i in range(self.num_args())] else: return [] def _to_expr_ref(a, ctx): if isinstance(a, Pattern): return PatternRef(a, ctx) ctx_ref = ctx.ref() k = Z3_get_ast_kind(ctx_ref, a) if k == Z3_QUANTIFIER_AST: return QuantifierRef(a, ctx) sk = Z3_get_sort_kind(ctx_ref, Z3_get_sort(ctx_ref, a)) if sk == Z3_BOOL_SORT: return BoolRef(a, ctx) if sk == Z3_INT_SORT: if k == Z3_NUMERAL_AST: return IntNumRef(a, ctx) return ArithRef(a, ctx) if sk == Z3_REAL_SORT: if k == Z3_NUMERAL_AST: return RatNumRef(a, ctx) if _is_algebraic(ctx, a): return AlgebraicNumRef(a, ctx) return ArithRef(a, ctx) if sk == Z3_BV_SORT: if k == Z3_NUMERAL_AST: return BitVecNumRef(a, ctx) else: return BitVecRef(a, ctx) if sk == Z3_ARRAY_SORT: return ArrayRef(a, ctx) if sk == Z3_DATATYPE_SORT: return DatatypeRef(a, ctx) if sk == Z3_FLOATING_POINT_SORT: if k == Z3_APP_AST and _is_numeral(ctx, a): return FPNumRef(a, ctx) else: return FPRef(a, ctx) if sk == Z3_ROUNDING_MODE_SORT: return FPRMRef(a, ctx) return ExprRef(a, ctx) def _coerce_expr_merge(s, a): if is_expr(a): s1 = a.sort() if s == None: return s1 if s1.eq(s): return s elif s.subsort(s1): return s1 elif s1.subsort(s): return s else: if __debug__: _z3_assert(s1.ctx == s.ctx, "context mismatch") _z3_assert(False, "sort mismatch") else: return s def _coerce_exprs(a, b, ctx=None): if not is_expr(a) and not is_expr(b): a = _py2expr(a, ctx) b = _py2expr(b, ctx) s = None s = _coerce_expr_merge(s, a) s = _coerce_expr_merge(s, b) a = s.cast(a) b = s.cast(b) return (a, b) def _reduce(f, l, a): r = a for e in l: r = f(r, e) return r def _coerce_expr_list(alist, ctx=None): has_expr = False for a in alist: if is_expr(a): has_expr = True break if not has_expr: alist = [ _py2expr(a, ctx) for a in alist ] s = _reduce(_coerce_expr_merge, alist, None) return [ s.cast(a) for a in alist ] def is_expr(a): """Return `True` if `a` is a Z3 expression. >>> a = Int('a') >>> is_expr(a) True >>> is_expr(a + 1) True >>> is_expr(IntSort()) False >>> is_expr(1) False >>> is_expr(IntVal(1)) True >>> x = Int('x') >>> is_expr(ForAll(x, x >= 0)) True """ return isinstance(a, ExprRef) def is_app(a): """Return `True` if `a` is a Z3 function application. Note that, constants are function applications with 0 arguments. >>> a = Int('a') >>> is_app(a) True >>> is_app(a + 1) True >>> is_app(IntSort()) False >>> is_app(1) False >>> is_app(IntVal(1)) True >>> x = Int('x') >>> is_app(ForAll(x, x >= 0)) False """ if not isinstance(a, ExprRef): return False k = _ast_kind(a.ctx, a) return k == Z3_NUMERAL_AST or k == Z3_APP_AST def is_const(a): """Return `True` if `a` is Z3 constant/variable expression. >>> a = Int('a') >>> is_const(a) True >>> is_const(a + 1) False >>> is_const(1) False >>> is_const(IntVal(1)) True >>> x = Int('x') >>> is_const(ForAll(x, x >= 0)) False """ return is_app(a) and a.num_args() == 0 def is_var(a): """Return `True` if `a` is variable. Z3 uses de-Bruijn indices for representing bound variables in quantifiers. >>> x = Int('x') >>> is_var(x) False >>> is_const(x) True >>> f = Function('f', IntSort(), IntSort()) >>> # Z3 replaces x with bound variables when ForAll is executed. >>> q = ForAll(x, f(x) == x) >>> b = q.body() >>> b f(Var(0)) == Var(0) >>> b.arg(1) Var(0) >>> is_var(b.arg(1)) True """ return is_expr(a) and _ast_kind(a.ctx, a) == Z3_VAR_AST def get_var_index(a): """Return the de-Bruijn index of the Z3 bounded variable `a`. >>> x = Int('x') >>> y = Int('y') >>> is_var(x) False >>> is_const(x) True >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> # Z3 replaces x and y with bound variables when ForAll is executed. >>> q = ForAll([x, y], f(x, y) == x + y) >>> q.body() f(Var(1), Var(0)) == Var(1) + Var(0) >>> b = q.body() >>> b.arg(0) f(Var(1), Var(0)) >>> v1 = b.arg(0).arg(0) >>> v2 = b.arg(0).arg(1) >>> v1 Var(1) >>> v2 Var(0) >>> get_var_index(v1) 1 >>> get_var_index(v2) 0 """ if __debug__: _z3_assert(is_var(a), "Z3 bound variable expected") return int(Z3_get_index_value(a.ctx.ref(), a.as_ast())) def is_app_of(a, k): """Return `True` if `a` is an application of the given kind `k`. >>> x = Int('x') >>> n = x + 1 >>> is_app_of(n, Z3_OP_ADD) True >>> is_app_of(n, Z3_OP_MUL) False """ return is_app(a) and a.decl().kind() == k def If(a, b, c, ctx=None): """Create a Z3 if-then-else expression. >>> x = Int('x') >>> y = Int('y') >>> max = If(x > y, x, y) >>> max If(x > y, x, y) >>> simplify(max) If(x <= y, y, x) """ if isinstance(a, Probe) or isinstance(b, Tactic) or isinstance(c, Tactic): return Cond(a, b, c, ctx) else: ctx = _get_ctx(_ctx_from_ast_arg_list([a, b, c], ctx)) s = BoolSort(ctx) a = s.cast(a) b, c = _coerce_exprs(b, c, ctx) if __debug__: _z3_assert(a.ctx == b.ctx, "Context mismatch") return _to_expr_ref(Z3_mk_ite(ctx.ref(), a.as_ast(), b.as_ast(), c.as_ast()), ctx) def Distinct(*args): """Create a Z3 distinct expression. >>> x = Int('x') >>> y = Int('y') >>> Distinct(x, y) x != y >>> z = Int('z') >>> Distinct(x, y, z) Distinct(x, y, z) >>> simplify(Distinct(x, y, z)) Distinct(x, y, z) >>> simplify(Distinct(x, y, z), blast_distinct=True) And(Not(x == y), Not(x == z), Not(y == z)) """ args = _get_args(args) ctx = _ctx_from_ast_arg_list(args) if __debug__: _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression") args = _coerce_expr_list(args, ctx) _args, sz = _to_ast_array(args) return BoolRef(Z3_mk_distinct(ctx.ref(), sz, _args), ctx) def _mk_bin(f, a, b): args = (Ast * 2)() if __debug__: _z3_assert(a.ctx == b.ctx, "Context mismatch") args[0] = a.as_ast() args[1] = b.as_ast() return f(a.ctx.ref(), 2, args) def Const(name, sort): """Create a constant of the given sort. >>> Const('x', IntSort()) x """ if __debug__: _z3_assert(isinstance(sort, SortRef), "Z3 sort expected") ctx = sort.ctx return _to_expr_ref(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), sort.ast), ctx) def Consts(names, sort): """Create a several constants of the given sort. `names` is a string containing the names of all constants to be created. Blank spaces separate the names of different constants. >>> x, y, z = Consts('x y z', IntSort()) >>> x + y + z x + y + z """ if isinstance(names, str): names = names.split(" ") return [Const(name, sort) for name in names] def Var(idx, s): """Create a Z3 free variable. Free variables are used to create quantified formulas. >>> Var(0, IntSort()) Var(0) >>> eq(Var(0, IntSort()), Var(0, BoolSort())) False """ if __debug__: _z3_assert(is_sort(s), "Z3 sort expected") return _to_expr_ref(Z3_mk_bound(s.ctx_ref(), idx, s.ast), s.ctx) def RealVar(idx, ctx=None): """ Create a real free variable. Free variables are used to create quantified formulas. They are also used to create polynomials. >>> RealVar(0) Var(0) """ return Var(idx, RealSort(ctx)) def RealVarVector(n, ctx=None): """ Create a list of Real free variables. The variables have ids: 0, 1, ..., n-1 >>> x0, x1, x2, x3 = RealVarVector(4) >>> x2 Var(2) """ return [ RealVar(i, ctx) for i in range(n) ] ######################################### # # Booleans # ######################################### class BoolSortRef(SortRef): """Boolean sort.""" def cast(self, val): """Try to cast `val` as a Boolean. >>> x = BoolSort().cast(True) >>> x True >>> is_expr(x) True >>> is_expr(True) False >>> x.sort() Bool """ if isinstance(val, bool): return BoolVal(val, self.ctx) if __debug__: _z3_assert(is_expr(val), "True, False or Z3 Boolean expression expected") _z3_assert(self.eq(val.sort()), "Value cannot be converted into a Z3 Boolean value") return val def subsort(self, other): return isinstance(other, ArithSortRef) def is_int(self): return True def is_bool(self): return True class BoolRef(ExprRef): """All Boolean expressions are instances of this class.""" def sort(self): return BoolSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def is_bool(a): """Return `True` if `a` is a Z3 Boolean expression. >>> p = Bool('p') >>> is_bool(p) True >>> q = Bool('q') >>> is_bool(And(p, q)) True >>> x = Real('x') >>> is_bool(x) False >>> is_bool(x == 0) True """ return isinstance(a, BoolRef) def is_true(a): """Return `True` if `a` is the Z3 true expression. >>> p = Bool('p') >>> is_true(p) False >>> is_true(simplify(p == p)) True >>> x = Real('x') >>> is_true(x == 0) False >>> # True is a Python Boolean expression >>> is_true(True) False """ return is_app_of(a, Z3_OP_TRUE) def is_false(a): """Return `True` if `a` is the Z3 false expression. >>> p = Bool('p') >>> is_false(p) False >>> is_false(False) False >>> is_false(BoolVal(False)) True """ return is_app_of(a, Z3_OP_FALSE) def is_and(a): """Return `True` if `a` is a Z3 and expression. >>> p, q = Bools('p q') >>> is_and(And(p, q)) True >>> is_and(Or(p, q)) False """ return is_app_of(a, Z3_OP_AND) def is_or(a): """Return `True` if `a` is a Z3 or expression. >>> p, q = Bools('p q') >>> is_or(Or(p, q)) True >>> is_or(And(p, q)) False """ return is_app_of(a, Z3_OP_OR) def is_not(a): """Return `True` if `a` is a Z3 not expression. >>> p = Bool('p') >>> is_not(p) False >>> is_not(Not(p)) True """ return is_app_of(a, Z3_OP_NOT) def is_eq(a): """Return `True` if `a` is a Z3 equality expression. >>> x, y = Ints('x y') >>> is_eq(x == y) True """ return is_app_of(a, Z3_OP_EQ) def is_distinct(a): """Return `True` if `a` is a Z3 distinct expression. >>> x, y, z = Ints('x y z') >>> is_distinct(x == y) False >>> is_distinct(Distinct(x, y, z)) True """ return is_app_of(a, Z3_OP_DISTINCT) def BoolSort(ctx=None): """Return the Boolean Z3 sort. If `ctx=None`, then the global context is used. >>> BoolSort() Bool >>> p = Const('p', BoolSort()) >>> is_bool(p) True >>> r = Function('r', IntSort(), IntSort(), BoolSort()) >>> r(0, 1) r(0, 1) >>> is_bool(r(0, 1)) True """ ctx = _get_ctx(ctx) return BoolSortRef(Z3_mk_bool_sort(ctx.ref()), ctx) def BoolVal(val, ctx=None): """Return the Boolean value `True` or `False`. If `ctx=None`, then the global context is used. >>> BoolVal(True) True >>> is_true(BoolVal(True)) True >>> is_true(True) False >>> is_false(BoolVal(False)) True """ ctx = _get_ctx(ctx) if val == False: return BoolRef(Z3_mk_false(ctx.ref()), ctx) else: return BoolRef(Z3_mk_true(ctx.ref()), ctx) def Bool(name, ctx=None): """Return a Boolean constant named `name`. If `ctx=None`, then the global context is used. >>> p = Bool('p') >>> q = Bool('q') >>> And(p, q) And(p, q) """ ctx = _get_ctx(ctx) return BoolRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), BoolSort(ctx).ast), ctx) def Bools(names, ctx=None): """Return a tuple of Boolean constants. `names` is a single string containing all names separated by blank spaces. If `ctx=None`, then the global context is used. >>> p, q, r = Bools('p q r') >>> And(p, Or(q, r)) And(p, Or(q, r)) """ ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [Bool(name, ctx) for name in names] def BoolVector(prefix, sz, ctx=None): """Return a list of Boolean constants of size `sz`. The constants are named using the given prefix. If `ctx=None`, then the global context is used. >>> P = BoolVector('p', 3) >>> P [p__0, p__1, p__2] >>> And(P) And(p__0, p__1, p__2) """ return [ Bool('%s__%s' % (prefix, i)) for i in range(sz) ] def FreshBool(prefix='b', ctx=None): """Return a fresh Boolean constant in the given context using the given prefix. If `ctx=None`, then the global context is used. >>> b1 = FreshBool() >>> b2 = FreshBool() >>> eq(b1, b2) False """ ctx = _get_ctx(ctx) return BoolRef(Z3_mk_fresh_const(ctx.ref(), prefix, BoolSort(ctx).ast), ctx) def Implies(a, b, ctx=None): """Create a Z3 implies expression. >>> p, q = Bools('p q') >>> Implies(p, q) Implies(p, q) >>> simplify(Implies(p, q)) Or(Not(p), q) """ ctx = _get_ctx(_ctx_from_ast_arg_list([a, b], ctx)) s = BoolSort(ctx) a = s.cast(a) b = s.cast(b) return BoolRef(Z3_mk_implies(ctx.ref(), a.as_ast(), b.as_ast()), ctx) def Xor(a, b, ctx=None): """Create a Z3 Xor expression. >>> p, q = Bools('p q') >>> Xor(p, q) Xor(p, q) >>> simplify(Xor(p, q)) Not(p) == q """ ctx = _get_ctx(_ctx_from_ast_arg_list([a, b], ctx)) s = BoolSort(ctx) a = s.cast(a) b = s.cast(b) return BoolRef(Z3_mk_xor(ctx.ref(), a.as_ast(), b.as_ast()), ctx) def Not(a, ctx=None): """Create a Z3 not expression or probe. >>> p = Bool('p') >>> Not(Not(p)) Not(Not(p)) >>> simplify(Not(Not(p))) p """ ctx = _get_ctx(_ctx_from_ast_arg_list([a], ctx)) if is_probe(a): # Not is also used to build probes return Probe(Z3_probe_not(ctx.ref(), a.probe), ctx) else: s = BoolSort(ctx) a = s.cast(a) return BoolRef(Z3_mk_not(ctx.ref(), a.as_ast()), ctx) def _has_probe(args): """Return `True` if one of the elements of the given collection is a Z3 probe.""" for arg in args: if is_probe(arg): return True return False def And(*args): """Create a Z3 and-expression or and-probe. >>> p, q, r = Bools('p q r') >>> And(p, q, r) And(p, q, r) >>> P = BoolVector('p', 5) >>> And(P) And(p__0, p__1, p__2, p__3, p__4) """ last_arg = None if len(args) > 0: last_arg = args[len(args)-1] if isinstance(last_arg, Context): ctx = args[len(args)-1] args = args[:len(args)-1] else: ctx = main_ctx() args = _get_args(args) ctx_args = _ctx_from_ast_arg_list(args, ctx) if __debug__: _z3_assert(ctx_args == None or ctx_args == ctx, "context mismatch") _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression or probe") if _has_probe(args): return _probe_and(args, ctx) else: args = _coerce_expr_list(args, ctx) _args, sz = _to_ast_array(args) return BoolRef(Z3_mk_and(ctx.ref(), sz, _args), ctx) def Or(*args): """Create a Z3 or-expression or or-probe. >>> p, q, r = Bools('p q r') >>> Or(p, q, r) Or(p, q, r) >>> P = BoolVector('p', 5) >>> Or(P) Or(p__0, p__1, p__2, p__3, p__4) """ last_arg = None if len(args) > 0: last_arg = args[len(args)-1] if isinstance(last_arg, Context): ctx = args[len(args)-1] args = args[:len(args)-1] else: ctx = main_ctx() args = _get_args(args) ctx_args = _ctx_from_ast_arg_list(args, ctx) if __debug__: _z3_assert(ctx_args == None or ctx_args == ctx, "context mismatch") _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression or probe") if _has_probe(args): return _probe_or(args, ctx) else: args = _coerce_expr_list(args, ctx) _args, sz = _to_ast_array(args) return BoolRef(Z3_mk_or(ctx.ref(), sz, _args), ctx) ######################################### # # Patterns # ######################################### class PatternRef(ExprRef): """Patterns are hints for quantifier instantiation. See http://rise4fun.com/Z3Py/tutorial/advanced for more details. """ def as_ast(self): return Z3_pattern_to_ast(self.ctx_ref(), self.ast) def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def is_pattern(a): """Return `True` if `a` is a Z3 pattern (hint for quantifier instantiation. See http://rise4fun.com/Z3Py/tutorial/advanced for more details. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0, patterns = [ f(x) ]) >>> q ForAll(x, f(x) == 0) >>> q.num_patterns() 1 >>> is_pattern(q.pattern(0)) True >>> q.pattern(0) f(Var(0)) """ return isinstance(a, PatternRef) def MultiPattern(*args): """Create a Z3 multi-pattern using the given expressions `*args` See http://rise4fun.com/Z3Py/tutorial/advanced for more details. >>> f = Function('f', IntSort(), IntSort()) >>> g = Function('g', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) != g(x), patterns = [ MultiPattern(f(x), g(x)) ]) >>> q ForAll(x, f(x) != g(x)) >>> q.num_patterns() 1 >>> is_pattern(q.pattern(0)) True >>> q.pattern(0) MultiPattern(f(Var(0)), g(Var(0))) """ if __debug__: _z3_assert(len(args) > 0, "At least one argument expected") _z3_assert(all([ is_expr(a) for a in args ]), "Z3 expressions expected") ctx = args[0].ctx args, sz = _to_ast_array(args) return PatternRef(Z3_mk_pattern(ctx.ref(), sz, args), ctx) def _to_pattern(arg): if is_pattern(arg): return arg else: return MultiPattern(arg) ######################################### # # Quantifiers # ######################################### class QuantifierRef(BoolRef): """Universally and Existentially quantified formulas.""" def as_ast(self): return self.ast def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def sort(self): """Return the Boolean sort.""" return BoolSort(self.ctx) def is_forall(self): """Return `True` if `self` is a universal quantifier. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> q.is_forall() True >>> q = Exists(x, f(x) != 0) >>> q.is_forall() False """ return Z3_is_quantifier_forall(self.ctx_ref(), self.ast) def weight(self): """Return the weight annotation of `self`. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> q.weight() 1 >>> q = ForAll(x, f(x) == 0, weight=10) >>> q.weight() 10 """ return int(Z3_get_quantifier_weight(self.ctx_ref(), self.ast)) def num_patterns(self): """Return the number of patterns (i.e., quantifier instantiation hints) in `self`. >>> f = Function('f', IntSort(), IntSort()) >>> g = Function('g', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) != g(x), patterns = [ f(x), g(x) ]) >>> q.num_patterns() 2 """ return int(Z3_get_quantifier_num_patterns(self.ctx_ref(), self.ast)) def pattern(self, idx): """Return a pattern (i.e., quantifier instantiation hints) in `self`. >>> f = Function('f', IntSort(), IntSort()) >>> g = Function('g', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) != g(x), patterns = [ f(x), g(x) ]) >>> q.num_patterns() 2 >>> q.pattern(0) f(Var(0)) >>> q.pattern(1) g(Var(0)) """ if __debug__: _z3_assert(idx < self.num_patterns(), "Invalid pattern idx") return PatternRef(Z3_get_quantifier_pattern_ast(self.ctx_ref(), self.ast, idx), self.ctx) def num_no_patterns(self): """Return the number of no-patterns.""" return Z3_get_quantifier_num_no_patterns(self.ctx_ref(), self.ast) def no_pattern(self, idx): """Return a no-pattern.""" if __debug__: _z3_assert(idx < self.num_no_patterns(), "Invalid no-pattern idx") return _to_expr_ref(Z3_get_quantifier_no_pattern_ast(self.ctx_ref(), self.ast, idx), self.ctx) def body(self): """Return the expression being quantified. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> q.body() f(Var(0)) == 0 """ return _to_expr_ref(Z3_get_quantifier_body(self.ctx_ref(), self.ast), self.ctx) def num_vars(self): """Return the number of variables bounded by this quantifier. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> x = Int('x') >>> y = Int('y') >>> q = ForAll([x, y], f(x, y) >= x) >>> q.num_vars() 2 """ return int(Z3_get_quantifier_num_bound(self.ctx_ref(), self.ast)) def var_name(self, idx): """Return a string representing a name used when displaying the quantifier. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> x = Int('x') >>> y = Int('y') >>> q = ForAll([x, y], f(x, y) >= x) >>> q.var_name(0) 'x' >>> q.var_name(1) 'y' """ if __debug__: _z3_assert(idx < self.num_vars(), "Invalid variable idx") return _symbol2py(self.ctx, Z3_get_quantifier_bound_name(self.ctx_ref(), self.ast, idx)) def var_sort(self, idx): """Return the sort of a bound variable. >>> f = Function('f', IntSort(), RealSort(), IntSort()) >>> x = Int('x') >>> y = Real('y') >>> q = ForAll([x, y], f(x, y) >= x) >>> q.var_sort(0) Int >>> q.var_sort(1) Real """ if __debug__: _z3_assert(idx < self.num_vars(), "Invalid variable idx") return SortRef(Z3_get_quantifier_bound_sort(self.ctx_ref(), self.ast, idx), self.ctx) def children(self): """Return a list containing a single element self.body() >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> q.children() [f(Var(0)) == 0] """ return [ self.body() ] def is_quantifier(a): """Return `True` if `a` is a Z3 quantifier. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> is_quantifier(q) True >>> is_quantifier(f(x)) False """ return isinstance(a, QuantifierRef) def _mk_quantifier(is_forall, vs, body, weight=1, qid="", skid="", patterns=[], no_patterns=[]): if __debug__: _z3_assert(is_bool(body), "Z3 expression expected") _z3_assert(is_const(vs) or (len(vs) > 0 and all([ is_const(v) for v in vs])), "Invalid bounded variable(s)") _z3_assert(all([is_pattern(a) or is_expr(a) for a in patterns]), "Z3 patterns expected") _z3_assert(all([is_expr(p) for p in no_patterns]), "no patterns are Z3 expressions") ctx = body.ctx if is_app(vs): vs = [vs] num_vars = len(vs) _vs = (Ast * num_vars)() for i in range(num_vars): ## TODO: Check if is constant _vs[i] = vs[i].as_ast() patterns = [ _to_pattern(p) for p in patterns ] num_pats = len(patterns) _pats = (Pattern * num_pats)() for i in range(num_pats): _pats[i] = patterns[i].ast _no_pats, num_no_pats = _to_ast_array(no_patterns) qid = to_symbol(qid, ctx) skid = to_symbol(skid, ctx) return QuantifierRef(Z3_mk_quantifier_const_ex(ctx.ref(), is_forall, weight, qid, skid, num_vars, _vs, num_pats, _pats, num_no_pats, _no_pats, body.as_ast()), ctx) def ForAll(vs, body, weight=1, qid="", skid="", patterns=[], no_patterns=[]): """Create a Z3 forall formula. The parameters `weight`, `qif`, `skid`, `patterns` and `no_patterns` are optional annotations. See http://rise4fun.com/Z3Py/tutorial/advanced for more details. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> x = Int('x') >>> y = Int('y') >>> ForAll([x, y], f(x, y) >= x) ForAll([x, y], f(x, y) >= x) >>> ForAll([x, y], f(x, y) >= x, patterns=[ f(x, y) ]) ForAll([x, y], f(x, y) >= x) >>> ForAll([x, y], f(x, y) >= x, weight=10) ForAll([x, y], f(x, y) >= x) """ return _mk_quantifier(True, vs, body, weight, qid, skid, patterns, no_patterns) def Exists(vs, body, weight=1, qid="", skid="", patterns=[], no_patterns=[]): """Create a Z3 exists formula. The parameters `weight`, `qif`, `skid`, `patterns` and `no_patterns` are optional annotations. See http://rise4fun.com/Z3Py/tutorial/advanced for more details. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> x = Int('x') >>> y = Int('y') >>> q = Exists([x, y], f(x, y) >= x, skid="foo") >>> q Exists([x, y], f(x, y) >= x) >>> is_quantifier(q) True >>> r = Tactic('nnf')(q).as_expr() >>> is_quantifier(r) False """ return _mk_quantifier(False, vs, body, weight, qid, skid, patterns, no_patterns) ######################################### # # Arithmetic # ######################################### class ArithSortRef(SortRef): """Real and Integer sorts.""" def is_real(self): """Return `True` if `self` is of the sort Real. >>> x = Real('x') >>> x.is_real() True >>> (x + 1).is_real() True >>> x = Int('x') >>> x.is_real() False """ return self.kind() == Z3_REAL_SORT def is_int(self): """Return `True` if `self` is of the sort Integer. >>> x = Int('x') >>> x.is_int() True >>> (x + 1).is_int() True >>> x = Real('x') >>> x.is_int() False """ return self.kind() == Z3_INT_SORT def subsort(self, other): """Return `True` if `self` is a subsort of `other`.""" return self.is_int() and is_arith_sort(other) and other.is_real() def cast(self, val): """Try to cast `val` as an Integer or Real. >>> IntSort().cast(10) 10 >>> is_int(IntSort().cast(10)) True >>> is_int(10) False >>> RealSort().cast(10) 10 >>> is_real(RealSort().cast(10)) True """ if is_expr(val): if __debug__: _z3_assert(self.ctx == val.ctx, "Context mismatch") val_s = val.sort() if self.eq(val_s): return val if val_s.is_int() and self.is_real(): return ToReal(val) if val_s.is_bool() and self.is_int(): return If(val, 1, 0) if val_s.is_bool() and self.is_real(): return ToReal(If(val, 1, 0)) if __debug__: _z3_assert(False, "Z3 Integer/Real expression expected" ) else: if self.is_int(): return IntVal(val, self.ctx) if self.is_real(): return RealVal(val, self.ctx) if __debug__: _z3_assert(False, "int, long, float, string (numeral), or Z3 Integer/Real expression expected") def is_arith_sort(s): """Return `True` if s is an arithmetical sort (type). >>> is_arith_sort(IntSort()) True >>> is_arith_sort(RealSort()) True >>> is_arith_sort(BoolSort()) False >>> n = Int('x') + 1 >>> is_arith_sort(n.sort()) True """ return isinstance(s, ArithSortRef) class ArithRef(ExprRef): """Integer and Real expressions.""" def sort(self): """Return the sort (type) of the arithmetical expression `self`. >>> Int('x').sort() Int >>> (Real('x') + 1).sort() Real """ return ArithSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def is_int(self): """Return `True` if `self` is an integer expression. >>> x = Int('x') >>> x.is_int() True >>> (x + 1).is_int() True >>> y = Real('y') >>> (x + y).is_int() False """ return self.sort().is_int() def is_real(self): """Return `True` if `self` is an real expression. >>> x = Real('x') >>> x.is_real() True >>> (x + 1).is_real() True """ return self.sort().is_real() def __add__(self, other): """Create the Z3 expression `self + other`. >>> x = Int('x') >>> y = Int('y') >>> x + y x + y >>> (x + y).sort() Int """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_add, a, b), self.ctx) def __radd__(self, other): """Create the Z3 expression `other + self`. >>> x = Int('x') >>> 10 + x 10 + x """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_add, b, a), self.ctx) def __mul__(self, other): """Create the Z3 expression `self * other`. >>> x = Real('x') >>> y = Real('y') >>> x * y x*y >>> (x * y).sort() Real """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_mul, a, b), self.ctx) def __rmul__(self, other): """Create the Z3 expression `other * self`. >>> x = Real('x') >>> 10 * x 10*x """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_mul, b, a), self.ctx) def __sub__(self, other): """Create the Z3 expression `self - other`. >>> x = Int('x') >>> y = Int('y') >>> x - y x - y >>> (x - y).sort() Int """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_sub, a, b), self.ctx) def __rsub__(self, other): """Create the Z3 expression `other - self`. >>> x = Int('x') >>> 10 - x 10 - x """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_sub, b, a), self.ctx) def __pow__(self, other): """Create the Z3 expression `self**other` (** is the power operator). >>> x = Real('x') >>> x**3 x**3 >>> (x**3).sort() Real >>> simplify(IntVal(2)**8) 256 """ a, b = _coerce_exprs(self, other) return ArithRef(Z3_mk_power(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rpow__(self, other): """Create the Z3 expression `other**self` (** is the power operator). >>> x = Real('x') >>> 2**x 2**x >>> (2**x).sort() Real >>> simplify(2**IntVal(8)) 256 """ a, b = _coerce_exprs(self, other) return ArithRef(Z3_mk_power(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __div__(self, other): """Create the Z3 expression `other/self`. >>> x = Int('x') >>> y = Int('y') >>> x/y x/y >>> (x/y).sort() Int >>> (x/y).sexpr() '(div x y)' >>> x = Real('x') >>> y = Real('y') >>> x/y x/y >>> (x/y).sort() Real >>> (x/y).sexpr() '(/ x y)' """ a, b = _coerce_exprs(self, other) return ArithRef(Z3_mk_div(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __truediv__(self, other): """Create the Z3 expression `other/self`.""" return self.__div__(other) def __rdiv__(self, other): """Create the Z3 expression `other/self`. >>> x = Int('x') >>> 10/x 10/x >>> (10/x).sexpr() '(div 10 x)' >>> x = Real('x') >>> 10/x 10/x >>> (10/x).sexpr() '(/ 10.0 x)' """ a, b = _coerce_exprs(self, other) return ArithRef(Z3_mk_div(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __rtruediv__(self, other): """Create the Z3 expression `other/self`.""" return self.__rdiv__(other) def __mod__(self, other): """Create the Z3 expression `other%self`. >>> x = Int('x') >>> y = Int('y') >>> x % y x%y >>> simplify(IntVal(10) % IntVal(3)) 1 """ a, b = _coerce_exprs(self, other) if __debug__: _z3_assert(a.is_int(), "Z3 integer expression expected") return ArithRef(Z3_mk_mod(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rmod__(self, other): """Create the Z3 expression `other%self`. >>> x = Int('x') >>> 10 % x 10%x """ a, b = _coerce_exprs(self, other) if __debug__: _z3_assert(a.is_int(), "Z3 integer expression expected") return ArithRef(Z3_mk_mod(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __neg__(self): """Return an expression representing `-self`. >>> x = Int('x') >>> -x -x >>> simplify(-(-x)) x """ return ArithRef(Z3_mk_unary_minus(self.ctx_ref(), self.as_ast()), self.ctx) def __pos__(self): """Return `self`. >>> x = Int('x') >>> +x x """ return self def __le__(self, other): """Create the Z3 expression `other <= self`. >>> x, y = Ints('x y') >>> x <= y x <= y >>> y = Real('y') >>> x <= y ToReal(x) <= y """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_le(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __lt__(self, other): """Create the Z3 expression `other < self`. >>> x, y = Ints('x y') >>> x < y x < y >>> y = Real('y') >>> x < y ToReal(x) < y """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_lt(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __gt__(self, other): """Create the Z3 expression `other > self`. >>> x, y = Ints('x y') >>> x > y x > y >>> y = Real('y') >>> x > y ToReal(x) > y """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_gt(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __ge__(self, other): """Create the Z3 expression `other >= self`. >>> x, y = Ints('x y') >>> x >= y x >= y >>> y = Real('y') >>> x >= y ToReal(x) >= y """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_ge(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def is_arith(a): """Return `True` if `a` is an arithmetical expression. >>> x = Int('x') >>> is_arith(x) True >>> is_arith(x + 1) True >>> is_arith(1) False >>> is_arith(IntVal(1)) True >>> y = Real('y') >>> is_arith(y) True >>> is_arith(y + 1) True """ return isinstance(a, ArithRef) def is_int(a): """Return `True` if `a` is an integer expression. >>> x = Int('x') >>> is_int(x + 1) True >>> is_int(1) False >>> is_int(IntVal(1)) True >>> y = Real('y') >>> is_int(y) False >>> is_int(y + 1) False """ return is_arith(a) and a.is_int() def is_real(a): """Return `True` if `a` is a real expression. >>> x = Int('x') >>> is_real(x + 1) False >>> y = Real('y') >>> is_real(y) True >>> is_real(y + 1) True >>> is_real(1) False >>> is_real(RealVal(1)) True """ return is_arith(a) and a.is_real() def _is_numeral(ctx, a): return Z3_is_numeral_ast(ctx.ref(), a) def _is_algebraic(ctx, a): return Z3_is_algebraic_number(ctx.ref(), a) def is_int_value(a): """Return `True` if `a` is an integer value of sort Int. >>> is_int_value(IntVal(1)) True >>> is_int_value(1) False >>> is_int_value(Int('x')) False >>> n = Int('x') + 1 >>> n x + 1 >>> n.arg(1) 1 >>> is_int_value(n.arg(1)) True >>> is_int_value(RealVal("1/3")) False >>> is_int_value(RealVal(1)) False """ return is_arith(a) and a.is_int() and _is_numeral(a.ctx, a.as_ast()) def is_rational_value(a): """Return `True` if `a` is rational value of sort Real. >>> is_rational_value(RealVal(1)) True >>> is_rational_value(RealVal("3/5")) True >>> is_rational_value(IntVal(1)) False >>> is_rational_value(1) False >>> n = Real('x') + 1 >>> n.arg(1) 1 >>> is_rational_value(n.arg(1)) True >>> is_rational_value(Real('x')) False """ return is_arith(a) and a.is_real() and _is_numeral(a.ctx, a.as_ast()) def is_algebraic_value(a): """Return `True` if `a` is an algerbraic value of sort Real. >>> is_algebraic_value(RealVal("3/5")) False >>> n = simplify(Sqrt(2)) >>> n 1.4142135623? >>> is_algebraic_value(n) True """ return is_arith(a) and a.is_real() and _is_algebraic(a.ctx, a.as_ast()) def is_add(a): """Return `True` if `a` is an expression of the form b + c. >>> x, y = Ints('x y') >>> is_add(x + y) True >>> is_add(x - y) False """ return is_app_of(a, Z3_OP_ADD) def is_mul(a): """Return `True` if `a` is an expression of the form b * c. >>> x, y = Ints('x y') >>> is_mul(x * y) True >>> is_mul(x - y) False """ return is_app_of(a, Z3_OP_MUL) def is_sub(a): """Return `True` if `a` is an expression of the form b - c. >>> x, y = Ints('x y') >>> is_sub(x - y) True >>> is_sub(x + y) False """ return is_app_of(a, Z3_OP_SUB) def is_div(a): """Return `True` if `a` is an expression of the form b / c. >>> x, y = Reals('x y') >>> is_div(x / y) True >>> is_div(x + y) False >>> x, y = Ints('x y') >>> is_div(x / y) False >>> is_idiv(x / y) True """ return is_app_of(a, Z3_OP_DIV) def is_idiv(a): """Return `True` if `a` is an expression of the form b div c. >>> x, y = Ints('x y') >>> is_idiv(x / y) True >>> is_idiv(x + y) False """ return is_app_of(a, Z3_OP_IDIV) def is_mod(a): """Return `True` if `a` is an expression of the form b % c. >>> x, y = Ints('x y') >>> is_mod(x % y) True >>> is_mod(x + y) False """ return is_app_of(a, Z3_OP_MOD) def is_le(a): """Return `True` if `a` is an expression of the form b <= c. >>> x, y = Ints('x y') >>> is_le(x <= y) True >>> is_le(x < y) False """ return is_app_of(a, Z3_OP_LE) def is_lt(a): """Return `True` if `a` is an expression of the form b < c. >>> x, y = Ints('x y') >>> is_lt(x < y) True >>> is_lt(x == y) False """ return is_app_of(a, Z3_OP_LT) def is_ge(a): """Return `True` if `a` is an expression of the form b >= c. >>> x, y = Ints('x y') >>> is_ge(x >= y) True >>> is_ge(x == y) False """ return is_app_of(a, Z3_OP_GE) def is_gt(a): """Return `True` if `a` is an expression of the form b > c. >>> x, y = Ints('x y') >>> is_gt(x > y) True >>> is_gt(x == y) False """ return is_app_of(a, Z3_OP_GT) def is_is_int(a): """Return `True` if `a` is an expression of the form IsInt(b). >>> x = Real('x') >>> is_is_int(IsInt(x)) True >>> is_is_int(x) False """ return is_app_of(a, Z3_OP_IS_INT) def is_to_real(a): """Return `True` if `a` is an expression of the form ToReal(b). >>> x = Int('x') >>> n = ToReal(x) >>> n ToReal(x) >>> is_to_real(n) True >>> is_to_real(x) False """ return is_app_of(a, Z3_OP_TO_REAL) def is_to_int(a): """Return `True` if `a` is an expression of the form ToInt(b). >>> x = Real('x') >>> n = ToInt(x) >>> n ToInt(x) >>> is_to_int(n) True >>> is_to_int(x) False """ return is_app_of(a, Z3_OP_TO_INT) class IntNumRef(ArithRef): """Integer values.""" def as_long(self): """Return a Z3 integer numeral as a Python long (bignum) numeral. >>> v = IntVal(1) >>> v + 1 1 + 1 >>> v.as_long() + 1 2 """ if __debug__: _z3_assert(self.is_int(), "Integer value expected") return int(self.as_string()) def as_string(self): """Return a Z3 integer numeral as a Python string. >>> v = IntVal(100) >>> v.as_string() '100' """ return Z3_get_numeral_string(self.ctx_ref(), self.as_ast()) class RatNumRef(ArithRef): """Rational values.""" def numerator(self): """ Return the numerator of a Z3 rational numeral. >>> is_rational_value(RealVal("3/5")) True >>> n = RealVal("3/5") >>> n.numerator() 3 >>> is_rational_value(Q(3,5)) True >>> Q(3,5).numerator() 3 """ return IntNumRef(Z3_get_numerator(self.ctx_ref(), self.as_ast()), self.ctx) def denominator(self): """ Return the denominator of a Z3 rational numeral. >>> is_rational_value(Q(3,5)) True >>> n = Q(3,5) >>> n.denominator() 5 """ return IntNumRef(Z3_get_denominator(self.ctx_ref(), self.as_ast()), self.ctx) def numerator_as_long(self): """ Return the numerator as a Python long. >>> v = RealVal(10000000000) >>> v 10000000000 >>> v + 1 10000000000 + 1 >>> v.numerator_as_long() + 1 == 10000000001 True """ return self.numerator().as_long() def denominator_as_long(self): """ Return the denominator as a Python long. >>> v = RealVal("1/3") >>> v 1/3 >>> v.denominator_as_long() 3 """ return self.denominator().as_long() def as_decimal(self, prec): """ Return a Z3 rational value as a string in decimal notation using at most `prec` decimal places. >>> v = RealVal("1/5") >>> v.as_decimal(3) '0.2' >>> v = RealVal("1/3") >>> v.as_decimal(3) '0.333?' """ return Z3_get_numeral_decimal_string(self.ctx_ref(), self.as_ast(), prec) def as_string(self): """Return a Z3 rational numeral as a Python string. >>> v = Q(3,6) >>> v.as_string() '1/2' """ return Z3_get_numeral_string(self.ctx_ref(), self.as_ast()) def as_fraction(self): """Return a Z3 rational as a Python Fraction object. >>> v = RealVal("1/5") >>> v.as_fraction() Fraction(1, 5) """ return Fraction(self.numerator_as_long(), self.denominator_as_long()) class AlgebraicNumRef(ArithRef): """Algebraic irrational values.""" def approx(self, precision=10): """Return a Z3 rational number that approximates the algebraic number `self`. The result `r` is such that |r - self| <= 1/10^precision >>> x = simplify(Sqrt(2)) >>> x.approx(20) 6838717160008073720548335/4835703278458516698824704 >>> x.approx(5) 2965821/2097152 """ return RatNumRef(Z3_get_algebraic_number_upper(self.ctx_ref(), self.as_ast(), precision), self.ctx) def as_decimal(self, prec): """Return a string representation of the algebraic number `self` in decimal notation using `prec` decimal places >>> x = simplify(Sqrt(2)) >>> x.as_decimal(10) '1.4142135623?' >>> x.as_decimal(20) '1.41421356237309504880?' """ return Z3_get_numeral_decimal_string(self.ctx_ref(), self.as_ast(), prec) def _py2expr(a, ctx=None): if isinstance(a, bool): return BoolVal(a, ctx) if _is_int(a): return IntVal(a, ctx) if isinstance(a, float): return RealVal(a, ctx) if __debug__: _z3_assert(False, "Python bool, int, long or float expected") def IntSort(ctx=None): """Return the interger sort in the given context. If `ctx=None`, then the global context is used. >>> IntSort() Int >>> x = Const('x', IntSort()) >>> is_int(x) True >>> x.sort() == IntSort() True >>> x.sort() == BoolSort() False """ ctx = _get_ctx(ctx) return ArithSortRef(Z3_mk_int_sort(ctx.ref()), ctx) def RealSort(ctx=None): """Return the real sort in the given context. If `ctx=None`, then the global context is used. >>> RealSort() Real >>> x = Const('x', RealSort()) >>> is_real(x) True >>> is_int(x) False >>> x.sort() == RealSort() True """ ctx = _get_ctx(ctx) return ArithSortRef(Z3_mk_real_sort(ctx.ref()), ctx) def _to_int_str(val): if isinstance(val, float): return str(int(val)) elif isinstance(val, bool): if val: return "1" else: return "0" elif _is_int(val): return str(val) elif isinstance(val, str): return val if __debug__: _z3_assert(False, "Python value cannot be used as a Z3 integer") def IntVal(val, ctx=None): """Return a Z3 integer value. If `ctx=None`, then the global context is used. >>> IntVal(1) 1 >>> IntVal("100") 100 """ ctx = _get_ctx(ctx) return IntNumRef(Z3_mk_numeral(ctx.ref(), _to_int_str(val), IntSort(ctx).ast), ctx) def RealVal(val, ctx=None): """Return a Z3 real value. `val` may be a Python int, long, float or string representing a number in decimal or rational notation. If `ctx=None`, then the global context is used. >>> RealVal(1) 1 >>> RealVal(1).sort() Real >>> RealVal("3/5") 3/5 >>> RealVal("1.5") 3/2 """ ctx = _get_ctx(ctx) return RatNumRef(Z3_mk_numeral(ctx.ref(), str(val), RealSort(ctx).ast), ctx) def RatVal(a, b, ctx=None): """Return a Z3 rational a/b. If `ctx=None`, then the global context is used. >>> RatVal(3,5) 3/5 >>> RatVal(3,5).sort() Real """ if __debug__: _z3_assert(_is_int(a) or isinstance(a, str), "First argument cannot be converted into an integer") _z3_assert(_is_int(b) or isinstance(b, str), "Second argument cannot be converted into an integer") return simplify(RealVal(a, ctx)/RealVal(b, ctx)) def Q(a, b, ctx=None): """Return a Z3 rational a/b. If `ctx=None`, then the global context is used. >>> Q(3,5) 3/5 >>> Q(3,5).sort() Real """ return simplify(RatVal(a, b)) def Int(name, ctx=None): """Return an integer constant named `name`. If `ctx=None`, then the global context is used. >>> x = Int('x') >>> is_int(x) True >>> is_int(x + 1) True """ ctx = _get_ctx(ctx) return ArithRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), IntSort(ctx).ast), ctx) def Ints(names, ctx=None): """Return a tuple of Integer constants. >>> x, y, z = Ints('x y z') >>> Sum(x, y, z) x + y + z """ ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [Int(name, ctx) for name in names] def IntVector(prefix, sz, ctx=None): """Return a list of integer constants of size `sz`. >>> X = IntVector('x', 3) >>> X [x__0, x__1, x__2] >>> Sum(X) x__0 + x__1 + x__2 """ return [ Int('%s__%s' % (prefix, i)) for i in range(sz) ] def FreshInt(prefix='x', ctx=None): """Return a fresh integer constant in the given context using the given prefix. >>> x = FreshInt() >>> y = FreshInt() >>> eq(x, y) False >>> x.sort() Int """ ctx = _get_ctx(ctx) return ArithRef(Z3_mk_fresh_const(ctx.ref(), prefix, IntSort(ctx).ast), ctx) def Real(name, ctx=None): """Return a real constant named `name`. If `ctx=None`, then the global context is used. >>> x = Real('x') >>> is_real(x) True >>> is_real(x + 1) True """ ctx = _get_ctx(ctx) return ArithRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), RealSort(ctx).ast), ctx) def Reals(names, ctx=None): """Return a tuple of real constants. >>> x, y, z = Reals('x y z') >>> Sum(x, y, z) x + y + z >>> Sum(x, y, z).sort() Real """ ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [Real(name, ctx) for name in names] def RealVector(prefix, sz, ctx=None): """Return a list of real constants of size `sz`. >>> X = RealVector('x', 3) >>> X [x__0, x__1, x__2] >>> Sum(X) x__0 + x__1 + x__2 >>> Sum(X).sort() Real """ return [ Real('%s__%s' % (prefix, i)) for i in range(sz) ] def FreshReal(prefix='b', ctx=None): """Return a fresh real constant in the given context using the given prefix. >>> x = FreshReal() >>> y = FreshReal() >>> eq(x, y) False >>> x.sort() Real """ ctx = _get_ctx(ctx) return ArithRef(Z3_mk_fresh_const(ctx.ref(), prefix, RealSort(ctx).ast), ctx) def ToReal(a): """ Return the Z3 expression ToReal(a). >>> x = Int('x') >>> x.sort() Int >>> n = ToReal(x) >>> n ToReal(x) >>> n.sort() Real """ if __debug__: _z3_assert(a.is_int(), "Z3 integer expression expected.") ctx = a.ctx return ArithRef(Z3_mk_int2real(ctx.ref(), a.as_ast()), ctx) def ToInt(a): """ Return the Z3 expression ToInt(a). >>> x = Real('x') >>> x.sort() Real >>> n = ToInt(x) >>> n ToInt(x) >>> n.sort() Int """ if __debug__: _z3_assert(a.is_real(), "Z3 real expression expected.") ctx = a.ctx return ArithRef(Z3_mk_real2int(ctx.ref(), a.as_ast()), ctx) def IsInt(a): """ Return the Z3 predicate IsInt(a). >>> x = Real('x') >>> IsInt(x + "1/2") IsInt(x + 1/2) >>> solve(IsInt(x + "1/2"), x > 0, x < 1) [x = 1/2] >>> solve(IsInt(x + "1/2"), x > 0, x < 1, x != "1/2") no solution """ if __debug__: _z3_assert(a.is_real(), "Z3 real expression expected.") ctx = a.ctx return BoolRef(Z3_mk_is_int(ctx.ref(), a.as_ast()), ctx) def Sqrt(a, ctx=None): """ Return a Z3 expression which represents the square root of a. >>> x = Real('x') >>> Sqrt(x) x**(1/2) """ if not is_expr(a): ctx = _get_ctx(ctx) a = RealVal(a, ctx) return a ** "1/2" def Cbrt(a, ctx=None): """ Return a Z3 expression which represents the cubic root of a. >>> x = Real('x') >>> Cbrt(x) x**(1/3) """ if not is_expr(a): ctx = _get_ctx(ctx) a = RealVal(a, ctx) return a ** "1/3" ######################################### # # Bit-Vectors # ######################################### class BitVecSortRef(SortRef): """Bit-vector sort.""" def size(self): """Return the size (number of bits) of the bit-vector sort `self`. >>> b = BitVecSort(32) >>> b.size() 32 """ return int(Z3_get_bv_sort_size(self.ctx_ref(), self.ast)) def subsort(self, other): return is_bv_sort(other) and self.size() < other.size() def cast(self, val): """Try to cast `val` as a Bit-Vector. >>> b = BitVecSort(32) >>> b.cast(10) 10 >>> b.cast(10).sexpr() '#x0000000a' """ if is_expr(val): if __debug__: _z3_assert(self.ctx == val.ctx, "Context mismatch") # Idea: use sign_extend if sort of val is a bitvector of smaller size return val else: return BitVecVal(val, self) def is_bv_sort(s): """Return True if `s` is a Z3 bit-vector sort. >>> is_bv_sort(BitVecSort(32)) True >>> is_bv_sort(IntSort()) False """ return isinstance(s, BitVecSortRef) class BitVecRef(ExprRef): """Bit-vector expressions.""" def sort(self): """Return the sort of the bit-vector expression `self`. >>> x = BitVec('x', 32) >>> x.sort() BitVec(32) >>> x.sort() == BitVecSort(32) True """ return BitVecSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def size(self): """Return the number of bits of the bit-vector expression `self`. >>> x = BitVec('x', 32) >>> (x + 1).size() 32 >>> Concat(x, x).size() 64 """ return self.sort().size() def __add__(self, other): """Create the Z3 expression `self + other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x + y x + y >>> (x + y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvadd(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __radd__(self, other): """Create the Z3 expression `other + self`. >>> x = BitVec('x', 32) >>> 10 + x 10 + x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvadd(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __mul__(self, other): """Create the Z3 expression `self * other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x * y x*y >>> (x * y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvmul(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rmul__(self, other): """Create the Z3 expression `other * self`. >>> x = BitVec('x', 32) >>> 10 * x 10*x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvmul(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __sub__(self, other): """Create the Z3 expression `self - other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x - y x - y >>> (x - y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsub(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rsub__(self, other): """Create the Z3 expression `other - self`. >>> x = BitVec('x', 32) >>> 10 - x 10 - x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsub(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __or__(self, other): """Create the Z3 expression bitwise-or `self | other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x | y x | y >>> (x | y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvor(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __ror__(self, other): """Create the Z3 expression bitwise-or `other | self`. >>> x = BitVec('x', 32) >>> 10 | x 10 | x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvor(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __and__(self, other): """Create the Z3 expression bitwise-and `self & other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x & y x & y >>> (x & y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvand(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rand__(self, other): """Create the Z3 expression bitwise-or `other & self`. >>> x = BitVec('x', 32) >>> 10 & x 10 & x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvand(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __xor__(self, other): """Create the Z3 expression bitwise-xor `self ^ other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x ^ y x ^ y >>> (x ^ y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvxor(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rxor__(self, other): """Create the Z3 expression bitwise-xor `other ^ self`. >>> x = BitVec('x', 32) >>> 10 ^ x 10 ^ x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvxor(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __pos__(self): """Return `self`. >>> x = BitVec('x', 32) >>> +x x """ return self def __neg__(self): """Return an expression representing `-self`. >>> x = BitVec('x', 32) >>> -x -x >>> simplify(-(-x)) x """ return BitVecRef(Z3_mk_bvneg(self.ctx_ref(), self.as_ast()), self.ctx) def __invert__(self): """Create the Z3 expression bitwise-not `~self`. >>> x = BitVec('x', 32) >>> ~x ~x >>> simplify(~(~x)) x """ return BitVecRef(Z3_mk_bvnot(self.ctx_ref(), self.as_ast()), self.ctx) def __div__(self, other): """Create the Z3 expression (signed) division `self / other`. Use the function UDiv() for unsigned division. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x / y x/y >>> (x / y).sort() BitVec(32) >>> (x / y).sexpr() '(bvsdiv x y)' >>> UDiv(x, y).sexpr() '(bvudiv x y)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsdiv(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __truediv__(self, other): """Create the Z3 expression (signed) division `self / other`.""" return self.__div__(other) def __rdiv__(self, other): """Create the Z3 expression (signed) division `other / self`. Use the function UDiv() for unsigned division. >>> x = BitVec('x', 32) >>> 10 / x 10/x >>> (10 / x).sexpr() '(bvsdiv #x0000000a x)' >>> UDiv(10, x).sexpr() '(bvudiv #x0000000a x)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsdiv(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __rtruediv__(self, other): """Create the Z3 expression (signed) division `other / self`.""" return self.__rdiv__(other) def __mod__(self, other): """Create the Z3 expression (signed) mod `self % other`. Use the function URem() for unsigned remainder, and SRem() for signed remainder. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x % y x%y >>> (x % y).sort() BitVec(32) >>> (x % y).sexpr() '(bvsmod x y)' >>> URem(x, y).sexpr() '(bvurem x y)' >>> SRem(x, y).sexpr() '(bvsrem x y)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsmod(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rmod__(self, other): """Create the Z3 expression (signed) mod `other % self`. Use the function URem() for unsigned remainder, and SRem() for signed remainder. >>> x = BitVec('x', 32) >>> 10 % x 10%x >>> (10 % x).sexpr() '(bvsmod #x0000000a x)' >>> URem(10, x).sexpr() '(bvurem #x0000000a x)' >>> SRem(10, x).sexpr() '(bvsrem #x0000000a x)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsmod(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __le__(self, other): """Create the Z3 expression (signed) `other <= self`. Use the function ULE() for unsigned less than or equal to. >>> x, y = BitVecs('x y', 32) >>> x <= y x <= y >>> (x <= y).sexpr() '(bvsle x y)' >>> ULE(x, y).sexpr() '(bvule x y)' """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_bvsle(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __lt__(self, other): """Create the Z3 expression (signed) `other < self`. Use the function ULT() for unsigned less than. >>> x, y = BitVecs('x y', 32) >>> x < y x < y >>> (x < y).sexpr() '(bvslt x y)' >>> ULT(x, y).sexpr() '(bvult x y)' """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_bvslt(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __gt__(self, other): """Create the Z3 expression (signed) `other > self`. Use the function UGT() for unsigned greater than. >>> x, y = BitVecs('x y', 32) >>> x > y x > y >>> (x > y).sexpr() '(bvsgt x y)' >>> UGT(x, y).sexpr() '(bvugt x y)' """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_bvsgt(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __ge__(self, other): """Create the Z3 expression (signed) `other >= self`. Use the function UGE() for unsigned greater than or equal to. >>> x, y = BitVecs('x y', 32) >>> x >= y x >= y >>> (x >= y).sexpr() '(bvsge x y)' >>> UGE(x, y).sexpr() '(bvuge x y)' """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_bvsge(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rshift__(self, other): """Create the Z3 expression (arithmetical) right shift `self >> other` Use the function LShR() for the right logical shift >>> x, y = BitVecs('x y', 32) >>> x >> y x >> y >>> (x >> y).sexpr() '(bvashr x y)' >>> LShR(x, y).sexpr() '(bvlshr x y)' >>> BitVecVal(4, 3) 4 >>> BitVecVal(4, 3).as_signed_long() -4 >>> simplify(BitVecVal(4, 3) >> 1).as_signed_long() -2 >>> simplify(BitVecVal(4, 3) >> 1) 6 >>> simplify(LShR(BitVecVal(4, 3), 1)) 2 >>> simplify(BitVecVal(2, 3) >> 1) 1 >>> simplify(LShR(BitVecVal(2, 3), 1)) 1 """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvashr(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __lshift__(self, other): """Create the Z3 expression left shift `self << other` >>> x, y = BitVecs('x y', 32) >>> x << y x << y >>> (x << y).sexpr() '(bvshl x y)' >>> simplify(BitVecVal(2, 3) << 1) 4 """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvshl(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rrshift__(self, other): """Create the Z3 expression (arithmetical) right shift `other` >> `self`. Use the function LShR() for the right logical shift >>> x = BitVec('x', 32) >>> 10 >> x 10 >> x >>> (10 >> x).sexpr() '(bvashr #x0000000a x)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvashr(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __rlshift__(self, other): """Create the Z3 expression left shift `other << self`. Use the function LShR() for the right logical shift >>> x = BitVec('x', 32) >>> 10 << x 10 << x >>> (10 << x).sexpr() '(bvshl #x0000000a x)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvshl(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) class BitVecNumRef(BitVecRef): """Bit-vector values.""" def as_long(self): """Return a Z3 bit-vector numeral as a Python long (bignum) numeral. >>> v = BitVecVal(0xbadc0de, 32) >>> v 195936478 >>> print("0x%.8x" % v.as_long()) 0x0badc0de """ return int(self.as_string()) def as_signed_long(self): """Return a Z3 bit-vector numeral as a Python long (bignum) numeral. The most significant bit is assumed to be the sign. >>> BitVecVal(4, 3).as_signed_long() -4 >>> BitVecVal(7, 3).as_signed_long() -1 >>> BitVecVal(3, 3).as_signed_long() 3 >>> BitVecVal(2**32 - 1, 32).as_signed_long() -1 >>> BitVecVal(2**64 - 1, 64).as_signed_long() -1 """ sz = self.size() val = self.as_long() if val >= 2**(sz - 1): val = val - 2**sz if val < -2**(sz - 1): val = val + 2**sz return int(val) def as_string(self): return Z3_get_numeral_string(self.ctx_ref(), self.as_ast()) def is_bv(a): """Return `True` if `a` is a Z3 bit-vector expression. >>> b = BitVec('b', 32) >>> is_bv(b) True >>> is_bv(b + 10) True >>> is_bv(Int('x')) False """ return isinstance(a, BitVecRef) def is_bv_value(a): """Return `True` if `a` is a Z3 bit-vector numeral value. >>> b = BitVec('b', 32) >>> is_bv_value(b) False >>> b = BitVecVal(10, 32) >>> b 10 >>> is_bv_value(b) True """ return is_bv(a) and _is_numeral(a.ctx, a.as_ast()) def BV2Int(a): """Return the Z3 expression BV2Int(a). >>> b = BitVec('b', 3) >>> BV2Int(b).sort() Int >>> x = Int('x') >>> x > BV2Int(b) x > BV2Int(b) >>> solve(x > BV2Int(b), b == 1, x < 3) [b = 1, x = 2] """ if __debug__: _z3_assert(is_bv(a), "Z3 bit-vector expression expected") ctx = a.ctx ## investigate problem with bv2int return ArithRef(Z3_mk_bv2int(ctx.ref(), a.as_ast(), 0), ctx) def BitVecSort(sz, ctx=None): """Return a Z3 bit-vector sort of the given size. If `ctx=None`, then the global context is used. >>> Byte = BitVecSort(8) >>> Word = BitVecSort(16) >>> Byte BitVec(8) >>> x = Const('x', Byte) >>> eq(x, BitVec('x', 8)) True """ ctx = _get_ctx(ctx) return BitVecSortRef(Z3_mk_bv_sort(ctx.ref(), sz), ctx) def BitVecVal(val, bv, ctx=None): """Return a bit-vector value with the given number of bits. If `ctx=None`, then the global context is used. >>> v = BitVecVal(10, 32) >>> v 10 >>> print("0x%.8x" % v.as_long()) 0x0000000a """ if is_bv_sort(bv): ctx = bv.ctx return BitVecNumRef(Z3_mk_numeral(ctx.ref(), _to_int_str(val), bv.ast), ctx) else: ctx = _get_ctx(ctx) return BitVecNumRef(Z3_mk_numeral(ctx.ref(), _to_int_str(val), BitVecSort(bv, ctx).ast), ctx) def BitVec(name, bv, ctx=None): """Return a bit-vector constant named `name`. `bv` may be the number of bits of a bit-vector sort. If `ctx=None`, then the global context is used. >>> x = BitVec('x', 16) >>> is_bv(x) True >>> x.size() 16 >>> x.sort() BitVec(16) >>> word = BitVecSort(16) >>> x2 = BitVec('x', word) >>> eq(x, x2) True """ if isinstance(bv, BitVecSortRef): ctx = bv.ctx else: ctx = _get_ctx(ctx) bv = BitVecSort(bv, ctx) return BitVecRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), bv.ast), ctx) def BitVecs(names, bv, ctx=None): """Return a tuple of bit-vector constants of size bv. >>> x, y, z = BitVecs('x y z', 16) >>> x.size() 16 >>> x.sort() BitVec(16) >>> Sum(x, y, z) 0 + x + y + z >>> Product(x, y, z) 1*x*y*z >>> simplify(Product(x, y, z)) x*y*z """ ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [BitVec(name, bv, ctx) for name in names] def Concat(*args): """Create a Z3 bit-vector concatenation expression. >>> v = BitVecVal(1, 4) >>> Concat(v, v+1, v) Concat(Concat(1, 1 + 1), 1) >>> simplify(Concat(v, v+1, v)) 289 >>> print("%.3x" % simplify(Concat(v, v+1, v)).as_long()) 121 """ args = _get_args(args) if __debug__: _z3_assert(all([is_bv(a) for a in args]), "All arguments must be Z3 bit-vector expressions.") _z3_assert(len(args) >= 2, "At least two arguments expected.") ctx = args[0].ctx r = args[0] for i in range(len(args) - 1): r = BitVecRef(Z3_mk_concat(ctx.ref(), r.as_ast(), args[i+1].as_ast()), ctx) return r def Extract(high, low, a): """Create a Z3 bit-vector extraction expression. >>> x = BitVec('x', 8) >>> Extract(6, 2, x) Extract(6, 2, x) >>> Extract(6, 2, x).sort() BitVec(5) """ if __debug__: _z3_assert(low <= high, "First argument must be greater than or equal to second argument") _z3_assert(isinstance(high, int) and high >= 0 and isinstance(low, int) and low >= 0, "First and second arguments must be non negative integers") _z3_assert(is_bv(a), "Third argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_extract(a.ctx_ref(), high, low, a.as_ast()), a.ctx) def _check_bv_args(a, b): if __debug__: _z3_assert(is_bv(a) or is_bv(b), "At least one of the arguments must be a Z3 bit-vector expression") def ULE(a, b): """Create the Z3 expression (unsigned) `other <= self`. Use the operator <= for signed less than or equal to. >>> x, y = BitVecs('x y', 32) >>> ULE(x, y) ULE(x, y) >>> (x <= y).sexpr() '(bvsle x y)' >>> ULE(x, y).sexpr() '(bvule x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvule(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def ULT(a, b): """Create the Z3 expression (unsigned) `other < self`. Use the operator < for signed less than. >>> x, y = BitVecs('x y', 32) >>> ULT(x, y) ULT(x, y) >>> (x < y).sexpr() '(bvslt x y)' >>> ULT(x, y).sexpr() '(bvult x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvult(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def UGE(a, b): """Create the Z3 expression (unsigned) `other >= self`. Use the operator >= for signed greater than or equal to. >>> x, y = BitVecs('x y', 32) >>> UGE(x, y) UGE(x, y) >>> (x >= y).sexpr() '(bvsge x y)' >>> UGE(x, y).sexpr() '(bvuge x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvuge(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def UGT(a, b): """Create the Z3 expression (unsigned) `other > self`. Use the operator > for signed greater than. >>> x, y = BitVecs('x y', 32) >>> UGT(x, y) UGT(x, y) >>> (x > y).sexpr() '(bvsgt x y)' >>> UGT(x, y).sexpr() '(bvugt x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvugt(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def UDiv(a, b): """Create the Z3 expression (unsigned) division `self / other`. Use the operator / for signed division. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> UDiv(x, y) UDiv(x, y) >>> UDiv(x, y).sort() BitVec(32) >>> (x / y).sexpr() '(bvsdiv x y)' >>> UDiv(x, y).sexpr() '(bvudiv x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_bvudiv(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def URem(a, b): """Create the Z3 expression (unsigned) remainder `self % other`. Use the operator % for signed modulus, and SRem() for signed remainder. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> URem(x, y) URem(x, y) >>> URem(x, y).sort() BitVec(32) >>> (x % y).sexpr() '(bvsmod x y)' >>> URem(x, y).sexpr() '(bvurem x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_bvurem(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def SRem(a, b): """Create the Z3 expression signed remainder. Use the operator % for signed modulus, and URem() for unsigned remainder. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> SRem(x, y) SRem(x, y) >>> SRem(x, y).sort() BitVec(32) >>> (x % y).sexpr() '(bvsmod x y)' >>> SRem(x, y).sexpr() '(bvsrem x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_bvsrem(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def LShR(a, b): """Create the Z3 expression logical right shift. Use the operator >> for the arithmetical right shift. >>> x, y = BitVecs('x y', 32) >>> LShR(x, y) LShR(x, y) >>> (x >> y).sexpr() '(bvashr x y)' >>> LShR(x, y).sexpr() '(bvlshr x y)' >>> BitVecVal(4, 3) 4 >>> BitVecVal(4, 3).as_signed_long() -4 >>> simplify(BitVecVal(4, 3) >> 1).as_signed_long() -2 >>> simplify(BitVecVal(4, 3) >> 1) 6 >>> simplify(LShR(BitVecVal(4, 3), 1)) 2 >>> simplify(BitVecVal(2, 3) >> 1) 1 >>> simplify(LShR(BitVecVal(2, 3), 1)) 1 """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_bvlshr(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def RotateLeft(a, b): """Return an expression representing `a` rotated to the left `b` times. >>> a, b = BitVecs('a b', 16) >>> RotateLeft(a, b) RotateLeft(a, b) >>> simplify(RotateLeft(a, 0)) a >>> simplify(RotateLeft(a, 16)) a """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_ext_rotate_left(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def RotateRight(a, b): """Return an expression representing `a` rotated to the right `b` times. >>> a, b = BitVecs('a b', 16) >>> RotateRight(a, b) RotateRight(a, b) >>> simplify(RotateRight(a, 0)) a >>> simplify(RotateRight(a, 16)) a """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_ext_rotate_right(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def SignExt(n, a): """Return a bit-vector expression with `n` extra sign-bits. >>> x = BitVec('x', 16) >>> n = SignExt(8, x) >>> n.size() 24 >>> n SignExt(8, x) >>> n.sort() BitVec(24) >>> v0 = BitVecVal(2, 2) >>> v0 2 >>> v0.size() 2 >>> v = simplify(SignExt(6, v0)) >>> v 254 >>> v.size() 8 >>> print("%.x" % v.as_long()) fe """ if __debug__: _z3_assert(isinstance(n, int), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_sign_ext(a.ctx_ref(), n, a.as_ast()), a.ctx) def ZeroExt(n, a): """Return a bit-vector expression with `n` extra zero-bits. >>> x = BitVec('x', 16) >>> n = ZeroExt(8, x) >>> n.size() 24 >>> n ZeroExt(8, x) >>> n.sort() BitVec(24) >>> v0 = BitVecVal(2, 2) >>> v0 2 >>> v0.size() 2 >>> v = simplify(ZeroExt(6, v0)) >>> v 2 >>> v.size() 8 """ if __debug__: _z3_assert(isinstance(n, int), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_zero_ext(a.ctx_ref(), n, a.as_ast()), a.ctx) def RepeatBitVec(n, a): """Return an expression representing `n` copies of `a`. >>> x = BitVec('x', 8) >>> n = RepeatBitVec(4, x) >>> n RepeatBitVec(4, x) >>> n.size() 32 >>> v0 = BitVecVal(10, 4) >>> print("%.x" % v0.as_long()) a >>> v = simplify(RepeatBitVec(4, v0)) >>> v.size() 16 >>> print("%.x" % v.as_long()) aaaa """ if __debug__: _z3_assert(isinstance(n, int), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_repeat(a.ctx_ref(), n, a.as_ast()), a.ctx) def BVRedAnd(a): """Return the reduction-and expression of `a`.""" if __debug__: _z3_assert(is_bv(a), "First argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_bvredand(a.ctx_ref(), a.as_ast()), a.ctx) def BVRedOr(a): """Return the reduction-or expression of `a`.""" if __debug__: _z3_assert(is_bv(a), "First argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_bvredor(a.ctx_ref(), a.as_ast()), a.ctx) ######################################### # # Arrays # ######################################### class ArraySortRef(SortRef): """Array sorts.""" def domain(self): """Return the domain of the array sort `self`. >>> A = ArraySort(IntSort(), BoolSort()) >>> A.domain() Int """ return _to_sort_ref(Z3_get_array_sort_domain(self.ctx_ref(), self.ast), self.ctx) def range(self): """Return the range of the array sort `self`. >>> A = ArraySort(IntSort(), BoolSort()) >>> A.range() Bool """ return _to_sort_ref(Z3_get_array_sort_range(self.ctx_ref(), self.ast), self.ctx) class ArrayRef(ExprRef): """Array expressions. """ def sort(self): """Return the array sort of the array expression `self`. >>> a = Array('a', IntSort(), BoolSort()) >>> a.sort() Array(Int, Bool) """ return ArraySortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def domain(self): """Shorthand for `self.sort().domain()`. >>> a = Array('a', IntSort(), BoolSort()) >>> a.domain() Int """ return self.sort().domain() def range(self): """Shorthand for `self.sort().range()`. >>> a = Array('a', IntSort(), BoolSort()) >>> a.range() Bool """ return self.sort().range() def __getitem__(self, arg): """Return the Z3 expression `self[arg]`. >>> a = Array('a', IntSort(), BoolSort()) >>> i = Int('i') >>> a[i] a[i] >>> a[i].sexpr() '(select a i)' """ arg = self.domain().cast(arg) return _to_expr_ref(Z3_mk_select(self.ctx_ref(), self.as_ast(), arg.as_ast()), self.ctx) def mk_default(self): return _to_expr_ref(Z3_mk_array_default(self.ctx_ref(), self.as_ast()), self.ctx) def is_array(a): """Return `True` if `a` is a Z3 array expression. >>> a = Array('a', IntSort(), IntSort()) >>> is_array(a) True >>> is_array(Store(a, 0, 1)) True >>> is_array(a[0]) False """ return isinstance(a, ArrayRef) def is_const_array(a): """Return `True` if `a` is a Z3 constant array. >>> a = K(IntSort(), 10) >>> is_const_array(a) True >>> a = Array('a', IntSort(), IntSort()) >>> is_const_array(a) False """ return is_app_of(a, Z3_OP_CONST_ARRAY) def is_K(a): """Return `True` if `a` is a Z3 constant array. >>> a = K(IntSort(), 10) >>> is_K(a) True >>> a = Array('a', IntSort(), IntSort()) >>> is_K(a) False """ return is_app_of(a, Z3_OP_CONST_ARRAY) def is_map(a): """Return `True` if `a` is a Z3 map array expression. >>> f = Function('f', IntSort(), IntSort()) >>> b = Array('b', IntSort(), IntSort()) >>> a = Map(f, b) >>> a Map(f, b) >>> is_map(a) True >>> is_map(b) False """ return is_app_of(a, Z3_OP_ARRAY_MAP) def is_default(a): """Return `True` if `a` is a Z3 default array expression. >>> d = Default(K(IntSort(), 10)) >>> is_default(d) True """ return is_app_of(a, Z3_OP_ARRAY_DEFAULT) def get_map_func(a): """Return the function declaration associated with a Z3 map array expression. >>> f = Function('f', IntSort(), IntSort()) >>> b = Array('b', IntSort(), IntSort()) >>> a = Map(f, b) >>> eq(f, get_map_func(a)) True >>> get_map_func(a) f >>> get_map_func(a)(0) f(0) """ if __debug__: _z3_assert(is_map(a), "Z3 array map expression expected.") return FuncDeclRef(Z3_to_func_decl(a.ctx_ref(), Z3_get_decl_ast_parameter(a.ctx_ref(), a.decl().ast, 0)), a.ctx) def ArraySort(d, r): """Return the Z3 array sort with the given domain and range sorts. >>> A = ArraySort(IntSort(), BoolSort()) >>> A Array(Int, Bool) >>> A.domain() Int >>> A.range() Bool >>> AA = ArraySort(IntSort(), A) >>> AA Array(Int, Array(Int, Bool)) """ if __debug__: _z3_assert(is_sort(d), "Z3 sort expected") _z3_assert(is_sort(r), "Z3 sort expected") _z3_assert(d.ctx == r.ctx, "Context mismatch") ctx = d.ctx return ArraySortRef(Z3_mk_array_sort(ctx.ref(), d.ast, r.ast), ctx) def Array(name, dom, rng): """Return an array constant named `name` with the given domain and range sorts. >>> a = Array('a', IntSort(), IntSort()) >>> a.sort() Array(Int, Int) >>> a[0] a[0] """ s = ArraySort(dom, rng) ctx = s.ctx return ArrayRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), s.ast), ctx) def Update(a, i, v): """Return a Z3 store array expression. >>> a = Array('a', IntSort(), IntSort()) >>> i, v = Ints('i v') >>> s = Update(a, i, v) >>> s.sort() Array(Int, Int) >>> prove(s[i] == v) proved >>> j = Int('j') >>> prove(Implies(i != j, s[j] == a[j])) proved """ if __debug__: _z3_assert(is_array(a), "First argument must be a Z3 array expression") i = a.domain().cast(i) v = a.range().cast(v) ctx = a.ctx return _to_expr_ref(Z3_mk_store(ctx.ref(), a.as_ast(), i.as_ast(), v.as_ast()), ctx) def Default(a): """ Return a default value for array expression. >>> b = K(IntSort(), 1) >>> prove(Default(b) == 1) proved """ if __debug__: _z3_assert(is_array(a), "First argument must be a Z3 array expression") return a.mk_default() def Store(a, i, v): """Return a Z3 store array expression. >>> a = Array('a', IntSort(), IntSort()) >>> i, v = Ints('i v') >>> s = Store(a, i, v) >>> s.sort() Array(Int, Int) >>> prove(s[i] == v) proved >>> j = Int('j') >>> prove(Implies(i != j, s[j] == a[j])) proved """ return Update(a, i, v) def Select(a, i): """Return a Z3 select array expression. >>> a = Array('a', IntSort(), IntSort()) >>> i = Int('i') >>> Select(a, i) a[i] >>> eq(Select(a, i), a[i]) True """ if __debug__: _z3_assert(is_array(a), "First argument must be a Z3 array expression") return a[i] def Map(f, *args): """Return a Z3 map array expression. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> a1 = Array('a1', IntSort(), IntSort()) >>> a2 = Array('a2', IntSort(), IntSort()) >>> b = Map(f, a1, a2) >>> b Map(f, a1, a2) >>> prove(b[0] == f(a1[0], a2[0])) proved """ args = _get_args(args) if __debug__: _z3_assert(len(args) > 0, "At least one Z3 array expression expected") _z3_assert(is_func_decl(f), "First argument must be a Z3 function declaration") _z3_assert(all([is_array(a) for a in args]), "Z3 array expected expected") _z3_assert(len(args) == f.arity(), "Number of arguments mismatch") _args, sz = _to_ast_array(args) ctx = f.ctx return ArrayRef(Z3_mk_map(ctx.ref(), f.ast, sz, _args), ctx) def K(dom, v): """Return a Z3 constant array expression. >>> a = K(IntSort(), 10) >>> a K(Int, 10) >>> a.sort() Array(Int, Int) >>> i = Int('i') >>> a[i] K(Int, 10)[i] >>> simplify(a[i]) 10 """ if __debug__: _z3_assert(is_sort(dom), "Z3 sort expected") ctx = dom.ctx if not is_expr(v): v = _py2expr(v, ctx) return ArrayRef(Z3_mk_const_array(ctx.ref(), dom.ast, v.as_ast()), ctx) def is_select(a): """Return `True` if `a` is a Z3 array select application. >>> a = Array('a', IntSort(), IntSort()) >>> is_select(a) False >>> i = Int('i') >>> is_select(a[i]) True """ return is_app_of(a, Z3_OP_SELECT) def is_store(a): """Return `True` if `a` is a Z3 array store application. >>> a = Array('a', IntSort(), IntSort()) >>> is_store(a) False >>> is_store(Store(a, 0, 1)) True """ return is_app_of(a, Z3_OP_STORE) ######################################### # # Datatypes # ######################################### def _valid_accessor(acc): """Return `True` if acc is pair of the form (String, Datatype or Sort). """ return isinstance(acc, tuple) and len(acc) == 2 and isinstance(acc[0], str) and (isinstance(acc[1], Datatype) or is_sort(acc[1])) class Datatype: """Helper class for declaring Z3 datatypes. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a Z3 declaration >>> List.nil nil >>> List.cons(10, List.nil) cons(10, nil) >>> List.cons(10, List.nil).sort() List >>> cons = List.cons >>> nil = List.nil >>> car = List.car >>> cdr = List.cdr >>> n = cons(1, cons(0, nil)) >>> n cons(1, cons(0, nil)) >>> simplify(cdr(n)) cons(0, nil) >>> simplify(car(n)) 1 """ def __init__(self, name, ctx=None): self.ctx = _get_ctx(ctx) self.name = name self.constructors = [] def declare_core(self, name, rec_name, *args): if __debug__: _z3_assert(isinstance(name, str), "String expected") _z3_assert(isinstance(rec_name, str), "String expected") _z3_assert(all([_valid_accessor(a) for a in args]), "Valid list of accessors expected. An accessor is a pair of the form (String, Datatype|Sort)") self.constructors.append((name, rec_name, args)) def declare(self, name, *args): """Declare constructor named `name` with the given accessors `args`. Each accessor is a pair `(name, sort)`, where `name` is a string and `sort` a Z3 sort or a reference to the datatypes being declared. In the followin example `List.declare('cons', ('car', IntSort()), ('cdr', List))` declares the constructor named `cons` that builds a new List using an integer and a List. It also declares the accessors `car` and `cdr`. The accessor `car` extracts the integer of a `cons` cell, and `cdr` the list of a `cons` cell. After all constructors were declared, we use the method create() to create the actual datatype in Z3. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() """ if __debug__: _z3_assert(isinstance(name, str), "String expected") _z3_assert(name != "", "Constructor name cannot be empty") return self.declare_core(name, "is_" + name, *args) def __repr__(self): return "Datatype(%s, %s)" % (self.name, self.constructors) def create(self): """Create a Z3 datatype based on the constructors declared using the mehtod `declare()`. The function `CreateDatatypes()` must be used to define mutually recursive datatypes. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> List.nil nil >>> List.cons(10, List.nil) cons(10, nil) """ return CreateDatatypes([self])[0] class ScopedConstructor: """Auxiliary object used to create Z3 datatypes.""" def __init__(self, c, ctx): self.c = c self.ctx = ctx def __del__(self): Z3_del_constructor(self.ctx.ref(), self.c) class ScopedConstructorList: """Auxiliary object used to create Z3 datatypes.""" def __init__(self, c, ctx): self.c = c self.ctx = ctx def __del__(self): Z3_del_constructor_list(self.ctx.ref(), self.c) def CreateDatatypes(*ds): """Create mutually recursive Z3 datatypes using 1 or more Datatype helper objects. In the following example we define a Tree-List using two mutually recursive datatypes. >>> TreeList = Datatype('TreeList') >>> Tree = Datatype('Tree') >>> # Tree has two constructors: leaf and node >>> Tree.declare('leaf', ('val', IntSort())) >>> # a node contains a list of trees >>> Tree.declare('node', ('children', TreeList)) >>> TreeList.declare('nil') >>> TreeList.declare('cons', ('car', Tree), ('cdr', TreeList)) >>> Tree, TreeList = CreateDatatypes(Tree, TreeList) >>> Tree.val(Tree.leaf(10)) val(leaf(10)) >>> simplify(Tree.val(Tree.leaf(10))) 10 >>> n1 = Tree.node(TreeList.cons(Tree.leaf(10), TreeList.cons(Tree.leaf(20), TreeList.nil))) >>> n1 node(cons(leaf(10), cons(leaf(20), nil))) >>> n2 = Tree.node(TreeList.cons(n1, TreeList.nil)) >>> simplify(n2 == n1) False >>> simplify(TreeList.car(Tree.children(n2)) == n1) True """ ds = _get_args(ds) if __debug__: _z3_assert(len(ds) > 0, "At least one Datatype must be specified") _z3_assert(all([isinstance(d, Datatype) for d in ds]), "Arguments must be Datatypes") _z3_assert(all([d.ctx == ds[0].ctx for d in ds]), "Context mismatch") _z3_assert(all([d.constructors != [] for d in ds]), "Non-empty Datatypes expected") ctx = ds[0].ctx num = len(ds) names = (Symbol * num)() out = (Sort * num)() clists = (ConstructorList * num)() to_delete = [] for i in range(num): d = ds[i] names[i] = to_symbol(d.name, ctx) num_cs = len(d.constructors) cs = (Constructor * num_cs)() for j in range(num_cs): c = d.constructors[j] cname = to_symbol(c[0], ctx) rname = to_symbol(c[1], ctx) fs = c[2] num_fs = len(fs) fnames = (Symbol * num_fs)() sorts = (Sort * num_fs)() refs = (ctypes.c_uint * num_fs)() for k in range(num_fs): fname = fs[k][0] ftype = fs[k][1] fnames[k] = to_symbol(fname, ctx) if isinstance(ftype, Datatype): if __debug__: _z3_assert(ds.count(ftype) == 1, "One and only one occurrence of each datatype is expected") sorts[k] = None refs[k] = ds.index(ftype) else: if __debug__: _z3_assert(is_sort(ftype), "Z3 sort expected") sorts[k] = ftype.ast refs[k] = 0 cs[j] = Z3_mk_constructor(ctx.ref(), cname, rname, num_fs, fnames, sorts, refs) to_delete.append(ScopedConstructor(cs[j], ctx)) clists[i] = Z3_mk_constructor_list(ctx.ref(), num_cs, cs) to_delete.append(ScopedConstructorList(clists[i], ctx)) Z3_mk_datatypes(ctx.ref(), num, names, out, clists) result = [] ## Create a field for every constructor, recognizer and accessor for i in range(num): dref = DatatypeSortRef(out[i], ctx) num_cs = dref.num_constructors() for j in range(num_cs): cref = dref.constructor(j) cref_name = cref.name() cref_arity = cref.arity() if cref.arity() == 0: cref = cref() setattr(dref, cref_name, cref) rref = dref.recognizer(j) setattr(dref, rref.name(), rref) for k in range(cref_arity): aref = dref.accessor(j, k) setattr(dref, aref.name(), aref) result.append(dref) return tuple(result) class DatatypeSortRef(SortRef): """Datatype sorts.""" def num_constructors(self): """Return the number of constructors in the given Z3 datatype. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a Z3 declaration >>> List.num_constructors() 2 """ return int(Z3_get_datatype_sort_num_constructors(self.ctx_ref(), self.ast)) def constructor(self, idx): """Return a constructor of the datatype `self`. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a Z3 declaration >>> List.num_constructors() 2 >>> List.constructor(0) cons >>> List.constructor(1) nil """ if __debug__: _z3_assert(idx < self.num_constructors(), "Invalid constructor index") return FuncDeclRef(Z3_get_datatype_sort_constructor(self.ctx_ref(), self.ast, idx), self.ctx) def recognizer(self, idx): """In Z3, each constructor has an associated recognizer predicate. If the constructor is named `name`, then the recognizer `is_name`. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a Z3 declaration >>> List.num_constructors() 2 >>> List.recognizer(0) is_cons >>> List.recognizer(1) is_nil >>> simplify(List.is_nil(List.cons(10, List.nil))) False >>> simplify(List.is_cons(List.cons(10, List.nil))) True >>> l = Const('l', List) >>> simplify(List.is_cons(l)) is_cons(l) """ if __debug__: _z3_assert(idx < self.num_constructors(), "Invalid recognizer index") return FuncDeclRef(Z3_get_datatype_sort_recognizer(self.ctx_ref(), self.ast, idx), self.ctx) def accessor(self, i, j): """In Z3, each constructor has 0 or more accessor. The number of accessors is equal to the arity of the constructor. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> List.num_constructors() 2 >>> List.constructor(0) cons >>> num_accs = List.constructor(0).arity() >>> num_accs 2 >>> List.accessor(0, 0) car >>> List.accessor(0, 1) cdr >>> List.constructor(1) nil >>> num_accs = List.constructor(1).arity() >>> num_accs 0 """ if __debug__: _z3_assert(i < self.num_constructors(), "Invalid constructor index") _z3_assert(j < self.constructor(i).arity(), "Invalid accessor index") return FuncDeclRef(Z3_get_datatype_sort_constructor_accessor(self.ctx_ref(), self.ast, i, j), self.ctx) class DatatypeRef(ExprRef): """Datatype expressions.""" def sort(self): """Return the datatype sort of the datatype expression `self`.""" return DatatypeSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def EnumSort(name, values, ctx=None): """Return a new enumeration sort named `name` containing the given values. The result is a pair (sort, list of constants). Example: >>> Color, (red, green, blue) = EnumSort('Color', ['red', 'green', 'blue']) """ if __debug__: _z3_assert(isinstance(name, str), "Name must be a string") _z3_assert(all([isinstance(v, str) for v in values]), "Eumeration sort values must be strings") _z3_assert(len(values) > 0, "At least one value expected") ctx = _get_ctx(ctx) num = len(values) _val_names = (Symbol * num)() for i in range(num): _val_names[i] = to_symbol(values[i]) _values = (FuncDecl * num)() _testers = (FuncDecl * num)() name = to_symbol(name) S = DatatypeSortRef(Z3_mk_enumeration_sort(ctx.ref(), name, num, _val_names, _values, _testers), ctx) V = [] for i in range(num): V.append(FuncDeclRef(_values[i], ctx)) V = [a() for a in V] return S, V ######################################### # # Parameter Sets # ######################################### class ParamsRef: """Set of parameters used to configure Solvers, Tactics and Simplifiers in Z3. Consider using the function `args2params` to create instances of this object. """ def __init__(self, ctx=None): self.ctx = _get_ctx(ctx) self.params = Z3_mk_params(self.ctx.ref()) Z3_params_inc_ref(self.ctx.ref(), self.params) def __del__(self): Z3_params_dec_ref(self.ctx.ref(), self.params) def set(self, name, val): """Set parameter name with value val.""" if __debug__: _z3_assert(isinstance(name, str), "parameter name must be a string") name_sym = to_symbol(name, self.ctx) if isinstance(val, bool): Z3_params_set_bool(self.ctx.ref(), self.params, name_sym, val) elif isinstance(val, int): Z3_params_set_uint(self.ctx.ref(), self.params, name_sym, val) elif isinstance(val, float): Z3_params_set_double(self.ctx.ref(), self.params, name_sym, val) elif isinstance(val, str): Z3_params_set_symbol(self.ctx.ref(), self.params, name_sym, to_symbol(val, self.ctx)) else: if __debug__: _z3_assert(False, "invalid parameter value") def __repr__(self): return Z3_params_to_string(self.ctx.ref(), self.params) def validate(self, ds): _z3_assert(isinstance(ds, ParamDescrsRef), "parameter description set expected") Z3_params_validate(self.ctx.ref(), self.params, ds.descr) def args2params(arguments, keywords, ctx=None): """Convert python arguments into a Z3_params object. A ':' is added to the keywords, and '_' is replaced with '-' >>> args2params(['model', True, 'relevancy', 2], {'elim_and' : True}) (params model true relevancy 2 elim_and true) """ if __debug__: _z3_assert(len(arguments) % 2 == 0, "Argument list must have an even number of elements.") prev = None r = ParamsRef(ctx) for a in arguments: if prev == None: prev = a else: r.set(prev, a) prev = None for k in keywords: v = keywords[k] r.set(k, v) return r class ParamDescrsRef: """Set of parameter descriptions for Solvers, Tactics and Simplifiers in Z3. """ def __init__(self, descr, ctx=None): _z3_assert(isinstance(descr, ParamDescrs), "parameter description object expected") self.ctx = _get_ctx(ctx) self.descr = descr Z3_param_descrs_inc_ref(self.ctx.ref(), self.descr) def __del__(self): Z3_param_descrs_dec_ref(self.ctx.ref(), self.descr) def size(self): """Return the size of in the parameter description `self`. """ return int(Z3_param_descrs_size(self.ctx.ref(), self.descr)) def __len__(self): """Return the size of in the parameter description `self`. """ return self.size() def get_name(self, i): """Return the i-th parameter name in the parameter description `self`. """ return _symbol2py(self.ctx, Z3_param_descrs_get_name(self.ctx.ref(), self.descr, i)) def get_kind(self, n): """Return the kind of the parameter named `n`. """ return Z3_param_descrs_get_kind(self.ctx.ref(), self.descr, to_symbol(n, self.ctx)) def __getitem__(self, arg): if _is_int(arg): return self.get_name(arg) else: return self.get_kind(arg) def __repr__(self): return Z3_param_descrs_to_string(self.ctx.ref(), self.descr) ######################################### # # Goals # ######################################### class Goal(Z3PPObject): """Goal is a collection of constraints we want to find a solution or show to be unsatisfiable (infeasible). Goals are processed using Tactics. A Tactic transforms a goal into a set of subgoals. A goal has a solution if one of its subgoals has a solution. A goal is unsatisfiable if all subgoals are unsatisfiable. """ def __init__(self, models=True, unsat_cores=False, proofs=False, ctx=None, goal=None): if __debug__: _z3_assert(goal == None or ctx != None, "If goal is different from None, then ctx must be also different from None") self.ctx = _get_ctx(ctx) self.goal = goal if self.goal == None: self.goal = Z3_mk_goal(self.ctx.ref(), models, unsat_cores, proofs) Z3_goal_inc_ref(self.ctx.ref(), self.goal) def __del__(self): if self.goal != None: Z3_goal_dec_ref(self.ctx.ref(), self.goal) def depth(self): """Return the depth of the goal `self`. The depth corresponds to the number of tactics applied to `self`. >>> x, y = Ints('x y') >>> g = Goal() >>> g.add(x == 0, y >= x + 1) >>> g.depth() 0 >>> r = Then('simplify', 'solve-eqs')(g) >>> # r has 1 subgoal >>> len(r) 1 >>> r[0].depth() 2 """ return int(Z3_goal_depth(self.ctx.ref(), self.goal)) def inconsistent(self): """Return `True` if `self` contains the `False` constraints. >>> x, y = Ints('x y') >>> g = Goal() >>> g.inconsistent() False >>> g.add(x == 0, x == 1) >>> g [x == 0, x == 1] >>> g.inconsistent() False >>> g2 = Tactic('propagate-values')(g)[0] >>> g2.inconsistent() True """ return Z3_goal_inconsistent(self.ctx.ref(), self.goal) def prec(self): """Return the precision (under-approximation, over-approximation, or precise) of the goal `self`. >>> g = Goal() >>> g.prec() == Z3_GOAL_PRECISE True >>> x, y = Ints('x y') >>> g.add(x == y + 1) >>> g.prec() == Z3_GOAL_PRECISE True >>> t = With(Tactic('add-bounds'), add_bound_lower=0, add_bound_upper=10) >>> g2 = t(g)[0] >>> g2 [x == y + 1, x <= 10, x >= 0, y <= 10, y >= 0] >>> g2.prec() == Z3_GOAL_PRECISE False >>> g2.prec() == Z3_GOAL_UNDER True """ return Z3_goal_precision(self.ctx.ref(), self.goal) def precision(self): """Alias for `prec()`. >>> g = Goal() >>> g.precision() == Z3_GOAL_PRECISE True """ return self.prec() def size(self): """Return the number of constraints in the goal `self`. >>> g = Goal() >>> g.size() 0 >>> x, y = Ints('x y') >>> g.add(x == 0, y > x) >>> g.size() 2 """ return int(Z3_goal_size(self.ctx.ref(), self.goal)) def __len__(self): """Return the number of constraints in the goal `self`. >>> g = Goal() >>> len(g) 0 >>> x, y = Ints('x y') >>> g.add(x == 0, y > x) >>> len(g) 2 """ return self.size() def get(self, i): """Return a constraint in the goal `self`. >>> g = Goal() >>> x, y = Ints('x y') >>> g.add(x == 0, y > x) >>> g.get(0) x == 0 >>> g.get(1) y > x """ return _to_expr_ref(Z3_goal_formula(self.ctx.ref(), self.goal, i), self.ctx) def __getitem__(self, arg): """Return a constraint in the goal `self`. >>> g = Goal() >>> x, y = Ints('x y') >>> g.add(x == 0, y > x) >>> g[0] x == 0 >>> g[1] y > x """ if arg >= len(self): raise IndexError return self.get(arg) def assert_exprs(self, *args): """Assert constraints into the goal. >>> x = Int('x') >>> g = Goal() >>> g.assert_exprs(x > 0, x < 2) >>> g [x > 0, x < 2] """ args = _get_args(args) s = BoolSort(self.ctx) for arg in args: arg = s.cast(arg) Z3_goal_assert(self.ctx.ref(), self.goal, arg.as_ast()) def append(self, *args): """Add constraints. >>> x = Int('x') >>> g = Goal() >>> g.append(x > 0, x < 2) >>> g [x > 0, x < 2] """ self.assert_exprs(*args) def insert(self, *args): """Add constraints. >>> x = Int('x') >>> g = Goal() >>> g.insert(x > 0, x < 2) >>> g [x > 0, x < 2] """ self.assert_exprs(*args) def add(self, *args): """Add constraints. >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0, x < 2) >>> g [x > 0, x < 2] """ self.assert_exprs(*args) def __repr__(self): return obj_to_string(self) def sexpr(self): """Return a textual representation of the s-expression representing the goal.""" return Z3_goal_to_string(self.ctx.ref(), self.goal) def translate(self, target): """Copy goal `self` to context `target`. >>> x = Int('x') >>> g = Goal() >>> g.add(x > 10) >>> g [x > 10] >>> c2 = Context() >>> g2 = g.translate(c2) >>> g2 [x > 10] >>> g.ctx == main_ctx() True >>> g2.ctx == c2 True >>> g2.ctx == main_ctx() False """ if __debug__: _z3_assert(isinstance(target, Context), "target must be a context") return Goal(goal=Z3_goal_translate(self.ctx.ref(), self.goal, target.ref()), ctx=target) def simplify(self, *arguments, **keywords): """Return a new simplified goal. This method is essentially invoking the simplify tactic. >>> g = Goal() >>> x = Int('x') >>> g.add(x + 1 >= 2) >>> g [x + 1 >= 2] >>> g2 = g.simplify() >>> g2 [x >= 1] >>> # g was not modified >>> g [x + 1 >= 2] """ t = Tactic('simplify') return t.apply(self, *arguments, **keywords)[0] def as_expr(self): """Return goal `self` as a single Z3 expression. >>> x = Int('x') >>> g = Goal() >>> g.as_expr() True >>> g.add(x > 1) >>> g.as_expr() x > 1 >>> g.add(x < 10) >>> g.as_expr() And(x > 1, x < 10) """ sz = len(self) if sz == 0: return BoolVal(True, self.ctx) elif sz == 1: return self.get(0) else: return And([ self.get(i) for i in range(len(self)) ]) ######################################### # # AST Vector # ######################################### class AstVector(Z3PPObject): """A collection (vector) of ASTs.""" def __init__(self, v=None, ctx=None): self.vector = None if v == None: self.ctx = _get_ctx(ctx) self.vector = Z3_mk_ast_vector(self.ctx.ref()) else: self.vector = v assert ctx != None self.ctx = ctx Z3_ast_vector_inc_ref(self.ctx.ref(), self.vector) def __del__(self): if self.vector != None: Z3_ast_vector_dec_ref(self.ctx.ref(), self.vector) def __len__(self): """Return the size of the vector `self`. >>> A = AstVector() >>> len(A) 0 >>> A.push(Int('x')) >>> A.push(Int('x')) >>> len(A) 2 """ return int(Z3_ast_vector_size(self.ctx.ref(), self.vector)) def __getitem__(self, i): """Return the AST at position `i`. >>> A = AstVector() >>> A.push(Int('x') + 1) >>> A.push(Int('y')) >>> A[0] x + 1 >>> A[1] y """ if i >= self.__len__(): raise IndexError return _to_ast_ref(Z3_ast_vector_get(self.ctx.ref(), self.vector, i), self.ctx) def __setitem__(self, i, v): """Update AST at position `i`. >>> A = AstVector() >>> A.push(Int('x') + 1) >>> A.push(Int('y')) >>> A[0] x + 1 >>> A[0] = Int('x') >>> A[0] x """ if i >= self.__len__(): raise IndexError Z3_ast_vector_set(self.ctx.ref(), self.vector, i, v.as_ast()) def push(self, v): """Add `v` in the end of the vector. >>> A = AstVector() >>> len(A) 0 >>> A.push(Int('x')) >>> len(A) 1 """ Z3_ast_vector_push(self.ctx.ref(), self.vector, v.as_ast()) def resize(self, sz): """Resize the vector to `sz` elements. >>> A = AstVector() >>> A.resize(10) >>> len(A) 10 >>> for i in range(10): A[i] = Int('x') >>> A[5] x """ Z3_ast_vector_resize(self.ctx.ref(), self.vector, sz) def __contains__(self, item): """Return `True` if the vector contains `item`. >>> x = Int('x') >>> A = AstVector() >>> x in A False >>> A.push(x) >>> x in A True >>> (x+1) in A False >>> A.push(x+1) >>> (x+1) in A True >>> A [x, x + 1] """ for elem in self: if elem.eq(item): return True return False def translate(self, other_ctx): """Copy vector `self` to context `other_ctx`. >>> x = Int('x') >>> A = AstVector() >>> A.push(x) >>> c2 = Context() >>> B = A.translate(c2) >>> B [x] """ return AstVector(Z3_ast_vector_translate(self.ctx.ref(), self.vector, other_ctx.ref()), other_ctx) def __repr__(self): return obj_to_string(self) def sexpr(self): """Return a textual representation of the s-expression representing the vector.""" return Z3_ast_vector_to_string(self.ctx.ref(), self.vector) ######################################### # # AST Map # ######################################### class AstMap: """A mapping from ASTs to ASTs.""" def __init__(self, m=None, ctx=None): self.map = None if m == None: self.ctx = _get_ctx(ctx) self.map = Z3_mk_ast_map(self.ctx.ref()) else: self.map = m assert ctx != None self.ctx = ctx Z3_ast_map_inc_ref(self.ctx.ref(), self.map) def __del__(self): if self.map != None: Z3_ast_map_dec_ref(self.ctx.ref(), self.map) def __len__(self): """Return the size of the map. >>> M = AstMap() >>> len(M) 0 >>> x = Int('x') >>> M[x] = IntVal(1) >>> len(M) 1 """ return int(Z3_ast_map_size(self.ctx.ref(), self.map)) def __contains__(self, key): """Return `True` if the map contains key `key`. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> x in M True >>> x+1 in M False """ return Z3_ast_map_contains(self.ctx.ref(), self.map, key.as_ast()) def __getitem__(self, key): """Retrieve the value associated with key `key`. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> M[x] x + 1 """ return _to_ast_ref(Z3_ast_map_find(self.ctx.ref(), self.map, key.as_ast()), self.ctx) def __setitem__(self, k, v): """Add/Update key `k` with value `v`. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> len(M) 1 >>> M[x] x + 1 >>> M[x] = IntVal(1) >>> M[x] 1 """ Z3_ast_map_insert(self.ctx.ref(), self.map, k.as_ast(), v.as_ast()) def __repr__(self): return Z3_ast_map_to_string(self.ctx.ref(), self.map) def erase(self, k): """Remove the entry associated with key `k`. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> len(M) 1 >>> M.erase(x) >>> len(M) 0 """ Z3_ast_map_erase(self.ctx.ref(), self.map, k.as_ast()) def reset(self): """Remove all entries from the map. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> M[x+x] = IntVal(1) >>> len(M) 2 >>> M.reset() >>> len(M) 0 """ Z3_ast_map_reset(self.ctx.ref(), self.map) def keys(self): """Return an AstVector containing all keys in the map. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> M[x+x] = IntVal(1) >>> M.keys() [x, x + x] """ return AstVector(Z3_ast_map_keys(self.ctx.ref(), self.map), self.ctx) ######################################### # # Model # ######################################### class FuncEntry: """Store the value of the interpretation of a function in a particular point.""" def __init__(self, entry, ctx): self.entry = entry self.ctx = ctx Z3_func_entry_inc_ref(self.ctx.ref(), self.entry) def __del__(self): Z3_func_entry_dec_ref(self.ctx.ref(), self.entry) def num_args(self): """Return the number of arguments in the given entry. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0, 1) == 10, f(1, 2) == 20, f(1, 0) == 10) >>> s.check() sat >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() 3 >>> e = f_i.entry(0) >>> e.num_args() 2 """ return int(Z3_func_entry_get_num_args(self.ctx.ref(), self.entry)) def arg_value(self, idx): """Return the value of argument `idx`. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0, 1) == 10, f(1, 2) == 20, f(1, 0) == 10) >>> s.check() sat >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() 3 >>> e = f_i.entry(0) >>> e [0, 1, 10] >>> e.num_args() 2 >>> e.arg_value(0) 0 >>> e.arg_value(1) 1 >>> try: ... e.arg_value(2) ... except IndexError: ... print("index error") index error """ if idx >= self.num_args(): raise IndexError return _to_expr_ref(Z3_func_entry_get_arg(self.ctx.ref(), self.entry, idx), self.ctx) def value(self): """Return the value of the function at point `self`. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0, 1) == 10, f(1, 2) == 20, f(1, 0) == 10) >>> s.check() sat >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() 3 >>> e = f_i.entry(0) >>> e [0, 1, 10] >>> e.num_args() 2 >>> e.value() 10 """ return _to_expr_ref(Z3_func_entry_get_value(self.ctx.ref(), self.entry), self.ctx) def as_list(self): """Return entry `self` as a Python list. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0, 1) == 10, f(1, 2) == 20, f(1, 0) == 10) >>> s.check() sat >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() 3 >>> e = f_i.entry(0) >>> e.as_list() [0, 1, 10] """ args = [ self.arg_value(i) for i in range(self.num_args())] args.append(self.value()) return args def __repr__(self): return repr(self.as_list()) class FuncInterp(Z3PPObject): """Stores the interpretation of a function in a Z3 model.""" def __init__(self, f, ctx): self.f = f self.ctx = ctx if self.f != None: Z3_func_interp_inc_ref(self.ctx.ref(), self.f) def __del__(self): if self.f != None: Z3_func_interp_dec_ref(self.ctx.ref(), self.f) def else_value(self): """ Return the `else` value for a function interpretation. Return None if Z3 did not specify the `else` value for this object. >>> f = Function('f', IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0) == 1, f(1) == 1, f(2) == 0) >>> s.check() sat >>> m = s.model() >>> m[f] [0 -> 1, 1 -> 1, 2 -> 0, else -> 1] >>> m[f].else_value() 1 """ r = Z3_func_interp_get_else(self.ctx.ref(), self.f) if r: return _to_expr_ref(r, self.ctx) else: return None def num_entries(self): """Return the number of entries/points in the function interpretation `self`. >>> f = Function('f', IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0) == 1, f(1) == 1, f(2) == 0) >>> s.check() sat >>> m = s.model() >>> m[f] [0 -> 1, 1 -> 1, 2 -> 0, else -> 1] >>> m[f].num_entries() 3 """ return int(Z3_func_interp_get_num_entries(self.ctx.ref(), self.f)) def arity(self): """Return the number of arguments for each entry in the function interpretation `self`. >>> f = Function('f', IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0) == 1, f(1) == 1, f(2) == 0) >>> s.check() sat >>> m = s.model() >>> m[f].arity() 1 """ return int(Z3_func_interp_get_arity(self.ctx.ref(), self.f)) def entry(self, idx): """Return an entry at position `idx < self.num_entries()` in the function interpretation `self`. >>> f = Function('f', IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0) == 1, f(1) == 1, f(2) == 0) >>> s.check() sat >>> m = s.model() >>> m[f] [0 -> 1, 1 -> 1, 2 -> 0, else -> 1] >>> m[f].num_entries() 3 >>> m[f].entry(0) [0, 1] >>> m[f].entry(1) [1, 1] >>> m[f].entry(2) [2, 0] """ if idx >= self.num_entries(): raise IndexError return FuncEntry(Z3_func_interp_get_entry(self.ctx.ref(), self.f, idx), self.ctx) def as_list(self): """Return the function interpretation as a Python list. >>> f = Function('f', IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0) == 1, f(1) == 1, f(2) == 0) >>> s.check() sat >>> m = s.model() >>> m[f] [0 -> 1, 1 -> 1, 2 -> 0, else -> 1] >>> m[f].as_list() [[0, 1], [1, 1], [2, 0], 1] """ r = [ self.entry(i).as_list() for i in range(self.num_entries())] r.append(self.else_value()) return r def __repr__(self): return obj_to_string(self) class ModelRef(Z3PPObject): """Model/Solution of a satisfiability problem (aka system of constraints).""" def __init__(self, m, ctx): assert ctx != None self.model = m self.ctx = ctx Z3_model_inc_ref(self.ctx.ref(), self.model) def __del__(self): Z3_model_dec_ref(self.ctx.ref(), self.model) def __repr__(self): return obj_to_string(self) def sexpr(self): """Return a textual representation of the s-expression representing the model.""" return Z3_model_to_string(self.ctx.ref(), self.model) def eval(self, t, model_completion=False): """Evaluate the expression `t` in the model `self`. If `model_completion` is enabled, then a default interpretation is automatically added for symbols that do not have an interpretation in the model `self`. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2) >>> s.check() sat >>> m = s.model() >>> m.eval(x + 1) 2 >>> m.eval(x == 1) True >>> y = Int('y') >>> m.eval(y + x) 1 + y >>> m.eval(y) y >>> m.eval(y, model_completion=True) 0 >>> # Now, m contains an interpretation for y >>> m.eval(y + x) 1 """ r = (Ast * 1)() if Z3_model_eval(self.ctx.ref(), self.model, t.as_ast(), model_completion, r): return _to_expr_ref(r[0], self.ctx) raise Z3Exception("failed to evaluate expression in the model") def evaluate(self, t, model_completion=False): """Alias for `eval`. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2) >>> s.check() sat >>> m = s.model() >>> m.evaluate(x + 1) 2 >>> m.evaluate(x == 1) True >>> y = Int('y') >>> m.evaluate(y + x) 1 + y >>> m.evaluate(y) y >>> m.evaluate(y, model_completion=True) 0 >>> # Now, m contains an interpretation for y >>> m.evaluate(y + x) 1 """ return self.eval(t, model_completion) def __len__(self): """Return the number of constant and function declarations in the model `self`. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, f(x) != x) >>> s.check() sat >>> m = s.model() >>> len(m) 2 """ return int(Z3_model_get_num_consts(self.ctx.ref(), self.model)) + int(Z3_model_get_num_funcs(self.ctx.ref(), self.model)) def get_interp(self, decl): """Return the interpretation for a given declaration or constant. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2, f(x) == 0) >>> s.check() sat >>> m = s.model() >>> m[x] 1 >>> m[f] [1 -> 0, else -> 0] """ if __debug__: _z3_assert(isinstance(decl, FuncDeclRef) or is_const(decl), "Z3 declaration expected") if is_const(decl): decl = decl.decl() try: if decl.arity() == 0: r = _to_expr_ref(Z3_model_get_const_interp(self.ctx.ref(), self.model, decl.ast), self.ctx) if is_as_array(r): return self.get_interp(get_as_array_func(r)) else: return r else: return FuncInterp(Z3_model_get_func_interp(self.ctx.ref(), self.model, decl.ast), self.ctx) except Z3Exception: return None def num_sorts(self): """Return the number of unintepreted sorts that contain an interpretation in the model `self`. >>> A = DeclareSort('A') >>> a, b = Consts('a b', A) >>> s = Solver() >>> s.add(a != b) >>> s.check() sat >>> m = s.model() >>> m.num_sorts() 1 """ return int(Z3_model_get_num_sorts(self.ctx.ref(), self.model)) def get_sort(self, idx): """Return the unintepreted sort at position `idx` < self.num_sorts(). >>> A = DeclareSort('A') >>> B = DeclareSort('B') >>> a1, a2 = Consts('a1 a2', A) >>> b1, b2 = Consts('b1 b2', B) >>> s = Solver() >>> s.add(a1 != a2, b1 != b2) >>> s.check() sat >>> m = s.model() >>> m.num_sorts() 2 >>> m.get_sort(0) A >>> m.get_sort(1) B """ if idx >= self.num_sorts(): raise IndexError return _to_sort_ref(Z3_model_get_sort(self.ctx.ref(), self.model, idx), self.ctx) def sorts(self): """Return all uninterpreted sorts that have an interpretation in the model `self`. >>> A = DeclareSort('A') >>> B = DeclareSort('B') >>> a1, a2 = Consts('a1 a2', A) >>> b1, b2 = Consts('b1 b2', B) >>> s = Solver() >>> s.add(a1 != a2, b1 != b2) >>> s.check() sat >>> m = s.model() >>> m.sorts() [A, B] """ return [ self.get_sort(i) for i in range(self.num_sorts()) ] def get_universe(self, s): """Return the intepretation for the uninterpreted sort `s` in the model `self`. >>> A = DeclareSort('A') >>> a, b = Consts('a b', A) >>> s = Solver() >>> s.add(a != b) >>> s.check() sat >>> m = s.model() >>> m.get_universe(A) [A!val!0, A!val!1] """ if __debug__: _z3_assert(isinstance(s, SortRef), "Z3 sort expected") try: return AstVector(Z3_model_get_sort_universe(self.ctx.ref(), self.model, s.ast), self.ctx) except Z3Exception: return None def __getitem__(self, idx): """If `idx` is an integer, then the declaration at position `idx` in the model `self` is returned. If `idx` is a declaration, then the actual interpreation is returned. The elements can be retrieved using position or the actual declaration. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2, f(x) == 0) >>> s.check() sat >>> m = s.model() >>> len(m) 2 >>> m[0] x >>> m[1] f >>> m[x] 1 >>> m[f] [1 -> 0, else -> 0] >>> for d in m: print("%s -> %s" % (d, m[d])) x -> 1 f -> [1 -> 0, else -> 0] """ if isinstance(idx, int): if idx >= len(self): raise IndexError num_consts = Z3_model_get_num_consts(self.ctx.ref(), self.model) if (idx < num_consts): return FuncDeclRef(Z3_model_get_const_decl(self.ctx.ref(), self.model, idx), self.ctx) else: return FuncDeclRef(Z3_model_get_func_decl(self.ctx.ref(), self.model, idx - num_consts), self.ctx) if isinstance(idx, FuncDeclRef): return self.get_interp(idx) if is_const(idx): return self.get_interp(idx.decl()) if isinstance(idx, SortRef): return self.get_universe(idx) if __debug__: _z3_assert(False, "Integer, Z3 declaration, or Z3 constant expected") return None def decls(self): """Return a list with all symbols that have an interpreation in the model `self`. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2, f(x) == 0) >>> s.check() sat >>> m = s.model() >>> m.decls() [x, f] """ r = [] for i in range(Z3_model_get_num_consts(self.ctx.ref(), self.model)): r.append(FuncDeclRef(Z3_model_get_const_decl(self.ctx.ref(), self.model, i), self.ctx)) for i in range(Z3_model_get_num_funcs(self.ctx.ref(), self.model)): r.append(FuncDeclRef(Z3_model_get_func_decl(self.ctx.ref(), self.model, i), self.ctx)) return r def is_as_array(n): """Return true if n is a Z3 expression of the form (_ as-array f).""" return isinstance(n, ExprRef) and Z3_is_as_array(n.ctx.ref(), n.as_ast()) def get_as_array_func(n): """Return the function declaration f associated with a Z3 expression of the form (_ as-array f).""" if __debug__: _z3_assert(is_as_array(n), "as-array Z3 expression expected.") return FuncDeclRef(Z3_get_as_array_func_decl(n.ctx.ref(), n.as_ast()), n.ctx) ######################################### # # Statistics # ######################################### class Statistics: """Statistics for `Solver.check()`.""" def __init__(self, stats, ctx): self.stats = stats self.ctx = ctx Z3_stats_inc_ref(self.ctx.ref(), self.stats) def __del__(self): Z3_stats_dec_ref(self.ctx.ref(), self.stats) def __repr__(self): if in_html_mode(): out = io.StringIO() even = True out.write(u('')) for k, v in self: if even: out.write(u('')) even = False else: out.write(u('')) even = True out.write(u('' % (k, v))) out.write(u('
%s%s
')) return out.getvalue() else: return Z3_stats_to_string(self.ctx.ref(), self.stats) def __len__(self): """Return the number of statistical counters. >>> x = Int('x') >>> s = Then('simplify', 'nlsat').solver() >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> len(st) 6 """ return int(Z3_stats_size(self.ctx.ref(), self.stats)) def __getitem__(self, idx): """Return the value of statistical counter at position `idx`. The result is a pair (key, value). >>> x = Int('x') >>> s = Then('simplify', 'nlsat').solver() >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> len(st) 6 >>> st[0] ('nlsat propagations', 2) >>> st[1] ('nlsat stages', 2) """ if idx >= len(self): raise IndexError if Z3_stats_is_uint(self.ctx.ref(), self.stats, idx): val = int(Z3_stats_get_uint_value(self.ctx.ref(), self.stats, idx)) else: val = Z3_stats_get_double_value(self.ctx.ref(), self.stats, idx) return (Z3_stats_get_key(self.ctx.ref(), self.stats, idx), val) def keys(self): """Return the list of statistical counters. >>> x = Int('x') >>> s = Then('simplify', 'nlsat').solver() >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> st.keys() ['nlsat propagations', 'nlsat stages', 'rlimit count', 'max memory', 'memory', 'num allocs'] """ return [Z3_stats_get_key(self.ctx.ref(), self.stats, idx) for idx in range(len(self))] def get_key_value(self, key): """Return the value of a particular statistical counter. >>> x = Int('x') >>> s = Then('simplify', 'nlsat').solver() >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> st.get_key_value('nlsat propagations') 2 """ for idx in range(len(self)): if key == Z3_stats_get_key(self.ctx.ref(), self.stats, idx): if Z3_stats_is_uint(self.ctx.ref(), self.stats, idx): return int(Z3_stats_get_uint_value(self.ctx.ref(), self.stats, idx)) else: return Z3_stats_get_double_value(self.ctx.ref(), self.stats, idx) raise Z3Exception("unknown key") def __getattr__(self, name): """Access the value of statistical using attributes. Remark: to access a counter containing blank spaces (e.g., 'nlsat propagations'), we should use '_' (e.g., 'nlsat_propagations'). >>> x = Int('x') >>> s = Then('simplify', 'nlsat').solver() >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> st.keys() ['nlsat propagations', 'nlsat stages', 'rlimit count', 'max memory', 'memory', 'num allocs'] >>> st.nlsat_propagations 2 >>> st.nlsat_stages 2 """ key = name.replace('_', ' ') try: return self.get_key_value(key) except Z3Exception: raise AttributeError ######################################### # # Solver # ######################################### class CheckSatResult: """Represents the result of a satisfiability check: sat, unsat, unknown. >>> s = Solver() >>> s.check() sat >>> r = s.check() >>> isinstance(r, CheckSatResult) True """ def __init__(self, r): self.r = r def __eq__(self, other): return isinstance(other, CheckSatResult) and self.r == other.r def __ne__(self, other): return not self.__eq__(other) def __repr__(self): if in_html_mode(): if self.r == Z3_L_TRUE: return "sat" elif self.r == Z3_L_FALSE: return "unsat" else: return "unknown" else: if self.r == Z3_L_TRUE: return "sat" elif self.r == Z3_L_FALSE: return "unsat" else: return "unknown" sat = CheckSatResult(Z3_L_TRUE) unsat = CheckSatResult(Z3_L_FALSE) unknown = CheckSatResult(Z3_L_UNDEF) class Solver(Z3PPObject): """Solver API provides methods for implementing the main SMT 2.0 commands: push, pop, check, get-model, etc.""" def __init__(self, solver=None, ctx=None): assert solver == None or ctx != None self.ctx = _get_ctx(ctx) self.solver = None if solver == None: self.solver = Z3_mk_solver(self.ctx.ref()) else: self.solver = solver Z3_solver_inc_ref(self.ctx.ref(), self.solver) def __del__(self): if self.solver != None: Z3_solver_dec_ref(self.ctx.ref(), self.solver) def set(self, *args, **keys): """Set a configuration option. The method `help()` return a string containing all available options. >>> s = Solver() >>> # The option MBQI can be set using three different approaches. >>> s.set(mbqi=True) >>> s.set('MBQI', True) >>> s.set(':mbqi', True) """ p = args2params(args, keys, self.ctx) Z3_solver_set_params(self.ctx.ref(), self.solver, p.params) def push(self): """Create a backtracking point. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0) >>> s [x > 0] >>> s.push() >>> s.add(x < 1) >>> s [x > 0, x < 1] >>> s.check() unsat >>> s.pop() >>> s.check() sat >>> s [x > 0] """ Z3_solver_push(self.ctx.ref(), self.solver) def pop(self, num=1): """Backtrack \c num backtracking points. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0) >>> s [x > 0] >>> s.push() >>> s.add(x < 1) >>> s [x > 0, x < 1] >>> s.check() unsat >>> s.pop() >>> s.check() sat >>> s [x > 0] """ Z3_solver_pop(self.ctx.ref(), self.solver, num) def reset(self): """Remove all asserted constraints and backtracking points created using `push()`. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0) >>> s [x > 0] >>> s.reset() >>> s [] """ Z3_solver_reset(self.ctx.ref(), self.solver) def assert_exprs(self, *args): """Assert constraints into the solver. >>> x = Int('x') >>> s = Solver() >>> s.assert_exprs(x > 0, x < 2) >>> s [x > 0, x < 2] """ args = _get_args(args) s = BoolSort(self.ctx) for arg in args: if isinstance(arg, Goal) or isinstance(arg, AstVector): for f in arg: Z3_solver_assert(self.ctx.ref(), self.solver, f.as_ast()) else: arg = s.cast(arg) Z3_solver_assert(self.ctx.ref(), self.solver, arg.as_ast()) def add(self, *args): """Assert constraints into the solver. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2) >>> s [x > 0, x < 2] """ self.assert_exprs(*args) def append(self, *args): """Assert constraints into the solver. >>> x = Int('x') >>> s = Solver() >>> s.append(x > 0, x < 2) >>> s [x > 0, x < 2] """ self.assert_exprs(*args) def insert(self, *args): """Assert constraints into the solver. >>> x = Int('x') >>> s = Solver() >>> s.insert(x > 0, x < 2) >>> s [x > 0, x < 2] """ self.assert_exprs(*args) def assert_and_track(self, a, p): """Assert constraint `a` and track it in the unsat core using the Boolean constant `p`. If `p` is a string, it will be automatically converted into a Boolean constant. >>> x = Int('x') >>> p3 = Bool('p3') >>> s = Solver() >>> s.set(unsat_core=True) >>> s.assert_and_track(x > 0, 'p1') >>> s.assert_and_track(x != 1, 'p2') >>> s.assert_and_track(x < 0, p3) >>> print(s.check()) unsat >>> c = s.unsat_core() >>> len(c) 2 >>> Bool('p1') in c True >>> Bool('p2') in c False >>> p3 in c True """ if isinstance(p, str): p = Bool(p, self.ctx) _z3_assert(isinstance(a, BoolRef), "Boolean expression expected") _z3_assert(isinstance(p, BoolRef) and is_const(p), "Boolean expression expected") Z3_solver_assert_and_track(self.ctx.ref(), self.solver, a.as_ast(), p.as_ast()) def check(self, *assumptions): """Check whether the assertions in the given solver plus the optional assumptions are consistent or not. >>> x = Int('x') >>> s = Solver() >>> s.check() sat >>> s.add(x > 0, x < 2) >>> s.check() sat >>> s.model() [x = 1] >>> s.add(x < 1) >>> s.check() unsat >>> s.reset() >>> s.add(2**x == 4) >>> s.check() unknown """ assumptions = _get_args(assumptions) num = len(assumptions) _assumptions = (Ast * num)() for i in range(num): _assumptions[i] = assumptions[i].as_ast() r = Z3_solver_check_assumptions(self.ctx.ref(), self.solver, num, _assumptions) return CheckSatResult(r) def model(self): """Return a model for the last `check()`. This function raises an exception if a model is not available (e.g., last `check()` returned unsat). >>> s = Solver() >>> a = Int('a') >>> s.add(a + 2 == 0) >>> s.check() sat >>> s.model() [a = -2] """ try: return ModelRef(Z3_solver_get_model(self.ctx.ref(), self.solver), self.ctx) except Z3Exception: raise Z3Exception("model is not available") def unsat_core(self): """Return a subset (as an AST vector) of the assumptions provided to the last check(). These are the assumptions Z3 used in the unsatisfiability proof. Assumptions are available in Z3. They are used to extract unsatisfiable cores. They may be also used to "retract" assumptions. Note that, assumptions are not really "soft constraints", but they can be used to implement them. >>> p1, p2, p3 = Bools('p1 p2 p3') >>> x, y = Ints('x y') >>> s = Solver() >>> s.add(Implies(p1, x > 0)) >>> s.add(Implies(p2, y > x)) >>> s.add(Implies(p2, y < 1)) >>> s.add(Implies(p3, y > -3)) >>> s.check(p1, p2, p3) unsat >>> core = s.unsat_core() >>> len(core) 2 >>> p1 in core True >>> p2 in core True >>> p3 in core False >>> # "Retracting" p2 >>> s.check(p1, p3) sat """ return AstVector(Z3_solver_get_unsat_core(self.ctx.ref(), self.solver), self.ctx) def proof(self): """Return a proof for the last `check()`. Proof construction must be enabled.""" return _to_expr_ref(Z3_solver_get_proof(self.ctx.ref(), self.solver), self.ctx) def assertions(self): """Return an AST vector containing all added constraints. >>> s = Solver() >>> s.assertions() [] >>> a = Int('a') >>> s.add(a > 0) >>> s.add(a < 10) >>> s.assertions() [a > 0, a < 10] """ return AstVector(Z3_solver_get_assertions(self.ctx.ref(), self.solver), self.ctx) def statistics(self): """Return statistics for the last `check()`. >>> s = SimpleSolver() >>> x = Int('x') >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> st.get_key_value('final checks') 1 >>> len(st) > 0 True >>> st[0] != 0 True """ return Statistics(Z3_solver_get_statistics(self.ctx.ref(), self.solver), self.ctx) def reason_unknown(self): """Return a string describing why the last `check()` returned `unknown`. >>> x = Int('x') >>> s = SimpleSolver() >>> s.add(2**x == 4) >>> s.check() unknown >>> s.reason_unknown() '(incomplete (theory arithmetic))' """ return Z3_solver_get_reason_unknown(self.ctx.ref(), self.solver) def help(self): """Display a string describing all available options.""" print(Z3_solver_get_help(self.ctx.ref(), self.solver)) def param_descrs(self): """Return the parameter description set.""" return ParamDescrsRef(Z3_solver_get_param_descrs(self.ctx.ref(), self.solver), self.ctx) def __repr__(self): """Return a formatted string with all added constraints.""" return obj_to_string(self) def sexpr(self): """Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0) >>> s.add(x < 2) >>> r = s.sexpr() """ return Z3_solver_to_string(self.ctx.ref(), self.solver) def to_smt2(self): """return SMTLIB2 formatted benchmark for solver's assertions""" es = self.assertions() sz = len(es) sz1 = sz if sz1 > 0: sz1 -= 1 v = (Ast * sz1)() for i in range(sz1): v[i] = es[i].as_ast() if sz > 0: e = es[sz1].as_ast() else: e = BoolVal(True, self.ctx).as_ast() return Z3_benchmark_to_smtlib_string(self.ctx.ref(), "benchmark generated from python API", "", "unknown", "", sz1, v, e) def SolverFor(logic, ctx=None): """Create a solver customized for the given logic. The parameter `logic` is a string. It should be contains the name of a SMT-LIB logic. See http://www.smtlib.org/ for the name of all available logics. >>> s = SolverFor("QF_LIA") >>> x = Int('x') >>> s.add(x > 0) >>> s.add(x < 2) >>> s.check() sat >>> s.model() [x = 1] """ ctx = _get_ctx(ctx) logic = to_symbol(logic) return Solver(Z3_mk_solver_for_logic(ctx.ref(), logic), ctx) def SimpleSolver(ctx=None): """Return a simple general purpose solver with limited amount of preprocessing. >>> s = SimpleSolver() >>> x = Int('x') >>> s.add(x > 0) >>> s.check() sat """ ctx = _get_ctx(ctx) return Solver(Z3_mk_simple_solver(ctx.ref()), ctx) ######################################### # # Fixedpoint # ######################################### class Fixedpoint(Z3PPObject): """Fixedpoint API provides methods for solving with recursive predicates""" def __init__(self, fixedpoint=None, ctx=None): assert fixedpoint == None or ctx != None self.ctx = _get_ctx(ctx) self.fixedpoint = None if fixedpoint == None: self.fixedpoint = Z3_mk_fixedpoint(self.ctx.ref()) else: self.fixedpoint = fixedpoint Z3_fixedpoint_inc_ref(self.ctx.ref(), self.fixedpoint) self.vars = [] def __del__(self): if self.fixedpoint != None: Z3_fixedpoint_dec_ref(self.ctx.ref(), self.fixedpoint) def set(self, *args, **keys): """Set a configuration option. The method `help()` return a string containing all available options. """ p = args2params(args, keys, self.ctx) Z3_fixedpoint_set_params(self.ctx.ref(), self.fixedpoint, p.params) def help(self): """Display a string describing all available options.""" print(Z3_fixedpoint_get_help(self.ctx.ref(), self.fixedpoint)) def param_descrs(self): """Return the parameter description set.""" return ParamDescrsRef(Z3_fixedpoint_get_param_descrs(self.ctx.ref(), self.fixedpoint), self.ctx) def assert_exprs(self, *args): """Assert constraints as background axioms for the fixedpoint solver.""" args = _get_args(args) s = BoolSort(self.ctx) for arg in args: if isinstance(arg, Goal) or isinstance(arg, AstVector): for f in arg: f = self.abstract(f) Z3_fixedpoint_assert(self.ctx.ref(), self.fixedpoint, f.as_ast()) else: arg = s.cast(arg) arg = self.abstract(arg) Z3_fixedpoint_assert(self.ctx.ref(), self.fixedpoint, arg.as_ast()) def add(self, *args): """Assert constraints as background axioms for the fixedpoint solver. Alias for assert_expr.""" self.assert_exprs(*args) def append(self, *args): """Assert constraints as background axioms for the fixedpoint solver. Alias for assert_expr.""" self.assert_exprs(*args) def insert(self, *args): """Assert constraints as background axioms for the fixedpoint solver. Alias for assert_expr.""" self.assert_exprs(*args) def add_rule(self, head, body = None, name = None): """Assert rules defining recursive predicates to the fixedpoint solver. >>> a = Bool('a') >>> b = Bool('b') >>> s = Fixedpoint() >>> s.register_relation(a.decl()) >>> s.register_relation(b.decl()) >>> s.fact(a) >>> s.rule(b, a) >>> s.query(b) sat """ if name == None: name = "" name = to_symbol(name, self.ctx) if body == None: head = self.abstract(head) Z3_fixedpoint_add_rule(self.ctx.ref(), self.fixedpoint, head.as_ast(), name) else: body = _get_args(body) f = self.abstract(Implies(And(body, self.ctx),head)) Z3_fixedpoint_add_rule(self.ctx.ref(), self.fixedpoint, f.as_ast(), name) def rule(self, head, body = None, name = None): """Assert rules defining recursive predicates to the fixedpoint solver. Alias for add_rule.""" self.add_rule(head, body, name) def fact(self, head, name = None): """Assert facts defining recursive predicates to the fixedpoint solver. Alias for add_rule.""" self.add_rule(head, None, name) def query(self, *query): """Query the fixedpoint engine whether formula is derivable. You can also pass an tuple or list of recursive predicates. """ query = _get_args(query) sz = len(query) if sz >= 1 and isinstance(query[0], FuncDeclRef): _decls = (FuncDecl * sz)() i = 0 for q in query: _decls[i] = q.ast i = i + 1 r = Z3_fixedpoint_query_relations(self.ctx.ref(), self.fixedpoint, sz, _decls) else: if sz == 1: query = query[0] else: query = And(query, self.ctx) query = self.abstract(query, False) r = Z3_fixedpoint_query(self.ctx.ref(), self.fixedpoint, query.as_ast()) return CheckSatResult(r) def push(self): """create a backtracking point for added rules, facts and assertions""" Z3_fixedpoint_push(self.ctx.ref(), self.fixedpoint) def pop(self): """restore to previously created backtracking point""" Z3_fixedpoint_pop(self.ctx.ref(), self.fixedpoint) def update_rule(self, head, body, name): """update rule""" if name == None: name = "" name = to_symbol(name, self.ctx) body = _get_args(body) f = self.abstract(Implies(And(body, self.ctx),head)) Z3_fixedpoint_update_rule(self.ctx.ref(), self.fixedpoint, f.as_ast(), name) def get_answer(self): """Retrieve answer from last query call.""" r = Z3_fixedpoint_get_answer(self.ctx.ref(), self.fixedpoint) return _to_expr_ref(r, self.ctx) def get_num_levels(self, predicate): """Retrieve number of levels used for predicate in PDR engine""" return Z3_fixedpoint_get_num_levels(self.ctx.ref(), self.fixedpoint, predicate.ast) def get_cover_delta(self, level, predicate): """Retrieve properties known about predicate for the level'th unfolding. -1 is treated as the limit (infinity)""" r = Z3_fixedpoint_get_cover_delta(self.ctx.ref(), self.fixedpoint, level, predicate.ast) return _to_expr_ref(r, self.ctx) def add_cover(self, level, predicate, property): """Add property to predicate for the level'th unfolding. -1 is treated as infinity (infinity)""" Z3_fixedpoint_add_cover(self.ctx.ref(), self.fixedpoint, level, predicate.ast, property.ast) def register_relation(self, *relations): """Register relation as recursive""" relations = _get_args(relations) for f in relations: Z3_fixedpoint_register_relation(self.ctx.ref(), self.fixedpoint, f.ast) def set_predicate_representation(self, f, *representations): """Control how relation is represented""" representations = _get_args(representations) representations = [to_symbol(s) for s in representations] sz = len(representations) args = (Symbol * sz)() for i in range(sz): args[i] = representations[i] Z3_fixedpoint_set_predicate_representation(self.ctx.ref(), self.fixedpoint, f.ast, sz, args) def parse_string(self, s): """Parse rules and queries from a string""" return AstVector(Z3_fixedpoint_from_string(self.ctx.ref(), self.fixedpoint, s), self.ctx) def parse_file(self, f): """Parse rules and queries from a file""" return AstVector(Z3_fixedpoint_from_file(self.ctx.ref(), self.fixedpoint, f), self.ctx) def get_rules(self): """retrieve rules that have been added to fixedpoint context""" return AstVector(Z3_fixedpoint_get_rules(self.ctx.ref(), self.fixedpoint), self.ctx) def get_assertions(self): """retrieve assertions that have been added to fixedpoint context""" return AstVector(Z3_fixedpoint_get_assertions(self.ctx.ref(), self.fixedpoint), self.ctx) def __repr__(self): """Return a formatted string with all added rules and constraints.""" return self.sexpr() def sexpr(self): """Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format. """ return Z3_fixedpoint_to_string(self.ctx.ref(), self.fixedpoint, 0, (Ast * 0)()) def to_string(self, queries): """Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format. Include also queries. """ args, len = _to_ast_array(queries) return Z3_fixedpoint_to_string(self.ctx.ref(), self.fixedpoint, len, args) def statistics(self): """Return statistics for the last `query()`. """ return Statistics(Z3_fixedpoint_get_statistics(self.ctx.ref(), self.fixedpoint), self.ctx) def reason_unknown(self): """Return a string describing why the last `query()` returned `unknown`. """ return Z3_fixedpoint_get_reason_unknown(self.ctx.ref(), self.fixedpoint) def declare_var(self, *vars): """Add variable or several variables. The added variable or variables will be bound in the rules and queries """ vars = _get_args(vars) for v in vars: self.vars += [v] def abstract(self, fml, is_forall=True): if self.vars == []: return fml if is_forall: return ForAll(self.vars, fml) else: return Exists(self.vars, fml) ######################################### # # Finite domain sorts # ######################################### class FiniteDomainSortRef(SortRef): """Finite domain sort.""" def size(self): """Return the size of the finite domain sort""" r = (ctype.c_ulonglong * 1)() if Z3_get_finite_domain_sort_size(self.ctx_ref(), self.ast(), r): return r[0] else: raise Z3Exception("Failed to retrieve finite domain sort size") def FiniteDomainSort(name, sz, ctx=None): """Create a named finite domain sort of a given size sz""" ctx = _get_ctx(ctx) return FiniteDomainSortRef(Z3_mk_finite_domain_sort(ctx.ref(), name, sz), ctx) ######################################### # # Optimize # ######################################### class OptimizeObjective: def __init__(self, opt, value, is_max): self._opt = opt self._value = value self._is_max = is_max def lower(self): opt = self._opt return _to_expr_ref(Z3_optimize_get_lower(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) def upper(self): opt = self._opt return _to_expr_ref(Z3_optimize_get_upper(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) def value(self): if self._is_max: return self.upper() else: return self.lower() class Optimize(Z3PPObject): """Optimize API provides methods for solving using objective functions and weighted soft constraints""" def __init__(self, ctx=None): self.ctx = _get_ctx(ctx) self.optimize = Z3_mk_optimize(self.ctx.ref()) Z3_optimize_inc_ref(self.ctx.ref(), self.optimize) def __del__(self): if self.optimize != None: Z3_optimize_dec_ref(self.ctx.ref(), self.optimize) def set(self, *args, **keys): """Set a configuration option. The method `help()` return a string containing all available options. """ p = args2params(args, keys, self.ctx) Z3_optimize_set_params(self.ctx.ref(), self.optimize, p.params) def help(self): """Display a string describing all available options.""" print(Z3_optimize_get_help(self.ctx.ref(), self.optimize)) def param_descrs(self): """Return the parameter description set.""" return ParamDescrsRef(Z3_optimize_get_param_descrs(self.ctx.ref(), self.optimize), self.ctx) def assert_exprs(self, *args): """Assert constraints as background axioms for the optimize solver.""" args = _get_args(args) for arg in args: if isinstance(arg, Goal) or isinstance(arg, AstVector): for f in arg: Z3_optimize_assert(self.ctx.ref(), self.optimize, f.as_ast()) else: Z3_optimize_assert(self.ctx.ref(), self.optimize, arg.as_ast()) def add(self, *args): """Assert constraints as background axioms for the optimize solver. Alias for assert_expr.""" self.assert_exprs(*args) def add_soft(self, arg, weight = "1", id = None): """Add soft constraint with optional weight and optional identifier. If no weight is supplied, then the penalty for violating the soft constraint is 1. Soft constraints are grouped by identifiers. Soft constraints that are added without identifiers are grouped by default. """ if _is_int(weight): weight = "%d" % weight if not isinstance(weight, str): raise Z3Exception("weight should be a string or an integer") if id == None: id = "" id = to_symbol(id, self.ctx) v = Z3_optimize_assert_soft(self.ctx.ref(), self.optimize, arg.as_ast(), weight, id) return OptimizeObjective(self, v, False) def maximize(self, arg): """Add objective function to maximize.""" return OptimizeObjective(self, Z3_optimize_maximize(self.ctx.ref(), self.optimize, arg.as_ast()), True) def minimize(self, arg): """Add objective function to minimize.""" return OptimizeObjective(self, Z3_optimize_minimize(self.ctx.ref(), self.optimize, arg.as_ast()), False) def push(self): """create a backtracking point for added rules, facts and assertions""" Z3_optimize_push(self.ctx.ref(), self.optimize) def pop(self): """restore to previously created backtracking point""" Z3_optimize_pop(self.ctx.ref(), self.optimize) def check(self): """Check satisfiability while optimizing objective functions.""" return CheckSatResult(Z3_optimize_check(self.ctx.ref(), self.optimize)) def reason_unknown(self): """Return a string that describes why the last `check()` returned `unknown`.""" return Z3_optimize_get_reason_unknown(self.ctx.ref(), self.optimize) def model(self): """Return a model for the last check().""" try: return ModelRef(Z3_optimize_get_model(self.ctx.ref(), self.optimize), self.ctx) except Z3Exception: raise Z3Exception("model is not available") def lower(self, obj): if not isinstance(obj, OptimizeObjective): raise Z3Exception("Expecting objective handle returned by maximize/minimize") return obj.lower() def upper(self, obj): if not isinstance(obj, OptimizeObjective): raise Z3Exception("Expecting objective handle returned by maximize/minimize") return obj.upper() def __repr__(self): """Return a formatted string with all added rules and constraints.""" return self.sexpr() def sexpr(self): """Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format. """ return Z3_optimize_to_string(self.ctx.ref(), self.optimize) def statistics(self): """Return statistics for the last `query()`. """ return Statistics(Z3_optimize_get_statistics(self.ctx.ref(), self.optimize), self.ctx) ######################################### # # ApplyResult # ######################################### class ApplyResult(Z3PPObject): """An ApplyResult object contains the subgoals produced by a tactic when applied to a goal. It also contains model and proof converters.""" def __init__(self, result, ctx): self.result = result self.ctx = ctx Z3_apply_result_inc_ref(self.ctx.ref(), self.result) def __del__(self): Z3_apply_result_dec_ref(self.ctx.ref(), self.result) def __len__(self): """Return the number of subgoals in `self`. >>> a, b = Ints('a b') >>> g = Goal() >>> g.add(Or(a == 0, a == 1), Or(b == 0, b == 1), a > b) >>> t = Tactic('split-clause') >>> r = t(g) >>> len(r) 2 >>> t = Then(Tactic('split-clause'), Tactic('split-clause')) >>> len(t(g)) 4 >>> t = Then(Tactic('split-clause'), Tactic('split-clause'), Tactic('propagate-values')) >>> len(t(g)) 1 """ return int(Z3_apply_result_get_num_subgoals(self.ctx.ref(), self.result)) def __getitem__(self, idx): """Return one of the subgoals stored in ApplyResult object `self`. >>> a, b = Ints('a b') >>> g = Goal() >>> g.add(Or(a == 0, a == 1), Or(b == 0, b == 1), a > b) >>> t = Tactic('split-clause') >>> r = t(g) >>> r[0] [a == 0, Or(b == 0, b == 1), a > b] >>> r[1] [a == 1, Or(b == 0, b == 1), a > b] """ if idx >= len(self): raise IndexError return Goal(goal=Z3_apply_result_get_subgoal(self.ctx.ref(), self.result, idx), ctx=self.ctx) def __repr__(self): return obj_to_string(self) def sexpr(self): """Return a textual representation of the s-expression representing the set of subgoals in `self`.""" return Z3_apply_result_to_string(self.ctx.ref(), self.result) def convert_model(self, model, idx=0): """Convert a model for a subgoal into a model for the original goal. >>> a, b = Ints('a b') >>> g = Goal() >>> g.add(Or(a == 0, a == 1), Or(b == 0, b == 1), a > b) >>> t = Then(Tactic('split-clause'), Tactic('solve-eqs')) >>> r = t(g) >>> r[0] [Or(b == 0, b == 1), Not(0 <= b)] >>> r[1] [Or(b == 0, b == 1), Not(1 <= b)] >>> # Remark: the subgoal r[0] is unsatisfiable >>> # Creating a solver for solving the second subgoal >>> s = Solver() >>> s.add(r[1]) >>> s.check() sat >>> s.model() [b = 0] >>> # Model s.model() does not assign a value to `a` >>> # It is a model for subgoal `r[1]`, but not for goal `g` >>> # The method convert_model creates a model for `g` from a model for `r[1]`. >>> r.convert_model(s.model(), 1) [b = 0, a = 1] """ if __debug__: _z3_assert(idx < len(self), "index out of bounds") _z3_assert(isinstance(model, ModelRef), "Z3 Model expected") return ModelRef(Z3_apply_result_convert_model(self.ctx.ref(), self.result, idx, model.model), self.ctx) def as_expr(self): """Return a Z3 expression consisting of all subgoals. >>> x = Int('x') >>> g = Goal() >>> g.add(x > 1) >>> g.add(Or(x == 2, x == 3)) >>> r = Tactic('simplify')(g) >>> r [[Not(x <= 1), Or(x == 2, x == 3)]] >>> r.as_expr() And(Not(x <= 1), Or(x == 2, x == 3)) >>> r = Tactic('split-clause')(g) >>> r [[x > 1, x == 2], [x > 1, x == 3]] >>> r.as_expr() Or(And(x > 1, x == 2), And(x > 1, x == 3)) """ sz = len(self) if sz == 0: return BoolVal(False, self.ctx) elif sz == 1: return self[0].as_expr() else: return Or([ self[i].as_expr() for i in range(len(self)) ]) ######################################### # # Tactics # ######################################### class Tactic: """Tactics transform, solver and/or simplify sets of constraints (Goal). A Tactic can be converted into a Solver using the method solver(). Several combinators are available for creating new tactics using the built-in ones: Then(), OrElse(), FailIf(), Repeat(), When(), Cond(). """ def __init__(self, tactic, ctx=None): self.ctx = _get_ctx(ctx) self.tactic = None if isinstance(tactic, TacticObj): self.tactic = tactic else: if __debug__: _z3_assert(isinstance(tactic, str), "tactic name expected") try: self.tactic = Z3_mk_tactic(self.ctx.ref(), str(tactic)) except Z3Exception: raise Z3Exception("unknown tactic '%s'" % tactic) Z3_tactic_inc_ref(self.ctx.ref(), self.tactic) def __del__(self): if self.tactic != None: Z3_tactic_dec_ref(self.ctx.ref(), self.tactic) def solver(self): """Create a solver using the tactic `self`. The solver supports the methods `push()` and `pop()`, but it will always solve each `check()` from scratch. >>> t = Then('simplify', 'nlsat') >>> s = t.solver() >>> x = Real('x') >>> s.add(x**2 == 2, x > 0) >>> s.check() sat >>> s.model() [x = 1.4142135623?] """ return Solver(Z3_mk_solver_from_tactic(self.ctx.ref(), self.tactic), self.ctx) def apply(self, goal, *arguments, **keywords): """Apply tactic `self` to the given goal or Z3 Boolean expression using the given options. >>> x, y = Ints('x y') >>> t = Tactic('solve-eqs') >>> t.apply(And(x == 0, y >= x + 1)) [[y >= 1]] """ if __debug__: _z3_assert(isinstance(goal, Goal) or isinstance(goal, BoolRef), "Z3 Goal or Boolean expressions expected") goal = _to_goal(goal) if len(arguments) > 0 or len(keywords) > 0: p = args2params(arguments, keywords, self.ctx) return ApplyResult(Z3_tactic_apply_ex(self.ctx.ref(), self.tactic, goal.goal, p.params), self.ctx) else: return ApplyResult(Z3_tactic_apply(self.ctx.ref(), self.tactic, goal.goal), self.ctx) def __call__(self, goal, *arguments, **keywords): """Apply tactic `self` to the given goal or Z3 Boolean expression using the given options. >>> x, y = Ints('x y') >>> t = Tactic('solve-eqs') >>> t(And(x == 0, y >= x + 1)) [[y >= 1]] """ return self.apply(goal, *arguments, **keywords) def help(self): """Display a string containing a description of the available options for the `self` tactic.""" print(Z3_tactic_get_help(self.ctx.ref(), self.tactic)) def param_descrs(self): """Return the parameter description set.""" return ParamDescrsRef(Z3_tactic_get_param_descrs(self.ctx.ref(), self.tactic), self.ctx) def _to_goal(a): if isinstance(a, BoolRef): goal = Goal(ctx = a.ctx) goal.add(a) return goal else: return a def _to_tactic(t, ctx=None): if isinstance(t, Tactic): return t else: return Tactic(t, ctx) def _and_then(t1, t2, ctx=None): t1 = _to_tactic(t1, ctx) t2 = _to_tactic(t2, ctx) if __debug__: _z3_assert(t1.ctx == t2.ctx, "Context mismatch") return Tactic(Z3_tactic_and_then(t1.ctx.ref(), t1.tactic, t2.tactic), t1.ctx) def _or_else(t1, t2, ctx=None): t1 = _to_tactic(t1, ctx) t2 = _to_tactic(t2, ctx) if __debug__: _z3_assert(t1.ctx == t2.ctx, "Context mismatch") return Tactic(Z3_tactic_or_else(t1.ctx.ref(), t1.tactic, t2.tactic), t1.ctx) def AndThen(*ts, **ks): """Return a tactic that applies the tactics in `*ts` in sequence. >>> x, y = Ints('x y') >>> t = AndThen(Tactic('simplify'), Tactic('solve-eqs')) >>> t(And(x == 0, y > x + 1)) [[Not(y <= 1)]] >>> t(And(x == 0, y > x + 1)).as_expr() Not(y <= 1) """ if __debug__: _z3_assert(len(ts) >= 2, "At least two arguments expected") ctx = ks.get('ctx', None) num = len(ts) r = ts[0] for i in range(num - 1): r = _and_then(r, ts[i+1], ctx) return r def Then(*ts, **ks): """Return a tactic that applies the tactics in `*ts` in sequence. Shorthand for AndThen(*ts, **ks). >>> x, y = Ints('x y') >>> t = Then(Tactic('simplify'), Tactic('solve-eqs')) >>> t(And(x == 0, y > x + 1)) [[Not(y <= 1)]] >>> t(And(x == 0, y > x + 1)).as_expr() Not(y <= 1) """ return AndThen(*ts, **ks) def OrElse(*ts, **ks): """Return a tactic that applies the tactics in `*ts` until one of them succeeds (it doesn't fail). >>> x = Int('x') >>> t = OrElse(Tactic('split-clause'), Tactic('skip')) >>> # Tactic split-clause fails if there is no clause in the given goal. >>> t(x == 0) [[x == 0]] >>> t(Or(x == 0, x == 1)) [[x == 0], [x == 1]] """ if __debug__: _z3_assert(len(ts) >= 2, "At least two arguments expected") ctx = ks.get('ctx', None) num = len(ts) r = ts[0] for i in range(num - 1): r = _or_else(r, ts[i+1], ctx) return r def ParOr(*ts, **ks): """Return a tactic that applies the tactics in `*ts` in parallel until one of them succeeds (it doesn't fail). >>> x = Int('x') >>> t = ParOr(Tactic('simplify'), Tactic('fail')) >>> t(x + 1 == 2) [[x == 1]] """ if __debug__: _z3_assert(len(ts) >= 2, "At least two arguments expected") ctx = _get_ctx(ks.get('ctx', None)) ts = [ _to_tactic(t, ctx) for t in ts ] sz = len(ts) _args = (TacticObj * sz)() for i in range(sz): _args[i] = ts[i].tactic return Tactic(Z3_tactic_par_or(ctx.ref(), sz, _args), ctx) def ParThen(t1, t2, ctx=None): """Return a tactic that applies t1 and then t2 to every subgoal produced by t1. The subgoals are processed in parallel. >>> x, y = Ints('x y') >>> t = ParThen(Tactic('split-clause'), Tactic('propagate-values')) >>> t(And(Or(x == 1, x == 2), y == x + 1)) [[x == 1, y == 2], [x == 2, y == 3]] """ t1 = _to_tactic(t1, ctx) t2 = _to_tactic(t2, ctx) if __debug__: _z3_assert(t1.ctx == t2.ctx, "Context mismatch") return Tactic(Z3_tactic_par_and_then(t1.ctx.ref(), t1.tactic, t2.tactic), t1.ctx) def ParAndThen(t1, t2, ctx=None): """Alias for ParThen(t1, t2, ctx).""" return ParThen(t1, t2, ctx) def With(t, *args, **keys): """Return a tactic that applies tactic `t` using the given configuration options. >>> x, y = Ints('x y') >>> t = With(Tactic('simplify'), som=True) >>> t((x + 1)*(y + 2) == 0) [[2*x + y + x*y == -2]] """ ctx = keys.get('ctx', None) t = _to_tactic(t, ctx) p = args2params(args, keys, t.ctx) return Tactic(Z3_tactic_using_params(t.ctx.ref(), t.tactic, p.params), t.ctx) def Repeat(t, max=4294967295, ctx=None): """Return a tactic that keeps applying `t` until the goal is not modified anymore or the maximum number of iterations `max` is reached. >>> x, y = Ints('x y') >>> c = And(Or(x == 0, x == 1), Or(y == 0, y == 1), x > y) >>> t = Repeat(OrElse(Tactic('split-clause'), Tactic('skip'))) >>> r = t(c) >>> for subgoal in r: print(subgoal) [x == 0, y == 0, x > y] [x == 0, y == 1, x > y] [x == 1, y == 0, x > y] [x == 1, y == 1, x > y] >>> t = Then(t, Tactic('propagate-values')) >>> t(c) [[x == 1, y == 0]] """ t = _to_tactic(t, ctx) return Tactic(Z3_tactic_repeat(t.ctx.ref(), t.tactic, max), t.ctx) def TryFor(t, ms, ctx=None): """Return a tactic that applies `t` to a given goal for `ms` milliseconds. If `t` does not terminate in `ms` milliseconds, then it fails. """ t = _to_tactic(t, ctx) return Tactic(Z3_tactic_try_for(t.ctx.ref(), t.tactic, ms), t.ctx) def tactics(ctx=None): """Return a list of all available tactics in Z3. >>> l = tactics() >>> l.count('simplify') == 1 True """ ctx = _get_ctx(ctx) return [ Z3_get_tactic_name(ctx.ref(), i) for i in range(Z3_get_num_tactics(ctx.ref())) ] def tactic_description(name, ctx=None): """Return a short description for the tactic named `name`. >>> d = tactic_description('simplify') """ ctx = _get_ctx(ctx) return Z3_tactic_get_descr(ctx.ref(), name) def describe_tactics(): """Display a (tabular) description of all available tactics in Z3.""" if in_html_mode(): even = True print('') for t in tactics(): if even: print('') even = False else: print('') even = True print('' % (t, insert_line_breaks(tactic_description(t), 40))) print('
%s%s
') else: for t in tactics(): print('%s : %s' % (t, tactic_description(t))) class Probe: """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.""" def __init__(self, probe, ctx=None): self.ctx = _get_ctx(ctx) self.probe = None if isinstance(probe, ProbeObj): self.probe = probe elif isinstance(probe, float): self.probe = Z3_probe_const(self.ctx.ref(), probe) elif _is_int(probe): self.probe = Z3_probe_const(self.ctx.ref(), float(probe)) elif isinstance(probe, bool): if probe: self.probe = Z3_probe_const(self.ctx.ref(), 1.0) else: self.probe = Z3_probe_const(self.ctx.ref(), 0.0) else: if __debug__: _z3_assert(isinstance(probe, str), "probe name expected") try: self.probe = Z3_mk_probe(self.ctx.ref(), probe) except Z3Exception: raise Z3Exception("unknown probe '%s'" % probe) Z3_probe_inc_ref(self.ctx.ref(), self.probe) def __del__(self): if self.probe != None: Z3_probe_dec_ref(self.ctx.ref(), self.probe) def __lt__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is less than the value returned by `other`. >>> p = Probe('size') < 10 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 1.0 """ return Probe(Z3_probe_lt(self.ctx.ref(), self.probe, _to_probe(other, self.ctx).probe), self.ctx) def __gt__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is greater than the value returned by `other`. >>> p = Probe('size') > 10 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 0.0 """ return Probe(Z3_probe_gt(self.ctx.ref(), self.probe, _to_probe(other, self.ctx).probe), self.ctx) def __le__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is less than or equal to the value returned by `other`. >>> p = Probe('size') <= 2 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 1.0 """ return Probe(Z3_probe_le(self.ctx.ref(), self.probe, _to_probe(other, self.ctx).probe), self.ctx) def __ge__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is greater than or equal to the value returned by `other`. >>> p = Probe('size') >= 2 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 1.0 """ return Probe(Z3_probe_ge(self.ctx.ref(), self.probe, _to_probe(other, self.ctx).probe), self.ctx) def __eq__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is equal to the value returned by `other`. >>> p = Probe('size') == 2 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 1.0 """ return Probe(Z3_probe_eq(self.ctx.ref(), self.probe, _to_probe(other, self.ctx).probe), self.ctx) def __ne__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is not equal to the value returned by `other`. >>> p = Probe('size') != 2 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 0.0 """ p = self.__eq__(other) return Probe(Z3_probe_not(self.ctx.ref(), p.probe), self.ctx) def __call__(self, goal): """Evaluate the probe `self` in the given goal. >>> p = Probe('size') >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 2.0 >>> g.add(x < 20) >>> p(g) 3.0 >>> p = Probe('num-consts') >>> p(g) 1.0 >>> p = Probe('is-propositional') >>> p(g) 0.0 >>> p = Probe('is-qflia') >>> p(g) 1.0 """ if __debug__: _z3_assert(isinstance(goal, Goal) or isinstance(goal, BoolRef), "Z3 Goal or Boolean expression expected") goal = _to_goal(goal) return Z3_probe_apply(self.ctx.ref(), self.probe, goal.goal) def is_probe(p): """Return `True` if `p` is a Z3 probe. >>> is_probe(Int('x')) False >>> is_probe(Probe('memory')) True """ return isinstance(p, Probe) def _to_probe(p, ctx=None): if is_probe(p): return p else: return Probe(p, ctx) def probes(ctx=None): """Return a list of all available probes in Z3. >>> l = probes() >>> l.count('memory') == 1 True """ ctx = _get_ctx(ctx) return [ Z3_get_probe_name(ctx.ref(), i) for i in range(Z3_get_num_probes(ctx.ref())) ] def probe_description(name, ctx=None): """Return a short description for the probe named `name`. >>> d = probe_description('memory') """ ctx = _get_ctx(ctx) return Z3_probe_get_descr(ctx.ref(), name) def describe_probes(): """Display a (tabular) description of all available probes in Z3.""" if in_html_mode(): even = True print('') for p in probes(): if even: print('') even = False else: print('') even = True print('' % (p, insert_line_breaks(probe_description(p), 40))) print('
%s%s
') else: for p in probes(): print('%s : %s' % (p, probe_description(p))) def _probe_nary(f, args, ctx): if __debug__: _z3_assert(len(args) > 0, "At least one argument expected") num = len(args) r = _to_probe(args[0], ctx) for i in range(num - 1): r = Probe(f(ctx.ref(), r.probe, _to_probe(args[i+1], ctx).probe), ctx) return r def _probe_and(args, ctx): return _probe_nary(Z3_probe_and, args, ctx) def _probe_or(args, ctx): return _probe_nary(Z3_probe_or, args, ctx) def FailIf(p, ctx=None): """Return a tactic that fails if the probe `p` evaluates to true. Otherwise, it returns the input goal unmodified. In the following example, the tactic applies 'simplify' if and only if there are more than 2 constraints in the goal. >>> t = OrElse(FailIf(Probe('size') > 2), Tactic('simplify')) >>> x, y = Ints('x y') >>> g = Goal() >>> g.add(x > 0) >>> g.add(y > 0) >>> t(g) [[x > 0, y > 0]] >>> g.add(x == y + 1) >>> t(g) [[Not(x <= 0), Not(y <= 0), x == 1 + y]] """ p = _to_probe(p, ctx) return Tactic(Z3_tactic_fail_if(p.ctx.ref(), p.probe), p.ctx) def When(p, t, ctx=None): """Return a tactic that applies tactic `t` only if probe `p` evaluates to true. Otherwise, it returns the input goal unmodified. >>> t = When(Probe('size') > 2, Tactic('simplify')) >>> x, y = Ints('x y') >>> g = Goal() >>> g.add(x > 0) >>> g.add(y > 0) >>> t(g) [[x > 0, y > 0]] >>> g.add(x == y + 1) >>> t(g) [[Not(x <= 0), Not(y <= 0), x == 1 + y]] """ p = _to_probe(p, ctx) t = _to_tactic(t, ctx) return Tactic(Z3_tactic_when(t.ctx.ref(), p.probe, t.tactic), t.ctx) def Cond(p, t1, t2, ctx=None): """Return a tactic that applies tactic `t1` to a goal if probe `p` evaluates to true, and `t2` otherwise. >>> t = Cond(Probe('is-qfnra'), Tactic('qfnra'), Tactic('smt')) """ p = _to_probe(p, ctx) t1 = _to_tactic(t1, ctx) t2 = _to_tactic(t2, ctx) return Tactic(Z3_tactic_cond(t1.ctx.ref(), p.probe, t1.tactic, t2.tactic), t1.ctx) ######################################### # # Utils # ######################################### def simplify(a, *arguments, **keywords): """Simplify the expression `a` using the given options. This function has many options. Use `help_simplify` to obtain the complete list. >>> x = Int('x') >>> y = Int('y') >>> simplify(x + 1 + y + x + 1) 2 + 2*x + y >>> simplify((x + 1)*(y + 1), som=True) 1 + x + y + x*y >>> simplify(Distinct(x, y, 1), blast_distinct=True) And(Not(x == y), Not(x == 1), Not(y == 1)) >>> simplify(And(x == 0, y == 1), elim_and=True) Not(Or(Not(x == 0), Not(y == 1))) """ if __debug__: _z3_assert(is_expr(a), "Z3 expression expected") if len(arguments) > 0 or len(keywords) > 0: p = args2params(arguments, keywords, a.ctx) return _to_expr_ref(Z3_simplify_ex(a.ctx_ref(), a.as_ast(), p.params), a.ctx) else: return _to_expr_ref(Z3_simplify(a.ctx_ref(), a.as_ast()), a.ctx) def help_simplify(): """Return a string describing all options available for Z3 `simplify` procedure.""" print(Z3_simplify_get_help(main_ctx().ref())) def simplify_param_descrs(): """Return the set of parameter descriptions for Z3 `simplify` procedure.""" return ParamDescrsRef(Z3_simplify_get_param_descrs(main_ctx().ref()), main_ctx()) def substitute(t, *m): """Apply substitution m on t, m is a list of pairs of the form (from, to). Every occurrence in t of from is replaced with to. >>> x = Int('x') >>> y = Int('y') >>> substitute(x + 1, (x, y + 1)) y + 1 + 1 >>> f = Function('f', IntSort(), IntSort()) >>> substitute(f(x) + f(y), (f(x), IntVal(1)), (f(y), IntVal(1))) 1 + 1 """ if isinstance(m, tuple): m1 = _get_args(m) if isinstance(m1, list): m = m1 if __debug__: _z3_assert(is_expr(t), "Z3 expression expected") _z3_assert(all([isinstance(p, tuple) and is_expr(p[0]) and is_expr(p[1]) and p[0].sort().eq(p[1].sort()) for p in m]), "Z3 invalid substitution, expression pairs expected.") num = len(m) _from = (Ast * num)() _to = (Ast * num)() for i in range(num): _from[i] = m[i][0].as_ast() _to[i] = m[i][1].as_ast() return _to_expr_ref(Z3_substitute(t.ctx.ref(), t.as_ast(), num, _from, _to), t.ctx) def substitute_vars(t, *m): """Substitute the free variables in t with the expression in m. >>> v0 = Var(0, IntSort()) >>> v1 = Var(1, IntSort()) >>> x = Int('x') >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> # replace v0 with x+1 and v1 with x >>> substitute_vars(f(v0, v1), x + 1, x) f(x + 1, x) """ if __debug__: _z3_assert(is_expr(t), "Z3 expression expected") _z3_assert(all([is_expr(n) for n in m]), "Z3 invalid substitution, list of expressions expected.") num = len(m) _to = (Ast * num)() for i in range(num): _to[i] = m[i].as_ast() return _to_expr_ref(Z3_substitute_vars(t.ctx.ref(), t.as_ast(), num, _to), t.ctx) def Sum(*args): """Create the sum of the Z3 expressions. >>> a, b, c = Ints('a b c') >>> Sum(a, b, c) a + b + c >>> Sum([a, b, c]) a + b + c >>> A = IntVector('a', 5) >>> Sum(A) a__0 + a__1 + a__2 + a__3 + a__4 """ args = _get_args(args) if __debug__: _z3_assert(len(args) > 0, "Non empty list of arguments expected") ctx = _ctx_from_ast_arg_list(args) if __debug__: _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression") args = _coerce_expr_list(args, ctx) if is_bv(args[0]): return _reduce(lambda a, b: a + b, args, 0) else: _args, sz = _to_ast_array(args) return ArithRef(Z3_mk_add(ctx.ref(), sz, _args), ctx) def Product(*args): """Create the product of the Z3 expressions. >>> a, b, c = Ints('a b c') >>> Product(a, b, c) a*b*c >>> Product([a, b, c]) a*b*c >>> A = IntVector('a', 5) >>> Product(A) a__0*a__1*a__2*a__3*a__4 """ args = _get_args(args) if __debug__: _z3_assert(len(args) > 0, "Non empty list of arguments expected") ctx = _ctx_from_ast_arg_list(args) if __debug__: _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression") args = _coerce_expr_list(args, ctx) if is_bv(args[0]): return _reduce(lambda a, b: a * b, args, 1) else: _args, sz = _to_ast_array(args) return ArithRef(Z3_mk_mul(ctx.ref(), sz, _args), ctx) def solve(*args, **keywords): """Solve the constraints `*args`. This is a simple function for creating demonstrations. It creates a solver, configure it using the options in `keywords`, adds the constraints in `args`, and invokes check. >>> a = Int('a') >>> solve(a > 0, a < 2) [a = 1] """ s = Solver() s.set(**keywords) s.add(*args) if keywords.get('show', False): print(s) r = s.check() if r == unsat: print("no solution") elif r == unknown: print("failed to solve") try: print(s.model()) except Z3Exception: return else: print(s.model()) def solve_using(s, *args, **keywords): """Solve the constraints `*args` using solver `s`. This is a simple function for creating demonstrations. It is similar to `solve`, but it uses the given solver `s`. It configures solver `s` using the options in `keywords`, adds the constraints in `args`, and invokes check. """ if __debug__: _z3_assert(isinstance(s, Solver), "Solver object expected") s.set(**keywords) s.add(*args) if keywords.get('show', False): print("Problem:") print(s) r = s.check() if r == unsat: print("no solution") elif r == unknown: print("failed to solve") try: print(s.model()) except Z3Exception: return else: if keywords.get('show', False): print("Solution:") print(s.model()) def prove(claim, **keywords): """Try to prove the given claim. This is a simple function for creating demonstrations. It tries to prove `claim` by showing the negation is unsatisfiable. >>> p, q = Bools('p q') >>> prove(Not(And(p, q)) == Or(Not(p), Not(q))) proved """ if __debug__: _z3_assert(is_bool(claim), "Z3 Boolean expression expected") s = Solver() s.set(**keywords) s.add(Not(claim)) if keywords.get('show', False): print(s) r = s.check() if r == unsat: print("proved") elif r == unknown: print("failed to prove") print(s.model()) else: print("counterexample") print(s.model()) def _solve_html(*args, **keywords): """Version of funcion `solve` used in RiSE4Fun.""" s = Solver() s.set(**keywords) s.add(*args) if keywords.get('show', False): print("Problem:") print(s) r = s.check() if r == unsat: print("no solution") elif r == unknown: print("failed to solve") try: print(s.model()) except Z3Exception: return else: if keywords.get('show', False): print("Solution:") print(s.model()) def _solve_using_html(s, *args, **keywords): """Version of funcion `solve_using` used in RiSE4Fun.""" if __debug__: _z3_assert(isinstance(s, Solver), "Solver object expected") s.set(**keywords) s.add(*args) if keywords.get('show', False): print("Problem:") print(s) r = s.check() if r == unsat: print("no solution") elif r == unknown: print("failed to solve") try: print(s.model()) except Z3Exception: return else: if keywords.get('show', False): print("Solution:") print(s.model()) def _prove_html(claim, **keywords): """Version of funcion `prove` used in RiSE4Fun.""" if __debug__: _z3_assert(is_bool(claim), "Z3 Boolean expression expected") s = Solver() s.set(**keywords) s.add(Not(claim)) if keywords.get('show', False): print(s) r = s.check() if r == unsat: print("proved") elif r == unknown: print("failed to prove") print(s.model()) else: print("counterexample") print(s.model()) def _dict2sarray(sorts, ctx): sz = len(sorts) _names = (Symbol * sz)() _sorts = (Sort * sz) () i = 0 for k in sorts: v = sorts[k] if __debug__: _z3_assert(isinstance(k, str), "String expected") _z3_assert(is_sort(v), "Z3 sort expected") _names[i] = to_symbol(k, ctx) _sorts[i] = v.ast i = i + 1 return sz, _names, _sorts def _dict2darray(decls, ctx): sz = len(decls) _names = (Symbol * sz)() _decls = (FuncDecl * sz) () i = 0 for k in decls: v = decls[k] if __debug__: _z3_assert(isinstance(k, str), "String expected") _z3_assert(is_func_decl(v) or is_const(v), "Z3 declaration or constant expected") _names[i] = to_symbol(k, ctx) if is_const(v): _decls[i] = v.decl().ast else: _decls[i] = v.ast i = i + 1 return sz, _names, _decls def parse_smt2_string(s, sorts={}, decls={}, ctx=None): """Parse a string in SMT 2.0 format using the given sorts and decls. The arguments sorts and decls are Python dictionaries used to initialize the symbol table used for the SMT 2.0 parser. >>> parse_smt2_string('(declare-const x Int) (assert (> x 0)) (assert (< x 10))') And(x > 0, x < 10) >>> x, y = Ints('x y') >>> f = Function('f', IntSort(), IntSort()) >>> parse_smt2_string('(assert (> (+ foo (g bar)) 0))', decls={ 'foo' : x, 'bar' : y, 'g' : f}) x + f(y) > 0 >>> parse_smt2_string('(declare-const a U) (assert (> a 0))', sorts={ 'U' : IntSort() }) a > 0 """ ctx = _get_ctx(ctx) ssz, snames, ssorts = _dict2sarray(sorts, ctx) dsz, dnames, ddecls = _dict2darray(decls, ctx) return _to_expr_ref(Z3_parse_smtlib2_string(ctx.ref(), s, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) def parse_smt2_file(f, sorts={}, decls={}, ctx=None): """Parse a file in SMT 2.0 format using the given sorts and decls. This function is similar to parse_smt2_string(). """ ctx = _get_ctx(ctx) ssz, snames, ssorts = _dict2sarray(sorts, ctx) dsz, dnames, ddecls = _dict2darray(decls, ctx) return _to_expr_ref(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) def Interpolant(a,ctx=None): """Create an interpolation operator. The argument is an interpolation pattern (see tree_interpolant). >>> x = Int('x') >>> print Interpolant(x>0) interp(x > 0) """ ctx = _get_ctx(_ctx_from_ast_arg_list([a], ctx)) s = BoolSort(ctx) a = s.cast(a) return BoolRef(Z3_mk_interpolant(ctx.ref(), a.as_ast()), ctx) def tree_interpolant(pat,p=None,ctx=None): """Compute interpolant for a tree of formulas. The input is an interpolation pattern over a set of formulas C. The pattern pat is a formula combining the formulas in C using logical conjunction and the "interp" operator (see Interp). This interp operator is logically the identity operator. It marks the sub-formulas of the pattern for which interpolants should be computed. The interpolant is a map sigma from marked subformulas to formulas, such that, for each marked subformula phi of pat (where phi sigma is phi with sigma(psi) substituted for each subformula psi of phi such that psi in dom(sigma)): 1) phi sigma implies sigma(phi), and 2) sigma(phi) is in the common uninterpreted vocabulary between the formulas of C occurring in phi and those not occurring in phi and moreover pat sigma implies false. In the simplest case an interpolant for the pattern "(and (interp A) B)" maps A to an interpolant for A /\ B. The return value is a vector of formulas representing sigma. This vector contains sigma(phi) for each marked subformula of pat, in pre-order traversal. This means that subformulas of phi occur before phi in the vector. Also, subformulas that occur multiply in pat will occur multiply in the result vector. If pat is satisfiable, raises an object of class ModelRef that represents a model of pat. If parameters p are supplied, these are used in creating the solver that determines satisfiability. >>> x = Int('x') >>> y = Int('y') >>> print tree_interpolant(And(Interpolant(x < 0), Interpolant(y > 2), x == y)) [Not(x >= 0), Not(y <= 2)] >>> g = And(Interpolant(x<0),x<2) >>> try: ... print tree_interpolant(g).sexpr() ... except ModelRef as m: ... print m.sexpr() (define-fun x () Int (- 1)) """ f = pat ctx = _get_ctx(_ctx_from_ast_arg_list([f], ctx)) ptr = (AstVectorObj * 1)() mptr = (Model * 1)() if p == None: p = ParamsRef(ctx) res = Z3_compute_interpolant(ctx.ref(),f.as_ast(),p.params,ptr,mptr) if res == Z3_L_FALSE: return AstVector(ptr[0],ctx) raise ModelRef(mptr[0], ctx) def binary_interpolant(a,b,p=None,ctx=None): """Compute an interpolant for a binary conjunction. If a & b is unsatisfiable, returns an interpolant for a & b. This is a formula phi such that 1) a implies phi 2) b implies not phi 3) All the uninterpreted symbols of phi occur in both a and b. If a & b is satisfiable, raises an object of class ModelRef that represents a model of a &b. If parameters p are supplied, these are used in creating the solver that determines satisfiability. x = Int('x') print binary_interpolant(x<0,x>2) Not(x >= 0) """ f = And(Interpolant(a),b) return tree_interpolant(f,p,ctx)[0] def sequence_interpolant(v,p=None,ctx=None): """Compute interpolant for a sequence of formulas. If len(v) == N, and if the conjunction of the formulas in v is unsatisfiable, the interpolant is a sequence of formulas w such that len(w) = N-1 and v[0] implies w[0] and for i in 0..N-1: 1) w[i] & v[i+1] implies w[i+1] (or false if i+1 = N) 2) All uninterpreted symbols in w[i] occur in both v[0]..v[i] and v[i+1]..v[n] Requires len(v) >= 1. If a & b is satisfiable, raises an object of class ModelRef that represents a model of a & b. If parameters p are supplied, these are used in creating the solver that determines satisfiability. >>> x = Int('x') >>> y = Int('y') >>> print sequence_interpolant([x < 0, y == x , y > 2]) [Not(x >= 0), Not(y >= 0)] """ f = v[0] for i in range(1,len(v)): f = And(Interpolant(f),v[i]) return tree_interpolant(f,p,ctx) ######################################### # # Floating-Point Arithmetic # ######################################### # Global default rounding mode _dflt_rounding_mode = Z3_OP_FPA_RM_TOWARD_ZERO _dflt_fpsort_ebits = 11 _dflt_fpsort_sbits = 53 def get_default_rounding_mode(ctx=None): """Retrieves the global default rounding mode.""" global _dflt_rounding_mode if _dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_ZERO: return RTZ(ctx) elif _dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_NEGATIVE: return RTN(ctx) elif _dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_POSITIVE: return RTP(ctx) elif _dflt_rounding_mode == Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN: return RNE(ctx) elif _dflt_rounding_mode == Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY: return RNA(ctx) def set_default_rounding_mode(rm, ctx=None): global _dflt_rounding_mode if is_fprm_value(rm): _dflt_rounding_mode = rm.decl().kind() else: _z3_assert(_dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_ZERO or _dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_NEGATIVE or _dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_POSITIVE or _dflt_rounding_mode == Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN or _dflt_rounding_mode == Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY, "illegal rounding mode") _dflt_rounding_mode = rm def get_default_fp_sort(ctx=None): return FPSort(_dflt_fpsort_ebits, _dflt_fpsort_sbits, ctx) def set_default_fp_sort(ebits, sbits, ctx=None): global _dflt_fpsort_ebits global _dflt_fpsort_sbits _dflt_fpsort_ebits = ebits _dflt_fpsort_sbits = sbits def _dflt_rm(ctx=None): return get_default_rounding_mode(ctx) def _dflt_fps(ctx=None): return get_default_fp_sort(ctx) ### FP Sorts class FPSortRef(SortRef): """Floating-point sort.""" def ebits(self): """Retrieves the number of bits reserved for the exponent in the FloatingPoint sort `self`. >>> b = FPSort(8, 24) >>> b.ebits() 8 """ return int(Z3_fpa_get_ebits(self.ctx_ref(), self.ast)) def sbits(self): """Retrieves the number of bits reserved for the exponent in the FloatingPoint sort `self`. >>> b = FPSort(8, 24) >>> b.sbits() 24 """ return int(Z3_fpa_get_sbits(self.ctx_ref(), self.ast)) def cast(self, val): """Try to cast `val` as a Floating-point expression >>> b = FPSort(8, 24) >>> b.cast(1.0) 1 >>> b.cast(1.0).sexpr() '(fp #b0 #x7f #b00000000000000000000000)' """ if is_expr(val): if __debug__: _z3_assert(self.ctx == val.ctx, "Context mismatch") return val else: return FPVal(val, None, self, self.ctx) def Float16(ctx=None): """Floating-point 16-bit (half) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_16(ctx.ref()), ctx) def FloatHalf(ctx=None): """Floating-point 16-bit (half) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_half(ctx.ref()), ctx) def Float32(ctx=None): """Floating-point 32-bit (single) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_32(ctx.ref()), ctx) def FloatSingle(ctx=None): """Floating-point 32-bit (single) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_single(ctx.ref()), ctx) def Float64(ctx=None): """Floating-point 64-bit (double) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_64(ctx.ref()), ctx) def FloatSingle(ctx=None): """Floating-point 64-bit (double) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_double(ctx.ref()), ctx) def Float128(ctx=None): """Floating-point 128-bit (quadruple) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_128(ctx.ref()), ctx) def FloatSingle(ctx=None): """Floating-point 128-bit (quadruple) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_quadruple(ctx.ref()), ctx) class FPRMSortRef(SortRef): """"Floating-point rounding mode sort.""" def is_fp_sort(s): """Return True if `s` is a Z3 floating-point sort. >>> is_fp_sort(FPSort(8, 24)) True >>> is_fp_sort(IntSort()) False """ return isinstance(s, FPSortRef) def is_fprm_sort(s): """Return True if `s` is a Z3 floating-point rounding mode sort. >>> is_fprm_sort(FPSort(8, 24)) False >>> is_fprm_sort(RNE().sort()) True """ return isinstance(s, FPRMSortRef) ### FP Expressions class FPRef(ExprRef): """Floating-point expressions.""" def sort(self): """Return the sort of the floating-point expression `self`. >>> x = FP('1.0', FPSort(8, 24)) >>> x.sort() FPSort(8, 24) >>> x.sort() == FPSort(8, 24) True """ return FPSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def ebits(self): """Retrieves the number of bits reserved for the exponent in the FloatingPoint expression `self`. >>> b = FPSort(8, 24) >>> b.ebits() 8 """ return self.sort().ebits(); def sbits(self): """Retrieves the number of bits reserved for the exponent in the FloatingPoint expression `self`. >>> b = FPSort(8, 24) >>> b.sbits() 24 """ return self.sort().sbits(); def as_string(self): """Return a Z3 floating point expression as a Python string.""" return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) def __le__(self, other): return fpLEQ(self, other) def __lt__(self, other): return fpLEQ(self, other) def __ge__(self, other): return fpGEQ(self, other) def __gt__(self, other): return fpGT(self, other) def __ne__(self, other): return fpNEQ(self, other) def __add__(self, other): """Create the Z3 expression `self + other`. >>> x = FP('x', FPSort(8, 24)) >>> y = FP('y', FPSort(8, 24)) >>> x + y x + y >>> (x + y).sort() FPSort(8, 24) """ a, b = z3._coerce_exprs(self, other) return fpAdd(_dflt_rm(), self, other) def __radd__(self, other): """Create the Z3 expression `other + self`. >>> x = FP('x', FPSort(8, 24)) >>> 10 + x 1.25*(2**3) + x """ a, b = _coerce_exprs(self, other) return fpAdd(_dflt_rm(), other, self) def __sub__(self, other): """Create the Z3 expression `self - other`. >>> x = FP('x', FPSort(8, 24)) >>> y = FP('y', FPSort(8, 24)) >>> x - y x - y >>> (x - y).sort() FPSort(8, 24) """ a, b = z3._coerce_exprs(self, other) return fpSub(_dflt_rm(), self, other) def __rsub__(self, other): """Create the Z3 expression `other - self`. >>> x = FP('x', FPSort(8, 24)) >>> 10 - x 1.25*(2**3) - x """ a, b = _coerce_exprs(self, other) return fpSub(_dflt_rm(), other, self) def __mul__(self, other): """Create the Z3 expression `self * other`. >>> x = FP('x', FPSort(8, 24)) >>> y = FP('y', FPSort(8, 24)) >>> x * y x * y >>> (x * y).sort() FPSort(8, 24) >>> 10 * y 1.25*(2**3) * y """ a, b = z3._coerce_exprs(self, other) return fpMul(_dflt_rm(), self, other) def __rmul__(self, other): """Create the Z3 expression `other * self`. >>> x = FP('x', FPSort(8, 24)) >>> y = FP('y', FPSort(8, 24)) >>> x * y x * y >>> x * 10 x * 1.25*(2**3) """ a, b = _coerce_exprs(self, other) return fpMul(_dflt_rm(), other, self) def __pos__(self): """Create the Z3 expression `+self`.""" return self def __neg__(self): """Create the Z3 expression `-self`.""" return FPRef(fpNeg(self)) def __truediv__(self, other): """Create the Z3 expression division `self / other`.""" return self.__div__(other) def __rtruediv__(self, other): """Create the Z3 expression division `other / self`.""" return self.__rdiv__(other) def __mod__(self, other): """Create the Z3 expression mod `self % other`.""" return fpRem(self, other) def __rmod__(self, other): """Create the Z3 expression mod `other % self`.""" return fpRem(other, self) class FPRMRef(ExprRef): """Floating-point rounding mode expressions""" def as_string(self): """Return a Z3 floating point expression as a Python string.""" return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) def RoundNearestTiesToEven(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_nearest_ties_to_even(ctx.ref()), ctx) def RNE (ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_nearest_ties_to_even(ctx.ref()), ctx) def RoundNearestTiesToAway(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_nearest_ties_to_away(ctx.ref()), ctx) def RNA (ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_nearest_ties_to_away(ctx.ref()), ctx) def RoundTowardPositive(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_positive(ctx.ref()), ctx) def RTP(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_positive(ctx.ref()), ctx) def RoundTowardNegative(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_negative(ctx.ref()), ctx) def RTN(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_negative(ctx.ref()), ctx) def RoundTowardZero(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_zero(ctx.ref()), ctx) def RTZ(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_zero(ctx.ref()), ctx) def is_fprm(a): """Return `True` if `a` is a Z3 floating-point rounding mode expression. >>> rm = RNE() >>> is_fprm(rm) True >>> rm = 1.0 >>> is_fprm(rm) False """ return isinstance(a, FPRMRef) def is_fprm_value(a): """Return `True` if `a` is a Z3 floating-point rounding mode numeral value.""" return is_fprm(a) and _is_numeral(a.ctx, a.ast) ### FP Numerals class FPNumRef(FPRef): def isNaN(self): return self.decl().kind() == Z3_OP_FPA_NAN def isInf(self): return self.decl().kind() == Z3_OP_FPA_PLUS_INF or self.decl().kind() == Z3_OP_FPA_MINUS_INF def isZero(self): return self.decl().kind() == Z3_OP_FPA_PLUS_ZERO or self.decl().kind() == Z3_OP_FPA_MINUS_ZERO def isNegative(self): k = self.decl().kind() return (self.num_args() == 0 and (k == Z3_OP_FPA_MINUS_INF or k == Z3_OP_FPA_MINUS_ZERO)) or (self.sign() == True) """ The sign of the numeral >>> x = FPNumRef(+1.0, FPSort(8, 24)) >>> x.sign() False >>> x = FPNumRef(-1.0, FPSort(8, 24)) >>> x.sign() True """ def sign(self): l = (ctypes.c_int)() if Z3_fpa_get_numeral_sign(self.ctx.ref(), self.as_ast(), byref(l)) == False: raise Z3Exception("error retrieving the sign of a numeral.") return l.value != 0 """ The significand of the numeral >>> x = FPNumRef(2.5, FPSort(8, 24)) 1.25 """ def significand(self): return Z3_fpa_get_numeral_significand_string(self.ctx.ref(), self.as_ast()) """ The exponent of the numeral >>> x = FPNumRef(2.5, FPSort(8, 24)) >>> 1 """ def exponent(self): return Z3_fpa_get_numeral_exponent_string(self.ctx.ref(), self.as_ast()) """ The exponent of the numeral as a long >>> x = FPNumRef(2.5, FPSort(8, 24)) 1 """ def exponent_as_long(self): ptr = (ctypes.c_longlong * 1)() if not Z3_fpa_get_numeral_exponent_int64(self.ctx.ref(), self.as_ast(), ptr): raise Z3Exception("error retrieving the exponent of a numeral.") return ptr[0] """ The string representation of the numeral >>> x = FPNumRef(20, FPSort(8, 24)) 1.25*(2**4) """ def as_string(self): s = Z3_fpa_get_numeral_string(self.ctx.ref(), self.as_ast()) return ("FPVal(%s, %s)" % (s, FPSortRef(self.sort()).as_string())) def _to_fpnum(num, ctx=None): if isinstance(num, FPNum): return num else: return FPNum(num, ctx) def is_fp(a): """Return `True` if `a` is a Z3 floating-point expression. >>> b = FP('b', FPSort(8, 24)) >>> is_fp(b) True >>> is_fp(b + 1.0) True >>> is_fp(Int('x')) False """ return isinstance(a, FPRef) def is_fp_value(a): """Return `True` if `a` is a Z3 floating-point numeral value. >>> b = FP('b', FPSort(8, 24)) >>> is_fp_value(b) False >>> b = FPVal(1.0, FPSort(8, 24)) >>> b 1 >>> is_fp_value(b) True """ return is_fp(a) and _is_numeral(a.ctx, a.ast) def FPSort(ebits, sbits, ctx=None): """Return a Z3 floating-point sort of the given sizes. If `ctx=None`, then the global context is used. >>> Single = FPSort(8, 24) >>> Double = FPSort(11, 53) >>> Single FPSort(8, 24) >>> x = Const('x', Single) >>> eq(x, FP('x', FPSort(8, 24))) True """ ctx = z3._get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort(ctx.ref(), ebits, sbits), ctx) def _to_float_str(val): if isinstance(val, float): return str(val) elif isinstance(val, bool): if val: return "1.0" else: return "0.0" elif _is_int(val): return str(val) elif isinstance(val, str): return val if __debug__: _z3_assert(False, "Python value cannot be used as a double") def fpNaN(s): _z3_assert(isinstance(s, FPSortRef), "sort mismatch") return FPNumRef(Z3_mk_fpa_nan(s.ctx_ref(), s.ast), s.ctx) def fpPlusInfinity(s): _z3_assert(isinstance(s, FPSortRef), "sort mismatch") return FPNumRef(Z3_mk_fpa_inf(s.ctx_ref(), s.ast, False), s.ctx) def fpMinusInfinity(s): _z3_assert(isinstance(s, FPSortRef), "sort mismatch") return FPNumRef(Z3_mk_fpa_inf(s.ctx_ref(), s.ast, True), s.ctx) def fpInfinity(s, negative): _z3_assert(isinstance(s, FPSortRef), "sort mismatch") _z3_assert(isinstance(negative, bool), "expected Boolean flag") return FPNumRef(Z3_mk_fpa_inf(s.ctx_ref(), s.ast, negative), s.ctx) def fpPlusZero(s): _z3_assert(isinstance(s, FPSortRef), "sort mismatch") return FPNumRef(Z3_mk_fpa_zero(s.ctx_ref(), s.ast, False), s.ctx) def fpMinusZero(s): _z3_assert(isinstance(s, FPSortRef), "sort mismatch") return FPNumRef(Z3_mk_fpa_zero(s.ctx_ref(), s.ast, True), s.ctx) def fpZero(s, negative): _z3_assert(isinstance(s, FPSortRef), "sort mismatch") _z3_assert(isinstance(negative, bool), "expected Boolean flag") return FPNumRef(Z3_mk_fpa_zero(s.ctx_ref(), s.ast, negative), s.ctx) def FPVal(sig, exp=None, fps=None, ctx=None): """Return a floating-point value of value `val` and sort `fps`. If `ctx=None`, then the global context is used. >>> v = FPVal(20.0, FPSort(8, 24)) >>> v 1.25*(2**4) >>> print("0x%.8x" % v.exponent_as_long()) 0x00000004 >>> v = FPVal(2.25, FPSort(8, 24)) >>> v 1.125*(2**1) >>> v = FPVal(-2.25, FPSort(8, 24)) >>> v -1.125*(2**1) """ ctx = _get_ctx(ctx) if is_fp_sort(exp): fps = exp exp = None elif fps == None: fps = _dflt_fps(ctx) _z3_assert(is_fp_sort(fps), "sort mismatch") if exp == None: exp = 0 val = _to_float_str(sig) val = val + 'p' val = val + _to_int_str(exp) return FPNumRef(Z3_mk_numeral(ctx.ref(), val, fps.ast), ctx) def FP(name, fpsort, ctx=None): """Return a floating-point constant named `name`. `fpsort` is the floating-point sort. If `ctx=None`, then the global context is used. >>> x = FP('x', FPSort(8, 24)) >>> is_fp(x) True >>> x.ebits() 8 >>> x.sort() FPSort(8, 24) >>> word = FPSort(8, 24) >>> x2 = FP('x', word) >>> eq(x, x2) True """ if isinstance(fpsort, FPSortRef): ctx = fpsort.ctx else: ctx = _get_ctx(ctx) return FPRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), fpsort.ast), ctx) def FPs(names, fpsort, ctx=None): """Return an array of floating-point constants. >>> x, y, z = FPs('x y z', FPSort(8, 24)) >>> x.sort() FPSort(8, 24) >>> x.sbits() 24 >>> x.ebits() 8 >>> fpMul(RNE(), fpAdd(RNE(), x, y), z) fpMul(RNE(), fpAdd(RNE(), x, y), z) """ ctx = z3._get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [FP(name, fpsort, ctx) for name in names] def fpAbs(a): """Create a Z3 floating-point absolute value expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FPVal(1.0, s) >>> fpAbs(x) fpAbs(1) >>> y = FPVal(-20.0, s) >>> y -1.25*(2**4) >>> fpAbs(y) fpAbs(-1.25*(2**4)) >>> fpAbs(-1.25*(2**4)) fpAbs(-1.25*(2**4)) >>> fpAbs(x).sort() FPSort(8, 24) """ ctx = None if not is_expr(a): ctx =_get_ctx(ctx) s = get_default_fp_sort(ctx) a = FPVal(a, s) else: ctx = a.ctx if __debug__: _z3_assert(is_fp(a), "First argument must be Z3 floating-point expression") return FPRef(Z3_mk_fpa_abs(a.ctx_ref(), a.as_ast()), a.ctx) def fpNeg(a): """Create a Z3 floating-point addition expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> fpNeg(x) -x >>> fpNeg(x).sort() FPSort(8, 24) """ ctx = None if not is_expr(a): ctx =_get_ctx(ctx) s = get_default_fp_sort(ctx) a = FPVal(a, s) else: ctx = a.ctx if __debug__: _z3_assert(is_fp(a), "First argument must be Z3 floating-point expression") return FPRef(Z3_mk_fpa_neg(a.ctx_ref(), a.as_ast()), a.ctx) def fpAdd(rm, a, b): """Create a Z3 floating-point addition expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpAdd(rm, x, y) fpAdd(RNE(), x, y) >>> fpAdd(rm, x, y).sort() FPSort(8, 24) """ if __debug__: _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(a) or is_fp(b), "Second or third argument must be a Z3 floating-point expression") a, b = _coerce_exprs(a, b) return FPRef(Z3_mk_fpa_add(rm.ctx_ref(), rm.as_ast(), a.as_ast(), b.as_ast()), rm.ctx) def fpSub(rm, a, b): """Create a Z3 floating-point subtraction expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpSub(rm, x, y) fpSub(RNE(), x, y) >>> fpSub(rm, x, y).sort() FPSort(8, 24) """ if __debug__: _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(a) or is_fp(b), "Second or third argument must be a Z3 floating-point expression") a, b = _coerce_exprs(a, b) return FPRef(Z3_mk_fpa_sub(rm.ctx_ref(), rm.as_ast(), a.as_ast(), b.as_ast()), rm.ctx) def fpMul(rm, a, b): """Create a Z3 floating-point multiplication expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpMul(rm, x, y) fpMul(RNE(), x, y) >>> fpMul(rm, x, y).sort() FPSort(8, 24) """ if __debug__: _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(a) or is_fp(b), "Second or third argument must be a Z3 floating-point expression") a, b = _coerce_exprs(a, b) return FPRef(Z3_mk_fpa_mul(rm.ctx_ref(), rm.as_ast(), a.as_ast(), b.as_ast()), rm.ctx) def fpDiv(rm, a, b): """Create a Z3 floating-point divison expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpDiv(rm, x, y) fpDiv(RNE(), x, y) >>> fpDiv(rm, x, y).sort() FPSort(8, 24) """ if __debug__: _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(a) or is_fp(b), "Second or third argument must be a Z3 floating-point expression") a, b = _coerce_exprs(a, b) return FPRef(Z3_mk_fpa_div(rm.ctx_ref(), rm.as_ast(), a.as_ast(), b.as_ast()), rm.ctx) def fpRem(a, b): """Create a Z3 floating-point remainder expression. >>> s = FPSort(8, 24) >>> x = FP('x', s) >>> y = FP('y', s) >>> fpRem(x, y) fpRem(x, y) >>> fpRem(x, y).sort() FPSort(8, 24) """ if __debug__: _z3_assert(is_fp(a) or is_fp(b), "Second or third argument must be a Z3 floating-point expression") a, b = _coerce_exprs(a, b) return FPRef(Z3_mk_fpa_rem(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def fpMin(a, b): """Create a Z3 floating-point minimium expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpMin(x, y) fpMin(x, y) >>> fpMin(x, y).sort() FPSort(8, 24) """ if __debug__: _z3_assert(is_fp(a) or is_fp(b), "Second or third argument must be a Z3 floating-point expression") a, b = _coerce_exprs(a, b) return FPRef(Z3_mk_fpa_min(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def fpMax(a, b): """Create a Z3 floating-point maximum expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpMax(x, y) fpMax(x, y) >>> fpMax(x, y).sort() FPSort(8, 24) """ if __debug__: _z3_assert(is_fp(a) or is_fp(b), "Second or third argument must be a Z3 floating-point expression") a, b = _coerce_exprs(a, b) return FPRef(Z3_mk_fpa_max(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def fpFMA(rm, a, b, c): """Create a Z3 floating-point fused multiply-add expression. """ if __debug__: _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(a) or is_fp(b) or is_fp(c), "Second, third, or fourth argument must be a Z3 floating-point expression") a, b, c = _coerce_expr_list([a, b, c]) return FPRef(Z3_mk_fpa_fma(rm.ctx_ref(), rm.as_ast(), a.as_ast(), b.as_ast(), c.as_ast()), rm.ctx) def fpSqrt(rm, a): """Create a Z3 floating-point square root expression. """ ctx = None if not is_expr(a): ctx =_get_ctx(ctx) s = get_default_fp_sort(ctx) a = FPVal(a, s) else: ctx = a.ctx if __debug__: _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(a), "Second argument must be a Z3 floating-point expressions") return FPRef(Z3_mk_fpa_sqrt(rm.ctx_ref(), rm.as_ast(), a.as_ast()), rm.ctx) def fpRoundToIntegral(rm, a): """Create a Z3 floating-point roundToIntegral expression. """ ctx = None if not is_expr(a): ctx =_get_ctx(ctx) s = get_default_fp_sort(ctx) a = FPVal(a, s) else: ctx = a.ctx if __debug__: _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(a), "Second argument must be a Z3 floating-point expressions") return FPRef(Z3_mk_fpa_round_to_integral(rm.ctx_ref(), rm.as_ast(), a.as_ast()), rm.ctx) def fpIsNaN(a): """Create a Z3 floating-point isNaN expression. """ if __debug__: _z3_assert(is_fp(a), "Argument must be Z3 floating-point expressions") return FPRef(Z3_mk_fpa_is_nan(a.ctx_ref(), a.as_ast()), a.ctx) def fpIsInfinite(a): """Create a Z3 floating-point isNaN expression. """ if __debug__: _z3_assert(is_fp(a), "Argument must be Z3 floating-point expressions") return FPRef(Z3_mk_fpa_is_infinite(a.ctx_ref(), a.as_ast()), a.ctx) def fpIsZero(a): """Create a Z3 floating-point isNaN expression. """ if __debug__: _z3_assert(is_fp(a), "Argument must be Z3 floating-point expressions") return FPRef(Z3_mk_fpa_is_zero(a.ctx_ref(), a.as_ast()), a.ctx) def fpIsNormal(a): """Create a Z3 floating-point isNaN expression. """ if __debug__: _z3_assert(is_fp(a), "Argument must be Z3 floating-point expressions") return FPRef(Z3_mk_fpa_is_normal(a.ctx_ref(), a.as_ast()), a.ctx) def fpIsSubnormal(a): """Create a Z3 floating-point isNaN expression. """ if __debug__: _z3_assert(is_fp(a), "Argument must be Z3 floating-point expressions") return FPRef(Z3_mk_fpa_is_subnormal(a.ctx_ref(), a.as_ast()), a.ctx) def fpIsNegative(a): """Create a Z3 floating-point isNaN expression. """ if __debug__: _z3_assert(is_fp(a), "Argument must be Z3 floating-point expressions") return FPRef(Z3_mk_fpa_is_negative(a.ctx_ref(), a.as_ast()), a.ctx) def fpIsPositive(a): """Create a Z3 floating-point isNaN expression. """ if __debug__: _z3_assert(is_fp(a), "Argument must be Z3 floating-point expressions") return FPRef(Z3_mk_fpa_is_positive(a.ctx_ref(), a.as_ast()), a.ctx) def _check_fp_args(a, b): if __debug__: _z3_assert(is_fp(a) or is_fp(b), "At least one of the arguments must be a Z3 floating-point expression") def fpLT(a, b): """Create the Z3 floating-point expression `other <= self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpLT(x, y) x < y >>> (x <= y).sexpr() '(fp.leq x y)' """ _check_fp_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_fpa_lt(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def fpLEQ(a, b): """Create the Z3 floating-point expression `other <= self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpLEQ(x, y) x <= y >>> (x <= y).sexpr() '(fp.leq x y)' """ _check_fp_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_fpa_leq(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def fpGT(a, b): """Create the Z3 floating-point expression `other <= self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpGT(x, y) x > y >>> (x > y).sexpr() '(fp.gt x y)' """ _check_fp_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_fpa_gt(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def fpGEQ(a, b): """Create the Z3 floating-point expression `other <= self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> x + y x + y >>> fpGEQ(x, y) x >= y >>> (x >= y).sexpr() '(fp.geq x y)' """ _check_fp_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_fpa_geq(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def fpEQ(a, b): """Create the Z3 floating-point expression `other <= self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpEQ(x, y) fpEQ(x, y) >>> fpEQ(x, y).sexpr() '(fp.eq x y)' """ _check_fp_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_fpa_eq(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def fpNEQ(a, b): """Create the Z3 floating-point expression `other <= self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpNEQ(x, y) Not(fpEQ(x, y)) >>> (x != y).sexpr() '(not (fp.eq x y))' """ _check_fp_args(a, b) a, b = _coerce_exprs(a, b) return Not(BoolRef(Z3_mk_fpa_eq(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx), a.ctx) def fpFP(sgn, exp, sig): """Create the Z3 floating-point value `fpFP(sgn, sig, exp)` from the three bit-vectorssgn, sig, and exp.""" _z3_assert(is_bv(sgn) and is_bv(exp) and is_bv(sig), "sort mismatch") _z3_assert(sgn.sort().size() == 1, "sort mismatch") return FPRef(Z3_mk_fpa_fp(sgn.ctx_ref(), sgn.ast, exp.ast, sig.ast), sgn.ctx) def fpToFP(a1, a2=None, a3=None): """Create a Z3 floating-point conversion expression from other terms.""" if is_bv(a1) and is_fp_sort(a2): return FPRef(Z3_mk_fpa_to_fp_bv(a1.ctx_ref(), a1.ast, a2.ast), a1.ctx) elif is_fprm(a1) and is_fp(a2) and is_fp_sort(a3): return FPRef(Z3_mk_fpa_to_fp_float(a1.ctx_ref(), a1.ast, a2.ast, a3.ast), a1.ctx) elif is_fprm(a1) and is_real(a2) and is_fp_sort(a3): return FPRef(Z3_mk_fpa_to_fp_real(a1.ctx_ref(), a1.ast, a2.ast, a3.ast), a1.ctx) elif is_fprm(a1) and is_bv(a2) and is_fp_sort(a3): return FPRef(Z3_mk_fpa_to_fp_signed(a1.ctx_ref(), a1.ast, a2.ast, a3.ast), a1.ctx) else: raise Z3Exception("Unsupported combination of arguments for conversion to floating-point term.") def fpToFPUnsigned(rm, x, s): """Create a Z3 floating-point conversion expression, from unsigned bit-vector to floating-point expression.""" if __debug__: _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_bv(x), "Second argument must be a Z3 bit-vector expression") _z3_assert(is_fp_sort(s), "Third argument must be Z3 floating-point sort") return FPRef(Z3_mk_fpa_to_fp_unsigned(rm.ctx_ref(), rm.ast, x.ast, s.ast), rm.ctx) def fpToSBV(rm, x, s): """Create a Z3 floating-point conversion expression, from floating-point expression to signed bit-vector. >>> x = FP('x', FPSort(8, 24)) >>> y = fpToSBV(RTZ(), x, BitVecSort(32)) >>> print is_fp(x) True >>> print is_bv(y) True >>> print is_fp(y) False >>> print is_bv(x) False """ if __debug__: _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(x), "Second argument must be a Z3 floating-point expression") _z3_assert(is_bv_sort(s), "Third argument must be Z3 bit-vector sort") return BitVecRef(Z3_mk_fpa_to_sbv(rm.ctx_ref(), rm.ast, x.ast, s.size()), rm.ctx) def fpToUBV(rm, x, s): """Create a Z3 floating-point conversion expression, from floating-point expression to unsigned bit-vector. >>> x = FP('x', FPSort(8, 24)) >>> y = fpToUBV(RTZ(), x, BitVecSort(32)) >>> print is_fp(x) True >>> print is_bv(y) True >>> print is_fp(y) False >>> print is_bv(x) False """ if __debug__: _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(x), "Second argument must be a Z3 floating-point expression") _z3_assert(is_bv_sort(s), "Third argument must be Z3 bit-vector sort") return BitVecRef(Z3_mk_fpa_to_ubv(rm.ctx_ref(), rm.ast, x.ast, s.size()), rm.ctx) def fpToReal(x): """Create a Z3 floating-point conversion expression, from floating-point expression to real. >>> x = FP('x', FPSort(8, 24)) >>> y = fpToReal(x) >>> print is_fp(x) True >>> print is_real(y) True >>> print is_fp(y) False >>> print is_real(x) False """ if __debug__: _z3_assert(is_fp(x), "First argument must be a Z3 floating-point expression") return ArithRef(Z3_mk_fpa_to_real(x.ctx_ref(), x.ast), x.ctx) def fpToIEEEBV(x): """\brief 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. >>> x = FP('x', FPSort(8, 24)) >>> y = fpToIEEEBV(x) >>> print is_fp(x) True >>> print is_bv(y) True >>> print is_fp(y) False >>> print is_bv(x) False """ if __debug__: _z3_assert(is_fp(x), "First argument must be a Z3 floating-point expression") return BitVecRef(Z3_mk_fpa_to_ieee_bv(x.ctx_ref(), x.ast), x.ctx) z3-z3-4.4.1/src/api/python/z3num.py000066400000000000000000000373741260446376700167520ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface for Z3 numerals # # Author: Leonardo de Moura (leonardo) ############################################ from z3 import * from z3core import * from z3printer import * from fractions import Fraction def _to_numeral(num, ctx=None): if isinstance(num, Numeral): return num else: return Numeral(num, ctx) class Numeral: """ A Z3 numeral can be used to perform computations over arbitrary precision integers, rationals and real algebraic numbers. It also automatically converts python numeric values. >>> Numeral(2) 2 >>> Numeral("3/2") + 1 5/2 >>> Numeral(Sqrt(2)) 1.4142135623? >>> Numeral(Sqrt(2)) + 2 3.4142135623? >>> Numeral(Sqrt(2)) + Numeral(Sqrt(3)) 3.1462643699? Z3 numerals can be used to perform computations with values in a Z3 model. >>> s = Solver() >>> x = Real('x') >>> s.add(x*x == 2) >>> s.add(x > 0) >>> s.check() sat >>> m = s.model() >>> m[x] 1.4142135623? >>> m[x] + 1 1.4142135623? + 1 The previous result is a Z3 expression. >>> (m[x] + 1).sexpr() '(+ (root-obj (+ (^ x 2) (- 2)) 2) 1.0)' >>> Numeral(m[x]) + 1 2.4142135623? >>> Numeral(m[x]).is_pos() True >>> Numeral(m[x])**2 2 We can also isolate the roots of polynomials. >>> x0, x1, x2 = RealVarVector(3) >>> r0 = isolate_roots(x0**5 - x0 - 1) >>> r0 [1.1673039782?] In the following example, we are isolating the roots of a univariate polynomial (on x1) obtained after substituting x0 -> r0[0] >>> r1 = isolate_roots(x1**2 - x0 + 1, [ r0[0] ]) >>> r1 [-0.4090280898?, 0.4090280898?] Similarly, in the next example we isolate the roots of a univariate polynomial (on x2) obtained after substituting x0 -> r0[0] and x1 -> r1[0] >>> isolate_roots(x1*x2 + x0, [ r0[0], r1[0] ]) [2.8538479564?] """ def __init__(self, num, ctx=None): if isinstance(num, Ast): self.ast = num self.ctx = z3._get_ctx(ctx) elif isinstance(num, RatNumRef) or isinstance(num, AlgebraicNumRef): self.ast = num.ast self.ctx = num.ctx elif isinstance(num, ArithRef): r = simplify(num) self.ast = r.ast self.ctx = r.ctx else: v = RealVal(num, ctx) self.ast = v.ast self.ctx = v.ctx Z3_inc_ref(self.ctx_ref(), self.as_ast()) assert Z3_algebraic_is_value(self.ctx_ref(), self.ast) def __del__(self): Z3_dec_ref(self.ctx_ref(), self.as_ast()) def is_integer(self): """ Return True if the numeral is integer. >>> Numeral(2).is_integer() True >>> (Numeral(Sqrt(2)) * Numeral(Sqrt(2))).is_integer() True >>> Numeral(Sqrt(2)).is_integer() False >>> Numeral("2/3").is_integer() False """ return self.is_rational() and self.denominator() == 1 def is_rational(self): """ Return True if the numeral is rational. >>> Numeral(2).is_rational() True >>> Numeral("2/3").is_rational() True >>> Numeral(Sqrt(2)).is_rational() False """ return Z3_get_ast_kind(self.ctx_ref(), self.as_ast()) == Z3_NUMERAL_AST def denominator(self): """ Return the denominator if `self` is rational. >>> Numeral("2/3").denominator() 3 """ assert(self.is_rational()) return Numeral(Z3_get_denominator(self.ctx_ref(), self.as_ast()), self.ctx) def numerator(self): """ Return the numerator if `self` is rational. >>> Numeral("2/3").numerator() 2 """ assert(self.is_rational()) return Numeral(Z3_get_numerator(self.ctx_ref(), self.as_ast()), self.ctx) def is_irrational(self): """ Return True if the numeral is irrational. >>> Numeral(2).is_irrational() False >>> Numeral("2/3").is_irrational() False >>> Numeral(Sqrt(2)).is_irrational() True """ return not self.is_rational() def as_long(self): """ Return a numeral (that is an integer) as a Python long. >>> (Numeral(10)**20).as_long() 100000000000000000000L """ assert(self.is_integer()) return long(Z3_get_numeral_string(self.ctx_ref(), self.as_ast())) def as_fraction(self): """ Return a numeral (that is a rational) as a Python Fraction. >>> Numeral("1/5").as_fraction() Fraction(1, 5) """ assert(self.is_rational()) return Fraction(self.numerator().as_long(), self.denominator().as_long()) def approx(self, precision=10): """Return a numeral that approximates the numeral `self`. The result `r` is such that |r - self| <= 1/10^precision If `self` is rational, then the result is `self`. >>> x = Numeral(2).root(2) >>> x.approx(20) 6838717160008073720548335/4835703278458516698824704 >>> x.approx(5) 2965821/2097152 >>> Numeral(2).approx(10) 2 """ return self.upper(precision) def upper(self, precision=10): """Return a upper bound that approximates the numeral `self`. The result `r` is such that r - self <= 1/10^precision If `self` is rational, then the result is `self`. >>> x = Numeral(2).root(2) >>> x.upper(20) 6838717160008073720548335/4835703278458516698824704 >>> x.upper(5) 2965821/2097152 >>> Numeral(2).upper(10) 2 """ if self.is_rational(): return self else: return Numeral(Z3_get_algebraic_number_upper(self.ctx_ref(), self.as_ast(), precision), self.ctx) def lower(self, precision=10): """Return a lower bound that approximates the numeral `self`. The result `r` is such that self - r <= 1/10^precision If `self` is rational, then the result is `self`. >>> x = Numeral(2).root(2) >>> x.lower(20) 1709679290002018430137083/1208925819614629174706176 >>> Numeral("2/3").lower(10) 2/3 """ if self.is_rational(): return self else: return Numeral(Z3_get_algebraic_number_lower(self.ctx_ref(), self.as_ast(), precision), self.ctx) def sign(self): """ Return the sign of the numeral. >>> Numeral(2).sign() 1 >>> Numeral(-3).sign() -1 >>> Numeral(0).sign() 0 """ return Z3_algebraic_sign(self.ctx_ref(), self.ast) def is_pos(self): """ Return True if the numeral is positive. >>> Numeral(2).is_pos() True >>> Numeral(-3).is_pos() False >>> Numeral(0).is_pos() False """ return Z3_algebraic_is_pos(self.ctx_ref(), self.ast) def is_neg(self): """ Return True if the numeral is negative. >>> Numeral(2).is_neg() False >>> Numeral(-3).is_neg() True >>> Numeral(0).is_neg() False """ return Z3_algebraic_is_neg(self.ctx_ref(), self.ast) def is_zero(self): """ Return True if the numeral is zero. >>> Numeral(2).is_zero() False >>> Numeral(-3).is_zero() False >>> Numeral(0).is_zero() True >>> sqrt2 = Numeral(2).root(2) >>> sqrt2.is_zero() False >>> (sqrt2 - sqrt2).is_zero() True """ return Z3_algebraic_is_zero(self.ctx_ref(), self.ast) def __add__(self, other): """ Return the numeral `self + other`. >>> Numeral(2) + 3 5 >>> Numeral(2) + Numeral(4) 6 >>> Numeral("2/3") + 1 5/3 """ return Numeral(Z3_algebraic_add(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __radd__(self, other): """ Return the numeral `other + self`. >>> 3 + Numeral(2) 5 """ return Numeral(Z3_algebraic_add(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __sub__(self, other): """ Return the numeral `self - other`. >>> Numeral(2) - 3 -1 """ return Numeral(Z3_algebraic_sub(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __rsub__(self, other): """ Return the numeral `other - self`. >>> 3 - Numeral(2) 1 """ return Numeral(Z3_algebraic_sub(self.ctx_ref(), _to_numeral(other, self.ctx).ast, self.ast), self.ctx) def __mul__(self, other): """ Return the numeral `self * other`. >>> Numeral(2) * 3 6 """ return Numeral(Z3_algebraic_mul(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __rmul__(self, other): """ Return the numeral `other * mul`. >>> 3 * Numeral(2) 6 """ return Numeral(Z3_algebraic_mul(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __div__(self, other): """ Return the numeral `self / other`. >>> Numeral(2) / 3 2/3 >>> Numeral(2).root(2) / 3 0.4714045207? >>> Numeral(Sqrt(2)) / Numeral(Sqrt(3)) 0.8164965809? """ return Numeral(Z3_algebraic_div(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __rdiv__(self, other): """ Return the numeral `other / self`. >>> 3 / Numeral(2) 3/2 >>> 3 / Numeral(2).root(2) 2.1213203435? """ return Numeral(Z3_algebraic_div(self.ctx_ref(), _to_numeral(other, self.ctx).ast, self.ast), self.ctx) def root(self, k): """ Return the numeral `self^(1/k)`. >>> sqrt2 = Numeral(2).root(2) >>> sqrt2 1.4142135623? >>> sqrt2 * sqrt2 2 >>> sqrt2 * 2 + 1 3.8284271247? >>> (sqrt2 * 2 + 1).sexpr() '(root-obj (+ (^ x 2) (* (- 2) x) (- 7)) 2)' """ return Numeral(Z3_algebraic_root(self.ctx_ref(), self.ast, k), self.ctx) def power(self, k): """ Return the numeral `self^k`. >>> sqrt3 = Numeral(3).root(2) >>> sqrt3 1.7320508075? >>> sqrt3.power(2) 3 """ return Numeral(Z3_algebraic_power(self.ctx_ref(), self.ast, k), self.ctx) def __pow__(self, k): """ Return the numeral `self^k`. >>> sqrt3 = Numeral(3).root(2) >>> sqrt3 1.7320508075? >>> sqrt3**2 3 """ return self.power(k) def __lt__(self, other): """ Return True if `self < other`. >>> Numeral(Sqrt(2)) < 2 True >>> Numeral(Sqrt(3)) < Numeral(Sqrt(2)) False >>> Numeral(Sqrt(2)) < Numeral(Sqrt(2)) False """ return Z3_algebraic_lt(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __rlt__(self, other): """ Return True if `other < self`. >>> 2 < Numeral(Sqrt(2)) False """ return self > other def __gt__(self, other): """ Return True if `self > other`. >>> Numeral(Sqrt(2)) > 2 False >>> Numeral(Sqrt(3)) > Numeral(Sqrt(2)) True >>> Numeral(Sqrt(2)) > Numeral(Sqrt(2)) False """ return Z3_algebraic_gt(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __rgt__(self, other): """ Return True if `other > self`. >>> 2 > Numeral(Sqrt(2)) True """ return self < other def __le__(self, other): """ Return True if `self <= other`. >>> Numeral(Sqrt(2)) <= 2 True >>> Numeral(Sqrt(3)) <= Numeral(Sqrt(2)) False >>> Numeral(Sqrt(2)) <= Numeral(Sqrt(2)) True """ return Z3_algebraic_le(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __rle__(self, other): """ Return True if `other <= self`. >>> 2 <= Numeral(Sqrt(2)) False """ return self >= other def __ge__(self, other): """ Return True if `self >= other`. >>> Numeral(Sqrt(2)) >= 2 False >>> Numeral(Sqrt(3)) >= Numeral(Sqrt(2)) True >>> Numeral(Sqrt(2)) >= Numeral(Sqrt(2)) True """ return Z3_algebraic_ge(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __rge__(self, other): """ Return True if `other >= self`. >>> 2 >= Numeral(Sqrt(2)) True """ return self <= other def __eq__(self, other): """ Return True if `self == other`. >>> Numeral(Sqrt(2)) == 2 False >>> Numeral(Sqrt(3)) == Numeral(Sqrt(2)) False >>> Numeral(Sqrt(2)) == Numeral(Sqrt(2)) True """ return Z3_algebraic_eq(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __ne__(self, other): """ Return True if `self != other`. >>> Numeral(Sqrt(2)) != 2 True >>> Numeral(Sqrt(3)) != Numeral(Sqrt(2)) True >>> Numeral(Sqrt(2)) != Numeral(Sqrt(2)) False """ return Z3_algebraic_neq(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __str__(self): if Z3_is_numeral_ast(self.ctx_ref(), self.ast): return str(RatNumRef(self.ast, self.ctx)) else: return str(AlgebraicNumRef(self.ast, self.ctx)) def __repr__(self): return self.__str__() def sexpr(self): return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) def as_ast(self): return self.ast def ctx_ref(self): return self.ctx.ref() def eval_sign_at(p, vs): """ Evaluate the sign of the polynomial `p` at `vs`. `p` is a Z3 Expression containing arithmetic operators: +, -, *, ^k where k is an integer; and free variables x that is_var(x) is True. Moreover, all variables must be real. The result is 1 if the polynomial is positive at the given point, -1 if negative, and 0 if zero. >>> x0, x1, x2 = RealVarVector(3) >>> eval_sign_at(x0**2 + x1*x2 + 1, (Numeral(0), Numeral(1), Numeral(2))) 1 >>> eval_sign_at(x0**2 - 2, [ Numeral(Sqrt(2)) ]) 0 >>> eval_sign_at((x0 + x1)*(x0 + x2), (Numeral(0), Numeral(Sqrt(2)), Numeral(Sqrt(3)))) 1 """ num = len(vs) _vs = (Ast * num)() for i in range(num): _vs[i] = vs[i].ast return Z3_algebraic_eval(p.ctx_ref(), p.as_ast(), num, _vs) def isolate_roots(p, vs=[]): """ Given a multivariate polynomial p(x_0, ..., x_{n-1}, x_n), returns the roots of the univariate polynomial p(vs[0], ..., vs[len(vs)-1], x_n). Remarks: * p is a Z3 expression that contains only arithmetic terms and free variables. * forall i in [0, n) vs is a numeral. The result is a list of numerals >>> x0 = RealVar(0) >>> isolate_roots(x0**5 - x0 - 1) [1.1673039782?] >>> x1 = RealVar(1) >>> isolate_roots(x0**2 - x1**4 - 1, [ Numeral(Sqrt(3)) ]) [-1.1892071150?, 1.1892071150?] >>> x2 = RealVar(2) >>> isolate_roots(x2**2 + x0 - x1, [ Numeral(Sqrt(3)), Numeral(Sqrt(2)) ]) [] """ num = len(vs) _vs = (Ast * num)() for i in range(num): _vs[i] = vs[i].ast _roots = AstVector(Z3_algebraic_roots(p.ctx_ref(), p.as_ast(), num, _vs), p.ctx) return [ Numeral(r) for r in _roots ] if __name__ == "__main__": import doctest if doctest.testmod().failed: exit(1) z3-z3-4.4.1/src/api/python/z3poly.py000066400000000000000000000021511260446376700171170ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface for Z3 polynomials # # Author: Leonardo de Moura (leonardo) ############################################ from z3 import * def subresultants(p, q, x): """ Return the non-constant subresultants of 'p' and 'q' with respect to the "variable" 'x'. 'p', 'q' and 'x' are Z3 expressions where 'p' and '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 b in the polynomial f(a)*f(a) + 2*f(a) + 1 >>> x, y = Reals('x y') >>> subresultants(2*x + y, 3*x - 2*y + 2, x) [-7*y + 4] >>> r = subresultants(3*y*x**2 + y**3 + 1, 2*x**3 + y + 3, x) >>> r[0] 4*y**9 + 12*y**6 + 27*y**5 + 162*y**4 + 255*y**3 + 4 >>> r[1] -6*y**4 + -6*y """ return AstVector(Z3_polynomial_subresultants(p.ctx_ref(), p.as_ast(), q.as_ast(), x.as_ast()), p.ctx) if __name__ == "__main__": import doctest if doctest.testmod().failed: exit(1) z3-z3-4.4.1/src/api/python/z3printer.py000066400000000000000000001164171260446376700176320ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface # # Author: Leonardo de Moura (leonardo) ############################################ import sys, io, z3 from z3consts import * from z3core import * from ctypes import * ############################## # # Configuration # ############################## # Z3 operator names to Z3Py _z3_op_to_str = { Z3_OP_TRUE : 'True', Z3_OP_FALSE : 'False', Z3_OP_EQ : '==', Z3_OP_DISTINCT : 'Distinct', Z3_OP_ITE : 'If', Z3_OP_AND : 'And', Z3_OP_OR : 'Or', Z3_OP_IFF : '==', Z3_OP_XOR : 'Xor', Z3_OP_NOT : 'Not', Z3_OP_IMPLIES : 'Implies', Z3_OP_IDIV : '/', Z3_OP_MOD : '%', Z3_OP_TO_REAL : 'ToReal', Z3_OP_TO_INT : 'ToInt', Z3_OP_POWER : '**', Z3_OP_IS_INT : 'IsInt', Z3_OP_BADD : '+', Z3_OP_BSUB : '-', Z3_OP_BMUL : '*', Z3_OP_BOR : '|', Z3_OP_BAND : '&', Z3_OP_BNOT : '~', Z3_OP_BXOR : '^', Z3_OP_BNEG : '-', Z3_OP_BUDIV : 'UDiv', Z3_OP_BSDIV : '/', Z3_OP_BSMOD : '%', Z3_OP_BSREM : 'SRem', Z3_OP_BUREM : 'URem', Z3_OP_EXT_ROTATE_LEFT : 'RotateLeft', Z3_OP_EXT_ROTATE_RIGHT : 'RotateRight', Z3_OP_SLEQ : '<=', Z3_OP_SLT : '<', Z3_OP_SGEQ : '>=', Z3_OP_SGT : '>', Z3_OP_ULEQ : 'ULE', Z3_OP_ULT : 'ULT', Z3_OP_UGEQ : 'UGE', Z3_OP_UGT : 'UGT', Z3_OP_SIGN_EXT : 'SignExt', Z3_OP_ZERO_EXT : 'ZeroExt', Z3_OP_REPEAT : 'RepeatBitVec', Z3_OP_BASHR : '>>', Z3_OP_BSHL : '<<', Z3_OP_BLSHR : 'LShR', Z3_OP_CONCAT : 'Concat', Z3_OP_EXTRACT : 'Extract', Z3_OP_BV2INT : 'BV2Int', Z3_OP_ARRAY_MAP : 'Map', Z3_OP_SELECT : 'Select', Z3_OP_STORE : 'Store', Z3_OP_CONST_ARRAY : 'K' } # List of infix operators _z3_infix = [ Z3_OP_EQ, Z3_OP_IFF, Z3_OP_ADD, Z3_OP_SUB, Z3_OP_MUL, Z3_OP_DIV, Z3_OP_IDIV, Z3_OP_MOD, Z3_OP_POWER, Z3_OP_LE, Z3_OP_LT, Z3_OP_GE, Z3_OP_GT, Z3_OP_BADD, Z3_OP_BSUB, Z3_OP_BMUL, Z3_OP_BSDIV, Z3_OP_BSMOD, Z3_OP_BOR, Z3_OP_BAND, Z3_OP_BXOR, Z3_OP_BSDIV, Z3_OP_SLEQ, Z3_OP_SLT, Z3_OP_SGEQ, Z3_OP_SGT, Z3_OP_BASHR, Z3_OP_BSHL ] _z3_unary = [ Z3_OP_UMINUS, Z3_OP_BNOT, Z3_OP_BNEG ] # Precedence _z3_precedence = { Z3_OP_POWER : 0, Z3_OP_UMINUS : 1, Z3_OP_BNEG : 1, Z3_OP_BNOT : 1, Z3_OP_MUL : 2, Z3_OP_DIV : 2, Z3_OP_IDIV : 2, Z3_OP_MOD : 2, Z3_OP_BMUL : 2, Z3_OP_BSDIV : 2, Z3_OP_BSMOD : 2, Z3_OP_ADD : 3, Z3_OP_SUB : 3, Z3_OP_BADD : 3, Z3_OP_BSUB : 3, Z3_OP_BASHR : 4, Z3_OP_BSHL : 4, Z3_OP_BAND : 5, Z3_OP_BXOR : 6, Z3_OP_BOR : 7, Z3_OP_LE : 8, Z3_OP_LT : 8, Z3_OP_GE : 8, Z3_OP_GT : 8, Z3_OP_EQ : 8, Z3_OP_SLEQ : 8, Z3_OP_SLT : 8, Z3_OP_SGEQ : 8, Z3_OP_SGT : 8, Z3_OP_IFF : 8, Z3_OP_FPA_NEG : 1, Z3_OP_FPA_MUL : 2, Z3_OP_FPA_DIV : 2, Z3_OP_FPA_REM : 2, Z3_OP_FPA_FMA : 2, Z3_OP_FPA_ADD: 3, Z3_OP_FPA_SUB : 3, Z3_OP_FPA_LE : 8, Z3_OP_FPA_LT : 8, Z3_OP_FPA_GE : 8, Z3_OP_FPA_GT : 8, Z3_OP_FPA_EQ : 8 } # FPA operators _z3_op_to_fpa_normal_str = { Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN : 'RoundNearestTiesToEven()', Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY : 'RoundNearestTiesToAway()', Z3_OP_FPA_RM_TOWARD_POSITIVE : 'RoundTowardPositive()', Z3_OP_FPA_RM_TOWARD_NEGATIVE : 'RoundTowardNegative()', Z3_OP_FPA_RM_TOWARD_ZERO : 'RoundTowardZero()', Z3_OP_FPA_PLUS_INF : '+oo', Z3_OP_FPA_MINUS_INF : '-oo', Z3_OP_FPA_NAN : 'NaN', Z3_OP_FPA_PLUS_ZERO : 'PZero', Z3_OP_FPA_MINUS_ZERO : 'NZero', Z3_OP_FPA_ADD : 'fpAdd', Z3_OP_FPA_SUB : 'fpSub', Z3_OP_FPA_NEG : 'fpNeg', Z3_OP_FPA_MUL : 'fpMul', Z3_OP_FPA_DIV : 'fpDiv', Z3_OP_FPA_REM : 'fpRem', Z3_OP_FPA_ABS : 'fpAbs', Z3_OP_FPA_MIN : 'fpMin', Z3_OP_FPA_MAX : 'fpMax', Z3_OP_FPA_FMA : 'fpFMA', Z3_OP_FPA_SQRT : 'fpSqrt', Z3_OP_FPA_ROUND_TO_INTEGRAL : 'fpRoundToIntegral', Z3_OP_FPA_EQ : 'fpEQ', Z3_OP_FPA_LT : 'fpLT', Z3_OP_FPA_GT : 'fpGT', Z3_OP_FPA_LE : 'fpLEQ', Z3_OP_FPA_GE : 'fpGEQ', Z3_OP_FPA_IS_NAN : 'fpIsNaN', Z3_OP_FPA_IS_INF : 'fpIsInf', Z3_OP_FPA_IS_ZERO : 'fpIsZero', Z3_OP_FPA_IS_NORMAL : 'fpIsNormal', Z3_OP_FPA_IS_SUBNORMAL : 'fpIsSubnormal', Z3_OP_FPA_IS_NEGATIVE : 'fpIsNegative', Z3_OP_FPA_IS_POSITIVE : 'fpIsPositive', Z3_OP_FPA_FP : 'fpFP', Z3_OP_FPA_TO_FP : 'fpToFP', Z3_OP_FPA_TO_FP_UNSIGNED: 'fpToFPUnsigned', Z3_OP_FPA_TO_UBV : 'fpToUBV', Z3_OP_FPA_TO_SBV : 'fpToSBV', Z3_OP_FPA_TO_REAL: 'fpToReal', Z3_OP_FPA_TO_IEEE_BV : 'fpToIEEEBV' } _z3_op_to_fpa_pretty_str = { Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN : 'RNE()', Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY : 'RNA()', Z3_OP_FPA_RM_TOWARD_POSITIVE : 'RTP()', Z3_OP_FPA_RM_TOWARD_NEGATIVE : 'RTN()', Z3_OP_FPA_RM_TOWARD_ZERO : 'RTZ()', Z3_OP_FPA_PLUS_INF : '+oo', Z3_OP_FPA_MINUS_INF : '-oo', Z3_OP_FPA_NAN : 'NaN', Z3_OP_FPA_PLUS_ZERO : '+0.0', Z3_OP_FPA_MINUS_ZERO : '-0.0', Z3_OP_FPA_ADD : '+', Z3_OP_FPA_SUB : '-', Z3_OP_FPA_MUL : '*', Z3_OP_FPA_DIV : '/', Z3_OP_FPA_REM : '%', Z3_OP_FPA_NEG : '-', Z3_OP_FPA_EQ : 'fpEQ', Z3_OP_FPA_LT : '<', Z3_OP_FPA_GT : '>', Z3_OP_FPA_LE : '<=', Z3_OP_FPA_GE : '>=' } _z3_fpa_infix = [ Z3_OP_FPA_ADD, Z3_OP_FPA_SUB, Z3_OP_FPA_MUL, Z3_OP_FPA_DIV, Z3_OP_FPA_REM, Z3_OP_FPA_LT, Z3_OP_FPA_GT, Z3_OP_FPA_LE, Z3_OP_FPA_GE ] def _is_assoc(k): return k == Z3_OP_BOR or k == Z3_OP_BXOR or k == Z3_OP_BAND or k == Z3_OP_ADD or k == Z3_OP_BADD or k == Z3_OP_MUL or k == Z3_OP_BMUL def _is_left_assoc(k): return _is_assoc(k) or k == Z3_OP_SUB or k == Z3_OP_BSUB def _is_html_assoc(k): return k == Z3_OP_AND or k == Z3_OP_OR or k == Z3_OP_IFF or _is_assoc(k) def _is_html_left_assoc(k): return _is_html_assoc(k) or k == Z3_OP_SUB or k == Z3_OP_BSUB def _is_add(k): return k == Z3_OP_ADD or k == Z3_OP_BADD def _is_sub(k): return k == Z3_OP_SUB or k == Z3_OP_BSUB import sys if sys.version < '3': import codecs def u(x): return codecs.unicode_escape_decode(x)[0] else: def u(x): return x _z3_infix_compact = [ Z3_OP_MUL, Z3_OP_BMUL, Z3_OP_POWER, Z3_OP_DIV, Z3_OP_IDIV, Z3_OP_MOD, Z3_OP_BSDIV, Z3_OP_BSMOD ] _ellipses = '...' _html_ellipses = '…' # Overwrite some of the operators for HTML _z3_pre_html_op_to_str = { Z3_OP_EQ : '=', Z3_OP_IFF : '=', Z3_OP_NOT : '¬', Z3_OP_AND : '∧', Z3_OP_OR : '∨', Z3_OP_IMPLIES : '⇒', Z3_OP_LT : '<', Z3_OP_GT : '>', Z3_OP_LE : '≤', Z3_OP_GE : '≥', Z3_OP_MUL : '·', Z3_OP_SLEQ : '≤', Z3_OP_SLT : '<', Z3_OP_SGEQ : '≥', Z3_OP_SGT : '>', Z3_OP_ULEQ : '≤u', Z3_OP_ULT : '<u', Z3_OP_UGEQ : '≥u', Z3_OP_UGT : '>u', Z3_OP_BMUL : '·', Z3_OP_BUDIV : '/u', Z3_OP_BUREM : '%u', Z3_OP_BASHR : '>>', Z3_OP_BSHL : '<<', Z3_OP_BLSHR : '>>u' } # Extra operators that are infix/unary for HTML _z3_html_infix = [ Z3_OP_AND, Z3_OP_OR, Z3_OP_IMPLIES, Z3_OP_ULEQ, Z3_OP_ULT, Z3_OP_UGEQ, Z3_OP_UGT, Z3_OP_BUDIV, Z3_OP_BUREM, Z3_OP_BLSHR ] _z3_html_unary = [ Z3_OP_NOT ] # Extra Precedence for HTML _z3_pre_html_precedence = { Z3_OP_BUDIV : 2, Z3_OP_BUREM : 2, Z3_OP_BLSHR : 4, Z3_OP_ULEQ : 8, Z3_OP_ULT : 8, Z3_OP_UGEQ : 8, Z3_OP_UGT : 8, Z3_OP_ULEQ : 8, Z3_OP_ULT : 8, Z3_OP_UGEQ : 8, Z3_OP_UGT : 8, Z3_OP_NOT : 1, Z3_OP_AND : 10, Z3_OP_OR : 11, Z3_OP_IMPLIES : 12 } ############################## # # End of Configuration # ############################## def _support_pp(a): return isinstance(a, z3.Z3PPObject) or isinstance(a, list) or isinstance(a, tuple) _infix_map = {} _unary_map = {} _infix_compact_map = {} for _k in _z3_infix: _infix_map[_k] = True for _k in _z3_unary: _unary_map[_k] = True for _k in _z3_infix_compact: _infix_compact_map[_k] = True def _is_infix(k): global _infix_map return _infix_map.get(k, False) def _is_infix_compact(k): global _infix_compact_map return _infix_compact_map.get(k, False) def _is_unary(k): global _unary_map return _unary_map.get(k, False) def _op_name(a): if isinstance(a, z3.FuncDeclRef): f = a else: f = a.decl() k = f.kind() n = _z3_op_to_str.get(k, None) if n == None: return f.name() else: return n def _get_precedence(k): global _z3_precedence return _z3_precedence.get(k, 100000) _z3_html_op_to_str = {} for _k in _z3_op_to_str: _v = _z3_op_to_str[_k] _z3_html_op_to_str[_k] = _v for _k in _z3_pre_html_op_to_str: _v = _z3_pre_html_op_to_str[_k] _z3_html_op_to_str[_k] = _v _z3_html_precedence = {} for _k in _z3_precedence: _v = _z3_precedence[_k] _z3_html_precedence[_k] = _v for _k in _z3_pre_html_precedence: _v = _z3_pre_html_precedence[_k] _z3_html_precedence[_k] = _v _html_infix_map = {} _html_unary_map = {} for _k in _z3_infix: _html_infix_map[_k] = True for _k in _z3_html_infix: _html_infix_map[_k] = True for _k in _z3_unary: _html_unary_map[_k] = True for _k in _z3_html_unary: _html_unary_map[_k] = True def _is_html_infix(k): global _html_infix_map return _html_infix_map.get(k, False) def _is_html_unary(k): global _html_unary_map return _html_unary_map.get(k, False) def _html_op_name(a): global _z3_html_op_to_str if isinstance(a, z3.FuncDeclRef): f = a else: f = a.decl() k = f.kind() n = _z3_html_op_to_str.get(k, None) if n == None: sym = Z3_get_decl_name(f.ctx_ref(), f.ast) if Z3_get_symbol_kind(f.ctx_ref(), sym) == Z3_INT_SYMBOL: return "ζ%s" % Z3_get_symbol_int(f.ctx_ref(), sym) else: # Sanitize the string return f.name() else: return n def _get_html_precedence(k): global _z3_html_predence return _z3_html_precedence.get(k, 100000) class FormatObject: def is_compose(self): return False def is_choice(self): return False def is_indent(self): return False def is_string(self): return False def is_linebreak(self): return False def is_nil(self): return True def children(self): return [] def as_tuple(self): return None def space_upto_nl(self): return (0, False) def flat(self): return self class NAryFormatObject(FormatObject): def __init__(self, fs): assert all([isinstance(a, FormatObject) for a in fs]) self.children = fs def children(self): return self.children class ComposeFormatObject(NAryFormatObject): def is_compose(sef): return True def as_tuple(self): return ('compose', [ a.as_tuple() for a in self.children ]) def space_upto_nl(self): r = 0 for child in self.children: s, nl = child.space_upto_nl() r = r + s if nl: return (r, True) return (r, False) def flat(self): return compose([a.flat() for a in self.children ]) class ChoiceFormatObject(NAryFormatObject): def is_choice(sef): return True def as_tuple(self): return ('choice', [ a.as_tuple() for a in self.children ]) def space_upto_nl(self): return self.children[0].space_upto_nl() def flat(self): return self.children[0].flat() class IndentFormatObject(FormatObject): def __init__(self, indent, child): assert isinstance(child, FormatObject) self.indent = indent self.child = child def children(self): return [self.child] def as_tuple(self): return ('indent', self.indent, self.child.as_tuple()) def space_upto_nl(self): return self.child.space_upto_nl() def flat(self): return indent(self.indent, self.child.flat()) def is_indent(self): return True class LineBreakFormatObject(FormatObject): def __init__(self): self.space = ' ' def is_linebreak(self): return True def as_tuple(self): return '' def space_upto_nl(self): return (0, True) def flat(self): return to_format(self.space) class StringFormatObject(FormatObject): def __init__(self, string): assert isinstance(string, str) self.string = string def is_string(self): return True def as_tuple(self): return self.string def space_upto_nl(self): return (getattr(self, 'size', len(self.string)), False) def fits(f, space_left): s, nl = f.space_upto_nl() return s <= space_left def to_format(arg, size=None): if isinstance(arg, FormatObject): return arg else: r = StringFormatObject(str(arg)) if size != None: r.size = size return r def compose(*args): if len(args) == 1 and (isinstance(args[0], list) or isinstance(args[0], tuple)): args = args[0] return ComposeFormatObject(args) def indent(i, arg): return IndentFormatObject(i, arg) def group(arg): return ChoiceFormatObject([arg.flat(), arg]) def line_break(): return LineBreakFormatObject() def _len(a): if isinstance(a, StringFormatObject): return getattr(a, 'size', len(a.string)) else: return len(a) def seq(args, sep=',', space=True): nl = line_break() if not space: nl.space = '' r = [] r.append(args[0]) num = len(args) for i in range(num - 1): r.append(to_format(sep)) r.append(nl) r.append(args[i+1]) return compose(r) def seq1(header, args, lp='(', rp=')'): return group(compose(to_format(header), to_format(lp), indent(len(lp) + _len(header), seq(args)), to_format(rp))) def seq2(header, args, i=4, lp='(', rp=')'): if len(args) == 0: return compose(to_format(header), to_format(lp), to_format(rp)) else: return group(compose(indent(len(lp), compose(to_format(lp), to_format(header))), indent(i, compose(seq(args), to_format(rp))))) def seq3(args, lp='(', rp=')'): if len(args) == 0: return compose(to_format(lp), to_format(rp)) else: return group(indent(len(lp), compose(to_format(lp), seq(args), to_format(rp)))) class StopPPException(Exception): def __str__(self): return 'pp-interrupted' class PP: def __init__(self): self.max_lines = 200 self.max_width = 60 self.bounded = False self.max_indent = 40 def pp_string(self, f, indent): if not self.bounded or self.pos <= self.max_width: sz = _len(f) if self.bounded and self.pos + sz > self.max_width: self.out.write(u(_ellipses)) else: self.pos = self.pos + sz self.ribbon_pos = self.ribbon_pos + sz self.out.write(u(f.string)) def pp_compose(self, f, indent): for c in f.children: self.pp(c, indent) def pp_choice(self, f, indent): space_left = self.max_width - self.pos if space_left > 0 and fits(f.children[0], space_left): self.pp(f.children[0], indent) else: self.pp(f.children[1], indent) def pp_line_break(self, f, indent): self.pos = indent self.ribbon_pos = 0 self.line = self.line + 1 if self.line < self.max_lines: self.out.write(u('\n')) for i in range(indent): self.out.write(u(' ')) else: self.out.write(u('\n...')) raise StopPPException() def pp(self, f, indent): if f.is_string(): self.pp_string(f, indent) elif f.is_indent(): self.pp(f.child, min(indent + f.indent, self.max_indent)) elif f.is_compose(): self.pp_compose(f, indent) elif f.is_choice(): self.pp_choice(f, indent) elif f.is_linebreak(): self.pp_line_break(f, indent) else: return def __call__(self, out, f): try: self.pos = 0 self.ribbon_pos = 0 self.line = 0 self.out = out self.pp(f, 0) except StopPPException: return class Formatter: def __init__(self): global _ellipses self.max_depth = 20 self.max_args = 128 self.rational_to_decimal = False self.precision = 10 self.ellipses = to_format(_ellipses) self.max_visited = 10000 self.fpa_pretty = True def pp_ellipses(self): return self.ellipses def pp_arrow(self): return ' ->' def pp_unknown(self): return '' def pp_name(self, a): return to_format(_op_name(a)) def is_infix(self, a): return _is_infix(a) def is_unary(self, a): return _is_unary(a) def get_precedence(self, a): return _get_precedence(a) def is_infix_compact(self, a): return _is_infix_compact(a) def is_infix_unary(self, a): return self.is_infix(a) or self.is_unary(a) def add_paren(self, a): return compose(to_format('('), indent(1, a), to_format(')')) def pp_sort(self, s): if isinstance(s, z3.ArraySortRef): return seq1('Array', (self.pp_sort(s.domain()), self.pp_sort(s.range()))) elif isinstance(s, z3.BitVecSortRef): return seq1('BitVec', (to_format(s.size()), )) elif isinstance(s, z3.FPSortRef): return seq1('FPSort', (to_format(s.ebits()), to_format(s.sbits()))) else: return to_format(s.name()) def pp_const(self, a): return self.pp_name(a) def pp_int(self, a): return to_format(a.as_string()) def pp_rational(self, a): if not self.rational_to_decimal: return to_format(a.as_string()) else: return to_format(a.as_decimal(self.precision)) def pp_algebraic(self, a): return to_format(a.as_decimal(self.precision)) def pp_bv(self, a): return to_format(a.as_string()) def pp_fprm_value(self, a): z3._z3_assert(z3.is_fprm_value(a), 'expected FPRMNumRef') if self.fpa_pretty and (a.decl().kind() in _z3_op_to_fpa_pretty_str): return to_format(_z3_op_to_fpa_pretty_str.get(a.decl().kind())) else: return to_format(_z3_op_to_fpa_normal_str.get(a.decl().kind())) def pp_fp_value(self, a): z3._z3_assert(isinstance(a, z3.FPNumRef), 'type mismatch') if not self.fpa_pretty: if (a.isNaN()): return to_format('NaN') elif (a.isInf()): if (a.isNegative()): return to_format('-oo') else: return to_format('+oo') elif (a.isZero()): if (a.isNegative()): return to_format('-zero') else: return to_format('+zero') else: z3._z3_assert(z3.is_fp_value(a), 'expecting FP num ast') r = [] sgn = c_int(0) sgnb = Z3_fpa_get_numeral_sign(a.ctx_ref(), a.ast, byref(sgn)) sig = Z3_fpa_get_numeral_significand_string(a.ctx_ref(), a.ast) exp = Z3_fpa_get_numeral_exponent_string(a.ctx_ref(), a.ast) r.append(to_format('FPVal(')) if sgnb and sgn.value != 0: r.append(to_format('-')) r.append(to_format(sig)) r.append(to_format('*(2**')) r.append(to_format(exp)) r.append(to_format(', ')) r.append(to_format(a.sort())) r.append(to_format('))')) return compose(r) else: if (a.isNaN()): return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_NAN]) elif (a.isInf()): if (a.isNegative()): return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_MINUS_INF]) else: return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_PLUS_INF]) elif (a.isZero()): if (a.isNegative()): return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_MINUS_ZERO]) else: return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_PLUS_ZERO]) else: z3._z3_assert(z3.is_fp_value(a), 'expecting FP num ast') r = [] sgn = (ctypes.c_int)(0) sgnb = Z3_fpa_get_numeral_sign(a.ctx_ref(), a.ast, byref(sgn)) sig = Z3_fpa_get_numeral_significand_string(a.ctx_ref(), a.ast) exp = Z3_fpa_get_numeral_exponent_string(a.ctx_ref(), a.ast) if sgnb and sgn.value != 0: r.append(to_format('-')) r.append(to_format(sig)) if (exp != '0'): r.append(to_format('*(2**')) r.append(to_format(exp)) r.append(to_format(')')) return compose(r) def pp_fp(self, a, d, xs): z3._z3_assert(isinstance(a, z3.FPRef), "type mismatch") k = a.decl().kind() op = '?' if (self.fpa_pretty and k in _z3_op_to_fpa_pretty_str): op = _z3_op_to_fpa_pretty_str[k] elif k in _z3_op_to_fpa_normal_str: op = _z3_op_to_fpa_normal_str[k] elif k in _z3_op_to_str: op = _z3_op_to_str[k] n = a.num_args() if self.fpa_pretty: if self.is_infix(k) and n >= 3: rm = a.arg(0) if z3.is_fprm_value(rm) and z3._dflt_rm(a.ctx).eq(rm): arg1 = to_format(self.pp_expr(a.arg(1), d+1, xs)) arg2 = to_format(self.pp_expr(a.arg(2), d+1, xs)) r = [] r.append(arg1) r.append(to_format(' ')) r.append(to_format(op)) r.append(to_format(' ')) r.append(arg2) return compose(r) elif k == Z3_OP_FPA_NEG: return compose([to_format('-') , to_format(self.pp_expr(a.arg(0), d+1, xs))]) if k in _z3_op_to_fpa_normal_str: op = _z3_op_to_fpa_normal_str[k] r = [] r.append(to_format(op)) if not z3.is_const(a): r.append(to_format('(')) first = True for c in a.children(): if first: first = False else: r.append(to_format(', ')) r.append(self.pp_expr(c, d+1, xs)) r.append(to_format(')')) return compose(r) else: return to_format(a.as_string()) def pp_prefix(self, a, d, xs): r = [] sz = 0 for child in a.children(): r.append(self.pp_expr(child, d+1, xs)) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break return seq1(self.pp_name(a), r) def is_assoc(self, k): return _is_assoc(k) def is_left_assoc(self, k): return _is_left_assoc(k) def infix_args_core(self, a, d, xs, r): sz = len(r) k = a.decl().kind() p = self.get_precedence(k) first = True for child in a.children(): child_pp = self.pp_expr(child, d+1, xs) child_k = None if z3.is_app(child): child_k = child.decl().kind() if k == child_k and (self.is_assoc(k) or (first and self.is_left_assoc(k))): self.infix_args_core(child, d, xs, r) sz = len(r) if sz > self.max_args: return elif self.is_infix_unary(child_k): child_p = self.get_precedence(child_k) if p > child_p or (_is_add(k) and _is_sub(child_k)) or (_is_sub(k) and first and _is_add(child_k)): r.append(child_pp) else: r.append(self.add_paren(child_pp)) sz = sz + 1 elif z3.is_quantifier(child): r.append(self.add_paren(child_pp)) else: r.append(child_pp) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) return first = False def infix_args(self, a, d, xs): r = [] self.infix_args_core(a, d, xs, r) return r def pp_infix(self, a, d, xs): k = a.decl().kind() if self.is_infix_compact(k): op = self.pp_name(a) return group(seq(self.infix_args(a, d, xs), op, False)) else: op = self.pp_name(a) sz = _len(op) op.string = ' ' + op.string op.size = sz + 1 return group(seq(self.infix_args(a, d, xs), op)) def pp_unary(self, a, d, xs): k = a.decl().kind() p = self.get_precedence(k) child = a.children()[0] child_k = None if z3.is_app(child): child_k = child.decl().kind() child_pp = self.pp_expr(child, d+1, xs) if k != child_k and self.is_infix_unary(child_k): child_p = self.get_precedence(child_k) if p <= child_p: child_pp = self.add_paren(child_pp) if z3.is_quantifier(child): child_pp = self.add_paren(child_pp) name = self.pp_name(a) return compose(to_format(name), indent(_len(name), child_pp)) def pp_power_arg(self, arg, d, xs): r = self.pp_expr(arg, d+1, xs) k = None if z3.is_app(arg): k = arg.decl().kind() if self.is_infix_unary(k) or (z3.is_rational_value(arg) and arg.denominator_as_long() != 1): return self.add_paren(r) else: return r def pp_power(self, a, d, xs): arg1_pp = self.pp_power_arg(a.arg(0), d+1, xs) arg2_pp = self.pp_power_arg(a.arg(1), d+1, xs) return group(seq((arg1_pp, arg2_pp), '**', False)) def pp_neq(self): return to_format("!=") def pp_distinct(self, a, d, xs): if a.num_args() == 2: op = self.pp_neq() sz = _len(op) op.string = ' ' + op.string op.size = sz + 1 return group(seq(self.infix_args(a, d, xs), op)) else: return self.pp_prefix(a, d, xs) def pp_select(self, a, d, xs): if a.num_args() != 2: return self.pp_prefix(a, d, xs) else: arg1_pp = self.pp_expr(a.arg(0), d+1, xs) arg2_pp = self.pp_expr(a.arg(1), d+1, xs) return compose(arg1_pp, indent(2, compose(to_format('['), arg2_pp, to_format(']')))) def pp_unary_param(self, a, d, xs): p = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 0) arg = self.pp_expr(a.arg(0), d+1, xs) return seq1(self.pp_name(a), [ to_format(p), arg ]) def pp_extract(self, a, d, xs): h = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 0) l = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 1) arg = self.pp_expr(a.arg(0), d+1, xs) return seq1(self.pp_name(a), [ to_format(h), to_format(l), arg ]) def pp_pattern(self, a, d, xs): if a.num_args() == 1: return self.pp_expr(a.arg(0), d, xs) else: return seq1('MultiPattern', [ self.pp_expr(arg, d+1, xs) for arg in a.children() ]) def pp_map(self, a, d, xs): r = [] sz = 0 f = z3.get_map_func(a) r.append(to_format(f.name())) for child in a.children(): r.append(self.pp_expr(child, d+1, xs)) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break return seq1(self.pp_name(a), r) def pp_K(self, a, d, xs): return seq1(self.pp_name(a), [ self.pp_sort(a.domain()), self.pp_expr(a.arg(0), d+1, xs) ]) def pp_app(self, a, d, xs): if z3.is_int_value(a): return self.pp_int(a) elif z3.is_rational_value(a): return self.pp_rational(a) elif z3.is_algebraic_value(a): return self.pp_algebraic(a) elif z3.is_bv_value(a): return self.pp_bv(a) elif z3.is_fprm_value(a): return self.pp_fprm_value(a) elif z3.is_fp_value(a): return self.pp_fp_value(a) elif z3.is_fp(a): return self.pp_fp(a, d, xs) elif z3.is_const(a): return self.pp_const(a) else: f = a.decl() k = f.kind() if k == Z3_OP_POWER: return self.pp_power(a, d, xs) elif k == Z3_OP_DISTINCT: return self.pp_distinct(a, d, xs) elif k == Z3_OP_SELECT: return self.pp_select(a, d, xs) elif k == Z3_OP_SIGN_EXT or k == Z3_OP_ZERO_EXT or k == Z3_OP_REPEAT: return self.pp_unary_param(a, d, xs) elif k == Z3_OP_EXTRACT: return self.pp_extract(a, d, xs) elif k == Z3_OP_ARRAY_MAP: return self.pp_map(a, d, xs) elif k == Z3_OP_CONST_ARRAY: return self.pp_K(a, d, xs) elif z3.is_pattern(a): return self.pp_pattern(a, d, xs) elif self.is_infix(k): return self.pp_infix(a, d, xs) elif self.is_unary(k): return self.pp_unary(a, d, xs) else: return self.pp_prefix(a, d, xs) def pp_var(self, a, d, xs): idx = z3.get_var_index(a) sz = len(xs) if idx >= sz: return seq1('Var', (to_format(idx),)) else: return to_format(xs[sz - idx - 1]) def pp_quantifier(self, a, d, xs): ys = [ to_format(a.var_name(i)) for i in range(a.num_vars()) ] new_xs = xs + ys body_pp = self.pp_expr(a.body(), d+1, new_xs) if len(ys) == 1: ys_pp = ys[0] else: ys_pp = seq3(ys, '[', ']') if a.is_forall(): header = 'ForAll' else: header = 'Exists' return seq1(header, (ys_pp, body_pp)) def pp_expr(self, a, d, xs): self.visited = self.visited + 1 if d > self.max_depth or self.visited > self.max_visited: return self.pp_ellipses() if z3.is_app(a): return self.pp_app(a, d, xs) elif z3.is_quantifier(a): return self.pp_quantifier(a, d, xs) elif z3.is_var(a): return self.pp_var(a, d, xs) else: return to_format(self.pp_unknown()) def pp_seq_core(self, f, a, d, xs): self.visited = self.visited + 1 if d > self.max_depth or self.visited > self.max_visited: return self.pp_ellipses() r = [] sz = 0 for elem in a: r.append(f(elem, d+1, xs)) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break return seq3(r, '[', ']') def pp_seq(self, a, d, xs): return self.pp_seq_core(self.pp_expr, a, d, xs) def pp_seq_seq(self, a, d, xs): return self.pp_seq_core(self.pp_seq, a, d, xs) def pp_model(self, m): r = [] sz = 0 for d in m: i = m[d] if isinstance(i, z3.FuncInterp): i_pp = self.pp_func_interp(i) else: i_pp = self.pp_expr(i, 0, []) name = self.pp_name(d) r.append(compose(name, to_format(' = '), indent(_len(name) + 3, i_pp))) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break return seq3(r, '[', ']') def pp_func_entry(self, e): num = e.num_args() if num > 1: args = [] for i in range(num): args.append(self.pp_expr(e.arg_value(i), 0, [])) args_pp = group(seq3(args)) else: args_pp = self.pp_expr(e.arg_value(0), 0, []) value_pp = self.pp_expr(e.value(), 0, []) return group(seq((args_pp, value_pp), self.pp_arrow())) def pp_func_interp(self, f): r = [] sz = 0 num = f.num_entries() for i in range(num): r.append(self.pp_func_entry(f.entry(i))) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break if sz <= self.max_args: else_val = f.else_value() if else_val == None: else_pp = to_format('#unspecified') else: else_pp = self.pp_expr(else_val, 0, []) r.append(group(seq((to_format('else'), else_pp), self.pp_arrow()))) return seq3(r, '[', ']') def pp_list(self, a): r = [] sz = 0 for elem in a: if _support_pp(elem): r.append(self.main(elem)) else: r.append(to_format(str(elem))) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break if isinstance(a, tuple): return seq3(r) else: return seq3(r, '[', ']') def main(self, a): if z3.is_expr(a): return self.pp_expr(a, 0, []) elif z3.is_sort(a): return self.pp_sort(a) elif z3.is_func_decl(a): return self.pp_name(a) elif isinstance(a, z3.Goal) or isinstance(a, z3.AstVector): return self.pp_seq(a, 0, []) elif isinstance(a, z3.Solver): return self.pp_seq(a.assertions(), 0, []) elif isinstance(a, z3.Fixedpoint): return a.sexpr() elif isinstance(a, z3.Optimize): return a.sexpr() elif isinstance(a, z3.ApplyResult): return self.pp_seq_seq(a, 0, []) elif isinstance(a, z3.ModelRef): return self.pp_model(a) elif isinstance(a, z3.FuncInterp): return self.pp_func_interp(a) elif isinstance(a, list) or isinstance(a, tuple): return self.pp_list(a) else: return to_format(self.pp_unknown()) def __call__(self, a): self.visited = 0 return self.main(a) class HTMLFormatter(Formatter): def __init__(self): Formatter.__init__(self) global _html_ellipses self.ellipses = to_format(_html_ellipses) def pp_arrow(self): return to_format(' →', 1) def pp_unknown(self): return 'unknown' def pp_name(self, a): r = _html_op_name(a) if r[0] == '&' or r[0] == '/' or r[0] == '%': return to_format(r, 1) else: pos = r.find('__') if pos == -1 or pos == 0: return to_format(r) else: sz = len(r) if pos + 2 == sz: return to_format(r) else: return to_format('%s%s' % (r[0:pos], r[pos+2:sz]), sz - 2) def is_assoc(self, k): return _is_html_assoc(k) def is_left_assoc(self, k): return _is_html_left_assoc(k) def is_infix(self, a): return _is_html_infix(a) def is_unary(self, a): return _is_html_unary(a) def get_precedence(self, a): return _get_html_precedence(a) def pp_neq(self): return to_format("≠") def pp_power(self, a, d, xs): arg1_pp = self.pp_power_arg(a.arg(0), d+1, xs) arg2_pp = self.pp_expr(a.arg(1), d+1, xs) return compose(arg1_pp, to_format('', 1), arg2_pp, to_format('', 1)) def pp_var(self, a, d, xs): idx = z3.get_var_index(a) sz = len(xs) if idx >= sz: # 957 is the greek letter nu return to_format('ν%s' % idx, 1) else: return to_format(xs[sz - idx - 1]) def pp_quantifier(self, a, d, xs): ys = [ to_format(a.var_name(i)) for i in range(a.num_vars()) ] new_xs = xs + ys body_pp = self.pp_expr(a.body(), d+1, new_xs) ys_pp = group(seq(ys)) if a.is_forall(): header = '∀' else: header = '∃' return group(compose(to_format(header, 1), indent(1, compose(ys_pp, to_format(' :'), line_break(), body_pp)))) _PP = PP() _Formatter = Formatter() def set_pp_option(k, v): if k == 'html_mode': if v: set_html_mode(True) else: set_html_mode(False) return True if k == 'fpa_pretty': if v: set_fpa_pretty(True) else: set_fpa_pretty(False) return True val = getattr(_PP, k, None) if val != None: z3._z3_assert(type(v) == type(val), "Invalid pretty print option value") setattr(_PP, k, v) return True val = getattr(_Formatter, k, None) if val != None: z3._z3_assert(type(v) == type(val), "Invalid pretty print option value") setattr(_Formatter, k, v) return True return False def obj_to_string(a): out = io.StringIO() _PP(out, _Formatter(a)) return out.getvalue() _html_out = None def set_html_mode(flag=True): global _Formatter if flag: _Formatter = HTMLFormatter() else: _Formatter = Formatter() def set_fpa_pretty(flag=True): global _Formatter global _z3_op_to_str _Formatter.fpa_pretty = flag if flag: for (_k,_v) in _z3_op_to_fpa_pretty_str.items(): _z3_op_to_str[_k] = _v for _k in _z3_fpa_infix: _infix_map[_k] = True else: for (_k,_v) in _z3_op_to_fpa_normal_str.items(): _z3_op_to_str[_k] = _v for _k in _z3_fpa_infix: _infix_map[_k] = False set_fpa_pretty(True) def in_html_mode(): return isinstance(_Formatter, HTMLFormatter) def pp(a): if _support_pp(a): print(obj_to_string(a)) else: print(a) def print_matrix(m): z3._z3_assert(isinstance(m, list) or isinstance(m, tuple), "matrix expected") if not in_html_mode(): print(obj_to_string(m)) else: print('') for r in m: z3._z3_assert(isinstance(r, list) or isinstance(r, tuple), "matrix expected") print('') for c in r: print('' % c) print('') print('
%s
') def insert_line_breaks(s, width): """Break s in lines of size width (approx)""" sz = len(s) if sz <= width: return s new_str = io.StringIO() w = 0 for i in range(sz): if w > width and s[i] == ' ': new_str.write(u('
')) w = 0 else: new_str.write(u(s[i])) w = w + 1 return new_str.getvalue() z3-z3-4.4.1/src/api/python/z3rcf.py000066400000000000000000000117501260446376700167130ustar00rootroot00000000000000############################################ # Copyright (c) 2013 Microsoft Corporation # # Z3 Python interface for Z3 Real Closed Fields # that may contain # - computable transcendentals # - infinitesimals # - algebraic extensions # # Author: Leonardo de Moura (leonardo) ############################################ from z3 import * from z3core import * from z3printer import * from fractions import Fraction def _to_rcfnum(num, ctx=None): if isinstance(num, RCFNum): return num else: return RCFNum(num, ctx) def Pi(ctx=None): ctx = z3._get_ctx(ctx) return RCFNum(Z3_rcf_mk_pi(ctx.ref()), ctx) def E(ctx=None): ctx = z3._get_ctx(ctx) return RCFNum(Z3_rcf_mk_e(ctx.ref()), ctx) def MkInfinitesimal(name="eps", ctx=None): # Todo: remove parameter name. # For now, we keep it for backward compatibility. ctx = z3._get_ctx(ctx) return RCFNum(Z3_rcf_mk_infinitesimal(ctx.ref()), ctx) def MkRoots(p, ctx=None): ctx = z3._get_ctx(ctx) num = len(p) _tmp = [] _as = (RCFNumObj * num)() _rs = (RCFNumObj * num)() for i in range(num): _a = _to_rcfnum(p[i], ctx) _tmp.append(_a) # prevent GC _as[i] = _a.num nr = Z3_rcf_mk_roots(ctx.ref(), num, _as, _rs) r = [] for i in range(nr): r.append(RCFNum(_rs[i], ctx)) return r class RCFNum: def __init__(self, num, ctx=None): # TODO: add support for converting AST numeral values into RCFNum if isinstance(num, RCFNumObj): self.num = num self.ctx = z3._get_ctx(ctx) else: self.ctx = z3._get_ctx(ctx) self.num = Z3_rcf_mk_rational(self.ctx_ref(), str(num)) def __del__(self): Z3_rcf_del(self.ctx_ref(), self.num) def ctx_ref(self): return self.ctx.ref() def __repr__(self): return Z3_rcf_num_to_string(self.ctx_ref(), self.num, False, in_html_mode()) def compact_str(self): return Z3_rcf_num_to_string(self.ctx_ref(), self.num, True, in_html_mode()) def __add__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_add(self.ctx_ref(), self.num, v.num), self.ctx) def __radd__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_add(self.ctx_ref(), v.num, self.num), self.ctx) def __mul__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_mul(self.ctx_ref(), self.num, v.num), self.ctx) def __rmul__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_mul(self.ctx_ref(), v.num, self.num), self.ctx) def __sub__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_sub(self.ctx_ref(), self.num, v.num), self.ctx) def __rsub__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_sub(self.ctx_ref(), v.num, self.num), self.ctx) def __div__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_div(self.ctx_ref(), self.num, v.num), self.ctx) def __rdiv__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_div(self.ctx_ref(), v.num, self.num), self.ctx) def __neg__(self): return self.__rsub__(0) def power(self, k): return RCFNum(Z3_rcf_power(self.ctx_ref(), self.num, k), self.ctx) def __pow__(self, k): return self.power(k) def decimal(self, prec=5): return Z3_rcf_num_to_decimal_string(self.ctx_ref(), self.num, prec) def __lt__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_lt(self.ctx_ref(), self.num, v.num) def __rlt__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_lt(self.ctx_ref(), v.num, self.num) def __gt__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_gt(self.ctx_ref(), self.num, v.num) def __rgt__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_gt(self.ctx_ref(), v.num, self.num) def __le__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_le(self.ctx_ref(), self.num, v.num) def __rle__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_le(self.ctx_ref(), v.num, self.num) def __ge__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_ge(self.ctx_ref(), self.num, v.num) def __rge__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_ge(self.ctx_ref(), v.num, self.num) def __eq__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_eq(self.ctx_ref(), self.num, v.num) def __ne__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_neq(self.ctx_ref(), self.num, v.num) def split(self): n = (RCFNumObj * 1)() d = (RCFNumObj * 1)() Z3_rcf_get_numerator_denominator(self.ctx_ref(), self.num, n, d) return (RCFNum(n[0], self.ctx), RCFNum(d[0], self.ctx)) z3-z3-4.4.1/src/api/python/z3test.py000066400000000000000000000004221260446376700171120ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface # # Author: Leonardo de Moura (leonardo) ############################################ import z3, doctest r = doctest.testmod(z3) if r.failed != 0: exit(1) z3-z3-4.4.1/src/api/python/z3types.py000066400000000000000000000073671260446376700173160ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface # # Author: Leonardo de Moura (leonardo) ############################################ import ctypes, z3core class Z3Exception(Exception): def __init__(self, value): self.value = value def __str__(self): return str(self.value) class ContextObj(ctypes.c_void_p): def __init__(self, context): self._as_parameter_ = context def from_param(obj): return obj class Config(ctypes.c_void_p): def __init__(self, config): self._as_parameter_ = config def from_param(obj): return obj class Symbol(ctypes.c_void_p): def __init__(self, symbol): self._as_parameter_ = symbol def from_param(obj): return obj class Sort(ctypes.c_void_p): def __init__(self, sort): self._as_parameter_ = sort def from_param(obj): return obj class FuncDecl(ctypes.c_void_p): def __init__(self, decl): self._as_parameter_ = decl def from_param(obj): return obj class Ast(ctypes.c_void_p): def __init__(self, ast): self._as_parameter_ = ast def from_param(obj): return obj class Pattern(ctypes.c_void_p): def __init__(self, pattern): self._as_parameter_ = pattern def from_param(obj): return obj class Model(ctypes.c_void_p): def __init__(self, model): self._as_parameter_ = model def from_param(obj): return obj class Literals(ctypes.c_void_p): def __init__(self, literals): self._as_parameter_ = literals def from_param(obj): return obj class Constructor(ctypes.c_void_p): def __init__(self, constructor): self._as_parameter_ = constructor def from_param(obj): return obj class ConstructorList(ctypes.c_void_p): def __init__(self, constructor_list): self._as_parameter_ = constructor_list def from_param(obj): return obj class GoalObj(ctypes.c_void_p): def __init__(self, goal): self._as_parameter_ = goal def from_param(obj): return obj class TacticObj(ctypes.c_void_p): def __init__(self, tactic): self._as_parameter_ = tactic def from_param(obj): return obj class ProbeObj(ctypes.c_void_p): def __init__(self, probe): self._as_parameter_ = probe def from_param(obj): return obj class ApplyResultObj(ctypes.c_void_p): def __init__(self, obj): self._as_parameter_ = obj def from_param(obj): return obj class StatsObj(ctypes.c_void_p): def __init__(self, statistics): self._as_parameter_ = statistics def from_param(obj): return obj class SolverObj(ctypes.c_void_p): def __init__(self, solver): self._as_parameter_ = solver def from_param(obj): return obj class FixedpointObj(ctypes.c_void_p): def __init__(self, fixedpoint): self._as_parameter_ = fixedpoint def from_param(obj): return obj class OptimizeObj(ctypes.c_void_p): def __init__(self, optimize): self._as_parameter_ = optimize def from_param(obj): return obj class ModelObj(ctypes.c_void_p): def __init__(self, model): self._as_parameter_ = model def from_param(obj): return obj class AstVectorObj(ctypes.c_void_p): def __init__(self, vector): self._as_parameter_ = vector def from_param(obj): return obj class AstMapObj(ctypes.c_void_p): def __init__(self, ast_map): self._as_parameter_ = ast_map def from_param(obj): return obj class Params(ctypes.c_void_p): def __init__(self, params): self._as_parameter_ = params def from_param(obj): return obj class ParamDescrs(ctypes.c_void_p): def __init__(self, paramdescrs): self._as_parameter_ = paramdescrs def from_param(obj): return obj class FuncInterpObj(ctypes.c_void_p): def __init__(self, f): self._as_parameter_ = f def from_param(obj): return obj class FuncEntryObj(ctypes.c_void_p): def __init__(self, e): self._as_parameter_ = e def from_param(obj): return obj class RCFNumObj(ctypes.c_void_p): def __init__(self, e): self._as_parameter_ = e def from_param(obj): return obj z3-z3-4.4.1/src/api/python/z3util.py000066400000000000000000000262411260446376700171170ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface # # Authors: Leonardo de Moura (leonardo) # ThanhVu (Vu) Nguyen ############################################ """ Usage: import common_z3 as CM_Z3 """ from z3 import * def vset(seq, idfun=None, as_list=True): # This functions preserves the order of arguments while removing duplicates. # This function is from https://code.google.com/p/common-python-vu/source/browse/vu_common.py # (Thanhu's personal code). It has been copied here to avoid a dependency on vu_common.py. """ order preserving >>> vset([[11,2],1, [10,['9',1]],2, 1, [11,2],[3,3],[10,99],1,[10,['9',1]]],idfun=repr) [[11, 2], 1, [10, ['9', 1]], 2, [3, 3], [10, 99]] """ def _uniq_normal(seq): d_ = {} for s in seq: if s not in d_: d_[s] = None yield s def _uniq_idfun(seq,idfun): d_ = {} for s in seq: h_ = idfun(s) if h_ not in d_: d_[h_] = None yield s if idfun is None: res = _uniq_normal(seq) else: res = _uniq_idfun(seq,idfun) return list(res) if as_list else res def get_z3_version(as_str=False): major = ctypes.c_uint(0) minor = ctypes.c_uint(0) build = ctypes.c_uint(0) rev = ctypes.c_uint(0) Z3_get_version(major,minor,build,rev) rs = map(int,(major.value,minor.value,build.value,rev.value)) if as_str: return "{}.{}.{}.{}".format(*rs) else: return rs def ehash(v): """ Returns a 'stronger' hash value than the default hash() method. The result from hash() is not enough to distinguish between 2 z3 expressions in some cases. Note: the following doctests will fail with Python 2.x as the default formatting doesn't match that of 3.x. >>> x1 = Bool('x'); x2 = Bool('x'); x3 = Int('x') >>> print(x1.hash(),x2.hash(),x3.hash()) #BAD: all same hash values 783810685 783810685 783810685 >>> print(ehash(x1), ehash(x2), ehash(x3)) x_783810685_1 x_783810685_1 x_783810685_2 """ if __debug__: assert is_expr(v) return "{}_{}_{}".format(str(v),v.hash(),v.sort_kind()) """ In Z3, variables are called *uninterpreted* consts and variables are *interpreted* consts. """ def is_expr_var(v): """ EXAMPLES: >>> is_expr_var(Int('7')) True >>> is_expr_var(IntVal('7')) False >>> is_expr_var(Bool('y')) True >>> is_expr_var(Int('x') + 7 == Int('y')) False >>> LOnOff, (On,Off) = EnumSort("LOnOff",['On','Off']) >>> Block,Reset,SafetyInjection=Consts("Block Reset SafetyInjection",LOnOff) >>> is_expr_var(LOnOff) False >>> is_expr_var(On) False >>> is_expr_var(Block) True >>> is_expr_var(SafetyInjection) True """ return is_const(v) and v.decl().kind()==Z3_OP_UNINTERPRETED def is_expr_val(v): """ EXAMPLES: >>> is_expr_val(Int('7')) False >>> is_expr_val(IntVal('7')) True >>> is_expr_val(Bool('y')) False >>> is_expr_val(Int('x') + 7 == Int('y')) False >>> LOnOff, (On,Off) = EnumSort("LOnOff",['On','Off']) >>> Block,Reset,SafetyInjection=Consts("Block Reset SafetyInjection",LOnOff) >>> is_expr_val(LOnOff) False >>> is_expr_val(On) True >>> is_expr_val(Block) False >>> is_expr_val(SafetyInjection) False """ return is_const(v) and v.decl().kind()!=Z3_OP_UNINTERPRETED def get_vars(f,rs=[]): """ >>> x,y = Ints('x y') >>> a,b = Bools('a b') >>> get_vars(Implies(And(x+y==0,x*2==10),Or(a,Implies(a,b==False)))) [x, y, a, b] """ if __debug__: assert is_expr(f) if is_const(f): if is_expr_val(f): return rs else: #variable return vset(rs + [f],str) else: for f_ in f.children(): rs = get_vars(f_,rs) return vset(rs,str) def mk_var(name,vsort): if vsort.kind() == Z3_INT_SORT: v = Int(name) elif vsort.kind() == Z3_REAL_SORT: v = Real(name) elif vsort.kind() == Z3_BOOL_SORT: v = Bool(name) elif vsort.kind() == Z3_DATATYPE_SORT: v = Const(name,vsort) else: assert False, 'Cannot handle this sort (s: %sid: %d)'\ %(vsort,vsort.kind()) return v def prove(claim,assume=None,verbose=0): """ >>> r,m = prove(BoolVal(True),verbose=0); r,model_str(m,as_str=False) (True, None) #infinite counter example when proving contradiction >>> r,m = prove(BoolVal(False)); r,model_str(m,as_str=False) (False, []) >>> x,y,z=Bools('x y z') >>> r,m = prove(And(x,Not(x))); r,model_str(m,as_str=True) (False, '[]') >>> r,m = prove(True,assume=And(x,Not(x)),verbose=0) Traceback (most recent call last): ... AssertionError: Assumption is alway False! >>> r,m = prove(Implies(x,x),assume=y,verbose=2); r,model_str(m,as_str=False) assume: y claim: Implies(x, x) to_prove: Implies(y, Implies(x, x)) (True, None) >>> r,m = prove(And(x,True),assume=y,verbose=0); r,model_str(m,as_str=False) (False, [(x, False), (y, True)]) >>> r,m = prove(And(x,y),assume=y,verbose=0) >>> print(r) False >>> print(model_str(m,as_str=True)) x = False y = True >>> a,b = Ints('a b') >>> r,m = prove(a**b == b**a,assume=None,verbose=0) E: cannot solve ! >>> r is None and m is None True """ if __debug__: assert not assume or is_expr(assume) to_prove = claim if assume: if __debug__: is_proved,_ = prove(Not(assume)) def _f(): emsg = "Assumption is alway False!" if verbose >= 2: emsg = "{}\n{}".format(assume,emsg) return emsg assert is_proved==False, _f() to_prove = Implies(assume,to_prove) if verbose >= 2: print('assume: ') print(assume) print('claim: ') print(claim) print('to_prove: ') print(to_prove) f = Not(to_prove) models = get_models(f,k=1) if models is None: #unknown print('E: cannot solve !') return None, None elif models == False: #unsat return True,None else: #sat if __debug__: assert isinstance(models,list) if models: return False, models[0] #the first counterexample else: return False, [] #infinite counterexample,models def get_models(f,k): """ Returns the first k models satisfiying f. If f is not satisfiable, returns False. If f cannot be solved, returns None If f is satisfiable, returns the first k models Note that if f is a tautology, e.g.\ True, then the result is [] Based on http://stackoverflow.com/questions/11867611/z3py-checking-all-solutions-for-equation EXAMPLES: >>> x, y = Ints('x y') >>> len(get_models(And(0<=x,x <= 4),k=11)) 5 >>> get_models(And(0<=x**y,x <= 1),k=2) is None True >>> get_models(And(0<=x,x <= -1),k=2) False >>> len(get_models(x+y==7,5)) 5 >>> len(get_models(And(x<=5,x>=1),7)) 5 >>> get_models(And(x<=0,x>=5),7) False >>> x = Bool('x') >>> get_models(And(x,Not(x)),k=1) False >>> get_models(Implies(x,x),k=1) [] >>> get_models(BoolVal(True),k=1) [] """ if __debug__: assert is_expr(f) assert k>=1 s = Solver() s.add(f) models = [] i = 0 while s.check() == sat and i < k: i = i + 1 m = s.model() if not m: #if m == [] break models.append(m) #create new constraint to block the current model block = Not(And([v() == m[v] for v in m])) s.add(block) if s.check() == unknown: return None elif s.check() == unsat and i==0: return False else: return models def is_tautology(claim,verbose=0): """ >>> is_tautology(Implies(Bool('x'),Bool('x'))) True >>> is_tautology(Implies(Bool('x'),Bool('y'))) False >>> is_tautology(BoolVal(True)) True >>> is_tautology(BoolVal(False)) False """ return prove(claim=claim,assume=None,verbose=verbose)[0] def is_contradiction(claim,verbose=0): """ >>> x,y=Bools('x y') >>> is_contradiction(BoolVal(False)) True >>> is_contradiction(BoolVal(True)) False >>> is_contradiction(x) False >>> is_contradiction(Implies(x,y)) False >>> is_contradiction(Implies(x,x)) False >>> is_contradiction(And(x,Not(x))) True """ return prove(claim=Not(claim),assume=None,verbose=verbose)[0] def exact_one_model(f): """ return True if f has exactly 1 model, False otherwise. EXAMPLES: >>> x, y = Ints('x y') >>> exact_one_model(And(0<=x**y,x <= 0)) False >>> exact_one_model(And(0<=x,x <= 0)) True >>> exact_one_model(And(0<=x,x <= 1)) False >>> exact_one_model(And(0<=x,x <= -1)) False """ models = get_models(f,k=2) if isinstance(models,list): return len(models)==1 else: return False def myBinOp(op,*L): """ >>> myAnd(*[Bool('x'),Bool('y')]) And(x, y) >>> myAnd(*[Bool('x'),None]) x >>> myAnd(*[Bool('x')]) x >>> myAnd(*[]) >>> myAnd(Bool('x'),Bool('y')) And(x, y) >>> myAnd(*[Bool('x'),Bool('y')]) And(x, y) >>> myAnd([Bool('x'),Bool('y')]) And(x, y) >>> myAnd((Bool('x'),Bool('y'))) And(x, y) >>> myAnd(*[Bool('x'),Bool('y'),True]) Traceback (most recent call last): ... AssertionError """ if __debug__: assert op == Z3_OP_OR or op == Z3_OP_AND or op == Z3_OP_IMPLIES if len(L)==1 and (isinstance(L[0],list) or isinstance(L[0],tuple)): L = L[0] if __debug__: assert all(not isinstance(l,bool) for l in L) L = [l for l in L if is_expr(l)] if L: if len(L)==1: return L[0] else: if op == Z3_OP_OR: return Or(L) elif op == Z3_OP_AND: return And(L) else: #IMPLIES return Implies(L[0],L[1]) else: return None def myAnd(*L): return myBinOp(Z3_OP_AND,*L) def myOr(*L): return myBinOp(Z3_OP_OR,*L) def myImplies(a,b):return myBinOp(Z3_OP_IMPLIES,[a,b]) Iff = lambda f: And(Implies(f[0],f[1]),Implies(f[1],f[0])) def model_str(m,as_str=True): """ Returned a 'sorted' model (so that it's easier to see) The model is sorted by its key, e.g. if the model is y = 3 , x = 10, then the result is x = 10, y = 3 EXAMPLES: see doctest exampels from function prove() """ if __debug__: assert m is None or m == [] or isinstance(m,ModelRef) if m : vs = [(v,m[v]) for v in m] vs = sorted(vs,key=lambda a,_: str(a)) if as_str: return '\n'.join(['{} = {}'.format(k,v) for (k,v) in vs]) else: return vs else: return str(m) if as_str else m z3-z3-4.4.1/src/api/z3.h000066400000000000000000000006251260446376700144750ustar00rootroot00000000000000/*++ 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" #endif z3-z3-4.4.1/src/api/z3_algebraic.h000066400000000000000000000152701260446376700164700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: z3_algebraic.h Abstract: Additional APIs for handling Z3 algebraic numbers encoded as Z3_ASTs Author: Leonardo de Moura (leonardo) 2012-12-07 Notes: --*/ #ifndef Z3_ALGEBRAIC_H_ #define Z3_ALGEBRAIC_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Algebraic Numbers API */ /*@{*/ /** \brief Return Z3_TRUE if \c can be used as value in the Z3 real algebraic number package. def_API('Z3_algebraic_is_value', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_algebraic_is_value(Z3_context c, Z3_ast a); /** \brief Return the Z3_TRUE if \c a is positive, and Z3_FALSE otherwise. \pre Z3_algebraic_is_value(c, a) def_API('Z3_algebraic_is_pos', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_algebraic_is_pos(Z3_context c, Z3_ast a); /** \brief Return the Z3_TRUE if \c a is negative, and Z3_FALSE otherwise. \pre Z3_algebraic_is_value(c, a) def_API('Z3_algebraic_is_neg', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_algebraic_is_neg(Z3_context c, Z3_ast a); /** \brief Return the Z3_TRUE if \c a is zero, and Z3_FALSE otherwise. \pre Z3_algebraic_is_value(c, a) def_API('Z3_algebraic_is_zero', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_algebraic_is_zero(Z3_context c, Z3_ast a); /** \brief Return 1 if \c a is positive, 0 if \c a is zero, and -1 if \c a is negative. \pre Z3_algebraic_is_value(c, a) def_API('Z3_algebraic_sign', INT, (_in(CONTEXT), _in(AST))) */ int Z3_API Z3_algebraic_sign(Z3_context c, Z3_ast a); /** \brief Return the value a + b. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_add', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_algebraic_add(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the value a - b. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_sub', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_algebraic_sub(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the value a * b. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_mul', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_algebraic_mul(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the value a / b. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) \pre !Z3_algebraic_is_zero(c, b) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_div', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_algebraic_div(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the a^(1/k) \pre Z3_algebraic_is_value(c, a) \pre k is even => !Z3_algebraic_is_neg(c, a) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_root', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_ast Z3_API Z3_algebraic_root(Z3_context c, Z3_ast a, unsigned k); /** \brief Return the a^k \pre Z3_algebraic_is_value(c, a) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_power', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_ast Z3_API Z3_algebraic_power(Z3_context c, Z3_ast a, unsigned k); /** \brief Return Z3_TRUE if a < b, and Z3_FALSE otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_lt', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_bool Z3_API Z3_algebraic_lt(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return Z3_TRUE if a > b, and Z3_FALSE otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_gt', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_bool Z3_API Z3_algebraic_gt(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return Z3_TRUE if a <= b, and Z3_FALSE otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_le', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_bool Z3_API Z3_algebraic_le(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return Z3_TRUE if a >= b, and Z3_FALSE otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_ge', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_bool Z3_API Z3_algebraic_ge(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return Z3_TRUE if a == b, and Z3_FALSE otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_eq', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_bool Z3_API Z3_algebraic_eq(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return Z3_TRUE if a != b, and Z3_FALSE otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_neq', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_bool Z3_API Z3_algebraic_neq(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Given a multivariate polynomial p(x_0, ..., x_{n-1}, x_n), returns the roots of the univariate polynomial p(a[0], ..., a[n-1], x_n). \pre p is a Z3 expression that contains only arithmetic terms and free variables. \pre forall i in [0, n) Z3_algebraic_is_value(c, a[i]) \post forall r in result Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_roots', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ Z3_ast_vector Z3_API Z3_algebraic_roots(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]); /** \brief Given a multivariate polynomial p(x_0, ..., x_{n-1}), return the sign of p(a[0], ..., a[n-1]). \pre p is a Z3 expression that contains only arithmetic terms and free variables. \pre forall i in [0, n) Z3_algebraic_is_value(c, a[i]) def_API('Z3_algebraic_eval', INT, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ int Z3_API Z3_algebraic_eval(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]); /*@}*/ /*@}*/ #ifdef __cplusplus }; #endif // __cplusplus #endif z3-z3-4.4.1/src/api/z3_api.h000066400000000000000000010552451260446376700153370ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifndef Z3_API_H_ #define Z3_API_H_ #ifdef CAMLIDL #ifdef MLAPIV3 #define ML3only #define CorML3 #else #define ML4only #define CorML4 #endif #else #define Conly #define CorML3 #define CorML4 #endif #ifdef CorML3 DEFINE_TYPE(Z3_symbol); DEFINE_TYPE(Z3_literals); DEFINE_TYPE(Z3_theory); DEFINE_TYPE(Z3_config); DEFINE_TYPE(Z3_context); DEFINE_TYPE(Z3_sort); #define Z3_sort_opt Z3_sort DEFINE_TYPE(Z3_func_decl); DEFINE_TYPE(Z3_ast); #define Z3_ast_opt Z3_ast DEFINE_TYPE(Z3_app); DEFINE_TYPE(Z3_pattern); DEFINE_TYPE(Z3_model); DEFINE_TYPE(Z3_constructor); DEFINE_TYPE(Z3_constructor_list); #endif #ifdef Conly DEFINE_TYPE(Z3_params); DEFINE_TYPE(Z3_param_descrs); DEFINE_TYPE(Z3_goal); DEFINE_TYPE(Z3_tactic); DEFINE_TYPE(Z3_probe); DEFINE_TYPE(Z3_stats); DEFINE_TYPE(Z3_solver); DEFINE_TYPE(Z3_ast_vector); DEFINE_TYPE(Z3_ast_map); DEFINE_TYPE(Z3_apply_result); DEFINE_TYPE(Z3_func_interp); #define Z3_func_interp_opt Z3_func_interp DEFINE_TYPE(Z3_func_entry); DEFINE_TYPE(Z3_fixedpoint); DEFINE_TYPE(Z3_optimize); DEFINE_TYPE(Z3_rcf_num); DEFINE_VOID(Z3_theory_data); #endif #ifndef __int64 #define __int64 long long #endif #ifndef __uint64 #define __uint64 unsigned long long #endif /** \defgroup capi C API */ /*@{*/ /** @name Types \conly Most of the types in the C API are opaque pointers. \mlonly Most of the types in the API are abstract. \endmlonly \conly - \c Z3_config: configuration object used to initialize logical contexts. - \c Z3_context: manager of all other Z3 objects, global configuration options, etc. - \c Z3_symbol: Lisp-like symbol used to name types, constants, and functions. A symbol can be created using string or integers. - \c Z3_ast: abstract syntax tree node. That is, the data-structure used in Z3 to represent terms, formulas and types. - \c Z3_sort: kind of AST used to represent types. - \c Z3_func_decl: kind of AST used to represent function symbols. - \c Z3_app: kind of AST used to represent function applications. - \c Z3_pattern: kind of AST used to represent pattern and multi-patterns used to guide quantifier instantiation. \conly - \c Z3_constructor: type constructor for a (recursive) datatype. - \c Z3_params: parameter set used to configure many components such as: simplifiers, tactics, solvers, etc. - \c Z3_model: model for the constraints asserted into the logical context. - \c Z3_func_interp: interpretation of a function in a model. - \c Z3_func_entry: representation of the value of a \c Z3_func_interp at a particular point. - \c Z3_fixedpoint: context for the recursive predicate solver. - \c Z3_optimize: context for solving optimization queries. - \c Z3_ast_vector: vector of \c Z3_ast objects. - \c Z3_ast_map: mapping from \c Z3_ast to \c Z3_ast objects. - \c Z3_goal: set of formulas that can be solved and/or transformed using tactics and solvers. - \c Z3_tactic: basic building block for creating custom solvers for specific problem domains. - \c Z3_probe: function/predicate used to inspect a goal and collect information that may be used to decide which solver and/or preprocessing step will be used. - \c Z3_apply_result: collection of subgoals resulting from applying of a tactic to a goal. - \c Z3_solver: (incremental) solver, possibly specialized by a particular tactic or logic. - \c Z3_stats: statistical data for a solver. */ #ifdef Conly /** \brief Z3 Boolean type. It is just an alias for \c int. */ typedef int Z3_bool; #else #define Z3_bool boolean #endif #ifdef Conly /** \brief Z3 string type. It is just an alias for const char *. */ typedef const char * Z3_string; typedef Z3_string * Z3_string_ptr; #else typedef [string] const char * Z3_string; #define Z3_string_ptr Z3_string * #endif #ifdef Conly /** \brief True value. It is just an alias for \c 1. */ #define Z3_TRUE 1 /** \brief False value. It is just an alias for \c 0. */ #define Z3_FALSE 0 #endif /** \mlonly {!lbool} \endmlonly \conly \brief Lifted Boolean type: \c false, \c undefined, \c true. */ typedef enum { Z3_L_FALSE = -1, Z3_L_UNDEF, Z3_L_TRUE } Z3_lbool; /** \mlonly {!symbol_kind} \endmlonly \conly \brief The different kinds of symbol. In Z3, a symbol can be represented using integers and strings (See #Z3_get_symbol_kind). \sa Z3_mk_int_symbol \sa Z3_mk_string_symbol */ typedef enum { Z3_INT_SYMBOL, Z3_STRING_SYMBOL } Z3_symbol_kind; /** \mlonly {!parameter_kind} \endmlonly \conly \brief The different kinds of parameters that can be associated with function symbols. \sa Z3_get_decl_num_parameters \sa Z3_get_decl_parameter_kind - Z3_PARAMETER_INT is used for integer parameters. - Z3_PARAMETER_DOUBLE is used for double parameters. - Z3_PARAMETER_RATIONAL is used for parameters that are rational numbers. - Z3_PARAMETER_SYMBOL is used for parameters that are symbols. - Z3_PARAMETER_SORT is used for sort parameters. - Z3_PARAMETER_AST is used for expression parameters. - Z3_PARAMETER_FUNC_DECL is used for function declaration parameters. */ typedef enum { Z3_PARAMETER_INT, Z3_PARAMETER_DOUBLE, Z3_PARAMETER_RATIONAL, Z3_PARAMETER_SYMBOL, Z3_PARAMETER_SORT, Z3_PARAMETER_AST, Z3_PARAMETER_FUNC_DECL, } Z3_parameter_kind; /** \mlonly {!sort_kind} \endmlonly \conly \brief The different kinds of Z3 types (See #Z3_get_sort_kind). */ typedef enum { 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_FLOATING_POINT_SORT, Z3_ROUNDING_MODE_SORT, Z3_UNKNOWN_SORT = 1000 } Z3_sort_kind; /** \mlonly {!ast_kind} \endmlonly \conly \brief The different kinds of Z3 AST (abstract syntax trees). That is, terms, formulas and types. - Z3_APP_AST: constant and applications - Z3_NUMERAL_AST: numeral constants - Z3_VAR_AST: bound variables - Z3_QUANTIFIER_AST: quantifiers - Z3_SORT_AST: sort - Z3_FUNC_DECL_AST: function declaration - Z3_UNKNOWN_AST: internal */ typedef enum { Z3_NUMERAL_AST, Z3_APP_AST, Z3_VAR_AST, Z3_QUANTIFIER_AST, Z3_SORT_AST, Z3_FUNC_DECL_AST, Z3_UNKNOWN_AST = 1000 } Z3_ast_kind; /** \mlonly {!decl_kind} \endmlonly \conly \brief The different kinds of interpreted function kinds. - Z3_OP_TRUE The constant true. - Z3_OP_FALSE The constant false. - Z3_OP_EQ The equality predicate. - Z3_OP_DISTINCT The n-ary distinct predicate (every argument is mutually distinct). - Z3_OP_ITE The ternary if-then-else term. - Z3_OP_AND n-ary conjunction. - Z3_OP_OR n-ary disjunction. - Z3_OP_IFF equivalence (binary). - Z3_OP_XOR Exclusive or. - Z3_OP_NOT Negation. - Z3_OP_IMPLIES Implication. - Z3_OP_OEQ Binary equivalence modulo namings. This binary predicate is used in proof terms. It captures equisatisfiability and equivalence modulo renamings. - Z3_OP_INTERP Marks a sub-formula for interpolation. - Z3_OP_ANUM Arithmetic numeral. - Z3_OP_AGNUM Arithmetic algebraic numeral. Algebraic numbers are used to represent irrational numbers in Z3. - Z3_OP_LE <=. - Z3_OP_GE >=. - Z3_OP_LT <. - Z3_OP_GT >. - Z3_OP_ADD Addition - Binary. - Z3_OP_SUB Binary subtraction. - Z3_OP_UMINUS Unary minus. - Z3_OP_MUL Multiplication - Binary. - Z3_OP_DIV Division - Binary. - Z3_OP_IDIV Integer division - Binary. - Z3_OP_REM Remainder - Binary. - Z3_OP_MOD Modulus - Binary. - Z3_OP_TO_REAL Coercion of integer to real - Unary. - Z3_OP_TO_INT Coercion of real to integer - Unary. - Z3_OP_IS_INT Check if real is also an integer - Unary. - Z3_OP_POWER Power operator x^y. - Z3_OP_STORE 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. - Z3_OP_SELECT Array select. - Z3_OP_CONST_ARRAY The constant array. For example, select(const(v),i) = v holds for every v and i. The function is unary. - Z3_OP_ARRAY_DEFAULT Default value of arrays. For example default(const(v)) = v. The function is unary. - Z3_OP_ARRAY_MAP Array map operator. It satisfies map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. - Z3_OP_SET_UNION Set union between two Booelan arrays (two arrays whose range type is Boolean). The function is binary. - Z3_OP_SET_INTERSECT Set intersection between two Boolean arrays. The function is binary. - Z3_OP_SET_DIFFERENCE Set difference between two Boolean arrays. The function is binary. - Z3_OP_SET_COMPLEMENT Set complement of a Boolean array. The function is unary. - Z3_OP_SET_SUBSET Subset predicate between two Boolean arrays. The relation is binary. - Z3_OP_AS_ARRAY An array value that behaves as the function graph of the function passed as parameter. - Z3_OP_BNUM Bit-vector numeral. - Z3_OP_BIT1 One bit bit-vector. - Z3_OP_BIT0 Zero bit bit-vector. - Z3_OP_BNEG Unary minus. - Z3_OP_BADD Binary addition. - Z3_OP_BSUB Binary subtraction. - Z3_OP_BMUL Binary multiplication. - Z3_OP_BSDIV Binary signed division. - Z3_OP_BUDIV Binary unsigned division. - Z3_OP_BSREM Binary signed remainder. - Z3_OP_BUREM Binary unsigned remainder. - Z3_OP_BSMOD Binary signed modulus. - Z3_OP_BSDIV0 Unary function. bsdiv(x,0) is congruent to bsdiv0(x). - Z3_OP_BUDIV0 Unary function. budiv(x,0) is congruent to budiv0(x). - Z3_OP_BSREM0 Unary function. bsrem(x,0) is congruent to bsrem0(x). - Z3_OP_BUREM0 Unary function. burem(x,0) is congruent to burem0(x). - Z3_OP_BSMOD0 Unary function. bsmod(x,0) is congruent to bsmod0(x). - Z3_OP_ULEQ Unsigned bit-vector <= - Binary relation. - Z3_OP_SLEQ Signed bit-vector <= - Binary relation. - Z3_OP_UGEQ Unsigned bit-vector >= - Binary relation. - Z3_OP_SGEQ Signed bit-vector >= - Binary relation. - Z3_OP_ULT Unsigned bit-vector < - Binary relation. - Z3_OP_SLT Signed bit-vector < - Binary relation. - Z3_OP_UGT Unsigned bit-vector > - Binary relation. - Z3_OP_SGT Signed bit-vector > - Binary relation. - Z3_OP_BAND Bit-wise and - Binary. - Z3_OP_BOR Bit-wise or - Binary. - Z3_OP_BNOT Bit-wise not - Unary. - Z3_OP_BXOR Bit-wise xor - Binary. - Z3_OP_BNAND Bit-wise nand - Binary. - Z3_OP_BNOR Bit-wise nor - Binary. - Z3_OP_BXNOR Bit-wise xnor - Binary. - Z3_OP_CONCAT Bit-vector concatenation - Binary. - Z3_OP_SIGN_EXT Bit-vector sign extension. - Z3_OP_ZERO_EXT Bit-vector zero extension. - Z3_OP_EXTRACT Bit-vector extraction. - Z3_OP_REPEAT Repeat bit-vector n times. - Z3_OP_BREDOR Bit-vector reduce or - Unary. - Z3_OP_BREDAND Bit-vector reduce and - Unary. - Z3_OP_BCOMP . - Z3_OP_BSHL Shift left. - Z3_OP_BLSHR Logical shift right. - Z3_OP_BASHR Arithmetical shift right. - Z3_OP_ROTATE_LEFT Left rotation. - Z3_OP_ROTATE_RIGHT Right rotation. - Z3_OP_EXT_ROTATE_LEFT (extended) Left rotation. Similar to Z3_OP_ROTATE_LEFT, but it is a binary operator instead of a parametric one. - Z3_OP_EXT_ROTATE_RIGHT (extended) Right rotation. Similar to Z3_OP_ROTATE_RIGHT, but it is a binary operator instead of a parametric one. - Z3_OP_INT2BV Coerce integer to bit-vector. NB. This function is not supported by the decision procedures. Only the most rudimentary simplification rules are applied to this function. - Z3_OP_BV2INT Coerce bit-vector to integer. NB. This function is not supported by the decision procedures. Only the most rudimentary simplification rules are applied to this function. - Z3_OP_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))) - Z3_OP_XOR3 Compute ternary XOR. The meaning is given by the equivalence (xor3 l1 l2 l3) <=> (xor (xor l1 l2) l3) - Z3_OP_PR_UNDEF: Undef/Null proof object. - Z3_OP_PR_TRUE: Proof for the expression 'true'. - Z3_OP_PR_ASSERTED: Proof for a fact asserted by the user. - Z3_OP_PR_GOAL: Proof for a fact (tagged as goal) asserted by the user. - Z3_OP_PR_MODUS_PONENS: Given a proof for p and a proof for (implies p q), produces a proof for q. \nicebox{ T1: p T2: (implies p q) [mp T1 T2]: q } The second antecedents may also be a proof for (iff p q). - Z3_OP_PR_REFLEXIVITY: 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'. - Z3_OP_PR_SYMMETRY: Given an symmetric relation R and a proof for (R t s), produces a proof for (R s t). \nicebox{ T1: (R t s) [symmetry T1]: (R s t) } T1 is the antecedent of this proof object. - Z3_OP_PR_TRANSITIVITY: Given a transitive relation R, and proofs for (R t s) and (R s u), produces a proof for (R t u). \nicebox{ T1: (R t s) T2: (R s u) [trans T1 T2]: (R t u) } - Z3_OP_PR_TRANSITIVITY_STAR: Condensed transitivity proof. This proof object is only used if the parameter PROOF_MODE is 1. It combines several symmetry and transitivity proofs. Example: \nicebox{ 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. - Z3_OP_PR_MONOTONICITY: Monotonicity proof object. \nicebox{ 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. - Z3_OP_PR_QUANT_INTRO: 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)) - Z3_OP_PR_DISTRIBUTIVITY: 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. - Z3_OP_PR_AND_ELIM: Given a proof for (and l_1 ... l_n), produces a proof for l_i \nicebox{ T1: (and l_1 ... l_n) [and-elim T1]: l_i } - Z3_OP_PR_NOT_OR_ELIM: Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). \nicebox{ T1: (not (or l_1 ... l_n)) [not-or-elim T1]: (not l_i) } - Z3_OP_PR_REWRITE: 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: \nicebox{ (= (+ x 0) x) (= (+ x 1 2) (+ 3 x)) (iff (or x false) x) } - Z3_OP_PR_REWRITE_STAR: 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) - Z3_OP_PR_PULL_QUANT: A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) r))). This proof object has no antecedents. - Z3_OP_PR_PULL_QUANT_STAR: 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. - Z3_OP_PR_PUSH_QUANT: A proof for: \nicebox{ (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. - Z3_OP_PR_ELIM_UNUSED_VARS: 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. - Z3_OP_PR_DER: 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. - Z3_OP_PR_QUANT_INST: A proof of (or (not (forall (x) (P x))) (P a)) - Z3_OP_PR_HYPOTHESIS: Mark a hypothesis in a natural deduction style proof. - Z3_OP_PR_LEMMA: \nicebox{ 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 open hypotheses: l_1, ..., l_n. The hypotheses are closed after an application of a lemma. Furthermore, there are no other open hypotheses in the subtree covered by the lemma. - Z3_OP_PR_UNIT_RESOLUTION: \nicebox{ 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') } - Z3_OP_PR_IFF_TRUE: \nicebox{ T1: p [iff-true T1]: (iff p true) } - Z3_OP_PR_IFF_FALSE: \nicebox{ T1: (not p) [iff-false T1]: (iff p false) } - Z3_OP_PR_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. - Z3_OP_PR_DEF_AXIOM: Proof object used to justify Tseitin's like axioms: \nicebox{ (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). - Z3_OP_PR_DEF_INTRO: 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) - Z3_OP_PR_APPLY_DEF: [apply-def T1]: F ~ n F is 'equivalent' to n, given that T1 is a proof that n is a name for F. - Z3_OP_PR_IFF_OEQ: T1: (iff p q) [iff~ T1]: (~ p q) - Z3_OP_PR_NNF_POS: Proof for a (positive) NNF step. Example: \nicebox{ 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 \nicebox{ 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'. - Z3_OP_PR_NNF_NEG: Proof for a (negative) NNF step. Examples: \nicebox{ 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'))) } - Z3_OP_PR_NNF_STAR: 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. - Z3_OP_PR_CNF_STAR: 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. - Z3_OP_PR_SKOLEMIZE: Proof for: \nicebox{ [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. - Z3_OP_PR_MODUS_PONENS_OEQ: Modus ponens style rule for equi-satisfiability. \nicebox{ T1: p T2: (~ p q) [mp~ T1 T2]: q } - Z3_OP_PR_TH_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: \nicebox{ (iff (= t1 t2) (and (<= t1 t2) (<= t2 t1))) } - gcd-test - Indicates an integer linear arithmetic lemma that uses a gcd test. - Z3_OP_PR_HYPER_RESOLVE: Hyper-resolution rule. The premises of the rules is a sequence of clauses. The first clause argument is the main clause of the rule. with a literal from the first (main) clause. Premises of the rules are of the form \nicebox{ (or l0 l1 l2 .. ln) } or \nicebox{ (=> (and l1 l2 .. ln) l0) } or in the most general (ground) form: \nicebox{ (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln)) } 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 For general implications where the head is a disjunction, the first n positions correspond to the n disjuncts in the head. The next m positions correspond to the m conjuncts in the body. The premises can be universally quantified so that the most general non-ground form is: \nicebox{ (forall (vars) (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln))) } The hyper-resolution rule takes a sequence of parameters. The parameters are substitutions of bound variables separated by pairs of literal positions from the main clause and side clause. - Z3_OP_RA_STORE: Insert a record into a relation. The function takes \c n+1 arguments, where the first argument is the relation and the remaining \c n elements correspond to the \c n columns of the relation. - Z3_OP_RA_EMPTY: Creates the empty relation. - Z3_OP_RA_IS_EMPTY: Tests if the relation is empty. - Z3_OP_RA_JOIN: Create the relational join. - Z3_OP_RA_UNION: Create the union or convex hull of two relations. The function takes two arguments. - Z3_OP_RA_WIDEN: Widen two relations. The function takes two arguments. - Z3_OP_RA_PROJECT: Project the columns (provided as numbers in the parameters). The function takes one argument. - Z3_OP_RA_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. - Z3_OP_RA_NEGATION_FILTER: 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. - Z3_OP_RA_RENAME: rename columns in the relation. The function takes one argument. The parameters contain the renaming as a cycle. - Z3_OP_RA_COMPLEMENT: Complement the relation. - Z3_OP_RA_SELECT: Check if a record is an element of the relation. The function takes \c n+1 arguments, where the first argument is a relation, and the remaining \c n arguments correspond to a record. - Z3_OP_RA_CLONE: Create a fresh copy (clone) of a relation. The function is logically the identity, but in the context of a register machine allows for \mlonly [OP_RA_UNION] \endmlonly \conly #Z3_OP_RA_UNION to perform destructive updates to the first argument. - Z3_OP_FD_LT: A less than predicate over the finite domain Z3_FINITE_DOMAIN_SORT. - Z3_OP_LABEL: 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. - Z3_OP_LABEL_LIT: A label literal (used by the Boogie Verification condition generator). A label literal has a set of string parameters. It takes no arguments. - Z3_OP_DT_CONSTRUCTOR: datatype constructor. - Z3_OP_DT_RECOGNISER: datatype recognizer. - Z3_OP_DT_ACCESSOR: datatype accessor. - Z3_OP_DT_UPDATE_FIELD: datatype field update. - Z3_OP_PB_AT_MOST: Cardinality constraint. E.g., x + y + z <= 2 - Z3_OP_PB_LE: Generalized Pseudo-Boolean cardinality constraint. Example 2*x + 3*y <= 4 - Z3_OP_PB_GE: Generalized Pseudo-Boolean cardinality constraint. Example 2*x + 3*y + 2*z >= 4 - Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN: Floating-point rounding mode RNE - Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY: Floating-point rounding mode RNA - Z3_OP_FPA_RM_TOWARD_POSITIVE: Floating-point rounding mode RTP - Z3_OP_FPA_RM_TOWARD_NEGATIVE: Floating-point rounding mode RTN - Z3_OP_FPA_RM_TOWARD_ZERO: Floating-point rounding mode RTZ - Z3_OP_FPA_NUM: Floating-point value - Z3_OP_FPA_PLUS_INF: Floating-point +oo - Z3_OP_FPA_MINUS_INF: Floating-point -oo - Z3_OP_FPA_NAN: Floating-point NaN - Z3_OP_FPA_PLUS_ZERO: Floating-point +zero - Z3_OP_FPA_MINUS_ZERO: Floating-point -zero - Z3_OP_FPA_ADD: Floating-point addition - Z3_OP_FPA_SUB: Floating-point subtraction - Z3_OP_FPA_NEG: Floating-point negation - Z3_OP_FPA_MUL: Floating-point multiplication - Z3_OP_FPA_DIV: Floating-point division - Z3_OP_FPA_REM: Floating-point remainder - Z3_OP_FPA_ABS: Floating-point absolute value - Z3_OP_FPA_MIN: Floating-point minimum - Z3_OP_FPA_MAX: Floating-point maximum - Z3_OP_FPA_FMA: Floating-point fused multiply-add - Z3_OP_FPA_SQRT: Floating-point square root - Z3_OP_FPA_ROUND_TO_INTEGRAL: Floating-point round to integral - Z3_OP_FPA_EQ: Floating-point equality - Z3_OP_FPA_LT: Floating-point less than - Z3_OP_FPA_GT: Floating-point greater than - Z3_OP_FPA_LE: Floating-point less than or equal - Z3_OP_FPA_GE: Floating-point greater than or equal - Z3_OP_FPA_IS_NAN: Floating-point isNaN - Z3_OP_FPA_IS_INF: Floating-point isInfinite - Z3_OP_FPA_IS_ZERO: Floating-point isZero - Z3_OP_FPA_IS_NORMAL: Floating-point isNormal - Z3_OP_FPA_IS_SUBNORMAL: Floating-point isSubnormal - Z3_OP_FPA_IS_NEGATIVE: Floating-point isNegative - Z3_OP_FPA_IS_POSITIVE: Floating-point isPositive - Z3_OP_FPA_FP: Floating-point constructor from 3 bit-vectors - Z3_OP_FPA_TO_FP: Floating-point conversion (various) - Z3_OP_FPA_TO_FP_UNSIGNED: Floating-point conversion from unsigend bit-vector - Z3_OP_FPA_TO_UBV: Floating-point conversion to unsigned bit-vector - Z3_OP_FPA_TO_SBV: Floating-point conversion to signed bit-vector - Z3_OP_FPA_TO_REAL: Floating-point conversion to real number - Z3_OP_FPA_TO_IEEE_BV: Floating-point conversion to IEEE-754 bit-vector - Z3_OP_UNINTERPRETED: kind used for uninterpreted symbols. */ typedef enum { // Basic Z3_OP_TRUE = 0x100, 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_INTERP, // Arithmetic Z3_OP_ANUM = 0x200, 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, // Arrays & Sets Z3_OP_STORE = 0x300, 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, // Bit-vectors Z3_OP_BNUM = 0x400, 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, // special functions to record the division by 0 cases // these are internal functions 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, // Proofs Z3_OP_PR_UNDEF = 0x500, 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, // Sequences Z3_OP_RA_STORE = 0x600, 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, // Auxiliary Z3_OP_LABEL = 0x700, Z3_OP_LABEL_LIT, // Datatypes Z3_OP_DT_CONSTRUCTOR=0x800, Z3_OP_DT_RECOGNISER, Z3_OP_DT_ACCESSOR, Z3_OP_DT_UPDATE_FIELD, // Pseudo Booleans Z3_OP_PB_AT_MOST=0x900, Z3_OP_PB_LE, Z3_OP_PB_GE, // Floating-Point Arithmetic Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN, Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY, Z3_OP_FPA_RM_TOWARD_POSITIVE, Z3_OP_FPA_RM_TOWARD_NEGATIVE, Z3_OP_FPA_RM_TOWARD_ZERO, Z3_OP_FPA_NUM, Z3_OP_FPA_PLUS_INF, Z3_OP_FPA_MINUS_INF, Z3_OP_FPA_NAN, Z3_OP_FPA_PLUS_ZERO, Z3_OP_FPA_MINUS_ZERO, Z3_OP_FPA_ADD, Z3_OP_FPA_SUB, Z3_OP_FPA_NEG, Z3_OP_FPA_MUL, Z3_OP_FPA_DIV, Z3_OP_FPA_REM, Z3_OP_FPA_ABS, Z3_OP_FPA_MIN, Z3_OP_FPA_MAX, Z3_OP_FPA_FMA, Z3_OP_FPA_SQRT, Z3_OP_FPA_ROUND_TO_INTEGRAL, Z3_OP_FPA_EQ, Z3_OP_FPA_LT, Z3_OP_FPA_GT, Z3_OP_FPA_LE, Z3_OP_FPA_GE, Z3_OP_FPA_IS_NAN, Z3_OP_FPA_IS_INF, Z3_OP_FPA_IS_ZERO, Z3_OP_FPA_IS_NORMAL, Z3_OP_FPA_IS_SUBNORMAL, Z3_OP_FPA_IS_NEGATIVE, Z3_OP_FPA_IS_POSITIVE, Z3_OP_FPA_FP, Z3_OP_FPA_TO_FP, Z3_OP_FPA_TO_FP_UNSIGNED, Z3_OP_FPA_TO_UBV, Z3_OP_FPA_TO_SBV, Z3_OP_FPA_TO_REAL, Z3_OP_FPA_TO_IEEE_BV, Z3_OP_UNINTERPRETED } Z3_decl_kind; /** \mlonly {!param_kind} \endmlonly \conly \brief The different kinds of parameters that can be associated with parameter sets. (see #Z3_mk_params). - Z3_PK_UINT integer parameters. - Z3_PK_BOOL boolean parameters. - Z3_PK_DOUBLE double parameters. - Z3_PK_SYMBOL symbol parameters. - Z3_PK_STRING string parameters. - Z3_PK_OTHER all internal parameter kinds which are not exposed in the API. - Z3_PK_INVALID invalid parameter. */ typedef enum { Z3_PK_UINT, Z3_PK_BOOL, Z3_PK_DOUBLE, Z3_PK_SYMBOL, Z3_PK_STRING, Z3_PK_OTHER, Z3_PK_INVALID } Z3_param_kind; #ifdef CorML3 /** \mlonly {!search_failure} \endmlonly \conly \brief The different kinds of search failure types. - Z3_NO_FAILURE: The last search was successful - Z3_UNKNOWN: Undocumented failure reason - Z3_TIMEOUT: Timeout - Z3_MEMOUT_WATERMAK: Search hit a memory high-watermak limit - Z3_CANCELED: External cancel flag was set - Z3_NUM_CONFLICTS: Maximum number of conflicts was reached - Z3_THEORY: Theory is incomplete - Z3_QUANTIFIERS: Logical context contains universal quantifiers */ typedef enum { Z3_NO_FAILURE, Z3_UNKNOWN, Z3_TIMEOUT, Z3_MEMOUT_WATERMARK, Z3_CANCELED, Z3_NUM_CONFLICTS, Z3_THEORY, Z3_QUANTIFIERS } Z3_search_failure; #endif /** \mlonly {!ast_print_mode} \endmlonly \conly \brief Z3 pretty printing modes (See #Z3_set_ast_print_mode). - Z3_PRINT_SMTLIB_FULL: Print AST nodes in SMTLIB verbose format. - Z3_PRINT_LOW_LEVEL: Print AST nodes using a low-level format. - Z3_PRINT_SMTLIB_COMPLIANT: Print AST nodes in SMTLIB 1.x compliant format. - Z3_PRINT_SMTLIB2_COMPLIANT: Print AST nodes in SMTLIB 2.x compliant format. */ typedef enum { Z3_PRINT_SMTLIB_FULL, Z3_PRINT_LOW_LEVEL, Z3_PRINT_SMTLIB_COMPLIANT, Z3_PRINT_SMTLIB2_COMPLIANT } Z3_ast_print_mode; #ifdef CorML4 /** \mlonly {!error_code} \endmlonly \conly \brief Z3 error codes \conly (See #Z3_get_error_code). - Z3_OK: No error. - Z3_SORT_ERROR: User tried to build an invalid (type incorrect) AST. - Z3_IOB: Index out of bounds. - Z3_INVALID_ARG: Invalid argument was provided. - Z3_PARSER_ERROR: An error occurred when parsing a string or file. - Z3_NO_PARSER: Parser output is not available, that is, user didn't invoke #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. - Z3_INVALID_PATTERN: Invalid pattern was used to build a quantifier. - Z3_MEMOUT_FAIL: A memory allocation failure was encountered. - Z3_FILE_ACCESS_ERRROR: A file could not be accessed. - Z3_INVALID_USAGE: API call is invalid in the current state. - Z3_INTERNAL_FATAL: An error internal to Z3 occurred. - Z3_DEC_REF_ERROR: Trying to decrement the reference counter of an AST that was deleted or the reference counter was not initialized \mlonly.\endmlonly \conly with #Z3_inc_ref. - Z3_EXCEPTION: Internal Z3 exception. Additional details can be retrieved using #Z3_get_error_msg. */ typedef enum { 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 } Z3_error_code; #endif /** Definitions for update_api.py def_Type('CONFIG', 'Z3_config', 'Config') def_Type('CONTEXT', 'Z3_context', 'ContextObj') def_Type('AST', 'Z3_ast', 'Ast') def_Type('APP', 'Z3_app', 'Ast') def_Type('SORT', 'Z3_sort', 'Sort') def_Type('FUNC_DECL', 'Z3_func_decl', 'FuncDecl') def_Type('PATTERN', 'Z3_pattern', 'Pattern') def_Type('MODEL', 'Z3_model', 'Model') def_Type('LITERALS', 'Z3_literals', 'Literals') def_Type('CONSTRUCTOR', 'Z3_constructor', 'Constructor') def_Type('CONSTRUCTOR_LIST', 'Z3_constructor_list', 'ConstructorList') def_Type('THEORY', 'Z3_theory', 'ctypes.c_void_p') def_Type('THEORY_DATA', 'Z3_theory_data', 'ctypes.c_void_p') def_Type('SOLVER', 'Z3_solver', 'SolverObj') def_Type('GOAL', 'Z3_goal', 'GoalObj') def_Type('TACTIC', 'Z3_tactic', 'TacticObj') def_Type('PARAMS', 'Z3_params', 'Params') def_Type('PROBE', 'Z3_probe', 'ProbeObj') def_Type('STATS', 'Z3_stats', 'StatsObj') def_Type('AST_VECTOR', 'Z3_ast_vector', 'AstVectorObj') def_Type('AST_MAP', 'Z3_ast_map', 'AstMapObj') def_Type('APPLY_RESULT', 'Z3_apply_result', 'ApplyResultObj') def_Type('FUNC_INTERP', 'Z3_func_interp', 'FuncInterpObj') def_Type('FUNC_ENTRY', 'Z3_func_entry', 'FuncEntryObj') def_Type('FIXEDPOINT', 'Z3_fixedpoint', 'FixedpointObj') def_Type('OPTIMIZE', 'Z3_optimize', 'OptimizeObj') def_Type('PARAM_DESCRS', 'Z3_param_descrs', 'ParamDescrs') def_Type('RCF_NUM', 'Z3_rcf_num', 'RCFNumObj') */ #ifdef Conly /** \brief Z3 custom error handler (See #Z3_set_error_handler). */ typedef void Z3_error_handler(Z3_context c, Z3_error_code e); #endif #ifdef ML4only #include #endif #ifdef CorML4 /** \mlonly {!goal_prec} \endmlonly \conly \brief A Goal is essentially a set of formulas. Z3 provide APIs for building strategies/tactics for solving and transforming Goals. Some of these transformations apply under/over approximations. - Z3_GOAL_PRECISE: Approximations/Relaxations were not applied on the goal (sat and unsat answers were preserved). - Z3_GOAL_UNDER: Goal is the product of a under-approximation (sat answers are preserved). - Z3_GOAL_OVER: Goal is the product of an over-approximation (unsat answers are preserved). - Z3_GOAL_UNDER_OVER: Goal is garbage (it is the product of over- and under-approximations, sat and unsat answers are not preserved). */ typedef enum { Z3_GOAL_PRECISE, Z3_GOAL_UNDER, Z3_GOAL_OVER, Z3_GOAL_UNDER_OVER } Z3_goal_prec; #endif /*@}*/ #ifndef CAMLIDL #ifdef __cplusplus extern "C" { #endif // __cplusplus #else [pointer_default(ref)] interface Z3 { #endif // CAMLIDL #ifdef CorML3 /** @name Configuration */ /*@{*/ /** \brief Set a global (or module) parameter. This setting 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: Z3_global_param_set('pp.decimal', 'true') will set the parameter "decimal" in the module "pp" to true. def_API('Z3_global_param_set', VOID, (_in(STRING), _in(STRING))) */ void Z3_API Z3_global_param_set(Z3_string param_id, Z3_string param_value); /** \brief Restore the value of all global (and module) parameters. This command will not affect already created objects (such as tactics and solvers). \sa Z3_global_param_set def_API('Z3_global_param_reset_all', VOID, ()) */ void Z3_API Z3_global_param_reset_all(void); /** \brief Get a global (or module) parameter. Returns \mlonly \c None \endmlonly \conly \c Z3_FALSE if the parameter value does not exist. \sa Z3_global_param_set \remark This function cannot be invoked simultaneously from different threads without synchronization. The result string stored in param_value is stored in shared location. def_API('Z3_global_param_get', BOOL, (_in(STRING), _out(STRING))) */ Z3_bool_opt Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value); /*@}*/ /** @name Create configuration */ /*@{*/ /** \brief Create a configuration object for the Z3 context object. Configurations are created in order to assign parameters prior to creating contexts for Z3 interaction. For example, if the users wishes to use proof generation, then call: \ccode{Z3_set_param_value(cfg\, "proof"\, "true")} \mlonly \remark Consider using {!mk_context_x} instead of using explicit configuration objects. The function {!mk_context_x} receives an array of string pairs. This array represents the configuration options. \endmlonly \remark In previous versions of Z3, the \c Z3_config was used to store global and module configurations. Now, we should use \c Z3_global_param_set. 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 \sa Z3_set_param_value \sa Z3_del_config def_API('Z3_mk_config', CONFIG, ()) */ Z3_config Z3_API Z3_mk_config(void); /** \brief Delete the given configuration object. \sa Z3_mk_config def_API('Z3_del_config', VOID, (_in(CONFIG),)) */ void Z3_API Z3_del_config(Z3_config c); /** \brief Set a configuration parameter. The following parameters can be set for \sa Z3_mk_config def_API('Z3_set_param_value', VOID, (_in(CONFIG), _in(STRING), _in(STRING))) */ void Z3_API Z3_set_param_value(Z3_config c, Z3_string param_id, Z3_string param_value); /*@}*/ #endif /** @name Create context */ /*@{*/ /** \brief Create a context using the given configuration. After a context is created, the configuration cannot be changed, although some parameters can be changed using #Z3_update_param_value. All main interaction with Z3 happens in the context of a \c Z3_context. In contrast to #Z3_mk_context_rc, the life time of Z3_ast objects are determined by the scope level of #Z3_push and #Z3_pop. In other words, a Z3_ast object remains valid until there is a call to Z3_pop that takes the current scope below the level where the object was created. Note that all other reference counted objects, including Z3_model, Z3_solver, Z3_func_interp have to be managed by the caller. Their reference counts are not handled by the context. \conly \sa Z3_del_context \conly \deprecated Use #Z3_mk_context_rc def_API('Z3_mk_context', CONTEXT, (_in(CONFIG),)) */ #ifdef CorML3 Z3_context Z3_API Z3_mk_context(Z3_config c); #endif #ifdef ML4only #include #endif #ifdef Conly /** \brief Create a context using the given configuration. This function is similar to #Z3_mk_context. However, in the context returned by this function, the user is responsible for managing Z3_ast reference counters. Managing reference counters is a burden and error-prone, but allows the user to use the memory more efficiently. The user must invoke #Z3_inc_ref for any Z3_ast returned by Z3, and #Z3_dec_ref whenever the Z3_ast is not needed anymore. This idiom is similar to the one used in BDD (binary decision diagrams) packages such as CUDD. Remark: Z3_sort, Z3_func_decl, Z3_app, Z3_pattern are Z3_ast's. After a context is created, the configuration cannot be changed. All main interaction with Z3 happens in the context of a \c Z3_context. def_API('Z3_mk_context_rc', CONTEXT, (_in(CONFIG),)) */ Z3_context Z3_API Z3_mk_context_rc(Z3_config c); #endif #ifdef CorML3 /** \brief Delete the given logical context. \sa Z3_mk_context def_API('Z3_del_context', VOID, (_in(CONTEXT),)) */ void Z3_API Z3_del_context(Z3_context c); #endif #ifdef Conly /** \brief Increment the reference counter of the given AST. The context \c c should have been created using #Z3_mk_context_rc. This function is a NOOP if \c c was created using #Z3_mk_context. def_API('Z3_inc_ref', VOID, (_in(CONTEXT), _in(AST))) */ void Z3_API Z3_inc_ref(Z3_context c, Z3_ast a); /** \brief Decrement the reference counter of the given AST. The context \c c should have been created using #Z3_mk_context_rc. This function is a NOOP if \c c was created using #Z3_mk_context. def_API('Z3_dec_ref', VOID, (_in(CONTEXT), _in(AST))) */ void Z3_API Z3_dec_ref(Z3_context c, Z3_ast a); #endif /** \brief Set a value of a context parameter. \sa Z3_global_param_set def_API('Z3_update_param_value', VOID, (_in(CONTEXT), _in(STRING), _in(STRING))) */ void Z3_API Z3_update_param_value(Z3_context c, Z3_string param_id, Z3_string param_value); #ifdef CorML4 /** \brief Interrupt the execution of a Z3 procedure. This procedure can be used to interrupt: solvers, simplifiers and tactics. def_API('Z3_interrupt', VOID, (_in(CONTEXT),)) */ void Z3_API Z3_interrupt(Z3_context c); #endif /*@}*/ #ifdef CorML4 /** @name Parameters */ /*@{*/ /** \brief Create a Z3 (empty) parameter set. Starting at Z3 4.0, parameter sets are used to configure many components such as: simplifiers, tactics, solvers, etc. \conly \remark Reference counting must be used to manage parameter sets, even when the Z3_context was \conly created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_params', PARAMS, (_in(CONTEXT),)) */ Z3_params Z3_API Z3_mk_params(Z3_context c); #ifdef Conly /** \brief Increment the reference counter of the given parameter set. def_API('Z3_params_inc_ref', VOID, (_in(CONTEXT), _in(PARAMS))) */ void Z3_API Z3_params_inc_ref(Z3_context c, Z3_params p); /** \brief Decrement the reference counter of the given parameter set. def_API('Z3_params_dec_ref', VOID, (_in(CONTEXT), _in(PARAMS))) */ void Z3_API Z3_params_dec_ref(Z3_context c, Z3_params p); #endif /** \brief Add a Boolean parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_bool', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(BOOL))) */ void Z3_API Z3_params_set_bool(Z3_context c, Z3_params p, Z3_symbol k, Z3_bool v); /** \brief Add a unsigned parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_uint', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(UINT))) */ void Z3_API Z3_params_set_uint(Z3_context c, Z3_params p, Z3_symbol k, unsigned v); /** \brief Add a double parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_double', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(DOUBLE))) */ void Z3_API Z3_params_set_double(Z3_context c, Z3_params p, Z3_symbol k, double v); /** \brief Add a symbol parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_symbol', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(SYMBOL))) */ void Z3_API Z3_params_set_symbol(Z3_context c, Z3_params p, Z3_symbol k, Z3_symbol v); /** \brief Convert a parameter set into a string. This function is mainly used for printing the contents of a parameter set. def_API('Z3_params_to_string', STRING, (_in(CONTEXT), _in(PARAMS))) */ Z3_string Z3_API Z3_params_to_string(Z3_context c, Z3_params p); /** \brief Validate the parameter set \c p against the parameter description set \c d. The procedure invokes the error handler if \c p is invalid. def_API('Z3_params_validate', VOID, (_in(CONTEXT), _in(PARAMS), _in(PARAM_DESCRS))) */ void Z3_API Z3_params_validate(Z3_context c, Z3_params p, Z3_param_descrs d); #endif /*@}*/ #ifdef CorML4 /** @name Parameter Descriptions */ /*@{*/ #ifdef Conly /** \brief Increment the reference counter of the given parameter description set. def_API('Z3_param_descrs_inc_ref', VOID, (_in(CONTEXT), _in(PARAM_DESCRS))) */ void Z3_API Z3_param_descrs_inc_ref(Z3_context c, Z3_param_descrs p); /** \brief Decrement the reference counter of the given parameter description set. def_API('Z3_param_descrs_dec_ref', VOID, (_in(CONTEXT), _in(PARAM_DESCRS))) */ void Z3_API Z3_param_descrs_dec_ref(Z3_context c, Z3_param_descrs p); #endif /** \brief Return the kind associated with the given parameter name \c n. def_API('Z3_param_descrs_get_kind', UINT, (_in(CONTEXT), _in(PARAM_DESCRS), _in(SYMBOL))) */ Z3_param_kind Z3_API Z3_param_descrs_get_kind(Z3_context c, Z3_param_descrs p, Z3_symbol n); /** \brief Return the number of parameters in the given parameter description set. def_API('Z3_param_descrs_size', UINT, (_in(CONTEXT), _in(PARAM_DESCRS))) */ unsigned Z3_API Z3_param_descrs_size(Z3_context c, Z3_param_descrs p); /** \brief Return the number of parameters in the given parameter description set. \pre i < Z3_param_descrs_size(c, p) def_API('Z3_param_descrs_get_name', SYMBOL, (_in(CONTEXT), _in(PARAM_DESCRS), _in(UINT))) */ Z3_symbol Z3_API Z3_param_descrs_get_name(Z3_context c, Z3_param_descrs p, unsigned i); /** \brief Convert a parameter description set into a string. This function is mainly used for printing the contents of a parameter description set. def_API('Z3_param_descrs_to_string', STRING, (_in(CONTEXT), _in(PARAM_DESCRS))) */ Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p); /*@}*/ #endif /** @name Symbols */ /*@{*/ #ifdef ML4only #include #endif /** \mlonly {4 {L Redundant low-level API}} \endmlonly */ /** \brief Create a Z3 symbol using an integer. Symbols are used to name several term and type constructors. NB. Not all integers can be passed to this function. The legal range of unsigned integers is 0 to 2^30-1. \sa Z3_mk_string_symbol def_API('Z3_mk_int_symbol', SYMBOL, (_in(CONTEXT), _in(INT))) */ Z3_symbol Z3_API Z3_mk_int_symbol(Z3_context c, int i); /** \brief Create a Z3 symbol using a C string. Symbols are used to name several term and type constructors. \sa Z3_mk_int_symbol def_API('Z3_mk_string_symbol', SYMBOL, (_in(CONTEXT), _in(STRING))) */ Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, Z3_string s); /*@}*/ /** @name Sorts */ /*@{*/ #ifdef ML4only #include #endif /** \mlonly {4 {L Redundant low-level API}} \endmlonly */ /** \brief Create a free (uninterpreted) type using the given name (symbol). Two free types are considered the same iff the have the same name. def_API('Z3_mk_uninterpreted_sort', SORT, (_in(CONTEXT), _in(SYMBOL))) */ Z3_sort Z3_API Z3_mk_uninterpreted_sort(Z3_context c, Z3_symbol s); /** \brief Create the Boolean type. This type is used to create propositional variables and predicates. def_API('Z3_mk_bool_sort', SORT, (_in(CONTEXT), )) */ Z3_sort Z3_API Z3_mk_bool_sort(Z3_context c); /** \brief Create the integer type. This type is not the int type found in programming languages. A machine integer can be represented using bit-vectors. The function #Z3_mk_bv_sort creates a bit-vector type. \sa Z3_mk_bv_sort def_API('Z3_mk_int_sort', SORT, (_in(CONTEXT), )) */ Z3_sort Z3_API Z3_mk_int_sort(Z3_context c); /** \brief Create the real type. Note that this type is not a floating point number. def_API('Z3_mk_real_sort', SORT, (_in(CONTEXT), )) */ Z3_sort Z3_API Z3_mk_real_sort(Z3_context c); /** \brief Create a bit-vector type of the given size. This type can also be seen as a machine integer. \remark The size of the bitvector type must be greater than zero. def_API('Z3_mk_bv_sort', SORT, (_in(CONTEXT), _in(UINT))) */ Z3_sort Z3_API Z3_mk_bv_sort(Z3_context c, unsigned sz); /** \brief Create a named finite domain sort. To create constants that belong to the finite domain, use the APIs for creating numerals and pass a numeric constant together with the sort returned by this call. The numeric constant should be between 0 and the less than the size of the domain. \sa Z3_get_finite_domain_sort_size def_API('Z3_mk_finite_domain_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT64))) */ Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, unsigned __int64 size); /** \brief Create an array type. We usually represent the array type as: [domain -> range]. Arrays are usually used to model the heap/memory in software verification. \sa Z3_mk_select \sa Z3_mk_store def_API('Z3_mk_array_sort', SORT, (_in(CONTEXT), _in(SORT), _in(SORT))) */ Z3_sort Z3_API Z3_mk_array_sort(Z3_context c, Z3_sort domain, Z3_sort range); /** \brief Create a tuple type. \mlonly [mk_tuple_sort c name field_names field_sorts] creates a tuple with a constructor named [name], a [n] fields, where [n] is the size of the arrays [field_names] and [field_sorts]. \endmlonly \conly A tuple with \c n fields has a constructor and \c n projections. \conly This function will also declare the constructor and projection functions. \param c logical context \param mk_tuple_name name of the constructor function associated with the tuple type. \param num_fields number of fields in the tuple type. \param field_names name of the projection functions. \param field_sorts type of the tuple fields. \param mk_tuple_decl output parameter that will contain the constructor declaration. \param proj_decl output parameter that will contain the projection function declarations. This field must be a buffer of size \c num_fields allocated by the user. def_API('Z3_mk_tuple_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _out(FUNC_DECL), _out_array(2, FUNC_DECL))) */ Z3_sort Z3_API Z3_mk_tuple_sort(Z3_context c, Z3_symbol mk_tuple_name, unsigned num_fields, Z3_symbol const field_names[], Z3_sort const field_sorts[], Z3_func_decl * mk_tuple_decl, Z3_func_decl proj_decl[]); /** \brief Create a enumeration sort. \mlonly [mk_enumeration_sort c enums] creates an enumeration sort with enumeration names [enums], it also returns [n] predicates, where [n] is the number of [enums] corresponding to testing whether an element is one of the enumerants. \endmlonly \conly An enumeration sort with \c n elements. \conly This function will also declare the functions corresponding to the enumerations. \param c logical context \param name name of the enumeration sort. \param n number of elemenets in enumeration sort. \param enum_names names of the enumerated elements. \param enum_consts constants corresponding to the enumerated elements. \param enum_testers predicates testing if terms of the enumeration sort correspond to an enumeration. For example, if this function is called with three symbols A, B, C and the name S, then \c s is a sort whose name is S, and the function returns three terms corresponding to A, B, C in \c enum_consts. The array \c enum_testers has three predicates of type (s -> Bool). The first predicate (corresponding to A) is true when applied to A, and false otherwise. Similarly for the other predicates. def_API('Z3_mk_enumeration_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SYMBOL), _out_array(2, FUNC_DECL), _out_array(2, FUNC_DECL))) */ 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[]); /** \brief Create a list sort \mlonly [mk_list_sort c name elem_sort] creates a list sort of [name], over elements of sort [elem_sort]. \endmlonly \conly A list sort over \c elem_sort \conly This function declares the corresponding constructors and testers for lists. \param c logical context \param name name of the list sort. \param elem_sort sort of list elements. \param nil_decl declaration for the empty list. \param is_nil_decl test for the empty list. \param cons_decl declaration for a cons cell. \param is_cons_decl cons cell test. \param head_decl list head. \param tail_decl list tail. def_API('Z3_mk_list_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(SORT), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL))) */ 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 ); BEGIN_MLAPI_EXCLUDE /** \brief Create a constructor. \param c logical context. \param name constructor name. \param recognizer name of recognizer function. \param num_fields number of fields in constructor. \param field_names names of the constructor fields. \param sorts field sorts, \mlonly [None] \endmlonly \conly 0 if the field sort refers to a recursive sort. \param sort_refs reference to datatype sort that is an argument to the constructor; if the corresponding sort reference is \mlonly [None], \endmlonly \conly 0, then the value in sort_refs should be an index referring to one of the recursive datatypes that is declared. def_API('Z3_mk_constructor', CONSTRUCTOR, (_in(CONTEXT), _in(SYMBOL), _in(SYMBOL), _in(UINT), _in_array(3, SYMBOL), _in_array(3, SORT), _in_array(3, UINT))) */ Z3_constructor Z3_API Z3_mk_constructor(Z3_context c, Z3_symbol name, Z3_symbol recognizer, unsigned num_fields, Z3_symbol const field_names[], Z3_sort_opt const sorts[], unsigned sort_refs[] ); /** \brief Reclaim memory allocated to constructor. \param c logical context. \param constr constructor. def_API('Z3_del_constructor', VOID, (_in(CONTEXT), _in(CONSTRUCTOR))) */ void Z3_API Z3_del_constructor(Z3_context c, Z3_constructor constr); /** \brief Create datatype, such as lists, trees, records, enumerations or unions of records. The datatype may be recursive. Return the datatype sort. \param c logical context. \param name name of datatype. \param num_constructors number of constructors passed in. \param constructors array of constructor containers. def_API('Z3_mk_datatype', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _inout_array(2, CONSTRUCTOR))) */ Z3_sort Z3_API Z3_mk_datatype(Z3_context c, Z3_symbol name, unsigned num_constructors, Z3_constructor constructors[]); /** \brief Create list of constructors. \param c logical context. \param num_constructors number of constructors in list. \param constructors list of constructors. def_API('Z3_mk_constructor_list', CONSTRUCTOR_LIST, (_in(CONTEXT), _in(UINT), _in_array(1, CONSTRUCTOR))) */ Z3_constructor_list Z3_API Z3_mk_constructor_list(Z3_context c, unsigned num_constructors, Z3_constructor const constructors[]); /** \brief Reclaim memory allocated for constructor list. Each constructor inside the constructor list must be independently reclaimed using #Z3_del_constructor. \param c logical context. \param clist constructor list container. def_API('Z3_del_constructor_list', VOID, (_in(CONTEXT), _in(CONSTRUCTOR_LIST))) */ void Z3_API Z3_del_constructor_list(Z3_context c, Z3_constructor_list clist); /** \brief Create mutually recursive datatypes. \param c logical context. \param num_sorts number of datatype sorts. \param sort_names names of datatype sorts. \param sorts array of datatype sorts. \param constructor_lists list of constructors, one list per sort. def_API('Z3_mk_datatypes', VOID, (_in(CONTEXT), _in(UINT), _in_array(1, SYMBOL), _out_array(1, SORT), _inout_array(1, CONSTRUCTOR_LIST))) */ 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[]); /** \brief Query constructor for declared functions. \param c logical context. \param constr constructor container. The container must have been passed in to a #Z3_mk_datatype call. \param num_fields number of accessor fields in the constructor. \param constructor constructor function declaration. \param tester constructor test function declaration. \param accessors array of accessor function declarations. def_API('Z3_query_constructor', VOID, (_in(CONTEXT), _in(CONSTRUCTOR), _in(UINT), _out(FUNC_DECL), _out(FUNC_DECL), _out_array(2, FUNC_DECL))) */ void Z3_API Z3_query_constructor(Z3_context c, Z3_constructor constr, unsigned num_fields, Z3_func_decl* constructor, Z3_func_decl* tester, Z3_func_decl accessors[]); END_MLAPI_EXCLUDE /*@}*/ /** @name Constants and Applications */ /*@{*/ /** \brief Declare a constant or function. \mlonly [mk_func_decl c n d r] creates a function with name [n], domain [d], and range [r]. The arity of the function is the size of the array [d]. \endmlonly \param c logical context. \param s name of the constant or function. \param domain_size number of arguments. It is 0 when declaring a constant. \param domain array containing the sort of each argument. The array must contain domain_size elements. It is 0 when declaring a constant. \param range sort of the constant or the return sort of the function. After declaring a constant or function, the function #Z3_mk_app can be used to create a constant or function application. \sa Z3_mk_app def_API('Z3_mk_func_decl', FUNC_DECL, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT), _in(SORT))) */ Z3_func_decl Z3_API Z3_mk_func_decl(Z3_context c, Z3_symbol s, unsigned domain_size, Z3_sort const domain[], Z3_sort range); /** \brief Create a constant or function application. \sa Z3_mk_func_decl def_API('Z3_mk_app', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT), _in_array(2, AST))) */ Z3_ast Z3_API Z3_mk_app( Z3_context c, Z3_func_decl d, unsigned num_args, Z3_ast const args[]); /** \brief Declare and create a constant. \conly This function is a shorthand for: \conly \code \conly Z3_func_decl d = Z3_mk_func_decl(c, s, 0, 0, ty); \conly Z3_ast n = Z3_mk_app(c, d, 0, 0); \conly \endcode \mlonly [mk_const c s t] is a shorthand for [mk_app c (mk_func_decl c s [||] t) [||]] \endmlonly \sa Z3_mk_func_decl \sa Z3_mk_app def_API('Z3_mk_const', AST, (_in(CONTEXT), _in(SYMBOL), _in(SORT))) */ Z3_ast Z3_API Z3_mk_const(Z3_context c, Z3_symbol s, Z3_sort ty); /** \brief Declare a fresh constant or function. Z3 will generate an unique name for this function declaration. \conly If prefix is different from \c NULL, then the name generate by Z3 will start with \c prefix. \conly \remark If \c prefix is \c NULL, then it is assumed to be the empty string. \sa Z3_mk_func_decl def_API('Z3_mk_fresh_func_decl', FUNC_DECL, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SORT), _in(SORT))) */ Z3_func_decl Z3_API Z3_mk_fresh_func_decl(Z3_context c, Z3_string prefix, unsigned domain_size, Z3_sort const domain[], Z3_sort range); /** \brief Declare and create a fresh constant. \conly This function is a shorthand for: \conly \code Z3_func_decl d = Z3_mk_fresh_func_decl(c, prefix, 0, 0, ty); Z3_ast n = Z3_mk_app(c, d, 0, 0); \endcode \mlonly [mk_fresh_const c p t] is a shorthand for [mk_app c (mk_fresh_func_decl c p [||] t) [||]]. \endmlonly \conly \remark If \c prefix is \c NULL, then it is assumed to be the empty string. \sa Z3_mk_func_decl \sa Z3_mk_app def_API('Z3_mk_fresh_const', AST, (_in(CONTEXT), _in(STRING), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fresh_const(Z3_context c, Z3_string prefix, Z3_sort ty); /*@}*/ /** @name Propositional Logic and Equality */ /*@{*/ /** \brief Create an AST node representing \c true. def_API('Z3_mk_true', AST, (_in(CONTEXT), )) */ Z3_ast Z3_API Z3_mk_true(Z3_context c); /** \brief Create an AST node representing \c false. def_API('Z3_mk_false', AST, (_in(CONTEXT), )) */ Z3_ast Z3_API Z3_mk_false(Z3_context c); /** \brief \mlh mk_eq c l r \endmlh Create an AST node representing l = r. The nodes \c l and \c r must have the same type. def_API('Z3_mk_eq', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_eq(Z3_context c, Z3_ast l, Z3_ast r); /** \conly \brief Create an AST node representing distinct(args[0], ..., args[num_args-1]). \mlonly \brief \[ [mk_distinct c [| t_1; ...; t_n |]] \] Create an AST node represeting a distinct construct. It is used for declaring the arguments t_i pairwise distinct. \endmlonly The \c distinct construct is used for declaring the arguments pairwise distinct. That is, Forall 0 <= i < j < num_args. not args[i] = args[j]. All arguments must have the same sort. \remark The number of arguments of a distinct construct must be greater than one. def_API('Z3_mk_distinct', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_distinct(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief \mlh mk_not c a \endmlh Create an AST node representing not(a). The node \c a must have Boolean sort. def_API('Z3_mk_not', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_not(Z3_context c, Z3_ast a); /** \brief \mlh mk_ite c t1 t2 t2 \endmlh Create an AST node representing an if-then-else: ite(t1, t2, t3). The node \c t1 must have Boolean sort, \c t2 and \c t3 must have the same sort. The sort of the new node is equal to the sort of \c t2 and \c t3. def_API('Z3_mk_ite', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_ite(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3); /** \brief \mlh mk_iff c t1 t2 \endmlh Create an AST node representing t1 iff t2. The nodes \c t1 and \c t2 must have Boolean sort. def_API('Z3_mk_iff', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_iff(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_implies c t1 t2 \endmlh Create an AST node representing t1 implies t2. The nodes \c t1 and \c t2 must have Boolean sort. def_API('Z3_mk_implies', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_implies(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_xor c t1 t2 \endmlh Create an AST node representing t1 xor t2. The nodes \c t1 and \c t2 must have Boolean sort. def_API('Z3_mk_xor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_xor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \conly \brief Create an AST node representing args[0] and ... and args[num_args-1]. \mlonly \brief \[ [mk_and c [| t_1; ...; t_n |]] \] Create the conjunction: {e t_1 and ... and t_n}. \endmlonly \conly The array \c args must have \c num_args elements. All arguments must have Boolean sort. \remark The number of arguments must be greater than zero. def_API('Z3_mk_and', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_and(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \conly \brief Create an AST node representing args[0] or ... or args[num_args-1]. \mlonly \brief \[ [mk_or c [| t_1; ...; t_n |]] \] Create the disjunction: {e t_1 or ... or t_n}. \endmlonly \conly The array \c args must have \c num_args elements. All arguments must have Boolean sort. \remark The number of arguments must be greater than zero. def_API('Z3_mk_or', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_or(Z3_context c, unsigned num_args, Z3_ast const args[]); /*@}*/ /** @name Arithmetic: Integers and Reals */ /*@{*/ /** \conly \brief Create an AST node representing args[0] + ... + args[num_args-1]. \mlonly \brief \[ [mk_add c [| t_1; ...; t_n |]] \] Create the term: {e t_1 + ... + t_n}. \endmlonly \conly The array \c args must have \c num_args elements. All arguments must have int or real sort. \remark The number of arguments must be greater than zero. def_API('Z3_mk_add', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_add(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \conly \brief Create an AST node representing args[0] * ... * args[num_args-1]. \mlonly \brief \[ [mk_mul c [| t_1; ...; t_n |]] \] Create the term: {e t_1 * ... * t_n}. \endmlonly \conly The array \c args must have \c num_args elements. All arguments must have int or real sort. \remark Z3 has limited support for non-linear arithmetic. \remark The number of arguments must be greater than zero. def_API('Z3_mk_mul', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_mul(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \conly \brief Create an AST node representing args[0] - ... - args[num_args - 1]. \mlonly \brief \[ [mk_sub c [| t_1; ...; t_n |]] \] Create the term: {e t_1 - ... - t_n}. \endmlonly \conly The array \c args must have \c num_args elements. All arguments must have int or real sort. \remark The number of arguments must be greater than zero. def_API('Z3_mk_sub', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_sub(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \conly \brief Create an AST node representing -arg. \mlonly \brief \[ [mk_unary_minus c arg] \] Create the term: {e - arg}. \endmlonly The arguments must have int or real type. def_API('Z3_mk_unary_minus', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_unary_minus(Z3_context c, Z3_ast arg); /** \conly \brief Create an AST node representing arg1 div arg2. \mlonly \brief \[ [mk_div c t_1 t_2] \] Create the term: {e t_1 div t_2}. \endmlonly The arguments must either both have int type or both have real type. If the arguments have int type, then the result type is an int type, otherwise the the result type is real. def_API('Z3_mk_div', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_div(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \conly \brief Create an AST node representing arg1 mod arg2. \mlonly \brief \[ [mk_mod c t_1 t_2] \] Create the term: {e t_1 mod t_2}. \endmlonly The arguments must have int type. def_API('Z3_mk_mod', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_mod(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \conly \brief Create an AST node representing arg1 rem arg2. \mlonly \brief \[ [mk_rem c t_1 t_2] \] Create the term: {e t_1 rem t_2}. \endmlonly The arguments must have int type. def_API('Z3_mk_rem', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_rem(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \conly \brief Create an AST node representing arg1^arg2. The arguments must have int or real type. def_API('Z3_mk_power', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_power(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \brief \mlh mk_lt c t1 t2 \endmlh Create less than. The nodes \c t1 and \c t2 must have the same sort, and must be int or real. def_API('Z3_mk_lt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_lt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_le c t1 t2 \endmlh Create less than or equal to. The nodes \c t1 and \c t2 must have the same sort, and must be int or real. def_API('Z3_mk_le', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_le(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_gt c t1 t2 \endmlh Create greater than. The nodes \c t1 and \c t2 must have the same sort, and must be int or real. def_API('Z3_mk_gt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_gt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_ge c t1 t2 \endmlh Create greater than or equal to. The nodes \c t1 and \c t2 must have the same sort, and must be int or real. def_API('Z3_mk_ge', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_ge(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_int2real c t1 \endmlh 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 constant \c k and and asserting mk_int2real(k) <= t1 < mk_int2real(k)+1. The node \c t1 must have sort integer. \sa Z3_mk_real2int \sa Z3_mk_is_int def_API('Z3_mk_int2real', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_int2real(Z3_context c, Z3_ast t1); /** \brief \mlh mk_real2int c t1 \endmlh Coerce a real to an integer. The semantics of this function follows the SMT-LIB standard for the function to_int \sa Z3_mk_int2real \sa Z3_mk_is_int def_API('Z3_mk_real2int', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_real2int(Z3_context c, Z3_ast t1); /** \brief \mlh mk_is_int c t1 \endmlh Check if a real number is an integer. \sa Z3_mk_int2real \sa Z3_mk_real2int def_API('Z3_mk_is_int', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_is_int(Z3_context c, Z3_ast t1); /*@}*/ /** @name Bit-vectors */ /*@{*/ /** \brief \mlh mk_bvnot c t1 \endmlh Bitwise negation. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_bvnot', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvnot(Z3_context c, Z3_ast t1); /** \brief \mlh mk_bvredand c t1 \endmlh Take conjunction of bits in vector, return vector of length 1. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_bvredand', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvredand(Z3_context c, Z3_ast t1); /** \brief \mlh mk_bvredor c t1 \endmlh Take disjunction of bits in vector, return vector of length 1. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_bvredor', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvredor(Z3_context c, Z3_ast t1); /** \brief \mlh mk_bvand c t1 t2 \endmlh Bitwise and. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvand', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvand(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvor c t1 t2 \endmlh Bitwise or. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvxor c t1 t2 \endmlh Bitwise exclusive-or. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvxor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvxor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvnand c t1 t2 \endmlh Bitwise nand. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvnand', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvnand(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvnor c t1 t2 \endmlh Bitwise nor. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvnor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvnor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvxnor c t1 t2 \endmlh Bitwise xnor. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvxnor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvxnor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvneg c t1 \endmlh Standard two's complement unary minus. The node \c t1 must have bit-vector sort. def_API('Z3_mk_bvneg', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvneg(Z3_context c, Z3_ast t1); /** \brief \mlh mk_bvadd c t1 t2 \endmlh Standard two's complement addition. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvadd', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvadd(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsub c t1 t2 \endmlh Standard two's complement subtraction. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsub', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsub(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvmul c t1 t2 \endmlh Standard two's complement multiplication. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvmul', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvmul(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvudiv c t1 t2 \endmlh Unsigned division. It is defined as the \c floor of t1/t2 if \c t2 is different from zero. If t2 is zero, then the result is undefined. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvudiv', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvudiv(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsdiv c t1 t2 \endmlh Two's complement 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 nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsdiv', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsdiv(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvurem c t1 t2 \endmlh 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 nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvurem', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvurem(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsrem c t1 t2 \endmlh Two's complement signed remainder (sign follows dividend). 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 nodes \c t1 and \c t2 must have the same bit-vector sort. \sa Z3_mk_bvsmod def_API('Z3_mk_bvsrem', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsrem(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsmod c t1 t2 \endmlh Two's complement signed remainder (sign follows divisor). If t2 is zero, then the result is undefined. The nodes \c t1 and \c t2 must have the same bit-vector sort. \sa Z3_mk_bvsrem def_API('Z3_mk_bvsmod', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsmod(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvult c t1 t2 \endmlh Unsigned less than. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvult', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvult(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvslt c t1 t2 \endmlh Two's complement signed less than. It abbreviates: \code (or (and (= (extract[|m-1|:|m-1|] t1) bit1) (= (extract[|m-1|:|m-1|] t2) bit0)) (and (= (extract[|m-1|:|m-1|] t1) (extract[|m-1|:|m-1|] t2)) (bvult t1 t2))) \endcode The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvslt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvslt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvule c t1 t2 \endmlh Unsigned less than or equal to. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvule', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvule(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsle c t1 t2 \endmlh Two's complement signed less than or equal to. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsle', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsle(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvuge c t1 t2 \endmlh Unsigned greater than or equal to. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvuge', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvuge(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsge c t1 t2 \endmlh Two's complement signed greater than or equal to. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsge', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsge(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvugt c t1 t2 \endmlh Unsigned greater than. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvugt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvugt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsgt c t1 t2 \endmlh Two's complement signed greater than. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsgt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsgt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_concat c t1 t2 \endmlh Concatenate the given bit-vectors. The nodes \c t1 and \c t2 must have (possibly different) bit-vector sorts The result is a bit-vector of size n1+n2, where \c n1 (\c n2) is the size of \c t1 (\c t2). def_API('Z3_mk_concat', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_concat(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_extract c high low t1 \endmlh Extract the bits \c high down to \c low from a bitvector of size \c m to yield a new bitvector of size \c n, where n = high - low + 1. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_extract', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_extract(Z3_context c, unsigned high, unsigned low, Z3_ast t1); /** \brief \mlh mk_sign_ext c i t1 \endmlh Sign-extend of 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 node \c t1 must have a bit-vector sort. def_API('Z3_mk_sign_ext', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_sign_ext(Z3_context c, unsigned i, Z3_ast t1); /** \brief \mlh mk_zero_ext c i t1 \endmlh 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 node \c t1 must have a bit-vector sort. def_API('Z3_mk_zero_ext', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_zero_ext(Z3_context c, unsigned i, Z3_ast t1); /** \brief \mlh mk_repeat c i t1 \endmlh Repeat the given bit-vector up length i. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_repeat', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_repeat(Z3_context c, unsigned i, Z3_ast t1); /** \brief \mlh mk_bvshl c t1 t2 \endmlh Shift left. It is equivalent to multiplication 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 nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvshl', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvshl(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvlshr c t1 t2 \endmlh 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 nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvlshr', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvlshr(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvashr c t1 t2 \endmlh 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. 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 nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvashr', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvashr(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_rotate_left c i t1 \endmlh Rotate bits of \c t1 to the left \c i times. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_rotate_left', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_rotate_left(Z3_context c, unsigned i, Z3_ast t1); /** \brief \mlh mk_rotate_right c i t1 \endmlh Rotate bits of \c t1 to the right \c i times. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_rotate_right', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_rotate_right(Z3_context c, unsigned i, Z3_ast t1); /** \brief \mlh mk_ext_rotate_left c t1 t2 \endmlh Rotate bits of \c t1 to the left \c t2 times. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_ext_rotate_left', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_ext_rotate_left(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_ext_rotate_right c t1 t2 \endmlh Rotate bits of \c t1 to the right \c t2 times. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_ext_rotate_right', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_ext_rotate_right(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_int2bv c n t1 \endmlh Create an \c n bit bit-vector from the integer argument \c t1. 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 node \c t1 must have integer sort. def_API('Z3_mk_int2bv', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_int2bv(Z3_context c, unsigned n, Z3_ast t1); /** \brief \mlh mk_bv2int c t1 is_signed \endmlh Create an integer from the bit-vector argument \c t1. 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 \c t1. If \c is_signed is true, \c t1 is treated as a signed bit-vector. 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 node \c t1 must have a bit-vector sort. def_API('Z3_mk_bv2int', AST, (_in(CONTEXT), _in(AST), _in(BOOL))) */ Z3_ast Z3_API Z3_mk_bv2int(Z3_context c,Z3_ast t1, Z3_bool is_signed); /** \brief \mlh mk_bvadd_no_overflow c t1 t2 is_signed \endmlh Create a predicate that checks that the bit-wise addition of \c t1 and \c t2 does not overflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvadd_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(BOOL))) */ Z3_ast Z3_API Z3_mk_bvadd_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed); /** \brief \mlh mk_bvadd_no_underflow c t1 t2 \endmlh Create a predicate that checks that the bit-wise signed addition of \c t1 and \c t2 does not underflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvadd_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvadd_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsub_no_overflow c t1 t2 \endmlh Create a predicate that checks that the bit-wise signed subtraction of \c t1 and \c t2 does not overflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsub_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsub_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsub_no_underflow c t1 t2 is_signed \endmlh Create a predicate that checks that the bit-wise subtraction of \c t1 and \c t2 does not underflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsub_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(BOOL))) */ Z3_ast Z3_API Z3_mk_bvsub_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed); /** \brief \mlh mk_bvsdiv_no_overflow c t1 t2 \endmlh Create a predicate that checks that the bit-wise signed division of \c t1 and \c t2 does not overflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsdiv_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsdiv_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvneg_no_overflow c t1 \endmlh Check that bit-wise negation does not overflow when \c t1 is interpreted as a signed bit-vector. The node \c t1 must have bit-vector sort. def_API('Z3_mk_bvneg_no_overflow', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvneg_no_overflow(Z3_context c, Z3_ast t1); /** \brief \mlh mk_bvmul_no_overflow c t1 t2 is_signed \endmlh Create a predicate that checks that the bit-wise multiplication of \c t1 and \c t2 does not overflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvmul_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(BOOL))) */ Z3_ast Z3_API Z3_mk_bvmul_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed); /** \brief \mlh mk_bvmul_no_underflow c t1 t2 \endmlh Create a predicate that checks that the bit-wise signed multiplication of \c t1 and \c t2 does not underflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvmul_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2); /*@}*/ /** @name Arrays */ /*@{*/ /** \brief \mlh mk_select c a i \endmlh Array read. The argument \c a is the array and \c i is the index of the array that gets read. The node \c a must have an array sort [domain -> range], and \c i must have the sort \c domain. The sort of the result is \c range. \sa Z3_mk_array_sort \sa Z3_mk_store def_API('Z3_mk_select', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i); /** \brief \mlh mk_store c a i v \endmlh Array update. The node \c a must have an array sort [domain -> range], \c i must have sort \c domain, \c 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 \c a (with respect to \c select) on all indices except for \c i, where it maps to \c v (and the \c select of \c a with respect to \c i may be a different value). \sa Z3_mk_array_sort \sa Z3_mk_select def_API('Z3_mk_store', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v); /** \brief Create the constant array. The resulting term is an array, such that a \c select on an arbitrary index produces the value \c v. \param c logical context. \param domain domain sort for the array. \param v value that the array maps to. def_API('Z3_mk_const_array', AST, (_in(CONTEXT), _in(SORT), _in(AST))) */ Z3_ast Z3_API Z3_mk_const_array(Z3_context c, Z3_sort domain, Z3_ast v); /** \brief \mlh mk_map f n args \endmlh map f on the the argument arrays. The \c n nodes \c args must be of array sorts [domain_i -> range_i]. The function declaration \c f must have type range_1 .. range_n -> range. \c v must have sort range. The sort of the result is [domain_i -> range]. \sa Z3_mk_array_sort \sa Z3_mk_store \sa Z3_mk_select def_API('Z3_mk_map', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT), _in_array(2, AST))) */ Z3_ast Z3_API Z3_mk_map(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast const* args); /** \brief Access the array default value. Produces the default range value, for arrays that can be represented as finite maps with a default range value. \param c logical context. \param array array value whose default range value is accessed. def_API('Z3_mk_array_default', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_array_default(Z3_context c, Z3_ast array); /*@}*/ /** @name Sets */ /*@{*/ /** \brief Create Set type. def_API('Z3_mk_set_sort', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_mk_set_sort(Z3_context c, Z3_sort ty); /** \brief Create the empty set. def_API('Z3_mk_empty_set', AST, (_in(CONTEXT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_empty_set(Z3_context c, Z3_sort domain); /** \brief Create the full set. def_API('Z3_mk_full_set', AST, (_in(CONTEXT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_full_set(Z3_context c, Z3_sort domain); /** \brief Add an element to a set. The first argument must be a set, the second an element. def_API('Z3_mk_set_add', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_add(Z3_context c, Z3_ast set, Z3_ast elem); /** \brief Remove an element to a set. The first argument must be a set, the second an element. def_API('Z3_mk_set_del', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_del(Z3_context c, Z3_ast set, Z3_ast elem); /** \brief Take the union of a list of sets. def_API('Z3_mk_set_union', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_set_union(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Take the intersection of a list of sets. def_API('Z3_mk_set_intersect', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_set_intersect(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Take the set difference between two sets. def_API('Z3_mk_set_difference', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_difference(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \brief Take the complement of a set. def_API('Z3_mk_set_complement', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_complement(Z3_context c, Z3_ast arg); /** \brief Check for set membership. The first argument should be an element type of the set. def_API('Z3_mk_set_member', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_member(Z3_context c, Z3_ast elem, Z3_ast set); /** \brief Check for subsetness of sets. def_API('Z3_mk_set_subset', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_subset(Z3_context c, Z3_ast arg1, Z3_ast arg2); /*@}*/ /** @name Numerals */ /*@{*/ #ifdef ML4only #include #endif /** \mlonly {4 {L Redundant low-level API}} \endmlonly */ /** \brief Create a numeral of a given sort. \param c logical context. \param numeral A string representing the numeral value in decimal notation. If the given sort is a real, then the numeral can be a rational, that is, a string of the form [num]* / [num]*. \param ty The sort of the numeral. In the current implementation, the given sort can be an int, real, finite-domain, or bit-vectors of arbitrary size. \sa Z3_mk_int \conly \sa Z3_mk_unsigned_int def_API('Z3_mk_numeral', AST, (_in(CONTEXT), _in(STRING), _in(SORT))) */ Z3_ast Z3_API Z3_mk_numeral(Z3_context c, Z3_string numeral, Z3_sort ty); /** \brief Create a real from a fraction. \param c logical context. \param num numerator of rational. \param den denomerator of rational. \pre den != 0 \sa Z3_mk_numeral \sa Z3_mk_int \conly \sa Z3_mk_unsigned_int def_API('Z3_mk_real', AST, (_in(CONTEXT), _in(INT), _in(INT))) */ Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den); /** \brief Create a numeral of an int, bit-vector, or finite-domain sort. This function can be use to create numerals that fit in a machine integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral def_API('Z3_mk_int', AST, (_in(CONTEXT), _in(INT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_int(Z3_context c, int v, Z3_sort ty); #ifdef Conly /** \brief Create a numeral of a int, bit-vector, or finite-domain sort. This function can be use to create numerals that fit in a machine unsinged integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral def_API('Z3_mk_unsigned_int', AST, (_in(CONTEXT), _in(UINT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_unsigned_int(Z3_context c, unsigned v, Z3_sort ty); #endif /** \brief Create a numeral of a int, bit-vector, or finite-domain sort. This function can be use to create numerals that fit in a machine __int64 integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral def_API('Z3_mk_int64', AST, (_in(CONTEXT), _in(INT64), _in(SORT))) */ Z3_ast Z3_API Z3_mk_int64(Z3_context c, __int64 v, Z3_sort ty); #ifdef Conly /** \brief Create a numeral of a int, bit-vector, or finite-domain sort. This function can be use to create numerals that fit in a machine unsigned __int64 integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral def_API('Z3_mk_unsigned_int64', AST, (_in(CONTEXT), _in(UINT64), _in(SORT))) */ Z3_ast Z3_API Z3_mk_unsigned_int64(Z3_context c, unsigned __int64 v, Z3_sort ty); #endif /*@}*/ /** @name Quantifiers */ /*@{*/ /** \brief Create a pattern for quantifier instantiation. Z3 uses pattern matching to instantiate quantifiers. If a pattern is not provided for a quantifier, then Z3 will automatically compute a set of patterns for it. However, for optimal performance, the user should provide the patterns. Patterns comprise a list of terms. The list should be non-empty. If the list comprises of more than one term, it is a called a multi-pattern. In general, one can pass in a list of (multi-)patterns in the quantifier constructor. \sa Z3_mk_forall \sa Z3_mk_exists def_API('Z3_mk_pattern', PATTERN, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_pattern Z3_API Z3_mk_pattern( Z3_context c, unsigned num_patterns, Z3_ast const terms[]); /** \brief Create 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. \verbatim 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)) \endverbatim 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. \param c logical context \param index de-Bruijn index \param ty sort of the bound variable \sa Z3_mk_forall \sa Z3_mk_exists def_API('Z3_mk_bound', AST, (_in(CONTEXT), _in(UINT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_bound(Z3_context c, unsigned index, Z3_sort ty); /** \brief Create a forall formula. It takes an expression \c body that contains bound variables of the same sorts as the sorts listed in the array \c sorts. The bound variables are de-Bruijn indices created using #Z3_mk_bound. The array \c decl_names contains the names that the quantified formula uses for the bound variables. Z3 applies the convention that the last element in the \c decl_names and \c sorts array refers to the variable with index 0, the second to last element of \c decl_names and \c sorts refers to the variable with index 1, etc. \mlonly [mk_forall c w p t n b] creates a forall formula, where [w] is the weight, [p] is an array of patterns, [t] is an array with the sorts of the bound variables, [n] is an array with the 'names' of the bound variables, and [b] is the body of the quantifier. Quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. \endmlonly \param c logical context. \param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. \param num_patterns number of patterns. \param patterns array containing the patterns created using #Z3_mk_pattern. \param num_decls number of variables to be bound. \param sorts the sorts of the bound variables. \param decl_names names of the bound variables \param body the body of the quantifier. \sa Z3_mk_pattern \sa Z3_mk_bound \sa Z3_mk_exists def_API('Z3_mk_forall', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, PATTERN), _in(UINT), _in_array(4, SORT), _in_array(4, SYMBOL), _in(AST))) */ 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 sorts[], Z3_symbol const decl_names[], Z3_ast body); /** \brief Create an exists formula. Similar to #Z3_mk_forall. \sa Z3_mk_pattern \sa Z3_mk_bound \sa Z3_mk_forall \sa Z3_mk_quantifier def_API('Z3_mk_exists', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, PATTERN), _in(UINT), _in_array(4, SORT), _in_array(4, SYMBOL), _in(AST))) */ 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 sorts[], Z3_symbol const decl_names[], Z3_ast body); /** \brief Create a quantifier - universal or existential, with pattern hints. See the documentation for #Z3_mk_forall for an explanation of the parameters. \param c logical context. \param is_forall flag to indicate if this is a universal or existential quantifier. \param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. \param num_patterns number of patterns. \param patterns array containing the patterns created using #Z3_mk_pattern. \param num_decls number of variables to be bound. \param sorts array of sorts of the bound variables. \param decl_names names of the bound variables. \param body the body of the quantifier. \sa Z3_mk_pattern \sa Z3_mk_bound \sa Z3_mk_forall \sa Z3_mk_exists def_API('Z3_mk_quantifier', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(UINT), _in_array(3, PATTERN), _in(UINT), _in_array(5, SORT), _in_array(5, SYMBOL), _in(AST))) */ 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); /** \brief Create a quantifier - universal or existential, with pattern hints, no patterns, and attributes \param c logical context. \param is_forall flag to indicate if this is a universal or existential quantifier. \param quantifier_id identifier to identify quantifier \param skolem_id identifier to identify skolem constants introduced by quantifier. \param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. \param num_patterns number of patterns. \param patterns array containing the patterns created using #Z3_mk_pattern. \param num_no_patterns number of no_patterns. \param no_patterns array containing subexpressions to be excluded from inferred patterns. \param num_decls number of variables to be bound. \param sorts array of sorts of the bound variables. \param decl_names names of the bound variables. \param body the body of the quantifier. \sa Z3_mk_pattern \sa Z3_mk_bound \sa Z3_mk_forall \sa Z3_mk_exists def_API('Z3_mk_quantifier_ex', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(SYMBOL), _in(SYMBOL), _in(UINT), _in_array(5, PATTERN), _in(UINT), _in_array(7, AST), _in(UINT), _in_array(9, SORT), _in_array(9, SYMBOL), _in(AST))) */ 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); /** \brief Create a universal quantifier using a list of constants that will form the set of bound variables. \param c logical context. \param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. \param num_bound number of constants to be abstracted into bound variables. \param bound array of constants to be abstracted into bound variables. \param num_patterns number of patterns. \param patterns array containing the patterns created using #Z3_mk_pattern. \param body the body of the quantifier. \sa Z3_mk_pattern \sa Z3_mk_exists_const def_API('Z3_mk_forall_const', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, APP), _in(UINT), _in_array(4, PATTERN), _in(AST))) */ 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 ); /** \brief Similar to #Z3_mk_forall_const. \brief Create an existential quantifier using a list of constants that will form the set of bound variables. \param c logical context. \param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. \param num_bound number of constants to be abstracted into bound variables. \param bound array of constants to be abstracted into bound variables. \param num_patterns number of patterns. \param patterns array containing the patterns created using #Z3_mk_pattern. \param body the body of the quantifier. \sa Z3_mk_pattern \sa Z3_mk_forall_const def_API('Z3_mk_exists_const', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, APP), _in(UINT), _in_array(4, PATTERN), _in(AST))) */ 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 ); /** \brief Create a universal or existential quantifier using a list of constants that will form the set of bound variables. def_API('Z3_mk_quantifier_const', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(UINT), _in_array(3, APP), _in(UINT), _in_array(5, PATTERN), _in(AST))) */ 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 ); /** \brief Create a universal or existential quantifier using a list of constants that will form the set of bound variables. def_API('Z3_mk_quantifier_const_ex', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(SYMBOL), _in(SYMBOL), _in(UINT), _in_array(5, APP), _in(UINT), _in_array(7, PATTERN), _in(UINT), _in_array(9, AST), _in(AST))) */ 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 ); /*@}*/ /** @name Accessors */ /*@{*/ /** \mlonly {3 {L Symbols}} \endmlonly */ #ifdef ML4only #include #endif /** \mlonly {4 {L Redundant low-level API}} \endmlonly */ /** \brief Return \c Z3_INT_SYMBOL if the symbol was constructed using #Z3_mk_int_symbol, and \c Z3_STRING_SYMBOL if the symbol was constructed using #Z3_mk_string_symbol. def_API('Z3_get_symbol_kind', UINT, (_in(CONTEXT), _in(SYMBOL))) */ Z3_symbol_kind Z3_API Z3_get_symbol_kind(Z3_context c, Z3_symbol s); /** \brief \mlh get_symbol_int c s \endmlh Return the symbol int value. \pre Z3_get_symbol_kind(s) == Z3_INT_SYMBOL \sa Z3_mk_int_symbol def_API('Z3_get_symbol_int', INT, (_in(CONTEXT), _in(SYMBOL))) */ int Z3_API Z3_get_symbol_int(Z3_context c, Z3_symbol s); /** \brief \mlh get_symbol_string c s \endmlh Return the symbol name. \pre Z3_get_symbol_string(s) == Z3_STRING_SYMBOL \conly \warning The returned buffer is statically allocated by Z3. It will \conly be automatically deallocated when #Z3_del_context is invoked. \conly So, the buffer is invalidated in the next call to \c Z3_get_symbol_string. \sa Z3_mk_string_symbol def_API('Z3_get_symbol_string', STRING, (_in(CONTEXT), _in(SYMBOL))) */ Z3_string Z3_API Z3_get_symbol_string(Z3_context c, Z3_symbol s); /** \mlonly {3 {L Sorts}} \endmlonly */ #ifdef ML4only #include #endif /** \brief Return the sort name as a symbol. def_API('Z3_get_sort_name', SYMBOL, (_in(CONTEXT), _in(SORT))) */ Z3_symbol Z3_API Z3_get_sort_name(Z3_context c, Z3_sort d); /** \brief Return a unique identifier for \c s. \mlonly \remark Implicitly used by [Pervasives.( = )] and [Pervasives.compare]. \endmlonly def_API('Z3_get_sort_id', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_sort_id(Z3_context c, Z3_sort s); /** \mlonly {4 {L Redundant low-level API}} \endmlonly */ /** \brief Convert a \c Z3_sort into \c Z3_ast. \conly This is just type casting. \mlonly \remark [sort_to_ast c s] can be replaced by [(s :> ast)]. \endmlonly def_API('Z3_sort_to_ast', AST, (_in(CONTEXT), _in(SORT))) */ Z3_ast Z3_API Z3_sort_to_ast(Z3_context c, Z3_sort s); /** \brief compare sorts. \mlonly \remark [Pervasives.( = )] or [Pervasives.compare] can also be used. \endmlonly def_API('Z3_is_eq_sort', BOOL, (_in(CONTEXT), _in(SORT), _in(SORT))) */ Z3_bool Z3_API Z3_is_eq_sort(Z3_context c, Z3_sort s1, Z3_sort s2); /** \brief Return the sort kind (e.g., array, tuple, int, bool, etc). \sa Z3_sort_kind def_API('Z3_get_sort_kind', UINT, (_in(CONTEXT), _in(SORT))) */ Z3_sort_kind Z3_API Z3_get_sort_kind(Z3_context c, Z3_sort t); /** \brief \mlh get_bv_sort_size c t \endmlh Return the size of the given bit-vector sort. \pre Z3_get_sort_kind(c, t) == Z3_BV_SORT \sa Z3_mk_bv_sort \sa Z3_get_sort_kind def_API('Z3_get_bv_sort_size', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_bv_sort_size(Z3_context c, Z3_sort t); /** \conly \brief Store the size of the sort in \c r. Return Z3_FALSE if the call failed. \mlonly \brief Return the size of the sort in \c r. Return \c None if the call failed. \endmlonly That is, Z3_get_sort_kind(s) == Z3_FINITE_DOMAIN_SORT def_API('Z3_get_finite_domain_sort_size', BOOL, (_in(CONTEXT), _in(SORT), _out(UINT64))) */ Z3_bool_opt Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, unsigned __int64* r); /** \brief \mlh get_array_sort_domain c t \endmlh Return the domain of the given array sort. \pre Z3_get_sort_kind(c, t) == Z3_ARRAY_SORT \sa Z3_mk_array_sort \sa Z3_get_sort_kind def_API('Z3_get_array_sort_domain', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_get_array_sort_domain(Z3_context c, Z3_sort t); /** \brief \mlh get_array_sort_range c t \endmlh Return the range of the given array sort. \pre Z3_get_sort_kind(c, t) == Z3_ARRAY_SORT \sa Z3_mk_array_sort \sa Z3_get_sort_kind def_API('Z3_get_array_sort_range', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_get_array_sort_range(Z3_context c, Z3_sort t); /** \brief \mlh get_tuple_sort_mk_decl c t \endmlh Return the constructor declaration of the given tuple sort. \pre Z3_get_sort_kind(c, t) == Z3_DATATYPE_SORT \sa Z3_mk_tuple_sort \sa Z3_get_sort_kind def_API('Z3_get_tuple_sort_mk_decl', FUNC_DECL, (_in(CONTEXT), _in(SORT))) */ Z3_func_decl Z3_API Z3_get_tuple_sort_mk_decl(Z3_context c, Z3_sort t); /** \brief \mlh get_tuple_sort_num_fields c t \endmlh Return the number of fields of the given tuple sort. \pre Z3_get_sort_kind(c, t) == Z3_DATATYPE_SORT \sa Z3_mk_tuple_sort \sa Z3_get_sort_kind def_API('Z3_get_tuple_sort_num_fields', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_tuple_sort_num_fields(Z3_context c, Z3_sort t); /** \brief \mlh get_tuple_sort_field_decl c t i \endmlh Return the i-th field declaration (i.e., projection function declaration) of the given tuple sort. \pre Z3_get_sort_kind(t) == Z3_DATATYPE_SORT \pre i < Z3_get_tuple_sort_num_fields(c, t) \sa Z3_mk_tuple_sort \sa Z3_get_sort_kind def_API('Z3_get_tuple_sort_field_decl', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_tuple_sort_field_decl(Z3_context c, Z3_sort t, unsigned i); /** \brief Return number of constructors for datatype. \pre Z3_get_sort_kind(t) == Z3_DATATYPE_SORT \sa Z3_get_datatype_sort_constructor \sa Z3_get_datatype_sort_recognizer \sa Z3_get_datatype_sort_constructor_accessor def_API('Z3_get_datatype_sort_num_constructors', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_datatype_sort_num_constructors( Z3_context c, Z3_sort t); /** \brief Return idx'th constructor. \pre Z3_get_sort_kind(t) == Z3_DATATYPE_SORT \pre idx < Z3_get_datatype_sort_num_constructors(c, t) \sa Z3_get_datatype_sort_num_constructors \sa Z3_get_datatype_sort_recognizer \sa Z3_get_datatype_sort_constructor_accessor def_API('Z3_get_datatype_sort_constructor', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_datatype_sort_constructor( Z3_context c, Z3_sort t, unsigned idx); /** \brief Return idx'th recognizer. \pre Z3_get_sort_kind(t) == Z3_DATATYPE_SORT \pre idx < Z3_get_datatype_sort_num_constructors(c, t) \sa Z3_get_datatype_sort_num_constructors \sa Z3_get_datatype_sort_constructor \sa Z3_get_datatype_sort_constructor_accessor def_API('Z3_get_datatype_sort_recognizer', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_datatype_sort_recognizer( Z3_context c, Z3_sort t, unsigned idx); /** \brief Return idx_a'th accessor for the idx_c'th constructor. \pre Z3_get_sort_kind(t) == Z3_DATATYPE_SORT \pre idx_c < Z3_get_datatype_sort_num_constructors(c, t) \pre idx_a < Z3_get_domain_size(c, Z3_get_datatype_sort_constructor(c, idx_c)) \sa Z3_get_datatype_sort_num_constructors \sa Z3_get_datatype_sort_constructor \sa Z3_get_datatype_sort_recognizer def_API('Z3_get_datatype_sort_constructor_accessor', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_datatype_sort_constructor_accessor( Z3_context c, Z3_sort t, unsigned idx_c, unsigned idx_a); /** \brief Update record field with a value. This corresponds to the 'with' construct in OCaml. It has the effect of updating a record field with a given value. The remaining fields are left unchanged. It is the record equivalent of an array store (see \sa Z3_mk_store). If the datatype has more than one constructor, then the update function behaves as identity if there is a miss-match between the accessor and constructor. For example ((_ update-field car) nil 1) is nil, while ((_ update-field car) (cons 2 nil) 1) is (cons 1 nil). \pre Z3_get_sort_kind(Z3_get_sort(c, t)) == Z3_get_domain(c, field_access, 1) == Z3_DATATYPE_SORT \pre Z3_get_sort(c, value) == Z3_get_range(c, field_access) def_API('Z3_datatype_update_field', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_datatype_update_field( Z3_context c, Z3_func_decl field_access, Z3_ast t, Z3_ast value); /** \brief Return arity of relation. \pre Z3_get_sort_kind(s) == Z3_RELATION_SORT \sa Z3_get_relation_column def_API('Z3_get_relation_arity', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_relation_arity(Z3_context c, Z3_sort s); /** \brief Return sort at i'th column of relation sort. \pre Z3_get_sort_kind(c, s) == Z3_RELATION_SORT \pre col < Z3_get_relation_arity(c, s) \sa Z3_get_relation_arity def_API('Z3_get_relation_column', SORT, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_sort Z3_API Z3_get_relation_column(Z3_context c, Z3_sort s, unsigned col); /** \brief Pseudo-Boolean relations. Encode p1 + p2 + ... + pn <= k def_API('Z3_mk_atmost', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in(UINT))) */ Z3_ast Z3_API Z3_mk_atmost(Z3_context c, unsigned num_args, Z3_ast const args[], unsigned k); /** \brief Pseudo-Boolean relations. Encode k1*p1 + k2*p2 + ... + kn*pn <= k def_API('Z3_mk_pble', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT))) */ Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, Z3_ast const args[], int coeffs[], int k); /** \mlonly {3 {L Function Declarations}} \endmlonly */ /** \brief Convert a \c Z3_func_decl into \c Z3_ast. \conly This is just type casting. \mlonly \remark [func_decl_to_ast c f] can be replaced by [(f :> ast)]. \endmlonly def_API('Z3_func_decl_to_ast', AST, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_ast Z3_API Z3_func_decl_to_ast(Z3_context c, Z3_func_decl f); /** \brief compare terms. \mlonly \remark [Pervasives.( = )] or [Pervasives.compare] can also be used. \endmlonly def_API('Z3_is_eq_func_decl', BOOL, (_in(CONTEXT), _in(FUNC_DECL), _in(FUNC_DECL))) */ Z3_bool Z3_API Z3_is_eq_func_decl(Z3_context c, Z3_func_decl f1, Z3_func_decl f2); /** \brief Return a unique identifier for \c f. \mlonly \remark Implicitly used by [Pervasives.( = )] and [Pervasives.compare]. \endmlonly def_API('Z3_get_func_decl_id', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ unsigned Z3_API Z3_get_func_decl_id(Z3_context c, Z3_func_decl f); /** \brief Return the constant declaration name as a symbol. def_API('Z3_get_decl_name', SYMBOL, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_symbol Z3_API Z3_get_decl_name(Z3_context c, Z3_func_decl d); /** \brief Return declaration kind corresponding to declaration. def_API('Z3_get_decl_kind', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_decl_kind Z3_API Z3_get_decl_kind(Z3_context c, Z3_func_decl d); /** \brief Return the number of parameters of the given declaration. \sa Z3_get_arity def_API('Z3_get_domain_size', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ unsigned Z3_API Z3_get_domain_size(Z3_context c, Z3_func_decl d); /** \brief Alias for \c Z3_get_domain_size. \sa Z3_get_domain_size def_API('Z3_get_arity', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ unsigned Z3_API Z3_get_arity(Z3_context c, Z3_func_decl d); /** \brief \mlh get_domain c d i \endmlh Return the sort of the i-th parameter of the given function declaration. \pre i < Z3_get_domain_size(d) \sa Z3_get_domain_size def_API('Z3_get_domain', SORT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_sort Z3_API Z3_get_domain(Z3_context c, Z3_func_decl d, unsigned i); #ifdef ML4only #include #endif /** \brief \mlh get_range c d \endmlh Return the range of the given declaration. If \c d is a constant (i.e., has zero arguments), then this function returns the sort of the constant. def_API('Z3_get_range', SORT, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_sort Z3_API Z3_get_range(Z3_context c, Z3_func_decl d); /** \brief Return the number of parameters associated with a declaration. def_API('Z3_get_decl_num_parameters', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ unsigned Z3_API Z3_get_decl_num_parameters(Z3_context c, Z3_func_decl d); /** \brief Return the parameter type associated with a declaration. \param c the context \param d the function declaration \param idx is the index of the named parameter it should be between 0 and the number of parameters. def_API('Z3_get_decl_parameter_kind', UINT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_parameter_kind Z3_API Z3_get_decl_parameter_kind(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the integer value associated with an integer parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_INT def_API('Z3_get_decl_int_parameter', INT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ int Z3_API Z3_get_decl_int_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the double value associated with an double parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_DOUBLE def_API('Z3_get_decl_double_parameter', DOUBLE, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ double Z3_API Z3_get_decl_double_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the double value associated with an double parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_SYMBOL def_API('Z3_get_decl_symbol_parameter', SYMBOL, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_symbol Z3_API Z3_get_decl_symbol_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the sort value associated with a sort parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_SORT def_API('Z3_get_decl_sort_parameter', SORT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_sort Z3_API Z3_get_decl_sort_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the expresson value associated with an expression parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_AST def_API('Z3_get_decl_ast_parameter', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_ast Z3_API Z3_get_decl_ast_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the expresson value associated with an expression parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_FUNC_DECL def_API('Z3_get_decl_func_decl_parameter', FUNC_DECL, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_decl_func_decl_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the rational value, as a string, associated with a rational parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_RATIONAL def_API('Z3_get_decl_rational_parameter', STRING, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_string Z3_API Z3_get_decl_rational_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \mlonly {3 {L Applications}} \endmlonly */ /** \brief Convert a \c Z3_app into \c Z3_ast. \conly This is just type casting. \mlonly \remark [app_to_ast c a] can be replaced by [(a :> ast)]. \endmlonly def_API('Z3_app_to_ast', AST, (_in(CONTEXT), _in(APP))) */ Z3_ast Z3_API Z3_app_to_ast(Z3_context c, Z3_app a); /** \brief Return the declaration of a constant or function application. def_API('Z3_get_app_decl', FUNC_DECL, (_in(CONTEXT), _in(APP))) */ Z3_func_decl Z3_API Z3_get_app_decl(Z3_context c, Z3_app a); /** \brief \mlh get_app_num_args c a \endmlh Return the number of argument of an application. If \c t is an constant, then the number of arguments is 0. def_API('Z3_get_app_num_args', UINT, (_in(CONTEXT), _in(APP))) */ unsigned Z3_API Z3_get_app_num_args(Z3_context c, Z3_app a); /** \brief \mlh get_app_arg c a i \endmlh Return the i-th argument of the given application. \pre i < Z3_get_num_args(c, a) def_API('Z3_get_app_arg', AST, (_in(CONTEXT), _in(APP), _in(UINT))) */ Z3_ast Z3_API Z3_get_app_arg(Z3_context c, Z3_app a, unsigned i); #ifdef ML4only #include #endif /** \mlonly {3 {L Terms}} \endmlonly */ #ifdef ML4only #include #endif /** \brief compare terms. \mlonly \remark [Pervasives.( = )] or [Pervasives.compare] can also be used. \endmlonly def_API('Z3_is_eq_ast', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_bool Z3_API Z3_is_eq_ast(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Return a unique identifier for \c t. The identifier is unique up to structural equality. Thus, two ast nodes created by the same context and having the same children and same function symbols have the same identifiers. Ast nodes created in the same context, but having different children or different functions have different identifiers. Variables and quantifiers are also assigned different identifiers according to their structure. \mlonly \remark Implicitly used by [Pervasives.compare] for values of type [ast], [app], [sort], [func_decl], and [pattern]. \endmlonly def_API('Z3_get_ast_id', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_ast_id(Z3_context c, Z3_ast t); /** \brief Return a hash code for the given AST. The hash code is structural. You can use Z3_get_ast_id interchangably with this function. \mlonly \remark Implicitly used by [Hashtbl.hash] for values of type [ast], [app], [sort], [func_decl], and [pattern]. \endmlonly def_API('Z3_get_ast_hash', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_ast_hash(Z3_context c, Z3_ast a); /** \brief Return the sort of an AST node. The AST node must be a constant, application, numeral, bound variable, or quantifier. def_API('Z3_get_sort', SORT, (_in(CONTEXT), _in(AST))) */ Z3_sort Z3_API Z3_get_sort(Z3_context c, Z3_ast a); /** \brief Return true if the given expression \c t is well sorted. def_API('Z3_is_well_sorted', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_is_well_sorted(Z3_context c, Z3_ast t); /** \brief Return Z3_L_TRUE if \c a is true, Z3_L_FALSE if it is false, and Z3_L_UNDEF otherwise. def_API('Z3_get_bool_value', UINT, (_in(CONTEXT), _in(AST))) */ Z3_lbool Z3_API Z3_get_bool_value(Z3_context c, Z3_ast a); /** \brief Return the kind of the given AST. def_API('Z3_get_ast_kind', UINT, (_in(CONTEXT), _in(AST))) */ Z3_ast_kind Z3_API Z3_get_ast_kind(Z3_context c, Z3_ast a); /** def_API('Z3_is_app', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_is_app(Z3_context c, Z3_ast a); /** def_API('Z3_is_numeral_ast', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_is_numeral_ast(Z3_context c, Z3_ast a); /** \brief Return true if the give AST is a real algebraic number. def_API('Z3_is_algebraic_number', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_is_algebraic_number(Z3_context c, Z3_ast a); /** \brief Convert an \c ast into an \c APP_AST. \conly This is just type casting. \pre \code Z3_get_ast_kind(c, a) == \c Z3_APP_AST \endcode def_API('Z3_to_app', APP, (_in(CONTEXT), _in(AST))) */ Z3_app Z3_API Z3_to_app(Z3_context c, Z3_ast a); /** \brief Convert an AST into a FUNC_DECL_AST. This is just type casting. \pre \code Z3_get_ast_kind(c, a) == Z3_FUNC_DECL_AST \endcode def_API('Z3_to_func_decl', FUNC_DECL, (_in(CONTEXT), _in(AST))) */ Z3_func_decl Z3_API Z3_to_func_decl(Z3_context c, Z3_ast a); /** \mlonly {4 {L Numerals}} \endmlonly */ #ifdef ML4only #include #endif /** \mlonly {5 {L Low-level API}} \endmlonly */ /** \brief Return numeral value, as a string of a numeric constant term \pre Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST def_API('Z3_get_numeral_string', STRING, (_in(CONTEXT), _in(AST))) */ Z3_string Z3_API Z3_get_numeral_string(Z3_context c, Z3_ast a); /** \brief Return numeral as a string in decimal notation. The result has at most \c precision decimal places. \pre Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST || Z3_is_algebraic_number(c, a) def_API('Z3_get_numeral_decimal_string', STRING, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_string Z3_API Z3_get_numeral_decimal_string(Z3_context c, Z3_ast a, unsigned precision); /** \brief Return the numerator (as a numeral AST) of a numeral AST of sort Real. \pre Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST def_API('Z3_get_numerator', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_get_numerator(Z3_context c, Z3_ast a); /** \brief Return the denominator (as a numeral AST) of a numeral AST of sort Real. \pre Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST def_API('Z3_get_denominator', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_get_denominator(Z3_context c, Z3_ast a); /** \brief Return numeral value, as a pair of 64 bit numbers if the representation fits. \param c logical context. \param a term. \param num numerator. \param den denominator. Return \c Z3_TRUE if the numeral value fits in 64 bit numerals, \c Z3_FALSE otherwise. \pre Z3_get_ast_kind(a) == Z3_NUMERAL_AST def_API('Z3_get_numeral_small', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _out(INT64))) */ Z3_bool Z3_API Z3_get_numeral_small(Z3_context c, Z3_ast a, __int64* num, __int64* den); /** \brief \mlh get_numeral_int c v \endmlh Similar to #Z3_get_numeral_string, but only succeeds if the value can fit in a machine int. Return Z3_TRUE if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST \sa Z3_get_numeral_string def_API('Z3_get_numeral_int', BOOL, (_in(CONTEXT), _in(AST), _out(INT))) */ Z3_bool Z3_API Z3_get_numeral_int(Z3_context c, Z3_ast v, int* i); #ifdef Conly /** \brief \mlh get_numeral_uint c v \endmlh Similar to #Z3_get_numeral_string, but only succeeds if the value can fit in a machine unsigned int. Return Z3_TRUE if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST \sa Z3_get_numeral_string def_API('Z3_get_numeral_uint', BOOL, (_in(CONTEXT), _in(AST), _out(UINT))) */ Z3_bool Z3_API Z3_get_numeral_uint(Z3_context c, Z3_ast v, unsigned* u); #endif #ifdef Conly /** \brief \mlh get_numeral_uint64 c v \endmlh Similar to #Z3_get_numeral_string, but only succeeds if the value can fit in a machine unsigned __int64 int. Return Z3_TRUE if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST \sa Z3_get_numeral_string def_API('Z3_get_numeral_uint64', BOOL, (_in(CONTEXT), _in(AST), _out(UINT64))) */ Z3_bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, unsigned __int64* u); #endif /** \brief \mlh get_numeral_int64 c v \endmlh Similar to #Z3_get_numeral_string, but only succeeds if the value can fit in a machine __int64 int. Return Z3_TRUE if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST \sa Z3_get_numeral_string def_API('Z3_get_numeral_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64))) */ Z3_bool Z3_API Z3_get_numeral_int64(Z3_context c, Z3_ast v, __int64* i); /** \brief \mlh get_numeral_rational_int64 c x y\endmlh Similar to #Z3_get_numeral_string, but only succeeds if the value can fit as a rational number as machine __int64 int. Return Z3_TRUE if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST \sa Z3_get_numeral_string def_API('Z3_get_numeral_rational_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _out(INT64))) */ Z3_bool Z3_API Z3_get_numeral_rational_int64(Z3_context c, Z3_ast v, __int64* num, __int64* den); /** \brief Return a lower bound for the given real algebraic number. The interval isolating the number is smaller than 1/10^precision. The result is a numeral AST of sort Real. \pre Z3_is_algebraic_number(c, a) def_API('Z3_get_algebraic_number_lower', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_ast Z3_API Z3_get_algebraic_number_lower(Z3_context c, Z3_ast a, unsigned precision); /** \brief Return a upper bound for the given real algebraic number. The interval isolating the number is smaller than 1/10^precision. The result is a numeral AST of sort Real. \pre Z3_is_algebraic_number(c, a) def_API('Z3_get_algebraic_number_upper', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_ast Z3_API Z3_get_algebraic_number_upper(Z3_context c, Z3_ast a, unsigned precision); /** \mlonly {4 {L Patterns}} \endmlonly */ /** \brief Convert a Z3_pattern into Z3_ast. \conly This is just type casting. \mlonly \remark [pattern_to_ast c p] can be replaced by [(p :> ast)]. \endmlonly def_API('Z3_pattern_to_ast', AST, (_in(CONTEXT), _in(PATTERN))) */ Z3_ast Z3_API Z3_pattern_to_ast(Z3_context c, Z3_pattern p); #ifdef ML4only #include #endif /** \brief Return number of terms in pattern. def_API('Z3_get_pattern_num_terms', UINT, (_in(CONTEXT), _in(PATTERN))) */ unsigned Z3_API Z3_get_pattern_num_terms(Z3_context c, Z3_pattern p); /** \brief Return i'th ast in pattern. def_API('Z3_get_pattern', AST, (_in(CONTEXT), _in(PATTERN), _in(UINT))) */ Z3_ast Z3_API Z3_get_pattern(Z3_context c, Z3_pattern p, unsigned idx); /** \mlonly {4 {L Quantifiers}} \endmlonly */ /** \brief Return index of de-Brujin bound variable. \pre Z3_get_ast_kind(a) == Z3_VAR_AST def_API('Z3_get_index_value', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_index_value(Z3_context c, Z3_ast a); /** \brief Determine if quantifier is universal. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_is_quantifier_forall', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_is_quantifier_forall(Z3_context c, Z3_ast a); /** \brief Obtain weight of quantifier. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_weight', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_quantifier_weight(Z3_context c, Z3_ast a); /** \brief Return number of patterns used in quantifier. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_num_patterns', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_quantifier_num_patterns(Z3_context c, Z3_ast a); /** \brief Return i'th pattern. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_pattern_ast', PATTERN, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_pattern Z3_API Z3_get_quantifier_pattern_ast(Z3_context c, Z3_ast a, unsigned i); /** \brief Return number of no_patterns used in quantifier. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_num_no_patterns', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_quantifier_num_no_patterns(Z3_context c, Z3_ast a); /** \brief Return i'th no_pattern. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_no_pattern_ast', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_ast Z3_API Z3_get_quantifier_no_pattern_ast(Z3_context c, Z3_ast a, unsigned i); /** \brief Return number of bound variables of quantifier. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_num_bound', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_quantifier_num_bound(Z3_context c, Z3_ast a); /** \brief Return symbol of the i'th bound variable. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_bound_name', SYMBOL, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_symbol Z3_API Z3_get_quantifier_bound_name(Z3_context c, Z3_ast a, unsigned i); /** \brief Return sort of the i'th bound variable. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_bound_sort', SORT, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_sort Z3_API Z3_get_quantifier_bound_sort(Z3_context c, Z3_ast a, unsigned i); /** \brief Return body of quantifier. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_body', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_get_quantifier_body(Z3_context c, Z3_ast a); /** \mlonly {3 {L Simplification}} \endmlonly */ /** \brief Interface to simplifier. Provides an interface to the AST simplifier used by Z3. def_API('Z3_simplify', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_simplify(Z3_context c, Z3_ast a); #ifdef CorML4 /** \brief Interface to simplifier. Provides an interface to the AST simplifier used by Z3. This procedure is similar to #Z3_simplify, but the behavior of the simplifier can be configured using the given parameter set. def_API('Z3_simplify_ex', AST, (_in(CONTEXT), _in(AST), _in(PARAMS))) */ Z3_ast Z3_API Z3_simplify_ex(Z3_context c, Z3_ast a, Z3_params p); /** \brief Return a string describing all available parameters. def_API('Z3_simplify_get_help', STRING, (_in(CONTEXT),)) */ Z3_string Z3_API Z3_simplify_get_help(Z3_context c); /** \brief Return the parameter description set for the simplify procedure. def_API('Z3_simplify_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT),)) */ Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(Z3_context c); #endif /*@}*/ /** @name Modifiers */ /*@{*/ /** \brief Update the arguments of term \c a using the arguments \c args. The number of arguments \c num_args should coincide with the number of arguments to \c a. If \c a is a quantifier, then num_args has to be 1. def_API('Z3_update_term', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ Z3_ast Z3_API Z3_update_term(Z3_context c, Z3_ast a, unsigned num_args, Z3_ast const args[]); /** \brief Substitute every occurrence of from[i] in \c a with to[i], for \c i smaller than \c num_exprs. The result is the new AST. The arrays \c from and \c to must have size \c num_exprs. For every \c i smaller than \c num_exprs, we must have that sort of from[i] must be equal to sort of to[i]. def_API('Z3_substitute', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST), _in_array(2, AST))) */ Z3_ast Z3_API Z3_substitute(Z3_context c, Z3_ast a, unsigned num_exprs, Z3_ast const from[], Z3_ast const to[]); /** \brief Substitute the free variables in \c a with the expressions in \c to. For every \c i smaller than \c num_exprs, the variable with de-Bruijn index \c i is replaced with term to[i]. def_API('Z3_substitute_vars', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ Z3_ast Z3_API Z3_substitute_vars(Z3_context c, Z3_ast a, unsigned num_exprs, Z3_ast const to[]); #ifdef CorML4 /** \brief Translate/Copy the AST \c a from context \c source to context \c target. AST \c a must have been created using context \c source. \pre source != target def_API('Z3_translate', AST, (_in(CONTEXT), _in(AST), _in(CONTEXT))) */ Z3_ast Z3_API Z3_translate(Z3_context source, Z3_ast a, Z3_context target); #endif /*@}*/ #ifdef CorML4 /** @name Models */ /*@{*/ #ifdef ML4only #include #endif #ifdef Conly /** \brief Increment the reference counter of the given model. def_API('Z3_model_inc_ref', VOID, (_in(CONTEXT), _in(MODEL))) */ void Z3_API Z3_model_inc_ref(Z3_context c, Z3_model m); /** \brief Decrement the reference counter of the given model. def_API('Z3_model_dec_ref', VOID, (_in(CONTEXT), _in(MODEL))) */ void Z3_API Z3_model_dec_ref(Z3_context c, Z3_model m); #endif /** \brief \mlh model_eval c m t \endmlh Evaluate the AST node \c t in the given model. \conly Return \c Z3_TRUE if succeeded, and store the result in \c v. \mlonly Return \c None if the term was not successfully evaluated. \endmlonly If \c model_completion is Z3_TRUE, then Z3 will assign an interpretation for any constant or function that does not have an interpretation in \c m. These constants and functions were essentially don't cares. The evaluation may fail for the following reasons: - \c t contains a quantifier. - the model \c m is partial, that is, it doesn't have a complete interpretation for uninterpreted functions. That is, the option MODEL_PARTIAL=true was used. - \c t is type incorrect. def_API('Z3_model_eval', BOOL, (_in(CONTEXT), _in(MODEL), _in(AST), _in(BOOL), _out(AST))) */ Z3_bool_opt Z3_API Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, Z3_bool model_completion, Z3_ast * v); /** \mlonly {4 {L Low-level API}} \endmlonly */ /** \brief Return the interpretation (i.e., assignment) of constant \c a in the model \c m. Return \mlonly [None], \endmlonly \conly \c NULL, if the model does not assign an interpretation for \c a. That should be interpreted as: the value of \c a does not matter. \pre Z3_get_arity(c, a) == 0 def_API('Z3_model_get_const_interp', AST, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL))) */ Z3_ast_opt Z3_API Z3_model_get_const_interp(Z3_context c, Z3_model m, Z3_func_decl a); /** \brief Test if there exists an interpretation (i.e., assignment) for \c a in the model \c m. def_API('Z3_model_has_interp', BOOL, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL))) */ Z3_bool Z3_API Z3_model_has_interp(Z3_context c, Z3_model m, Z3_func_decl a); /** \brief Return the interpretation of the function \c f in the model \c m. Return \mlonly [None], \endmlonly \conly \c NULL, if the model does not assign an interpretation for \c f. That should be interpreted as: the \c f does not matter. \pre Z3_get_arity(c, f) > 0 \conly \remark Reference counting must be used to manage Z3_func_interp objects, even when the Z3_context was \conly created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_model_get_func_interp', FUNC_INTERP, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL))) */ Z3_func_interp_opt Z3_API Z3_model_get_func_interp(Z3_context c, Z3_model m, Z3_func_decl f); /** \brief Return the number of constants assigned by the given model. \sa Z3_model_get_const_decl def_API('Z3_model_get_num_consts', UINT, (_in(CONTEXT), _in(MODEL))) */ unsigned Z3_API Z3_model_get_num_consts(Z3_context c, Z3_model m); /** \brief \mlh model_get_const_decl c m i \endmlh Return the i-th constant in the given model. \pre i < Z3_model_get_num_consts(c, m) \sa Z3_model_eval def_API('Z3_model_get_const_decl', FUNC_DECL, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ Z3_func_decl Z3_API Z3_model_get_const_decl(Z3_context c, Z3_model m, unsigned i); /** \brief Return the number of function interpretations in the given model. 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. def_API('Z3_model_get_num_funcs', UINT, (_in(CONTEXT), _in(MODEL))) */ unsigned Z3_API Z3_model_get_num_funcs(Z3_context c, Z3_model m); /** \brief \mlh model_get_func_decl c m i \endmlh Return the declaration of the i-th function in the given model. \pre i < Z3_model_get_num_funcs(c, m) \sa Z3_model_get_num_funcs def_API('Z3_model_get_func_decl', FUNC_DECL, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ Z3_func_decl Z3_API Z3_model_get_func_decl(Z3_context c, Z3_model m, unsigned i); /** \brief Return the number of uninterpreted sorts that \c m assigs an interpretation to. Z3 also provides an intepretation for uninterpreted sorts used in a formua. The interpretation for a sort \c s is a finite set of distinct values. We say this finite set is the "universe" of \c s. \sa Z3_model_get_sort \sa Z3_model_get_sort_universe def_API('Z3_model_get_num_sorts', UINT, (_in(CONTEXT), _in(MODEL))) */ unsigned Z3_API Z3_model_get_num_sorts(Z3_context c, Z3_model m); /** \brief Return a uninterpreted sort that \c m assigns an interpretation. \pre i < Z3_model_get_num_sorts(c, m) \sa Z3_model_get_num_sorts \sa Z3_model_get_sort_universe def_API('Z3_model_get_sort', SORT, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ Z3_sort Z3_API Z3_model_get_sort(Z3_context c, Z3_model m, unsigned i); /** \brief Return the finite set of distinct values that represent the interpretation for sort \c s. \sa Z3_model_get_num_sorts \sa Z3_model_get_sort def_API('Z3_model_get_sort_universe', AST_VECTOR, (_in(CONTEXT), _in(MODEL), _in(SORT))) */ Z3_ast_vector Z3_API Z3_model_get_sort_universe(Z3_context c, Z3_model m, Z3_sort s); /** \brief The (_ as-array f) AST node is a construct for assigning interpretations for arrays in Z3. It is the array such that forall indices \c i we have that (select (_ as-array f) i) is equal to (f i). This procedure returns Z3_TRUE if the \c a is an \c as-array AST node. Z3 current solvers have minimal support for \c as_array nodes. \sa Z3_get_as_array_func_decl def_API('Z3_is_as_array', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_is_as_array(Z3_context c, Z3_ast a); /** \brief Return the function declaration \c f associated with a (_ as_array f) node. \sa Z3_is_as_array def_API('Z3_get_as_array_func_decl', FUNC_DECL, (_in(CONTEXT), _in(AST))) */ Z3_func_decl Z3_API Z3_get_as_array_func_decl(Z3_context c, Z3_ast a); #ifdef Conly /** \brief Increment the reference counter of the given Z3_func_interp object. def_API('Z3_func_interp_inc_ref', VOID, (_in(CONTEXT), _in(FUNC_INTERP))) */ void Z3_API Z3_func_interp_inc_ref(Z3_context c, Z3_func_interp f); /** \brief Decrement the reference counter of the given Z3_func_interp object. def_API('Z3_func_interp_dec_ref', VOID, (_in(CONTEXT), _in(FUNC_INTERP))) */ void Z3_API Z3_func_interp_dec_ref(Z3_context c, Z3_func_interp f); #endif /** \brief Return the number of entries in the given function interpretation. 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. This procedure return the number of element in the finite map of \c f. def_API('Z3_func_interp_get_num_entries', UINT, (_in(CONTEXT), _in(FUNC_INTERP))) */ unsigned Z3_API Z3_func_interp_get_num_entries(Z3_context c, Z3_func_interp f); /** \brief Return a "point" of the given function intepretation. It represents the value of \c f in a particular point. \pre i < Z3_func_interp_get_num_entries(c, f) \sa Z3_func_interp_get_num_entries def_API('Z3_func_interp_get_entry', FUNC_ENTRY, (_in(CONTEXT), _in(FUNC_INTERP), _in(UINT))) */ Z3_func_entry Z3_API Z3_func_interp_get_entry(Z3_context c, Z3_func_interp f, unsigned i); /** \brief Return the 'else' value of the given function interpretation. A function interpretation is represented as a finite map and an 'else' value. This procedure returns the 'else' value. def_API('Z3_func_interp_get_else', AST, (_in(CONTEXT), _in(FUNC_INTERP))) */ Z3_ast Z3_API Z3_func_interp_get_else(Z3_context c, Z3_func_interp f); /** \brief Return the arity (number of arguments) of the given function interpretation. def_API('Z3_func_interp_get_arity', UINT, (_in(CONTEXT), _in(FUNC_INTERP))) */ unsigned Z3_API Z3_func_interp_get_arity(Z3_context c, Z3_func_interp f); #ifdef Conly /** \brief Increment the reference counter of the given Z3_func_entry object. def_API('Z3_func_entry_inc_ref', VOID, (_in(CONTEXT), _in(FUNC_ENTRY))) */ void Z3_API Z3_func_entry_inc_ref(Z3_context c, Z3_func_entry e); /** \brief Decrement the reference counter of the given Z3_func_entry object. def_API('Z3_func_entry_dec_ref', VOID, (_in(CONTEXT), _in(FUNC_ENTRY))) */ void Z3_API Z3_func_entry_dec_ref(Z3_context c, Z3_func_entry e); #endif /** \brief Return the value of this point. A Z3_func_entry object represents an element in the finite map used to encode a function interpretation. \sa Z3_func_interp_get_entry def_API('Z3_func_entry_get_value', AST, (_in(CONTEXT), _in(FUNC_ENTRY))) */ Z3_ast Z3_API Z3_func_entry_get_value(Z3_context c, Z3_func_entry e); /** \brief Return the number of arguments in a Z3_func_entry object. \sa Z3_func_interp_get_entry def_API('Z3_func_entry_get_num_args', UINT, (_in(CONTEXT), _in(FUNC_ENTRY))) */ unsigned Z3_API Z3_func_entry_get_num_args(Z3_context c, Z3_func_entry e); /** \brief Return an argument of a Z3_func_entry object. \pre i < Z3_func_entry_get_num_args(c, e) \sa Z3_func_interp_get_entry def_API('Z3_func_entry_get_arg', AST, (_in(CONTEXT), _in(FUNC_ENTRY), _in(UINT))) */ Z3_ast Z3_API Z3_func_entry_get_arg(Z3_context c, Z3_func_entry e, unsigned i); /*@}*/ #endif // CorML4 /** @name Interaction logging. */ /*@{*/ /** \brief Log interaction to a file. extra_API('Z3_open_log', INT, (_in(STRING),)) */ Z3_bool Z3_API Z3_open_log(Z3_string filename); /** \brief Append user-defined string to interaction log. The interaction log is opened using Z3_open_log. It contains the formulas that are checked using Z3. You can use this command to append comments, for instance. extra_API('Z3_append_log', VOID, (_in(STRING),)) */ void Z3_API Z3_append_log(Z3_string string); /** \brief Close interaction log. extra_API('Z3_close_log', VOID, ()) */ void Z3_API Z3_close_log(void); /** \brief Enable/disable printing warning messages to the console. Warnings are printed after passing \c true, warning messages are suppressed after calling this method with \c false. def_API('Z3_toggle_warning_messages', VOID, (_in(BOOL),)) */ void Z3_API Z3_toggle_warning_messages(Z3_bool enabled); /*@}*/ /** @name String conversion */ /*@{*/ /** \brief Select mode for the format used for pretty-printing AST nodes. The default mode for pretty printing AST nodes 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. \sa Z3_ast_to_string \sa Z3_pattern_to_string \sa Z3_func_decl_to_string def_API('Z3_set_ast_print_mode', VOID, (_in(CONTEXT), _in(PRINT_MODE))) */ void Z3_API Z3_set_ast_print_mode(Z3_context c, Z3_ast_print_mode mode); /** \brief Convert the given AST node into a string. \conly \warning The result buffer is statically allocated by Z3. It will \conly be automatically deallocated when #Z3_del_context is invoked. \conly So, the buffer is invalidated in the next call to \c Z3_ast_to_string. \sa Z3_pattern_to_string \sa Z3_sort_to_string def_API('Z3_ast_to_string', STRING, (_in(CONTEXT), _in(AST))) */ Z3_string Z3_API Z3_ast_to_string(Z3_context c, Z3_ast a); /** def_API('Z3_pattern_to_string', STRING, (_in(CONTEXT), _in(PATTERN))) */ Z3_string Z3_API Z3_pattern_to_string(Z3_context c, Z3_pattern p); /** def_API('Z3_sort_to_string', STRING, (_in(CONTEXT), _in(SORT))) */ Z3_string Z3_API Z3_sort_to_string(Z3_context c, Z3_sort s); /** def_API('Z3_func_decl_to_string', STRING, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_string Z3_API Z3_func_decl_to_string(Z3_context c, Z3_func_decl d); /** \brief Convert the given model into a string. \conly \warning The result buffer is statically allocated by Z3. It will \conly be automatically deallocated when #Z3_del_context is invoked. \conly So, the buffer is invalidated in the next call to \c Z3_model_to_string. def_API('Z3_model_to_string', STRING, (_in(CONTEXT), _in(MODEL))) */ Z3_string Z3_API Z3_model_to_string(Z3_context c, Z3_model m); /** \brief Convert the given benchmark into SMT-LIB formatted string. \conly \warning The result buffer is statically allocated by Z3. It will \conly be automatically deallocated when #Z3_del_context is invoked. \conly So, the buffer is invalidated in the next call to \c Z3_benchmark_to_smtlib_string. \param c - context. \param name - name of benchmark. The argument is optional. \param logic - the benchmark logic. \param status - the status string (sat, unsat, or unknown) \param attributes - other attributes, such as source, difficulty or category. \param num_assumptions - number of assumptions. \param assumptions - auxiliary assumptions. \param formula - formula to be checked for consistency in conjunction with assumptions. def_API('Z3_benchmark_to_smtlib_string', STRING, (_in(CONTEXT), _in(STRING), _in(STRING), _in(STRING), _in(STRING), _in(UINT), _in_array(5, AST), _in(AST))) */ Z3_string Z3_API Z3_benchmark_to_smtlib_string(Z3_context c, Z3_string name, Z3_string logic, Z3_string status, Z3_string attributes, unsigned num_assumptions, Z3_ast const assumptions[], Z3_ast formula); /*@}*/ /** @name Parser interface */ /*@{*/ /** \brief \mlh parse_smtlib2_string c str \endmlh Parse the given string using the SMT-LIB2 parser. It returns a formula comprising of the conjunction of assertions in the scope (up to push/pop) at the end of the string. def_API('Z3_parse_smtlib2_string', AST, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ 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[]); /** \brief Similar to #Z3_parse_smtlib2_string, but reads the benchmark from a file. def_API('Z3_parse_smtlib2_file', AST, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ 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[]); #ifdef ML4only #include #endif /** \mlonly {4 {L Low-level API}} \endmlonly */ /** \brief \mlh parse_smtlib_string c str sort_names sorts decl_names decls \endmlh 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 \c sort_names and \c decl_names don't need to match the names of the sorts and declarations in the arrays \c sorts and \c decls. This is an useful feature since we can use arbitrary names to reference sorts and declarations defined using the C API. The formulas, assumptions and declarations defined in \c str can be extracted using the functions: #Z3_get_smtlib_num_formulas, #Z3_get_smtlib_formula, #Z3_get_smtlib_num_assumptions, #Z3_get_smtlib_assumption, #Z3_get_smtlib_num_decls, and #Z3_get_smtlib_decl. def_API('Z3_parse_smtlib_string', VOID, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ void Z3_API Z3_parse_smtlib_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[] ); /** \brief Similar to #Z3_parse_smtlib_string, but reads the benchmark from a file. def_API('Z3_parse_smtlib_file', VOID, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ void Z3_API Z3_parse_smtlib_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[] ); /** \brief Return the number of SMTLIB formulas parsed by the last call to #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. def_API('Z3_get_smtlib_num_formulas', UINT, (_in(CONTEXT), )) */ unsigned Z3_API Z3_get_smtlib_num_formulas(Z3_context c); /** \brief \mlh get_smtlib_formula c i \endmlh Return the i-th formula parsed by the last call to #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. \pre i < Z3_get_smtlib_num_formulas(c) def_API('Z3_get_smtlib_formula', AST, (_in(CONTEXT), _in(UINT))) */ Z3_ast Z3_API Z3_get_smtlib_formula(Z3_context c, unsigned i); /** \brief Return the number of SMTLIB assumptions parsed by #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. def_API('Z3_get_smtlib_num_assumptions', UINT, (_in(CONTEXT), )) */ unsigned Z3_API Z3_get_smtlib_num_assumptions(Z3_context c); /** \brief \mlh get_smtlib_assumption c i \endmlh Return the i-th assumption parsed by the last call to #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. \pre i < Z3_get_smtlib_num_assumptions(c) def_API('Z3_get_smtlib_assumption', AST, (_in(CONTEXT), _in(UINT))) */ Z3_ast Z3_API Z3_get_smtlib_assumption(Z3_context c, unsigned i); /** \brief Return the number of declarations parsed by #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. def_API('Z3_get_smtlib_num_decls', UINT, (_in(CONTEXT), )) */ unsigned Z3_API Z3_get_smtlib_num_decls(Z3_context c); /** \brief \mlh get_smtlib_decl c i \endmlh Return the i-th declaration parsed by the last call to #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. \pre i < Z3_get_smtlib_num_decls(c) def_API('Z3_get_smtlib_decl', FUNC_DECL, (_in(CONTEXT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_smtlib_decl(Z3_context c, unsigned i); /** \brief Return the number of sorts parsed by #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. def_API('Z3_get_smtlib_num_sorts', UINT, (_in(CONTEXT), )) */ unsigned Z3_API Z3_get_smtlib_num_sorts(Z3_context c); /** \brief \mlh get_smtlib_sort c i \endmlh Return the i-th sort parsed by the last call to #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. \pre i < Z3_get_smtlib_num_sorts(c) def_API('Z3_get_smtlib_sort', SORT, (_in(CONTEXT), _in(UINT))) */ Z3_sort Z3_API Z3_get_smtlib_sort(Z3_context c, unsigned i); BEGIN_MLAPI_EXCLUDE /** \brief \mlh get_smtlib_error c \endmlh Retrieve that last error message information generated from parsing. def_API('Z3_get_smtlib_error', STRING, (_in(CONTEXT), )) */ Z3_string Z3_API Z3_get_smtlib_error(Z3_context c); END_MLAPI_EXCLUDE /*@}*/ #ifdef CorML4 /** @name Error Handling */ /*@{*/ #ifndef SAFE_ERRORS /** \brief Return the error code for the last API call. A call to a Z3 function may return a non Z3_OK error code, when it is not used correctly. \sa Z3_set_error_handler def_API('Z3_get_error_code', UINT, (_in(CONTEXT), )) */ Z3_error_code Z3_API Z3_get_error_code(Z3_context c); /** \brief Register a Z3 error handler. A call to a Z3 function may return a non Z3_OK error code, when it is not used correctly. An error handler can be registered and will be called in this case. \conly To disable the use of the error handler, simply register with \c h=NULL. \warning Log files, created using #Z3_open_log, may be potentially incomplete/incorrect if error handlers are used. \sa Z3_get_error_code */ void Z3_API Z3_set_error_handler(Z3_context c, Z3_error_handler h); #endif /** \brief Set an error. def_API('Z3_set_error', VOID, (_in(CONTEXT), _in(ERROR_CODE))) */ void Z3_API Z3_set_error(Z3_context c, Z3_error_code e); #ifdef Conly /** \brief Return a string describing the given error code. \deprecated Use #Z3_get_error_msg_ex instead. def_API('Z3_get_error_msg', STRING, (_in(ERROR_CODE),)) */ Z3_string Z3_API Z3_get_error_msg(Z3_error_code err); #endif BEGIN_MLAPI_EXCLUDE /** \brief Return a string describing the given error code. def_API('Z3_get_error_msg_ex', STRING, (_in(CONTEXT), _in(ERROR_CODE))) */ Z3_string Z3_API Z3_get_error_msg_ex(Z3_context c, Z3_error_code err); END_MLAPI_EXCLUDE #ifdef ML4only #include #endif /*@}*/ #endif /** @name Miscellaneous */ /*@{*/ /** \brief Return Z3 version number information. def_API('Z3_get_version', VOID, (_out(UINT), _out(UINT), _out(UINT), _out(UINT))) */ void Z3_API Z3_get_version(unsigned * major, unsigned * minor, unsigned * build_number, unsigned * revision_number); /** \brief Enable tracing messages tagged as \c tag when Z3 is compiled in debug mode. It is a NOOP otherwise def_API('Z3_enable_trace', VOID, (_in(STRING),)) */ void Z3_API Z3_enable_trace(Z3_string tag); /** \brief Disable tracing messages tagged as \c tag when Z3 is compiled in debug mode. It is a NOOP otherwise def_API('Z3_disable_trace', VOID, (_in(STRING),)) */ void Z3_API Z3_disable_trace(Z3_string tag); #ifdef CorML3 /** \brief Reset all allocated resources. Use this facility on out-of memory errors. It allows discharging the previous state and resuming afresh. Any pointers previously returned by the API become invalid. def_API('Z3_reset_memory', VOID, ()) */ void Z3_API Z3_reset_memory(void); #endif #ifdef CorML3 /** \brief Destroy all allocated resources. Any pointers previously returned by the API become invalid. Can be used for memory leak detection. def_API('Z3_finalize_memory', VOID, ()) */ void Z3_API Z3_finalize_memory(void); #endif /*@}*/ #ifdef CorML3 /** @name External Theory Plugins */ /*@{*/ #ifdef Conly // // callbacks and void* don't work with CAMLIDL. // typedef Z3_bool Z3_reduce_eq_callback_fptr(Z3_theory t, Z3_ast a, Z3_ast b, Z3_ast * r); typedef Z3_bool Z3_reduce_app_callback_fptr(Z3_theory, Z3_func_decl, unsigned, Z3_ast const [], Z3_ast *); typedef Z3_bool Z3_reduce_distinct_callback_fptr(Z3_theory, unsigned, Z3_ast const [], Z3_ast *); typedef void Z3_theory_callback_fptr(Z3_theory t); typedef Z3_bool Z3_theory_final_check_callback_fptr(Z3_theory); typedef void Z3_theory_ast_callback_fptr(Z3_theory, Z3_ast); typedef void Z3_theory_ast_bool_callback_fptr(Z3_theory, Z3_ast, Z3_bool); typedef void Z3_theory_ast_ast_callback_fptr(Z3_theory, Z3_ast, Z3_ast); #endif #ifdef Conly /** \brief Create a new user defined theory. The new theory will be identified by the name \c th_name. A theory must be created before asserting any assertion to the given context. \conly Return \c NULL in case of failure. \conly \c data is a pointer to an external data-structure that may be used to store \conly theory specific additional data. */ Z3_theory Z3_API Z3_mk_theory(Z3_context c, Z3_string th_name, Z3_theory_data data); /** \brief Return a pointer to the external data-structure supplied to the function #Z3_mk_theory. \see Z3_mk_theory */ Z3_theory_data Z3_API Z3_theory_get_ext_data(Z3_theory t); #endif /** \brief Create an interpreted theory sort. */ Z3_sort Z3_API Z3_theory_mk_sort(Z3_context c, Z3_theory t, Z3_symbol s); /** \brief Create an interpreted theory constant value. Values are assumed to be different from each other. */ Z3_ast Z3_API Z3_theory_mk_value(Z3_context c, Z3_theory t, Z3_symbol n, Z3_sort s); /** \brief Create an interpreted constant for the given theory. */ Z3_ast Z3_API Z3_theory_mk_constant(Z3_context c, Z3_theory t, Z3_symbol n, Z3_sort s); /** \brief Create an interpreted function declaration for the given theory. */ Z3_func_decl Z3_API Z3_theory_mk_func_decl(Z3_context c, Z3_theory t, Z3_symbol n, unsigned domain_size, Z3_sort const domain[], Z3_sort range); /** \brief Return the context where the given theory is installed. */ Z3_context Z3_API Z3_theory_get_context(Z3_theory t); #ifdef Conly /** \brief Set a callback that is invoked when theory \c t is deleted. This callback should be used to delete external data-structures associated with the given theory. \conly The callback has the form f(t), where \conly - \c t is the given theory \see Z3_mk_theory \conly \see Z3_theory_get_ext_data */ void Z3_API Z3_set_delete_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback for simplifying operators of the given theory. The callback \c f is invoked by Z3's simplifier. \conly It is of the form f(t, d, n, args, r), where: \conly - \c t is the given theory \conly - \c d is the declaration of the theory operator \conly - \c n is the number of arguments in the array \c args \conly - \c args are arguments for the theory operator \conly - \c r should contain the result: an expression equivalent to d(args[0], ..., args[n-1]). \conly If f(t, d, n, args, r) returns false, then \c r is ignored, and Z3 assumes that no simplification was performed. */ void Z3_API Z3_set_reduce_app_callback(Z3_theory t, Z3_reduce_app_callback_fptr f); /** \brief Set a callback for simplifying the atom s_1 = s_2, when the sort of \c s_1 and \c s_2 is an interpreted sort of the given theory. The callback \c f is invoked by Z3's simplifier. \conly It has the form f(t, s_1, s_2, r), where: \conly - \c t is the given theory \conly - \c s_1 is the left-hand-side \conly - \c s_2 is the right-hand-side \conly - \c r should contain the result: an expression equivalent to s_1 = s_2. \conly If f(t, s_1, s_2, r) returns false, then \c r is ignored, and Z3 assumes that no simplification was performed. */ void Z3_API Z3_set_reduce_eq_callback(Z3_theory t, Z3_reduce_eq_callback_fptr f); /** \brief Set a callback for simplifying the atom distinct(s_1, ..., s_n), when the sort of \c s_1, ..., \c s_n is an interpreted sort of the given theory. The callback \c f is invoked by Z3's simplifier. \conly It has the form f(t, n, args, r), where: \conly - \c t is the given theory \conly - \c n is the number of arguments in the array \c args \conly - \c args are arguments for distinct. \conly - \c r should contain the result: an expression equivalent to distinct(s_1, ..., s_n). \conly If f(t, n, args, r) returns false, then \c r is ignored, and Z3 assumes that no simplification was performed. */ void Z3_API Z3_set_reduce_distinct_callback(Z3_theory t, Z3_reduce_distinct_callback_fptr f); /** \brief Set a callback that is invoked when a theory application is finally added into the logical context. Note that, not every application contained in an asserted expression is actually added into the logical context because it may be simplified during a preprocessing step. \conly The callback has the form f(t, n), where \conly - \c t is the given theory \conly - \c n is a theory application, that is, an expression of the form g(...) where \c g is a theory operator. \remark An expression \c n added to the logical context at search level \c n, will remain in the logical context until this level is backtracked. */ void Z3_API Z3_set_new_app_callback(Z3_theory t, Z3_theory_ast_callback_fptr f); /** \brief Set a callback that is invoked when an expression of sort \c s, where \c s is an interpreted sort of the theory \c t, is finally added into the logical context. Note that, not every expression contained in an asserted expression is actually added into the logical context because it may be simplified during a preprocessing step. \conly The callback has the form f(t, n), where \conly - \c t is the given theory \conly - \c n is an expression of sort \c s, where \c s is an interpreted sort of \c t. \remark An expression \c n added to the logical context at search level \c n, will remain in the logical context until this level is backtracked. */ void Z3_API Z3_set_new_elem_callback(Z3_theory t, Z3_theory_ast_callback_fptr f); /** \brief Set a callback that is invoked when Z3 starts searching for a satisfying assignment. \conly The callback has the form f(t), where \conly - \c t is the given theory */ void Z3_API Z3_set_init_search_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback that is invoked when Z3 creates a case-split (aka backtracking point). When a case-split is created we say the search level is increased. \conly The callback has the form f(t), where \conly - \c t is the given theory */ void Z3_API Z3_set_push_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback that is invoked when Z3 backtracks a case-split. When a case-split is backtracked we say the search level is decreased. \conly The callback has the form f(t), where \conly - \c t is the given theory */ void Z3_API Z3_set_pop_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback that is invoked when Z3 restarts the search for a satisfying assignment. \conly The callback has the form f(t), where \conly - \c t is the given theory */ void Z3_API Z3_set_restart_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback that is invoked when the logical context is reset by the user. This callback is useful for reseting any data-structure maintained by the user theory solver. \conly The callback has the form f(t), where \conly - \c t is the given theory */ void Z3_API Z3_set_reset_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback that is invoked before Z3 starts building a model. A theory may use this callback to perform expensive operations. \conly The callback has the form f(t), where \conly - \c t is the given theory If the theory returns \mlonly \c false, \endmlonly \conly \c Z3_false, Z3 will assume that theory is giving up, and it will assume that it was not possible to decide if the asserted constraints are satisfiable or not. */ void Z3_API Z3_set_final_check_callback(Z3_theory t, Z3_theory_final_check_callback_fptr f); /** \brief Set a callback that is invoked when an equality s_1 = s_2 is found by the logical context. \conly The callback has the form f(t, s_1, s_2), where: \conly - \c t is the given theory \conly - \c s_1 is the left-hand-side \conly - \c s_2 is the right-hand-side */ void Z3_API Z3_set_new_eq_callback(Z3_theory t, Z3_theory_ast_ast_callback_fptr f); /** \brief Set a callback that is invoked when a disequality s_1 != s_2 is found by the logical context. \conly The callback has the form f(t, s_1, s_2), where: \conly - \c t is the given theory \conly - \c s_1 is the left-hand-side \conly - \c s_2 is the right-hand-side */ void Z3_API Z3_set_new_diseq_callback(Z3_theory t, Z3_theory_ast_ast_callback_fptr f); /** \brief Set a callback that is invoked when a theory predicate is assigned to true/false by Z3. \conly The callback has the form f(t, p, v), where: \conly - \c t is the given theory \conly - \c p is the assigned predicate. \conly - \c v is the value (true/false) assigned to \c p. */ void Z3_API Z3_set_new_assignment_callback(Z3_theory t, Z3_theory_ast_bool_callback_fptr f); /** \brief Set a callback that is invoked when an expression is marked as relevant during the search. This callback is only invoked when relevancy propagation is enabled. \conly The callback has the form f(t, n), where: \conly - \c t is the given theory \conly - \c n is the relevant expression */ void Z3_API Z3_set_new_relevant_callback(Z3_theory t, Z3_theory_ast_callback_fptr f); #endif /** \brief Assert a theory axiom/lemmas during the search. An axiom added at search level \c n will remain in the logical context until level \c n is backtracked. The callbacks for push (#Z3_set_push_callback) and pop (#Z3_set_pop_callback) can be used to track when the search level is increased (i.e., new case-split) and decreased (i.e., case-split is backtracked). Z3 tracks the theory axioms asserted. So, multiple assertions of the same axiom are ignored. */ void Z3_API Z3_theory_assert_axiom(Z3_theory t, Z3_ast ax); /** \brief Inform to the logical context that \c lhs and \c rhs have the same interpretation in the model being built by theory \c t. If lhs = rhs is inconsistent with other theories, then the logical context will backtrack. For more information, see the paper "Model-Based Theory Combination" in the Z3 website. */ void Z3_API Z3_theory_assume_eq(Z3_theory t, Z3_ast lhs, Z3_ast rhs); /** \brief Enable/disable the simplification of theory axioms asserted using #Z3_theory_assert_axiom. By default, the simplification of theory specific operators is disabled. That is, the reduce theory callbacks are not invoked for theory axioms. The default behavior is useful when asserting axioms stating properties of theory operators. */ void Z3_API Z3_theory_enable_axiom_simplification(Z3_theory t, Z3_bool flag); /** \brief Return the root of the equivalence class containing \c n. */ Z3_ast Z3_API Z3_theory_get_eqc_root(Z3_theory t, Z3_ast n); /** \brief Return the next element in the equivalence class containing \c n. The elements in an equivalence class are organized in a circular list. You can traverse the list by calling this function multiple times using the result from the previous call. This is illustrated in the code snippet below. \code Z3_ast curr = n; do curr = Z3_theory_get_eqc_next(theory, curr); while (curr != n); \endcode */ Z3_ast Z3_API Z3_theory_get_eqc_next(Z3_theory t, Z3_ast n); /** \brief Return the number of parents of \c n that are operators of the given theory. */ unsigned Z3_API Z3_theory_get_num_parents(Z3_theory t, Z3_ast n); /** \brief Return the i-th parent of \c n. See #Z3_theory_get_num_parents. */ Z3_ast Z3_API Z3_theory_get_parent(Z3_theory t, Z3_ast n, unsigned i); /** \brief Return \c Z3_TRUE if \c n is an interpreted theory value. */ Z3_bool Z3_API Z3_theory_is_value(Z3_theory t, Z3_ast n); /** \brief Return \c Z3_TRUE if \c d is an interpreted theory declaration. */ Z3_bool Z3_API Z3_theory_is_decl(Z3_theory t, Z3_func_decl d); /** \brief Return the number of expressions of the given theory in the logical context. These are the expressions notified using the callback #Z3_set_new_elem_callback. */ unsigned Z3_API Z3_theory_get_num_elems(Z3_theory t); /** \brief Return the i-th elem of the given theory in the logical context. \see Z3_theory_get_num_elems */ Z3_ast Z3_API Z3_theory_get_elem(Z3_theory t, unsigned i); /** \brief Return the number of theory applications in the logical context. These are the expressions notified using the callback #Z3_set_new_app_callback. */ unsigned Z3_API Z3_theory_get_num_apps(Z3_theory t); /** \brief Return the i-th application of the given theory in the logical context. \see Z3_theory_get_num_apps */ Z3_ast Z3_API Z3_theory_get_app(Z3_theory t, unsigned i); /*@}*/ #endif #ifdef CorML4 /** @name Fixedpoint facilities */ /*@{*/ /** \brief Create a new fixedpoint context. \conly \remark User must use #Z3_fixedpoint_inc_ref and #Z3_fixedpoint_dec_ref to manage fixedpoint objects. \conly Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_fixedpoint', FIXEDPOINT, (_in(CONTEXT), )) */ Z3_fixedpoint Z3_API Z3_mk_fixedpoint(Z3_context c); #ifdef Conly /** \brief Increment the reference counter of the given fixedpoint context def_API('Z3_fixedpoint_inc_ref', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) */ void Z3_API Z3_fixedpoint_inc_ref(Z3_context c,Z3_fixedpoint d); /** \brief Decrement the reference counter of the given fixedpoint context. def_API('Z3_fixedpoint_dec_ref', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) */ void Z3_API Z3_fixedpoint_dec_ref(Z3_context c,Z3_fixedpoint d); #endif /** \brief Add a universal Horn clause as a named rule. The \c horn_rule should be of the form: \code horn_rule ::= (forall (bound-vars) horn_rule) | (=> atoms horn_rule) | atom \endcode def_API('Z3_fixedpoint_add_rule', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(SYMBOL))) */ void Z3_API Z3_fixedpoint_add_rule(Z3_context c,Z3_fixedpoint d, Z3_ast rule, Z3_symbol name); /** \brief Add a Database fact. \param c - context \param d - fixed point context \param r - relation signature for the row. \param num_args - number of columns for the given row. \param args - array of the row elements. The number of arguments \c num_args should be equal to the number of sorts in the domain of \c r. Each sort in the domain should be an integral (bit-vector, Boolean or or finite domain sort). The call has the same effect as adding a rule where \c r is applied to the arguments. def_API('Z3_fixedpoint_add_fact', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL), _in(UINT), _in_array(3, UINT))) */ void Z3_API Z3_fixedpoint_add_fact(Z3_context c,Z3_fixedpoint d, Z3_func_decl r, unsigned num_args, unsigned args[]); /** \brief Assert a constraint to the fixedpoint context. The constraints are used as background axioms when the fixedpoint engine uses the PDR mode. They are ignored for standard Datalog mode. def_API('Z3_fixedpoint_assert', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST))) */ void Z3_API Z3_fixedpoint_assert(Z3_context c,Z3_fixedpoint d, Z3_ast axiom); /** \brief Pose a query against the asserted rules. \code query ::= (exists (bound-vars) query) | literals \endcode query returns - Z3_L_FALSE if the query is unsatisfiable. - Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. - Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. def_API('Z3_fixedpoint_query', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST))) */ Z3_lbool Z3_API Z3_fixedpoint_query(Z3_context c,Z3_fixedpoint d, Z3_ast query); /** \brief Pose multiple queries against the asserted rules. The queries are encoded as relations (function declarations). query returns - Z3_L_FALSE if the query is unsatisfiable. - Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. - Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. def_API('Z3_fixedpoint_query_relations', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(UINT), _in_array(2, FUNC_DECL))) */ Z3_lbool Z3_API Z3_fixedpoint_query_relations( Z3_context c,Z3_fixedpoint d, unsigned num_relations, Z3_func_decl const relations[]); /** \brief Retrieve a formula that encodes satisfying answers to the query. When used in Datalog mode, the returned answer is a disjunction of conjuncts. Each conjunct encodes values of the bound variables of the query that are satisfied. In PDR mode, the returned answer is a single conjunction. When used in Datalog mode the previous call to Z3_fixedpoint_query must have returned Z3_L_TRUE. When used with the PDR engine, the previous call must have been either Z3_L_TRUE or Z3_L_FALSE. def_API('Z3_fixedpoint_get_answer', AST, (_in(CONTEXT), _in(FIXEDPOINT))) */ Z3_ast Z3_API Z3_fixedpoint_get_answer(Z3_context c,Z3_fixedpoint d); /** \brief Retrieve a string that describes the last status returned by #Z3_fixedpoint_query. Use this method when #Z3_fixedpoint_query returns Z3_L_UNDEF. def_API('Z3_fixedpoint_get_reason_unknown', STRING, (_in(CONTEXT), _in(FIXEDPOINT) )) */ Z3_string Z3_API Z3_fixedpoint_get_reason_unknown(Z3_context c,Z3_fixedpoint d); /** \brief Update a named rule. A rule with the same name must have been previously created. def_API('Z3_fixedpoint_update_rule', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(SYMBOL))) */ void Z3_API Z3_fixedpoint_update_rule(Z3_context c, Z3_fixedpoint d, Z3_ast a, Z3_symbol name); /** \brief Query the PDR engine for the maximal levels properties are known about predicate. This call retrieves the maximal number of relevant unfoldings of \c pred with respect to the current exploration state. Note: this functionality is PDR specific. def_API('Z3_fixedpoint_get_num_levels', UINT, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL))) */ unsigned Z3_API Z3_fixedpoint_get_num_levels(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred); /** Retrieve the current cover of \c pred up to \c level unfoldings. Return just the delta that is known at \c level. To obtain the full set of properties of \c pred one should query at \c level+1 , \c level+2 etc, and include \c level=-1. Note: this functionality is PDR specific. def_API('Z3_fixedpoint_get_cover_delta', AST, (_in(CONTEXT), _in(FIXEDPOINT), _in(INT), _in(FUNC_DECL))) */ Z3_ast Z3_API Z3_fixedpoint_get_cover_delta(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred); /** \brief Add property about the predicate \c pred. Add a property of predicate \c pred at \c level. It gets pushed forward when possible. Note: level = -1 is treated as the fixedpoint. So passing -1 for the \c level means that the property is true of the fixed-point unfolding with respect to \c pred. Note: this functionality is PDR specific. def_API('Z3_fixedpoint_add_cover', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(INT), _in(FUNC_DECL), _in(AST))) */ void Z3_API Z3_fixedpoint_add_cover(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred, Z3_ast property); /** \brief Retrieve statistics information from the last call to #Z3_fixedpoint_query. def_API('Z3_fixedpoint_get_statistics', STATS, (_in(CONTEXT), _in(FIXEDPOINT))) */ Z3_stats Z3_API Z3_fixedpoint_get_statistics(Z3_context c,Z3_fixedpoint d); /** \brief Register relation as Fixedpoint defined. Fixedpoint defined relations have least-fixedpoint semantics. For example, the relation is empty if it does not occur in a head or a fact. def_API('Z3_fixedpoint_register_relation', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL))) */ void Z3_API Z3_fixedpoint_register_relation(Z3_context c,Z3_fixedpoint d, Z3_func_decl f); /** \brief Configure the predicate representation. It sets the predicate to use a set of domains given by the list of symbols. The domains given by the list of symbols must belong to a set of built-in domains. def_API('Z3_fixedpoint_set_predicate_representation', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL), _in(UINT), _in_array(3, SYMBOL))) */ 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[]); /** \brief Retrieve set of rules from fixedpoint context. def_API('Z3_fixedpoint_get_rules', AST_VECTOR, (_in(CONTEXT),_in(FIXEDPOINT))) */ Z3_ast_vector Z3_API Z3_fixedpoint_get_rules( Z3_context c, Z3_fixedpoint f); /** \brief Retrieve set of background assertions from fixedpoint context. def_API('Z3_fixedpoint_get_assertions', AST_VECTOR, (_in(CONTEXT),_in(FIXEDPOINT))) */ Z3_ast_vector Z3_API Z3_fixedpoint_get_assertions( Z3_context c, Z3_fixedpoint f); /** \brief Set parameters on fixedpoint context. def_API('Z3_fixedpoint_set_params', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(PARAMS))) */ void Z3_API Z3_fixedpoint_set_params(Z3_context c, Z3_fixedpoint f, Z3_params p); /** \brief Return a string describing all fixedpoint available parameters. def_API('Z3_fixedpoint_get_help', STRING, (_in(CONTEXT), _in(FIXEDPOINT))) */ Z3_string Z3_API Z3_fixedpoint_get_help(Z3_context c, Z3_fixedpoint f); /** \brief Return the parameter description set for the given fixedpoint object. def_API('Z3_fixedpoint_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(FIXEDPOINT))) */ Z3_param_descrs Z3_API Z3_fixedpoint_get_param_descrs(Z3_context c, Z3_fixedpoint f); /** \brief Print the current rules and background axioms as a string. \param c - context. \param f - fixedpoint context. \param num_queries - number of additional queries to print. \param queries - additional queries. def_API('Z3_fixedpoint_to_string', STRING, (_in(CONTEXT), _in(FIXEDPOINT), _in(UINT), _in_array(2, AST))) */ Z3_string Z3_API Z3_fixedpoint_to_string( Z3_context c, Z3_fixedpoint f, unsigned num_queries, Z3_ast queries[]); /** \brief Parse an SMT-LIB2 string with fixedpoint rules. Add the rules to the current fixedpoint context. Return the set of queries in the string. \param c - context. \param f - fixedpoint context. \param s - string containing SMT2 specification. def_API('Z3_fixedpoint_from_string', AST_VECTOR, (_in(CONTEXT), _in(FIXEDPOINT), _in(STRING))) */ Z3_ast_vector Z3_API Z3_fixedpoint_from_string( Z3_context c, Z3_fixedpoint f, Z3_string s); /** \brief Parse an SMT-LIB2 file with fixedpoint rules. Add the rules to the current fixedpoint context. Return the set of queries in the file. \param c - context. \param f - fixedpoint context. \param s - string containing SMT2 specification. def_API('Z3_fixedpoint_from_file', AST_VECTOR, (_in(CONTEXT), _in(FIXEDPOINT), _in(STRING))) */ Z3_ast_vector Z3_API Z3_fixedpoint_from_file( Z3_context c, Z3_fixedpoint f, Z3_string s); /** \brief Create a backtracking point. The fixedpoint solver contains a set of rules, added facts and assertions. The set of rules, facts and assertions are restored upon calling #Z3_fixedpoint_pop. \sa Z3_fixedpoint_pop def_API('Z3_fixedpoint_push', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) */ void Z3_API Z3_fixedpoint_push(Z3_context c,Z3_fixedpoint d); /** \brief Backtrack one backtracking point. \sa Z3_fixedpoint_push \pre The number of calls to pop cannot exceed calls to push. def_API('Z3_fixedpoint_pop', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) */ void Z3_API Z3_fixedpoint_pop(Z3_context c,Z3_fixedpoint d); #ifdef Conly /** \brief The following utilities allows adding user-defined domains. */ typedef void Z3_fixedpoint_reduce_assign_callback_fptr( void*, Z3_func_decl, unsigned, Z3_ast const [], unsigned, Z3_ast const []); typedef void Z3_fixedpoint_reduce_app_callback_fptr( void*, Z3_func_decl, unsigned, Z3_ast const [], Z3_ast*); /** \brief Initialize the context with a user-defined state. */ void Z3_API Z3_fixedpoint_init(Z3_context c,Z3_fixedpoint d, void* state); /** \brief Register a callback to destructive updates. Registers are identified with terms encoded as fresh constants, */ void Z3_API Z3_fixedpoint_set_reduce_assign_callback( Z3_context c,Z3_fixedpoint d, Z3_fixedpoint_reduce_assign_callback_fptr cb); /** \brief Register a callback for buildling terms based on the relational operators. */ void Z3_API Z3_fixedpoint_set_reduce_app_callback( Z3_context c,Z3_fixedpoint d, Z3_fixedpoint_reduce_app_callback_fptr cb); #endif #endif #ifdef CorML4 /** @name Optimize facilities */ /*@{*/ /** \brief Create a new optimize context. \conly \remark User must use #Z3_optimize_inc_ref and #Z3_optimize_dec_ref to manage optimize objects. \conly Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_optimize', OPTIMIZE, (_in(CONTEXT), )) */ Z3_optimize Z3_API Z3_mk_optimize(Z3_context c); #ifdef Conly /** \brief Increment the reference counter of the given optimize context def_API('Z3_optimize_inc_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE))) */ void Z3_API Z3_optimize_inc_ref(Z3_context c,Z3_optimize d); /** \brief Decrement the reference counter of the given optimize context. def_API('Z3_optimize_dec_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE))) */ void Z3_API Z3_optimize_dec_ref(Z3_context c,Z3_optimize d); #endif /** \brief Assert hard constraint to the optimization context. def_API('Z3_optimize_assert', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) */ void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a); /** \brief Assert soft constraint to the optimization context. \param c - context \param o - optimization context \param a - formula \param weight - a positive weight, penalty for violating soft constraint \param id - optional identifier to group soft constraints def_API('Z3_optimize_assert_soft', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST), _in(STRING), _in(SYMBOL))) */ unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id); /** \brief Add a maximization constraint. \param c - context \param o - optimization context \param a - arithmetical term def_API('Z3_optimize_maximize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) */ unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t); /** \brief Add a minimization constraint. \param c - context \param o - optimization context \param a - arithmetical term def_API('Z3_optimize_minimize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) */ unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t); /** \brief Create a backtracking point. The optimize solver contains a set of rules, added facts and assertions. The set of rules, facts and assertions are restored upon calling #Z3_optimize_pop. \sa Z3_optimize_pop def_API('Z3_optimize_push', VOID, (_in(CONTEXT), _in(OPTIMIZE))) */ void Z3_API Z3_optimize_push(Z3_context c,Z3_optimize d); /** \brief Backtrack one level. \sa Z3_optimize_push \pre The number of calls to pop cannot exceed calls to push. def_API('Z3_optimize_pop', VOID, (_in(CONTEXT), _in(OPTIMIZE))) */ void Z3_API Z3_optimize_pop(Z3_context c,Z3_optimize d); /** \brief Check consistency and produce optimal values. \param c - context \param o - optimization context def_API('Z3_optimize_check', INT, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o); /** \brief Retrieve a string that describes the last status returned by #Z3_optimize_check. Use this method when #Z3_optimize_check returns Z3_L_UNDEF. def_API('Z3_optimize_get_reason_unknown', STRING, (_in(CONTEXT), _in(OPTIMIZE) )) */ Z3_string Z3_API Z3_optimize_get_reason_unknown(Z3_context c,Z3_optimize d); /** \brief Retrieve the model for the last #Z3_optimize_check The error handler is invoked if a model is not available because the commands above were not invoked for the given optimization solver, or if the result was \c Z3_L_FALSE. def_API('Z3_optimize_get_model', MODEL, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o); /** \brief Set parameters on optimization context. \param c - context \param o - optimization context \param p - parameters def_API('Z3_optimize_set_params', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(PARAMS))) */ void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p); /** \brief Return the parameter description set for the given optimize object. \param c - context \param o - optimization context def_API('Z3_optimize_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o); /** \brief Retrieve lower bound value or approximation for the i'th optimization objective. \param c - context \param o - optimization context \param idx - index of optimization objective def_API('Z3_optimize_get_lower', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) */ Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx); /** \brief Retrieve upper bound value or approximation for the i'th optimization objective. \param c - context \param o - optimization context \param idx - index of optimization objective def_API('Z3_optimize_get_upper', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) */ Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx); /** \brief Print the current context as a string. \param c - context. \param o - optimization context. def_API('Z3_optimize_to_string', STRING, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_string Z3_API Z3_optimize_to_string( Z3_context c, Z3_optimize o); /** \brief Return a string containing a description of parameters accepted by optimize. def_API('Z3_optimize_get_help', STRING, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_string Z3_API Z3_optimize_get_help(Z3_context c, Z3_optimize t); /** \brief Retrieve statistics information from the last call to #Z3_optimize_check def_API('Z3_optimize_get_statistics', STATS, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_stats Z3_API Z3_optimize_get_statistics(Z3_context c,Z3_optimize d); #endif #ifdef CorML4 /*@}*/ /** @name AST vectors */ /*@{*/ /** \brief Return an empty AST vector. \conly \remark Reference counting must be used to manage AST vectors, even when the Z3_context was \conly created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_ast_vector', AST_VECTOR, (_in(CONTEXT),)) */ Z3_ast_vector Z3_API Z3_mk_ast_vector(Z3_context c); #ifdef Conly /** \brief Increment the reference counter of the given AST vector. def_API('Z3_ast_vector_inc_ref', VOID, (_in(CONTEXT), _in(AST_VECTOR))) */ void Z3_API Z3_ast_vector_inc_ref(Z3_context c, Z3_ast_vector v); /** \brief Decrement the reference counter of the given AST vector. def_API('Z3_ast_vector_dec_ref', VOID, (_in(CONTEXT), _in(AST_VECTOR))) */ void Z3_API Z3_ast_vector_dec_ref(Z3_context c, Z3_ast_vector v); #endif /** \brief Return the size of the given AST vector. def_API('Z3_ast_vector_size', UINT, (_in(CONTEXT), _in(AST_VECTOR))) */ unsigned Z3_API Z3_ast_vector_size(Z3_context c, Z3_ast_vector v); /** \brief Return the AST at position \c i in the AST vector \c v. \pre i < Z3_ast_vector_size(c, v) def_API('Z3_ast_vector_get', AST, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT))) */ Z3_ast Z3_API Z3_ast_vector_get(Z3_context c, Z3_ast_vector v, unsigned i); /** \brief Update position \c i of the AST vector \c v with the AST \c a. \pre i < Z3_ast_vector_size(c, v) def_API('Z3_ast_vector_set', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT), _in(AST))) */ void Z3_API Z3_ast_vector_set(Z3_context c, Z3_ast_vector v, unsigned i, Z3_ast a); /** \brief Resize the AST vector \c v. def_API('Z3_ast_vector_resize', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT))) */ void Z3_API Z3_ast_vector_resize(Z3_context c, Z3_ast_vector v, unsigned n); /** \brief Add the AST \c a in the end of the AST vector \c v. The size of \c v is increased by one. def_API('Z3_ast_vector_push', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(AST))) */ void Z3_API Z3_ast_vector_push(Z3_context c, Z3_ast_vector v, Z3_ast a); /** \brief Translate the AST vector \c v from context \c s into an AST vector in context \c t. def_API('Z3_ast_vector_translate', AST_VECTOR, (_in(CONTEXT), _in(AST_VECTOR), _in(CONTEXT))) */ Z3_ast_vector Z3_API Z3_ast_vector_translate(Z3_context s, Z3_ast_vector v, Z3_context t); /** \brief Convert AST vector into a string. def_API('Z3_ast_vector_to_string', STRING, (_in(CONTEXT), _in(AST_VECTOR))) */ Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v); /*@}*/ /** @name AST maps */ /*@{*/ /** \brief Return an empty mapping from AST to AST \conly \remark Reference counting must be used to manage AST maps, even when the Z3_context was \conly created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_ast_map', AST_MAP, (_in(CONTEXT),) ) */ Z3_ast_map Z3_API Z3_mk_ast_map(Z3_context c); #ifdef Conly /** \brief Increment the reference counter of the given AST map. def_API('Z3_ast_map_inc_ref', VOID, (_in(CONTEXT), _in(AST_MAP))) */ void Z3_API Z3_ast_map_inc_ref(Z3_context c, Z3_ast_map m); /** \brief Decrement the reference counter of the given AST map. def_API('Z3_ast_map_dec_ref', VOID, (_in(CONTEXT), _in(AST_MAP))) */ void Z3_API Z3_ast_map_dec_ref(Z3_context c, Z3_ast_map m); #endif /** \brief Return true if the map \c m contains the AST key \c k. def_API('Z3_ast_map_contains', BOOL, (_in(CONTEXT), _in(AST_MAP), _in(AST))) */ Z3_bool Z3_API Z3_ast_map_contains(Z3_context c, Z3_ast_map m, Z3_ast k); /** \brief Return the value associated with the key \c k. The procedure invokes the error handler if \c k is not in the map. def_API('Z3_ast_map_find', AST, (_in(CONTEXT), _in(AST_MAP), _in(AST))) */ Z3_ast Z3_API Z3_ast_map_find(Z3_context c, Z3_ast_map m, Z3_ast k); /** \brief Store/Replace a new key, value pair in the given map. def_API('Z3_ast_map_insert', VOID, (_in(CONTEXT), _in(AST_MAP), _in(AST), _in(AST))) */ void Z3_API Z3_ast_map_insert(Z3_context c, Z3_ast_map m, Z3_ast k, Z3_ast v); /** \brief Erase a key from the map. def_API('Z3_ast_map_erase', VOID, (_in(CONTEXT), _in(AST_MAP), _in(AST))) */ void Z3_API Z3_ast_map_erase(Z3_context c, Z3_ast_map m, Z3_ast k); /** \brief Remove all keys from the given map. def_API('Z3_ast_map_reset', VOID, (_in(CONTEXT), _in(AST_MAP))) */ void Z3_API Z3_ast_map_reset(Z3_context c, Z3_ast_map m); /** \brief Return the size of the given map. def_API('Z3_ast_map_size', UINT, (_in(CONTEXT), _in(AST_MAP))) */ unsigned Z3_API Z3_ast_map_size(Z3_context c, Z3_ast_map m); /** \brief Return the keys stored in the given map. def_API('Z3_ast_map_keys', AST_VECTOR, (_in(CONTEXT), _in(AST_MAP))) */ Z3_ast_vector Z3_API Z3_ast_map_keys(Z3_context c, Z3_ast_map m); /** \brief Convert the given map into a string. def_API('Z3_ast_map_to_string', STRING, (_in(CONTEXT), _in(AST_MAP))) */ Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m); /*@}*/ /** @name Goals */ /*@{*/ /** \brief Create a goal (aka problem). A goal is essentially a set of formulas, that can be solved and/or transformed using tactics and solvers. If models == true, then model generation is enabled for the new goal. If unsat_cores == true, then unsat core generation is enabled for the new goal. If proofs == true, then proof generation is enabled for the new goal. Remark, the Z3 context c must have been created with proof generation support. \conly \remark Reference counting must be used to manage goals, even when the Z3_context was \conly created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_goal', GOAL, (_in(CONTEXT), _in(BOOL), _in(BOOL), _in(BOOL))) */ Z3_goal Z3_API Z3_mk_goal(Z3_context c, Z3_bool models, Z3_bool unsat_cores, Z3_bool proofs); #ifdef Conly /** \brief Increment the reference counter of the given goal. def_API('Z3_goal_inc_ref', VOID, (_in(CONTEXT), _in(GOAL))) */ void Z3_API Z3_goal_inc_ref(Z3_context c, Z3_goal g); /** \brief Decrement the reference counter of the given goal. def_API('Z3_goal_dec_ref', VOID, (_in(CONTEXT), _in(GOAL))) */ void Z3_API Z3_goal_dec_ref(Z3_context c, Z3_goal g); #endif /** \brief Return the "precision" of the given goal. Goals can be transformed using over and under approximations. A 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. def_API('Z3_goal_precision', UINT, (_in(CONTEXT), _in(GOAL))) */ Z3_goal_prec Z3_API Z3_goal_precision(Z3_context c, Z3_goal g); /** \brief Add a new formula \c a to the given goal. def_API('Z3_goal_assert', VOID, (_in(CONTEXT), _in(GOAL), _in(AST))) */ void Z3_API Z3_goal_assert(Z3_context c, Z3_goal g, Z3_ast a); /** \brief Return true if the given goal contains the formula \c false. def_API('Z3_goal_inconsistent', BOOL, (_in(CONTEXT), _in(GOAL))) */ Z3_bool Z3_API Z3_goal_inconsistent(Z3_context c, Z3_goal g); /** \brief Return the depth of the given goal. It tracks how many transformations were applied to it. def_API('Z3_goal_depth', UINT, (_in(CONTEXT), _in(GOAL))) */ unsigned Z3_API Z3_goal_depth(Z3_context c, Z3_goal g); /** \brief Erase all formulas from the given goal. def_API('Z3_goal_reset', VOID, (_in(CONTEXT), _in(GOAL))) */ void Z3_API Z3_goal_reset(Z3_context c, Z3_goal g); /** \brief Return the number of formulas in the given goal. def_API('Z3_goal_size', UINT, (_in(CONTEXT), _in(GOAL))) */ unsigned Z3_API Z3_goal_size(Z3_context c, Z3_goal g); /** \brief Return a formula from the given goal. \pre idx < Z3_goal_size(c, g) def_API('Z3_goal_formula', AST, (_in(CONTEXT), _in(GOAL), _in(UINT))) */ Z3_ast Z3_API Z3_goal_formula(Z3_context c, Z3_goal g, unsigned idx); /** \brief Return the number of formulas, subformulas and terms in the given goal. def_API('Z3_goal_num_exprs', UINT, (_in(CONTEXT), _in(GOAL))) */ unsigned Z3_API Z3_goal_num_exprs(Z3_context c, Z3_goal g); /** \brief Return true if the goal is empty, and it is precise or the product of a under approximation. def_API('Z3_goal_is_decided_sat', BOOL, (_in(CONTEXT), _in(GOAL))) */ Z3_bool Z3_API Z3_goal_is_decided_sat(Z3_context c, Z3_goal g); /** \brief Return true if the goal contains false, and it is precise or the product of an over approximation. def_API('Z3_goal_is_decided_unsat', BOOL, (_in(CONTEXT), _in(GOAL))) */ Z3_bool Z3_API Z3_goal_is_decided_unsat(Z3_context c, Z3_goal g); /** \brief Copy a goal \c g from the context \c source to a the context \c target. def_API('Z3_goal_translate', GOAL, (_in(CONTEXT), _in(GOAL), _in(CONTEXT))) */ Z3_goal Z3_API Z3_goal_translate(Z3_context source, Z3_goal g, Z3_context target); /** \brief Convert a goal into a string. def_API('Z3_goal_to_string', STRING, (_in(CONTEXT), _in(GOAL))) */ Z3_string Z3_API Z3_goal_to_string(Z3_context c, Z3_goal g); /*@}*/ /** @name Tactics and Probes */ /*@{*/ /** \brief Return a tactic associated with the given name. The complete list of tactics may be obtained using the procedures #Z3_get_num_tactics and #Z3_get_tactic_name. It may also be obtained using the command (help-tactics) in the SMT 2.0 front-end. Tactics are the basic building block for creating custom solvers for specific problem domains. def_API('Z3_mk_tactic', TACTIC, (_in(CONTEXT), _in(STRING))) */ Z3_tactic Z3_API Z3_mk_tactic(Z3_context c, Z3_string name); #ifdef Conly /** \brief Increment the reference counter of the given tactic. def_API('Z3_tactic_inc_ref', VOID, (_in(CONTEXT), _in(TACTIC))) */ void Z3_API Z3_tactic_inc_ref(Z3_context c, Z3_tactic t); /** \brief Decrement the reference counter of the given tactic. def_API('Z3_tactic_dec_ref', VOID, (_in(CONTEXT), _in(TACTIC))) */ void Z3_API Z3_tactic_dec_ref(Z3_context c, Z3_tactic g); #endif /** \brief Return a probe associated with the given name. The complete list of probes may be obtained using the procedures #Z3_get_num_probes and #Z3_get_probe_name. It may also be obtained using the command (help-tactics) in the SMT 2.0 front-end. 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. def_API('Z3_mk_probe', PROBE, (_in(CONTEXT), _in(STRING))) */ Z3_probe Z3_API Z3_mk_probe(Z3_context c, Z3_string name); #ifdef Conly /** \brief Increment the reference counter of the given probe. def_API('Z3_probe_inc_ref', VOID, (_in(CONTEXT), _in(PROBE))) */ void Z3_API Z3_probe_inc_ref(Z3_context c, Z3_probe p); /** \brief Decrement the reference counter of the given probe. def_API('Z3_probe_dec_ref', VOID, (_in(CONTEXT), _in(PROBE))) */ void Z3_API Z3_probe_dec_ref(Z3_context c, Z3_probe p); #endif /** \brief Return a tactic that applies \c t1 to a given goal and \c t2 to every subgoal produced by t1. def_API('Z3_tactic_and_then', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(TACTIC))) */ Z3_tactic Z3_API Z3_tactic_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that first applies \c t1 to a given goal, if it fails then returns the result of \c t2 applied to the given goal. def_API('Z3_tactic_or_else', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(TACTIC))) */ Z3_tactic Z3_API Z3_tactic_or_else(Z3_context c, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that applies the given tactics in parallel. def_API('Z3_tactic_par_or', TACTIC, (_in(CONTEXT), _in(UINT), _in_array(1, TACTIC))) */ Z3_tactic Z3_API Z3_tactic_par_or(Z3_context c, unsigned num, Z3_tactic const ts[]); /** \brief Return a tactic that applies \c t1 to a given goal and then \c t2 to every subgoal produced by t1. The subgoals are processed in parallel. def_API('Z3_tactic_par_and_then', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(TACTIC))) */ Z3_tactic Z3_API Z3_tactic_par_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that applies \c t to a given goal for \c ms milliseconds. If \c t does not terminate in \c ms milliseconds, then it fails. def_API('Z3_tactic_try_for', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(UINT))) */ Z3_tactic Z3_API Z3_tactic_try_for(Z3_context c, Z3_tactic t, unsigned ms); /** \brief Return a tactic that applies \c t to a given goal is the probe \c p evaluates to true. If \c p evaluates to false, then the new tactic behaves like the skip tactic. def_API('Z3_tactic_when', TACTIC, (_in(CONTEXT), _in(PROBE), _in(TACTIC))) */ Z3_tactic Z3_API Z3_tactic_when(Z3_context c, Z3_probe p, Z3_tactic t); /** \brief Return a tactic that applies \c t1 to a given goal if the probe \c p evaluates to true, and \c t2 if \c p evaluates to false. def_API('Z3_tactic_cond', TACTIC, (_in(CONTEXT), _in(PROBE), _in(TACTIC), _in(TACTIC))) */ Z3_tactic Z3_API Z3_tactic_cond(Z3_context c, Z3_probe p, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that keeps applying \c t until the goal is not modified anymore or the maximum number of iterations \c max is reached. def_API('Z3_tactic_repeat', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(UINT))) */ Z3_tactic Z3_API Z3_tactic_repeat(Z3_context c, Z3_tactic t, unsigned max); /** \brief Return a tactic that just return the given goal. def_API('Z3_tactic_skip', TACTIC, (_in(CONTEXT),)) */ Z3_tactic Z3_API Z3_tactic_skip(Z3_context c); /** \brief Return a tactic that always fails. def_API('Z3_tactic_fail', TACTIC, (_in(CONTEXT),)) */ Z3_tactic Z3_API Z3_tactic_fail(Z3_context c); /** \brief Return a tactic that fails if the probe \c p evaluates to false. def_API('Z3_tactic_fail_if', TACTIC, (_in(CONTEXT), _in(PROBE))) */ Z3_tactic Z3_API Z3_tactic_fail_if(Z3_context c, Z3_probe p); /** \brief Return a tactic that fails if the goal is not trivially satisfiable (i.e., empty) or trivially unsatisfiable (i.e., contains false). def_API('Z3_tactic_fail_if_not_decided', TACTIC, (_in(CONTEXT),)) */ Z3_tactic Z3_API Z3_tactic_fail_if_not_decided(Z3_context c); /** \brief Return a tactic that applies \c t using the given set of parameters. def_API('Z3_tactic_using_params', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(PARAMS))) */ Z3_tactic Z3_API Z3_tactic_using_params(Z3_context c, Z3_tactic t, Z3_params p); /** \brief Return a probe that always evaluates to val. def_API('Z3_probe_const', PROBE, (_in(CONTEXT), _in(DOUBLE))) */ Z3_probe Z3_API Z3_probe_const(Z3_context x, double val); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is less than the value returned by \c p2. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_lt', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_lt(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is greater than the value returned by \c p2. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_gt', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_gt(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is less than or equal to the value returned by \c p2. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_le', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_le(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is greater than or equal to the value returned by \c p2. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_ge', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_ge(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is equal to the value returned by \c p2. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_eq', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_eq(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when \c p1 and \c p2 evaluates to true. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_and', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_and(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when \c p1 or \c p2 evaluates to true. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_or', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_or(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when \c p does not evaluate to true. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_not', PROBE, (_in(CONTEXT), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_not(Z3_context x, Z3_probe p); /** \brief Return the number of builtin tactics available in Z3. def_API('Z3_get_num_tactics', UINT, (_in(CONTEXT),)) */ unsigned Z3_API Z3_get_num_tactics(Z3_context c); /** \brief Return the name of the idx tactic. \pre i < Z3_get_num_tactics(c) def_API('Z3_get_tactic_name', STRING, (_in(CONTEXT), _in(UINT))) */ Z3_string Z3_API Z3_get_tactic_name(Z3_context c, unsigned i); /** \brief Return the number of builtin probes available in Z3. def_API('Z3_get_num_probes', UINT, (_in(CONTEXT),)) */ unsigned Z3_API Z3_get_num_probes(Z3_context c); /** \brief Return the name of the i probe. \pre i < Z3_get_num_probes(c) def_API('Z3_get_probe_name', STRING, (_in(CONTEXT), _in(UINT))) */ Z3_string Z3_API Z3_get_probe_name(Z3_context c, unsigned i); /** \brief Return a string containing a description of parameters accepted by the given tactic. def_API('Z3_tactic_get_help', STRING, (_in(CONTEXT), _in(TACTIC))) */ Z3_string Z3_API Z3_tactic_get_help(Z3_context c, Z3_tactic t); /** \brief Return the parameter description set for the given tactic object. def_API('Z3_tactic_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(TACTIC))) */ Z3_param_descrs Z3_API Z3_tactic_get_param_descrs(Z3_context c, Z3_tactic t); /** \brief Return a string containing a description of the tactic with the given name. def_API('Z3_tactic_get_descr', STRING, (_in(CONTEXT), _in(STRING))) */ Z3_string Z3_API Z3_tactic_get_descr(Z3_context c, Z3_string name); /** \brief Return a string containing a description of the probe with the given name. def_API('Z3_probe_get_descr', STRING, (_in(CONTEXT), _in(STRING))) */ Z3_string Z3_API Z3_probe_get_descr(Z3_context c, Z3_string name); /** \brief Execute the probe over the goal. The probe always produce a double value. "Boolean" probes return 0.0 for false, and a value different from 0.0 for true. def_API('Z3_probe_apply', DOUBLE, (_in(CONTEXT), _in(PROBE), _in(GOAL))) */ double Z3_API Z3_probe_apply(Z3_context c, Z3_probe p, Z3_goal g); /** \brief Apply tactic \c t to the goal \c g. def_API('Z3_tactic_apply', APPLY_RESULT, (_in(CONTEXT), _in(TACTIC), _in(GOAL))) */ Z3_apply_result Z3_API Z3_tactic_apply(Z3_context c, Z3_tactic t, Z3_goal g); /** \brief Apply tactic \c t to the goal \c g using the parameter set \c p. def_API('Z3_tactic_apply_ex', APPLY_RESULT, (_in(CONTEXT), _in(TACTIC), _in(GOAL), _in(PARAMS))) */ Z3_apply_result Z3_API Z3_tactic_apply_ex(Z3_context c, Z3_tactic t, Z3_goal g, Z3_params p); #ifdef CorML3 /** \brief Increment the reference counter of the given \c Z3_apply_result object. def_API('Z3_apply_result_inc_ref', VOID, (_in(CONTEXT), _in(APPLY_RESULT))) */ void Z3_API Z3_apply_result_inc_ref(Z3_context c, Z3_apply_result r); /** \brief Decrement the reference counter of the given \c Z3_apply_result object. def_API('Z3_apply_result_dec_ref', VOID, (_in(CONTEXT), _in(APPLY_RESULT))) */ void Z3_API Z3_apply_result_dec_ref(Z3_context c, Z3_apply_result r); #endif /** \brief Convert the \c Z3_apply_result object returned by #Z3_tactic_apply into a string. def_API('Z3_apply_result_to_string', STRING, (_in(CONTEXT), _in(APPLY_RESULT))) */ Z3_string Z3_API Z3_apply_result_to_string(Z3_context c, Z3_apply_result r); /** \brief Return the number of subgoals in the \c Z3_apply_result object returned by #Z3_tactic_apply. def_API('Z3_apply_result_get_num_subgoals', UINT, (_in(CONTEXT), _in(APPLY_RESULT))) */ unsigned Z3_API Z3_apply_result_get_num_subgoals(Z3_context c, Z3_apply_result r); /** \brief Return one of the subgoals in the \c Z3_apply_result object returned by #Z3_tactic_apply. \pre i < Z3_apply_result_get_num_subgoals(c, r) def_API('Z3_apply_result_get_subgoal', GOAL, (_in(CONTEXT), _in(APPLY_RESULT), _in(UINT))) */ Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i); /** \brief Convert a model for the subgoal \c Z3_apply_result_get_subgoal(c, r, i) into a model for the original goal \c g. Where \c g is the goal used to create \c r using \c Z3_tactic_apply(c, t, g). def_API('Z3_apply_result_convert_model', MODEL, (_in(CONTEXT), _in(APPLY_RESULT), _in(UINT), _in(MODEL))) */ Z3_model Z3_API Z3_apply_result_convert_model(Z3_context c, Z3_apply_result r, unsigned i, Z3_model m); /*@}*/ /** @name Solvers */ /*@{*/ /** \brief Create 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. \conly \remark User must use #Z3_solver_inc_ref and #Z3_solver_dec_ref to manage solver objects. \conly Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_solver', SOLVER, (_in(CONTEXT),)) */ Z3_solver Z3_API Z3_mk_solver(Z3_context c); /** \brief Create a new (incremental) solver. The function #Z3_solver_get_model retrieves a model if the assertions is satisfiable (i.e., the result is \c Z3_L_TRUE) and model construction is enabled. The function #Z3_solver_get_model can also be used even if the result is \c Z3_L_UNDEF, but the returned model is not guaranteed to satisfy quantified assertions. def_API('Z3_mk_simple_solver', SOLVER, (_in(CONTEXT),)) */ Z3_solver Z3_API Z3_mk_simple_solver(Z3_context c); /** \brief Create a new solver customized for the given logic. It behaves like #Z3_mk_solver if the logic is unknown or unsupported. \conly \remark User must use #Z3_solver_inc_ref and #Z3_solver_dec_ref to manage solver objects. \conly Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_solver_for_logic', SOLVER, (_in(CONTEXT), _in(SYMBOL))) */ Z3_solver Z3_API Z3_mk_solver_for_logic(Z3_context c, Z3_symbol logic); /** \brief Create a new solver that is implemented using the given tactic. The solver supports the commands #Z3_solver_push and #Z3_solver_pop, but it will always solve each #Z3_solver_check from scratch. def_API('Z3_mk_solver_from_tactic', SOLVER, (_in(CONTEXT), _in(TACTIC))) */ Z3_solver Z3_API Z3_mk_solver_from_tactic(Z3_context c, Z3_tactic t); /** \brief Return a string describing all solver available parameters. def_API('Z3_solver_get_help', STRING, (_in(CONTEXT), _in(SOLVER))) */ Z3_string Z3_API Z3_solver_get_help(Z3_context c, Z3_solver s); /** \brief Return the parameter description set for the given solver object. def_API('Z3_solver_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(SOLVER))) */ Z3_param_descrs Z3_API Z3_solver_get_param_descrs(Z3_context c, Z3_solver s); /** \brief Set the given solver using the given parameters. def_API('Z3_solver_set_params', VOID, (_in(CONTEXT), _in(SOLVER), _in(PARAMS))) */ void Z3_API Z3_solver_set_params(Z3_context c, Z3_solver s, Z3_params p); #ifdef Conly /** \brief Increment the reference counter of the given solver. def_API('Z3_solver_inc_ref', VOID, (_in(CONTEXT), _in(SOLVER))) */ void Z3_API Z3_solver_inc_ref(Z3_context c, Z3_solver s); /** \brief Decrement the reference counter of the given solver. def_API('Z3_solver_dec_ref', VOID, (_in(CONTEXT), _in(SOLVER))) */ void Z3_API Z3_solver_dec_ref(Z3_context c, Z3_solver s); #endif /** \brief Create a backtracking point. The solver contains a stack of assertions. \sa Z3_solver_pop def_API('Z3_solver_push', VOID, (_in(CONTEXT), _in(SOLVER))) */ void Z3_API Z3_solver_push(Z3_context c, Z3_solver s); /** \brief Backtrack \c n backtracking points. \sa Z3_solver_push \pre n <= Z3_solver_get_num_scopes(c, s) def_API('Z3_solver_pop', VOID, (_in(CONTEXT), _in(SOLVER), _in(UINT))) */ void Z3_API Z3_solver_pop(Z3_context c, Z3_solver s, unsigned n); /** \brief Remove all assertions from the solver. def_API('Z3_solver_reset', VOID, (_in(CONTEXT), _in(SOLVER))) */ void Z3_API Z3_solver_reset(Z3_context c, Z3_solver s); /** \brief Return the number of backtracking points. \sa Z3_solver_push \sa Z3_solver_pop def_API('Z3_solver_get_num_scopes', UINT, (_in(CONTEXT), _in(SOLVER))) */ unsigned Z3_API Z3_solver_get_num_scopes(Z3_context c, Z3_solver s); /** \brief Assert a constraint into the solver. The functions #Z3_solver_check and #Z3_solver_check_assumptions should be used to check whether the logical context is consistent or not. def_API('Z3_solver_assert', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST))) */ void Z3_API Z3_solver_assert(Z3_context c, Z3_solver s, Z3_ast a); /** \brief Assert a constraint \c a into the solver, and track it (in the unsat) core using the Boolean constant \c p. This API is an alternative to #Z3_solver_check_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 Z3_solver_assert_and_track and the Boolean literals provided using #Z3_solver_check_assumptions. \pre \c a must be a Boolean expression \pre \c p must be a Boolean constant (aka variable). def_API('Z3_solver_assert_and_track', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST), _in(AST))) */ void Z3_API Z3_solver_assert_and_track(Z3_context c, Z3_solver s, Z3_ast a, Z3_ast p); /** \brief Return the set of asserted formulas as a goal object. def_API('Z3_solver_get_assertions', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) */ Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s); /** \brief Check whether the assertions in a given solver are consistent or not. The function #Z3_solver_get_model retrieves a model if the assertions is satisfiable (i.e., the result is \c Z3_L_TRUE) and model construction is enabled. Note that if the call returns Z3_L_UNDEF, Z3 does not ensure that calls to #Z3_solver_get_model succeed and any models produced in this case are not guaranteed to satisfy the assertions. The function #Z3_solver_get_proof retrieves a proof if proof generation was enabled when the context was created, and the assertions are unsatisfiable (i.e., the result is \c Z3_L_FALSE). def_API('Z3_solver_check', INT, (_in(CONTEXT), _in(SOLVER))) */ Z3_lbool Z3_API Z3_solver_check(Z3_context c, Z3_solver s); /** \brief Check whether the assertions in the given solver and optional assumptions are consistent or not. The function #Z3_solver_get_unsat_core retrieves the subset of the assumptions used in the unsatisfiability proof produced by Z3. \sa Z3_solver_check def_API('Z3_solver_check_assumptions', INT, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST))) */ Z3_lbool Z3_API Z3_solver_check_assumptions(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]); /** \brief Retrieve the model for the last #Z3_solver_check or #Z3_solver_check_assumptions The error handler is invoked if a model is not available because the commands above were not invoked for the given solver, or if the result was \c Z3_L_FALSE. def_API('Z3_solver_get_model', MODEL, (_in(CONTEXT), _in(SOLVER))) */ Z3_model Z3_API Z3_solver_get_model(Z3_context c, Z3_solver s); /** \brief Retrieve the proof for the last #Z3_solver_check or #Z3_solver_check_assumptions The error handler is invoked if proof generation is not enabled, or if the commands above were not invoked for the given solver, or if the result was different from \c Z3_L_FALSE. def_API('Z3_solver_get_proof', AST, (_in(CONTEXT), _in(SOLVER))) */ Z3_ast Z3_API Z3_solver_get_proof(Z3_context c, Z3_solver s); /** \brief Retrieve the unsat core for the last #Z3_solver_check_assumptions The unsat core is a subset of the assumptions \c a. def_API('Z3_solver_get_unsat_core', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) */ Z3_ast_vector Z3_API Z3_solver_get_unsat_core(Z3_context c, Z3_solver s); /** \brief Return a brief justification for an "unknown" result (i.e., Z3_L_UNDEF) for the commands #Z3_solver_check and #Z3_solver_check_assumptions def_API('Z3_solver_get_reason_unknown', STRING, (_in(CONTEXT), _in(SOLVER))) */ Z3_string Z3_API Z3_solver_get_reason_unknown(Z3_context c, Z3_solver s); /** \brief Return statistics for the given solver. \conly \remark User must use #Z3_stats_inc_ref and #Z3_stats_dec_ref to manage Z3_stats objects. def_API('Z3_solver_get_statistics', STATS, (_in(CONTEXT), _in(SOLVER))) */ Z3_stats Z3_API Z3_solver_get_statistics(Z3_context c, Z3_solver s); /** \brief Convert a solver into a string. def_API('Z3_solver_to_string', STRING, (_in(CONTEXT), _in(SOLVER))) */ Z3_string Z3_API Z3_solver_to_string(Z3_context c, Z3_solver s); /*@}*/ /** @name Statistics */ /*@{*/ #ifdef ML4only #include #endif /** \brief Convert a statistics into a string. def_API('Z3_stats_to_string', STRING, (_in(CONTEXT), _in(STATS))) */ Z3_string Z3_API Z3_stats_to_string(Z3_context c, Z3_stats s); /** \mlonly {4 {L Low-level API}} \endmlonly */ #ifdef Conly /** \brief Increment the reference counter of the given statistics object. def_API('Z3_stats_inc_ref', VOID, (_in(CONTEXT), _in(STATS))) */ void Z3_API Z3_stats_inc_ref(Z3_context c, Z3_stats s); /** \brief Decrement the reference counter of the given statistics object. def_API('Z3_stats_dec_ref', VOID, (_in(CONTEXT), _in(STATS))) */ void Z3_API Z3_stats_dec_ref(Z3_context c, Z3_stats s); #endif /** \brief Return the number of statistical data in \c s. def_API('Z3_stats_size', UINT, (_in(CONTEXT), _in(STATS))) */ unsigned Z3_API Z3_stats_size(Z3_context c, Z3_stats s); /** \brief Return the key (a string) for a particular statistical data. \pre idx < Z3_stats_size(c, s) def_API('Z3_stats_get_key', STRING, (_in(CONTEXT), _in(STATS), _in(UINT))) */ Z3_string Z3_API Z3_stats_get_key(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return Z3_TRUE if the given statistical data is a unsigned integer. \pre idx < Z3_stats_size(c, s) def_API('Z3_stats_is_uint', BOOL, (_in(CONTEXT), _in(STATS), _in(UINT))) */ Z3_bool Z3_API Z3_stats_is_uint(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return Z3_TRUE if the given statistical data is a double. \pre idx < Z3_stats_size(c, s) def_API('Z3_stats_is_double', BOOL, (_in(CONTEXT), _in(STATS), _in(UINT))) */ Z3_bool Z3_API Z3_stats_is_double(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return the unsigned value of the given statistical data. \pre idx < Z3_stats_size(c, s) && Z3_stats_is_uint(c, s) def_API('Z3_stats_get_uint_value', UINT, (_in(CONTEXT), _in(STATS), _in(UINT))) */ unsigned Z3_API Z3_stats_get_uint_value(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return the double value of the given statistical data. \pre idx < Z3_stats_size(c, s) && Z3_stats_is_double(c, s) def_API('Z3_stats_get_double_value', DOUBLE, (_in(CONTEXT), _in(STATS), _in(UINT))) */ double Z3_API Z3_stats_get_double_value(Z3_context c, Z3_stats s, unsigned idx); /*@}*/ #endif #ifdef CorML3 /** @name Deprecated Injective functions API */ /*@{*/ /** \brief Create injective function declaration \deprecated This method just asserts a (universally quantified) formula that asserts that the new function is injective. It is compatible with the old interface for solving: #Z3_assert_cnstr, #Z3_check_assumptions, etc. def_API('Z3_mk_injective_function', FUNC_DECL, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT), _in(SORT))) */ 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 ); /*@}*/ #endif /** @name Deprecated Constraints API */ /*@{*/ #ifdef CorML3 /** \brief Set the SMTLIB logic to be used in the given logical context. It is incorrect to invoke this function after invoking #Z3_check, #Z3_check_and_get_model, #Z3_check_assumptions and #Z3_push. Return \c Z3_TRUE if the logic was changed successfully, and \c Z3_FALSE otherwise. \deprecated Subsumed by #Z3_mk_solver_for_logic def_API('Z3_set_logic', VOID, (_in(CONTEXT), _in(STRING))) */ Z3_bool Z3_API Z3_set_logic(Z3_context c, Z3_string logic); /** \brief Create a backtracking point. The logical context can be viewed as a stack of contexts. The scope level is the number of elements on this stack. The stack of contexts is simulated using trail (undo) stacks. \sa Z3_pop \deprecated Subsumed by #Z3_solver_push def_API('Z3_push', VOID, (_in(CONTEXT),)) */ void Z3_API Z3_push(Z3_context c); /** \brief Backtrack. Restores the context from the top of the stack, and pops it off the stack. Any changes to the logical context (by #Z3_assert_cnstr or other functions) between the matching #Z3_push and \c Z3_pop operators are flushed, and the context is completely restored to what it was right before the #Z3_push. \sa Z3_push \deprecated Subsumed by #Z3_solver_pop def_API('Z3_pop', VOID, (_in(CONTEXT), _in(UINT))) */ void Z3_API Z3_pop(Z3_context c, unsigned num_scopes); /** \brief Retrieve the current scope level. It retrieves the number of scopes that have been pushed, but not yet popped. \sa Z3_push \sa Z3_pop \deprecated Subsumed by #Z3_solver_get_num_scopes. def_API('Z3_get_num_scopes', UINT, (_in(CONTEXT),)) */ unsigned Z3_API Z3_get_num_scopes(Z3_context c); /** \conly \brief Persist AST through num_scopes pops. \conly This function is only relevant if \c c was created using #Z3_mk_context. \conly If \c c was created using #Z3_mk_context_rc, this function is a NOOP. \conly Normally, for contexts created using #Z3_mk_context, \conly references to terms are no longer valid when \conly popping scopes beyond the level where the terms are created. \conly If you want to reference a term below the scope where it \conly was created, use this method to specify how many pops \conly the term should survive. \conly The num_scopes should be at most equal to the number of \conly calls to Z3_push subtracted with the calls to Z3_pop. \conly \deprecated Z3 users should move to #Z3_mk_context_rc and use the \conly reference counting APIs for managing AST nodes. \mlonly \deprecated This function has no effect. \endmlonly def_API('Z3_persist_ast', VOID, (_in(CONTEXT), _in(AST), _in(UINT))) */ void Z3_API Z3_persist_ast(Z3_context c, Z3_ast a, unsigned num_scopes); /** \brief Assert a constraint into the logical context. After one assertion, the logical context may become inconsistent. The functions #Z3_check or #Z3_check_and_get_model should be used to check whether the logical context is consistent or not. \sa Z3_check \sa Z3_check_and_get_model \deprecated Subsumed by #Z3_solver_assert def_API('Z3_assert_cnstr', VOID, (_in(CONTEXT), _in(AST))) */ void Z3_API Z3_assert_cnstr(Z3_context c, Z3_ast a); /** \brief Check whether the given logical context is consistent or not. If the logical context is not unsatisfiable (i.e., the return value is different from \c Z3_L_FALSE) and model construction is enabled (see #Z3_mk_config), \conly then a model is stored in \c m. Otherwise, the value \c NULL is stored in \c m. \mlonly then a valid model is returned. Otherwise, it is unsafe to use the returned model.\endmlonly \conly The caller is responsible for deleting the model using the function #Z3_del_model. \conly \remark In contrast with the rest of the Z3 API, the reference counter of the \conly model is incremented. This is to guarantee backward compatibility. In previous \conly versions, models did not support reference counting. \remark Model construction must be enabled using configuration parameters (See, #Z3_mk_config). \sa Z3_check \conly \sa Z3_del_model \deprecated Subsumed by #Z3_solver_check def_API('Z3_check_and_get_model', INT, (_in(CONTEXT), _out(MODEL))) */ Z3_lbool Z3_API Z3_check_and_get_model(Z3_context c, Z3_model * m); /** \brief Check whether the given logical context is consistent or not. The function #Z3_check_and_get_model should be used when models are needed. \sa Z3_check_and_get_model \deprecated Subsumed by #Z3_solver_check def_API('Z3_check', INT, (_in(CONTEXT),)) */ Z3_lbool Z3_API Z3_check(Z3_context c); /** \brief Check whether the given logical context and optional assumptions is consistent or not. If the logical context is not unsatisfiable (i.e., the return value is different from \c Z3_L_FALSE), \conly a non-NULL model argument is passed in, and model construction is enabled (see #Z3_mk_config), \conly then a model is stored in \c m. Otherwise, \c m is left unchanged. \mlonly then a valid model is returned. Otherwise, it is unsafe to use the returned model.\endmlonly \conly The caller is responsible for deleting the model using the function #Z3_del_model. \conly \remark If the model argument is non-NULL, then model construction must be enabled using configuration \conly parameters (See, #Z3_mk_config). \param c logical context. \param num_assumptions number of auxiliary assumptions. \param assumptions array of auxiliary assumptions \param m optional pointer to a model. \param proof optional pointer to a proof term. \param core_size size of unsatisfiable core. \param core pointer to an array receiving unsatisfiable core. The unsatisfiable core is a subset of the assumptions, so the array has the same size as the assumptions. The \c core array is not populated if \c core_size is set to 0. \pre assumptions comprises of propositional literals. In other words, you cannot use compound formulas for assumptions, but should use propositional variables or negations of propositional variables. \conly \remark In constrast with the rest of the Z3 API, the reference counter of the \conly model is incremented. This is to guarantee backward compatibility. In previous \conly versions, models did not support reference counting. \sa Z3_check \conly \sa Z3_del_model \deprecated Subsumed by #Z3_solver_check_assumptions def_API('Z3_check_assumptions', INT, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _out(MODEL), _out(AST), _out(UINT), _out_array2(1, 5, AST))) */ Z3_lbool Z3_API Z3_check_assumptions( Z3_context c, unsigned num_assumptions, Z3_ast const assumptions[], Z3_model * m, Z3_ast* proof, unsigned* core_size, Z3_ast core[] ); #endif #ifdef CorML4 /** \brief Retrieve congruence class representatives for terms. The function can be used for relying on Z3 to identify equal terms under the current set of assumptions. The array of terms and array of class identifiers should have the same length. The class identifiers are numerals that are assigned to the same value for their corresponding terms if the current context forces the terms to be equal. You cannot deduce that terms corresponding to different numerals must be all different, (especially when using non-convex theories). All implied equalities are returned by this call. This means that two terms map to the same class identifier if and only if the current context implies that they are equal. A side-effect of the function is a satisfiability check on the assertions on the solver that is passed in. The function return Z3_L_FALSE if the current assertions are not satisfiable. \sa Z3_check_and_get_model \sa Z3_check \deprecated To be moved outside of API. def_API('Z3_get_implied_equalities', UINT, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST), _out_array(2, UINT))) */ Z3_lbool Z3_API Z3_get_implied_equalities( Z3_context c, Z3_solver s, unsigned num_terms, Z3_ast const terms[], unsigned class_ids[] ); #endif #ifdef CorML3 /** \brief Delete a model object. \sa Z3_check_and_get_model \conly \remark The Z3_check_and_get_model automatically increments a reference count on the model. \conly The expected usage is that models created by that method are deleted using Z3_del_model. \conly This is for backwards compatibility and in contrast to the rest of the API where \conly callers are responsible for managing reference counts. \deprecated Subsumed by Z3_solver API def_API('Z3_del_model', VOID, (_in(CONTEXT), _in(MODEL))) */ void Z3_API Z3_del_model(Z3_context c, Z3_model m); /*@}*/ /** @name Deprecated Search control API */ /*@{*/ /** \brief Cancel an ongoing check. Notifies the current check to abort and return. This method should be called from a different thread than the one performing the check. \deprecated Use #Z3_interrupt instead. def_API('Z3_soft_check_cancel', VOID, (_in(CONTEXT), )) */ void Z3_API Z3_soft_check_cancel(Z3_context c); /** \brief Retrieve reason for search failure. If a call to #Z3_check or #Z3_check_and_get_model returns Z3_L_UNDEF, use this facility to determine the more detailed cause of search failure. \deprecated Subsumed by #Z3_solver_get_reason_unknown def_API('Z3_get_search_failure', UINT, (_in(CONTEXT), )) */ Z3_search_failure Z3_API Z3_get_search_failure(Z3_context c); /*@}*/ /** @name Deprecated Labels API */ /*@{*/ /** \brief Create a labeled formula. \param c logical context. \param s name of the label. \param is_pos label polarity. \param f formula being labeled. A label behaves as an identity function, so the truth value of the labeled formula is unchanged. Labels are used for identifying useful sub-formulas when generating counter-examples. \deprecated Labels are only supported by the old Solver API. This feature is not essential (it can be simulated using auxiliary Boolean variables). It is only available for backward compatibility. def_API('Z3_mk_label', AST, (_in(CONTEXT), _in(SYMBOL), _in(BOOL), _in(AST))) */ Z3_ast Z3_API Z3_mk_label(Z3_context c, Z3_symbol s, Z3_bool is_pos, Z3_ast f); /** \brief Retrieve the set of labels that were relevant in the context of the current satisfied context. \sa Z3_del_literals \sa Z3_get_num_literals \sa Z3_get_label_symbol \sa Z3_get_literal \deprecated This procedure is based on the old Solver API. def_API('Z3_get_relevant_labels', LITERALS, (_in(CONTEXT), )) */ Z3_literals Z3_API Z3_get_relevant_labels(Z3_context c); /** \brief Retrieve the set of literals that satisfy the current context. \sa Z3_del_literals \sa Z3_get_num_literals \sa Z3_get_label_symbol \sa Z3_get_literal \deprecated This procedure is based on the old Solver API. def_API('Z3_get_relevant_literals', LITERALS, (_in(CONTEXT), )) */ Z3_literals Z3_API Z3_get_relevant_literals(Z3_context c); /** \brief Retrieve the set of literals that whose assignment were guess, but not propagated during the search. \sa Z3_del_literals \sa Z3_get_num_literals \sa Z3_get_label_symbol \sa Z3_get_literal \deprecated This procedure is based on the old Solver API. def_API('Z3_get_guessed_literals', LITERALS, (_in(CONTEXT), )) */ Z3_literals Z3_API Z3_get_guessed_literals(Z3_context c); /** \brief Delete a labels context. \sa Z3_get_relevant_labels \deprecated This procedure is based on the old Solver API. def_API('Z3_del_literals', VOID, (_in(CONTEXT), _in(LITERALS))) */ void Z3_API Z3_del_literals(Z3_context c, Z3_literals lbls); /** \brief Retrieve the number of label symbols that were returned. \sa Z3_get_relevant_labels \deprecated This procedure is based on the old Solver API. def_API('Z3_get_num_literals', UINT, (_in(CONTEXT), _in(LITERALS))) */ unsigned Z3_API Z3_get_num_literals(Z3_context c, Z3_literals lbls); /** \brief Retrieve label symbol at idx. \deprecated This procedure is based on the old Solver API. def_API('Z3_get_label_symbol', SYMBOL, (_in(CONTEXT), _in(LITERALS), _in(UINT))) */ Z3_symbol Z3_API Z3_get_label_symbol(Z3_context c, Z3_literals lbls, unsigned idx); /** \brief Retrieve literal expression at idx. \deprecated This procedure is based on the old Solver API. def_API('Z3_get_literal', AST, (_in(CONTEXT), _in(LITERALS), _in(UINT))) */ Z3_ast Z3_API Z3_get_literal(Z3_context c, Z3_literals lbls, unsigned idx); /** \brief Disable label. The disabled label is not going to be used when blocking the subsequent search. \sa Z3_block_literals \deprecated This procedure is based on the old Solver API. def_API('Z3_disable_literal', VOID, (_in(CONTEXT), _in(LITERALS), _in(UINT))) */ void Z3_API Z3_disable_literal(Z3_context c, Z3_literals lbls, unsigned idx); /** \brief Block subsequent checks using the remaining enabled labels. \deprecated This procedure is based on the old Solver API. def_API('Z3_block_literals', VOID, (_in(CONTEXT), _in(LITERALS))) */ void Z3_API Z3_block_literals(Z3_context c, Z3_literals lbls); /*@}*/ /** @name Deprecated Model API */ /*@{*/ /** \brief Return the number of constants assigned by the given model. \mlonly \remark Consider using {!get_model_constants}. \endmlonly \sa Z3_get_model_constant \deprecated use #Z3_model_get_num_consts def_API('Z3_get_model_num_constants', UINT, (_in(CONTEXT), _in(MODEL))) */ unsigned Z3_API Z3_get_model_num_constants(Z3_context c, Z3_model m); /** \brief \mlh get_model_constant c m i \endmlh Return the i-th constant in the given model. \mlonly \remark Consider using {!get_model_constants}. \endmlonly \pre i < Z3_get_model_num_constants(c, m) \deprecated use #Z3_model_get_const_decl def_API('Z3_get_model_constant', FUNC_DECL, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_model_constant(Z3_context c, Z3_model m, unsigned i); /** \brief Return the number of function interpretations in the given model. 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. \deprecated use #Z3_model_get_num_funcs def_API('Z3_get_model_num_funcs', UINT, (_in(CONTEXT), _in(MODEL))) */ unsigned Z3_API Z3_get_model_num_funcs(Z3_context c, Z3_model m); /** \brief \mlh get_model_func_decl c m i \endmlh Return the declaration of the i-th function in the given model. \pre i < Z3_get_model_num_funcs(c, m) \sa Z3_get_model_num_funcs \deprecated use #Z3_model_get_func_decl def_API('Z3_get_model_func_decl', FUNC_DECL, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_model_func_decl(Z3_context c, Z3_model m, unsigned i); /** \brief Return the value of the given constant or function in the given model. \deprecated Consider using #Z3_model_eval or #Z3_model_get_func_interp def_API('Z3_eval_func_decl', BOOL, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL), _out(AST))) */ Z3_bool Z3_API Z3_eval_func_decl(Z3_context c, Z3_model m, Z3_func_decl decl, Z3_ast* v); /** \brief \mlh is_array_value c v \endmlh Determine whether the term encodes an array value. A term encodes an array value if it is a nested sequence of applications of store on top of a constant array. The indices to the stores have to be values (for example, integer constants) so that equality between the indices can be evaluated. Array values are useful for representing interpretations for arrays. Return the number of entries mapping to non-default values of the array. \deprecated Use #Z3_is_as_array def_API('Z3_is_array_value', BOOL, (_in(CONTEXT), _in(MODEL), _in(AST), _out(UINT))) */ Z3_bool Z3_API Z3_is_array_value(Z3_context c, Z3_model m, Z3_ast v, unsigned* num_entries); /** \brief \mlh get_array_value c v \endmlh An array values is represented as a dictionary plus a default (else) value. This function returns the array graph. \pre Z3_TRUE == Z3_is_array_value(c, v, &num_entries) \deprecated Use Z3_func_interp objects and #Z3_get_as_array_func_decl def_API('Z3_get_array_value', VOID, (_in(CONTEXT), _in(MODEL), _in(AST), _in(UINT), _out_array(3, AST), _out_array(3, AST), _out (AST))) */ void Z3_API Z3_get_array_value(Z3_context c, Z3_model m, Z3_ast v, unsigned num_entries, Z3_ast indices[], Z3_ast values[], Z3_ast* else_value ); /** \brief \mlh get_model_func_else c m i \endmlh Return the 'else' value of the i-th function interpretation in the given model. A function interpretation is represented as a finite map and an 'else' value. \mlonly \remark Consider using {!get_model_funcs}. \endmlonly \pre i < Z3_get_model_num_funcs(c, m) \sa Z3_get_model_num_funcs \sa Z3_get_model_func_num_entries \sa Z3_get_model_func_entry_num_args \sa Z3_get_model_func_entry_arg \deprecated Use Z3_func_interp objects def_API('Z3_get_model_func_else', AST, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ Z3_ast Z3_API Z3_get_model_func_else(Z3_context c, Z3_model m, unsigned i); /** \brief \mlh get_model_func_num_entries c m i \endmlh Return the number of entries of the i-th function interpretation in the given model. A function interpretation is represented as a finite map and an 'else' value. \mlonly \remark Consider using {!get_model_funcs}. \endmlonly \pre i < Z3_get_model_num_funcs(c, m) \sa Z3_get_model_num_funcs \sa Z3_get_model_func_else \sa Z3_get_model_func_entry_num_args \sa Z3_get_model_func_entry_arg \deprecated Use Z3_func_interp objects def_API('Z3_get_model_func_num_entries', UINT, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ unsigned Z3_API Z3_get_model_func_num_entries(Z3_context c, Z3_model m, unsigned i); /** \brief \mlh get_model_func_entry_num_args c m i j \endmlh Return the number of arguments of the j-th entry of the i-th function interpretation in the given model. A function interpretation is represented as a finite map and an 'else' value. This function returns the j-th entry of this map. An entry represents the value of a function given a set of arguments. \conly That is: it has the following format f(args[0],...,args[num_args - 1]) = val. \mlonly \remark Consider using {!get_model_funcs}. \endmlonly \pre i < Z3_get_model_num_funcs(c, m) \pre j < Z3_get_model_func_num_entries(c, m, i) \sa Z3_get_model_num_funcs \sa Z3_get_model_func_num_entries \sa Z3_get_model_func_entry_arg \deprecated Use Z3_func_interp objects def_API('Z3_get_model_func_entry_num_args', UINT, (_in(CONTEXT), _in(MODEL), _in(UINT), _in(UINT))) */ unsigned Z3_API Z3_get_model_func_entry_num_args(Z3_context c, Z3_model m, unsigned i, unsigned j); /** \brief \mlh get_model_func_entry_arg c m i j k \endmlh Return the k-th argument of the j-th entry of the i-th function interpretation in the given model. A function interpretation is represented as a finite map and an 'else' value. This function returns the j-th entry of this map. An entry represents the value of a function given a set of arguments. \conly That is: it has the following format f(args[0],...,args[num_args - 1]) = val. \mlonly \remark Consider using {!get_model_funcs}. \endmlonly \pre i < Z3_get_model_num_funcs(c, m) \pre j < Z3_get_model_func_num_entries(c, m, i) \pre k < Z3_get_model_func_entry_num_args(c, m, i, j) \sa Z3_get_model_num_funcs \sa Z3_get_model_func_num_entries \sa Z3_get_model_func_entry_num_args \deprecated Use Z3_func_interp objects def_API('Z3_get_model_func_entry_arg', AST, (_in(CONTEXT), _in(MODEL), _in(UINT), _in(UINT), _in(UINT))) */ Z3_ast Z3_API Z3_get_model_func_entry_arg(Z3_context c, Z3_model m, unsigned i, unsigned j, unsigned k); /** \brief \mlh get_model_func_entry_value c m i j \endmlh Return the return value of the j-th entry of the i-th function interpretation in the given model. A function interpretation is represented as a finite map and an 'else' value. This function returns the j-th entry of this map. An entry represents the value of a function given a set of arguments. \conly That is: it has the following format f(args[0],...,args[num_args - 1]) = val. \mlonly \remark Consider using {!get_model_funcs}. \endmlonly \pre i < Z3_get_model_num_funcs(c, m) \pre j < Z3_get_model_func_num_entries(c, m, i) \sa Z3_get_model_num_funcs \sa Z3_get_model_func_num_entries \deprecated Use Z3_func_interp objects def_API('Z3_get_model_func_entry_value', AST, (_in(CONTEXT), _in(MODEL), _in(UINT), _in(UINT))) */ Z3_ast Z3_API Z3_get_model_func_entry_value(Z3_context c, Z3_model m, unsigned i, unsigned j); /** \brief \mlh eval c m t \endmlh Evaluate the AST node \c t in the given model. \conly Return \c Z3_TRUE if succeeded, and store the result in \c v. \mlonly Return a pair: Boolean and value. The Boolean is true if the term was successfully evaluated. \endmlonly The evaluation may fail for the following reasons: - \c t contains a quantifier. - the model \c m is partial, that is, it doesn't have a complete interpretation for uninterpreted functions. That is, the option MODEL_PARTIAL=true was used. - \c t is type incorrect. \deprecated Use #Z3_model_eval def_API('Z3_eval', BOOL, (_in(CONTEXT), _in(MODEL), _in(AST), _out(AST))) */ Z3_bool Z3_API Z3_eval(Z3_context c, Z3_model m, Z3_ast t, Z3_ast * v); /** \brief Evaluate declaration given values. Provides direct way to evaluate declarations without going over terms. \deprecated Consider using #Z3_model_eval and #Z3_substitute_vars def_API('Z3_eval_decl', BOOL, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL), _in(UINT), _in_array(3, AST), _out(AST))) */ Z3_bool Z3_API Z3_eval_decl(Z3_context c, Z3_model m, Z3_func_decl d, unsigned num_args, Z3_ast const args[], Z3_ast* v); /*@}*/ /** @name Deprecated String conversion API */ /*@{*/ /** \brief Convert the given logical context into a string. This function is mainly used for debugging purposes. It displays the internal structure of a logical context. \conly \warning The result buffer is statically allocated by Z3. It will \conly be automatically deallocated when #Z3_del_context is invoked. \conly So, the buffer is invalidated in the next call to \c Z3_context_to_string. \deprecated This method is obsolete. It just displays the internal representation of the global solver available for backward compatibility reasons. def_API('Z3_context_to_string', STRING, (_in(CONTEXT),)) */ Z3_string Z3_API Z3_context_to_string(Z3_context c); /** \brief Return runtime statistics as a string. This function is mainly used for debugging purposes. It displays statistics of the search activity. \conly \warning The result buffer is statically allocated by Z3. It will \conly be automatically deallocated when #Z3_del_context is invoked. \conly So, the buffer is invalidated in the next call to \c Z3_context_to_string. \deprecated This method is based on the old solver API. Use #Z3_stats_to_string when using the new solver API. def_API('Z3_statistics_to_string', STRING, (_in(CONTEXT),)) */ Z3_string Z3_API Z3_statistics_to_string(Z3_context c); /** \brief Extract satisfying assignment from context as a conjunction. This function can be used for debugging purposes. It returns a conjunction of formulas that are assigned to true in the current context. This conjunction will contain not only the assertions that are set to true under the current assignment, but will also include additional literals if there has been a call to #Z3_check or #Z3_check_and_get_model. \deprecated This method is based on the old solver API. def_API('Z3_get_context_assignment', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_get_context_assignment(Z3_context c); /*@}*/ #endif #ifndef CAMLIDL #ifdef __cplusplus }; #endif // __cplusplus #else } #endif // CAMLIDL /*@}*/ #endif z3-z3-4.4.1/src/api/z3_fpa.h000066400000000000000000000736101260446376700153270ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: z3_fpa.h Abstract: Additional APIs for floating-point arithmetic (FP). Author: Christoph M. Wintersteiger (cwinter) 2013-06-05 Notes: --*/ #ifndef Z3_FPA_H_ #define Z3_FPA_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Floating-Point API */ /*@{*/ /** \brief Create the RoundingMode sort. \param c logical context def_API('Z3_mk_fpa_rounding_mode_sort', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_rounding_mode_sort(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. \param c logical context def_API('Z3_mk_fpa_round_nearest_ties_to_even', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_even(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. \param c logical context def_API('Z3_mk_fpa_rne', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rne(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. \param c logical context def_API('Z3_mk_fpa_round_nearest_ties_to_away', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_away(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. \param c logical context def_API('Z3_mk_fpa_rna', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rna(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. \param c logical context def_API('Z3_mk_fpa_round_toward_positive', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_toward_positive(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. \param c logical context def_API('Z3_mk_fpa_rtp', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rtp(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. \param c logical context def_API('Z3_mk_fpa_round_toward_negative', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_toward_negative(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. \param c logical context def_API('Z3_mk_fpa_rtn', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rtn(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. \param c logical context def_API('Z3_mk_fpa_round_toward_zero', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_toward_zero(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. \param c logical context def_API('Z3_mk_fpa_rtz', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rtz(Z3_context c); /** \brief Create a FloatingPoint sort. \param c logical context \param ebits number of exponent bits \param sbits number of significand bits \remark ebits must be larger than 1 and sbits must be larger than 2. def_API('Z3_mk_fpa_sort', SORT, (_in(CONTEXT), _in(UINT), _in(UINT))) */ Z3_sort Z3_API Z3_mk_fpa_sort(Z3_context c, unsigned ebits, unsigned sbits); /** \brief Create the half-precision (16-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_half', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_half(Z3_context c); /** \brief Create the half-precision (16-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_16', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_16(Z3_context c); /** \brief Create the single-precision (32-bit) FloatingPoint sort. \param c logical context. def_API('Z3_mk_fpa_sort_single', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_single(Z3_context c); /** \brief Create the single-precision (32-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_32', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_32(Z3_context c); /** \brief Create the double-precision (64-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_double', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_double(Z3_context c); /** \brief Create the double-precision (64-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_64', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_64(Z3_context c); /** \brief Create the quadruple-precision (128-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_quadruple', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_quadruple(Z3_context c); /** \brief Create the quadruple-precision (128-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_128', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_128(Z3_context c); /** \brief Create a floating-point NaN of sort s. \param c logical context \param s target sort def_API('Z3_mk_fpa_nan', AST, (_in(CONTEXT),_in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_nan(Z3_context c, Z3_sort s); /** \brief Create a floating-point infinity of sort s. \param c logical context \param s target sort \param negative indicates whether the result should be negative When \c negative is true, -oo will be generated instead of +oo. def_API('Z3_mk_fpa_inf', AST, (_in(CONTEXT),_in(SORT),_in(BOOL))) */ Z3_ast Z3_API Z3_mk_fpa_inf(Z3_context c, Z3_sort s, Z3_bool negative); /** \brief Create a floating-point zero of sort s. \param c logical context \param s target sort \param negative indicates whether the result should be negative When \c negative is true, -zero will be generated instead of +zero. def_API('Z3_mk_fpa_zero', AST, (_in(CONTEXT),_in(SORT),_in(BOOL))) */ Z3_ast Z3_API Z3_mk_fpa_zero(Z3_context c, Z3_sort s, Z3_bool negative); /** \brief 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. \param c logical context \param sgn sign \param exp exponent \param sig significand def_API('Z3_mk_fpa_fp', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_fp(Z3_context c, Z3_ast sgn, Z3_ast exp, Z3_ast sig); /** \brief 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. \param c logical context \param v value \param ty sort ty must be a FloatingPoint sort \sa Z3_mk_numeral def_API('Z3_mk_fpa_numeral_float', AST, (_in(CONTEXT), _in(FLOAT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_numeral_float(Z3_context c, float v, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a double. This function is used to create numerals that fit in a double value. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \param c logical context \param v value \param ty sort ty must be a FloatingPoint sort \sa Z3_mk_numeral def_API('Z3_mk_fpa_numeral_double', AST, (_in(CONTEXT), _in(DOUBLE), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_numeral_double(Z3_context c, double v, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a signed integer. \param c logical context \param v value \param ty result sort ty must be a FloatingPoint sort \sa Z3_mk_numeral def_API('Z3_mk_fpa_numeral_int', AST, (_in(CONTEXT), _in(INT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_numeral_int(Z3_context c, signed v, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a sign bit and two integers. \param c logical context \param sgn sign bit (true == negative) \param sig significand \param exp exponent \param ty result sort ty must be a FloatingPoint sort \sa Z3_mk_numeral def_API('Z3_mk_fpa_numeral_int_uint', AST, (_in(CONTEXT), _in(BOOL), _in(INT), _in(UINT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_numeral_int_uint(Z3_context c, Z3_bool sgn, signed exp, unsigned sig, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a sign bit and two 64-bit integers. \param c logical context \param sgn sign bit (true == negative) \param sig significand \param exp exponent \param ty result sort ty must be a FloatingPoint sort \sa Z3_mk_numeral def_API('Z3_mk_fpa_numeral_int64_uint64', AST, (_in(CONTEXT), _in(BOOL), _in(INT64), _in(UINT64), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_numeral_int64_uint64(Z3_context c, Z3_bool sgn, __int64 exp, __uint64 sig, Z3_sort ty); /** \brief Floating-point absolute value \param c logical context \param t term of FloatingPoint sort def_API('Z3_mk_fpa_abs', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_abs(Z3_context c, Z3_ast t); /** \brief Floating-point negation \param c logical context \param t term of FloatingPoint sort def_API('Z3_mk_fpa_neg', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_neg(Z3_context c, Z3_ast t); /** \brief Floating-point addition \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort rm must be of RoundingMode sort, t1 and t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_add', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_add(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point subtraction \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort rm must be of RoundingMode sort, t1 and t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_sub', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_sub(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point multiplication \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort rm must be of RoundingMode sort, t1 and t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_mul', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_mul(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point division \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort. \param t2 term of FloatingPoint sort The nodes rm must be of RoundingMode sort t1 and t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_div', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_div(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point fused multiply-add. \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sor \param t3 term of FloatingPoint sort The result is round((t1 * t2) + t3) rm must be of RoundingMode sort, t1, t2, and t3 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_fma', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_fma(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2, Z3_ast t3); /** \brief Floating-point square root \param c logical context \param rm term of RoundingMode sort \param t term of FloatingPoint sort rm must be of RoundingMode sort, t must have FloatingPoint sort. def_API('Z3_mk_fpa_sqrt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_sqrt(Z3_context c, Z3_ast rm, Z3_ast t); /** \brief Floating-point remainder \param c logical context \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort t1 and t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_rem', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_rem(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point roundToIntegral. Rounds a floating-point number to the closest integer, again represented as a floating-point number. \param c logical context \param rm term of RoundingMode sort \param t term of FloatingPoint sort t must be of FloatingPoint sort. def_API('Z3_mk_fpa_round_to_integral', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_round_to_integral(Z3_context c, Z3_ast rm, Z3_ast t); /** \brief Minimum of floating-point numbers. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort t1, t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_min', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_min(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Maximum of floating-point numbers. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort t1, t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_max', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_max(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point less than or equal. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort t1 and t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_leq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_leq(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point less than. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort t1 and t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_lt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_lt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point greater than or equal. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort t1 and t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_geq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_geq(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point greater than. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort t1 and t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_gt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_gt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point equality. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort Note that this is IEEE 754 equality (as opposed to SMT-LIB =). t1 and t2 must have the same FloatingPoint sort. def_API('Z3_mk_fpa_eq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_eq(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Predicate indicating whether t is a normal floating-point number. \param c logical context \param t term of FloatingPoint sort t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_normal', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_normal(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a subnormal floating-point number. \param c logical context \param t term of FloatingPoint sort t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_subnormal', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_subnormal(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a floating-point number with zero value, i.e., +zero or -zero. \param c logical context \param t term of FloatingPoint sort t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_zero', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_zero(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a floating-point number representing +oo or -oo. \param c logical context \param t term of FloatingPoint sort t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_infinite', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_infinite(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a NaN. \param c logical context \param t term of FloatingPoint sort t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_nan', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_nan(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a negative floating-point number. \param c logical context \param t term of FloatingPoint sort t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_negative', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_negative(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a positive floating-point number. \param c logical context \param t term of FloatingPoint sort t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_positive', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_positive(Z3_context c, Z3_ast t); /** \brief 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. \param c logical context \param bv a bit-vector term \param s floating-point sort s must be a FloatingPoint sort, t must be of bit-vector sort, and the bit-vector size of bv must be equal to ebits+sbits of s. The format of the bit-vector is as defined by the IEEE 754-2008 interchange format. def_API('Z3_mk_fpa_to_fp_bv', AST, (_in(CONTEXT),_in(AST),_in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_bv(Z3_context c, Z3_ast bv, Z3_sort s); /** \brief 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. \param c logical context \param rm term of RoundingMode sort \param t term of FloatingPoint sort \param s floating-point sort s must be a FloatingPoint sort, rm must be of RoundingMode sort, t must be of floating-point sort. def_API('Z3_mk_fpa_to_fp_float', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_float(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \brief 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. \param c logical context \param rm term of RoundingMode sort \param t term of Real sort \param s floating-point sort s must be a FloatingPoint sort, rm must be of RoundingMode sort, t must be of real sort. def_API('Z3_mk_fpa_to_fp_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_real(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \brief 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. If necessary, the result will be rounded according to rounding mode rm. \param c logical context \param rm term of RoundingMode sort \param t term of bit-vector sort \param s floating-point sort s must be a FloatingPoint sort, rm must be of RoundingMode sort, t must be of bit-vector sort. def_API('Z3_mk_fpa_to_fp_signed', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_signed(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \brief Conversion of a 2's complement unsigned 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 unsigned 2's complement format. If necessary, the result will be rounded according to rounding mode rm. \param c logical context \param rm term of RoundingMode sort \param t term of bit-vector sort \param s floating-point sort s must be a FloatingPoint sort, rm must be of RoundingMode sort, t must be of bit-vector sort. def_API('Z3_mk_fpa_to_fp_unsigned', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_unsigned(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \brief Conversion of a floating-point term into an unsigned bit-vector. Produces a term that represents the conversion of the floating-poiunt term t into a bit-vector term of size sz in unsigned 2's complement format. If necessary, the result will be rounded according to rounding mode rm. \param c logical context \param rm term of RoundingMode sort \param t term of FloatingPoint sort \param sz size of the resulting bit-vector def_API('Z3_mk_fpa_to_ubv', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(UINT))) */ Z3_ast Z3_API Z3_mk_fpa_to_ubv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz); /** \brief Conversion of a floating-point term into a signed bit-vector. Produces a term that represents the conversion of the floating-poiunt term t into a bit-vector term of size sz in signed 2's complement format. If necessary, the result will be rounded according to rounding mode rm. \param c logical context \param rm term of RoundingMode sort \param t term of FloatingPoint sort \param sz size of the resulting bit-vector def_API('Z3_mk_fpa_to_sbv', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(UINT))) */ Z3_ast Z3_API Z3_mk_fpa_to_sbv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz); /** \brief 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. \param c logical context \param t term of FloatingPoint sort def_API('Z3_mk_fpa_to_real', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_to_real(Z3_context c, Z3_ast t); /** @name Z3-specific floating-point extensions */ /*@{*/ /** \brief Retrieves the number of bits reserved for the exponent in a FloatingPoint sort. \param c logical context \param s FloatingPoint sort def_API('Z3_fpa_get_ebits', UINT, (_in(CONTEXT),_in(SORT))) */ unsigned Z3_API Z3_fpa_get_ebits(Z3_context c, Z3_sort s); /** \brief Retrieves the number of bits reserved for the significand in a FloatingPoint sort. \param c logical context \param s FloatingPoint sort def_API('Z3_fpa_get_sbits', UINT, (_in(CONTEXT),_in(SORT))) */ unsigned Z3_API Z3_fpa_get_sbits(Z3_context c, Z3_sort s); /** \brief Retrieves the sign of a floating-point literal. \param c logical context \param t a floating-point numeral \param sgn sign Remarks: sets \c sgn to 0 if the literal is NaN or positive and to 1 otherwise. def_API('Z3_fpa_get_numeral_sign', BOOL, (_in(CONTEXT), _in(AST), _out(INT))) */ Z3_bool Z3_API Z3_fpa_get_numeral_sign(Z3_context c, Z3_ast t, int * sgn); /** \brief Return the significand value of a floating-point numeral as a string. \param c logical context \param t a floating-point numeral Remarks: The significand s is always 0 < s < 2.0; the resulting string is long enough to represent the real significand precisely. def_API('Z3_fpa_get_numeral_significand_string', STRING, (_in(CONTEXT), _in(AST))) */ Z3_string Z3_API Z3_fpa_get_numeral_significand_string(Z3_context c, Z3_ast t); /** \brief Return the significand value of a floating-point numeral as a uint64. \param c logical context \param t a floating-point numeral Remarks: This function extracts the significand bits in `t`, without the hidden bit or normalization. Sets the Z3_INVALID_ARG error code if the significand does not fit into a uint64. def_API('Z3_fpa_get_numeral_significand_uint64', BOOL, (_in(CONTEXT), _in(AST), _out(UINT64))) */ Z3_bool Z3_API Z3_fpa_get_numeral_significand_uint64(Z3_context c, Z3_ast t, __uint64 * n); /** \brief Return the exponent value of a floating-point numeral as a string \param c logical context \param t a floating-point numeral def_API('Z3_fpa_get_numeral_exponent_string', STRING, (_in(CONTEXT), _in(AST))) */ Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(Z3_context c, Z3_ast t); /** \brief Return the exponent value of a floating-point numeral as a signed 64-bit integer \param c logical context \param t a floating-point numeral \param n exponent def_API('Z3_fpa_get_numeral_exponent_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64))) */ Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, __int64 * n); /** \brief Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. \param c logical context \param t term of FloatingPoint sort t must have FloatingPoint sort. 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. def_API('Z3_mk_fpa_to_ieee_bv', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_to_ieee_bv(Z3_context c, Z3_ast t); /** \brief 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. \param c logical context \param rm term of RoundingMode sort \param exp exponent term of Int sort \param sig significand term of Real sort \param s FloatingPoint sort s must be a FloatingPoint sort, rm must be of RoundingMode sort, exp must be of int sort, sig must be of real sort. def_API('Z3_mk_fpa_to_fp_int_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(SORT))) */ 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); /*@}*/ /*@}*/ /*@}*/ #ifdef __cplusplus }; #endif // __cplusplus #endif z3-z3-4.4.1/src/api/z3_interp.h000066400000000000000000000272171260446376700160640ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: z3_interp.h Abstract: API for interpolation Author: Kenneth McMillan (kenmcmil) Notes: --*/ #ifndef Z3_INTERPOLATION_H_ #define Z3_INTERPOLATION_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Interpolation API */ /*@{*/ /** \brief \mlh mk_interp c a \endmlh Create an AST node marking a formula position for interpolation. The node \c a must have Boolean sort. def_API('Z3_mk_interpolant', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_interpolant(Z3_context c, Z3_ast a); /** \brief This function generates a Z3 context suitable for generation of interpolants. Formulas can be generated as abstract syntax trees in this context using the Z3 C API. Interpolants are also generated as AST's in this context. If cfg is non-null, it will be used as the base configuration for the Z3 context. This makes it possible to set Z3 options to be used during interpolation. This feature should be used with some caution however, as it may be that certain Z3 options are incompatible with interpolation. def_API('Z3_mk_interpolation_context', CONTEXT, (_in(CONFIG),)) */ Z3_context Z3_API Z3_mk_interpolation_context(Z3_config cfg); /** Compute an interpolant from a refutation. This takes a proof of "false" from a set of formulas C, and an interpolation pattern. The pattern pat is a formula combining the formulas in C using logical conjunction and the "interp" operator (see #Z3_mk_interpolant). This interp operator is logically the identity operator. It marks the sub-formulas of the pattern for which interpolants should be computed. The interpolant is a map sigma from marked subformulas to formulas, such that, for each marked subformula phi of pat (where phi sigma is phi with sigma(psi) substituted for each subformula psi of phi such that psi in dom(sigma)): 1) phi sigma implies sigma(phi), and 2) sigma(phi) is in the common uninterpreted vocabulary between the formulas of C occurring in phi and those not occurring in phi and moreover pat sigma implies false. In the simplest case an interpolant for the pattern "(and (interp A) B)" maps A to an interpolant for A /\ B. The return value is a vector of formulas representing sigma. The vector contains sigma(phi) for each marked subformula of pat, in pre-order traversal. This means that subformulas of phi occur before phi in the vector. Also, subformulas that occur multiply in pat will occur multiply in the result vector. In particular, calling Z3_get_interpolant on a pattern of the form (interp ... (interp (and (interp A_1) A_2)) ... A_N) will result in a sequence interpolant for A_1, A_2,... A_N. Neglecting interp markers, the pattern must be a conjunction of formulas in C, the set of premises of the proof. Otherwise an error is flagged. Any premises of the proof not present in the pattern are treated as "background theory". Predicate and function symbols occurring in the background theory are treated as interpreted and thus always allowed in the interpolant. Interpolant may not necessarily be computable from all proofs. To be sure an interpolant can be computed, the proof must be generated by an SMT solver for which interpoaltion is supported, and the premises must be expressed using only theories and operators for which interpolation is supported. Currently, the only SMT solver that is supported is the legacy SMT solver. Such a solver is available as the default solver in #Z3_context objects produced by #Z3_mk_interpolation_context. Currently, the theories supported are equality with uninterpreted functions, linear integer arithmetic, and the theory of arrays (in SMT-LIB terms, this is AUFLIA). Quantifiers are allowed. Use of any other operators (including "labels") may result in failure to compute an interpolant from a proof. Parameters: \param c logical context. \param pf a refutation from premises (assertions) C \param pat an interpolation pattern over C \param p parameters def_API('Z3_get_interpolant', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(PARAMS))) */ Z3_ast_vector Z3_API Z3_get_interpolant(Z3_context c, Z3_ast pf, Z3_ast pat, Z3_params p); /* Compute an interpolant for an unsatisfiable conjunction of formulas. This takes as an argument an interpolation pattern as in #Z3_get_interpolant. This is a conjunction, some subformulas of which are marked with the "interp" operator (see #Z3_mk_interpolant). The conjunction is first checked for unsatisfiability. The result of this check is returned in the out parameter "status". If the result is unsat, an interpolant is computed from the refutation as in #Z3_get_interpolant and returned as a vector of formulas. Otherwise the return value is an empty formula. See #Z3_get_interpolant for a discussion of supported theories. The advantage of this function over #Z3_get_interpolant is that it is not necessary to create a suitable SMT solver and generate a proof. The disadvantage is that it is not possible to use the solver incrementally. Parameters: \param c logical context. \param pat an interpolation pattern \param p parameters for solver creation \param status returns the status of the sat check \param model returns model if satisfiable Return value: status of SAT check def_API('Z3_compute_interpolant', INT, (_in(CONTEXT), _in(AST), _in(PARAMS), _out(AST_VECTOR), _out(MODEL))) */ Z3_lbool Z3_API Z3_compute_interpolant(Z3_context c, Z3_ast pat, Z3_params p, Z3_ast_vector *interp, Z3_model *model); /** Return a string summarizing cumulative time used for interpolation. This string is purely for entertainment purposes and has no semantics. \param ctx The context (currently ignored) def_API('Z3_interpolation_profile', STRING, (_in(CONTEXT),)) */ Z3_string Z3_API Z3_interpolation_profile(Z3_context ctx); /** \brief Read an interpolation problem from file. \param ctx The Z3 context. This resets the error handler of ctx. \param filename The file name to read. \param num Returns length of sequence. \param cnsts Returns sequence of formulas (do not free) \param parents Returns the parents vector (or NULL for sequence) \param error Returns an error message in case of failure (do not free the string) \param num_theory Number of theory terms \param theory Theory terms Returns true on success. File formats: Currently two formats are supported, based on SMT-LIB2. For sequence interpolants, the sequence of constraints is represented by the sequence of "assert" commands in the file. For tree interpolants, one symbol of type bool is associated to each vertex of the tree. For each vertex v there is an "assert" of the form: (implies (and c1 ... cn f) v) where c1 .. cn are the children of v (which must precede v in the file) and f is the formula assiciated to node v. The last formula in the file is the root vertex, and is represented by the predicate "false". A solution to a tree interpolation problem can be thought of as a valuation of the vertices that makes all the implications true where each value is represented using the common symbols between the formulas in the subtree and the remainder of the formulas. def_API('Z3_read_interpolation_problem', INT, (_in(CONTEXT), _out(UINT), _out_managed_array(1, AST), _out_managed_array(1, UINT), _in(STRING), _out(STRING), _out(UINT), _out_managed_array(6, AST))) */ int Z3_API Z3_read_interpolation_problem(Z3_context ctx, unsigned *num, Z3_ast *cnsts[], unsigned *parents[], Z3_string filename, Z3_string_ptr error, unsigned *num_theory, Z3_ast *theory[]); /** Check the correctness of an interpolant. The Z3 context must have no constraints asserted when this call is made. That means that after interpolating, you must first fully pop the Z3 context before calling this. See Z3_interpolate for meaning of parameters. \param ctx The Z3 context. Must be generated by Z3_mk_interpolation_context \param num The number of constraints in the sequence \param cnsts Array of constraints (AST's in context ctx) \param parents The parents vector (or NULL for sequence) \param interps The interpolant to check \param error Returns an error message if interpolant incorrect (do not free the string) \param num_theory Number of theory terms \param theory Theory terms Return value is Z3_L_TRUE if interpolant is verified, Z3_L_FALSE if incorrect, and Z3_L_UNDEF if unknown. def_API('Z3_check_interpolant', INT, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in_array(1, AST), _out(STRING), _in(UINT), _in_array(6, AST))) */ int Z3_API Z3_check_interpolant(Z3_context ctx, unsigned num, Z3_ast cnsts[], unsigned parents[], Z3_ast *interps, Z3_string_ptr error, unsigned num_theory, Z3_ast theory[]); /** Write an interpolation problem to file suitable for reading with Z3_read_interpolation_problem. The output file is a sequence of SMT-LIB2 format commands, suitable for reading with command-line Z3 or other interpolating solvers. \param ctx The Z3 context. Must be generated by z3_mk_interpolation_context \param num The number of constraints in the sequence \param cnsts Array of constraints \param parents The parents vector (or NULL for sequence) \param filename The file name to write \param num_theory Number of theory terms \param theory Theory terms def_API('Z3_write_interpolation_problem', VOID, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(STRING), _in(UINT), _in_array(5, AST))) */ void Z3_API Z3_write_interpolation_problem(Z3_context ctx, unsigned num, Z3_ast cnsts[], unsigned parents[], Z3_string filename, unsigned num_theory, Z3_ast theory[]); /*@}*/ /*@}*/ #ifdef __cplusplus }; #endif // __cplusplus #endif z3-z3-4.4.1/src/api/z3_logger.h000066400000000000000000000053101260446376700160300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/z3_macros.h000066400000000000000000000007721260446376700160440ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifndef Z3_bool_opt #define Z3_bool_opt Z3_bool #endif #ifndef Z3_API # ifdef __GNUC__ # define Z3_API __attribute__ ((visibility ("default"))) # else # define Z3_API # endif #endif #ifndef DEFINE_TYPE #define DEFINE_TYPE(T) typedef struct _ ## T *T #endif #ifndef DEFINE_VOID #define DEFINE_VOID(T) typedef void* T #endif #ifndef BEGIN_MLAPI_EXCLUDE #define BEGIN_MLAPI_EXCLUDE #endif #ifndef END_MLAPI_EXCLUDE #define END_MLAPI_EXCLUDE #endif z3-z3-4.4.1/src/api/z3_polynomial.h000066400000000000000000000021601260446376700167340ustar00rootroot00000000000000/*++ 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(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x); /*@}*/ /*@}*/ #ifdef __cplusplus }; #endif // __cplusplus #endif z3-z3-4.4.1/src/api/z3_private.h000066400000000000000000000011621260446376700162240ustar00rootroot00000000000000/*++ 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(Z3_context c, Z3_ast a, rational& r); #ifndef CAMLIDL #ifdef __cplusplus }; #endif // __cplusplus #else } #endif // CAMLIDL #endif z3-z3-4.4.1/src/api/z3_rcf.h000066400000000000000000000134661260446376700153360ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: z3_rcf.h Abstract: Additional APIs for handling elements of the Z3 real closed field that contains: - transcendental extensions - infinitesimal extensions - algebraic extensions Author: Leonardo de Moura (leonardo) 2012-01-05 Notes: --*/ #ifndef Z3_RCF_H_ #define Z3_RCF_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Real Closed Fields API */ /*@{*/ /** \brief Delete a RCF numeral created using the RCF API. def_API('Z3_rcf_del', VOID, (_in(CONTEXT), _in(RCF_NUM))) */ void Z3_API Z3_rcf_del(Z3_context c, Z3_rcf_num a); /** \brief Return a RCF rational using the given string. def_API('Z3_rcf_mk_rational', RCF_NUM, (_in(CONTEXT), _in(STRING))) */ Z3_rcf_num Z3_API Z3_rcf_mk_rational(Z3_context c, Z3_string val); /** \brief Return a RCF small integer. def_API('Z3_rcf_mk_small_int', RCF_NUM, (_in(CONTEXT), _in(INT))) */ Z3_rcf_num Z3_API Z3_rcf_mk_small_int(Z3_context c, int val); /** \brief Return Pi def_API('Z3_rcf_mk_pi', RCF_NUM, (_in(CONTEXT),)) */ Z3_rcf_num Z3_API Z3_rcf_mk_pi(Z3_context c); /** \brief Return e (Euler's constant) def_API('Z3_rcf_mk_e', RCF_NUM, (_in(CONTEXT),)) */ Z3_rcf_num Z3_API Z3_rcf_mk_e(Z3_context c); /** \brief Return a new infinitesimal that is smaller than all elements in the Z3 field. def_API('Z3_rcf_mk_infinitesimal', RCF_NUM, (_in(CONTEXT),)) */ Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(Z3_context c); /** \brief Store in roots the roots of the polynomial a[n-1]*x^{n-1} + ... + a[0]. The output vector \c roots must have size \c n. It returns the number of roots of the polynomial. \pre The input polynomial is not the zero polynomial. def_API('Z3_rcf_mk_roots', UINT, (_in(CONTEXT), _in(UINT), _in_array(1, RCF_NUM), _out_array(1, RCF_NUM))) */ unsigned Z3_API Z3_rcf_mk_roots(Z3_context c, unsigned n, Z3_rcf_num const a[], Z3_rcf_num roots[]); /** \brief Return the value a + b. def_API('Z3_rcf_add', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_add(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return the value a - b. def_API('Z3_rcf_sub', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_sub(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return the value a * b. def_API('Z3_rcf_mul', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_mul(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return the value a / b. def_API('Z3_rcf_div', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_div(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return the value -a def_API('Z3_rcf_neg', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_neg(Z3_context c, Z3_rcf_num a); /** \brief Return the value 1/a def_API('Z3_rcf_inv', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM))) */ Z3_rcf_num Z3_API Z3_rcf_inv(Z3_context c, Z3_rcf_num a); /** \brief Return the value a^k def_API('Z3_rcf_power', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) */ Z3_rcf_num Z3_API Z3_rcf_power(Z3_context c, Z3_rcf_num a, unsigned k); /** \brief Return Z3_TRUE if a < b def_API('Z3_rcf_lt', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_bool Z3_API Z3_rcf_lt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return Z3_TRUE if a > b def_API('Z3_rcf_gt', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_bool Z3_API Z3_rcf_gt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return Z3_TRUE if a <= b def_API('Z3_rcf_le', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_bool Z3_API Z3_rcf_le(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return Z3_TRUE if a >= b def_API('Z3_rcf_ge', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_bool Z3_API Z3_rcf_ge(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return Z3_TRUE if a == b def_API('Z3_rcf_eq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_bool Z3_API Z3_rcf_eq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return Z3_TRUE if a != b def_API('Z3_rcf_neq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ Z3_bool Z3_API Z3_rcf_neq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Convert the RCF numeral into a string. def_API('Z3_rcf_num_to_string', STRING, (_in(CONTEXT), _in(RCF_NUM), _in(BOOL), _in(BOOL))) */ Z3_string Z3_API Z3_rcf_num_to_string(Z3_context c, Z3_rcf_num a, Z3_bool compact, Z3_bool html); /** \brief Convert the RCF numeral into a string in decimal notation. def_API('Z3_rcf_num_to_decimal_string', STRING, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) */ Z3_string Z3_API Z3_rcf_num_to_decimal_string(Z3_context c, Z3_rcf_num a, unsigned prec); /** \brief Extract the "numerator" and "denominator" of the given RCF numeral. We have that a = n/d, moreover n and d are not represented using rational functions. def_API('Z3_rcf_get_numerator_denominator', VOID, (_in(CONTEXT), _in(RCF_NUM), _out(RCF_NUM), _out(RCF_NUM))) */ void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d); /*@}*/ /*@}*/ #ifdef __cplusplus }; #endif // __cplusplus #endif z3-z3-4.4.1/src/api/z3_replayer.cpp000066400000000000000000000614301260446376700167340ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_replayer.cpp Abstract: Interpreter for Z3 logs Author: Leonardo de Moura (leonardo) 2011-09-22 Notes: --*/ #include"vector.h" #include"map.h" #include"z3_replayer.h" #include"stream_buffer.h" #include"symbol.h" #include"trace.h" #include void register_z3_replayer_cmds(z3_replayer & in); void throw_invalid_reference() { TRACE("z3_replayer", tout << "invalid argument reference\n";); throw z3_replayer_exception("invalid argument reference1"); } struct z3_replayer::imp { z3_replayer & m_owner; std::istream & m_stream; char m_curr; // current char; int m_line; // line svector m_string; symbol m_id; __int64 m_int64; __uint64 m_uint64; double m_double; float m_float; size_t m_ptr; size_t_map m_heap; svector m_cmds; enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, INT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY, FLOAT }; char const* kind2string(value_kind k) const { switch (k) { case INT64: return "int64"; case UINT64: return "uint64"; case DOUBLE: return "double"; case STRING: return "string"; case SYMBOL: return "symbol"; case OBJECT: return "object"; case UINT_ARRAY: return "uint_array"; case INT_ARRAY: return "int_array"; case SYMBOL_ARRAY: return "symbol_array"; case OBJECT_ARRAY: return "object_array"; case FLOAT: return "float"; default: UNREACHABLE(); return "unknown"; } } void check_arg(unsigned pos, value_kind k) const { if (pos >= m_args.size()) { TRACE("z3_replayer", tout << "too few arguments " << m_args.size() << " expecting " << kind2string(k) << "\n";); throw z3_replayer_exception("invalid argument reference2"); } if (m_args[pos].m_kind != k) { std::stringstream strm; strm << "expecting " << kind2string(k) << " at position " << pos << " but got " << kind2string(m_args[pos].m_kind); throw z3_replayer_exception(strm.str().c_str()); } } struct value { value_kind m_kind; union { __int64 m_int; __uint64 m_uint; double m_double; char const * m_str; void * m_obj; float m_float; }; value():m_kind(OBJECT), m_int(0) {} value(void * obj):m_kind(OBJECT), m_obj(obj) {} value(value_kind k, char const * str):m_kind(k), m_str(str) {} value(value_kind k, __uint64 u):m_kind(k), m_uint(u) {} value(value_kind k, __int64 i):m_kind(k), m_int(i) {} value(value_kind k, double d):m_kind(k), m_double(d) {} value(value_kind k, float f):m_kind(k), m_float(f) {} }; svector m_args; void * m_result; vector > m_obj_arrays; vector > m_sym_arrays; vector m_unsigned_arrays; vector > m_int_arrays; imp(z3_replayer & o, std::istream & in): m_owner(o), m_stream(in), m_curr(0), m_line(1) { next(); } void display_arg(std::ostream & out, value const & v) const { switch (v.m_kind) { case INT64: out << v.m_int; break; case UINT64: out << v.m_uint; break; case FLOAT: out << v.m_float; break; case DOUBLE: out << v.m_double; break; case STRING: out << v.m_str; break; case SYMBOL: out << symbol::mk_symbol_from_c_ptr(v.m_str); break; case OBJECT: out << v.m_obj; break; case UINT_ARRAY: case OBJECT_ARRAY: case SYMBOL_ARRAY: out << ""; break; default: out << ""; break; } } void display_args(std::ostream & out) const { for (unsigned i = 0; i < m_args.size(); i++) { if (i > 0) out << " "; display_arg(out, m_args[i]); } } char curr() const { return m_curr; } void new_line() { m_line++; } void next() { m_curr = m_stream.get(); } void read_string_core(char delimiter) { if (curr() != delimiter) throw z3_replayer_exception("invalid string/symbol"); m_string.reset(); next(); while (true) { char c = curr(); if (c == EOF) { throw z3_replayer_exception("unexpected end of file"); } else if (c == '\n') { throw z3_replayer_exception("unexpected end of line"); } else if (c == '\\') { next(); unsigned val = 0; unsigned sz = 0; while (sz < 3) { c = curr(); if ('0' <= c && c <= '9') { val *= 10; val += c - '0'; sz++; } else { throw z3_replayer_exception("invalid scaped character"); } if (val > 255) throw z3_replayer_exception("invalid scaped character"); next(); } TRACE("z3_replayer_escape", tout << "val: " << val << "\n";); m_string.push_back(static_cast(val)); } else if (c == delimiter) { next(); m_string.push_back(0); return; } else { m_string.push_back(c); next(); } } } void read_string() { read_string_core('"'); } void read_quoted_symbol() { read_string_core('|'); m_id = m_string.begin(); } void read_int64() { if (!(curr() == '-' || ('0' <= curr() && curr() <= '9'))) throw z3_replayer_exception("invalid integer"); bool sign = false; if (curr() == '-') { sign = true; next(); if (!('0' <= curr() && curr() <= '9')) throw z3_replayer_exception("invalid integer"); } m_int64 = 0; while (true) { char c = curr(); if ('0' <= c && c <= '9') { m_int64 = 10*m_int64 + (c - '0'); next(); } else { break; } } if (sign) m_int64 = -m_int64; } void read_uint64() { if (!('0' <= curr() && curr() <= '9')) throw z3_replayer_exception("invalid unsigned"); m_uint64 = 0; while (true) { char c = curr(); if ('0' <= c && c <= '9') { m_uint64 = 10*m_uint64 + (c - '0'); next(); } else { break; } } } bool is_double_char() const { return curr() == '-' || curr() == '.' || ('0' <= curr() && curr() <= '9') || curr() == 'e' || curr() == 'E'; } #if (!defined(strtof)) float strtof(const char * str, char ** end_ptr) { // Note: This may introduce a double-rounding problem. return (float)strtod(str, end_ptr); } #endif void read_float() { m_string.reset(); while (is_double_char()) { m_string.push_back(curr()); next(); } if (m_string.empty()) throw z3_replayer_exception("invalid float"); m_string.push_back(0); char * ptr; m_float = strtof(m_string.begin(), &ptr); } void read_double() { m_string.reset(); while (is_double_char()) { m_string.push_back(curr()); next(); } if (m_string.empty()) throw z3_replayer_exception("invalid double"); m_string.push_back(0); char * ptr; m_double = strtod(m_string.begin(), &ptr); } void read_ptr() { if (!(('0' <= curr() && curr() <= '9') || ('A' <= curr() && curr() <= 'F') || ('a' <= curr() && curr() <= 'f'))) { TRACE("invalid_ptr", tout << "curr: " << curr() << "\n";); throw z3_replayer_exception("invalid ptr"); } unsigned pos = 0; m_ptr = 0; while (true) { char c = curr(); if ('0' <= c && c <= '9') { m_ptr = m_ptr * 16 + (c - '0'); } else if ('a' <= c && c <= 'f') { m_ptr = m_ptr * 16 + 10 + (c - 'a'); } else if ('A' <= c && c <= 'F') { m_ptr = m_ptr * 16 + 10 + (c - 'A'); } else if (pos == 1 && (c == 'x' || c == 'X')) { // support for 0x.... notation } else { return; } next(); pos++; } } void skip_blank() { while (true) { char c = curr(); if (c == '\n') { new_line(); next(); } else if (c == ' ' || c == '\t') { next(); } else { break; } } } void push_array(unsigned sz, value_kind k) { unsigned asz = m_args.size(); if (sz > asz) throw z3_replayer_exception("invalid array size"); __uint64 aidx; value_kind nk; for (unsigned i = asz - sz; i < asz; i++) { if (m_args[i].m_kind != k) throw z3_replayer_exception("invalid array: mixed value types"); } if (k == UINT64) { aidx = m_unsigned_arrays.size(); nk = UINT_ARRAY; m_unsigned_arrays.push_back(unsigned_vector()); unsigned_vector & v = m_unsigned_arrays.back(); for (unsigned i = asz - sz; i < asz; i++) { v.push_back(static_cast(m_args[i].m_uint)); } } if (k == INT64) { aidx = m_int_arrays.size(); nk = INT_ARRAY; m_int_arrays.push_back(svector()); svector & v = m_int_arrays.back(); for (unsigned i = asz - sz; i < asz; i++) { v.push_back(static_cast(m_args[i].m_int)); } } else if (k == SYMBOL) { aidx = m_sym_arrays.size(); nk = SYMBOL_ARRAY; m_sym_arrays.push_back(svector()); svector & v = m_sym_arrays.back(); for (unsigned i = asz - sz; i < asz; i++) { v.push_back(reinterpret_cast(const_cast(m_args[i].m_str))); } } else if (k == OBJECT) { TRACE("z3_replayer_bug", tout << "args: "; display_args(tout); tout << "\n"; tout << "push_back, sz: " << sz << ", m_obj_arrays.size(): " << m_obj_arrays.size() << "\n"; for (unsigned i = asz - sz; i < asz; i++) { tout << "pushing: " << m_args[i].m_obj << "\n"; }); aidx = m_obj_arrays.size(); nk = OBJECT_ARRAY; m_obj_arrays.push_back(ptr_vector()); ptr_vector & v = m_obj_arrays.back(); for (unsigned i = asz - sz; i < asz; i++) { v.push_back(m_args[i].m_obj); } } else { throw z3_replayer_exception("unsupported array type"); } m_args.shrink(asz - sz); m_args.push_back(value(nk, aidx)); } #define TICK_FREQUENCY 100000 void parse() { unsigned long long counter = 0; unsigned tick = 0; while (true) { IF_VERBOSE(1, { counter++; tick++; if (tick >= TICK_FREQUENCY) { std::cout << "[replayer] " << counter << " operations executed" << std::endl; tick = 0; } }); skip_blank(); char c = curr(); if (c == EOF) return; switch (c) { case 'R': // reset next(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "R\n";); reset(); break; case 'P': { // push pointer next(); skip_blank(); read_ptr(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "P " << m_ptr << "\n";); if (m_ptr == 0) { m_args.push_back(0); } else { void * obj = 0; if (!m_heap.find(m_ptr, obj)) throw z3_replayer_exception("invalid pointer"); m_args.push_back(value(obj)); TRACE("z3_replayer_bug", tout << "args after 'P':\n"; display_args(tout); tout << "\n";); } break; } case 'S': { // push string next(); skip_blank(); read_string(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "S " << m_string.begin() << "\n";); symbol sym(m_string.begin()); // save string m_args.push_back(value(STRING, sym.bare_str())); break; } case 'N': // push null symbol next(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "N\n";); m_args.push_back(value(SYMBOL, static_cast(0))); break; case '$': { // push symbol next(); skip_blank(); read_quoted_symbol(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "$ " << m_id << "\n";); m_args.push_back(value(SYMBOL, m_id.bare_str())); break; } case '#': { // push numeral symbol next(); skip_blank(); read_uint64(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "# " << m_uint64 << "\n";); symbol sym(static_cast(m_uint64)); m_args.push_back(value(SYMBOL, static_cast(sym.c_ptr()))); break; } case 'I': // push integer; next(); skip_blank(); read_int64(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "I " << m_int64 << "\n";); m_args.push_back(value(INT64, m_int64)); break; case 'U': // push unsigned; next(); skip_blank(); read_uint64(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "U " << m_uint64 << "\n";); m_args.push_back(value(UINT64, m_uint64)); break; case 'F': // push float next(); skip_blank(); read_float(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "F " << m_float << "\n";); m_args.push_back(value(FLOAT, m_float)); break; case 'D': // push double next(); skip_blank(); read_double(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "D " << m_double << "\n";); m_args.push_back(value(DOUBLE, m_double)); break; case 'p': case 's': case 'u': // push array next(); skip_blank(); read_uint64(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "A " << m_uint64 << "\n";); if (c == 'p') push_array(static_cast(m_uint64), OBJECT); else if (c == 's') push_array(static_cast(m_uint64), SYMBOL); else push_array(static_cast(m_uint64), UINT64); break; case 'C': { // call procedure next(); skip_blank(); read_uint64(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "C " << m_uint64 << "\n";); unsigned idx = static_cast(m_uint64); if (idx >= m_cmds.size()) throw z3_replayer_exception("invalid command"); try { m_cmds[idx](m_owner); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { std::cout << "[z3 exception]: " << ex.msg() << std::endl; } break; } case '=': // save result // = obj_id next(); skip_blank(); read_ptr(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "= " << m_ptr << "\n";); m_heap.insert(m_ptr, m_result); break; case '*': { // save out // @ obj_id pos next(); skip_blank(); read_ptr(); skip_blank(); read_uint64(); unsigned pos = static_cast(m_uint64); TRACE("z3_replayer", tout << "[" << m_line << "] " << "* " << m_ptr << " " << pos << "\n";); check_arg(pos, OBJECT); m_heap.insert(m_ptr, m_args[pos].m_obj); break; } case '@': { // save array out // @ obj_id array_pos idx next(); skip_blank(); read_ptr(); skip_blank(); read_uint64(); unsigned pos = static_cast(m_uint64); check_arg(pos, OBJECT_ARRAY); unsigned aidx = static_cast(m_args[pos].m_uint); ptr_vector & v = m_obj_arrays[aidx]; skip_blank(); read_uint64(); unsigned idx = static_cast(m_uint64); TRACE("z3_replayer", tout << "[" << m_line << "] " << "@ " << m_ptr << " " << pos << " " << idx << "\n";); TRACE("z3_replayer_bug", tout << "v[idx]: " << v[idx] << "\n";); m_heap.insert(m_ptr, v[idx]); break; } case 'M': // user message next(); skip_blank(); read_string(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "M " << m_string.begin() << "\n";); std::cout << m_string.begin() << "\n"; std::cout.flush(); break; default: TRACE("z3_replayer", tout << "unknown command " << c << "\n";); throw z3_replayer_exception("unknown log command"); break; } } } int get_int(unsigned pos) const { check_arg(pos, INT64); return static_cast(m_args[pos].m_int); } __int64 get_int64(unsigned pos) const { check_arg(pos, INT64); return m_args[pos].m_int; } unsigned get_uint(unsigned pos) const { check_arg(pos, UINT64); return static_cast(m_args[pos].m_uint); } __uint64 get_uint64(unsigned pos) const { check_arg(pos, UINT64); return m_args[pos].m_uint; } float get_float(unsigned pos) const { if (pos >= m_args.size() || m_args[pos].m_kind != FLOAT) throw_invalid_reference(); return m_args[pos].m_float; } double get_double(unsigned pos) const { check_arg(pos, DOUBLE); return m_args[pos].m_double; } Z3_string get_str(unsigned pos) const { check_arg(pos, STRING); return m_args[pos].m_str; } Z3_symbol get_symbol(unsigned pos) const { check_arg(pos, SYMBOL); return reinterpret_cast(const_cast(m_args[pos].m_str)); } void * get_obj(unsigned pos) const { check_arg(pos, OBJECT); return m_args[pos].m_obj; } unsigned * get_uint_array(unsigned pos) const { check_arg(pos, UINT_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); return m_unsigned_arrays[idx].c_ptr(); } int * get_int_array(unsigned pos) const { check_arg(pos, INT_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); return m_int_arrays[idx].c_ptr(); } Z3_symbol * get_symbol_array(unsigned pos) const { check_arg(pos, SYMBOL_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); return m_sym_arrays[idx].c_ptr(); } void ** get_obj_array(unsigned pos) const { check_arg(pos, OBJECT_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); ptr_vector const & v = m_obj_arrays[idx]; TRACE("z3_replayer_bug", tout << "pos: " << pos << ", idx: " << idx << " size(): " << v.size() << "\n"; for (unsigned i = 0; i < v.size(); i++) tout << v[i] << " "; tout << "\n";); return v.c_ptr(); } int * get_int_addr(unsigned pos) { check_arg(pos, INT64); return reinterpret_cast(&(m_args[pos].m_int)); } __int64 * get_int64_addr(unsigned pos) { check_arg(pos, INT64); return &(m_args[pos].m_int); } unsigned * get_uint_addr(unsigned pos) { check_arg(pos, UINT64); return reinterpret_cast(&(m_args[pos].m_uint)); } __uint64 * get_uint64_addr(unsigned pos) { check_arg(pos, UINT64); return &(m_args[pos].m_uint); } Z3_string * get_str_addr(unsigned pos) { check_arg(pos, STRING); return &(m_args[pos].m_str); } void ** get_obj_addr(unsigned pos) { check_arg(pos, OBJECT); return &(m_args[pos].m_obj); } void store_result(void * obj) { m_result = obj; } void register_cmd(unsigned id, z3_replayer_cmd cmd) { m_cmds.reserve(id+1, 0); m_cmds[id] = cmd; } void reset() { m_result = 0; m_args.reset(); m_obj_arrays.reset(); m_sym_arrays.reset(); m_unsigned_arrays.reset(); m_int_arrays.reset(); } }; z3_replayer::z3_replayer(std::istream & in) { m_imp = alloc(imp, *this, in); register_z3_replayer_cmds(*this); } z3_replayer::~z3_replayer() { dealloc(m_imp); } unsigned z3_replayer::get_line() const { return m_imp->m_line; } bool z3_replayer::get_bool(unsigned pos) const { return get_int(pos) != 0; } int z3_replayer::get_int(unsigned pos) const { return m_imp->get_int(pos); } unsigned z3_replayer::get_uint(unsigned pos) const { return m_imp->get_uint(pos); } __int64 z3_replayer::get_int64(unsigned pos) const { return m_imp->get_int64(pos); } __uint64 z3_replayer::get_uint64(unsigned pos) const { return m_imp->get_uint64(pos); } float z3_replayer::get_float(unsigned pos) const { return m_imp->get_float(pos); } double z3_replayer::get_double(unsigned pos) const { return m_imp->get_double(pos); } Z3_string z3_replayer::get_str(unsigned pos) const { return m_imp->get_str(pos); } Z3_symbol z3_replayer::get_symbol(unsigned pos) const { return m_imp->get_symbol(pos); } void * z3_replayer::get_obj(unsigned pos) const { return m_imp->get_obj(pos); } unsigned * z3_replayer::get_uint_array(unsigned pos) const { return m_imp->get_uint_array(pos); } int * z3_replayer::get_int_array(unsigned pos) const { return m_imp->get_int_array(pos); } Z3_symbol * z3_replayer::get_symbol_array(unsigned pos) const { return m_imp->get_symbol_array(pos); } void ** z3_replayer::get_obj_array(unsigned pos) const { return m_imp->get_obj_array(pos); } int * z3_replayer::get_int_addr(unsigned pos) { return m_imp->get_int_addr(pos); } __int64 * z3_replayer::get_int64_addr(unsigned pos) { return m_imp->get_int64_addr(pos); } unsigned * z3_replayer::get_uint_addr(unsigned pos) { return m_imp->get_uint_addr(pos); } __uint64 * z3_replayer::get_uint64_addr(unsigned pos) { return m_imp->get_uint64_addr(pos); } Z3_string * z3_replayer::get_str_addr(unsigned pos) { return m_imp->get_str_addr(pos); } void ** z3_replayer::get_obj_addr(unsigned pos) { return m_imp->get_obj_addr(pos); } void z3_replayer::store_result(void * obj) { return m_imp->store_result(obj); } void z3_replayer::register_cmd(unsigned id, z3_replayer_cmd cmd) { return m_imp->register_cmd(id, cmd); } void z3_replayer::parse() { return m_imp->parse(); } z3-z3-4.4.1/src/api/z3_replayer.h000066400000000000000000000030341260446376700163750ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/api/z3_v1.h000066400000000000000000000043031260446376700151000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_v1.h Abstract: Z3 1.x backwards compatibility macros. These macros are used to simulate the Z3 API using in the 1.x versions. This file should only be used by users still using the Z3 1.x API. Author: Leonardo de Moura (leonardo) 2011-09-22 Notes: --*/ #ifndef Z3_V1_H_ #define Z3_V1_H_ #include"z3.h" // Backwards compatibility #define Z3_type_ast Z3_sort #define Z3_const_decl_ast Z3_func_decl #define Z3_const Z3_app #define Z3_pattern_ast Z3_pattern #define Z3_UNINTERPRETED_TYPE Z3_UNINTERPRETED_SORT #define Z3_BOOL_TYPE Z3_BOOL_SORT #define Z3_INT_TYPE Z3_INT_SORT #define Z3_REAL_TYPE Z3_REAL_SORT #define Z3_BV_TYPE Z3_BV_SORT #define Z3_ARRAY_TYPE Z3_ARRAY_SORT #define Z3_TUPLE_TYPE Z3_DATATYPE_SORT #define Z3_UNKNOWN_TYPE Z3_UNKNOWN_SORT #define Z3_CONST_DECL_AST Z3_FUNC_DECL_AST #define Z3_TYPE_AST Z3_SORT_AST #define Z3_SORT_ERROR Z3_TYPE_ERROR #define Z3_mk_uninterpreted_type Z3_mk_uninterpreted_sort #define Z3_mk_bool_type Z3_mk_bool_sort #define Z3_mk_int_type Z3_mk_int_sort #define Z3_mk_real_type Z3_mk_real_sort #define Z3_mk_bv_type Z3_mk_bv_sort #define Z3_mk_array_type Z3_mk_array_sort #define Z3_mk_tuple_type Z3_mk_tuple_sort #define Z3_get_type Z3_get_sort #define Z3_get_pattern_ast Z3_get_pattern #define Z3_get_type_kind Z3_get_sort_kind #define Z3_get_type_name Z3_get_sort_name #define Z3_get_bv_type_size Z3_get_bv_sort_size #define Z3_get_array_type_domain Z3_get_array_sort_domain #define Z3_get_array_type_range Z3_get_array_sort_range #define Z3_get_tuple_type_num_fields Z3_get_tuple_sort_num_fields #define Z3_get_tuple_type_field_decl Z3_get_tuple_sort_field_decl #define Z3_get_tuple_type_mk_decl Z3_get_tuple_sort_mk_decl #define Z3_to_const_ast Z3_to_app #define Z3_get_numeral_value_string Z3_get_numeral_string #define Z3_get_const_ast_decl Z3_get_app_decl #define Z3_get_value Z3_eval_func_decl #endif z3-z3-4.4.1/src/ast/000077500000000000000000000000001260446376700140035ustar00rootroot00000000000000z3-z3-4.4.1/src/ast/act_cache.cpp000066400000000000000000000134461260446376700164110ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/act_cache.h000066400000000000000000000022351260446376700160500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/arith_decl_plugin.cpp000066400000000000000000000565101260446376700201720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/arith_decl_plugin.h000066400000000000000000000355151260446376700176410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/array_decl_plugin.cpp000066400000000000000000000541111260446376700201740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/array_decl_plugin.h000066400000000000000000000132051260446376700176400ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast.cpp000066400000000000000000003460131260446376700153050ustar00rootroot00000000000000/*++ 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(unsigned n, sort* const* srts) { SASSERT(n > 0); sort* s = srts[0]; while (n > 1) { ++srts; --n; s = join(s, *srts); } return s; } sort* basic_decl_plugin::join(unsigned n, expr* const* es) { SASSERT(n > 0); sort* s = m_manager->get_sort(*es); while (n > 1) { ++es; --n; s = join(s, m_manager->get_sort(*es)); } return s; } 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; } std::ostringstream buffer; buffer << "Sorts " << mk_pp(s1, *m_manager) << " and " << mk_pp(s2, *m_manager) << " are incompatible"; throw ast_exception(buffer.str().c_str()); } 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(arity, domain), m_eq_decls) : 0; case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(arity, domain), 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(num_args, args), m_eq_decls) : 0; case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(num_args, args), 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) { app * r = 0; app * new_node = 0; unsigned sz = app::get_obj_size(num_args); void * mem = allocate_node(sz); try { 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]); } } } check_args(decl, num_args, new_args.c_ptr()); SASSERT(new_args.size() == num_args); new_node = new (mem)app(decl, num_args, new_args.c_ptr()); r = register_node(new_node); } else { check_args(decl, num_args, args); 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"; } } } catch (...) { deallocate_node(static_cast(mem), sz); throw; } return r; } void ast_manager::check_args(func_decl* f, unsigned n, expr* const* es) { for (unsigned i = 0; i < n; i++) { sort * actual_sort = get_sort(es[i]); sort * expected_sort = f->is_associative() ? f->get_domain(0) : f->get_domain(i); if (expected_sort != actual_sort) { std::ostringstream buffer; buffer << "Sort mismatch at argument #" << (i+1) << " for function " << mk_pp(f,*this) << " supplied sort is " << mk_pp(actual_sort, *this); throw ast_exception(buffer.str().c_str()); } } } 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) { bool type_error = decl->get_arity() != num_args && !decl->is_right_associative() && !decl->is_left_associative() && !decl->is_chainable(); type_error |= (decl->get_arity() != num_args && num_args < 2 && decl->get_family_id() == m_basic_family_id && !decl->is_associative()); if (type_error) { 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_iff(get_fact(p1)) || is_eq(get_fact(p1))) && (is_iff(get_fact(p2)) || is_eq(get_fact(p2)))) || ( (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-z3-4.4.1/src/ast/ast.h000066400000000000000000003004121260446376700147430ustar00rootroot00000000000000/*++ 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" #include"rlimit.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 = (1u << 31u); /** \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); sort* join(unsigned n, sort*const* srts); sort* join(unsigned n, expr*const* es); 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: reslimit m_limit; 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); void check_args(func_decl* f, unsigned n, expr* const* es); 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; } reslimit& limit() { return m_limit; } 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-z3-4.4.1/src/ast/ast_ll_pp.cpp000066400000000000000000000216411260446376700164700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_ll_pp.h000066400000000000000000000030521260446376700161310ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_lt.cpp000066400000000000000000000143061260446376700160010ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_lt.h000066400000000000000000000012611260446376700154420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_pp.h000066400000000000000000000013241260446376700154420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_pp_util.cpp000066400000000000000000000027461260446376700170430ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: ast_pp_util.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2015-8-6. Revision History: --*/ #include "ast_pp_util.h" #include "ast_smt2_pp.h" #include "ast_smt_pp.h" void ast_pp_util::collect(expr* e) { coll.visit(e); } void ast_pp_util::collect(unsigned n, expr* const* es) { for (unsigned i = 0; i < n; ++i) { coll.visit(es[i]); } } void ast_pp_util::collect(expr_ref_vector const& es) { collect(es.size(), es.c_ptr()); } void ast_pp_util::display_decls(std::ostream& out) { smt2_pp_environment_dbg env(m); ast_smt_pp pp(m); unsigned n = coll.get_num_sorts(); for (unsigned i = 0; i < n; ++i) { pp.display_ast_smt2(out, coll.get_sorts()[i], 0, 0, 0); } n = coll.get_num_decls(); for (unsigned i = 0; i < n; ++i) { ast_smt2_pp(out, coll.get_func_decls()[i], env); out << "\n"; } } void ast_pp_util::display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat) { if (neat) { smt2_pp_environment_dbg env(m); for (unsigned i = 0; i < fmls.size(); ++i) { out << "(assert "; ast_smt2_pp(out, fmls[i], env); out << ")\n"; } } else { ast_smt_pp ll_smt2_pp(m); for (unsigned i = 0; i < fmls.size(); ++i) { out << "(assert "; ll_smt2_pp.display_expr_smt2(out, fmls[i]); out << ")\n"; } } } z3-z3-4.4.1/src/ast/ast_pp_util.h000066400000000000000000000013541260446376700165020ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: ast_pp_util.h Abstract: Utilities for printing SMT2 declarations and assertions. Author: Nikolaj Bjorner (nbjorner) 2015-8-6. Revision History: --*/ #ifndef AST_PP_UTIL_H_ #define AST_PP_UTIL_H_ #include "decl_collector.h" class ast_pp_util { ast_manager& m; public: decl_collector coll; ast_pp_util(ast_manager& m): m(m), coll(m, false) {} void collect(expr* e); void collect(unsigned n, expr* const* es); void collect(expr_ref_vector const& es); void display_decls(std::ostream& out); void display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat = true); }; #endif /* AST_PP_UTIL_H_ */ z3-z3-4.4.1/src/ast/ast_printer.cpp000066400000000000000000000037561260446376700170540ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_printer.h000066400000000000000000000032731260446376700165130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_smt2_pp.cpp000066400000000000000000001231701260446376700167460ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_smt2_pp.h000066400000000000000000000114661260446376700164170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_smt_pp.cpp000066400000000000000000001076761260446376700167010ustar00rootroot00000000000000/*++ 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.empty() || !m_is_smt2) { 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-z3-4.4.1/src/ast/ast_smt_pp.h000066400000000000000000000061471260446376700163350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_trail.h000066400000000000000000000025161260446376700161420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_translation.cpp000066400000000000000000000312371260446376700177220ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_translation.h000066400000000000000000000051041260446376700173610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/ast_util.cpp000066400000000000000000000212001260446376700163260ustar00rootroot00000000000000/*++ 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()); } 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); } z3-z3-4.4.1/src/ast/ast_util.h000066400000000000000000000072401260446376700160030ustar00rootroot00000000000000/*++ 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); /** \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); #endif /* AST_UTIL_H_ */ z3-z3-4.4.1/src/ast/bv_decl_plugin.cpp000066400000000000000000001013411260446376700174630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/bv_decl_plugin.h000066400000000000000000000364331260446376700171410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/datatype_decl_plugin.cpp000066400000000000000000001136151260446376700206760ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/datatype_decl_plugin.h000066400000000000000000000207321260446376700203400ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/decl_collector.cpp000066400000000000000000000052271260446376700174720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/decl_collector.h000066400000000000000000000026151260446376700171350ustar00rootroot00000000000000/*++ 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); void visit(unsigned n, expr* const* es); void visit(expr_ref_vector const& es); 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-z3-4.4.1/src/ast/dl_decl_plugin.cpp000066400000000000000000000730451260446376700174640ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/dl_decl_plugin.h000066400000000000000000000213001260446376700171140ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr2polynomial.cpp000066400000000000000000000372401260446376700176610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr2polynomial.h000066400000000000000000000062751260446376700173320ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr2var.cpp000066400000000000000000000043061260446376700162630ustar00rootroot00000000000000/*++ 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_recent_lim.reset(); m_interpreted_vars = false; } void expr2var::push() { m_recent_lim.push_back(m_recent_exprs.size()); } void expr2var::pop(unsigned num_scopes) { if (num_scopes > 0) { unsigned sz = m_recent_lim[m_recent_lim.size() - num_scopes]; for (unsigned i = sz; i < m_recent_exprs.size(); ++i) { m_mapping.erase(m_recent_exprs[i]); m().dec_ref(m_recent_exprs[i]); } m_recent_exprs.shrink(sz); m_recent_lim.shrink(m_recent_lim.size() - num_scopes); } } z3-z3-4.4.1/src/ast/expr2var.h000066400000000000000000000036121260446376700157270ustar00rootroot00000000000000/*++ 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; unsigned_vector m_recent_lim; 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() { SASSERT(m_recent_lim.empty()); 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(); void push(); void pop(unsigned num_scopes); }; #endif z3-z3-4.4.1/src/ast/expr_abstract.cpp000066400000000000000000000054771260446376700173650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr_abstract.h000066400000000000000000000013401260446376700170130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr_delta_pair.h000066400000000000000000000013771260446376700173260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr_functors.cpp000066400000000000000000000062041260446376700174120ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr_functors.h000066400000000000000000000043161260446376700170610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr_map.cpp000066400000000000000000000041551260446376700163270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr_map.h000066400000000000000000000022361260446376700157720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr_stat.cpp000066400000000000000000000040751260446376700165260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr_stat.h000066400000000000000000000020111260446376700161570ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr_substitution.cpp000066400000000000000000000104071260446376700203230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/expr_substitution.h000066400000000000000000000026721260446376700177750ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/for_each_ast.cpp000066400000000000000000000015751260446376700171340ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/for_each_ast.h000066400000000000000000000214741260446376700166010ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/for_each_expr.cpp000066400000000000000000000027461260446376700173240ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/for_each_expr.h000066400000000000000000000111171260446376700167610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/format.cpp000066400000000000000000000157661260446376700160160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/format.h000066400000000000000000000154411260446376700154510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/fpa/000077500000000000000000000000001260446376700145515ustar00rootroot00000000000000z3-z3-4.4.1/src/ast/fpa/fpa2bv_converter.cpp000066400000000000000000004220201260446376700205240ustar00rootroot00000000000000/*++ 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_output(sort * rng, func_decl * fbv, expr_ref_buffer & new_args, expr_ref & result) { if (m_util.is_float(rng)) { unsigned ebits = m_util.get_ebits(rng); unsigned sbits = m_util.get_sbits(rng); unsigned bv_sz = ebits + sbits; app_ref na(m); na = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); mk_fp(m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, na), m_bv_util.mk_extract(bv_sz - 2, sbits - 1, na), m_bv_util.mk_extract(sbits - 2, 0, na), result); } else result = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); } void fpa2bv_converter::mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv", 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, *exp, *sig; split_fp(args[i], sgn, exp, sig); expr * args[3] = { sgn, exp, sig }; new_args.push_back(m_bv_util.mk_concat(3, args)); } else new_args.push_back(args[i]); } func_decl * fd; if (m_uf2bvuf.find(f, fd)) mk_uninterpreted_output(f->get_range(), fd, new_args, result); else { sort_ref_buffer new_domain(m); for (unsigned i = 0; i < f->get_arity(); i++) { sort * di = f->get_domain()[i]; if (is_float(di)) new_domain.push_back(m_bv_util.mk_sort(m_util.get_sbits(di) + m_util.get_ebits(di))); else if (is_rm(di)) new_domain.push_back(m_bv_util.mk_sort(3)); else new_domain.push_back(di); } sort * orig_rng = f->get_range(); sort_ref rng(orig_rng, m); if (m_util.is_float(orig_rng)) rng = m_bv_util.mk_sort(m_util.get_ebits(orig_rng) + m_util.get_sbits(orig_rng)); else if (m_util.is_rm(orig_rng)) rng = m_bv_util.mk_sort(3); func_decl_ref fbv(m); fbv = m.mk_fresh_func_decl(new_domain.size(), new_domain.c_ptr(), rng); TRACE("fpa2bv", 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); mk_uninterpreted_output(f->get_range(), fbv, new_args, result); } TRACE("fpa2bv", 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_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result) { TRACE("fpa2bv_to_bv", 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); if (!is_signed) c1 = m.mk_or(x_is_nan, x_is_inf, m.mk_and(x_is_neg, m.mk_not(x_is_nzero))); else c1 = m.mk_or(x_is_nan, x_is_inf); v1 = mk_to_ubv_unspecified(bv_sz); dbg_decouple("fpa2bv_to_bv_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_bv_c2", c2); // Otherwise... expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); dbg_decouple("fpa2bv_to_bv_sgn", sgn); dbg_decouple("fpa2bv_to_bv_sig", sig); dbg_decouple("fpa2bv_to_bv_exp", exp); dbg_decouple("fpa2bv_to_bv_lz", lz); // 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); 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), e_m_lz_m_bv_sz(m), shift(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)); e_m_lz_m_bv_sz = m_bv_util.mk_bv_sub(exp_m_lz, m_bv_util.mk_numeral(bv_sz - 1, ebits + 2)); shift = m_bv_util.mk_bv_neg(e_m_lz_m_bv_sz); bv0_e2 = m_bv_util.mk_numeral(0, ebits + 2); shift_abs = m.mk_ite(m_bv_util.mk_sle(shift, bv0_e2), e_m_lz_m_bv_sz, shift); SASSERT(m_bv_util.get_bv_size(shift) == ebits + 2); SASSERT(m_bv_util.get_bv_size(shift_abs) == ebits + 2); dbg_decouple("fpa2bv_to_bv_shift", shift); dbg_decouple("fpa2bv_to_bv_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 ... ] 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); if (!is_signed) c_in_limits = m_bv_util.mk_sle(bv0_e2, shift); else c_in_limits = m.mk_or(m_bv_util.mk_sle(m_bv_util.mk_numeral(1, ebits + 2), shift), m.mk_and(m.mk_eq(m_bv_util.mk_numeral(0, ebits + 2), shift), m.mk_eq(sig, m_bv_util.mk_concat(bv1, m_bv_util.mk_numeral(0, sig_sz-1))))); dbg_decouple("fpa2bv_to_bv_in_limits", c_in_limits); expr_ref r_shifted_sig(m), l_shifted_sig(m); r_shifted_sig = m_bv_util.mk_bv_lshr(sig, shift_abs); l_shifted_sig = m_bv_util.mk_bv_shl(sig, m_bv_util.mk_bv_sub( m_bv_util.mk_numeral(m_bv_util.get_bv_size(sig), m_bv_util.get_bv_size(sig)), shift_abs)); dbg_decouple("fpa2bv_to_bv_r_shifted_sig", r_shifted_sig); dbg_decouple("fpa2bv_to_bv_l_shifted_sig", l_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, r_shifted_sig); round = m_bv_util.mk_extract(sig_sz - bv_sz - 1, sig_sz - bv_sz - 1, r_shifted_sig); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, l_shifted_sig.get()); dbg_decouple("fpa2bv_to_bv_last", last); dbg_decouple("fpa2bv_to_bv_round", round); dbg_decouple("fpa2bv_to_bv_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_bv_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, r_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); dbg_decouple("fpa2bv_to_bv_inc", inc); dbg_decouple("fpa2bv_to_bv_pre_rounded", pre_rounded); 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_bv_rnd_has_overflown", rnd_has_overflown); if (is_signed) rnd = m.mk_ite(m.mk_eq(sgn, bv1), m_bv_util.mk_bv_neg(rnd), rnd); dbg_decouple("fpa2bv_to_bv_rnd", rnd); 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_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;); mk_to_bv(f, num, args, false, 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;); mk_to_bv(f, num, args, true, 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); dbg_decouple("fpa2bv_unpack_sgn", sgn); dbg_decouple("fpa2bv_unpack_exp", exp); dbg_decouple("fpa2bv_unpack_sig", sig); 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); 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); m_extra_assertions.reset(); } z3-z3-4.4.1/src/ast/fpa/fpa2bv_converter.h000066400000000000000000000216411260446376700201750ustar00rootroot00000000000000/*++ 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_H_ #define FPA2BV_CONVERTER_H_ #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; 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); virtual 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; } 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); void mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result); void mk_uninterpreted_output(sort * rng, func_decl * fbv, expr_ref_buffer & new_args, expr_ref & result); }; #endif z3-z3-4.4.1/src/ast/fpa/fpa2bv_rewriter.h000066400000000000000000000305641260446376700200350ustar00rootroot00000000000000/*++ 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); TRACE("fpa2bv_rw", tout << "(= " << mk_ismt2_pp(args[0], m()) << " " << mk_ismt2_pp(args[1], m()) << ")" << std::endl;); 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() != 0 && f->get_family_id() != m_conv.fu().get_family_id()) { bool is_float_uf = m_conv.is_float(f->get_range()) || m_conv.is_rm(f->get_range()); for (unsigned i = 0; i < num; i++) is_float_uf |= m_conv.is_float(f->get_domain()[i]) || m_conv.is_rm(f->get_domain()[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-z3-4.4.1/src/ast/fpa/fpa2bv_rewriter_params.pyg000066400000000000000000000004441260446376700217420ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/fpa_decl_plugin.cpp000066400000000000000000001316701260446376700176320ustar00rootroot00000000000000/*++ 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_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 to_ieee_bv"); 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_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_ieee_bv_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-z3-4.4.1/src/ast/fpa_decl_plugin.h000066400000000000000000000374161260446376700173020ustar00rootroot00000000000000/*++ 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_IEEE_BV_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_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_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_ieee_bv_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-z3-4.4.1/src/ast/func_decl_dependencies.cpp000066400000000000000000000135301260446376700211410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/func_decl_dependencies.h000066400000000000000000000054531260446376700206130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/has_free_vars.cpp000066400000000000000000000045621260446376700173250ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/has_free_vars.h000066400000000000000000000004721260446376700167660ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/macro_substitution.cpp000066400000000000000000000123641260446376700204520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/macro_substitution.h000066400000000000000000000033421260446376700201130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/macros/000077500000000000000000000000001260446376700152675ustar00rootroot00000000000000z3-z3-4.4.1/src/ast/macros/macro_finder.cpp000066400000000000000000000204131260446376700204230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/macros/macro_finder.h000066400000000000000000000033141260446376700200710ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/macros/macro_manager.cpp000066400000000000000000000244611260446376700205750ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/macros/macro_manager.h000066400000000000000000000064641260446376700202450ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/macros/macro_util.cpp000066400000000000000000000751471260446376700201470ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/macros/macro_util.h000066400000000000000000000140011260446376700175720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/macros/quasi_macros.cpp000066400000000000000000000256401260446376700204700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/macros/quasi_macros.h000066400000000000000000000036341260446376700201340ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/normal_forms/000077500000000000000000000000001260446376700165015ustar00rootroot00000000000000z3-z3-4.4.1/src/ast/normal_forms/defined_names.cpp000066400000000000000000000266361260446376700220030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/normal_forms/defined_names.h000066400000000000000000000050071260446376700214350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/normal_forms/name_exprs.cpp000066400000000000000000000110541260446376700213470ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/normal_forms/name_exprs.h000066400000000000000000000034121260446376700210130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/normal_forms/nnf.cpp000066400000000000000000000737471260446376700200100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/normal_forms/nnf.h000066400000000000000000000024001260446376700174270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/normal_forms/nnf_params.pyg000066400000000000000000000012351260446376700213470ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/normal_forms/pull_quant.cpp000066400000000000000000000363201260446376700213750ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/normal_forms/pull_quant.h000066400000000000000000000021711260446376700210370ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/num_occurs.cpp000066400000000000000000000042071260446376700166670ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/num_occurs.h000066400000000000000000000021051260446376700163270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/occurs.cpp000066400000000000000000000026121260446376700160060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/occurs.h000066400000000000000000000006731260446376700154600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/pattern/000077500000000000000000000000001260446376700154605ustar00rootroot00000000000000z3-z3-4.4.1/src/ast/pattern/database.smt2000066400000000000000000000362201260446376700200360ustar00rootroot00000000000000(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-z3-4.4.1/src/ast/pattern/expr_pattern_match.cpp000066400000000000000000000337751260446376700220720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/pattern/expr_pattern_match.h000066400000000000000000000076471260446376700215360ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/pattern/pattern_inference.cpp000066400000000000000000000652551260446376700216740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/pattern/pattern_inference.h000066400000000000000000000175651260446376700213420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/pattern/pattern_inference_params.cpp000066400000000000000000000015601260446376700232240ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/pattern/pattern_inference_params.h000066400000000000000000000025741260446376700226770ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/pattern/pattern_inference_params_helper.pyg000066400000000000000000000027431260446376700246040ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/pb_decl_plugin.cpp000066400000000000000000000200451260446376700174560ustar00rootroot00000000000000/*++ 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; } app* pb_util::mk_fresh_bool() { symbol name = m.mk_fresh_var_name("pb"); func_decl_info info(m_fid, OP_PB_AUX_BOOL, 0, 0); return m.mk_const(m.mk_func_decl(name, 0, (sort *const*)0, m.mk_bool_sort(), info)); } z3-z3-4.4.1/src/ast/pb_decl_plugin.h000066400000000000000000000104031260446376700171200ustar00rootroot00000000000000/*++ 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 OP_PB_AUX_BOOL, // auxiliary internal Boolean variable. 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; bool is_aux_bool(expr* e) const { return is_app_of(e, m_fid, OP_PB_AUX_BOOL); } 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; app* mk_fresh_bool(); private: rational to_rational(parameter const& p) const; }; #endif /* PB_DECL_PLUGIN_H_ */ z3-z3-4.4.1/src/ast/pp.cpp000066400000000000000000000105041260446376700151260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/pp.h000066400000000000000000000005641260446376700146000ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/pp_params.pyg000066400000000000000000000037251260446376700165150ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/proof_checker/000077500000000000000000000000001260446376700166145ustar00rootroot00000000000000z3-z3-4.4.1/src/ast/proof_checker/proof_checker.cpp000066400000000000000000001327261260446376700221440ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/ast/proof_checker/proof_checker.h000066400000000000000000000103571260446376700216040ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/recurse_expr.h000066400000000000000000000023121260446376700166600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/recurse_expr_def.h000066400000000000000000000066241260446376700175100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/reg_decl_plugins.cpp000066400000000000000000000031361260446376700200170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/reg_decl_plugins.h000066400000000000000000000005761260446376700174710ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/000077500000000000000000000000001260446376700156465ustar00rootroot00000000000000z3-z3-4.4.1/src/ast/rewriter/arith_rewriter.cpp000066400000000000000000001512771260446376700214210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/arith_rewriter.h000066400000000000000000000164341260446376700210610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/arith_rewriter_params.pyg000066400000000000000000000030251260446376700227640ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/rewriter/array_rewriter.cpp000066400000000000000000000340431260446376700214170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/array_rewriter.h000066400000000000000000000042051260446376700210610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/array_rewriter_params.pyg000066400000000000000000000007461260446376700230020ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/rewriter/ast_counter.cpp000066400000000000000000000074421260446376700207070ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/ast_counter.h000066400000000000000000000046661260446376700203610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/bit_blaster/000077500000000000000000000000001260446376700201405ustar00rootroot00000000000000z3-z3-4.4.1/src/ast/rewriter/bit_blaster/bit_blaster.cpp000066400000000000000000000075631260446376700231510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/bit_blaster/bit_blaster.h000066400000000000000000000042151260446376700226050ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/bit_blaster/bit_blaster_params.h000066400000000000000000000013651260446376700241530ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp000066400000000000000000000607671260446376700251010ustar00rootroot00000000000000/*++ 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; func_decl_ref_vector m_keys; expr_ref_vector m_values; unsigned_vector m_keyval_lim; 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), m_keys(m), m_values(m) { updt_params(p); } ~blaster_rewriter_cfg() { } 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()); } } void push() { m_keyval_lim.push_back(m_keys.size()); } void pop(unsigned num_scopes) { if (num_scopes > 0) { unsigned new_sz = m_keyval_lim.size() - num_scopes; unsigned lim = m_keyval_lim[new_sz]; for (unsigned i = m_keys.size(); i > lim; ) { --i; m_const2bits.remove(m_keys[i].get()); } m_keys.resize(lim); m_values.resize(lim); m_keyval_lim.resize(new_sz); } } 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_keys.push_back(f); m_values.push_back(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")); } void push() { m_cfg.push(); } void pop(unsigned s) { m_cfg.pop(s); } }; 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); } void bit_blaster_rewriter::push() { m_imp->push(); } void bit_blaster_rewriter::pop(unsigned num_scopes) { m_imp->pop(num_scopes); } 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-z3-4.4.1/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h000066400000000000000000000015001260446376700245220ustar00rootroot00000000000000/*++ 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); void push(); void pop(unsigned num_scopes); }; #endif z3-z3-4.4.1/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h000066400000000000000000000170311260446376700234640ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h000066400000000000000000001264011260446376700243040ustar00rootroot00000000000000/*++ 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; iget_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-z3-4.4.1/src/ast/rewriter/bool_rewriter.h000066400000000000000000000172101260446376700206760ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/bool_rewriter_params.pyg000066400000000000000000000021051260446376700226060ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/rewriter/bv_rewriter.cpp000066400000000000000000002030071260446376700207060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/bv_rewriter.h000066400000000000000000000202761260446376700203600ustar00rootroot00000000000000/*++ 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); bool hi_div0() const { return m_hi_div0; } }; #endif z3-z3-4.4.1/src/ast/rewriter/bv_rewriter_params.pyg000066400000000000000000000021501260446376700222620ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/rewriter/datatype_rewriter.cpp000066400000000000000000000107671260446376700221230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/datatype_rewriter.h000066400000000000000000000013471260446376700215620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/der.cpp000066400000000000000000000347171260446376700171400ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/der.h000066400000000000000000000151471260446376700166010ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/dl_rewriter.cpp000066400000000000000000000026271260446376700207030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/dl_rewriter.h000066400000000000000000000011341260446376700203400ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/expr_replacer.cpp000066400000000000000000000103461260446376700212110ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/expr_replacer.h000066400000000000000000000030761260446376700206600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/expr_safe_replace.cpp000066400000000000000000000064311260446376700220250ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/expr_safe_replace.h000066400000000000000000000017121260446376700214670ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/factor_rewriter.cpp000066400000000000000000000245771260446376700215720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/factor_rewriter.h000066400000000000000000000044001260446376700212160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/fpa_rewriter.cpp000066400000000000000000000673331260446376700210570ustar00rootroot00000000000000/*++ 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_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(f, args[0], result); break; case OP_FPA_TO_REAL: SASSERT(num_args == 1); st = mk_to_real(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_ubv_unspecified(bv_sz); 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_sbv_unspecified(bv_sz); return BR_DONE; } br_status fpa_rewriter::mk_to_ieee_bv_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_ieee_bv_unspecified(bv_sz); 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.fma(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_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)) { mk_to_ubv_unspecified(f, result); 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 mk_to_ubv_unspecified(f, result); 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)) { mk_to_sbv_unspecified(f, result); 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 mk_to_sbv_unspecified(f, result); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_to_ieee_bv(func_decl * f, 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)) { mk_to_ieee_bv_unspecified(f, result); return BR_REWRITE_FULL; } bv_util bu(m()); scoped_mpz rz(m_fm.mpq_manager()); m_fm.to_ieee_bv_mpz(v, rz); result = bu.mk_numeral(rational(rz), v.get().get_ebits() + v.get().get_sbits()); 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-z3-4.4.1/src/ast/rewriter/fpa_rewriter.h000066400000000000000000000072761260446376700205240ustar00rootroot00000000000000/*++ 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_ieee_bv(func_decl * f, expr * arg, 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_ieee_bv_unspecified(func_decl * f, expr_ref & result); br_status mk_to_real_unspecified(expr_ref & result); }; #endif z3-z3-4.4.1/src/ast/rewriter/fpa_rewriter_params.pyg000066400000000000000000000004251260446376700224240ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/rewriter/mk_simplified_app.cpp000066400000000000000000000064721260446376700220370ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/mk_simplified_app.h000066400000000000000000000012751260446376700215000ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/pb_rewriter.cpp000066400000000000000000000171211260446376700207000ustar00rootroot00000000000000/*++ 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_util.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: { bool all_unit = true; unsigned sz = vec.size(); m_args.reset(); m_coeffs.reset(); for (unsigned i = 0; i < sz; ++i) { m_args.push_back(vec[i].first); m_coeffs.push_back(vec[i].second); all_unit &= m_coeffs.back().is_one(); } if (is_eq) { result = m_util.mk_eq(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k); } else if (all_unit && k.is_one()) { result = mk_or(m, sz, m_args.c_ptr()); } else if (all_unit && k == rational(sz)) { result = mk_and(m, sz, m_args.c_ptr()); } else { result = m_util.mk_ge(sz, 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-z3-4.4.1/src/ast/rewriter/pb_rewriter.h000066400000000000000000000032311260446376700203420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/pb_rewriter_def.h000066400000000000000000000205021260446376700211600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/poly_rewriter.h000066400000000000000000000133651260446376700207350ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: poly_rewriter.h Abstract: Basic rewriting rules for Polynomials. Author: Leonardo (leonardo) 2011-04-08 Notes: --*/ #ifndef POLY_REWRITER_H_ #define POLY_REWRITER_H_ #include"ast.h" #include"obj_hashtable.h" #include"rewriter_types.h" #include"params.h" template 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-z3-4.4.1/src/ast/rewriter/poly_rewriter_def.h000066400000000000000000000741421260446376700215530ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/poly_rewriter_params.pyg000066400000000000000000000014001260446376700226330ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/rewriter/quant_hoist.cpp000066400000000000000000000222521260446376700207130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/quant_hoist.h000066400000000000000000000035271260446376700203640ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/rewriter.cpp000066400000000000000000000252201260446376700202160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/rewriter.h000066400000000000000000000342431260446376700176700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/rewriter.txt000066400000000000000000000126641260446376700202630ustar00rootroot00000000000000 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-z3-4.4.1/src/ast/rewriter/rewriter_def.h000066400000000000000000000532351260446376700205100ustar00rootroot00000000000000/*++ 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); if (!m().limit().inc()) throw rewriter_exception(Z3_MAX_RESOURCE_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-z3-4.4.1/src/ast/rewriter/rewriter_params.pyg000066400000000000000000000014111260446376700215720ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/rewriter/rewriter_types.h000066400000000000000000000024541260446376700211130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/th_rewriter.cpp000066400000000000000000000645761260446376700207320ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/th_rewriter.h000066400000000000000000000031021260446376700203510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/var_subst.cpp000066400000000000000000000165061260446376700203720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/rewriter/var_subst.h000066400000000000000000000052731260446376700200360ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/scoped_proof.h000066400000000000000000000020201260446376700166300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/seq_decl_plugin.cpp000066400000000000000000000243521260446376700176520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/seq_decl_plugin.h000066400000000000000000000050151260446376700173120ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/shared_occs.cpp000066400000000000000000000067141260446376700167740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/shared_occs.h000066400000000000000000000043261260446376700164360ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/000077500000000000000000000000001260446376700161465ustar00rootroot00000000000000z3-z3-4.4.1/src/ast/simplifier/README000066400000000000000000000002271260446376700170270ustar00rootroot00000000000000Simplifier 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-z3-4.4.1/src/ast/simplifier/arith_simplifier_params.cpp000066400000000000000000000007661260446376700235600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/arith_simplifier_params.h000066400000000000000000000011111260446376700232060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/arith_simplifier_params_helper.pyg000066400000000000000000000010321260446376700251170ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/simplifier/arith_simplifier_plugin.cpp000066400000000000000000000342671260446376700235760ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/arith_simplifier_plugin.h000066400000000000000000000071511260446376700232330ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/array_simplifier_params.cpp000066400000000000000000000010171260446376700235550ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/array_simplifier_params.h000066400000000000000000000012571260446376700232300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/array_simplifier_params_helper.pyg000066400000000000000000000006651260446376700251410ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/simplifier/array_simplifier_plugin.cpp000066400000000000000000000701051260446376700235740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/array_simplifier_plugin.h000066400000000000000000000131761260446376700232460ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/base_simplifier.h000066400000000000000000000036731260446376700214650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/basic_simplifier_plugin.cpp000066400000000000000000000141621260446376700235400ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/basic_simplifier_plugin.h000066400000000000000000000051601260446376700232030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/bit2int.cpp000066400000000000000000000317611260446376700202350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/bit2int.h000066400000000000000000000044511260446376700176760ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/bv_elim.cpp000066400000000000000000000073061260446376700202750ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/ast/simplifier/bv_elim.h000066400000000000000000000015051260446376700177350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/bv_simplifier_params.cpp000066400000000000000000000007161260446376700230530ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/bv_simplifier_params.h000066400000000000000000000013201260446376700225100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/bv_simplifier_params_helper.pyg000066400000000000000000000010431260446376700244210ustar00rootroot00000000000000def_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-z3-4.4.1/src/ast/simplifier/bv_simplifier_plugin.cpp000066400000000000000000002301501260446376700230630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/bv_simplifier_plugin.h000066400000000000000000000202331260446376700225270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/datatype_simplifier_plugin.cpp000066400000000000000000000060301260446376700242650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/datatype_simplifier_plugin.h000066400000000000000000000015511260446376700237350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/distribute_forall.cpp000066400000000000000000000110131260446376700223630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/distribute_forall.h000066400000000000000000000042161260446376700220370ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/elim_bounds.cpp000066400000000000000000000137531260446376700211630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/elim_bounds.h000066400000000000000000000031071260446376700206200ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/fpa_simplifier_plugin.cpp000066400000000000000000000015521260446376700232240ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/fpa_simplifier_plugin.h000066400000000000000000000014231260446376700226660ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/inj_axiom.cpp000066400000000000000000000131051260446376700206270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/inj_axiom.h000066400000000000000000000005351260446376700202770ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/maximise_ac_sharing.cpp000066400000000000000000000124131260446376700226450ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/maximise_ac_sharing.h000066400000000000000000000064421260446376700223170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/poly_simplifier_plugin.cpp000066400000000000000000000613201260446376700234400ustar00rootroot00000000000000/*++ 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, d; 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; if (is_numeral(body, d)) { c_prime = norm(c_prime*d); if (c_prime.is_zero()) return 0; return mk_numeral(c_prime); } 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-z3-4.4.1/src/ast/simplifier/poly_simplifier_plugin.h000066400000000000000000000140001260446376700230760ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/pull_ite_tree.cpp000066400000000000000000000140711260446376700215110ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/pull_ite_tree.h000066400000000000000000000053361260446376700211620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/push_app_ite.cpp000066400000000000000000000132561260446376700213410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/push_app_ite.h000066400000000000000000000031111260446376700207730ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/simplifier.cpp000066400000000000000000000771251260446376700210310ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/simplifier.h000066400000000000000000000245321260446376700204700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/simplifier_plugin.cpp000066400000000000000000000020421260446376700223710ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/simplifier/simplifier_plugin.h000066400000000000000000000053401260446376700220420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/static_features.cpp000066400000000000000000000606671260446376700177130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/static_features.h000066400000000000000000000211261260446376700173430ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/000077500000000000000000000000001260446376700165575ustar00rootroot00000000000000z3-z3-4.4.1/src/ast/substitution/expr_offset.h000066400000000000000000000025711260446376700212610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/expr_offset_map.h000066400000000000000000000047121260446376700221150ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/matcher.cpp000066400000000000000000000032701260446376700207100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/matcher.h000066400000000000000000000024741260446376700203620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/substitution.cpp000066400000000000000000000263661260446376700220540ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/substitution.h000066400000000000000000000162401260446376700215070ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/substitution_tree.cpp000066400000000000000000000706301260446376700230640ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/substitution_tree.h000066400000000000000000000103351260446376700225250ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/unifier.cpp000066400000000000000000000117411260446376700207300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/unifier.h000066400000000000000000000034631260446376700203770ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/substitution/var_offset_map.h000066400000000000000000000056461260446376700217360ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/used_symbols.h000066400000000000000000000057471260446376700167010ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/used_vars.cpp000066400000000000000000000055161260446376700165110ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/used_vars.h000066400000000000000000000023371260446376700161540ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/well_sorted.cpp000066400000000000000000000051601260446376700170340ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/ast/well_sorted.h000066400000000000000000000005351260446376700165020ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/000077500000000000000000000000001260446376700155235ustar00rootroot00000000000000z3-z3-4.4.1/src/cmd_context/README000066400000000000000000000002761260446376700164100ustar00rootroot00000000000000Command 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-z3-4.4.1/src/cmd_context/basic_cmds.cpp000066400000000000000000000711371260446376700203270ustar00rootroot00000000000000/*++ 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"array_decl_plugin.h" #include"pp.h" #include"cmd_util.h" #include"simplify_cmd.h" #include"eval_cmd.h" #include"gparams.h" #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); ctx.display_model(m); }); 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-z3-4.4.1/src/cmd_context/basic_cmds.h000066400000000000000000000005401260446376700177620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/check_logic.cpp000066400000000000000000000344351260446376700204720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/check_logic.h000066400000000000000000000011261260446376700201260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/cmd_context.cpp000066400000000000000000001603731260446376700205500ustar00rootroot00000000000000/*++ 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" #include"model_v2_pp.h" #include"model_params.hpp" 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; unsigned rlimit = m_params.m_rlimit; 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); scoped_rlimit _rlimit(m().limit(), rlimit); 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); display_model(mdl); } 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); scoped_rlimit _rlimit(m().limit(), rlimit); 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(); if (m_params.m_dump_models) { model_ref md; get_check_sat_result()->get_model(md); display_model(md); } } } void cmd_context::display_model(model_ref& mdl) { if (mdl) { model_params p; if (p.v1() || p.v2()) { std::ostringstream buffer; model_v2_pp(buffer, *mdl, p.partial()); regular_stream() << "\"" << escaped(buffer.str().c_str(), true) << "\"" << std::endl; } else { regular_stream() << "(model " << std::endl; model_smt2_pp(regular_stream(), *this, *mdl, 2); regular_stream() << ")" << std::endl; } } } 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; if (show_total_time) st.update("total time", total_time); st.update("time", get_seconds()); get_memory_statistics(st); get_rlimit_statistics(m().limit(), st); 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-z3-4.4.1/src/cmd_context/cmd_context.h000066400000000000000000000441301260446376700202050ustar00rootroot00000000000000/*++ 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 display_model(model_ref& mdl); 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-z3-4.4.1/src/cmd_context/cmd_context_to_goal.cpp000066400000000000000000000027541260446376700222520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/cmd_context_to_goal.h000066400000000000000000000005651260446376700217150ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/cmd_util.cpp000066400000000000000000000015421260446376700200310ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/cmd_util.h000066400000000000000000000070331260446376700174770ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/context_params.cpp000066400000000000000000000150231260446376700212570ustar00rootroot00000000000000/*++ 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_dump_models = 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; m_rlimit = 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 { std::stringstream strm; strm << "invalid value '" << value << "' for Boolean parameter '" << param; throw default_exception(strm.str()); } } 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); } if (p == "rlimit") { long val = strtol(value, 0, 10); m_rlimit = 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 == "dump_models") { set_bool(m_dump_models, 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_rlimit = p.get_uint("rlimit", m_rlimit); 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_dump_models = p.get_bool("dump_models", m_dump_models); 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("rlimit", CPK_UINT, "default resource limit used for solvers", "4294967295"); d.insert("well_sorted_check", CPK_BOOL, "type checker", "false"); 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("dump_models", CPK_BOOL, "dump models whenever check-sat returns sat", "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-z3-4.4.1/src/cmd_context/context_params.h000066400000000000000000000035631260446376700207320ustar00rootroot00000000000000/*++ 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_dump_models; 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; unsigned m_rlimit; 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-z3-4.4.1/src/cmd_context/echo_tactic.cpp000066400000000000000000000045461260446376700205050ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/echo_tactic.h000066400000000000000000000010311260446376700201340ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/eval_cmd.cpp000066400000000000000000000044031260446376700200020ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/eval_cmd.h000066400000000000000000000004171260446376700174500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/extra_cmds/000077500000000000000000000000001260446376700176545ustar00rootroot00000000000000z3-z3-4.4.1/src/cmd_context/extra_cmds/dbg_cmds.cpp000066400000000000000000000306751260446376700221350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/extra_cmds/dbg_cmds.h000066400000000000000000000004651260446376700215740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/extra_cmds/polynomial_cmds.cpp000066400000000000000000000171451260446376700235610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/extra_cmds/polynomial_cmds.h000066400000000000000000000005121260446376700232140ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/extra_cmds/subpaving_cmds.cpp000066400000000000000000000027351260446376700233730ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/extra_cmds/subpaving_cmds.h000066400000000000000000000004121260446376700230260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/interpolant_cmds.cpp000066400000000000000000000202141260446376700215730ustar00rootroot00000000000000/*++ 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_ref foo(make_tree(ctx, exprs),ctx.m()); get_interpolant(ctx,foo.get(),m_params); } static void compute_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { expr_ref foo(make_tree(ctx, exprs),ctx.m()); compute_interpolant_and_maybe_check(ctx,foo.get(),m_params,false); } // 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-z3-4.4.1/src/cmd_context/interpolant_cmds.h000066400000000000000000000005061260446376700212420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/parametric_cmd.cpp000066400000000000000000000030371260446376700212040ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/parametric_cmd.h000066400000000000000000000045211260446376700206500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/pdecl.cpp000066400000000000000000001004271260446376700173220ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/pdecl.h000066400000000000000000000330611260446376700167660ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/simplify_cmd.cpp000066400000000000000000000106521260446376700207120ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/simplify_cmd.h000066400000000000000000000005341260446376700203550ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/tactic_cmds.cpp000066400000000000000000001052371260446376700205140ustar00rootroot00000000000000/*++ 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; get_memory_statistics(stats); get_rlimit_statistics(ctx.m().limit(), stats); stats.update("time", ctx.get_seconds()); 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-z3-4.4.1/src/cmd_context/tactic_cmds.h000066400000000000000000000024421260446376700201530ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/tactic_manager.cpp000066400000000000000000000024371260446376700211760ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/cmd_context/tactic_manager.h000066400000000000000000000027001260446376700206340ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/duality/000077500000000000000000000000001260446376700146675ustar00rootroot00000000000000z3-z3-4.4.1/src/duality/duality.h000066400000000000000000001350111260446376700165140ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/duality/duality_profiling.cpp000077500000000000000000000066221260446376700211300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/duality/duality_profiling.h000077500000000000000000000011331260446376700205650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/duality/duality_rpfp.cpp000077500000000000000000004667451260446376700201260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/duality/duality_solver.cpp000066400000000000000000004412271260446376700204520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/duality/duality_wrapper.cpp000077500000000000000000000615541260446376700206240ustar00rootroot00000000000000/*++ 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(); try { iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts); } // If there's an interpolation bug, throw a char * // exception so duality can catch it and restart. catch (const interpolation_failure &f) { throw f.msg(); } 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-z3-4.4.1/src/duality/duality_wrapper.h000066400000000000000000001533141260446376700202620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/000077500000000000000000000000001260446376700145155ustar00rootroot00000000000000z3-z3-4.4.1/src/interp/foci2.h000077500000000000000000000035421260446376700156770ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/foci2stub/000077500000000000000000000000001260446376700164155ustar00rootroot00000000000000z3-z3-4.4.1/src/interp/foci2stub/foci2.cpp000066400000000000000000000004201260446376700201170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/foci2stub/foci2.h000077500000000000000000000034301260446376700175730ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3base.cpp000077500000000000000000000277471260446376700166050ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3base.h000077500000000000000000000127571260446376700162450ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3checker.cpp000077500000000000000000000156061260446376700172660ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3checker.h000066400000000000000000000017011260446376700167170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3foci.cpp000077500000000000000000000277341260446376700166070ustar00rootroot00000000000000/*++ 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 "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"; SASSERT(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: SASSERT(false && "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){ SASSERT(n == 2); res = make(Select,args[0],args[1]); } else if(f == store_op){ SASSERT(n == 3); res = make(Store,args[0],args[1],args[2]); } else if(f == mod_op){ SASSERT(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); SASSERT(0); } res = make(func_decl,args); } } else { std::cerr << "iZ3: unknown FOCI expression kind\n"; SASSERT(0 && "iZ3: unknown FOCI expression kind"); } return res; } int interpolate(const std::vector &cnsts, std::vector &itps){ SASSERT((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"; SASSERT(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){ SASSERT((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-z3-4.4.1/src/interp/iz3foci.h000077500000000000000000000006451260446376700162440ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3hash.h000066400000000000000000000302561260446376700162450ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3interp.cpp000077500000000000000000000457401260446376700171650ustar00rootroot00000000000000/*++ 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"); try { tr->translate(proof,pf); } catch (const char *msg) { throw interpolation_failure(msg); } catch (const iz3translation::unsupported &) { throw interpolation_error(); } catch (const iz3proof::proof_error &) { throw interpolation_error(); } 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"); try { tr->translate(proof,pf); } catch (const char *msg) { throw interpolation_failure(msg); } catch (const iz3translation::unsupported &) { throw interpolation_error(); } catch (const iz3proof::proof_error &) { throw interpolation_error(); } 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-z3-4.4.1/src/interp/iz3interp.h000066400000000000000000000061511260446376700166200ustar00rootroot00000000000000/*++ 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 { }; // This is thrown if there is some bug in the // interpolation procedure class interpolation_failure : public default_exception { public: interpolation_failure(const char *msg) : default_exception(msg) { } }; // This is thrown if we cannot derive an interpolant from a proof // because it contains unsupported theories or if the proof contains // errors class interpolation_error : public default_exception { public: interpolation_error() : default_exception("theory not supported by interpolation or bad proof" ) { } }; 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-z3-4.4.1/src/interp/iz3mgr.cpp000077500000000000000000000676411260446376700164550ustar00rootroot00000000000000/*++ 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 (round_off && get_type(Qrhs) != int_type()) round_off = false; 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-z3-4.4.1/src/interp/iz3mgr.h000077500000000000000000000454161260446376700161160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3params.pyg000066400000000000000000000004721260446376700171520ustar00rootroot00000000000000def_module_params('interp', description='interpolation parameters', export=True, params=(('profile', BOOL, False, '(INTERP) profile interpolation'), ('check', BOOL, False, '(INTERP) check interpolants'), )) z3-z3-4.4.1/src/interp/iz3pp.cpp000066400000000000000000000105261260446376700162720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3pp.h000066400000000000000000000007371260446376700157420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3profiling.cpp000077500000000000000000000064601260446376700176510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3profiling.h000077500000000000000000000011011260446376700173010ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3proof.cpp000077500000000000000000000450531260446376700170060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3proof.h000077500000000000000000000174341260446376700164550ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3proof_itp.cpp000077500000000000000000003315611260446376700176640ustar00rootroot00000000000000/*++ 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 if (round_off && get_type(Qrhs) != int_type()) round_off = false; 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-z3-4.4.1/src/interp/iz3proof_itp.h000066400000000000000000000107321260446376700173200ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3scopes.cpp000077500000000000000000000214531260446376700171530ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3scopes.h000077500000000000000000000116661260446376700166250ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3secondary.h000077500000000000000000000011051260446376700173030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3translate.cpp000077500000000000000000002331301260446376700176510ustar00rootroot00000000000000/*++ 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; m().enable_int_real_coercions(true); 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_NOT_OR_ELIM: 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()); try { Iproof::node ipf = translate_main(proof); ast itp = iproof->interpolate(ipf); itps.push_back(itp); delete iproof; clear_translation(); } catch (const iz3proof_itp::proof_error &) { delete iproof; clear_translation(); throw iz3proof::proof_error(); } catch (const unsupported &exc) { delete iproof; clear_translation(); throw exc; } } // 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-z3-4.4.1/src/interp/iz3translate.h000077500000000000000000000025641260446376700173230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/interp/iz3translate_direct.cpp000077500000000000000000001666131260446376700212160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/math/000077500000000000000000000000001260446376700141455ustar00rootroot00000000000000z3-z3-4.4.1/src/math/euclid/000077500000000000000000000000001260446376700154125ustar00rootroot00000000000000z3-z3-4.4.1/src/math/euclid/README000066400000000000000000000001341260446376700162700ustar00rootroot00000000000000Basic Euclidean solver for linear integer equations. This solver generates "explanations". z3-z3-4.4.1/src/math/euclid/euclidean_solver.cpp000066400000000000000000000715631260446376700214550ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: euclidean_solver.cpp Abstract: Euclidean Solver with support for explanations. Author: Leonardo de Moura (leonardo) 2011-07-08. Revision History: --*/ #include"euclidean_solver.h" #include"numeral_buffer.h" #include"heap.h" struct euclidean_solver::imp { typedef unsigned var; typedef unsigned justification; typedef unsynch_mpq_manager numeral_manager; typedef numeral_buffer mpz_buffer; typedef numeral_buffer mpq_buffer; typedef svector justification_vector; static const justification null_justification = UINT_MAX; #define null_var UINT_MAX #define null_eq_idx UINT_MAX typedef svector var_vector; typedef svector mpz_vector; typedef svector mpq_vector; struct elim_order_lt { unsigned_vector & m_solved; elim_order_lt(unsigned_vector & s):m_solved(s) {} bool operator()(var x1, var x2) const { return m_solved[x1] < m_solved[x2]; } }; typedef heap var_queue; // queue used for scheduling variables for applying substitution. static unsigned pos(unsigned_vector const & xs, unsigned x_i) { if (xs.empty()) return UINT_MAX; int low = 0; int high = xs.size() - 1; while (true) { int mid = low + ((high - low) / 2); var x_mid = 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; } } } /** Equation as[0]*xs[0] + ... + as[n-1]*xs[n-1] + c = 0 with justification bs[0]*js[0] + ... + bs[m-1]*js[m-1] */ struct equation { mpz_vector m_as; var_vector m_xs; mpz m_c; // justification mpq_vector m_bs; justification_vector m_js; unsigned size() const { return m_xs.size(); } unsigned js_size() const { return m_js.size(); } var x(unsigned i) const { return m_xs[i]; } var & x(unsigned i) { return m_xs[i]; } mpz const & a(unsigned i) const { return m_as[i]; } mpz & a(unsigned i) { return m_as[i]; } mpz const & c() const { return m_c; } mpz & c() { return m_c; } var j(unsigned i) const { return m_js[i]; } var & j(unsigned i) { return m_js[i]; } mpq const & b(unsigned i) const { return m_bs[i]; } mpq & b(unsigned i) { return m_bs[i]; } unsigned pos_x(unsigned x_i) const { return pos(m_xs, x_i); } }; typedef ptr_vector equations; typedef svector occs; numeral_manager * m_manager; bool m_owns_m; volatile bool m_cancel; equations m_equations; equations m_solution; svector m_parameter; unsigned_vector m_solved; // null_eq_idx if var is not solved, otherwise the position in m_solution vector m_occs; // occurrences of the variable in m_equations. unsigned m_inconsistent; // null_eq_idx if not inconsistent, otherwise it is the index of an unsatisfiable equality in m_equations. unsigned m_next_justification; mpz_buffer m_decompose_buffer; mpz_buffer m_as_buffer; mpq_buffer m_bs_buffer; var_vector m_tmp_xs; mpz_buffer m_tmp_as; mpq_buffer m_tmp_bs; var_vector m_norm_xs_vector; mpz_vector m_norm_as_vector; mpq_vector m_norm_bs_vector; var_queue m_var_queue; // next candidate unsigned m_next_eq; var m_next_x; mpz m_next_a; bool m_next_pos_a; numeral_manager & m() const { return *m_manager; } bool solved(var x) const { return m_solved[x] != null_eq_idx; } template void sort_core(svector & as, unsigned_vector & xs, numeral_buffer & buffer) { std::sort(xs.begin(), xs.end()); unsigned num = as.size(); for (unsigned i = 0; i < num; i++) { m().swap(as[i], buffer[xs[i]]); } } template void sort(svector & as, unsigned_vector & xs, numeral_buffer & buffer) { unsigned num = as.size(); for (unsigned i = 0; i < num; i++) { m().set(buffer[xs[i]], as[i]); } sort_core(as, xs, buffer); } equation * mk_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, unsigned num_js, mpq const * bs, justification const * js, bool sort = true) { equation * new_eq = alloc(equation); for (unsigned i = 0; i < num; i++) { m().set(m_as_buffer[xs[i]], as[i]); new_eq->m_as.push_back(mpz()); new_eq->m_xs.push_back(xs[i]); } sort_core(new_eq->m_as, new_eq->m_xs, m_as_buffer); m().set(new_eq->m_c, c); for (unsigned i = 0; i < num_js; i++) { m().set(m_bs_buffer[js[i]], bs[i]); new_eq->m_bs.push_back(mpq()); new_eq->m_js.push_back(js[i]); } if (sort) sort_core(new_eq->m_bs, new_eq->m_js, m_bs_buffer); return new_eq; } template void div(svector & as, mpz const & g) { unsigned n = as.size(); for (unsigned i = 0; i < n; i++) m().div(as[i], g, as[i]); } void normalize_eq(unsigned eq_idx) { if (inconsistent()) return; equation & eq = *(m_equations[eq_idx]); TRACE("euclidean_solver", tout << "normalizing:\n"; display(tout, eq); tout << "\n";); unsigned num = eq.size(); if (num == 0) { // c == 0 inconsistency if (!m().is_zero(eq.c())) { TRACE("euclidean_solver", tout << "c = 0 inconsistency detected\n";); m_inconsistent = eq_idx; } else { del_eq(&eq); m_equations[eq_idx] = 0; } return; } mpz g; mpz a; m().set(g, eq.a(0)); m().abs(g); for (unsigned i = 1; i < num; i++) { if (m().is_one(g)) break; m().set(a, eq.a(i)); m().abs(a); m().gcd(g, a, g); } if (m().is_one(g)) return; if (!m().divides(g, eq.c())) { // g does not divide c TRACE("euclidean_solver", tout << "gcd inconsistency detected\n";); m_inconsistent = eq_idx; return; } div(eq.m_as, g); div(eq.m_bs, g); m().del(g); m().del(a); TRACE("euclidean_solver", tout << "after normalization:\n"; display(tout, eq); tout << "\n";); } bool is_better(mpz const & a, var x, unsigned eq_sz) { SASSERT(m().is_pos(a)); if (m_next_x == null_var) return true; if (m().lt(a, m_next_a)) return true; if (m().lt(m_next_a, a)) return false; if (m_occs[x].size() < m_occs[m_next_x].size()) return true; if (m_occs[x].size() > m_occs[m_next_x].size()) return false; return eq_sz < m_equations[m_next_eq]->size(); } void updt_next_candidate(unsigned eq_idx) { if (!m_equations[eq_idx]) return; mpz abs_a; equation const & eq = *(m_equations[eq_idx]); unsigned num = eq.size(); for (unsigned i = 0; i < num; i++) { mpz const & a = eq.a(i); m().set(abs_a, a); m().abs(abs_a); if (is_better(abs_a, eq.x(i), num)) { m().set(m_next_a, abs_a); m_next_x = eq.x(i); m_next_eq = eq_idx; m_next_pos_a = m().is_pos(a); } } m().del(abs_a); } /** \brief Select next variable to be eliminated. Return false if there is not variable to eliminate. The result is in m_next_x variable to be eliminated m_next_eq id of the equation containing x m_next_a absolute value of the coefficient of x in eq. m_next_pos_a true if the coefficient of x is positive in eq. */ bool select_next_var() { while (!m_equations.empty() && m_equations.back() == 0) m_equations.pop_back(); if (m_equations.empty()) return false; SASSERT(!m_equations.empty() && m_equations.back() != 0); m_next_x = null_var; unsigned eq_idx = m_equations.size(); while (eq_idx > 0) { --eq_idx; updt_next_candidate(eq_idx); // stop as soon as possible // TODO: use heuristics if (m_next_x != null_var && m().is_one(m_next_a)) return true; } CTRACE("euclidean_solver_bug", m_next_x == null_var, display(tout);); SASSERT(m_next_x != null_var); return true; } template void del_nums(svector & as) { unsigned sz = as.size(); for (unsigned i = 0; i < sz; i++) m().del(as[i]); as.reset(); } void del_eq(equation * eq) { m().del(eq->c()); del_nums(eq->m_as); del_nums(eq->m_bs); dealloc(eq); } void del_equations(equations & eqs) { unsigned sz = eqs.size(); for (unsigned i = 0; i < sz; i++) { if (eqs[i]) del_eq(eqs[i]); } } /** \brief Store the "solved" variables in xs into m_var_queue. */ void schedule_var_subst(unsigned num, var const * xs) { for (unsigned i = 0; i < num; i++) { if (solved(xs[i])) m_var_queue.insert(xs[i]); } } void schedule_var_subst(var_vector const & xs) { schedule_var_subst(xs.size(), xs.c_ptr()); } /** \brief Store as1*xs1 + k*as2*xs2 into new_as*new_xs If UpdateOcc == true, Then, 1) for each variable x occurring in xs2 but not in xs1: - eq_idx is added to m_occs[x] 2) for each variable that occurs in xs1 and xs2 and the resultant coefficient is zero, - eq_idx is removed from m_occs[x] IF x != except_var If UpdateQueue == true Then, 1) for each variable x occurring in xs2 but not in xs1: - if x is solved, then x is inserted into m_var_queue 2) for each variable that occurs in xs1 and xs2 and the resultant coefficient is zero, - if x is solved, then x is removed from m_var_queue */ template void addmul(svector const & as1, var_vector const & xs1, mpz const & k, svector const & as2, var_vector const & xs2, numeral_buffer & new_as, var_vector & new_xs, unsigned eq_idx = null_eq_idx, var except_var = null_var) { Numeral new_a; SASSERT(as1.size() == xs1.size()); SASSERT(as2.size() == xs2.size()); new_as.reset(); new_xs.reset(); unsigned sz1 = xs1.size(); unsigned sz2 = xs2.size(); unsigned i1 = 0; unsigned i2 = 0; while (true) { if (i1 == sz1) { // copy remaining entries from as2*xs2 while (i2 < sz2) { var x2 = xs2[i2]; if (UpdateOcc) m_occs[x2].push_back(eq_idx); if (UpdateQueue && solved(x2)) m_var_queue.insert(x2); new_as.push_back(Numeral()); m().mul(k, as2[i2], new_as.back()); new_xs.push_back(x2); i2++; } break; } if (i2 == sz2) { // copy remaining entries from as1*xs1 while (i1 < sz1) { new_as.push_back(as1[i1]); new_xs.push_back(xs1[i1]); i1++; } break; } var x1 = xs1[i1]; var x2 = xs2[i2]; if (x1 < x2) { new_as.push_back(as1[i1]); new_xs.push_back(xs1[i1]); i1++; } else if (x1 > x2) { if (UpdateOcc) m_occs[x2].push_back(eq_idx); if (UpdateQueue && solved(x2)) m_var_queue.insert(x2); new_as.push_back(Numeral()); m().mul(k, as2[i2], new_as.back()); new_xs.push_back(x2); i2++; } else { m().addmul(as1[i1], k, as2[i2], new_a); TRACE("euclidean_solver_add_mul", tout << "i1: " << i1 << ", i2: " << i2 << " new_a: " << m().to_string(new_a) << "\n"; tout << "as1: " << m().to_string(as1[i1]) << ", k: " << m().to_string(k) << ", as2: " << m().to_string(as2[i2]) << "\n";); if (m().is_zero(new_a)) { // variable was canceled if (UpdateOcc && x1 != except_var) m_occs[x1].erase(eq_idx); if (UpdateQueue && solved(x1) && m_var_queue.contains(x1)) m_var_queue.erase(x1); } else { new_as.push_back(new_a); new_xs.push_back(x1); } i1++; i2++; } } m().del(new_a); } template void apply_solution(var x, mpz_vector & as, var_vector & xs, mpz & c, mpq_vector & bs, justification_vector & js, unsigned eq_idx = null_eq_idx, var except_var = null_var) { SASSERT(solved(x)); unsigned idx = pos(xs, x); if (idx == UINT_MAX) return; mpz const & a1 = as[idx]; SASSERT(!m().is_zero(a1)); equation const & eq2 = *(m_solution[m_solved[x]]); SASSERT(eq2.pos_x(x) != UINT_MAX); SASSERT(m().is_minus_one(eq2.a(eq2.pos_x(x)))); TRACE("euclidean_solver_apply", tout << "applying: " << m().to_string(a1) << " * "; display(tout, eq2); tout << "\n"; for (unsigned i = 0; i < xs.size(); i++) tout << m().to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";); addmul(as, xs, a1, eq2.m_as, eq2.m_xs, m_tmp_as, m_tmp_xs, eq_idx, except_var); m().addmul(c, a1, eq2.m_c, c); m_tmp_as.swap(as); m_tmp_xs.swap(xs); SASSERT(as.size() == xs.size()); TRACE("euclidean_solver_apply", for (unsigned i = 0; i < xs.size(); i++) tout << m().to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";); addmul(bs, js, a1, eq2.m_bs, eq2.m_js, m_tmp_bs, m_tmp_xs); m_tmp_bs.swap(bs); m_tmp_xs.swap(js); SASSERT(pos(xs, x) == UINT_MAX); } void apply_solution(mpz_vector & as, var_vector & xs, mpz & c, mpq_vector & bs, justification_vector & js) { m_var_queue.reset(); schedule_var_subst(xs); while (!m_var_queue.empty()) { var x = m_var_queue.erase_min(); apply_solution(x, as, xs, c, bs, js); } } void apply_solution(equation & eq) { apply_solution(eq.m_as, eq.m_xs, eq.m_c, eq.m_bs, eq.m_js); } void display(std::ostream & out, equation const & eq) const { unsigned num = eq.js_size(); for (unsigned i = 0; i < num; i ++) { if (i > 0) out << " "; out << m().to_string(eq.b(i)) << "*j" << eq.j(i); } if (num > 0) out << " "; out << "|= "; num = eq.size(); for (unsigned i = 0; i < num; i++) { out << m().to_string(eq.a(i)) << "*x" << eq.x(i) << " + "; } out << m().to_string(eq.c()) << " = 0"; } void display(std::ostream & out, equations const & eqs) const { unsigned num = eqs.size(); for (unsigned i = 0; i < num; i++) { if (eqs[i]) { display(out, *(eqs[i])); out << "\n"; } } } void display(std::ostream & out) const { if (inconsistent()) { out << "inconsistent: "; display(out, *(m_equations[m_inconsistent])); out << "\n"; } out << "solution set:\n"; display(out, m_solution); out << "todo:\n"; display(out, m_equations); } void add_occs(unsigned eq_idx) { equation const & eq = *(m_equations[eq_idx]); unsigned sz = eq.size(); for (unsigned i = 0; i < sz; i++) m_occs[eq.x(i)].push_back(eq_idx); } imp(numeral_manager * m): m_manager(m == 0 ? alloc(numeral_manager) : m), m_owns_m(m == 0), m_decompose_buffer(*m_manager), m_as_buffer(*m_manager), m_bs_buffer(*m_manager), m_tmp_as(*m_manager), m_tmp_bs(*m_manager), m_var_queue(16, elim_order_lt(m_solved)) { m_inconsistent = null_eq_idx; m_next_justification = 0; m_cancel = false; m_next_x = null_var; m_next_eq = null_eq_idx; } ~imp() { m().del(m_next_a); del_equations(m_equations); del_equations(m_solution); if (m_owns_m) dealloc(m_manager); } var mk_var(bool parameter) { var x = m_solved.size(); m_parameter.push_back(parameter); m_solved.push_back(null_eq_idx); m_occs.push_back(occs()); m_as_buffer.push_back(mpz()); m_var_queue.reserve(x+1); return x; } justification mk_justification() { justification r = m_next_justification; m_bs_buffer.push_back(mpq()); m_next_justification++; return r; } void assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j) { if (inconsistent()) return; equation * eq; if (j == null_justification) { eq = mk_eq(num, as, xs, c, 0, 0, 0); } else { mpq one(1); eq = mk_eq(num, as, xs, c, 1, &one, &j); } TRACE("euclidean_solver", tout << "new-eq:\n"; display(tout, *eq); tout << "\n";); unsigned eq_idx = m_equations.size(); m_equations.push_back(eq); apply_solution(*eq); normalize_eq(eq_idx); add_occs(eq_idx); TRACE("euclidean_solver", tout << "asserted:\n"; display(tout, *eq); tout << "\n";); } justification_vector const & get_justification() const { SASSERT(inconsistent()); return m_equations[m_inconsistent]->m_js; } template void neg_coeffs(svector & as) { unsigned sz = as.size(); for (unsigned i = 0; i < sz; i++) { m().neg(as[i]); } } void substitute_most_recent_solution(var x) { SASSERT(!m_solution.empty()); equation & eq = *(m_solution.back()); TRACE("euclidean_solver", tout << "applying solution for x" << x << "\n"; display(tout, eq); tout << "\n";); occs & use_list = m_occs[x]; occs::iterator it = use_list.begin(); occs::iterator end = use_list.end(); for (; it != end; ++it) { unsigned eq_idx = *it; // remark we don't want to update the use_list of x while we are traversing it. equation & eq2 = *(m_equations[eq_idx]); TRACE("euclidean_solver", tout << "eq before substituting x" << x << "\n"; display(tout, eq2); tout << "\n";); apply_solution(x, eq2.m_as, eq2.m_xs, eq2.m_c, eq2.m_bs, eq2.m_js, eq_idx, x); TRACE("euclidean_solver", tout << "eq after substituting x" << x << "\n"; display(tout, eq2); tout << "\n";); normalize_eq(eq_idx); if (inconsistent()) break; } use_list.reset(); } void elim_unit() { SASSERT(m().is_one(m_next_a)); equation & eq = *(m_equations[m_next_eq]); TRACE("euclidean_solver", tout << "eliminating equation with unit coefficient:\n"; display(tout, eq); tout << "\n";); if (m_next_pos_a) { // neg coeffs... to make sure that m_next_x is -1 neg_coeffs(eq.m_as); neg_coeffs(eq.m_bs); m().neg(eq.m_c); } unsigned sz = eq.size(); for (unsigned i = 0; i < sz; i++) { m_occs[eq.x(i)].erase(m_next_eq); } m_solved[m_next_x] = m_solution.size(); m_solution.push_back(&eq); m_equations[m_next_eq] = 0; substitute_most_recent_solution(m_next_x); } void decompose(bool pos_a, mpz const & abs_a, mpz const & a_i, mpz & new_a_i, mpz & r_i) { mpz abs_a_i; bool pos_a_i = m().is_pos(a_i); m().set(abs_a_i, a_i); if (!pos_a_i) m().neg(abs_a_i); bool new_pos_a_i = pos_a_i; if (pos_a) new_pos_a_i = !new_pos_a_i; m().div(abs_a_i, abs_a, new_a_i); if (m().divides(abs_a, a_i)) { m().reset(r_i); } else { if (pos_a_i) m().submul(a_i, abs_a, new_a_i, r_i); else m().addmul(a_i, abs_a, new_a_i, r_i); } if (!new_pos_a_i) m().neg(new_a_i); m().del(abs_a_i); } void decompose_and_elim() { m_tmp_xs.reset(); mpz_buffer & buffer = m_decompose_buffer; buffer.reset(); var p = mk_var(true); mpz new_a_i; equation & eq = *(m_equations[m_next_eq]); TRACE("euclidean_solver", tout << "decompositing equation for x" << m_next_x << "\n"; display(tout, eq); tout << "\n";); unsigned sz = eq.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { var x_i = eq.x(i); if (x_i == m_next_x) { m().set(new_a_i, -1); buffer.push_back(new_a_i); m_tmp_xs.push_back(m_next_x); m_occs[x_i].erase(m_next_eq); } else { decompose(m_next_pos_a, m_next_a, eq.a(i), new_a_i, eq.m_as[j]); buffer.push_back(new_a_i); m_tmp_xs.push_back(x_i); if (m().is_zero(eq.m_as[j])) { m_occs[x_i].erase(m_next_eq); } else { eq.m_xs[j] = x_i; j++; } } } SASSERT(j < sz); // add parameter: p to new equality, and m_next_pos_a * m_next_a * p to current eq m().set(new_a_i, 1); buffer.push_back(new_a_i); m_tmp_xs.push_back(p); m().set(eq.m_as[j], m_next_a); if (!m_next_pos_a) m().neg(eq.m_as[j]); eq.m_xs[j] = p; j++; unsigned new_sz = j; // shrink current eq for (; j < sz; j++) m().del(eq.m_as[j]); eq.m_as.shrink(new_sz); eq.m_xs.shrink(new_sz); // ajust c mpz new_c; decompose(m_next_pos_a, m_next_a, eq.m_c, new_c, eq.m_c); // create auxiliary equation equation * new_eq = mk_eq(m_tmp_xs.size(), buffer.c_ptr(), m_tmp_xs.c_ptr(), new_c, 0, 0, 0, false); // new_eq doesn't need to normalized, since it has unit coefficients TRACE("euclidean_solver", tout << "decomposition: new parameter x" << p << " aux eq:\n"; display(tout, *new_eq); tout << "\n"; display(tout, eq); tout << "\n";); m_solved[m_next_x] = m_solution.size(); m_solution.push_back(new_eq); substitute_most_recent_solution(m_next_x); m().del(new_a_i); m().del(new_c); } bool solve() { if (inconsistent()) return false; TRACE("euclidean_solver", tout << "solving...\n"; display(tout);); while (select_next_var()) { CTRACE("euclidean_solver_bug", m_next_x == null_var, display(tout);); TRACE("euclidean_solver", tout << "eliminating x" << m_next_x << "\n";); if (m().is_one(m_next_a) || m().is_minus_one(m_next_a)) elim_unit(); else decompose_and_elim(); TRACE("euclidean_solver", display(tout);); if (inconsistent()) return false; } return true; } bool inconsistent() const { return m_inconsistent != null_eq_idx; } bool is_parameter(var x) const { return m_parameter[x]; } void normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime, justification_vector & js) { TRACE("euclidean_solver", tout << "before applying solution set\n"; for (unsigned i = 0; i < num; i++) { tout << m().to_string(as[i]) << "*x" << xs[i] << " "; } tout << "\n";); m_norm_xs_vector.reset(); m_norm_as_vector.reset(); for (unsigned i = 0; i < num; i++) { m_norm_xs_vector.push_back(xs[i]); m_norm_as_vector.push_back(mpz()); m().set(m_norm_as_vector.back(), as[i]); } sort(m_norm_as_vector, m_norm_xs_vector, m_as_buffer); m_norm_bs_vector.reset(); js.reset(); m().set(c_prime, c); apply_solution(m_norm_as_vector, m_norm_xs_vector, c_prime, m_norm_bs_vector, js); TRACE("euclidean_solver", tout << "after applying solution set\n"; for (unsigned i = 0; i < m_norm_as_vector.size(); i++) { tout << m().to_string(m_norm_as_vector[i]) << "*x" << m_norm_xs_vector[i] << " "; } tout << "\n";); // compute gcd of the result m_norm_as_vector if (m_norm_as_vector.empty()) { m().set(a_prime, 0); } else { mpz a; m().set(a_prime, m_norm_as_vector[0]); m().abs(a_prime); unsigned sz = m_norm_as_vector.size(); for (unsigned i = 1; i < sz; i++) { if (m().is_one(a_prime)) break; m().set(a, m_norm_as_vector[i]); m().abs(a); m().gcd(a_prime, a, a_prime); } m().del(a); } // REMARK: m_norm_bs_vector contains the linear combination of the justifications. It may be useful if we // decided (one day) to generate detailed proofs for this step. del_nums(m_norm_as_vector); del_nums(m_norm_bs_vector); } void set_cancel(bool f) { m_cancel = f; } }; euclidean_solver::euclidean_solver(numeral_manager * m): m_imp(alloc(imp, m)) { } euclidean_solver::~euclidean_solver() { dealloc(m_imp); } euclidean_solver::numeral_manager & euclidean_solver::m() const { return m_imp->m(); } void euclidean_solver::reset() { numeral_manager * m = m_imp->m_manager; bool owns_m = m_imp->m_owns_m; m_imp->m_owns_m = false; #pragma omp critical (euclidean_solver) { dealloc(m_imp); m_imp = alloc(imp, m); m_imp->m_owns_m = owns_m; } } euclidean_solver::var euclidean_solver::mk_var() { return m_imp->mk_var(false); } euclidean_solver::justification euclidean_solver::mk_justification() { return m_imp->mk_justification(); } void euclidean_solver::assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j) { m_imp->assert_eq(num, as, xs, c, j); } bool euclidean_solver::solve() { return m_imp->solve(); } euclidean_solver::justification_vector const & euclidean_solver::get_justification() const { return m_imp->get_justification(); } bool euclidean_solver::inconsistent() const { return m_imp->inconsistent(); } bool euclidean_solver::is_parameter(var x) const { return m_imp->is_parameter(x); } void euclidean_solver::normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime, justification_vector & js) { return m_imp->normalize(num, as, xs, c, a_prime, c_prime, js); } void euclidean_solver::set_cancel(bool f) { #pragma omp critical (euclidean_solver) { m_imp->set_cancel(f); } } void euclidean_solver::display(std::ostream & out) const { m_imp->display(out); } z3-z3-4.4.1/src/math/euclid/euclidean_solver.h000066400000000000000000000053001260446376700211040ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: euclidean_solver.h Abstract: Euclidean Solver with support for explanations. Author: Leonardo de Moura (leonardo) 2011-07-08. Revision History: --*/ #ifndef EUCLIDEAN_SOLVER_H_ #define EUCLIDEAN_SOLVER_H_ #include"mpq.h" #include"vector.h" class euclidean_solver { struct imp; imp * m_imp; public: typedef unsigned var; typedef unsigned justification; typedef unsynch_mpq_manager numeral_manager; typedef svector justification_vector; static const justification null_justification = UINT_MAX; /** \brief If m == 0, then the solver will create its own numeral manager. */ euclidean_solver(numeral_manager * m); ~euclidean_solver(); numeral_manager & m() const; /** \brief Reset the state of the euclidean solver. */ void reset(); /** \brief Creates a integer variable. */ var mk_var(); /** \brief Creates a fresh justification id. */ justification mk_justification(); /** \brief Asserts an equation of the form as[0]*xs[0] + ... + as[num-1]*xs[num-1] + c = 0 with justification j. The numerals must be created using the numeral_manager m(). */ void assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j = null_justification); /** \brief Solve the current set of equations. Return false if it is inconsistent. */ bool solve(); /** \brief Return a set of justifications (proof) for the inconsitency. \pre inconsistent() */ justification_vector const & get_justification() const; bool inconsistent() const; /** \brief Return true if the variable is a "parameter" created by the Euclidean solver. */ bool is_parameter(var x) const; /** Given a linear polynomial as[0]*xs[0] + ... + as[num-1]*xs[num-1] + c and the current solution set, It applies the solution set to produce a polynomial of the for a_prime * p + c_prime, where a_prime * p represents a linear polynomial where the coefficient of every monomial is a multiple of a_prime. The justification is stored in js. Note that, this function does not return the actual p. The numerals must be created using the numeral_manager m(). */ void normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime, justification_vector & js); /** \brief Set/Reset the cancel flag. */ void set_cancel(bool f); void display(std::ostream & out) const; }; #endif z3-z3-4.4.1/src/math/grobner/000077500000000000000000000000001260446376700156035ustar00rootroot00000000000000z3-z3-4.4.1/src/math/grobner/grobner.cpp000066400000000000000000000760461260446376700177620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: grobner.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-12-04. Revision History: --*/ #include"grobner.h" #include"ast_pp.h" #include"ref_util.h" // #define PROFILE_GB grobner::grobner(ast_manager & m, v_dependency_manager & d): m_manager(m), m_dep_manager(d), m_util(m), m_var_lt(m_var2weight), m_monomial_lt(m_var_lt), m_changed_leading_term(false), m_unsat(0) { } grobner::~grobner() { flush(); } void grobner::flush() { dec_ref_map_keys(m_manager, m_var2weight); del_equations(0); } void grobner::del_equations(unsigned old_size) { SASSERT(m_equations_to_delete.size() >= old_size); equation_vector::iterator it = m_equations_to_delete.begin(); equation_vector::iterator end = m_equations_to_delete.end(); it += old_size; for (; it != end; ++it) { equation * eq = *it; if (eq) del_equation(eq); } m_equations_to_delete.shrink(old_size); } void grobner::del_equation(equation * eq) { m_processed.erase(eq); m_to_process.erase(eq); SASSERT(m_equations_to_delete[eq->m_bidx] == eq); m_equations_to_delete[eq->m_bidx] = 0; del_monomials(eq->m_monomials); dealloc(eq); } void grobner::del_monomials(ptr_vector& ms) { ptr_vector::iterator it = ms.begin(); ptr_vector::iterator end = ms.end(); for (; it != end; ++it) { del_monomial(*it); } ms.reset(); } void grobner::del_monomial(monomial * m) { ptr_vector::iterator it2 = m->m_vars.begin(); ptr_vector::iterator end2 = m->m_vars.end(); for (; it2 != end2; ++it2) { expr * v = *it2; m_manager.dec_ref(v); } dealloc(m); } void grobner::unfreeze_equations(unsigned old_size) { SASSERT(m_equations_to_unfreeze.size() >= old_size); equation_vector::iterator it = m_equations_to_unfreeze.begin(); equation_vector::iterator end = m_equations_to_unfreeze.end(); it += old_size; for (; it != end; ++it) { equation * eq = *it; m_to_process.insert(eq); } m_equations_to_unfreeze.shrink(old_size); } void grobner::push_scope() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_equations_to_unfreeze_lim = m_equations_to_unfreeze.size(); s.m_equations_to_delete_lim = m_equations_to_delete.size(); } void grobner::pop_scope(unsigned num_scopes) { SASSERT(num_scopes >= get_scope_level()); unsigned new_lvl = get_scope_level() - num_scopes; scope & s = m_scopes[new_lvl]; unfreeze_equations(s.m_equations_to_unfreeze_lim); del_equations(s.m_equations_to_delete_lim); m_scopes.shrink(new_lvl); } void grobner::reset() { flush(); m_processed.reset(); m_to_process.reset(); m_equations_to_unfreeze.reset(); m_equations_to_delete.reset(); m_unsat = 0; } void grobner::display_var(std::ostream & out, expr * var) const { if (is_app(var) && to_app(var)->get_num_args() > 0) out << "#" << var->get_id(); else out << mk_pp(var, m_manager); } void grobner::display_vars(std::ostream & out, unsigned num_vars, expr * const * vars) const { for (unsigned i = 0; i < num_vars; i++) { display_var(out, vars[i]); out << " "; } } void grobner::display_monomial(std::ostream & out, monomial const & m) const { if (!m.m_coeff.is_one() || m.m_vars.empty()) { out << m.m_coeff; if (!m.m_vars.empty()) out << "*"; } if (!m.m_vars.empty()) { ptr_vector::const_iterator it = m.m_vars.begin(); ptr_vector::const_iterator end = m.m_vars.end(); unsigned power = 1; expr * prev = *it; it++; for (; it != end; ++it) { expr * curr = *it; if (curr == prev) { power++; } else { display_var(out, prev); if (power > 1) out << "^" << power; power = 1; prev = curr; out << "*"; } } display_var(out, prev); if (power > 1) out << "^" << power; } } void grobner::display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const { bool first = true; for (unsigned i = 0; i < num_monomials; i++) { monomial const * m = monomials[i]; if (first) first = false; else out << " + "; display_monomial(out, *m); } } void grobner::display_equation(std::ostream & out, equation const & eq) const { display_monomials(out, eq.m_monomials.size(), eq.m_monomials.c_ptr()); out << " = 0\n"; } void grobner::display_equations(std::ostream & out, equation_set const & v, char const * header) const { if (!v.empty()) { out << header << "\n"; equation_set::iterator it = v.begin(); equation_set::iterator end = v.end(); for (; it != end; ++it) display_equation(out, *(*it)); } } void grobner::display(std::ostream & out) const { display_equations(out, m_processed, "processed:"); display_equations(out, m_to_process, "to process:"); } void grobner::set_weight(expr * n, int weight) { if (weight == 0) return; if (!m_var2weight.contains(n)) m_manager.inc_ref(n); m_var2weight.insert(n, weight); } /** \brief Update equation using the new variable order. Return true if the leading term was modified. */ bool grobner::update_order(equation * eq) { if (eq->get_num_monomials() == 0) return false; monomial * first = eq->m_monomials[0]; ptr_vector::iterator it = eq->m_monomials.begin(); ptr_vector::iterator end = eq->m_monomials.end(); for (; it != end; ++it) { monomial * m = *it; std::stable_sort(m->m_vars.begin(), m->m_vars.end(), m_var_lt); } std::stable_sort(eq->m_monomials.begin(), eq->m_monomials.end(), m_monomial_lt); return eq->m_monomials[0] != first; } void grobner::update_order(equation_set & s, bool processed) { ptr_buffer to_remove; equation_set::iterator it = s.begin(); equation_set::iterator end = s.end(); for (;it != end; ++it) { equation * eq = *it; if (update_order(eq)) { if (processed) { to_remove.push_back(eq); m_to_process.insert(eq); } } } ptr_buffer::iterator it2 = to_remove.begin(); ptr_buffer::iterator end2 = to_remove.end(); for (; it2 != end2; ++it2) s.erase(*it2); } void grobner::update_order() { update_order(m_to_process, false); update_order(m_processed, true); } bool grobner::var_lt::operator()(expr * v1, expr * v2) const { int w1 = 0; int w2 = 0; m_var2weight.find(v1, w1); m_var2weight.find(v2, w2); return (w1 > w2) || (w1 == w2 && v1->get_id() < v2->get_id()); } bool grobner::monomial_lt::operator()(monomial * m1, monomial * m2) const { // Using graded lex order. if (m1->get_degree() > m2->get_degree()) return true; if (m1->get_degree() < m2->get_degree()) return false; ptr_vector::iterator it1 = m1->m_vars.begin(); ptr_vector::iterator it2 = m2->m_vars.begin(); ptr_vector::iterator end1 = m1->m_vars.end(); for (; it1 != end1; ++it1, ++it2) { expr * v1 = *it1; expr * v2 = *it2; if (m_var_lt(v1, v2)) return true; if (v1 != v2) return false; } return false; } inline void grobner::add_var(monomial * m, expr * v) { SASSERT(!m_util.is_numeral(v)); m_manager.inc_ref(v); m->m_vars.push_back(v); } grobner::monomial * grobner::mk_monomial(rational const & coeff, unsigned num_vars, expr * const * vars) { monomial * r = alloc(monomial); r->m_coeff = coeff; for (unsigned i = 0; i < num_vars; i++) add_var(r, vars[i]); std::stable_sort(r->m_vars.begin(), r->m_vars.end(), m_var_lt); return r; } grobner::monomial * grobner::mk_monomial(rational const & coeff, expr * m) { monomial * r = alloc(monomial); if (m_util.is_numeral(m, r->m_coeff)) { r->m_coeff *= coeff; return r; } if (m_util.is_mul(m)) { expr * body = m; SASSERT(!m_util.is_numeral(to_app(m)->get_arg(1))); // monomial is in normal form if (m_util.is_numeral(to_app(m)->get_arg(0), r->m_coeff)) { r->m_coeff *= coeff; body = to_app(m)->get_arg(1); } else { r->m_coeff = coeff; } while (m_util.is_mul(body)) { add_var(r, to_app(body)->get_arg(0)); body = to_app(body)->get_arg(1); } add_var(r, body); std::stable_sort(r->m_vars.begin(), r->m_vars.end(), m_var_lt); } else { r->m_coeff = coeff; r->m_vars.push_back(m); m_manager.inc_ref(m); } return r; } void grobner::init_equation(equation * eq, v_dependency * d) { eq->m_scope_lvl = get_scope_level(); unsigned bidx = m_equations_to_delete.size(); eq->m_bidx = bidx; eq->m_dep = d; eq->m_lc = true; m_equations_to_delete.push_back(eq); SASSERT(m_equations_to_delete[eq->m_bidx] == eq); } void grobner::assert_eq_0(unsigned num_monomials, monomial * const * monomials, v_dependency * ex) { ptr_vector ms; ms.append(num_monomials, monomials); std::stable_sort(ms.begin(), ms.end(), m_monomial_lt); merge_monomials(ms); if (!ms.empty()) { normalize_coeff(ms); equation * eq = alloc(equation); eq->m_monomials.swap(ms); init_equation(eq, ex); m_to_process.insert(eq); } } void grobner::assert_eq_0(unsigned num_monomials, rational const * coeffs, expr * const * monomials, v_dependency * ex) { #define MK_EQ(COEFF) \ ptr_vector ms; \ for (unsigned i = 0; i < num_monomials; i++) \ ms.push_back(mk_monomial(COEFF, monomials[i])); \ std::stable_sort(ms.begin(), ms.end(), m_monomial_lt); \ merge_monomials(ms); \ if (!ms.empty()) { \ equation * eq = alloc(equation); \ normalize_coeff(ms); \ eq->m_monomials.swap(ms); \ init_equation(eq, ex); \ m_to_process.insert(eq); \ } MK_EQ(coeffs[i]); } void grobner::assert_eq_0(unsigned num_monomials, expr * const * monomials, v_dependency * ex) { rational one(1); MK_EQ(one); } void grobner::extract_monomials(expr * lhs, ptr_buffer & monomials) { while (m_util.is_add(lhs)) { SASSERT(!m_util.is_add(to_app(lhs)->get_arg(0))); monomials.push_back(to_app(lhs)->get_arg(0)); lhs = to_app(lhs)->get_arg(1); } monomials.push_back(lhs); } void grobner::assert_eq(expr * eq, v_dependency * ex) { SASSERT(m_manager.is_eq(eq)); expr * lhs = to_app(eq)->get_arg(0); expr * rhs = to_app(eq)->get_arg(1); SASSERT(m_util.is_numeral(rhs)); ptr_buffer monomials; extract_monomials(lhs, monomials); rational c; bool is_int = false; m_util.is_numeral(rhs, c, is_int); expr_ref new_c(m_manager); if (!c.is_zero()) { c.neg(); new_c = m_util.mk_numeral(c, is_int); monomials.push_back(new_c); } assert_eq_0(monomials.size(), monomials.c_ptr(), ex); } void grobner::assert_monomial_tautology(expr * m) { equation * eq = alloc(equation); eq->m_monomials.push_back(mk_monomial(rational(1), m)); // create (quote m) monomial * m1 = alloc(monomial); m1->m_coeff = rational(-1); m_manager.inc_ref(m); m1->m_vars.push_back(m); eq->m_monomials.push_back(m1); normalize_coeff(eq->m_monomials); init_equation(eq, static_cast(0)); \ m_to_process.insert(eq); } /** \brief Return true if the body of m1 and m2 are equal */ bool grobner::is_eq_monomial_body(monomial const * m1, monomial const * m2) { if (m1->get_degree() != m2->get_degree()) return false; ptr_vector::const_iterator it1 = m1->m_vars.begin(); ptr_vector::const_iterator it2 = m2->m_vars.begin(); ptr_vector::const_iterator end1 = m1->m_vars.end(); for (; it1 != end1; ++it1, ++it2) { expr * v1 = *it1; expr * v2 = *it2; if (v1 != v2) return false; } return true; } /** \brief Merge monomials (* c1 m) (* c2 m). \remark This method assumes the monomials are sorted. */ void grobner::merge_monomials(ptr_vector & monomials) { TRACE("grobner", tout << "before merging monomials:\n"; display_monomials(tout, monomials.size(), monomials.c_ptr()); tout << "\n";); unsigned j = 0; unsigned sz = monomials.size(); if (sz == 0) return; SASSERT(&m_del_monomials != &monomials); ptr_vector& to_delete = m_del_monomials; to_delete.reset(); for (unsigned i = 1; i < sz; ++i) { monomial * m1 = monomials[j]; monomial * m2 = monomials[i]; if (is_eq_monomial_body(m1, m2)) { m1->m_coeff += m2->m_coeff; to_delete.push_back(m2); } else { if (m1->m_coeff.is_zero()) to_delete.push_back(m1); else j++; monomials[j] = m2; } } SASSERT(j < sz); monomial * m1 = monomials[j]; if (m1->m_coeff.is_zero()) to_delete.push_back(m1); else j++; monomials.shrink(j); del_monomials(to_delete); TRACE("grobner", tout << "after merging monomials:\n"; display_monomials(tout, monomials.size(), monomials.c_ptr()); tout << "\n";); } /** \brief Divide the coefficients by the coefficient of the leading term. */ void grobner::normalize_coeff(ptr_vector & monomials) { if (monomials.empty()) return; rational c = monomials[0]->m_coeff; if (c.is_one()) return; unsigned sz = monomials.size(); for (unsigned i = 0; i < sz; i++) monomials[i]->m_coeff /= c; } /** \brief Simplify the given monomials */ void grobner::simplify(ptr_vector & monomials) { std::stable_sort(monomials.begin(), monomials.end(), m_monomial_lt); merge_monomials(monomials); normalize_coeff(monomials); } /** \brief Return true if the equation is of the form k = 0, where k is a numeral different from zero. \remark This method assumes the equation is simplified. */ inline bool grobner::is_inconsistent(equation * eq) const { SASSERT(!(eq->m_monomials.size() == 1 && eq->m_monomials[0]->get_degree() == 0) || !eq->m_monomials[0]->m_coeff.is_zero()); return eq->m_monomials.size() == 1 && eq->m_monomials[0]->get_degree() == 0; } /** \brief Return true if the equation is of the form 0 = 0. */ inline bool grobner::is_trivial(equation * eq) const { return eq->m_monomials.empty(); } /** \brief Sort monomials, and merge monomials with the same body. */ void grobner::simplify(equation * eq) { simplify(eq->m_monomials); if (is_inconsistent(eq) && !m_unsat) m_unsat = eq; } /** \brief Return true if monomial m1 is (* c1 M) and m2 is (* c2 M M'). Store M' in rest. \remark This method assumes the variables of m1 and m2 are sorted. */ bool grobner::is_subset(monomial const * m1, monomial const * m2, ptr_vector & rest) const { unsigned i1 = 0; unsigned i2 = 0; unsigned sz1 = m1->m_vars.size(); unsigned sz2 = m2->m_vars.size(); if (sz1 <= sz2) { while (true) { if (i1 >= sz1) { for (; i2 < sz2; i2++) rest.push_back(m2->m_vars[i2]); TRACE("grobner", tout << "monomail: "; display_monomial(tout, *m1); tout << " is a subset of "; display_monomial(tout, *m2); tout << "\n"; tout << "rest: "; display_vars(tout, rest.size(), rest.c_ptr()); tout << "\n";); return true; } if (i2 >= sz2) break; expr * var1 = m1->m_vars[i1]; expr * var2 = m2->m_vars[i2]; if (var1 == var2) { i1++; i2++; continue; } if (m_var_lt(var2, var1)) { i2++; rest.push_back(var2); continue; } SASSERT(m_var_lt(var1, var2)); break; } } // is not subset TRACE("grobner", tout << "monomail: "; display_monomial(tout, *m1); tout << " is not a subset of "; display_monomial(tout, *m2); tout << "\n";); return false; } /** \brief Multiply the monomials of source starting at position start_idx by (coeff * vars), and store the resultant monomials at result. */ void grobner::mul_append(unsigned start_idx, equation const * source, rational const & coeff, ptr_vector const & vars, ptr_vector & result) { unsigned sz = source->get_num_monomials(); for (unsigned i = start_idx; i < sz; i++) { monomial const * m = source->get_monomial(i); monomial * new_m = alloc(monomial); new_m->m_coeff = m->m_coeff; new_m->m_coeff *= coeff; new_m->m_vars.append(m->m_vars.size(), m->m_vars.c_ptr()); new_m->m_vars.append(vars.size(), vars.c_ptr()); ptr_vector::iterator it = new_m->m_vars.begin(); ptr_vector::iterator end = new_m->m_vars.end(); for (; it != end; ++it) m_manager.inc_ref(*it); std::stable_sort(new_m->m_vars.begin(), new_m->m_vars.end(), m_var_lt); result.push_back(new_m); } } /** \brief Copy the given monomial. */ grobner::monomial * grobner::copy_monomial(monomial const * m) { monomial * r = alloc(monomial); r->m_coeff = m->m_coeff; ptr_vector::const_iterator it = m->m_vars.begin(); ptr_vector::const_iterator end = m->m_vars.end(); for (; it != end; ++it) add_var(r, *it); return r; } /** \brief Copy the given equation. */ grobner::equation * grobner::copy_equation(equation const * eq) { equation * r = alloc(equation); unsigned sz = eq->get_num_monomials(); for (unsigned i = 0; i < sz; i++) r->m_monomials.push_back(copy_monomial(eq->get_monomial(i))); init_equation(r, eq->m_dep); r->m_lc = eq->m_lc; return r; } /** \brief Simplify the target equation using the source as a rewrite rule. Return 0 if target was not simplified. Return target if target was simplified but source->m_scope_lvl <= target->m_scope_lvl. Return new_equation if source->m_scope_lvl > target->m_scope_lvl, moreover target is freezed, and new_equation contains the result. */ grobner::equation * grobner::simplify(equation const * source, equation * target) { TRACE("grobner", tout << "simplifying: "; display_equation(tout, *target); tout << "using: "; display_equation(tout, *source);); if (source->get_num_monomials() == 0) return 0; m_stats.m_simplify++; bool result = false; bool simplified; do { simplified = false; unsigned i = 0; unsigned j = 0; unsigned sz = target->m_monomials.size(); monomial const * LT = source->get_monomial(0); ptr_vector & new_monomials = m_tmp_monomials; new_monomials.reset(); ptr_vector & rest = m_tmp_vars1; for (; i < sz; i++) { monomial * curr = target->m_monomials[i]; rest.reset(); if (is_subset(LT, curr, rest)) { if (i == 0) m_changed_leading_term = true; if (source->m_scope_lvl > target->m_scope_lvl) { target = copy_equation(target); SASSERT(target->m_scope_lvl >= source->m_scope_lvl); } if (!result) { // first time that source is being applied. target->m_dep = m_dep_manager.mk_join(target->m_dep, source->m_dep); } simplified = true; result = true; rational coeff = curr->m_coeff; coeff /= LT->m_coeff; coeff.neg(); if (!rest.empty()) target->m_lc = false; mul_append(1, source, coeff, rest, new_monomials); del_monomial(curr); target->m_monomials[i] = 0; } else { target->m_monomials[j] = curr; j++; } } if (simplified) { target->m_monomials.shrink(j); target->m_monomials.append(new_monomials.size(), new_monomials.c_ptr()); simplify(target); } } while (simplified); TRACE("grobner", tout << "result: "; display_equation(tout, *target);); return result ? target : 0; } /** \brief Simplify given equation using processed equalities. Return 0, if the equation was not simplified. Return eq, if the equation was simplified using destructive updates. Return new_eq otherwise. */ grobner::equation * grobner::simplify_using_processed(equation * eq) { bool result = false; bool simplified; TRACE("grobner", tout << "simplifying: "; display_equation(tout, *eq); tout << "using already processed equalities\n";); do { simplified = false; equation_set::iterator it = m_processed.begin(); equation_set::iterator end = m_processed.end(); for (; it != end; ++it) { equation const * p = *it; equation * new_eq = simplify(p, eq); if (new_eq) { result = true; simplified = true; eq = new_eq; } } } while (simplified); TRACE("grobner", tout << "simplification result: "; display_equation(tout, *eq);); return result ? eq : 0; } /** \brief Return true if eq1 is a better than e2, for being the next equation to be processed. */ bool grobner::is_better_choice(equation * eq1, equation * eq2) { if (!eq2) return true; if (eq1->m_monomials.empty()) return true; if (eq2->m_monomials.empty()) return false; if (eq1->m_monomials[0]->get_degree() < eq2->m_monomials[0]->get_degree()) return true; if (eq1->m_monomials[0]->get_degree() > eq2->m_monomials[0]->get_degree()) return false; return eq1->m_monomials.size() < eq2->m_monomials.size(); } /** \brief Pick next unprocessed equation */ grobner::equation * grobner::pick_next() { equation * r = 0; ptr_buffer to_delete; equation_set::iterator it = m_to_process.begin(); equation_set::iterator end = m_to_process.end(); for (; it != end; ++it) { equation * curr = *it; if (is_trivial(curr)) to_delete.push_back(curr); else if (is_better_choice(curr, r)) r = curr; } ptr_buffer::const_iterator it1 = to_delete.begin(); ptr_buffer::const_iterator end1 = to_delete.end(); for (; it1 != end1; ++it1) del_equation(*it1); if (r) m_to_process.erase(r); TRACE("grobner", tout << "selected equation: "; if (!r) tout << "\n"; else display_equation(tout, *r);); return r; } /** \brief Use the given equation to simplify processed terms. */ void grobner::simplify_processed(equation * eq) { ptr_buffer to_insert; ptr_buffer to_remove; ptr_buffer to_delete; equation_set::iterator it = m_processed.begin(); equation_set::iterator end = m_processed.end(); for (; it != end; ++it) { equation * curr = *it; m_changed_leading_term = false; // if the leading term is simplified, then the equation has to be moved to m_to_process equation * new_curr = simplify(eq, curr); if (new_curr != 0) { if (new_curr != curr) { m_equations_to_unfreeze.push_back(curr); to_remove.push_back(curr); if (m_changed_leading_term) { m_to_process.insert(new_curr); to_remove.push_back(curr); } else { to_insert.push_back(new_curr); } curr = new_curr; } else { if (m_changed_leading_term) { m_to_process.insert(curr); to_remove.push_back(curr); } } } if (is_trivial(curr)) to_delete.push_back(curr); } ptr_buffer::const_iterator it1 = to_insert.begin(); ptr_buffer::const_iterator end1 = to_insert.end(); for (; it1 != end1; ++it1) m_processed.insert(*it1); it1 = to_remove.begin(); end1 = to_remove.end(); for (; it1 != end1; ++it1) m_processed.erase(*it1); it1 = to_delete.begin(); end1 = to_delete.end(); for (; it1 != end1; ++it1) del_equation(*it1); } /** \brief Use the given equation to simplify to-process terms. */ void grobner::simplify_to_process(equation * eq) { equation_set::iterator it = m_to_process.begin(); equation_set::iterator end = m_to_process.end(); ptr_buffer to_insert; ptr_buffer to_remove; ptr_buffer to_delete; for (; it != end; ++it) { equation * curr = *it; equation * new_curr = simplify(eq, curr); if (new_curr != 0 && new_curr != curr) { m_equations_to_unfreeze.push_back(curr); to_insert.push_back(new_curr); to_remove.push_back(curr); curr = new_curr; } if (is_trivial(curr)) to_delete.push_back(curr); } ptr_buffer::const_iterator it1 = to_insert.begin(); ptr_buffer::const_iterator end1 = to_insert.end(); for (; it1 != end1; ++it1) m_to_process.insert(*it1); it1 = to_remove.begin(); end1 = to_remove.end(); for (; it1 != end1; ++it1) m_to_process.erase(*it1); it1 = to_delete.begin(); end1 = to_delete.end(); for (; it1 != end1; ++it1) del_equation(*it1); } /** \brief If m1 = (* c M M1) and m2 = (* d M M2) and M is non empty, then return true and store M1 in rest1 and M2 in rest2. */ bool grobner::unify(monomial const * m1, monomial const * m2, ptr_vector & rest1, ptr_vector & rest2) { TRACE("grobner", tout << "unifying: "; display_monomial(tout, *m1); tout << " "; display_monomial(tout, *m2); tout << "\n";); bool found_M = false; unsigned i1 = 0; unsigned i2 = 0; unsigned sz1 = m1->m_vars.size(); unsigned sz2 = m2->m_vars.size(); while (true) { if (i1 >= sz1) { if (found_M) { for (; i2 < sz2; i2++) rest2.push_back(m2->m_vars[i2]); return true; } return false; } if (i2 >= sz2) { if (found_M) { for (; i1 < sz1; i1++) rest1.push_back(m1->m_vars[i1]); return true; } return false; } expr * var1 = m1->m_vars[i1]; expr * var2 = m2->m_vars[i2]; if (var1 == var2) { found_M = true; i1++; i2++; } else if (m_var_lt(var2, var1)) { i2++; rest2.push_back(var2); } else { i1++; rest1.push_back(var1); } } } /** \brief Superpose the given two equations. */ void grobner::superpose(equation * eq1, equation * eq2) { if (eq1->m_monomials.empty() || eq2->m_monomials.empty()) return; m_stats.m_superpose++; ptr_vector & rest1 = m_tmp_vars1; rest1.reset(); ptr_vector & rest2 = m_tmp_vars2; rest2.reset(); if (unify(eq1->m_monomials[0], eq2->m_monomials[0], rest1, rest2)) { TRACE("grobner", tout << "superposing:\n"; display_equation(tout, *eq1); display_equation(tout, *eq2); tout << "rest1: "; display_vars(tout, rest1.size(), rest1.c_ptr()); tout << "\n"; tout << "rest2: "; display_vars(tout, rest2.size(), rest2.c_ptr()); tout << "\n";); ptr_vector & new_monomials = m_tmp_monomials; new_monomials.reset(); mul_append(1, eq1, eq2->m_monomials[0]->m_coeff, rest2, new_monomials); rational c = eq1->m_monomials[0]->m_coeff; c.neg(); mul_append(1, eq2, c, rest1, new_monomials); simplify(new_monomials); TRACE("grobner", tout << "resulting monomials: "; display_monomials(tout, new_monomials.size(), new_monomials.c_ptr()); tout << "\n";); if (new_monomials.empty()) return; m_num_new_equations++; equation * new_eq = alloc(equation); new_eq->m_monomials.swap(new_monomials); init_equation(new_eq, m_dep_manager.mk_join(eq1->m_dep, eq2->m_dep)); new_eq->m_lc = false; m_to_process.insert(new_eq); } } /** \brief Superpose the given equations with the equations in m_processed. */ void grobner::superpose(equation * eq) { equation_set::iterator it = m_processed.begin(); equation_set::iterator end = m_processed.end(); for (; it != end; ++it) { equation * curr = *it; superpose(eq, curr); } } bool grobner::compute_basis(unsigned threshold) { m_stats.m_compute_basis++; m_num_new_equations = 0; while (m_num_new_equations < threshold) { equation * eq = pick_next(); if (!eq) return true; m_stats.m_num_processed++; #ifdef PROFILE_GB if (m_stats.m_num_processed % 100 == 0) { verbose_stream() << "[grobner] " << m_processed.size() << " " << m_to_process.size() << "\n"; } #endif equation * new_eq = simplify_using_processed(eq); if (new_eq != 0 && eq != new_eq) { // equation was updated using non destructive updates m_equations_to_unfreeze.push_back(eq); eq = new_eq; } simplify_processed(eq); superpose(eq); m_processed.insert(eq); simplify_to_process(eq); TRACE("grobner", tout << "end of iteration:\n"; display(tout);); } return false; } void grobner::copy_to(equation_set const & s, ptr_vector & result) const { equation_set::iterator it = s.begin(); equation_set::iterator end = s.end(); for (; it != end; ++it) result.push_back(*it); } /** \brief Copy the equations in m_processed and m_to_process to result. \warning This equations can be deleted when compute_basis is invoked. */ void grobner::get_equations(ptr_vector & result) const { copy_to(m_processed, result); copy_to(m_to_process, result); } z3-z3-4.4.1/src/math/grobner/grobner.h000066400000000000000000000213071260446376700174150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: grobner.h Abstract: Author: Leonardo de Moura (leonardo) 2008-12-04. Revision History: --*/ #ifndef GROBNER_H_ #define GROBNER_H_ #include"ast.h" #include"arith_decl_plugin.h" #include"heap.h" #include"obj_hashtable.h" #include"region.h" #include"dependency.h" struct grobner_stats { long m_simplify; long m_superpose; long m_compute_basis; long m_num_processed; void reset() { memset(this, 0, sizeof(grobner_stats)); } grobner_stats() { reset(); } }; /** \brief Simple Grobner basis implementation with no indexing. */ class grobner { protected: struct monomial_lt; public: grobner_stats m_stats; class monomial { rational m_coeff; ptr_vector m_vars; //!< sorted variables friend class grobner; friend struct monomial_lt; monomial() {} public: rational const & get_coeff() const { return m_coeff; } unsigned get_degree() const { return m_vars.size(); } unsigned get_size() const { return get_degree(); } expr * get_var(unsigned idx) const { return m_vars[idx]; } }; class equation { unsigned m_scope_lvl; //!< scope level when this equation was created. unsigned m_bidx:31; //!< position at m_equations_to_delete unsigned m_lc:1; //!< true if equation if a linear combination of the input equations. ptr_vector m_monomials; //!< sorted monomials v_dependency * m_dep; //!< justification for the equality friend class grobner; equation() {} public: unsigned get_num_monomials() const { return m_monomials.size(); } monomial const * get_monomial(unsigned idx) const { return m_monomials[idx]; } monomial * const * get_monomials() const { return m_monomials.c_ptr(); } v_dependency * get_dependency() const { return m_dep; } unsigned hash() const { return m_bidx; } bool is_linear_combination() const { return m_lc; } }; protected: static bool is_eq_monomial_body(monomial const * m1, monomial const * m2); struct var_lt { obj_map & m_var2weight; var_lt(obj_map & m):m_var2weight(m) {} bool operator()(expr * v1, expr * v2) const; }; struct monomial_lt { var_lt & m_var_lt; monomial_lt(var_lt & lt):m_var_lt(lt) {} bool operator()(monomial * m1, monomial * m2) const; }; typedef obj_hashtable equation_set; typedef ptr_vector equation_vector; ast_manager & m_manager; v_dependency_manager & m_dep_manager; arith_util m_util; obj_map m_var2weight; var_lt m_var_lt; monomial_lt m_monomial_lt; equation_set m_processed; equation_set m_to_process; equation_vector m_equations_to_unfreeze; equation_vector m_equations_to_delete; bool m_changed_leading_term; // set to true, if the leading term was simplified. equation * m_unsat; struct scope { unsigned m_equations_to_unfreeze_lim; unsigned m_equations_to_delete_lim; }; svector m_scopes; ptr_vector m_tmp_monomials; ptr_vector m_del_monomials; ptr_vector m_tmp_vars1; ptr_vector m_tmp_vars2; unsigned m_num_new_equations; // temporary variable bool is_monomial_lt(monomial const & m1, monomial const & m2) const; void display_vars(std::ostream & out, unsigned num_vars, expr * const * vars) const; void display_var(std::ostream & out, expr * var) const; void display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const; void display_equations(std::ostream & out, equation_set const & v, char const * header) const; void del_equations(unsigned old_size); void del_monomials(ptr_vector& monomials); void unfreeze_equations(unsigned old_size); void del_equation(equation * eq); void flush(); bool update_order(equation * eq); void update_order(equation_set & s, bool processed); void add_var(monomial * m, expr * v); monomial * mk_monomial(rational const & coeff, expr * m); void init_equation(equation * eq, v_dependency * d); void extract_monomials(expr * lhs, ptr_buffer & monomials); void merge_monomials(ptr_vector & monomials); bool is_inconsistent(equation * eq) const; bool is_trivial(equation * eq) const; void normalize_coeff(ptr_vector & monomials); void simplify(ptr_vector & monomials); void simplify(equation * eq); bool is_subset(monomial const * m1, monomial const * m2, ptr_vector & rest) const; void mul_append(unsigned start_idx, equation const * source, rational const & coeff, ptr_vector const & vars, ptr_vector & result); monomial * copy_monomial(monomial const * m); equation * copy_equation(equation const * eq); equation * simplify(equation const * source, equation * target); equation * simplify_using_processed(equation * eq); bool is_better_choice(equation * eq1, equation * eq2); equation * pick_next(); void simplify_processed(equation * eq); void simplify_to_process(equation * eq); bool unify(monomial const * m1, monomial const * m2, ptr_vector & rest1, ptr_vector & rest2); void superpose(equation * eq1, equation * eq2); void superpose(equation * eq); void copy_to(equation_set const & s, ptr_vector & result) const; public: grobner(ast_manager & m, v_dependency_manager & dep_m); ~grobner(); unsigned get_scope_level() const { return m_scopes.size(); } /** \brief Set the weight of a term that is viewed as a variable by this module. The weight is used to order monomials. If the weight is not set for a term t, then the weight of t is assumed to be 0. */ void set_weight(expr * n, int weight); int get_weight(expr * n) const { int w = 0; m_var2weight.find(n, w); return w; } /** \brief Update equations after set_weight was invoked once or more. */ void update_order(); /** \brief Create a new monomial. The caller owns the monomial until it invokes assert_eq_0. A monomial cannot be use to create several equations. */ monomial * mk_monomial(rational const & coeff, unsigned num_vars, expr * const * vars); void del_monomial(monomial * m); /** \brief Assert the given equality. This method assumes eq is simplified. */ void assert_eq(expr * eq, v_dependency * ex = 0); /** \brief Assert the equality monomials[0] + ... + monomials[num_monomials - 1] = 0. This method assumes the monomials were simplified. */ void assert_eq_0(unsigned num_monomials, expr * const * monomials, v_dependency * ex = 0); /** \brief Assert the equality monomials[0] + ... + monomials[num_monomials - 1] = 0. This method assumes the monomials were simplified. */ void assert_eq_0(unsigned num_monomials, monomial * const * monomials, v_dependency * ex = 0); /** \brief Assert the equality coeffs[0] * monomials[0] + ... + coeffs[num_monomials-1] * monomials[num_monomials - 1] = 0. This method assumes the monomials were simplified. */ void assert_eq_0(unsigned num_monomials, rational const * coeffs, expr * const * monomials, v_dependency * ex = 0); /** \brief Assert the monomial tautology (quote (x_1 * ... * x_n)) - x_1 * ... * x_n = 0 */ void assert_monomial_tautology(expr * m); /** \brief Compute Grobner basis. Return true if the threshold was not reached. */ bool compute_basis(unsigned threshold); /** \brief Return true if an inconsistency was detected. */ bool inconsistent() const { return m_unsat != 0; } /** \brief Simplify the given expression using the equalities asserted using assert_eq. Store the result in 'result'. */ void simplify(expr * n, expr_ref & result); /** \brief Reset state. Remove all equalities asserted with assert_eq. */ void reset(); void get_equations(ptr_vector & result) const; void push_scope(); void pop_scope(unsigned num_scopes); void display_equation(std::ostream & out, equation const & eq) const; void display_monomial(std::ostream & out, monomial const & m) const; void display(std::ostream & out) const; }; #endif /* GROBNER_H_ */ z3-z3-4.4.1/src/math/hilbert/000077500000000000000000000000001260446376700155765ustar00rootroot00000000000000z3-z3-4.4.1/src/math/hilbert/heap_trie.h000066400000000000000000000505741260446376700177220ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: heap_trie.h Abstract: Heap trie structure. Structure that lets you retrieve point-wise smaller entries of a tuple. A lookup is to identify entries whose keys are point-wise dominated by the lookup key. Author: Nikolaj Bjorner (nbjorner) 2013-02-15. Notes: tries are unordered vectors of keys. This could be enhanced to use either heaps or sorting. The problem with using the heap implementation directly is that there is no way to retrieve elements less or equal to a key that is not already in the heap. If nodes have only a few elements, then this would also be a bloated data-structure to maintain. Nodes are not de-allocated. Their reference count indicates if they are valid. Possibly, add garbage collection. Maintaining sorted ranges for larger domains is another option. Another possible enhancement is to resplay the tree. Keep current key index in the nodes. --*/ #ifndef HEAP_TRIE_H_ #define HEAP_TRIE_H_ #include "map.h" #include "vector.h" #include "buffer.h" #include "statistics.h" #include "small_object_allocator.h" template class heap_trie { struct stats { unsigned m_num_inserts; unsigned m_num_removes; unsigned m_num_find_eq; unsigned m_num_find_le; unsigned m_num_find_le_nodes; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; enum node_t { trie_t, leaf_t }; class node { node_t m_type; unsigned m_ref; public: node(node_t t): m_type(t), m_ref(0) {} virtual ~node() {} node_t type() const { return m_type; } void inc_ref() { ++m_ref; } void dec_ref() { SASSERT(m_ref > 0); --m_ref; } unsigned ref_count() const { return m_ref; } virtual void display(std::ostream& out, unsigned indent) const = 0; virtual unsigned num_nodes() const = 0; virtual unsigned num_leaves() const = 0; }; class leaf : public node { Value m_value; public: leaf(): node(leaf_t) {} virtual ~leaf() {} Value const& get_value() const { return m_value; } void set_value(Value const& v) { m_value = v; } virtual void display(std::ostream& out, unsigned indent) const { out << " value: " << m_value; } virtual unsigned num_nodes() const { return 1; } virtual unsigned num_leaves() const { return this->ref_count()>0?1:0; } }; typedef buffer, true, 2> children_t; // lean trie node class trie : public node { children_t m_nodes; public: trie(): node(trie_t) {} virtual ~trie() { } node* find_or_insert(Key k, node* n) { for (unsigned i = 0; i < m_nodes.size(); ++i) { if (m_nodes[i].first == k) { return m_nodes[i].second; } } m_nodes.push_back(std::make_pair(k, n)); return n; } bool find(Key k, node*& n) const { for (unsigned i = 0; i < m_nodes.size(); ++i) { if (m_nodes[i].first == k) { n = m_nodes[i].second; return n->ref_count() > 0; } } return false; } // push nodes whose keys are <= key into vector. void find_le(KeyLE& le, Key key, ptr_vector& nodes) { for (unsigned i = 0; i < m_nodes.size(); ++i) { if (le.le(m_nodes[i].first, key)) { node* n = m_nodes[i].second; if (n->ref_count() > 0){ nodes.push_back(n); } } } } children_t const& nodes() const { return m_nodes; } children_t & nodes() { return m_nodes; } virtual void display(std::ostream& out, unsigned indent) const { for (unsigned j = 0; j < m_nodes.size(); ++j) { if (j != 0 || indent > 0) { out << "\n"; } for (unsigned i = 0; i < indent; ++i) { out << " "; } node* n = m_nodes[j].second; out << m_nodes[j].first << " refs: " << n->ref_count(); n->display(out, indent + 1); } } virtual unsigned num_nodes() const { unsigned sz = 1; for (unsigned j = 0; j < m_nodes.size(); ++j) { sz += m_nodes[j].second->num_nodes(); } return sz; } virtual unsigned num_leaves() const { unsigned sz = 0; for (unsigned j = 0; j < m_nodes.size(); ++j) { sz += m_nodes[j].second->num_leaves(); } return sz; } private: bool contains(Key k) { for (unsigned j = 0; j < m_nodes.size(); ++j) { if (m_nodes[j].first == k) { return true; } } return false; } }; small_object_allocator m_alloc; KeyLE& m_le; unsigned m_num_keys; unsigned_vector m_keys; unsigned m_do_reshuffle; node* m_root; stats m_stats; node* m_spare_leaf; node* m_spare_trie; public: heap_trie(KeyLE& le): m_alloc("heap_trie"), m_le(le), m_num_keys(0), m_do_reshuffle(4), m_root(0), m_spare_leaf(0), m_spare_trie(0) {} ~heap_trie() { del_node(m_root); del_node(m_spare_leaf); del_node(m_spare_trie); } unsigned size() const { return m_root?m_root->num_leaves():0; } void reset(unsigned num_keys) { del_node(m_root); del_node(m_spare_leaf); del_node(m_spare_trie); m_num_keys = num_keys; m_keys.resize(num_keys); for (unsigned i = 0; i < num_keys; ++i) { m_keys[i] = i; } m_root = mk_trie(); m_spare_trie = mk_trie(); m_spare_leaf = mk_leaf(); } void insert(Key const* keys, Value const& val) { ++m_stats.m_num_inserts; insert(m_root, num_keys(), keys, m_keys.c_ptr(), val); #if 0 if (m_stats.m_num_inserts == (1 << m_do_reshuffle)) { m_do_reshuffle++; reorder_keys(); } #endif } bool find_eq(Key const* keys, Value& value) { ++m_stats.m_num_find_eq; node* n = m_root; node* m; for (unsigned i = 0; i < num_keys(); ++i) { if (!to_trie(n)->find(get_key(keys, i), m)) { return false; } n = m; } value = to_leaf(n)->get_value(); return true; } void find_all_le(Key const* keys, vector& values) { ++m_stats.m_num_find_le; ptr_vector todo[2]; todo[0].push_back(m_root); bool index = false; for (unsigned i = 0; i < num_keys(); ++i) { for (unsigned j = 0; j < todo[index].size(); ++j) { ++m_stats.m_num_find_le_nodes; to_trie(todo[index][j])->find_le(m_le, get_key(keys, i), todo[!index]); } todo[index].reset(); index = !index; } for (unsigned j = 0; j < todo[index].size(); ++j) { values.push_back(to_leaf(todo[index][j])->get_value()); } } // callback based find function class check_value { public: virtual bool operator()(Value const& v) = 0; }; bool find_le(Key const* keys, check_value& check) { ++m_stats.m_num_find_le; ++m_stats.m_num_find_le_nodes; return find_le(m_root, 0, keys, check); } void remove(Key const* keys) { ++m_stats.m_num_removes; // assumption: key is in table. node* n = m_root; node* m; for (unsigned i = 0; i < num_keys(); ++i) { n->dec_ref(); VERIFY (to_trie(n)->find(get_key(keys, i), m)); n = m; } n->dec_ref(); } void reset_statistics() { m_stats.reset(); } void collect_statistics(statistics& st) const { st.update("heap_trie.num_inserts", m_stats.m_num_inserts); st.update("heap_trie.num_removes", m_stats.m_num_removes); st.update("heap_trie.num_find_eq", m_stats.m_num_find_eq); st.update("heap_trie.num_find_le", m_stats.m_num_find_le); st.update("heap_trie.num_find_le_nodes", m_stats.m_num_find_le_nodes); if (m_root) st.update("heap_trie.num_nodes", m_root->num_nodes()); unsigned_vector nums; ptr_vector todo; if (m_root) todo.push_back(m_root); while (!todo.empty()) { node* n = todo.back(); todo.pop_back(); if (is_trie(n)) { trie* t = to_trie(n); unsigned sz = t->nodes().size(); if (nums.size() <= sz) { nums.resize(sz+1); } ++nums[sz]; for (unsigned i = 0; i < sz; ++i) { todo.push_back(t->nodes()[i].second); } } } if (nums.size() < 16) nums.resize(16); st.update("heap_trie.num_1_children", nums[1]); st.update("heap_trie.num_2_children", nums[2]); st.update("heap_trie.num_3_children", nums[3]); st.update("heap_trie.num_4_children", nums[4]); st.update("heap_trie.num_5_children", nums[5]); st.update("heap_trie.num_6_children", nums[6]); st.update("heap_trie.num_7_children", nums[7]); st.update("heap_trie.num_8_children", nums[8]); st.update("heap_trie.num_9_children", nums[9]); st.update("heap_trie.num_10_children", nums[10]); st.update("heap_trie.num_11_children", nums[11]); st.update("heap_trie.num_12_children", nums[12]); st.update("heap_trie.num_13_children", nums[13]); st.update("heap_trie.num_14_children", nums[14]); st.update("heap_trie.num_15_children", nums[15]); unsigned sz = 0; for (unsigned i = 16; i < nums.size(); ++i) { sz += nums[i]; } st.update("heap_trie.num_16+_children", sz); } void display(std::ostream& out) const { m_root->display(out, 0); out << "\n"; } class iterator { ptr_vector m_path; unsigned_vector m_idx; vector m_keys; unsigned m_count; public: iterator(node* n) { if (!n) { m_count = UINT_MAX; } else { m_count = 0; first(n); } } Key const* keys() { return m_keys.c_ptr(); } Value const& value() const { return to_leaf(m_path.back())->get_value(); } iterator& operator++() { fwd(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const& it) const {return m_count == it.m_count; } bool operator!=(iterator const& it) const {return m_count != it.m_count; } private: void first(node* r) { SASSERT(r->ref_count() > 0); while (is_trie(r)) { trie* t = to_trie(r); m_path.push_back(r); unsigned sz = t->nodes().size(); for (unsigned i = 0; i < sz; ++i) { r = t->nodes()[i].second; if (r->ref_count() > 0) { m_idx.push_back(i); m_keys.push_back(t->nodes()[i].first); break; } } } SASSERT(is_leaf(r)); m_path.push_back(r); } void fwd() { if (m_path.empty()) { m_count = UINT_MAX; return; } m_path.pop_back(); while (!m_path.empty()) { trie* t = to_trie(m_path.back()); unsigned idx = m_idx.back(); unsigned sz = t->nodes().size(); m_idx.pop_back(); m_keys.pop_back(); for (unsigned i = idx+1; i < sz; ++i) { node* r = t->nodes()[i].second; if (r->ref_count() > 0) { m_idx.push_back(i); m_keys.push_back(t->nodes()[i].first); first(r); ++m_count; return; } } m_path.pop_back(); } m_count = UINT_MAX; } }; iterator begin() const { return iterator(m_root); } iterator end() const { return iterator(0); } private: inline unsigned num_keys() const { return m_num_keys; } inline Key const& get_key(Key const* keys, unsigned i) const { return keys[m_keys[i]]; } struct KeyEq { bool operator()(Key const& k1, Key const& k2) const { return k1 == k2; } }; typedef hashtable key_set; struct key_info { unsigned m_index; unsigned m_index_size; key_info(unsigned i, unsigned sz): m_index(i), m_index_size(sz) {} bool operator<(key_info const& other) const { return (m_index_size < other.m_index_size) || ((m_index_size == other.m_index_size) && (m_index < other.m_index)); } }; void reorder_keys() { vector weights; weights.resize(num_keys()); unsigned_vector depth; ptr_vector nodes; depth.push_back(0); nodes.push_back(m_root); while (!nodes.empty()) { node* n = nodes.back(); unsigned d = depth.back(); nodes.pop_back(); depth.pop_back(); if (is_trie(n)) { trie* t = to_trie(n); unsigned sz = t->nodes().size(); for (unsigned i = 0; i < sz; ++i) { nodes.push_back(t->nodes()[i].second); depth.push_back(d+1); weights[d].insert(t->nodes()[i].first); } } } SASSERT(weights.size() == num_keys()); svector infos; unsigned sz = 0; bool is_sorted = true; for (unsigned i = 0; i < weights.size(); ++i) { unsigned sz2 = weights[i].size(); if (sz > sz2) { is_sorted = false; } sz = sz2; infos.push_back(key_info(i, sz)); } if (is_sorted) { return; } std::sort(infos.begin(), infos.end()); unsigned_vector sorted_keys, new_keys; for (unsigned i = 0; i < num_keys(); ++i) { unsigned j = infos[i].m_index; sorted_keys.push_back(j); new_keys.push_back(m_keys[j]); } // m_keys: i |-> key_index // new_keys: i |-> new_key_index // permutation: key_index |-> new_key_index SASSERT(sorted_keys.size() == num_keys()); SASSERT(new_keys.size() == num_keys()); SASSERT(m_keys.size() == num_keys()); iterator it = begin(); trie* new_root = mk_trie(); IF_VERBOSE(2, verbose_stream() << "before reshuffle: " << m_root->num_nodes() << " nodes\n";); for (; it != end(); ++it) { IF_VERBOSE(2, for (unsigned i = 0; i < num_keys(); ++i) { for (unsigned j = 0; j < num_keys(); ++j) { if (m_keys[j] == i) { verbose_stream() << it.keys()[j] << " "; break; } } } verbose_stream() << " |-> " << it.value() << "\n";); insert(new_root, num_keys(), it.keys(), sorted_keys.c_ptr(), it.value()); } del_node(m_root); m_root = new_root; for (unsigned i = 0; i < m_keys.size(); ++i) { m_keys[i] = new_keys[i]; } IF_VERBOSE(2, verbose_stream() << "after reshuffle: " << new_root->num_nodes() << " nodes\n";); IF_VERBOSE(2, it = begin(); for (; it != end(); ++it) { for (unsigned i = 0; i < num_keys(); ++i) { for (unsigned j = 0; j < num_keys(); ++j) { if (m_keys[j] == i) { verbose_stream() << it.keys()[j] << " "; break; } } } verbose_stream() << " |-> " << it.value() << "\n"; }); } bool find_le(node* n, unsigned index, Key const* keys, check_value& check) { if (index == num_keys()) { SASSERT(n->ref_count() > 0); bool r = check(to_leaf(n)->get_value()); IF_VERBOSE(2, for (unsigned j = 0; j < index; ++j) { verbose_stream() << " "; } verbose_stream() << to_leaf(n)->get_value() << (r?" hit\n":" miss\n");); return r; } else { Key const& key = get_key(keys, index); children_t& nodes = to_trie(n)->nodes(); for (unsigned i = 0; i < nodes.size(); ++i) { ++m_stats.m_num_find_le_nodes; node* m = nodes[i].second; IF_VERBOSE(2, for (unsigned j = 0; j < index; ++j) { verbose_stream() << " "; } verbose_stream() << nodes[i].first << " <=? " << key << " rc:" << m->ref_count() << "\n";); if (m->ref_count() > 0 && m_le.le(nodes[i].first, key) && find_le(m, index+1, keys, check)) { if (i > 0) { std::swap(nodes[i], nodes[0]); } return true; } } return false; } } void insert(node* n, unsigned num_keys, Key const* keys, unsigned const* permutation, Value const& val) { // assumption: key is not in table. for (unsigned i = 0; i < num_keys; ++i) { n->inc_ref(); n = insert_key(to_trie(n), (i + 1 == num_keys), keys[permutation[i]]); } n->inc_ref(); to_leaf(n)->set_value(val); SASSERT(n->ref_count() == 1); } node* insert_key(trie* n, bool is_leaf, Key const& key) { node* m1 = is_leaf?m_spare_leaf:m_spare_trie; node* m2 = n->find_or_insert(key, m1); if (m1 == m2) { if (is_leaf) { m_spare_leaf = mk_leaf(); } else { m_spare_trie = mk_trie(); } } return m2; } leaf* mk_leaf() { void* mem = m_alloc.allocate(sizeof(leaf)); return new (mem) leaf(); } trie* mk_trie() { void* mem = m_alloc.allocate(sizeof(trie)); return new (mem) trie(); } void del_node(node* n) { if (!n) { return; } if (is_trie(n)) { trie* t = to_trie(n); for (unsigned i = 0; i < t->nodes().size(); ++i) { del_node(t->nodes()[i].second); } t->~trie(); m_alloc.deallocate(sizeof(trie), t); } else { leaf* l = to_leaf(n); l->~leaf(); m_alloc.deallocate(sizeof(leaf), l); } } static trie* to_trie(node* n) { SASSERT(is_trie(n)); return static_cast(n); } static leaf* to_leaf(node* n) { SASSERT(is_leaf(n)); return static_cast(n); } static bool is_leaf(node* n) { return n->type() == leaf_t; } static bool is_trie(node* n) { return n->type() == trie_t; } }; #endif z3-z3-4.4.1/src/math/hilbert/hilbert_basis.cpp000066400000000000000000001073671260446376700211320ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: hilbert_basis.cpp Abstract: Basic Hilbert Basis computation. Author: Nikolaj Bjorner (nbjorner) 2013-02-09. Revision History: --*/ #include "hilbert_basis.h" #include "heap.h" #include "map.h" #include "heap_trie.h" #include "stopwatch.h" typedef int_hashtable > int_table; class hilbert_basis::value_index1 { struct stats { unsigned m_num_comparisons; unsigned m_num_hit; unsigned m_num_miss; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; hilbert_basis& hb; int_table m_table; stats m_stats; public: value_index1(hilbert_basis& hb): hb(hb) {} void insert(offset_t idx, values const& vs) { m_table.insert(idx.m_offset); } void remove(offset_t idx, values const& vs) { m_table.remove(idx.m_offset); } void reset() { m_table.reset(); } bool find(offset_t idx, values const& vs) { // display_profile(idx, std::cout); int_table::iterator it = m_table.begin(), end = m_table.end(); for (; it != end; ++it) { offset_t offs(*it); ++m_stats.m_num_comparisons; if (*it != static_cast(idx.m_offset) && hb.is_subsumed(idx, offs)) { ++m_stats.m_num_hit; return true; } } ++m_stats.m_num_miss; return false; } void collect_statistics(statistics& st) const { st.update("hb.index.num_comparisons", m_stats.m_num_comparisons); st.update("hb.index.num_hit", m_stats.m_num_hit); st.update("hb.index.num_miss", m_stats.m_num_miss); } void reset_statistics() { m_stats.reset(); } unsigned size() const { return m_table.size(); } void display(std::ostream& out) const { int_table::iterator it = m_table.begin(), end = m_table.end(); for (; it != end; ++it) { offset_t offs(*it); hb.display(out, offs); } display_profile(out); } private: typedef hashtable numeral_set; void display_profile(std::ostream& out) const { vector weights; weights.resize(hb.get_num_vars()+1); int_table::iterator it = m_table.begin(), end = m_table.end(); for (; it != end; ++it) { offset_t offs(*it); values const& ws = hb.vec(offs); weights[0].insert(ws.weight()); for (unsigned i = 0; i < hb.get_num_vars(); ++i) { weights[i+1].insert(ws[i]); } } out << "profile: "; for (unsigned i = 0; i < weights.size(); ++i) { out << weights[i].size() << " "; } out << "\n"; } void display_profile(offset_t idx, std::ostream& out) { unsigned_vector leq; unsigned nv = hb.get_num_vars(); values const& vs = hb.vec(idx); leq.resize(nv+1); numeral maxw(0); for (unsigned i = 0; i < nv; ++i) { if (!hb.is_abs_geq(maxw, vs[i])) { maxw = vs[i]; } } unsigned num_below_max = 0; int_table::iterator it = m_table.begin(), end = m_table.end(); for (; it != end; ++it) { offset_t offs(*it); values const& ws = hb.vec(offs); if (ws.weight() <= vs.weight()) { leq[0]++; } bool filtered = false; for (unsigned i = 0; i < nv; ++i) { if (hb.is_abs_geq(vs[i], ws[i])) { leq[i+1]++; } if (!hb.is_abs_geq(maxw, ws[i])) { filtered = true; } } if (!filtered) { ++num_below_max; } } out << vs.weight() << ":" << leq[0] << " "; for (unsigned i = 0; i < nv; ++i) { out << vs[i] << ":" << leq[i+1] << " "; } out << " max<= " << num_below_max; out << "\n"; } }; class hilbert_basis::value_index2 { struct key_le { hilbert_basis& hb; key_le(hilbert_basis& hb): hb(hb) {} bool le(numeral const& n1, numeral const& n2) const { return hb.is_abs_geq(n2, n1); } }; typedef heap_trie ht; struct checker : public ht::check_value { hilbert_basis* hb; offset_t m_value; checker(): hb(0) {} virtual bool operator()(unsigned const& v) { if (m_value.m_offset != v) { // && hb->is_subsumed(m_value, offset_t(v))) { return true; } else { return false; } } }; hilbert_basis& hb; key_le m_le; ht m_trie; checker m_checker; unsigned m_offset; numeral const* get_keys(values const& vs) { return vs()-m_offset; } public: value_index2(hilbert_basis& hb): hb(hb), m_le(hb), m_trie(m_le), m_offset(1) { m_checker.hb = &hb; } void insert(offset_t idx, values const& vs) { m_trie.insert(get_keys(vs), idx.m_offset); } void remove(offset_t idx, values const& vs) { m_trie.remove(get_keys(vs)); } void reset(unsigned offset) { m_offset = offset; m_trie.reset(hb.get_num_vars()+m_offset); } bool find(offset_t idx, values const& vs) { m_checker.m_value = idx; return m_trie.find_le(get_keys(vs), m_checker); } void collect_statistics(statistics& st) const { m_trie.collect_statistics(st); } void reset_statistics() { m_trie.reset_statistics(); } unsigned size() const { return m_trie.size(); } void display(std::ostream& out) const { // m_trie.display(out); } }; class hilbert_basis::index { // for each non-positive weight a separate index. // for positive weights a shared value index. // typedef value_index1 value_index; typedef value_index2 value_index; // typedef value_index3 value_index; struct stats { unsigned m_num_find; unsigned m_num_insert; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; template class numeral_map : public map {}; typedef numeral_map value_map; hilbert_basis& hb; value_map m_neg; value_index m_pos; value_index m_zero; stats m_stats; unsigned m_num_ineqs; public: index(hilbert_basis& hb): hb(hb), m_pos(hb), m_zero(hb), m_num_ineqs(0) {} void insert(offset_t idx, values const& vs) { ++m_stats.m_num_insert; if (vs.weight().is_pos()) { m_pos.insert(idx, vs); } else if (vs.weight().is_zero()) { m_zero.insert(idx, vs); } else { value_index* map = 0; if (!m_neg.find(vs.weight(), map)) { map = alloc(value_index, hb); map->reset(m_num_ineqs); m_neg.insert(vs.weight(), map); } map->insert(idx, vs); } } void remove(offset_t idx, values const& vs) { if (vs.weight().is_pos()) { m_pos.remove(idx, vs); } else if (vs.weight().is_zero()) { m_zero.remove(idx, vs); } else { m_neg.find(vs.weight())->remove(idx, vs); } } bool find(offset_t idx, values const& vs) { ++m_stats.m_num_find; if (vs.weight().is_pos()) { return m_pos.find(idx, vs); } else if (vs.weight().is_zero()) { return m_zero.find(idx, vs); } else { value_index* map; return m_neg.find(vs.weight(), map) && map->find(idx, vs); } } void reset(unsigned num_ineqs) { value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { dealloc(it->m_value); } m_pos.reset(num_ineqs); m_zero.reset(num_ineqs); m_num_ineqs = num_ineqs; m_neg.reset(); } void collect_statistics(statistics& st) const { m_pos.collect_statistics(st); m_zero.collect_statistics(st); value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { it->m_value->collect_statistics(st); } st.update("hb.index.num_find", m_stats.m_num_find); st.update("hb.index.num_insert", m_stats.m_num_insert); st.update("hb.index.size", size()); } void reset_statistics() { m_pos.reset_statistics(); m_zero.reset_statistics(); value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { it->m_value->reset_statistics(); } } void display(std::ostream& out) const { m_pos.display(out); m_zero.display(out); value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { it->m_value->display(out); } } private: unsigned size() const { unsigned sz = m_pos.size(); sz += m_zero.size(); value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { sz += it->m_value->size(); } return sz; } }; /** \brief priority queue for passive list. */ class hilbert_basis::passive { struct lt { passive** p; lt(passive** p): p(p) {} bool operator()(int v1, int v2) const { return (**p)(v1, v2); } }; hilbert_basis& hb; svector m_passive; unsigned_vector m_free_list; passive* m_this; lt m_lt; heap m_heap; // binary heap over weights numeral sum_abs(offset_t idx) const { numeral w(0); unsigned nv = hb.get_num_vars(); for (unsigned i = 0; i < nv; ++i) { w += abs(hb.vec(idx)[i]); } return w; } public: passive(hilbert_basis& hb): hb(hb), m_lt(&m_this), m_heap(10, m_lt) { m_this = this; } void reset() { m_heap.reset(); m_free_list.reset(); m_passive.reset(); } bool empty() const { return m_heap.empty(); } offset_t pop() { SASSERT(!empty()); unsigned val = static_cast(m_heap.erase_min()); offset_t result = m_passive[val]; m_free_list.push_back(val); m_passive[val] = mk_invalid_offset(); return result; } void insert(offset_t idx) { unsigned v; if (m_free_list.empty()) { v = m_passive.size(); m_passive.push_back(idx); m_heap.set_bounds(v+1); } else { v = m_free_list.back(); m_free_list.pop_back(); m_passive[v] = idx; } m_heap.insert(v); } class iterator { passive& p; unsigned m_idx; void fwd() { while (m_idx < p.m_passive.size() && is_invalid_offset(p.m_passive[m_idx])) { ++m_idx; } } public: iterator(passive& p, unsigned i): p(p), m_idx(i) { fwd(); } offset_t operator*() const { return p.m_passive[m_idx]; } iterator& operator++() { ++m_idx; fwd(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const& it) const {return m_idx == it.m_idx; } bool operator!=(iterator const& it) const {return m_idx != it.m_idx; } }; iterator begin() { return iterator(*this, 0); } iterator end() { return iterator(*this, m_passive.size()); } bool operator()(int v1, int v2) const { offset_t idx1 = m_passive[v1]; offset_t idx2 = m_passive[v2]; return sum_abs(idx1) < sum_abs(idx2); } }; class hilbert_basis::vector_lt_t { hilbert_basis& hb; public: vector_lt_t(hilbert_basis& hb): hb(hb) {} bool operator()(offset_t idx1, offset_t idx2) const { return hb.vector_lt(idx1, idx2); } }; class hilbert_basis::passive2 { struct lt { passive2** p; lt(passive2** p): p(p) {} bool operator()(int v1, int v2) const { return (**p)(v1, v2); } }; hilbert_basis& hb; svector m_pos_sos; svector m_neg_sos; vector m_pos_sos_sum; vector m_neg_sos_sum; vector m_sum_abs; unsigned_vector m_psos; svector m_pas; vector m_weight; unsigned_vector m_free_list; passive2* m_this; lt m_lt; heap m_heap; // binary heap over weights numeral sum_abs(offset_t idx) const { numeral w(0); unsigned nv = hb.get_num_vars(); for (unsigned i = 0; i < nv; ++i) { w += abs(hb.vec(idx)[i]); } return w; } public: passive2(hilbert_basis& hb): hb(hb), m_lt(&m_this), m_heap(10, m_lt) { m_this = this; } void init(svector const& I) { for (unsigned i = 0; i < I.size(); ++i) { numeral const& w = hb.vec(I[i]).weight(); if (w.is_pos()) { m_pos_sos.push_back(I[i]); m_pos_sos_sum.push_back(sum_abs(I[i])); } else { m_neg_sos.push_back(I[i]); m_neg_sos_sum.push_back(sum_abs(I[i])); } } } void reset() { m_heap.reset(); m_free_list.reset(); m_psos.reset(); m_pas.reset(); m_sum_abs.reset(); m_pos_sos.reset(); m_neg_sos.reset(); m_pos_sos_sum.reset(); m_neg_sos_sum.reset(); m_weight.reset(); } void insert(offset_t idx, unsigned offset) { SASSERT(!m_pos_sos.empty()); unsigned v; if (m_free_list.empty()) { v = m_pas.size(); m_pas.push_back(idx); m_psos.push_back(offset); m_weight.push_back(numeral(0)); m_heap.set_bounds(v+1); m_sum_abs.push_back(sum_abs(idx)); } else { v = m_free_list.back(); m_free_list.pop_back(); m_pas[v] = idx; m_psos[v] = offset; m_weight[v] = numeral(0); m_sum_abs[v] = sum_abs(idx); } next_resolvable(hb.vec(idx).weight().is_pos(), v); } bool empty() const { return m_heap.empty(); } unsigned pop(offset_t& sos, offset_t& pas) { SASSERT (!empty()); unsigned val = static_cast(m_heap.erase_min()); pas = m_pas[val]; numeral old_weight = hb.vec(pas).weight(); bool is_positive = old_weight.is_pos(); unsigned psos = m_psos[val]; sos = is_positive?m_neg_sos[psos]:m_pos_sos[psos]; m_psos[val]++; next_resolvable(is_positive, val); numeral new_weight = hb.vec(sos).weight() + old_weight; if (new_weight.is_pos() != old_weight.is_pos()) { psos = 0; } return psos; } bool operator()(int v1, int v2) const { return m_weight[v1] < m_weight[v2]; } class iterator { passive2& p; unsigned m_idx; void fwd() { while (m_idx < p.m_pas.size() && is_invalid_offset(p.m_pas[m_idx])) { ++m_idx; } } public: iterator(passive2& p, unsigned i): p(p), m_idx(i) { fwd(); } offset_t pas() const { return p.m_pas[m_idx]; } offset_t sos() const { return (p.hb.vec(pas()).weight().is_pos()?p.m_neg_sos:p.m_pos_sos)[p.m_psos[m_idx]]; } iterator& operator++() { ++m_idx; fwd(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const& it) const {return m_idx == it.m_idx; } bool operator!=(iterator const& it) const {return m_idx != it.m_idx; } }; iterator begin() { return iterator(*this, 0); } iterator end() { return iterator(*this, m_pas.size()); } private: void next_resolvable(bool is_positive, unsigned v) { offset_t pas = m_pas[v]; svector const& soss = is_positive?m_neg_sos:m_pos_sos; while (m_psos[v] < soss.size()) { unsigned psos = m_psos[v]; offset_t sos = soss[psos]; if (hb.can_resolve(sos, pas, false)) { m_weight[v] = m_sum_abs[v] + (is_positive?m_neg_sos_sum[psos]:m_pos_sos_sum[psos]); m_heap.insert(v); return; } ++m_psos[v]; } // add pas to free-list for hb if it is not in sos. m_free_list.push_back(v); m_psos[v] = UINT_MAX; m_pas[v] = mk_invalid_offset(); } }; hilbert_basis::hilbert_basis(): m_cancel(false), m_use_support(true), m_use_ordered_support(true), m_use_ordered_subsumption(true) { m_index = alloc(index, *this); m_passive = alloc(passive, *this); m_passive2 = alloc(passive2, *this); } hilbert_basis::~hilbert_basis() { dealloc(m_index); dealloc(m_passive); dealloc(m_passive2); } hilbert_basis::offset_t hilbert_basis::mk_invalid_offset() { return offset_t(UINT_MAX); } bool hilbert_basis::is_invalid_offset(offset_t offs) { return offs.m_offset == UINT_MAX; } void hilbert_basis::reset() { m_ineqs.reset(); m_iseq.reset(); m_store.reset(); m_basis.reset(); m_free_list.reset(); m_sos.reset(); m_zero.reset(); m_active.reset(); if (m_passive) { m_passive->reset(); } if (m_passive2) { m_passive2->reset(); } if (m_index) { m_index->reset(1); } m_ints.reset(); m_current_ineq = 0; } void hilbert_basis::collect_statistics(statistics& st) const { st.update("hb.num_subsumptions", m_stats.m_num_subsumptions); st.update("hb.num_resolves", m_stats.m_num_resolves); st.update("hb.num_saturations", m_stats.m_num_saturations); st.update("hb.basis_size", get_basis_size()); m_index->collect_statistics(st); } void hilbert_basis::reset_statistics() { m_stats.reset(); m_index->reset_statistics(); } void hilbert_basis::add_ge(rational_vector const& v, rational const& b) { SASSERT(m_ineqs.empty() || v.size() + 1 == m_ineqs.back().size()); num_vector w; w.push_back(to_numeral(-b)); for (unsigned i = 0; i < v.size(); ++i) { w.push_back(to_numeral(v[i])); } m_ineqs.push_back(w); m_iseq.push_back(false); } void hilbert_basis::add_le(rational_vector const& v, rational const& b) { rational_vector w(v); for (unsigned i = 0; i < w.size(); ++i) { w[i].neg(); } add_ge(w, -b); } void hilbert_basis::add_eq(rational_vector const& v, rational const& b) { SASSERT(m_ineqs.empty() || v.size() + 1 == m_ineqs.back().size()); num_vector w; w.push_back(to_numeral(-b)); for (unsigned i = 0; i < v.size(); ++i) { w.push_back(to_numeral(v[i])); } m_ineqs.push_back(w); m_iseq.push_back(true); } void hilbert_basis::add_ge(rational_vector const& v) { add_ge(v, rational(0)); } void hilbert_basis::add_le(rational_vector const& v) { add_le(v, rational(0)); } void hilbert_basis::add_eq(rational_vector const& v) { add_eq(v, rational(0)); } void hilbert_basis::set_is_int(unsigned var_index) { // // The 0'th index is reserved for the constant // coefficient. Shift indices by 1. // m_ints.push_back(var_index+1); } bool hilbert_basis::get_is_int(unsigned var_index) const { return m_ints.contains(var_index+1); } unsigned hilbert_basis::get_num_vars() const { if (m_ineqs.empty()) { return 0; } else { SASSERT(m_ineqs.back().size() > 1); return m_ineqs.back().size(); } } hilbert_basis::values hilbert_basis::vec(offset_t offs) const { return values(m_ineqs.size(), m_store.c_ptr() + offs.m_offset); } void hilbert_basis::init_basis() { m_basis.reset(); m_store.reset(); m_free_list.reset(); unsigned nv = get_num_vars(); for (unsigned i = 0; i < nv; ++i) { add_unit_vector(i, numeral(1)); } for (unsigned i = 0; i < m_ints.size(); ++i) { add_unit_vector(m_ints[i], numeral(-1)); } } void hilbert_basis::add_unit_vector(unsigned i, numeral const& e) { unsigned num_vars = get_num_vars(); num_vector w(num_vars, numeral(0)); w[i] = e; offset_t idx = alloc_vector(); values v = vec(idx); for (unsigned j = 0; j < num_vars; ++j) { v[j] = w[j]; } m_basis.push_back(idx); } lbool hilbert_basis::saturate() { init_basis(); m_current_ineq = 0; while (!m_cancel && m_current_ineq < m_ineqs.size()) { select_inequality(); stopwatch sw; sw.start(); lbool r = saturate(m_ineqs[m_current_ineq], m_iseq[m_current_ineq]); IF_VERBOSE(3, { statistics st; collect_statistics(st); st.display(verbose_stream()); sw.stop(); verbose_stream() << "time: " << sw.get_seconds() << "\n"; }); ++m_stats.m_num_saturations; if (r != l_true) { return r; } ++m_current_ineq; } if (m_cancel) { return l_undef; } return l_true; } lbool hilbert_basis::saturate_orig(num_vector const& ineq, bool is_eq) { m_active.reset(); m_passive->reset(); m_zero.reset(); m_index->reset(m_current_ineq+1); int_table support; TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq);); iterator it = begin(); for (; it != end(); ++it) { offset_t idx = *it; values v = vec(idx); v.weight() = get_weight(v, ineq); for (unsigned k = 0; k < m_current_ineq; ++k) { v.weight(k) = get_weight(v, m_ineqs[k]); } add_goal(idx); if (m_use_support) { support.insert(idx.m_offset); } } TRACE("hilbert_basis", display(tout);); // resolve passive into active offset_t j = alloc_vector(); while (!m_passive->empty()) { if (m_cancel) { return l_undef; } offset_t idx = m_passive->pop(); TRACE("hilbert_basis", display(tout);); if (is_subsumed(idx)) { recycle(idx); continue; } for (unsigned i = 0; !m_cancel && i < m_active.size(); ++i) { if ((!m_use_support || support.contains(m_active[i].m_offset)) && can_resolve(idx, m_active[i], true)) { resolve(idx, m_active[i], j); if (add_goal(j)) { j = alloc_vector(); } } } m_active.push_back(idx); } m_free_list.push_back(j); // Move positive from active and zeros to new basis. m_basis.reset(); m_basis.append(m_zero); for (unsigned i = 0; !is_eq && i < m_active.size(); ++i) { offset_t idx = m_active[i]; if (vec(idx).weight().is_pos()) { m_basis.push_back(idx); } else { m_free_list.push_back(idx); } } m_active.reset(); m_passive->reset(); m_zero.reset(); TRACE("hilbert_basis", display(tout);); return m_basis.empty()?l_false:l_true; } bool hilbert_basis::vector_lt(offset_t idx1, offset_t idx2) const { values v = vec(idx1); values w = vec(idx2); numeral a(0), b(0); for (unsigned i = 0; i < get_num_vars(); ++i) { a += abs(v[i]); b += abs(w[i]); } return a < b; } lbool hilbert_basis::saturate(num_vector const& ineq, bool is_eq) { m_zero.reset(); m_index->reset(m_current_ineq+1); m_passive2->reset(); m_sos.reset(); TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq);); unsigned init_basis_size = 0; for (unsigned i = 0; i < m_basis.size(); ++i) { offset_t idx = m_basis[i]; values v = vec(idx); v.weight() = get_weight(v, ineq); for (unsigned k = 0; k < m_current_ineq; ++k) { v.weight(k) = get_weight(v, m_ineqs[k]); } m_index->insert(idx, v); if (v.weight().is_zero()) { m_zero.push_back(idx); } else { if (v.weight().is_pos()) { m_basis[init_basis_size++] = idx; } m_sos.push_back(idx); } } m_basis.resize(init_basis_size); m_passive2->init(m_sos); // ASSERT basis is sorted by weight. // initialize passive for (unsigned i = 0; (init_basis_size > 0) && i < m_sos.size(); ++i) { if (vec(m_sos[i]).weight().is_neg()) { m_passive2->insert(m_sos[i], 0); } } TRACE("hilbert_basis", display(tout);); // resolve passive into active offset_t idx = alloc_vector(); while (!m_cancel && !m_passive2->empty()) { offset_t sos, pas; TRACE("hilbert_basis", display(tout); ); unsigned offset = m_passive2->pop(sos, pas); SASSERT(can_resolve(sos, pas, true)); resolve(sos, pas, idx); if (is_subsumed(idx)) { continue; } values v = vec(idx); m_index->insert(idx, v); if (v.weight().is_zero()) { m_zero.push_back(idx); } else { if (!m_use_ordered_support) { offset = 0; } m_passive2->insert(idx, offset); if (v.weight().is_pos()) { m_basis.push_back(idx); } } idx = alloc_vector(); } if (m_cancel) { return l_undef; } m_free_list.push_back(idx); // remove positive values from basis if we are looking for an equality. while (is_eq && !m_basis.empty()) { m_free_list.push_back(m_basis.back()); m_basis.pop_back(); } m_basis.append(m_zero); std::sort(m_basis.begin(), m_basis.end(), vector_lt_t(*this)); m_zero.reset(); TRACE("hilbert_basis", display(tout);); return m_basis.empty()?l_false:l_true; } void hilbert_basis::get_basis_solution(unsigned i, rational_vector& v, bool& is_initial) { offset_t offs = m_basis[i]; v.reset(); for (unsigned i = 1; i < get_num_vars(); ++i) { v.push_back(to_rational(vec(offs)[i])); } is_initial = !vec(offs)[0].is_zero(); } void hilbert_basis::get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq) { v.reset(); for (unsigned j = 1; j < m_ineqs[i].size(); ++j) { v.push_back(to_rational(m_ineqs[i][j])); } b = to_rational(-m_ineqs[i][0]); is_eq = m_iseq[i]; } void hilbert_basis::select_inequality() { SASSERT(m_current_ineq < m_ineqs.size()); unsigned best = m_current_ineq; unsigned non_zeros = get_num_nonzeros(m_ineqs[best]); unsigned prod = get_ineq_product(m_ineqs[best]); for (unsigned j = best+1; prod != 0 && j < m_ineqs.size(); ++j) { unsigned non_zeros2 = get_num_nonzeros(m_ineqs[j]); unsigned prod2 = get_ineq_product(m_ineqs[j]); if (prod2 == 0) { prod = prod2; non_zeros = non_zeros2; best = j; break; } if (non_zeros2 < non_zeros || (non_zeros2 == non_zeros && prod2 < prod)) { prod = prod2; non_zeros = non_zeros2; best = j; } } if (best != m_current_ineq) { std::swap(m_ineqs[m_current_ineq], m_ineqs[best]); std::swap(m_iseq[m_current_ineq], m_iseq[best]); } } unsigned hilbert_basis::get_num_nonzeros(num_vector const& ineq) { unsigned count = 0; for (unsigned i = 0; i < ineq.size(); ++i) { if (!ineq[i].is_zero()) { ++count; } } return count; } unsigned hilbert_basis::get_ineq_product(num_vector const& ineq) { unsigned num_pos = 0, num_neg = 0; iterator it = begin(); for (; it != end(); ++it) { values v = vec(*it); numeral w = get_weight(v, ineq); if (w.is_pos()) { ++num_pos; } else if (w.is_neg()) { ++num_neg; } } return num_pos * num_neg; } hilbert_basis::numeral hilbert_basis::get_ineq_diff(num_vector const& ineq) { numeral max_pos(0), min_neg(0); iterator it = begin(); for (; it != end(); ++it) { values v = vec(*it); numeral w = get_weight(v, ineq); if (w > max_pos) { max_pos = w; } else if (w < min_neg) { min_neg = w; } } return max_pos - min_neg; } void hilbert_basis::recycle(offset_t idx) { m_index->remove(idx, vec(idx)); m_free_list.push_back(idx); } void hilbert_basis::resolve(offset_t i, offset_t j, offset_t r) { ++m_stats.m_num_resolves; values v = vec(i); values w = vec(j); values u = vec(r); unsigned nv = get_num_vars(); for (unsigned k = 0; k < nv; ++k) { u[k] = v[k] + w[k]; } u.weight() = v.weight() + w.weight(); for (unsigned k = 0; k < m_current_ineq; ++k) { u.weight(k) = v.weight(k) + w.weight(k); } TRACE("hilbert_basis_verbose", display(tout, i); display(tout, j); display(tout, r); ); } hilbert_basis::offset_t hilbert_basis::alloc_vector() { if (m_free_list.empty()) { unsigned sz = m_ineqs.size() + get_num_vars(); unsigned idx = m_store.size(); m_store.resize(idx + sz); // std::cout << "alloc vector: " << idx << " " << sz << " " << m_store.c_ptr() + idx << " " << m_ineqs.size() << "\n"; return offset_t(idx); } else { offset_t result = m_free_list.back(); m_free_list.pop_back(); return result; } } bool hilbert_basis::add_goal(offset_t idx) { TRACE("hilbert_basis", display(tout, idx);); values v = vec(idx); if (is_subsumed(idx)) { return false; } m_index->insert(idx, v); if (v.weight().is_zero()) { m_zero.push_back(idx); } else { m_passive->insert(idx); } return true; } bool hilbert_basis::is_subsumed(offset_t idx) { if (m_index->find(idx, vec(idx))) { ++m_stats.m_num_subsumptions; return true; } return false; } bool hilbert_basis::can_resolve(offset_t i, offset_t j, bool check_sign) const { if (check_sign && get_sign(i) == get_sign(j)) { return false; } SASSERT(get_sign(i) != get_sign(j)); values const& v1 = vec(i); values const& v2 = vec(j); if (v1[0].is_one() && v2[0].is_one()) { return false; } for (unsigned i = 0; i < m_ints.size(); ++i) { unsigned j = m_ints[i]; if (v1[j].is_pos() && v2[j].is_neg()) { return false; } if (v1[j].is_neg() && v2[j].is_pos()) { return false; } } return true; } hilbert_basis::sign_t hilbert_basis::get_sign(offset_t idx) const { numeral const& val = vec(idx).weight(); if (val.is_pos()) { return pos; } if (val.is_neg()) { return neg; } return zero; } hilbert_basis::numeral hilbert_basis::get_weight(values const & val, num_vector const& ineq) const { numeral result(0); unsigned num_vars = get_num_vars(); for (unsigned i = 0; i < num_vars; ++i) { result += val[i]*ineq[i]; } return result; } void hilbert_basis::display(std::ostream& out) const { out << "inequalities:\n"; for (unsigned i = 0; i < m_ineqs.size(); ++i) { display_ineq(out, m_ineqs[i], m_iseq[i]); } if (!m_basis.empty()) { out << "basis:\n"; for (iterator it = begin(); it != end(); ++it) { display(out, *it); } } if (!m_active.empty()) { out << "active:\n"; for (unsigned i = 0; i < m_active.size(); ++i) { display(out, m_active[i]); } } if (!m_passive->empty()) { passive::iterator it = m_passive->begin(); passive::iterator end = m_passive->end(); out << "passive:\n"; for (; it != end; ++it) { display(out, *it); } } if (!m_passive2->empty()) { passive2::iterator it = m_passive2->begin(); passive2::iterator end = m_passive2->end(); out << "passive:\n"; for (; it != end; ++it) { display(out << "sos:", it.sos()); display(out << "pas:", it.pas()); } } if (!m_zero.empty()) { out << "zero:\n"; for (unsigned i = 0; i < m_zero.size(); ++i) { display(out, m_zero[i]); } } if (m_index) { m_index->display(out); } } void hilbert_basis::display(std::ostream& out, offset_t o) const { display(out, vec(o)); out << " -> " << vec(o).weight() << "\n"; } void hilbert_basis::display(std::ostream& out, values const& v) const { unsigned nv = get_num_vars(); for (unsigned j = 0; j < nv; ++j) { out << v[j] << " "; } } void hilbert_basis::display_ineq(std::ostream& out, num_vector const& v, bool is_eq) const { unsigned nv = v.size(); for (unsigned j = 1; j < nv; ++j) { if (!v[j].is_zero()) { if (j > 0) { if (v[j].is_pos()) { out << " + "; } else { out << " - "; } } else if (j == 0 && v[0].is_neg()) { out << "-"; } if (!v[j].is_one() && !v[j].is_minus_one()) { out << abs(v[j]) << "*"; } out << "x" << j; } } if (is_eq) { out << " = " << -v[0] << "\n"; } else { out << " >= " << -v[0] << "\n"; } } /** Vector v is subsumed by vector w if v[i] >= w[i] for each index i. a*v >= a*w for the evaluation of vectors with respect to a. . a*v < 0 => a*v = a*w . a*v > 0 => a*w > 0 . a*v = 0 => a*w = 0 Justification: let u := v - w, then u[i] >= 0 for each index i a*u = a*(v-w) >= 0 So v = u + w, where a*u >= 0, a*w >= 0. If a*v >= a*w >= 0 then v and w are linear solutions of e_i, and also v-w is a solution. If a*v = a*w < 0, then a*(v-w) = 0, so v can be obtained from w + (v - w). */ bool hilbert_basis::is_subsumed(offset_t i, offset_t j) const { values v = vec(i); values w = vec(j); numeral const& n = v.weight(); numeral const& m = w.weight(); bool r = i.m_offset != j.m_offset && n >= m && (!m.is_neg() || n == m) && is_geq(v, w); for (unsigned k = 0; r && k < m_current_ineq; ++k) { r = v.weight(k) >= w.weight(k); } CTRACE("hilbert_basis", r, display(tout, i); tout << " <= \n"; display(tout, j); tout << "\n";); return r; } bool hilbert_basis::is_geq(values const& v, values const& w) const { unsigned nv = get_num_vars(); for (unsigned i = 0; i < nv; ++i) { if (!is_abs_geq(v[i], w[i])) { return false; } } return true; } bool hilbert_basis::is_abs_geq(numeral const& v, numeral const& w) const { if (w.is_neg()) { return v <= w; } else { return v >= w; } } z3-z3-4.4.1/src/math/hilbert/hilbert_basis.h000066400000000000000000000156551260446376700205750ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: hilbert_basis.h Abstract: Basic Hilbert Basis computation. hilbert_basis computes a Hilbert basis for linear homogeneous inequalities over naturals. Author: Nikolaj Bjorner (nbjorner) 2013-02-09. Revision History: Hilbert basis can be templatized based on traits that define numeral: as rational, mpz, checked_int64 (checked or unchecked). --*/ #ifndef HILBERT_BASIS_H_ #define HILBERT_BASIS_H_ #include "rational.h" #include "lbool.h" #include "statistics.h" #include "checked_int64.h" typedef vector rational_vector; class hilbert_basis { static const bool check = true; typedef checked_int64 numeral; typedef vector num_vector; static checked_int64 to_numeral(rational const& r) { if (!r.is_int64()) { throw checked_int64::overflow_exception(); } return checked_int64(r.get_int64()); } static rational to_rational(checked_int64 const& i) { return rational(i.get_int64(), rational::i64()); } class value_index1; class value_index2; class value_index3; class index; class passive; class passive2; struct offset_t { unsigned m_offset; offset_t(unsigned o) : m_offset(o) {} offset_t(): m_offset(0) {} bool operator<(offset_t const& other) const { return m_offset < other.m_offset; } }; enum sign_t { pos, neg, zero }; struct stats { unsigned m_num_subsumptions; unsigned m_num_resolves; unsigned m_num_saturations; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; class values { numeral* m_values; public: values(unsigned offset, numeral* v): m_values(v+offset) { } numeral& weight() { return m_values[-1]; } // value of a*x numeral const& weight() const { return m_values[-1]; } // value of a*x numeral& weight(int i) { return m_values[-2-i]; } // value of b_i*x for 0 <= i < current inequality. numeral const& weight(int i) const { return m_values[-2-i]; } // value of b_i*x numeral& operator[](unsigned i) { return m_values[i]; } // value of x_i numeral const& operator[](unsigned i) const { return m_values[i]; } // value of x_i numeral const* operator()() const { return m_values; } }; vector m_ineqs; // set of asserted inequalities svector m_iseq; // inequalities that are equalities num_vector m_store; // store of vectors svector m_basis; // vector of current basis svector m_free_list; // free list of unused storage svector m_active; // active set svector m_sos; // set of support svector m_zero; // zeros passive* m_passive; // passive set passive2* m_passive2; // passive set volatile bool m_cancel; stats m_stats; index* m_index; // index of generated vectors unsigned_vector m_ints; // indices that can be both positive and negative unsigned m_current_ineq; bool m_use_support; // parameter: (associativity) resolve only against vectors that are initially in basis. bool m_use_ordered_support; // parameter: (commutativity) resolve in order bool m_use_ordered_subsumption; // parameter class iterator { hilbert_basis const& hb; unsigned m_idx; public: iterator(hilbert_basis const& hb, unsigned idx): hb(hb), m_idx(idx) {} offset_t operator*() const { return hb.m_basis[m_idx]; } iterator& operator++() { ++m_idx; return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const& it) const {return m_idx == it.m_idx; } bool operator!=(iterator const& it) const {return m_idx != it.m_idx; } }; static offset_t mk_invalid_offset(); static bool is_invalid_offset(offset_t offs); lbool saturate(num_vector const& ineq, bool is_eq); lbool saturate_orig(num_vector const& ineq, bool is_eq); void init_basis(); void select_inequality(); unsigned get_num_nonzeros(num_vector const& ineq); unsigned get_ineq_product(num_vector const& ineq); numeral get_ineq_diff(num_vector const& ineq); void add_unit_vector(unsigned i, numeral const& e); unsigned get_num_vars() const; numeral get_weight(values const & val, num_vector const& ineq) const; bool is_geq(values const& v, values const& w) const; bool is_abs_geq(numeral const& v, numeral const& w) const; bool is_subsumed(offset_t idx); bool is_subsumed(offset_t i, offset_t j) const; void recycle(offset_t idx); bool can_resolve(offset_t i, offset_t j, bool check_sign) const; sign_t get_sign(offset_t idx) const; bool add_goal(offset_t idx); offset_t alloc_vector(); void resolve(offset_t i, offset_t j, offset_t r); iterator begin() const { return iterator(*this,0); } iterator end() const { return iterator(*this, m_basis.size()); } class vector_lt_t; bool vector_lt(offset_t i, offset_t j) const; values vec(offset_t offs) const; void display(std::ostream& out, offset_t o) const; void display(std::ostream& out, values const & v) const; void display_ineq(std::ostream& out, num_vector const& v, bool is_eq) const; public: hilbert_basis(); ~hilbert_basis(); void reset(); void set_use_support(bool b) { m_use_support = b; } void set_use_ordered_support(bool b) { m_use_ordered_support = b; } void set_use_ordered_subsumption(bool b) { m_use_ordered_subsumption = b; } // add inequality v*x >= 0 // add inequality v*x <= 0 // add equality v*x = 0 void add_ge(rational_vector const& v); void add_le(rational_vector const& v); void add_eq(rational_vector const& v); // add inequality v*x >= b // add inequality v*x <= b // add equality v*x = b void add_ge(rational_vector const& v, rational const& b); void add_le(rational_vector const& v, rational const& b); void add_eq(rational_vector const& v, rational const& b); void set_is_int(unsigned var_index); bool get_is_int(unsigned var_index) const; lbool saturate(); unsigned get_basis_size() const { return m_basis.size(); } void get_basis_solution(unsigned i, rational_vector& v, bool& is_initial); unsigned get_num_ineqs() const { return m_ineqs.size(); } void get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq); void set_cancel(bool f) { m_cancel = f; } void display(std::ostream& out) const; void collect_statistics(statistics& st) const; void reset_statistics(); }; #endif z3-z3-4.4.1/src/math/interval/000077500000000000000000000000001260446376700157715ustar00rootroot00000000000000z3-z3-4.4.1/src/math/interval/README000066400000000000000000000005011260446376700166450ustar00rootroot00000000000000Template for interval arithmetic. The template can be instantiated using different numeral (integers/mpz, rationals/mpq, floating-point/mpf, etc) packages. The class im_default_config defines a default configuration for the template that uses rationals. It also shows what is the expected signature used by the template. z3-z3-4.4.1/src/math/interval/interval.h000066400000000000000000000341471260446376700177770ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: interval.h Abstract: Goodies/Templates for interval arithmetic Author: Leonardo de Moura (leonardo) 2012-07-19. Revision History: --*/ #ifndef INTERVAL_H_ #define INTERVAL_H_ #include"mpq.h" #include"ext_numeral.h" /** \brief Default configuration for interval manager. It is used for documenting the required interface. */ class im_default_config { unsynch_mpq_manager & m_manager; public: typedef unsynch_mpq_manager numeral_manager; typedef mpq numeral; // Every configuration object must provide an interval type. // The actual fields are irrelevant, the interval manager // accesses interval data using the following API. struct interval { numeral m_lower; numeral m_upper; unsigned m_lower_open:1; unsigned m_upper_open:1; unsigned m_lower_inf:1; unsigned m_upper_inf:1; }; // Should be NOOPs for precise numeral types. // For imprecise types (e.g., floats) it should set the rounding mode. void round_to_minus_inf() {} void round_to_plus_inf() {} void set_rounding(bool to_plus_inf) {} // Getters numeral const & lower(interval const & a) const { return a.m_lower; } numeral const & upper(interval const & a) const { return a.m_upper; } numeral & lower(interval & a) { return a.m_lower; } numeral & upper(interval & a) { return a.m_upper; } bool lower_is_open(interval const & a) const { return a.m_lower_open; } bool upper_is_open(interval const & a) const { return a.m_upper_open; } bool lower_is_inf(interval const & a) const { return a.m_lower_inf; } bool upper_is_inf(interval const & a) const { return a.m_upper_inf; } // Setters void set_lower(interval & a, numeral const & n) { m_manager.set(a.m_lower, n); } void set_upper(interval & a, numeral const & n) { m_manager.set(a.m_upper, n); } void set_lower_is_open(interval & a, bool v) { a.m_lower_open = v; } void set_upper_is_open(interval & a, bool v) { a.m_upper_open = v; } void set_lower_is_inf(interval & a, bool v) { a.m_lower_inf = v; } void set_upper_is_inf(interval & a, bool v) { a.m_upper_inf = v; } // Reference to numeral manager numeral_manager & m() const { return m_manager; } im_default_config(numeral_manager & m):m_manager(m) {} }; #define DEP_IN_LOWER1 1 #define DEP_IN_UPPER1 2 #define DEP_IN_LOWER2 4 #define DEP_IN_UPPER2 8 typedef short bound_deps; inline bool dep_in_lower1(bound_deps d) { return (d & DEP_IN_LOWER1) != 0; } inline bool dep_in_lower2(bound_deps d) { return (d & DEP_IN_LOWER2) != 0; } inline bool dep_in_upper1(bound_deps d) { return (d & DEP_IN_UPPER1) != 0; } inline bool dep_in_upper2(bound_deps d) { return (d & DEP_IN_UPPER2) != 0; } inline bound_deps dep1_to_dep2(bound_deps d) { SASSERT(!dep_in_lower2(d) && !dep_in_upper2(d)); bound_deps r = d << 2; SASSERT(dep_in_lower1(d) == dep_in_lower2(r)); SASSERT(dep_in_upper1(d) == dep_in_upper2(r)); SASSERT(!dep_in_lower1(r) && !dep_in_upper1(r)); return r; } /** \brief Interval dependencies for unary and binary operations on intervals. It contains the dependencies for the output lower and upper bounds for the resultant interval. */ struct interval_deps { bound_deps m_lower_deps; bound_deps m_upper_deps; }; template class interval_manager { public: typedef typename C::numeral_manager numeral_manager; typedef typename numeral_manager::numeral numeral; typedef typename C::interval interval; private: C m_c; numeral m_result_lower; numeral m_result_upper; numeral m_mul_ad; numeral m_mul_bc; numeral m_mul_ac; numeral m_mul_bd; numeral m_one; numeral m_minus_one; numeral m_inv_k; unsigned m_pi_n; interval m_pi_div_2; interval m_pi; interval m_3_pi_div_2; interval m_2_pi; volatile bool m_cancel; void round_to_minus_inf() { m_c.round_to_minus_inf(); } void round_to_plus_inf() { m_c.round_to_plus_inf(); } void set_rounding(bool to_plus_inf) { m_c.set_rounding(to_plus_inf); } ext_numeral_kind lower_kind(interval const & a) const { return m_c.lower_is_inf(a) ? EN_MINUS_INFINITY : EN_NUMERAL; } ext_numeral_kind upper_kind(interval const & a) const { return m_c.upper_is_inf(a) ? EN_PLUS_INFINITY : EN_NUMERAL; } void set_lower(interval & a, numeral const & n) { m_c.set_lower(a, n); } void set_upper(interval & a, numeral const & n) { m_c.set_upper(a, n); } void set_lower_is_open(interval & a, bool v) { m_c.set_lower_is_open(a, v); } void set_upper_is_open(interval & a, bool v) { m_c.set_upper_is_open(a, v); } void set_lower_is_inf(interval & a, bool v) { m_c.set_lower_is_inf(a, v); } void set_upper_is_inf(interval & a, bool v) { m_c.set_upper_is_inf(a, v); } void nth_root_slow(numeral const & a, unsigned n, numeral const & p, numeral & lo, numeral & hi); void A_div_x_n(numeral const & A, numeral const & x, unsigned n, bool to_plus_inf, numeral & r); void rough_approx_nth_root(numeral const & a, unsigned n, numeral & o); void approx_nth_root(numeral const & a, unsigned n, numeral const & p, numeral & o); void nth_root_pos(numeral const & A, unsigned n, numeral const & p, numeral & lo, numeral & hi); void nth_root(numeral const & a, unsigned n, numeral const & p, numeral & lo, numeral & hi); void pi_series(int x, numeral & r, bool to_plus_inf); void fact(unsigned n, numeral & o); void sine_series(numeral const & a, unsigned k, bool upper, numeral & o); void cosine_series(numeral const & a, unsigned k, bool upper, numeral & o); void e_series(unsigned k, bool upper, numeral & o); void div_mul(numeral const & k, interval const & a, interval & b, bool inv_k); void checkpoint(); public: interval_manager(C const & c); ~interval_manager(); void set_cancel(bool f) { m_cancel = f; } numeral_manager & m() const { return m_c.m(); } void del(interval & a); numeral const & lower(interval const & a) const { return m_c.lower(a); } numeral const & upper(interval const & a) const { return m_c.upper(a); } numeral & lower(interval & a) { return m_c.lower(a); } numeral & upper(interval & a) { return m_c.upper(a); } bool lower_is_open(interval const & a) const { return m_c.lower_is_open(a); } bool upper_is_open(interval const & a) const { return m_c.upper_is_open(a); } bool lower_is_inf(interval const & a) const { return m_c.lower_is_inf(a); } bool upper_is_inf(interval const & a) const { return m_c.upper_is_inf(a); } bool lower_is_neg(interval const & a) const { return ::is_neg(m(), lower(a), lower_kind(a)); } bool lower_is_pos(interval const & a) const { return ::is_pos(m(), lower(a), lower_kind(a)); } bool lower_is_zero(interval const & a) const { return ::is_zero(m(), lower(a), lower_kind(a)); } bool upper_is_neg(interval const & a) const { return ::is_neg(m(), upper(a), upper_kind(a)); } bool upper_is_pos(interval const & a) const { return ::is_pos(m(), upper(a), upper_kind(a)); } bool upper_is_zero(interval const & a) const { return ::is_zero(m(), upper(a), upper_kind(a)); } bool is_P(interval const & n) const { return lower_is_pos(n) || lower_is_zero(n); } bool is_P0(interval const & n) const { return lower_is_zero(n) && !lower_is_open(n); } bool is_P1(interval const & n) const { return lower_is_pos(n) || (lower_is_zero(n) && lower_is_open(n)); } bool is_N(interval const & n) const { return upper_is_neg(n) || upper_is_zero(n); } bool is_N0(interval const & n) const { return upper_is_zero(n) && !upper_is_open(n); } bool is_N1(interval const & n) const { return upper_is_neg(n) || (upper_is_zero(n) && upper_is_open(n)); } bool is_M(interval const & n) const { return lower_is_neg(n) && upper_is_pos(n); } bool is_zero(interval const & n) const { return lower_is_zero(n) && upper_is_zero(n); } void set(interval & t, interval const & s); bool eq(interval const & a, interval const & b) const; /** \brief Return true if all values in 'a' are less than all values in 'b'. */ bool before(interval const & a, interval const & b) const; /** \brief Set lower bound to -oo. */ void reset_lower(interval & a); /** \brief Set upper bound to +oo. */ void reset_upper(interval & a); /** \brief Set interval to (-oo, oo) */ void reset(interval & a); /** \brief Return true if the given interval contains 0. */ bool contains_zero(interval const & n) const; /** \brief Return true if n contains v. */ bool contains(interval const & n, numeral const & v) const; void display(std::ostream & out, interval const & n) const; void display_pp(std::ostream & out, interval const & n) const; bool check_invariant(interval const & n) const; /** \brief b <- -a */ void neg(interval const & a, interval & b, interval_deps & b_deps); void neg(interval const & a, interval & b); void neg_jst(interval const & a, interval_deps & b_deps); /** \brief c <- a + b */ void add(interval const & a, interval const & b, interval & c, interval_deps & c_deps); void add(interval const & a, interval const & b, interval & c); void add_jst(interval const & a, interval const & b, interval_deps & c_deps); /** \brief c <- a - b */ void sub(interval const & a, interval const & b, interval & c, interval_deps & c_deps); void sub(interval const & a, interval const & b, interval & c); void sub_jst(interval const & a, interval const & b, interval_deps & c_deps); /** \brief b <- k * a */ void mul(numeral const & k, interval const & a, interval & b, interval_deps & b_deps); void mul(numeral const & k, interval const & a, interval & b) { div_mul(k, a, b, false); } void mul_jst(numeral const & k, interval const & a, interval_deps & b_deps); /** \brief b <- (n/d) * a */ void mul(int n, int d, interval const & a, interval & b); /** \brief b <- a/k \remark For imprecise numerals, this is not equivalent to m().inv(k) mul(k, a, b) That is, we must invert k rounding towards +oo or -oo depending whether we are computing a lower or upper bound. */ void div(interval const & a, numeral const & k, interval & b, interval_deps & b_deps); void div(interval const & a, numeral const & k, interval & b) { div_mul(k, a, b, true); } void div_jst(interval const & a, numeral const & k, interval_deps & b_deps) { mul_jst(k, a, b_deps); } /** \brief c <- a * b */ void mul(interval const & a, interval const & b, interval & c, interval_deps & c_deps); void mul(interval const & a, interval const & b, interval & c); void mul_jst(interval const & a, interval const & b, interval_deps & c_deps); /** \brief b <- a^n */ void power(interval const & a, unsigned n, interval & b, interval_deps & b_deps); void power(interval const & a, unsigned n, interval & b); void power_jst(interval const & a, unsigned n, interval_deps & b_deps); /** \brief b <- a^(1/n) with precision p. \pre if n is even, then a must not contain negative numbers. */ void nth_root(interval const & a, unsigned n, numeral const & p, interval & b, interval_deps & b_deps); void nth_root(interval const & a, unsigned n, numeral const & p, interval & b); void nth_root_jst(interval const & a, unsigned n, numeral const & p, interval_deps & b_deps); /** \brief Given an equation x^n = y and an interval for y, compute the solution set for x with precision p. \pre if n is even, then !lower_is_neg(y) */ void xn_eq_y(interval const & y, unsigned n, numeral const & p, interval & x, interval_deps & x_deps); void xn_eq_y(interval const & y, unsigned n, numeral const & p, interval & x); void xn_eq_y_jst(interval const & y, unsigned n, numeral const & p, interval_deps & x_deps); /** \brief b <- 1/a \pre !contains_zero(a) */ void inv(interval const & a, interval & b, interval_deps & b_deps); void inv(interval const & a, interval & b); void inv_jst(interval const & a, interval_deps & b_deps); /** \brief c <- a/b \pre !contains_zero(b) \pre &a == &c (that is, c should not be an alias for a) */ void div(interval const & a, interval const & b, interval & c, interval_deps & c_deps); void div(interval const & a, interval const & b, interval & c); void div_jst(interval const & a, interval const & b, interval_deps & c_deps); /** \brief Store in r an interval that contains the number pi. The size of the interval is (1/15)*(1/16^n) */ void pi(unsigned n, interval & r); /** \brief Set the precision of the internal interval representing pi. */ void set_pi_prec(unsigned n); /** \brief Set the precision of the internal interval representing pi to a precision of at least n. */ void set_pi_at_least_prec(unsigned n); void sine(numeral const & a, unsigned k, numeral & lo, numeral & hi); void cosine(numeral const & a, unsigned k, numeral & lo, numeral & hi); /** \brief Store in r the Euler's constant e. The size of the interval is 4/(k+1)! */ void e(unsigned k, interval & r); }; template class _scoped_interval { public: typedef typename Manager::interval interval; private: Manager & m_manager; interval m_interval; public: _scoped_interval(Manager & m):m_manager(m) {} ~_scoped_interval() { m_manager.del(m_interval); } Manager & m() const { return m_manager; } operator interval const &() const { return m_interval; } operator interval&() { return m_interval; } interval const & get() const { return m_interval; } interval & get() { return m_interval; } interval * operator->() { return &m_interval; } interval const * operator->() const { return &m_interval; } }; #endif z3-z3-4.4.1/src/math/interval/interval_def.h000066400000000000000000002101651260446376700206110ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: interval_def.h Abstract: Goodies/Templates for interval arithmetic Author: Leonardo de Moura (leonardo) 2012-07-19. Revision History: --*/ #ifndef INTERVAL_DEF_H_ #define INTERVAL_DEF_H_ #include"interval.h" #include"debug.h" #include"trace.h" #include"scoped_numeral.h" #include"cooperate.h" #define DEFAULT_PI_PRECISION 2 // #define TRACE_NTH_ROOT template interval_manager::interval_manager(C const & c):m_c(c) { m().set(m_minus_one, -1); m().set(m_one, 1); m_pi_n = 0; m_cancel = false; } template interval_manager::~interval_manager() { del(m_pi_div_2); del(m_pi); del(m_3_pi_div_2); del(m_2_pi); m().del(m_result_lower); m().del(m_result_upper); m().del(m_mul_ad); m().del(m_mul_bc); m().del(m_mul_ac); m().del(m_mul_bd); m().del(m_minus_one); m().del(m_one); m().del(m_inv_k); } template void interval_manager::del(interval & a) { m().del(lower(a)); m().del(upper(a)); } template void interval_manager::checkpoint() { if (m_cancel) throw default_exception("canceled"); cooperate("interval"); } /* Compute the n-th root of a with precision p. The result hi - lo <= p lo and hi are lower/upper bounds for the value of the n-th root of a. That is, the n-th root is in the interval [lo, hi] If n is even, then a is assumed to be nonnegative. If numeral_manager is not precise, the procedure does not guarantee the precision p. */ template void interval_manager::nth_root_slow(numeral const & a, unsigned n, numeral const & p, numeral & lo, numeral & hi) { #ifdef TRACE_NTH_ROOT static unsigned counter = 0; static unsigned loop_counter = 0; counter++; if (counter % 1000 == 0) std::cerr << "[nth-root] " << counter << " " << loop_counter << " " << ((double)loop_counter)/((double)counter) << std::endl; #endif bool n_is_even = (n % 2 == 0); SASSERT(!n_is_even || m().is_nonneg(a)); if (m().is_zero(a) || m().is_one(a) || (!n_is_even && m().eq(a, m_minus_one))) { m().set(lo, a); m().set(hi, a); return; } if (m().lt(a, m_minus_one)) { m().set(lo, a); m().set(hi, -1); } else if (m().is_neg(a)) { m().set(lo, -1); m().set(hi, 0); } else if (m().lt(a, m_one)) { m().set(lo, 0); m().set(hi, 1); } else { m().set(lo, 1); m().set(hi, a); } SASSERT(m().le(lo, hi)); _scoped_numeral c(m()), cn(m()); _scoped_numeral two(m()); m().set(two, 2); while (true) { checkpoint(); #ifdef TRACE_NTH_ROOT loop_counter++; #endif m().add(hi, lo, c); m().div(c, two, c); if (m().precise()) { m().power(c, n, cn); if (m().gt(cn, a)) { m().set(hi, c); } else if (m().eq(cn, a)) { // root is precise m().set(lo, c); m().set(hi, c); return; } else { m().set(lo, c); } } else { round_to_minus_inf(); m().power(c, n, cn); if (m().gt(cn, a)) { m().set(hi, c); } else { round_to_plus_inf(); m().power(c, n, cn); if (m().lt(cn, a)) { m().set(lo, c); } else { // can't improve, numeral_manager is not precise enough, // a is between round-to-minus-inf(c^n) and round-to-plus-inf(c^n) return; } } } round_to_plus_inf(); m().sub(hi, lo, c); if (m().le(c, p)) return; // result is precise enough } } /** \brief Store in o a rough approximation of a^1/n. It uses 2^Floor[Floor(Log2(a))/n] \pre is_pos(a) */ template void interval_manager::rough_approx_nth_root(numeral const & a, unsigned n, numeral & o) { SASSERT(m().is_pos(a)); SASSERT(n > 0); round_to_minus_inf(); unsigned k = m().prev_power_of_two(a); m().set(o, 2); m().power(o, k/n, o); } /* Compute the n-th root of \c a with (suggested) precision p. The only guarantee provided by this method is that a^(1/n) is in [lo, hi]. If n is even, then a is assumed to be nonnegative. */ template void interval_manager::nth_root(numeral const & a, unsigned n, numeral const & p, numeral & lo, numeral & hi) { // nth_root_slow(a, n, p, lo, hi); // return; SASSERT(n > 0); SASSERT(n % 2 != 0 || m().is_nonneg(a)); if (n == 1 || m().is_zero(a) || m().is_one(a) || m().is_minus_one(a)) { // easy cases: 1, -1, 0 m().set(lo, a); m().set(hi, a); return; } bool is_neg = m().is_neg(a); _scoped_numeral A(m()); m().set(A, a); m().abs(A); nth_root_pos(A, n, p, lo, hi); STRACE("nth_root_trace", tout << "[nth-root] ("; m().display(tout, A); tout << ")^(1/" << n << ") >= "; m().display(tout, lo); tout << "\n"; tout << "[nth-root] ("; m().display(tout, A); tout << ")^(1/" << n << ") <= "; m().display(tout, hi); tout << "\n";); if (is_neg) { m().swap(lo, hi); m().neg(lo); m().neg(hi); } } /** r <- A/(x^n) If to_plus_inf, then r >= A/(x^n) If not to_plus_inf, then r <= A/(x^n) */ template void interval_manager::A_div_x_n(numeral const & A, numeral const & x, unsigned n, bool to_plus_inf, numeral & r) { if (n == 1) { if (m().precise()) { m().div(A, x, r); } else { set_rounding(to_plus_inf); m().div(A, x, r); } } else { if (m().precise()) { m().power(x, n, r); m().div(A, r, r); } else { set_rounding(!to_plus_inf); m().power(x, n, r); set_rounding(to_plus_inf); m().div(A, r, r); } } } /** \brief Compute an approximation of A^(1/n) using the sequence x' = 1/n((n-1)*x + A/(x^(n-1))) The computation stops when the difference between current and new x is less than p. The procedure may not terminate if m() is not precise and p is very small. */ template void interval_manager::approx_nth_root(numeral const & A, unsigned n, numeral const & p, numeral & x) { SASSERT(m().is_pos(A)); SASSERT(n > 1); #ifdef TRACE_NTH_ROOT static unsigned counter = 0; static unsigned loop_counter = 0; counter++; if (counter % 1000 == 0) std::cerr << "[nth-root] " << counter << " " << loop_counter << " " << ((double)loop_counter)/((double)counter) << std::endl; #endif _scoped_numeral x_prime(m()), d(m()); m().set(d, 1); if (m().lt(A, d)) m().set(x, A); else rough_approx_nth_root(A, n, x); round_to_minus_inf(); if (n == 2) { _scoped_numeral two(m()); m().set(two, 2); while (true) { checkpoint(); #ifdef TRACE_NTH_ROOT loop_counter++; #endif m().div(A, x, x_prime); m().add(x, x_prime, x_prime); m().div(x_prime, two, x_prime); m().sub(x_prime, x, d); m().abs(d); m().swap(x, x_prime); if (m().lt(d, p)) return; } } else { _scoped_numeral _n(m()), _n_1(m()); m().set(_n, n); // _n contains n m().set(_n_1, n); m().dec(_n_1); // _n_1 contains n-1 while (true) { checkpoint(); #ifdef TRACE_NTH_ROOT loop_counter++; #endif m().power(x, n-1, x_prime); m().div(A, x_prime, x_prime); m().mul(_n_1, x, d); m().add(d, x_prime, x_prime); m().div(x_prime, _n, x_prime); m().sub(x_prime, x, d); m().abs(d); TRACE("nth_root", tout << "A: "; m().display(tout, A); tout << "\n"; tout << "x: "; m().display(tout, x); tout << "\n"; tout << "x_prime: "; m().display(tout, x_prime); tout << "\n"; tout << "d: "; m().display(tout, d); tout << "\n"; ); m().swap(x, x_prime); if (m().lt(d, p)) return; } } } template void interval_manager::nth_root_pos(numeral const & A, unsigned n, numeral const & p, numeral & lo, numeral & hi) { approx_nth_root(A, n, p, hi); if (m().precise()) { // Assuming hi has a upper bound for A^(n-1) // Then, A/(x^(n-1)) must be lower bound A_div_x_n(A, hi, n-1, false, lo); // Check if we were wrong if (m().lt(hi, lo)) { // swap if wrong m().swap(lo, hi); } } else { // Check if hi is really a upper bound for A^(n-1) A_div_x_n(A, hi, n-1, true /* lo will be greater than the actual lower bound */, lo); TRACE("nth_root_bug", tout << "Assuming upper\n"; tout << "A: "; m().display(tout, A); tout << "\n"; tout << "hi: "; m().display(tout, hi); tout << "\n"; tout << "lo: "; m().display(tout, hi); tout << "\n";); if (m().le(lo, hi)) { // hi is really the upper bound // Must compute lo again but approximating to -oo A_div_x_n(A, hi, n-1, false, lo); } else { // hi should be lower bound m().swap(lo, hi); // check if lo is lower bound A_div_x_n(A, lo, n-1, false /* hi will less than the actual upper bound */, hi); if (m().le(lo, hi)) { // lo is really the lower bound // Must compute hi again but approximating to +oo A_div_x_n(A, lo, n-1, true, hi); } else { // we don't have anything due to rounding errors // Be supper conservative // This should not really happen very often. _scoped_numeral one(m()); if (m().lt(A, one)) { m().set(lo, 0); m().set(hi, 1); } else { m().set(lo, 1); m().set(hi, A); } } } } } /** \brief o <- n! */ template void interval_manager::fact(unsigned n, numeral & o) { _scoped_numeral aux(m()); m().set(o, 1); for (unsigned i = 2; i <= n; i++) { m().set(aux, static_cast(i)); m().mul(aux, o, o); TRACE("fact_bug", tout << "i: " << i << ", o: " << m().to_rational_string(o) << "\n";); } } template void interval_manager::sine_series(numeral const & a, unsigned k, bool upper, numeral & o) { SASSERT(k % 2 == 1); // Compute sine using taylor series up to k // x - x^3/3! + x^5/5! - x^7/7! + ... // The result should be greater than or equal to the actual value if upper == true // Otherwise it must be less than or equal to the actual value. // The argument upper only matter if the numeral_manager is not precise. // Taylor series up to k with rounding to _scoped_numeral f(m()); _scoped_numeral aux(m()); m().set(o, a); bool sign = true; bool upper_factor = !upper; // since the first sign is negative, we must minimize factor to maximize result for (unsigned i = 3; i <= k; i+=2) { TRACE("sine_bug", tout << "[begin-loop] o: " << m().to_rational_string(o) << "\ni: " << i << "\n"; tout << "upper: " << upper << ", upper_factor: " << upper_factor << "\n"; tout << "o (default): " << m().to_string(o) << "\n";); set_rounding(upper_factor); m().power(a, i, f); TRACE("sine_bug", tout << "a^i " << m().to_rational_string(f) << "\n";); set_rounding(!upper_factor); fact(i, aux); TRACE("sine_bug", tout << "i! " << m().to_rational_string(aux) << "\n";); set_rounding(upper_factor); m().div(f, aux, f); TRACE("sine_bug", tout << "a^i/i! " << m().to_rational_string(f) << "\n";); set_rounding(upper); if (sign) m().sub(o, f, o); else m().add(o, f, o); TRACE("sine_bug", tout << "o: " << m().to_rational_string(o) << "\n";); sign = !sign; upper_factor = !upper_factor; } } template void interval_manager::sine(numeral const & a, unsigned k, numeral & lo, numeral & hi) { TRACE("sine", tout << "sine(a), a: " << m().to_rational_string(a) << "\na: " << m().to_string(a) << "\n";); SASSERT(&lo != &hi); if (m().is_zero(a)) { m().reset(lo); m().reset(hi); return; } // Compute sine using taylor series // x - x^3/3! + x^5/5! - x^7/7! + ... // // Note that, the coefficient of even terms is 0. // So, we force k to be odd to make sure the error is minimized. if (k % 2 == 0) k++; // Taylor series error = |x|^(k+1)/(k+1)! _scoped_numeral error(m()); _scoped_numeral aux(m()); round_to_plus_inf(); m().set(error, a); if (m().is_neg(error)) m().neg(error); m().power(error, k+1, error); TRACE("sine", tout << "a^(k+1): " << m().to_rational_string(error) << "\nk : " << k << "\n";); round_to_minus_inf(); fact(k+1, aux); TRACE("sine", tout << "(k+1)!: " << m().to_rational_string(aux) << "\n";); round_to_plus_inf(); m().div(error, aux, error); TRACE("sine", tout << "error: " << m().to_rational_string(error) << "\n";); // Taylor series up to k with rounding to -oo sine_series(a, k, false, lo); if (m().precise()) { m().set(hi, lo); m().sub(lo, error, lo); if (m().lt(lo, m_minus_one)) { m().set(lo, -1); m().set(hi, 1); } else { m().add(hi, error, hi); } } else { // We must recompute the series with rounding to +oo TRACE("sine", tout << "lo before -error: " << m().to_rational_string(lo) << "\n";); round_to_minus_inf(); m().sub(lo, error, lo); TRACE("sine", tout << "lo: " << m().to_rational_string(lo) << "\n";); if (m().lt(lo, m_minus_one)) { m().set(lo, -1); m().set(hi, 1); return; } sine_series(a, k, true, hi); round_to_plus_inf(); m().add(hi, error, hi); TRACE("sine", tout << "hi: " << m().to_rational_string(hi) << "\n";); } } template void interval_manager::cosine_series(numeral const & a, unsigned k, bool upper, numeral & o) { SASSERT(k % 2 == 0); // Compute cosine using taylor series up to k // 1 - x^2/2! + x^4/4! - x^6/6! + ... // The result should be greater than or equal to the actual value if upper == true // Otherwise it must be less than or equal to the actual value. // The argument upper only matter if the numeral_manager is not precise. // Taylor series up to k with rounding to -oo _scoped_numeral f(m()); _scoped_numeral aux(m()); m().set(o, 1); bool sign = true; bool upper_factor = !upper; // since the first sign is negative, we must minimize factor to maximize result for (unsigned i = 2; i <= k; i+=2) { set_rounding(upper_factor); m().power(a, i, f); set_rounding(!upper_factor); fact(i, aux); set_rounding(upper_factor); m().div(f, aux, f); set_rounding(upper); if (sign) m().sub(o, f, o); else m().add(o, f, o); sign = !sign; upper_factor = !upper_factor; } } template void interval_manager::cosine(numeral const & a, unsigned k, numeral & lo, numeral & hi) { TRACE("cosine", tout << "cosine(a): "; m().display_decimal(tout, a, 32); tout << "\n";); SASSERT(&lo != &hi); if (m().is_zero(a)) { m().set(lo, 1); m().set(hi, 1); return; } // Compute cosine using taylor series // 1 - x^2/2! + x^4/4! - x^6/6! + ... // // Note that, the coefficient of odd terms is 0. // So, we force k to be even to make sure the error is minimized. if (k % 2 == 1) k++; // Taylor series error = |x|^(k+1)/(k+1)! _scoped_numeral error(m()); _scoped_numeral aux(m()); round_to_plus_inf(); m().set(error, a); if (m().is_neg(error)) m().neg(error); m().power(error, k+1, error); round_to_minus_inf(); fact(k+1, aux); round_to_plus_inf(); m().div(error, aux, error); TRACE("sine", tout << "error: "; m().display_decimal(tout, error, 32); tout << "\n";); // Taylor series up to k with rounding to -oo cosine_series(a, k, false, lo); if (m().precise()) { m().set(hi, lo); m().sub(lo, error, lo); if (m().lt(lo, m_minus_one)) { m().set(lo, -1); m().set(hi, 1); } else { m().add(hi, error, hi); } } else { // We must recompute the series with rounding to +oo round_to_minus_inf(); m().sub(lo, error, lo); if (m().lt(lo, m_minus_one)) { m().set(lo, -1); m().set(hi, 1); return; } cosine_series(a, k, true, hi); round_to_plus_inf(); m().add(hi, error, hi); } } template void interval_manager::reset_lower(interval & a) { m().reset(lower(a)); set_lower_is_open(a, true); set_lower_is_inf(a, true); } template void interval_manager::reset_upper(interval & a) { m().reset(upper(a)); set_upper_is_open(a, true); set_upper_is_inf(a, true); } template void interval_manager::reset(interval & a) { reset_lower(a); reset_upper(a); } template bool interval_manager::contains_zero(interval const & n) const { return (lower_is_neg(n) || (lower_is_zero(n) && !lower_is_open(n))) && (upper_is_pos(n) || (upper_is_zero(n) && !upper_is_open(n))); } template bool interval_manager::contains(interval const & n, numeral const & v) const { if (!lower_is_inf(n)) { if (m().lt(v, lower(n))) return false; if (m().eq(v, lower(n)) && lower_is_open(n)) return false; } if (!upper_is_inf(n)) { if (m().gt(v, upper(n))) return false; if (m().eq(v, upper(n)) && upper_is_open(n)) return false; } return true; } template void interval_manager::display(std::ostream & out, interval const & n) const { out << (lower_is_open(n) ? "(" : "["); ::display(out, m(), lower(n), lower_kind(n)); out << ", "; ::display(out, m(), upper(n), upper_kind(n)); out << (upper_is_open(n) ? ")" : "]"); } template void interval_manager::display_pp(std::ostream & out, interval const & n) const { out << (lower_is_open(n) ? "(" : "["); ::display_pp(out, m(), lower(n), lower_kind(n)); out << ", "; ::display_pp(out, m(), upper(n), upper_kind(n)); out << (upper_is_open(n) ? ")" : "]"); } template bool interval_manager::check_invariant(interval const & n) const { if (::eq(m(), lower(n), lower_kind(n), upper(n), upper_kind(n))) { SASSERT(!lower_is_open(n)); SASSERT(!upper_is_open(n)); } else { SASSERT(lt(m(), lower(n), lower_kind(n), upper(n), upper_kind(n))); } return true; } template void interval_manager::set(interval & t, interval const & s) { if (&t == &const_cast(s)) return; if (lower_is_inf(s)) { set_lower_is_inf(t, true); } else { m().set(lower(t), lower(s)); set_lower_is_inf(t, false); } if (upper_is_inf(s)) { set_upper_is_inf(t, true); } else { m().set(upper(t), upper(s)); set_upper_is_inf(t, false); } set_lower_is_open(t, lower_is_open(s)); set_upper_is_open(t, upper_is_open(s)); SASSERT(check_invariant(t)); } template bool interval_manager::eq(interval const & a, interval const & b) const { return ::eq(m(), lower(a), lower_kind(a), lower(b), lower_kind(b)) && ::eq(m(), upper(a), upper_kind(a), upper(b), upper_kind(b)) && lower_is_open(a) == lower_is_open(b) && upper_is_open(a) == upper_is_open(b); } template bool interval_manager::before(interval const & a, interval const & b) const { if (upper_is_inf(a) || lower_is_inf(b)) return false; return m().lt(upper(a), lower(b)) || (upper_is_open(a) && m().eq(upper(a), lower(b))); } template void interval_manager::neg_jst(interval const & a, interval_deps & b_deps) { if (lower_is_inf(a)) { if (upper_is_inf(a)) { b_deps.m_lower_deps = 0; b_deps.m_upper_deps = 0; } else { b_deps.m_lower_deps = DEP_IN_UPPER1; b_deps.m_upper_deps = 0; } } else { if (upper_is_inf(a)) { b_deps.m_lower_deps = 0; b_deps.m_upper_deps = DEP_IN_LOWER1; } else { b_deps.m_lower_deps = DEP_IN_UPPER1; b_deps.m_upper_deps = DEP_IN_LOWER1; } } } template void interval_manager::neg(interval const & a, interval & b, interval_deps & b_deps) { neg_jst(a, b_deps); neg(a, b); } template void interval_manager::neg(interval const & a, interval & b) { if (lower_is_inf(a)) { if (upper_is_inf(a)) { reset(b); } else { m().set(lower(b), upper(a)); m().neg(lower(b)); set_lower_is_inf(b, false); set_lower_is_open(b, upper_is_open(a)); m().reset(upper(b)); set_upper_is_inf(b, true); set_upper_is_open(b, true); } } else { if (upper_is_inf(a)) { m().set(upper(b), lower(a)); m().neg(upper(b)); set_upper_is_inf(b, false); set_upper_is_open(b, lower_is_open(a)); m().reset(lower(b)); set_lower_is_inf(b, true); set_lower_is_open(b, true); } else { if (&a == &b) { m().swap(lower(b), upper(b)); } else { m().set(lower(b), upper(a)); m().set(upper(b), lower(a)); } m().neg(lower(b)); m().neg(upper(b)); set_lower_is_inf(b, false); set_upper_is_inf(b, false); bool l_o = lower_is_open(a); bool u_o = upper_is_open(a); set_lower_is_open(b, u_o); set_upper_is_open(b, l_o); } } SASSERT(check_invariant(b)); } template void interval_manager::add_jst(interval const & a, interval const & b, interval_deps & c_deps) { c_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2; c_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2; } template void interval_manager::add(interval const & a, interval const & b, interval & c, interval_deps & c_deps) { add_jst(a, b, c_deps); add(a, b, c); } template void interval_manager::add(interval const & a, interval const & b, interval & c) { ext_numeral_kind new_l_kind, new_u_kind; round_to_minus_inf(); ::add(m(), lower(a), lower_kind(a), lower(b), lower_kind(b), lower(c), new_l_kind); round_to_plus_inf(); ::add(m(), upper(a), upper_kind(a), upper(b), upper_kind(b), upper(c), new_u_kind); set_lower_is_inf(c, new_l_kind == EN_MINUS_INFINITY); set_upper_is_inf(c, new_u_kind == EN_PLUS_INFINITY); set_lower_is_open(c, lower_is_open(a) || lower_is_open(b)); set_upper_is_open(c, upper_is_open(a) || upper_is_open(b)); SASSERT(check_invariant(c)); } template void interval_manager::sub_jst(interval const & a, interval const & b, interval_deps & c_deps) { c_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2; c_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2; } template void interval_manager::sub(interval const & a, interval const & b, interval & c, interval_deps & c_deps) { sub_jst(a, b, c_deps); sub(a, b, c); } template void interval_manager::sub(interval const & a, interval const & b, interval & c) { ext_numeral_kind new_l_kind, new_u_kind; round_to_minus_inf(); ::sub(m(), lower(a), lower_kind(a), upper(b), upper_kind(b), lower(c), new_l_kind); round_to_plus_inf(); ::sub(m(), upper(a), upper_kind(a), lower(b), lower_kind(b), upper(c), new_u_kind); set_lower_is_inf(c, new_l_kind == EN_MINUS_INFINITY); set_upper_is_inf(c, new_u_kind == EN_PLUS_INFINITY); set_lower_is_open(c, lower_is_open(a) || upper_is_open(b)); set_upper_is_open(c, upper_is_open(a) || lower_is_open(b)); SASSERT(check_invariant(c)); } template void interval_manager::mul_jst(numeral const & k, interval const & a, interval_deps & b_deps) { if (m().is_zero(k)) { b_deps.m_lower_deps = 0; b_deps.m_upper_deps = 0; } else if (m().is_neg(k)) { b_deps.m_lower_deps = DEP_IN_UPPER1; b_deps.m_upper_deps = DEP_IN_LOWER1; } else { b_deps.m_lower_deps = DEP_IN_LOWER1; b_deps.m_upper_deps = DEP_IN_UPPER1; } } template void interval_manager::div_mul(numeral const & k, interval const & a, interval & b, bool inv_k) { if (m().is_zero(k)) { reset(b); } else { numeral const & l = lower(a); ext_numeral_kind l_k = lower_kind(a); numeral const & u = upper(a); ext_numeral_kind u_k = upper_kind(a); numeral & new_l_val = m_result_lower; numeral & new_u_val = m_result_upper; ext_numeral_kind new_l_kind, new_u_kind; bool l_o = lower_is_open(a); bool u_o = upper_is_open(a); if (m().is_pos(k)) { set_lower_is_open(b, l_o); set_upper_is_open(b, u_o); if (inv_k) { round_to_minus_inf(); m().inv(k, m_inv_k); ::mul(m(), l, l_k, m_inv_k, EN_NUMERAL, new_l_val, new_l_kind); round_to_plus_inf(); m().inv(k, m_inv_k); ::mul(m(), u, u_k, m_inv_k, EN_NUMERAL, new_u_val, new_u_kind); } else { round_to_minus_inf(); ::mul(m(), l, l_k, k, EN_NUMERAL, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), u, u_k, k, EN_NUMERAL, new_u_val, new_u_kind); } } else { set_lower_is_open(b, u_o); set_upper_is_open(b, l_o); if (inv_k) { round_to_minus_inf(); m().inv(k, m_inv_k); ::mul(m(), u, u_k, m_inv_k, EN_NUMERAL, new_l_val, new_l_kind); round_to_plus_inf(); m().inv(k, m_inv_k); ::mul(m(), l, l_k, m_inv_k, EN_NUMERAL, new_u_val, new_u_kind); } else { round_to_minus_inf(); ::mul(m(), u, u_k, k, EN_NUMERAL, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), l, l_k, k, EN_NUMERAL, new_u_val, new_u_kind); } } m().swap(lower(b), new_l_val); m().swap(upper(b), new_u_val); set_lower_is_inf(b, new_l_kind == EN_MINUS_INFINITY); set_upper_is_inf(b, new_u_kind == EN_PLUS_INFINITY); } } template void interval_manager::mul(numeral const & k, interval const & a, interval & b, interval_deps & b_deps) { mul_jst(k, a, b_deps); mul(k, a, b); } template void interval_manager::mul(int n, int d, interval const & a, interval & b) { _scoped_numeral aux(m()); m().set(aux, n, d); mul(aux, a, b); } template void interval_manager::div(interval const & a, numeral const & k, interval & b, interval_deps & b_deps) { div_jst(a, k, b_deps); div(a, k, b); } template void interval_manager::mul_jst(interval const & i1, interval const & i2, interval_deps & r_deps) { if (is_zero(i1)) { r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; } else if (is_zero(i2)) { r_deps.m_lower_deps = DEP_IN_LOWER2 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER2 | DEP_IN_UPPER2; } else if (is_N(i1)) { if (is_N(i2)) { // x <= b <= 0, y <= d <= 0 --> b*d <= x*y // a <= x <= b <= 0, c <= y <= d <= 0 --> x*y <= a*c (we can use the fact that x or y is always negative (i.e., b is neg or d is neg)) r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER1; // we can replace DEP_IN_UPPER1 with DEP_IN_UPPER2 } else if (is_M(i2)) { // a <= x <= b <= 0, y <= d, d > 0 --> a*d <= x*y (uses the fact that b is not positive) // a <= x <= b <= 0, c <= y, c < 0 --> x*y <= a*c (uses the fact that b is not positive) r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2 | DEP_IN_UPPER1; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER1; } else { // a <= x <= b <= 0, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that x is neg (b is not positive) or y is pos (c is not negative)) // x <= b <= 0, 0 <= c <= y --> x*y <= b*c r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2 | DEP_IN_UPPER1; // we can replace DEP_IN_UPPER1 with DEP_IN_UPPER2 r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2; } } else if (is_M(i1)) { if (is_N(i2)) { // b > 0, x <= b, c <= y <= d <= 0 --> b*c <= x*y (uses the fact that d is not positive) // a < 0, a <= x, c <= y <= d <= 0 --> x*y <= a*c (uses the fact that d is not positive) r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; } else if (is_M(i2)) { r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; } else { // a < 0, a <= x, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that c is not negative) // b > 0, x <= b, 0 <= c <= y <= d --> x*y <= b*d (uses the fact that c is not negative) r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2 | DEP_IN_LOWER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2 | DEP_IN_LOWER2; } } else { SASSERT(is_P(i1)); if (is_N(i2)) { // 0 <= a <= x <= b, c <= y <= d <= 0 --> x*y <= b*c (uses the fact that x is pos (a is not neg) or y is neg (d is not pos)) // 0 <= a <= x, y <= d <= 0 --> a*d <= x*y r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_LOWER1; // we can replace DEP_IN_LOWER1 with DEP_IN_UPPER2 r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2; } else if (is_M(i2)) { // 0 <= a <= x <= b, c <= y --> b*c <= x*y (uses the fact that a is not negative) // 0 <= a <= x <= b, y <= d --> x*y <= b*d (uses the fact that a is not negative) r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_LOWER1; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2 | DEP_IN_LOWER1; } else { SASSERT(is_P(i2)); // 0 <= a <= x, 0 <= c <= y --> a*c <= x*y // x <= b, y <= d --> x*y <= b*d (uses the fact that x is pos (a is not negative) or y is pos (c is not negative)) r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2 | DEP_IN_LOWER1; // we can replace DEP_IN_LOWER1 with DEP_IN_LOWER2 } } } template void interval_manager::mul(interval const & i1, interval const & i2, interval & r, interval_deps & r_deps) { mul_jst(i1, i2, r_deps); mul(i1, i2, r); } template void interval_manager::mul(interval const & i1, interval const & i2, interval & r) { #ifdef _TRACE static unsigned call_id = 0; #endif #if Z3DEBUG || _TRACE bool i1_contains_zero = contains_zero(i1); bool i2_contains_zero = contains_zero(i2); #endif if (is_zero(i1)) { set(r, i1); return; } if (is_zero(i2)) { set(r, i2); return; } numeral const & a = lower(i1); ext_numeral_kind a_k = lower_kind(i1); numeral const & b = upper(i1); ext_numeral_kind b_k = upper_kind(i1); numeral const & c = lower(i2); ext_numeral_kind c_k = lower_kind(i2); numeral const & d = upper(i2); ext_numeral_kind d_k = upper_kind(i2); bool a_o = lower_is_open(i1); bool b_o = upper_is_open(i1); bool c_o = lower_is_open(i2); bool d_o = upper_is_open(i2); numeral & new_l_val = m_result_lower; numeral & new_u_val = m_result_upper; ext_numeral_kind new_l_kind, new_u_kind; if (is_N(i1)) { if (is_N(i2)) { // x <= b <= 0, y <= d <= 0 --> b*d <= x*y // a <= x <= b <= 0, c <= y <= d <= 0 --> x*y <= a*c (we can use the fact that x or y is always negative (i.e., b is neg or d is neg)) TRACE("interval_bug", tout << "(N, N) #" << call_id << "\n"; display(tout, i1); tout << "\n"; display(tout, i2); tout << "\n"; tout << "a: "; m().display(tout, a); tout << "\n"; tout << "b: "; m().display(tout, b); tout << "\n"; tout << "c: "; m().display(tout, c); tout << "\n"; tout << "d: "; m().display(tout, d); tout << "\n"; tout << "is_N0(i1): " << is_N0(i1) << "\n"; tout << "is_N0(i2): " << is_N0(i2) << "\n"; ); set_lower_is_open(r, (is_N0(i1) || is_N0(i2)) ? false : (b_o || d_o)); set_upper_is_open(r, a_o || c_o); // if b = 0 (and the interval is closed), then the lower bound is closed round_to_minus_inf(); ::mul(m(), b, b_k, d, d_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), a, a_k, c, c_k, new_u_val, new_u_kind); } else if (is_M(i2)) { // a <= x <= b <= 0, y <= d, d > 0 --> a*d <= x*y (uses the fact that b is not positive) // a <= x <= b <= 0, c <= y, c < 0 --> x*y <= a*c (uses the fact that b is not positive) TRACE("interval_bug", tout << "(N, M) #" << call_id << "\n";); set_lower_is_open(r, a_o || d_o); set_upper_is_open(r, a_o || c_o); round_to_minus_inf(); ::mul(m(), a, a_k, d, d_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), a, a_k, c, c_k, new_u_val, new_u_kind); } else { // a <= x <= b <= 0, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that x is neg (b is not positive) or y is pos (c is not negative)) // x <= b <= 0, 0 <= c <= y --> x*y <= b*c TRACE("interval_bug", tout << "(N, P) #" << call_id << "\n";); SASSERT(is_P(i2)); // must update upper_is_open first, since value of is_N0(i1) and is_P0(i2) may be affected by update set_upper_is_open(r, (is_N0(i1) || is_P0(i2)) ? false : (b_o || c_o)); set_lower_is_open(r, a_o || d_o); round_to_minus_inf(); ::mul(m(), a, a_k, d, d_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), b, b_k, c, c_k, new_u_val, new_u_kind); } } else if (is_M(i1)) { if (is_N(i2)) { // b > 0, x <= b, c <= y <= d <= 0 --> b*c <= x*y (uses the fact that d is not positive) // a < 0, a <= x, c <= y <= d <= 0 --> x*y <= a*c (uses the fact that d is not positive) TRACE("interval_bug", tout << "(M, N) #" << call_id << "\n";); set_lower_is_open(r, b_o || c_o); set_upper_is_open(r, a_o || c_o); round_to_minus_inf(); ::mul(m(), b, b_k, c, c_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), a, a_k, c, c_k, new_u_val, new_u_kind); } else if (is_M(i2)) { numeral & ad = m_mul_ad; ext_numeral_kind ad_k; numeral & bc = m_mul_bc; ext_numeral_kind bc_k; numeral & ac = m_mul_ac; ext_numeral_kind ac_k; numeral & bd = m_mul_bd; ext_numeral_kind bd_k; bool ad_o = a_o || d_o; bool bc_o = b_o || c_o; bool ac_o = a_o || c_o; bool bd_o = b_o || d_o; round_to_minus_inf(); ::mul(m(), a, a_k, d, d_k, ad, ad_k); ::mul(m(), b, b_k, c, c_k, bc, bc_k); round_to_plus_inf(); ::mul(m(), a, a_k, c, c_k, ac, ac_k); ::mul(m(), b, b_k, d, d_k, bd, bd_k); if (::lt(m(), ad, ad_k, bc, bc_k) || (::eq(m(), ad, ad_k, bc, bc_k) && !ad_o && bc_o)) { m().swap(new_l_val, ad); new_l_kind = ad_k; set_lower_is_open(r, ad_o); } else { m().swap(new_l_val, bc); new_l_kind = bc_k; set_lower_is_open(r, bc_o); } if (::gt(m(), ac, ac_k, bd, bd_k) || (::eq(m(), ac, ac_k, bd, bd_k) && !ac_o && bd_o)) { m().swap(new_u_val, ac); new_u_kind = ac_k; set_upper_is_open(r, ac_o); } else { m().swap(new_u_val, bd); new_u_kind = bd_k; set_upper_is_open(r, bd_o); } } else { // a < 0, a <= x, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that c is not negative) // b > 0, x <= b, 0 <= c <= y <= d --> x*y <= b*d (uses the fact that c is not negative) TRACE("interval_bug", tout << "(M, P) #" << call_id << "\n";); SASSERT(is_P(i2)); set_lower_is_open(r, a_o || d_o); set_upper_is_open(r, b_o || d_o); round_to_minus_inf(); ::mul(m(), a, a_k, d, d_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), b, b_k, d, d_k, new_u_val, new_u_kind); } } else { SASSERT(is_P(i1)); if (is_N(i2)) { // 0 <= a <= x <= b, c <= y <= d <= 0 --> x*y <= b*c (uses the fact that x is pos (a is not neg) or y is neg (d is not pos)) // 0 <= a <= x, y <= d <= 0 --> a*d <= x*y TRACE("interval_bug", tout << "(P, N) #" << call_id << "\n";); // must update upper_is_open first, since value of is_P0(i1) and is_N0(i2) may be affected by update set_upper_is_open(r, (is_P0(i1) || is_N0(i2)) ? false : a_o || d_o); set_lower_is_open(r, b_o || c_o); round_to_minus_inf(); ::mul(m(), b, b_k, c, c_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), a, a_k, d, d_k, new_u_val, new_u_kind); } else if (is_M(i2)) { // 0 <= a <= x <= b, c <= y --> b*c <= x*y (uses the fact that a is not negative) // 0 <= a <= x <= b, y <= d --> x*y <= b*d (uses the fact that a is not negative) TRACE("interval_bug", tout << "(P, M) #" << call_id << "\n";); set_lower_is_open(r, b_o || c_o); set_upper_is_open(r, b_o || d_o); round_to_minus_inf(); ::mul(m(), b, b_k, c, c_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), b, b_k, d, d_k, new_u_val, new_u_kind); } else { SASSERT(is_P(i2)); // 0 <= a <= x, 0 <= c <= y --> a*c <= x*y // x <= b, y <= d --> x*y <= b*d (uses the fact that x is pos (a is not negative) or y is pos (c is not negative)) TRACE("interval_bug", tout << "(P, P) #" << call_id << "\n";); set_lower_is_open(r, (is_P0(i1) || is_P0(i2)) ? false : a_o || c_o); set_upper_is_open(r, b_o || d_o); round_to_minus_inf(); ::mul(m(), a, a_k, c, c_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), b, b_k, d, d_k, new_u_val, new_u_kind); } } m().swap(lower(r), new_l_val); m().swap(upper(r), new_u_val); set_lower_is_inf(r, new_l_kind == EN_MINUS_INFINITY); set_upper_is_inf(r, new_u_kind == EN_PLUS_INFINITY); SASSERT(!(i1_contains_zero || i2_contains_zero) || contains_zero(r)); TRACE("interval_bug", tout << "result: "; display(tout, r); tout << "\n";); #ifdef _TRACE call_id++; #endif } template void interval_manager::power_jst(interval const & a, unsigned n, interval_deps & b_deps) { if (n == 1) { b_deps.m_lower_deps = DEP_IN_LOWER1; b_deps.m_upper_deps = DEP_IN_UPPER1; } else if (n % 2 == 0) { if (lower_is_pos(a)) { // [l, u]^n = [l^n, u^n] if l > 0 // 0 < l <= x --> l^n <= x^n (lower bound guarantees that is positive) // 0 < l <= x <= u --> x^n <= u^n (use lower and upper bound -- need the fact that x is positive) b_deps.m_lower_deps = DEP_IN_LOWER1; if (upper_is_inf(a)) b_deps.m_upper_deps = 0; else b_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; } else if (upper_is_neg(a)) { // [l, u]^n = [u^n, l^n] if u < 0 // l <= x <= u < 0 --> x^n <= l^n (use lower and upper bound -- need the fact that x is negative) // x <= u < 0 --> u^n <= x^n b_deps.m_lower_deps = DEP_IN_UPPER1; if (lower_is_inf(a)) b_deps.m_upper_deps = 0; else b_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; } else { // [l, u]^n = [0, max{l^n, u^n}] otherwise // we need both bounds to justify upper bound b_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; b_deps.m_lower_deps = 0; } } else { // Remark: when n is odd x^n is monotonic. if (lower_is_inf(a)) b_deps.m_lower_deps = 0; else b_deps.m_lower_deps = DEP_IN_LOWER1; if (upper_is_inf(a)) b_deps.m_upper_deps = 0; else b_deps.m_upper_deps = DEP_IN_UPPER1; } } template void interval_manager::power(interval const & a, unsigned n, interval & b, interval_deps & b_deps) { power_jst(a, n, b_deps); power(a, n, b); } template void interval_manager::power(interval const & a, unsigned n, interval & b) { #ifdef _TRACE static unsigned call_id = 0; #endif if (n == 1) { set(b, a); } else if (n % 2 == 0) { if (lower_is_pos(a)) { // [l, u]^n = [l^n, u^n] if l > 0 // 0 < l <= x --> l^n <= x^n (lower bound guarantees that is positive) // 0 < l <= x <= u --> x^n <= u^n (use lower and upper bound -- need the fact that x is positive) SASSERT(!lower_is_inf(a)); round_to_minus_inf(); m().power(lower(a), n, lower(b)); set_lower_is_inf(b, false); set_lower_is_open(b, lower_is_open(a)); if (upper_is_inf(a)) { reset_upper(b); } else { round_to_plus_inf(); m().power(upper(a), n, upper(b)); set_upper_is_inf(b, false); set_upper_is_open(b, upper_is_open(a)); } } else if (upper_is_neg(a)) { // [l, u]^n = [u^n, l^n] if u < 0 // l <= x <= u < 0 --> x^n <= l^n (use lower and upper bound -- need the fact that x is negative) // x <= u < 0 --> u^n <= x^n SASSERT(!upper_is_inf(a)); bool lower_a_open = lower_is_open(a), upper_a_open = upper_is_open(a); bool lower_a_inf = lower_is_inf(a); m().set(lower(b), lower(a)); m().set(upper(b), upper(a)); m().swap(lower(b), upper(b)); // we use a swap because a and b can be aliased round_to_minus_inf(); m().power(lower(b), n, lower(b)); set_lower_is_open(b, upper_a_open); set_lower_is_inf(b, false); if (lower_a_inf) { reset_upper(b); } else { round_to_plus_inf(); m().power(upper(b), n, upper(b)); set_upper_is_inf(b, false); set_upper_is_open(b, lower_a_open); } } else { // [l, u]^n = [0, max{l^n, u^n}] otherwise // we need both bounds to justify upper bound TRACE("interval_bug", tout << "(M) #" << call_id << "\n"; display(tout, a); tout << "\nn:" << n << "\n";); ext_numeral_kind un1_kind = lower_kind(a), un2_kind = upper_kind(a); numeral & un1 = m_result_lower; numeral & un2 = m_result_upper; m().set(un1, lower(a)); m().set(un2, upper(a)); round_to_plus_inf(); ::power(m(), un1, un1_kind, n); ::power(m(), un2, un2_kind, n); if (::gt(m(), un1, un1_kind, un2, un2_kind) || (::eq(m(), un1, un1_kind, un2, un2_kind) && !lower_is_open(a) && upper_is_open(a))) { m().swap(upper(b), un1); set_upper_is_inf(b, un1_kind == EN_PLUS_INFINITY); set_upper_is_open(b, lower_is_open(a)); } else { m().swap(upper(b), un2); set_upper_is_inf(b, un2_kind == EN_PLUS_INFINITY); set_upper_is_open(b, upper_is_open(a)); } m().reset(lower(b)); set_lower_is_inf(b, false); set_lower_is_open(b, false); } } else { // Remark: when n is odd x^n is monotonic. if (lower_is_inf(a)) { reset_lower(b); } else { m().power(lower(a), n, lower(b)); set_lower_is_inf(b, false); set_lower_is_open(b, lower_is_open(a)); } if (upper_is_inf(a)) { reset_upper(b); } else { m().power(upper(a), n, upper(b)); set_upper_is_inf(b, false); set_upper_is_open(b, upper_is_open(a)); } } TRACE("interval_bug", tout << "result: "; display(tout, b); tout << "\n";); #ifdef _TRACE call_id++; #endif } template void interval_manager::nth_root(interval const & a, unsigned n, numeral const & p, interval & b, interval_deps & b_deps) { nth_root_jst(a, n, p, b_deps); nth_root(a, n, p, b); } template void interval_manager::nth_root(interval const & a, unsigned n, numeral const & p, interval & b) { SASSERT(n % 2 != 0 || !lower_is_neg(a)); if (n == 1) { set(b, a); return; } if (lower_is_inf(a)) { SASSERT(n % 2 != 0); // n must not be even. m().reset(lower(b)); set_lower_is_inf(b, true); set_lower_is_open(b, true); } else { numeral & lo = m_result_lower; numeral & hi = m_result_upper; nth_root(lower(a), n, p, lo, hi); set_lower_is_inf(b, false); set_lower_is_open(b, lower_is_open(a) && m().eq(lo, hi)); m().set(lower(b), lo); } if (upper_is_inf(a)) { m().reset(upper(b)); set_upper_is_inf(b, true); set_upper_is_open(b, true); } else { numeral & lo = m_result_lower; numeral & hi = m_result_upper; nth_root(upper(a), n, p, lo, hi); set_upper_is_inf(b, false); set_upper_is_open(b, upper_is_open(a) && m().eq(lo, hi)); m().set(upper(b), hi); } TRACE("interval_nth_root", display(tout, a); tout << " --> "; display(tout, b); tout << "\n";); } template void interval_manager::nth_root_jst(interval const & a, unsigned n, numeral const & p, interval_deps & b_deps) { b_deps.m_lower_deps = DEP_IN_LOWER1; if (n % 2 == 0) b_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; else b_deps.m_upper_deps = DEP_IN_UPPER1; } template void interval_manager::xn_eq_y(interval const & y, unsigned n, numeral const & p, interval & x, interval_deps & x_deps) { xn_eq_y_jst(y, n, p, x_deps); xn_eq_y(y, n, p, x); } template void interval_manager::xn_eq_y(interval const & y, unsigned n, numeral const & p, interval & x) { SASSERT(n % 2 != 0 || !lower_is_neg(y)); if (n % 2 == 0) { SASSERT(!lower_is_inf(y)); if (upper_is_inf(y)) { reset(x); } else { numeral & lo = m_result_lower; numeral & hi = m_result_upper; nth_root(upper(y), n, p, lo, hi); // result is [-hi, hi] // result is open if upper(y) is open and lo == hi TRACE("interval_xn_eq_y", tout << "x^n = "; display(tout, y); tout << "\n"; tout << "sqrt(y) in "; m().display(tout, lo); tout << " "; m().display(tout, hi); tout << "\n";); bool open = upper_is_open(y) && m().eq(lo, hi); set_lower_is_inf(x, false); set_upper_is_inf(x, false); set_lower_is_open(x, open); set_upper_is_open(x, open); m().set(upper(x), hi); round_to_minus_inf(); m().set(lower(x), hi); m().neg(lower(x)); TRACE("interval_xn_eq_y", tout << "interval for x: "; display(tout, x); tout << "\n";); } } else { SASSERT(n % 2 == 1); // n is odd nth_root(y, n, p, x); } } template void interval_manager::xn_eq_y_jst(interval const & y, unsigned n, numeral const & p, interval_deps & x_deps) { if (n % 2 == 0) { x_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; x_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; } else { x_deps.m_lower_deps = DEP_IN_LOWER1; x_deps.m_upper_deps = DEP_IN_UPPER1; } } template void interval_manager::inv_jst(interval const & a, interval_deps & b_deps) { SASSERT(!contains_zero(a)); if (is_P1(a)) { b_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; b_deps.m_upper_deps = DEP_IN_LOWER1; } else if (is_N1(a)) { // x <= u < 0 --> 1/u <= 1/x // l <= x <= u < 0 --> 1/l <= 1/x (use lower and upper bounds) b_deps.m_lower_deps = DEP_IN_UPPER1; b_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; } else { UNREACHABLE(); } } template void interval_manager::inv(interval const & a, interval & b, interval_deps & b_deps) { inv_jst(a, b_deps); inv(a, b); } template void interval_manager::inv(interval const & a, interval & b) { #ifdef _TRACE static unsigned call_id = 0; #endif // If the interval [l,u] does not contain 0, then 1/[l,u] = [1/u, 1/l] SASSERT(!contains_zero(a)); TRACE("interval_bug", tout << "(inv) #" << call_id << "\n"; display(tout, a); tout << "\n";); numeral & new_l_val = m_result_lower; numeral & new_u_val = m_result_upper; ext_numeral_kind new_l_kind, new_u_kind; if (is_P1(a)) { // 0 < l <= x --> 1/x <= 1/l // 0 < l <= x <= u --> 1/u <= 1/x (use lower and upper bounds) round_to_minus_inf(); m().set(new_l_val, upper(a)); new_l_kind = upper_kind(a); ::inv(m(), new_l_val, new_l_kind); SASSERT(new_l_kind == EN_NUMERAL); bool new_l_open = upper_is_open(a); if (lower_is_zero(a)) { SASSERT(lower_is_open(a)); m().reset(upper(b)); set_upper_is_inf(b, true); set_upper_is_open(b, true); } else { round_to_plus_inf(); m().set(new_u_val, lower(a)); m().inv(new_u_val); m().swap(upper(b), new_u_val); set_upper_is_inf(b, false); set_upper_is_open(b, lower_is_open(a)); } m().swap(lower(b), new_l_val); set_lower_is_inf(b, false); set_lower_is_open(b, new_l_open); } else if (is_N1(a)) { // x <= u < 0 --> 1/u <= 1/x // l <= x <= u < 0 --> 1/l <= 1/x (use lower and upper bounds) round_to_plus_inf(); m().set(new_u_val, lower(a)); new_u_kind = lower_kind(a); ::inv(m(), new_u_val, new_u_kind); SASSERT(new_u_kind == EN_NUMERAL); bool new_u_open = lower_is_open(a); if (upper_is_zero(a)) { SASSERT(upper_is_open(a)); m().reset(lower(b)); set_lower_is_open(b, true); set_lower_is_inf(b, true); } else { round_to_minus_inf(); m().set(new_l_val, upper(a)); m().inv(new_l_val); m().swap(lower(b), new_l_val); set_lower_is_inf(b, false); set_lower_is_open(b, upper_is_open(a)); } m().swap(upper(b), new_u_val); set_upper_is_inf(b, false); set_upper_is_open(b, new_u_open); } else { UNREACHABLE(); } TRACE("interval_bug", tout << "result: "; display(tout, b); tout << "\n";); #ifdef _TRACE call_id++; #endif } template void interval_manager::div_jst(interval const & i1, interval const & i2, interval_deps & r_deps) { SASSERT(!contains_zero(i2)); if (is_zero(i1)) { if (is_P1(i2)) { r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2; } else { r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2; } } else { if (is_N(i1)) { if (is_N1(i2)) { // x <= b <= 0, c <= y <= d < 0 --> b/c <= x/y // a <= x <= b <= 0, y <= d < 0 --> x/y <= a/d r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2; } else { // a <= x, a < 0, 0 < c <= y --> a/c <= x/y // x <= b <= 0, 0 < c <= y <= d --> x/y <= b/d r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; } } else if (is_M(i1)) { if (is_N1(i2)) { // 0 < a <= x <= b < 0, y <= d < 0 --> b/d <= x/y // 0 < a <= x <= b < 0, y <= d < 0 --> x/y <= a/d r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2; } else { // 0 < a <= x <= b < 0, 0 < c <= y --> a/c <= x/y // 0 < a <= x <= b < 0, 0 < c <= y --> x/y <= b/c r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2; } } else { SASSERT(is_P(i1)); if (is_N1(i2)) { // b > 0, x <= b, c <= y <= d < 0 --> b/d <= x/y // 0 <= a <= x, c <= y <= d < 0 --> x/y <= a/c r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; } else { SASSERT(is_P1(i2)); // 0 <= a <= x, 0 < c <= y <= d --> a/d <= x/y // b > 0 x <= b, 0 < c <= y --> x/y <= b/c r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2; } } } } template void interval_manager::div(interval const & i1, interval const & i2, interval & r, interval_deps & r_deps) { div_jst(i1, i2, r_deps); div(i1, i2, r); } template void interval_manager::div(interval const & i1, interval const & i2, interval & r) { #ifdef _TRACE static unsigned call_id = 0; #endif SASSERT(!contains_zero(i2)); SASSERT(&i1 != &r); if (is_zero(i1)) { TRACE("interval_bug", tout << "div #" << call_id << "\n"; display(tout, i1); tout << "\n"; display(tout, i2); tout << "\n";); // 0/other = 0 if other != 0 m().reset(lower(r)); m().reset(upper(r)); set_lower_is_inf(r, false); set_upper_is_inf(r, false); set_lower_is_open(r, false); set_upper_is_open(r, false); } else { numeral const & a = lower(i1); ext_numeral_kind a_k = lower_kind(i1); numeral const & b = upper(i1); ext_numeral_kind b_k = upper_kind(i1); numeral const & c = lower(i2); ext_numeral_kind c_k = lower_kind(i2); numeral const & d = upper(i2); ext_numeral_kind d_k = upper_kind(i2); bool a_o = lower_is_open(i1); bool b_o = upper_is_open(i1); bool c_o = lower_is_open(i2); bool d_o = upper_is_open(i2); numeral & new_l_val = m_result_lower; numeral & new_u_val = m_result_upper; ext_numeral_kind new_l_kind, new_u_kind; TRACE("interval_bug", tout << "div #" << call_id << "\n"; display(tout, i1); tout << "\n"; display(tout, i2); tout << "\n"; tout << "a: "; m().display(tout, a); tout << "\n"; tout << "b: "; m().display(tout, b); tout << "\n"; tout << "c: "; m().display(tout, c); tout << "\n"; tout << "d: "; m().display(tout, d); tout << "\n"; ); if (is_N(i1)) { if (is_N1(i2)) { // x <= b <= 0, c <= y <= d < 0 --> b/c <= x/y // a <= x <= b <= 0, y <= d < 0 --> x/y <= a/d TRACE("interval_bug", tout << "(N, N) #" << call_id << "\n";); set_lower_is_open(r, is_N0(i1) ? false : b_o || c_o); set_upper_is_open(r, a_o || d_o); round_to_minus_inf(); ::div(m(), b, b_k, c, c_k, new_l_val, new_l_kind); if (m().is_zero(d)) { SASSERT(d_o); m().reset(new_u_val); new_u_kind = EN_PLUS_INFINITY; } else { round_to_plus_inf(); ::div(m(), a, a_k, d, d_k, new_u_val, new_u_kind); } } else { // a <= x, a < 0, 0 < c <= y --> a/c <= x/y // x <= b <= 0, 0 < c <= y <= d --> x/y <= b/d TRACE("interval_bug", tout << "(N, P) #" << call_id << "\n";); SASSERT(is_P1(i2)); set_upper_is_open(r, is_N0(i1) ? false : (b_o || d_o)); set_lower_is_open(r, a_o || c_o); if (m().is_zero(c)) { SASSERT(c_o); m().reset(new_l_val); new_l_kind = EN_MINUS_INFINITY; } else { round_to_minus_inf(); ::div(m(), a, a_k, c, c_k, new_l_val, new_l_kind); } round_to_plus_inf(); ::div(m(), b, b_k, d, d_k, new_u_val, new_u_kind); } } else if (is_M(i1)) { if (is_N1(i2)) { // 0 < a <= x <= b < 0, y <= d < 0 --> b/d <= x/y // 0 < a <= x <= b < 0, y <= d < 0 --> x/y <= a/d TRACE("interval_bug", tout << "(M, N) #" << call_id << "\n";); set_lower_is_open(r, b_o || d_o); set_upper_is_open(r, a_o || d_o); if (m().is_zero(d)) { SASSERT(d_o); m().reset(new_l_val); m().reset(new_u_val); new_l_kind = EN_MINUS_INFINITY; new_u_kind = EN_PLUS_INFINITY; } else { round_to_minus_inf(); ::div(m(), b, b_k, d, d_k, new_l_val, new_l_kind); round_to_plus_inf(); ::div(m(), a, a_k, d, d_k, new_u_val, new_u_kind); TRACE("interval_bug", tout << "new_l_kind: " << new_l_kind << ", new_u_kind: " << new_u_kind << "\n";); } } else { // 0 < a <= x <= b < 0, 0 < c <= y --> a/c <= x/y // 0 < a <= x <= b < 0, 0 < c <= y --> x/y <= b/c TRACE("interval_bug", tout << "(M, P) #" << call_id << "\n";); SASSERT(is_P1(i2)); set_lower_is_open(r, a_o || c_o); set_upper_is_open(r, b_o || c_o); if (m().is_zero(c)) { SASSERT(c_o); m().reset(new_l_val); m().reset(new_u_val); new_l_kind = EN_MINUS_INFINITY; new_u_kind = EN_PLUS_INFINITY; } else { round_to_minus_inf(); ::div(m(), a, a_k, c, c_k, new_l_val, new_l_kind); round_to_plus_inf(); ::div(m(), b, b_k, c, c_k, new_u_val, new_u_kind); } } } else { SASSERT(is_P(i1)); if (is_N1(i2)) { // b > 0, x <= b, c <= y <= d < 0 --> b/d <= x/y // 0 <= a <= x, c <= y <= d < 0 --> x/y <= a/c TRACE("interval_bug", tout << "(P, N) #" << call_id << "\n";); set_upper_is_open(r, is_P0(i1) ? false : a_o || c_o); set_lower_is_open(r, b_o || d_o); if (m().is_zero(d)) { SASSERT(d_o); m().reset(new_l_val); new_l_kind = EN_MINUS_INFINITY; } else { round_to_minus_inf(); ::div(m(), b, b_k, d, d_k, new_l_val, new_l_kind); } round_to_plus_inf(); ::div(m(), a, a_k, c, c_k, new_u_val, new_u_kind); } else { SASSERT(is_P1(i2)); // 0 <= a <= x, 0 < c <= y <= d --> a/d <= x/y // b > 0 x <= b, 0 < c <= y --> x/y <= b/c TRACE("interval_bug", tout << "(P, P) #" << call_id << "\n";); set_lower_is_open(r, is_P0(i1) ? false : a_o || d_o); set_upper_is_open(r, b_o || c_o); round_to_minus_inf(); ::div(m(), a, a_k, d, d_k, new_l_val, new_l_kind); if (m().is_zero(c)) { SASSERT(c_o); m().reset(new_u_val); new_u_kind = EN_PLUS_INFINITY; } else { round_to_plus_inf(); ::div(m(), b, b_k, c, c_k, new_u_val, new_u_kind); } } } m().swap(lower(r), new_l_val); m().swap(upper(r), new_u_val); set_lower_is_inf(r, new_l_kind == EN_MINUS_INFINITY); set_upper_is_inf(r, new_u_kind == EN_PLUS_INFINITY); } TRACE("interval_bug", tout << "result: "; display(tout, r); tout << "\n";); #ifdef _TRACE call_id++; #endif } template void interval_manager::pi_series(int x, numeral & r, bool up) { // Store in r the value: 1/16^x (4/(8x + 1) - 2/(8x + 4) - 1/(8x + 5) - 1/(8x + 6)) _scoped_numeral f(m()); set_rounding(up); m().set(r, 4, 8*x + 1); set_rounding(!up); m().set(f, 2, 8*x + 4); set_rounding(up); m().sub(r, f, r); set_rounding(!up); m().set(f, 1, 8*x + 5); set_rounding(up); m().sub(r, f, r); set_rounding(!up); m().set(f, 1, 8*x + 6); set_rounding(up); m().sub(r, f, r); m().set(f, 1, 16); m().power(f, x, f); m().mul(r, f, r); } template void interval_manager::pi(unsigned n, interval & r) { // Compute an interval that contains pi using the series // P[0] + P[1] + ... + P[n] // where // P[n] := 1/16^x (4/(8x + 1) - 2/(8x + 4) - 1/(8x + 5) - 1/(8x + 6)) // // The size of the interval is 1/15 * 1/(16^n) // // Lower is P[0] + P[1] + ... + P[n] // Upper is Lower + 1/15 * 1/(16^n) // compute size of the resulting interval round_to_plus_inf(); // overestimate size of the interval _scoped_numeral len(m()); _scoped_numeral p(m()); m().set(len, 1, 16); m().power(len, n, len); m().set(p, 1, 15); m().mul(p, len, len); // compute lower bound numeral & l_val = m_result_lower; m().reset(l_val); for (unsigned i = 0; i <= n; i++) { pi_series(i, p, false); round_to_minus_inf(); m().add(l_val, p, l_val); } // computer upper bound numeral & u_val = m_result_upper; if (m().precise()) { // the numeral manager is precise, so we do not need to recompute the series m().add(l_val, len, u_val); } else { // recompute the sum rounding to plus infinite m().reset(u_val); for (unsigned i = 0; i <= n; i++) { pi_series(i, p, true); round_to_plus_inf(); m().add(u_val, p, u_val); } round_to_plus_inf(); m().add(u_val, len, u_val); } set_lower_is_open(r, false); set_upper_is_open(r, false); set_lower_is_inf(r, false); set_upper_is_inf(r, false); m().set(lower(r), l_val); m().set(upper(r), u_val); } template void interval_manager::set_pi_prec(unsigned n) { SASSERT(n > 0); m_pi_n = n; pi(n, m_pi); mul(1, 2, m_pi, m_pi_div_2); mul(3, 2, m_pi, m_3_pi_div_2); mul(2, 1, m_pi, m_2_pi); } template void interval_manager::set_pi_at_least_prec(unsigned n) { if (n > m_pi_n) set_pi_prec(n); } template void interval_manager::e_series(unsigned k, bool upper, numeral & o) { _scoped_numeral d(m()), a(m()); m().set(o, 2); m().set(d, 1); for (unsigned i = 2; i <= k; i++) { set_rounding(!upper); m().set(a, static_cast(i)); m().mul(d, a, d); // d == i! m().set(a, d); set_rounding(upper); m().inv(a); // a == 1/i! m().add(o, a, o); } } template void interval_manager::e(unsigned k, interval & r) { // Store in r lower and upper bounds for Euler's constant. // // The procedure uses the series // // V = 1 + 1/1 + 1/2! + 1/3! + ... + 1/k! // // The error in the approximation above is <= E = 4/(k+1)! // Thus, e must be in the interval [V, V+E] numeral & lo = m_result_lower; numeral & hi = m_result_upper; e_series(k, false, lo); _scoped_numeral error(m()), aux(m()); round_to_minus_inf(); fact(k+1, error); round_to_plus_inf(); m().inv(error); // error == 1/(k+1)! m().set(aux, 4); m().mul(aux, error, error); // error == 4/(k+1)! if (m().precise()) { m().set(hi, lo); m().add(hi, error, hi); } else { e_series(k, true, hi); round_to_plus_inf(); m().add(hi, error, hi); } set_lower_is_open(r, false); set_upper_is_open(r, false); set_lower_is_inf(r, false); set_upper_is_inf(r, false); m().set(lower(r), lo); m().set(upper(r), hi); } #endif z3-z3-4.4.1/src/math/interval/interval_mpq.cpp000066400000000000000000000004451260446376700212010ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: interval_mpq.cpp Abstract: Instantiate template using defaults. Author: Leonardo de Moura (leonardo) 2012-10-31. Revision History: --*/ #include"interval_def.h" template class interval_manager; z3-z3-4.4.1/src/math/polynomial/000077500000000000000000000000001260446376700163305ustar00rootroot00000000000000z3-z3-4.4.1/src/math/polynomial/README000066400000000000000000000003531260446376700172110ustar00rootroot00000000000000Polynomial manipulation package. It contains support for univariate (upolynomial.*) and multivariate polynomials (polynomial.*). Multivariate polynomial factorization does not work yet (polynomial_factorization.*), and it is disabled. z3-z3-4.4.1/src/math/polynomial/algebraic.pyg000066400000000000000000000042061260446376700207640ustar00rootroot00000000000000def_module_params('algebraic', description='real algebraic number package', export=True, params=(('zero_accuracy', UINT, 0, 'one of the most time-consuming operations in the real algebraic number module is determining the sign of a polynomial evaluated at a sample point with non-rational algebraic number values. Let k be the value of this option. If k is 0, Z3 uses precise computation. Otherwise, the result of a polynomial evaluation is considered to be 0 if Z3 can show it is inside the interval (-1/2^k, 1/2^k)'), ('min_mag', UINT, 16, 'Z3 represents algebraic numbers using a (square-free) polynomial p and an isolating interval (which contains one and only one root of p). This interval may be refined during the computations. This parameter specifies whether to cache the value of a refined interval or not. It says the minimal size of an interval for caching purposes is 1/2^16'), ('factor', BOOL, True, 'use polynomial factorization to simplify polynomials representing algebraic numbers'), ('factor_max_prime', UINT, 31, 'parameter for the polynomial factorization procedure in the algebraic number module. Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter limits the maximum prime number p to be used in the first step'), ('factor_num_primes', UINT, 1, 'parameter for the polynomial factorization procedure in the algebraic number module. Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. The search space may be reduced by factoring the polynomial in different GF(p)\'s. This parameter specify the maximum number of finite factorizations to be considered, before lifiting and searching'), ('factor_search_size', UINT, 5000, 'parameter for the polynomial factorization procedure in the algebraic number module. Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter can be used to limit the search space'))) z3-z3-4.4.1/src/math/polynomial/algebraic_numbers.cpp000066400000000000000000003617731260446376700225210ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: algebraic_numbers.cpp Abstract: Real Algebraic Numbers Author: Leonardo (leonardo) 2011-11-22 Notes: --*/ #include"algebraic_numbers.h" #include"upolynomial.h" #include"mpbq.h" #include"basic_interval.h" #include"cooperate.h" #include"sexpr2upolynomial.h" #include"scoped_ptr_vector.h" #include"mpbqi.h" #include"timeit.h" #include"algebraic_params.hpp" namespace algebraic_numbers { struct basic_cell { mpq m_value; }; // Each algebraic number is associated with two // isolating (refinable) intervals. The second // interval just caches refinements of the first one. struct algebraic_cell { // polynomial unsigned m_p_sz; mpz * m_p; mpbqi m_interval; // isolating/refinable interval // sign of p at the lower and upper bounds of m_interval unsigned m_minimal:1; // true if p is a minimal polynomial for representing the number unsigned m_sign_lower:1; unsigned m_not_rational:1; // if true we know for sure it is not a rational unsigned m_i:29; // number is the i-th root of p, 0 if it is not known which root of p the number is. algebraic_cell():m_p_sz(0), m_p(0), m_minimal(false), m_not_rational(false), m_i(0) {} bool is_minimal() const { return m_minimal != 0; } }; typedef polynomial::manager poly_manager; typedef upolynomial::manager upoly_manager; typedef upolynomial::numeral_vector upoly; typedef upolynomial::scoped_numeral_vector scoped_upoly; typedef upolynomial::factors factors; void manager::get_param_descrs(param_descrs & r) { algebraic_params::collect_param_descrs(r); } struct manager::imp { manager & m_wrapper; small_object_allocator & m_allocator; unsynch_mpq_manager & m_qmanager; mpbq_manager m_bqmanager; mpbqi_manager m_bqimanager; poly_manager m_pmanager; upoly_manager m_upmanager; mpq m_zero; scoped_mpz m_is_rational_tmp; scoped_upoly m_isolate_tmp1; scoped_upoly m_isolate_tmp2; scoped_upoly m_isolate_tmp3; scoped_upoly m_eval_sign_tmp; factors m_isolate_factors; scoped_mpbq_vector m_isolate_roots; scoped_mpbq_vector m_isolate_lowers; scoped_mpbq_vector m_isolate_uppers; scoped_upoly m_add_tmp; polynomial::var m_x; polynomial::var m_y; volatile bool m_cancel; // configuration int m_min_magnitude; bool m_factor; polynomial::factor_params m_factor_params; int m_zero_accuracy; // statistics unsigned m_compare_cheap; unsigned m_compare_sturm; unsigned m_compare_refine; unsigned m_compare_poly_eq; imp(manager & w, unsynch_mpq_manager & m, params_ref const & p, small_object_allocator & a): m_wrapper(w), m_allocator(a), m_qmanager(m), m_bqmanager(m), m_bqimanager(m_bqmanager), m_pmanager(m, &a), m_upmanager(m), m_is_rational_tmp(m), m_isolate_tmp1(upm()), m_isolate_tmp2(upm()), m_isolate_tmp3(upm()), m_eval_sign_tmp(upm()), m_isolate_factors(upm()), m_isolate_roots(bqm()), m_isolate_lowers(bqm()), m_isolate_uppers(bqm()), m_add_tmp(upm()) { updt_params(p); reset_statistics(); m_cancel = false; m_x = pm().mk_var(); m_y = pm().mk_var(); } ~imp() { } void set_cancel(bool f) { m_cancel = f; pm().set_cancel(f); upm().set_cancel(f); } void checkpoint() { if (m_cancel) throw algebraic_exception("canceled"); cooperate("algebraic"); } void reset_statistics() { m_compare_cheap = 0; m_compare_sturm = 0; m_compare_refine = 0; m_compare_poly_eq = 0; } void collect_statistics(statistics & st) { #ifndef _EXTERNAL_RELEASE st.update("algebraic compare cheap", m_compare_cheap); st.update("algebraic compare sturm", m_compare_sturm); st.update("algebraic compare refine", m_compare_refine); st.update("algebraic compare poly", m_compare_poly_eq); #endif } void updt_params(params_ref const & _p) { algebraic_params p(_p); m_min_magnitude = -static_cast(p.min_mag()); m_factor = p.factor(); m_factor_params.m_max_p = p.factor_max_prime(); m_factor_params.m_p_trials = p.factor_num_primes(); m_factor_params.m_max_search_size = p.factor_search_size(); m_zero_accuracy = -static_cast(p.zero_accuracy()); } unsynch_mpq_manager & qm() { return m_qmanager; } mpbq_manager & bqm() { return m_bqmanager; } mpbqi_manager & bqim() { return m_bqimanager; } poly_manager & pm() { return m_pmanager; } upoly_manager & upm() { return m_upmanager; } void del(basic_cell * c) { qm().del(c->m_value); m_allocator.deallocate(sizeof(basic_cell), c); } void del_poly(algebraic_cell * c) { for (unsigned i = 0; i < c->m_p_sz; i++) qm().del(c->m_p[i]); m_allocator.deallocate(sizeof(mpz)*c->m_p_sz, c->m_p); c->m_p = 0; c->m_p_sz = 0; } void del_interval(algebraic_cell * c) { bqim().del(c->m_interval); } void del(algebraic_cell * c) { del_poly(c); del_interval(c); m_allocator.deallocate(sizeof(algebraic_cell), c); } void del(numeral & a) { if (a.m_cell == 0) return; if (a.is_basic()) del(a.to_basic()); else del(a.to_algebraic()); a.m_cell = 0; } void reset(numeral & a) { del(a); } bool is_zero(numeral const & a) { return a.m_cell == 0; } bool is_pos(numeral const & a) { if (a.is_basic()) return qm().is_pos(basic_value(a)); else return bqim().is_pos(a.to_algebraic()->m_interval); } bool is_neg(numeral const & a) { if (a.is_basic()) return qm().is_neg(basic_value(a)); else return bqim().is_neg(a.to_algebraic()->m_interval); } mpq const & basic_value(numeral const & a) { SASSERT(a.is_basic()); if (is_zero(a)) return m_zero; else return a.to_basic()->m_value; } bool is_int(numeral & a) { if (a.is_basic()) return qm().is_int(basic_value(a)); if (a.to_algebraic()->m_not_rational) return false; // we know for sure a is not a rational (and consequently an integer) // make sure the isolating interval has at most one integer if (!refine_until_prec(a, 1)) { SASSERT(a.is_basic()); // a became basic return qm().is_int(basic_value(a)); } // Find unique integer in the isolating interval algebraic_cell * c = a.to_algebraic(); scoped_mpz candidate(qm()); bqm().floor(qm(), upper(c), candidate); SASSERT(bqm().ge(upper(c), candidate)); if (bqm().lt(lower(c), candidate) && upm().eval_sign_at(c->m_p_sz, c->m_p, candidate) == 0) { m_wrapper.set(a, candidate); return true; } return false; } /* In our representation, non-basic numbers are encoded by polynomials of the form: a_n * x^n + ... + a_0 where a_0 != 0. Thus, we can find whether a non-basic number is actually a rational by using the Rational root theorem. p/q is a root of a_n * x^n + ... + a_0 If p is a factor of a_0, and q is a factor of a_n. If the isolating interval (lower, upper) has size less than 1/a_n, then (a_n*lower, a_n*upper) contains at most one integer. Let u be this integer, then the non-basic number is a rational iff u/a_n is the actual root. */ bool is_rational(numeral & a) { if (a.is_basic()) return true; if (a.to_algebraic()->m_not_rational) return false; // we know for sure a is not a rational TRACE("algebraic_bug", tout << "is_rational(a):\n"; display_root(tout, a); tout << "\n"; display_interval(tout, a); tout << "\n";); algebraic_cell * c = a.to_algebraic(); save_intervals saved_a(*this, c); mpz & a_n = c->m_p[c->m_p_sz - 1]; scoped_mpz & abs_a_n = m_is_rational_tmp; qm().set(abs_a_n, a_n); qm().abs(abs_a_n); // 1/2^{log2(a_n)+1} <= 1/a_n unsigned k = qm().log2(abs_a_n); k++; TRACE("algebraic_bug", tout << "abs(an): " << qm().to_string(abs_a_n) << ", k: " << k << "\n";); // make sure the isolating interval size is less than 1/2^k if (!refine_until_prec(a, k)) { SASSERT(a.is_basic()); // a became basic return true; } TRACE("algebraic_bug", tout << "interval after refinement: "; display_interval(tout, a); tout << "\n";); // Find unique candidate rational in the isolating interval scoped_mpbq a_n_lower(bqm()); scoped_mpbq a_n_upper(bqm()); bqm().mul(lower(c), abs_a_n, a_n_lower); bqm().mul(upper(c), abs_a_n, a_n_upper); scoped_mpz zcandidate(qm()); bqm().floor(qm(), a_n_upper, zcandidate); scoped_mpq candidate(qm()); qm().set(candidate, zcandidate, abs_a_n); SASSERT(bqm().ge(upper(c), candidate)); // Find if candidate is an actual root if (bqm().lt(lower(c), candidate) && upm().eval_sign_at(c->m_p_sz, c->m_p, candidate) == 0) { saved_a.restore_if_too_small(); set(a, candidate); return true; } else { saved_a.restore_if_too_small(); c->m_not_rational = true; return false; } } void to_rational(numeral & a, mpq & r) { VERIFY(is_rational(a)); SASSERT(a.is_basic()); qm().set(r, basic_value(a)); } void to_rational(numeral & a, rational & r) { scoped_mpq tmp(qm()); to_rational(a, tmp); rational tmp2(tmp); r = tmp2; } unsigned degree(numeral const & a) { if (is_zero(a)) return 0; if (a.is_basic()) return 1; return a.to_algebraic()->m_p_sz - 1; } void swap(numeral & a, numeral & b) { std::swap(a.m_cell, b.m_cell); } basic_cell * mk_basic_cell(mpq & n) { if (qm().is_zero(n)) return 0; void * mem = static_cast(m_allocator.allocate(sizeof(basic_cell))); basic_cell * c = new (mem) basic_cell(); qm().swap(c->m_value, n); return c; } int sign_lower(algebraic_cell * c) { return c->m_sign_lower == 0 ? 1 : -1; } mpbq & lower(algebraic_cell * c) { return c->m_interval.lower(); } mpbq & upper(algebraic_cell * c) { return c->m_interval.upper(); } void update_sign_lower(algebraic_cell * c) { int sl = upm().eval_sign_at(c->m_p_sz, c->m_p, lower(c)); // The isolating intervals are refinable. Thus, the polynomial has opposite signs at lower and upper. SASSERT(sl != 0); SASSERT(upm().eval_sign_at(c->m_p_sz, c->m_p, upper(c)) == -sl); c->m_sign_lower = sl < 0; } // Make sure the GCD of the coefficients is one and the leading coefficient is positive void normalize_coeffs(algebraic_cell * c) { SASSERT(c->m_p_sz > 2); upm().normalize(c->m_p_sz, c->m_p); if (upm().m().is_neg(c->m_p[c->m_p_sz-1])) { upm().neg(c->m_p_sz, c->m_p); c->m_sign_lower = !(c->m_sign_lower); } } algebraic_cell * mk_algebraic_cell(unsigned sz, mpz const * p, mpbq const & lower, mpbq const & upper, bool minimal) { SASSERT(sz > 2); void * mem = static_cast(m_allocator.allocate(sizeof(algebraic_cell))); algebraic_cell * c = new (mem) algebraic_cell(); c->m_p_sz = sz; c->m_p = static_cast(m_allocator.allocate(sizeof(mpz)*sz)); for (unsigned i = 0; i < sz; i++) { new (c->m_p + i) mpz(); qm().set(c->m_p[i], p[i]); } bqim().set(c->m_interval, lower, upper); update_sign_lower(c); c->m_minimal = minimal; SASSERT(c->m_i == 0); SASSERT(c->m_not_rational == false); if (c->m_minimal) c->m_not_rational = true; normalize_coeffs(c); return c; } void set(numeral & a, mpq & n) { if (qm().is_zero(n)) { reset(a); SASSERT(is_zero(a)); return; } if (a.is_basic()) { if (is_zero(a)) a.m_cell = mk_basic_cell(n); else qm().set(a.to_basic()->m_value, n); } else { del(a); a.m_cell = mk_basic_cell(n); } } void set(numeral & a, mpq const & n) { scoped_mpq tmp(qm()); qm().set(tmp, n); set(a, tmp); } void copy_poly(algebraic_cell * c, unsigned sz, mpz const * p) { SASSERT(c->m_p == 0); SASSERT(c->m_p_sz == 0); c->m_p_sz = sz; c->m_p = static_cast(m_allocator.allocate(sizeof(mpz)*sz)); for (unsigned i = 0; i < sz; i++) { new (c->m_p + i) mpz(); qm().set(c->m_p[i], p[i]); } } void set_interval(algebraic_cell * c, mpbqi const & i) { bqim().set(c->m_interval, i); } void set_interval(algebraic_cell * c, mpbq const & l, mpbq const & u) { bqim().set(c->m_interval, l, u); } // Copy fields from source to target. // It assumes that fields target->m_p is NULL or was deleted. void copy(algebraic_cell * target, algebraic_cell const * source) { copy_poly(target, source->m_p_sz, source->m_p); set_interval(target, source->m_interval); target->m_minimal = source->m_minimal; target->m_sign_lower = source->m_sign_lower; target->m_not_rational = source->m_not_rational; target->m_i = source->m_i; } void set(numeral & a, unsigned sz, mpz const * p, mpbq const & lower, mpbq const & upper, bool minimal) { SASSERT(sz > 1); if (sz == 2) { // it is linear scoped_mpq tmp(qm()); qm().set(tmp, p[0], p[1]); qm().neg(tmp); set(a, tmp); } else { if (a.is_basic()) { del(a); a.m_cell = TAG(void*, mk_algebraic_cell(sz, p, lower, upper, minimal), ROOT); } else { SASSERT(sz > 2); algebraic_cell * c = a.to_algebraic(); del_poly(c); copy_poly(c, sz, p); set_interval(c, lower, upper); c->m_minimal = minimal; c->m_not_rational = false; if (c->m_minimal) c->m_not_rational = true; c->m_i = 0; update_sign_lower(c); normalize_coeffs(c); } SASSERT(sign_lower(a.to_algebraic()) == upm().eval_sign_at(a.to_algebraic()->m_p_sz, a.to_algebraic()->m_p, a.to_algebraic()->m_interval.lower())); } TRACE("algebraic", tout << "a: "; display_root(tout, a); tout << "\n";); } void set(numeral & a, numeral const & b) { if (&a == &b) return; if (a.is_basic()) { if (b.is_basic()) { SASSERT(a.is_basic() && b.is_basic()); set(a, basic_value(b)); } else { SASSERT(a.is_basic() && !b.is_basic()); del(a); void * mem = m_allocator.allocate(sizeof(algebraic_cell)); algebraic_cell * c = new (mem) algebraic_cell(); a.m_cell = TAG(void *, c, ROOT); copy(c, b.to_algebraic()); } } else { if (b.is_basic()) { SASSERT(!a.is_basic() && b.is_basic()); del(a); set(a, basic_value(b)); } else { SASSERT(!a.is_basic() && !b.is_basic()); del_poly(a.to_algebraic()); del_interval(a.to_algebraic()); copy(a.to_algebraic(), b.to_algebraic()); } } } bool factor(scoped_upoly const & up, factors & r) { // std::cout << "factor: "; upm().display(std::cout, up); std::cout << std::endl; if (m_factor) { return upm().factor(up, r, m_factor_params); } else { scoped_upoly & up_sqf = m_isolate_tmp3; up_sqf.reset(); upm().square_free(up.size(), up.c_ptr(), up_sqf); TRACE("anum_bug", upm().display(tout, up_sqf.size(), up_sqf.c_ptr()); tout << "\n";); r.push_back(up_sqf, 1); return false; } } struct lt_proc { manager & m; lt_proc(manager & _m):m(_m) {} bool operator()(numeral const & a1, numeral const & a2) const { return m.lt(a1, a2); } }; void sort_roots(numeral_vector & r) { std::sort(r.begin(), r.end(), lt_proc(m_wrapper)); } void isolate_roots(scoped_upoly const & up, numeral_vector & roots) { if (up.empty()) return; // ignore the zero polynomial factors & fs = m_isolate_factors; fs.reset(); bool full_fact; if (upm().has_zero_roots(up.size(), up.c_ptr())) { roots.push_back(numeral()); scoped_upoly & nz_up = m_isolate_tmp2; upm().remove_zero_roots(up.size(), up.c_ptr(), nz_up); full_fact = factor(nz_up, fs); } else { full_fact = factor(up, fs); } unsigned num_factors = fs.distinct_factors(); for (unsigned i = 0; i < num_factors; i++) { upolynomial::numeral_vector const & f = fs[i]; // polynomial f contains the non zero roots unsigned d = upm().degree(f); if (d == 0) continue; // found all roots of f scoped_mpq r(qm()); if (d == 1) { TRACE("algebraic", tout << "linear polynomial...\n";); // f is a linear polynomial ax + b // set r <- -b/a qm().set(r, f[0]); qm().div(r, f[1], r); qm().neg(r); roots.push_back(numeral(mk_basic_cell(r))); continue; } SASSERT(m_isolate_roots.empty() && m_isolate_lowers.empty() && m_isolate_uppers.empty()); upm().sqf_isolate_roots(f.size(), f.c_ptr(), bqm(), m_isolate_roots, m_isolate_lowers, m_isolate_uppers); // collect rational/basic roots unsigned sz = m_isolate_roots.size(); for (unsigned i = 0; i < sz; i++) { to_mpq(qm(), m_isolate_roots[i], r); roots.push_back(numeral(mk_basic_cell(r))); } SASSERT(m_isolate_uppers.size() == m_isolate_lowers.size()); // collect non-basic roots sz = m_isolate_lowers.size(); for (unsigned i = 0; i < sz; i++) { mpbq & lower = m_isolate_lowers[i]; mpbq & upper = m_isolate_uppers[i]; if (!upm().isolating2refinable(f.size(), f.c_ptr(), bqm(), lower, upper)) { // found rational root... it is stored in lower to_mpq(qm(), lower, r); roots.push_back(numeral(mk_basic_cell(r))); } else { algebraic_cell * c = mk_algebraic_cell(f.size(), f.c_ptr(), lower, upper, full_fact); roots.push_back(numeral(c)); } } m_isolate_roots.reset(); m_isolate_lowers.reset(); m_isolate_uppers.reset(); } sort_roots(roots); } void isolate_roots(polynomial_ref const & p, numeral_vector & roots) { SASSERT(is_univariate(p)); TRACE("algebraic", tout << "isolating roots of: " << p << "\n";); if (::is_zero(p)) return; // ignore the zero polynomial scoped_upoly & up = m_isolate_tmp1; upm().to_numeral_vector(p, up); isolate_roots(up, roots); } void mk_root(scoped_upoly const & up, unsigned i, numeral & r) { // TODO: implement version that finds i-th root without isolating all roots. if (i == 0) throw algebraic_exception("invalid root object, root index must be greater than 0"); if (up.empty()) throw algebraic_exception("invalid root object, polynomial must not be the zero polynomial"); SASSERT(i != 0); scoped_numeral_vector roots(m_wrapper); isolate_roots(up, roots); unsigned num_roots = roots.size(); TRACE("algebraic", tout << "num-roots: " << num_roots << "\n"; for (unsigned i = 0; i < num_roots; i++) { display_interval(tout, roots[i]); tout << "\n"; }); if (i > num_roots) throw algebraic_exception("invalid root object, polynomial does have sufficient roots"); set(r, roots[i-1]); } void mk_root(polynomial_ref const & p, unsigned i, numeral & r) { SASSERT(i != 0); SASSERT(is_univariate(p)); TRACE("algebraic", tout << "isolating roots of: " << p << "\n";); scoped_upoly & up = m_isolate_tmp1; upm().to_numeral_vector(p, up); mk_root(up, i, r); } void mk_root(sexpr const * p, unsigned i, numeral & r) { SASSERT(i != 0); scoped_upoly & up = m_isolate_tmp1; sexpr2upolynomial(upm(), p, up); TRACE("algebraic", tout << "mk_root " << i << "\n"; upm().display(tout, up); tout << "\n";); mk_root(up, i, r); } /** \brief Make sure that if a is 0, then a.m_cell == 0 */ void normalize(numeral & a) { if (is_zero(a)) return; if (a.is_basic()) { if (qm().is_zero(a.to_basic()->m_value)) reset(a); } else { algebraic_cell * c = a.to_algebraic(); if (!upm().normalize_interval_core(c->m_p_sz, c->m_p, sign_lower(c), bqm(), lower(c), upper(c))) reset(a); } } /** \brief Return the magnitude of the given interval. The magnitude is an approximation of the size of the interval. */ int magnitude(mpbq const & l, mpbq const & u) { SASSERT(bqm().is_nonneg(l) || bqm().is_nonpos(u)); int l_k = l.k(); int u_k = u.k(); if (l_k == u_k) return bqm().magnitude_ub(l); if (bqm().is_nonneg(l)) return qm().log2(u.numerator()) - qm().log2(l.numerator()) - u_k + l_k - u_k; else return qm().mlog2(u.numerator()) - qm().mlog2(l.numerator()) - u_k + l_k - u_k; } /** \brief Return the magnitude of the isolating interval associated with the given algebraic_cell */ int magnitude(algebraic_cell * c) { return magnitude(lower(c), upper(c)); } /** \brief Refine isolating interval associated with algebraic number. The new interval will half of the size of the original one. Return TRUE, if interval was refined Return FALSE, if actual root was found. */ bool refine_core(algebraic_cell * c) { return upm().refine_core(c->m_p_sz, c->m_p, sign_lower(c), bqm(), lower(c), upper(c)); } /** \brief Refine isolating interval associated with algebraic number. This procedure is a noop if algebraic number is basic. This method essentially updates the field m_interval The new interval will half of the size of the original one. Remark: a root object may become basic when invoking this method, since we may find the actual rational root. This can only happen when non minimal polynomials are used to encode root objects. */ bool refine(numeral & a) { if (a.is_basic()) return false; algebraic_cell * c = a.to_algebraic(); if (!refine_core(c)) { // root was found scoped_mpq r(qm()); to_mpq(qm(), lower(c), r); del(c); a.m_cell = mk_basic_cell(r); return false; } return true; } bool refine(numeral & a, unsigned k) { for (unsigned i = 0; i < k; i++) if (!refine(a)) return false; return true; } bool refine_until_prec(numeral & a, unsigned prec) { if (a.is_basic()) return true; algebraic_cell * c = a.to_algebraic(); if (!upm().refine(c->m_p_sz, c->m_p, bqm(), lower(c), upper(c), prec)) { // actual root was found scoped_mpq r(qm()); to_mpq(qm(), lower(c), r); del(c); a.m_cell = mk_basic_cell(r); return false; } return true; } /** Functor for computing the polynomial resultant_y(pa(x-y), pb(y)) where pa and pb are the polynomials for algebraic cells: a and b. Remark: If alpha and beta are roots of pa and pb, then alpha + beta is a root of the new polynomial. Remark: If the argument IsAdd == false, then the functor computes resultant_y(pa(x+y), pb(y)) */ template struct mk_add_polynomial { imp & m; mk_add_polynomial(imp & _m):m(_m) {} void operator()(algebraic_cell * a, algebraic_cell * b, scoped_upoly & r) const { polynomial_ref pa_x(m.pm()); // pa(x) polynomial_ref pa_x_y(m.pm()); // pa(x-y) for addition and pa(x+y) for subtraction polynomial_ref pb_y(m.pm()); // pb(y) polynomial_ref r_x(m.pm()); // r(x) = resultant_y(pa(x-y), pb(y)) pa_x = m.pm().to_polynomial(a->m_p_sz, a->m_p, m.m_x); pb_y = m.pm().to_polynomial(b->m_p_sz, b->m_p, m.m_y); if (IsAdd) m.pm().compose_x_minus_y(pa_x, m.m_y, pa_x_y); else m.pm().compose_x_plus_y(pa_x, m.m_y, pa_x_y); m.pm().resultant(pa_x_y, pb_y, m.m_y, r_x); m.upm().to_numeral_vector(r_x, r); } }; /** Functor for computing the polynomial resultant_y(y^n * pa(x/y), pb(y)) where pa and pb are the polynomials for algebraic cells: a and b. n is degree of pa. */ struct mk_mul_polynomial { imp & m; mk_mul_polynomial(imp & _m):m(_m) {} void operator()(algebraic_cell * a, algebraic_cell * b, scoped_upoly & r) const { polynomial_ref pa_x(m.pm()); // pa(x) polynomial_ref pa_x_div_y(m.pm()); // y^n * pa(x/y) polynomial_ref pb_y(m.pm()); // pb(y) polynomial_ref r_x(m.pm()); // r(x) = resultant_y(y^n * pa(x/y), pb(y)) pa_x = m.pm().to_polynomial(a->m_p_sz, a->m_p, m.m_x); pb_y = m.pm().to_polynomial(b->m_p_sz, b->m_p, m.m_y); pa_x_div_y = m.pm().compose_x_div_y(pa_x, m.m_y); m.pm().resultant(pa_x_div_y, pb_y, m.m_y, r_x); m.upm().to_numeral_vector(r_x, r); } }; /** \brief Return the sum (interval) of the intervals of algebraic cells a and b. */ template struct add_interval_proc { imp & m; add_interval_proc(imp & _m):m(_m) {} void operator()(algebraic_cell * a, algebraic_cell * b, mpbqi & r) const { if (IsAdd) m.bqim().add(a->m_interval, b->m_interval, r); else m.bqim().sub(a->m_interval, b->m_interval, r); } }; /** \brief Return the product of the intervals of algebraic cells a and b. */ struct mul_interval_proc { imp & m; mul_interval_proc(imp & _m):m(_m) {} void operator()(algebraic_cell * a, algebraic_cell * b, mpbqi & r) const { m.bqim().mul(a->m_interval, b->m_interval, r); } }; /** \brief Functor for c <- a + b */ struct add_proc { imp & m; add_proc(imp & _m):m(_m) {} void operator()(numeral & a, numeral & b, numeral & c) const { return m.add(a, b, c); } }; /** \brief Functor for c <- a - b */ struct sub_proc { imp & m; sub_proc(imp & _m):m(_m) {} void operator()(numeral & a, numeral & b, numeral & c) const { return m.sub(a, b, c); } }; /** \brief Functor for c <- a * b */ struct mul_proc { imp & m; mul_proc(imp & _m):m(_m) {} void operator()(numeral & a, numeral & b, numeral & c) const { return m.mul(a, b, c); } }; // Save the isolating interval of an algebraic cell. struct save_intervals { imp & m_owner; numeral const & m_num; mpbqi m_old_interval; bool m_restore_invoked; // true if restore_if_too_small was invoked save_intervals(imp & o, numeral const & num): m_owner(o), m_num(num), m_restore_invoked(false) { SASSERT(!num.is_basic()); m_owner.bqim().set(m_old_interval, num.to_algebraic()->m_interval); } ~save_intervals() { if (!m_restore_invoked) restore_if_too_small(); m_owner.bqim().del(m_old_interval); } // Restore the intervals of m_cell, if its current magnitude is too small void restore_if_too_small() { m_restore_invoked = true; if (m_num.is_basic()) return; // m_num is not algebraic anymore algebraic_cell * cell = m_num.to_algebraic(); if (m_owner.magnitude(cell) < m_owner.m_min_magnitude) { // restore old interval m_owner.bqim().swap(cell->m_interval, m_old_interval); } } }; /** \brief Set c with the algebraic number associated with polynomial p and isolating interval r_i == (l, u). The isolating interval is not normalized, that is, it may contain zero. The method also requires the following (redundant) additional information: - seq: The Sturm sequence for p - lV: The Number of sign variations (in seq) at l - lU: The Number of sign variations (in seq) at u \pre p must be square free \pre r_i must be an isolating interval for p \pre seq must be the Sturm sequence for p \pre lV and uV are the sign variations (in seq) for r_i.lower() and r_i.upper() */ void set_core(numeral & c, scoped_upoly & p, mpbqi & r_i, upolynomial::scoped_upolynomial_sequence & seq, int lV, int uV, bool minimal) { TRACE("algebraic", tout << "set_core p: "; upm().display(tout, p); tout << "\n";); if (bqim().contains_zero(r_i)) { if (upm().has_zero_roots(p.size(), p.c_ptr())) { // zero is a root of p, and r_i is an isolating interval containing zero, // then c is zero reset(c); TRACE("algebraic", tout << "reseting\nresult: "; display_root(tout, c); tout << "\n";); return; } int zV = upm().sign_variations_at_zero(seq); if (lV == zV) { // root is in the second half bqim().set_lower(r_i, mpbq()); } else { SASSERT(zV == uV); // root is in the first half bqim().set_upper(r_i, mpbq()); } SASSERT(bqm().lt(r_i.lower(), r_i.upper())); } // make sure 0 is not a root of p scoped_upoly & nz_p = m_add_tmp; if (upm().has_zero_roots(p.size(), p.c_ptr())) { // remove zero root upm().remove_zero_roots(p.size(), p.c_ptr(), nz_p); } else { p.swap(nz_p); } if (!upm().isolating2refinable(nz_p.size(), nz_p.c_ptr(), bqm(), r_i.lower(), r_i.upper())) { // found actual root scoped_mpq r(qm()); to_mpq(qm(), r_i.lower(), r); set(c, r); } else { TRACE("algebraic", tout << "set_core...\n";); set(c, nz_p.size(), nz_p.c_ptr(), r_i.lower(), r_i.upper(), minimal); } } /** \brief Apply a binary operation on the given algebraic numbers. \pre !a.is_basic() and !b.is_basic() The template arguments: MkResultPoly: functor for constructing a polynomial p(x) s.t. p(u) = 0 (where u is the result of the operation). MkResultInterval: functor for computing an approximation of the resultant interval using the interval of the arguments. The functor must be "monotonic". That is, if we provide better (smaller) input intervals, it produces a better (smaller) output interval. MkBasic: functor for applying the operation if a or b become a basic cell. The numerals a and b may become basic during refinement. */ template void mk_binary(numeral & a, numeral & b, numeral & c, MkResultPoly const & mk_poly, MkResultInterval const & mk_interval, MkBasic const & mk_basic) { SASSERT(!a.is_basic()); SASSERT(!b.is_basic()); algebraic_cell * cell_a = a.to_algebraic(); algebraic_cell * cell_b = b.to_algebraic(); scoped_upoly p(upm()); scoped_upoly f(upm()); mk_poly(cell_a, cell_b, p); TRACE("anum_mk_binary", tout << "a: "; display_root(tout, a); tout << "\nb: "; display_root(tout, b); tout << "\np: "; upm().display(tout, p); tout << "\n";); factors fs(upm()); bool full_fact = factor(p, fs); unsigned num_fs = fs.distinct_factors(); scoped_ptr_vector seqs; for (unsigned i = 0; i < num_fs; i++) { TRACE("anum_mk_binary", tout << "factor " << i << "\n"; upm().display(tout, fs[i]); tout << "\n";); typename upolynomial::scoped_upolynomial_sequence * seq = alloc(typename upolynomial::scoped_upolynomial_sequence, upm()); upm().sturm_seq(fs[i].size(), fs[i].c_ptr(), *seq); seqs.push_back(seq); } SASSERT(seqs.size() == num_fs); save_intervals saved_a(*this, a); save_intervals saved_b(*this, b); scoped_mpbqi r_i(bqim()); while (true) { checkpoint(); SASSERT(!a.is_basic()); SASSERT(!b.is_basic()); mk_interval(cell_a, cell_b, r_i); unsigned num_rem = 0; // number of remaining sequences unsigned target_i = UINT_MAX; // index of sequence that is isolating int target_lV, target_uV; for (unsigned i = 0; i < num_fs; i++) { if (seqs[i] == 0) continue; // sequence was discarded because it does not contain the root. TRACE("anum_mk_binary", tout << "sequence " << i << "\n"; upm().display(tout, *(seqs[i])); tout << "\n";); int lV = upm().sign_variations_at(*(seqs[i]), r_i.lower()); int uV = upm().sign_variations_at(*(seqs[i]), r_i.upper()); int V = lV - uV; TRACE("algebraic", tout << "r_i: "; bqim().display(tout, r_i); tout << "\n"; tout << "lV: " << lV << ", uV: " << uV << "\n"; tout << "a.m_interval: "; bqim().display(tout, cell_a->m_interval); tout << "\n"; tout << "b.m_interval: "; bqim().display(tout, cell_b->m_interval); tout << "\n"; ); if (V <= 0) { // discard sequence, since factor does not contain the root seqs.set(i, 0); } else if (V == 1) { target_i = i; target_lV = lV; target_uV = uV; num_rem++; } else { num_rem++; } } if (num_rem == 1 && target_i != UINT_MAX) { // found isolating interval TRACE("anum_mk_binary", tout << "target_i: " << target_i << "\n";); saved_a.restore_if_too_small(); saved_b.restore_if_too_small(); upm().set(fs[target_i].size(), fs[target_i].c_ptr(), f); set_core(c, f, r_i, *(seqs[target_i]), target_lV, target_uV, full_fact); return; } if (!refine(a) || !refine(b)) { // a or b became basic SASSERT(a.is_basic() || b.is_basic()); saved_a.restore_if_too_small(); saved_a.restore_if_too_small(); return mk_basic(a, b, c); } } } template void mk_unary(numeral & a, numeral & b, MkResultPoly const & mk_poly, MkResultInterval const & mk_interval, MkBasic const & mk_basic) { SASSERT(!a.is_basic()); algebraic_cell * cell_a = a.to_algebraic(); scoped_upoly p(upm()); scoped_upoly f(upm()); mk_poly(cell_a, p); factors fs(upm()); bool full_fact = factor(p, fs); unsigned num_fs = fs.distinct_factors(); scoped_ptr_vector seqs; for (unsigned i = 0; i < num_fs; i++) { typename upolynomial::scoped_upolynomial_sequence * seq = alloc(typename upolynomial::scoped_upolynomial_sequence, upm()); upm().sturm_seq(fs[i].size(), fs[i].c_ptr(), *seq); seqs.push_back(seq); } SASSERT(seqs.size() == num_fs); save_intervals saved_a(*this, a); scoped_mpbqi r_i(bqim()); while (true) { checkpoint(); SASSERT(!a.is_basic()); mk_interval(cell_a, r_i); unsigned num_rem = 0; // number of remaining sequences unsigned target_i = UINT_MAX; // index of sequence that is isolating int target_lV, target_uV; for (unsigned i = 0; i < num_fs; i++) { if (seqs[i] == 0) continue; // sequence was discarded because it does not contain the root. int lV = upm().sign_variations_at(*(seqs[i]), r_i.lower()); int uV = upm().sign_variations_at(*(seqs[i]), r_i.upper()); int V = lV - uV; TRACE("algebraic", tout << "r_i: "; bqim().display(tout, r_i); tout << "\n"; tout << "lV: " << lV << ", uV: " << uV << "\n"; tout << "a.m_interval: "; bqim().display(tout, cell_a->m_interval); tout << "\n"; ); if (V <= 0) { // discard sequence, since factor does not contain the root seqs.set(i, 0); } else if (V == 1) { target_i = i; target_lV = lV; target_uV = uV; num_rem++; } else { num_rem++; } } if (num_rem == 1 && target_i != UINT_MAX) { // found isolating interval saved_a.restore_if_too_small(); upm().set(fs[target_i].size(), fs[target_i].c_ptr(), f); set_core(b, f, r_i, *(seqs[target_i]), target_lV, target_uV, full_fact); return; } if (!refine(a)) { // a became basic SASSERT(a.is_basic()); saved_a.restore_if_too_small(); return mk_basic(a, b); } } } /** Functor for computing the polynomial resultant_y(x^k - y, pa(y)) where pa is the polynomial for algebraic cell: a. k is a parameter. */ struct mk_root_polynomial { imp & m; unsigned k; mk_root_polynomial(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(algebraic_cell * a, scoped_upoly & r) const { // Let p be the polynomial associated with a. // Then, r(x) := Resultant(x^k - y, p(y), y) // is a polynomial s.t. a^{1/k} is a root of r(x). // Create r(x) polynomial_ref p_y(m.pm()); polynomial_ref xk_y(m.pm()); polynomial_ref y(m.pm()); polynomial_ref r_x(m.pm()); p_y = m.pm().to_polynomial(a->m_p_sz, a->m_p, m.m_y); y = m.pm().mk_polynomial(m.m_y); xk_y = m.pm().mk_polynomial(m.m_x, k); xk_y = xk_y - y; m.pm().resultant(xk_y, p_y, m.m_y, r_x); m.upm().to_numeral_vector(r_x, r); } }; /** \brief Return the k-th root of the interval of an algebraic cell a. */ struct root_interval_proc { imp & m; unsigned k; root_interval_proc(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(algebraic_cell * a, mpbqi & r) const { m.bqm().root_lower(m.lower(a), k, r.lower()); m.bqm().root_upper(m.upper(a), k, r.upper()); } }; /** \brief Functor for b <- a^{1/k} */ struct root_proc { imp & m; unsigned k; root_proc(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(numeral & a, numeral & b) const { return m.root(a, k, b); } }; /** Functor for computing the polynomial resultant_y(x - y^k, pa(y)) where pa is the polynomial for algebraic cell: a. k is a parameter. */ struct mk_power_polynomial { imp & m; unsigned k; mk_power_polynomial(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(algebraic_cell * a, scoped_upoly & r) const { polynomial_ref p_y(m.pm()); polynomial_ref x(m.pm()); polynomial_ref x_yk(m.pm()); polynomial_ref r_x(m.pm()); p_y = m.pm().to_polynomial(a->m_p_sz, a->m_p, m.m_y); x = m.pm().mk_polynomial(m.m_x); x_yk = m.pm().mk_polynomial(m.m_y, k); x_yk = x - x_yk; m.pm().resultant(x_yk, p_y, m.m_y, r_x); m.upm().to_numeral_vector(r_x, r); } }; /** \brief Return the ^k of the interval of an algebraic cell a. */ struct power_interval_proc { imp & m; unsigned k; power_interval_proc(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(algebraic_cell * a, mpbqi & r) const { m.bqim().power(a->m_interval, k, r); } }; /** \brief Functor for b <- a^k */ struct power_proc { imp & m; unsigned k; power_proc(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(numeral & a, numeral & b) const { return m.power(a, k, b); } }; void root_core(basic_cell * a, unsigned k, numeral & b) { SASSERT(!qm().is_zero(a->m_value)); SASSERT(k > 1); mpq & a_val = a->m_value; scoped_mpq r_a_val(qm()); if (qm().root(a_val, k, r_a_val)) { // the result is rational TRACE("root_core", tout << "r_a_val: " << r_a_val << " a_val: "; qm().display(tout, a_val); tout << "\n";); set(b, r_a_val); return; } // Let a_val be of the form n/d // create polynomial p: d*x^k - n // a_val > 0 --> (0, a_val+1) is an isolating interval // a_val < 0 --> (a_val-1, 0) is an isolating interval // Create p scoped_upoly p(upm()); p.push_back(mpz()); qm().set(p.back(), a_val.numerator()); qm().neg(p.back()); for (unsigned i = 0; i < k; i++) p.push_back(mpz()); qm().set(p.back(), a_val.denominator()); // Create isolating interval scoped_mpbq lower(bqm()); scoped_mpbq upper(bqm()); if (qm().is_neg(a_val)) { if (!bqm().to_mpbq(a_val, lower)) { // a_val is not a binary rational, lower is just an approximation. // lower == a_val.numerator() / 2^{log2(a_val.denominator()) + 1} // Thus, 2*lower <= a_val <= lower bqm().mul2(lower); // make sure lower <= a_val } bqm().sub(lower, mpz(1), lower); // make sure lower < (a_val)^{1/k} } else { if (!bqm().to_mpbq(a_val, upper)) { // a_val is not a binary rational, upper is just an approximation. // upper == a_val.numerator() / 2^{log2(a_val.denominator()) + 1} // Thus, upper <= a_val <= 2*upper bqm().mul2(upper); // make sure a_val <= upper } bqm().add(upper, mpz(1), upper); // make sure (a_val)^{1/k} < upper } SASSERT(bqm().lt(lower, upper)); TRACE("algebraic", tout << "root_core:\n"; upm().display(tout, p.size(), p.c_ptr()); tout << "\n";); // p is not necessarily a minimal polynomial. // So, we set the m_minimal flag to false. TODO: try to factor. set(b, p.size(), p.c_ptr(), lower, upper, false); } void root(numeral & a, unsigned k, numeral & b) { if (k == 0) throw algebraic_exception("0-th root is indeterminate"); if (k == 1 || is_zero(a)) { set(b, a); return; } if (is_neg(a) && k % 2 == 0) { // Remark: some computer algebra systems (e.g., Mathematica) define the // k-th root of a negative number as a complex number for any k. // We should check if our definition will not confuse users. throw algebraic_exception("even root of negative number is not real"); } if (a.is_basic()) root_core(a.to_basic(), k, b); else mk_unary(a, b, mk_root_polynomial(*this, k), root_interval_proc(*this, k), root_proc(*this, k)); } void power_core(basic_cell * a, unsigned k, numeral & b) { scoped_mpq r(qm()); qm().power(basic_value(a), k, r); set(b, r); } void power(numeral & a, unsigned k, numeral & b) { if (is_zero(a) && k == 0) throw algebraic_exception("0^0 is indeterminate"); if (k == 0) { set(b, 1); return; } if (k == 1) { set(b, a); return; } if (is_zero(a)) { reset(b); return; } if (a.is_basic()) { scoped_mpq r(qm()); qm().power(basic_value(a), k, r); set(b, r); } else { mk_unary(a, b, mk_power_polynomial(*this, k), power_interval_proc(*this, k), power_proc(*this, k)); } } void add(basic_cell * a, basic_cell * b, numeral & c) { scoped_mpq r(qm()); qm().add(basic_value(a), basic_value(b), r); set(c, r); normalize(c); } void sub(basic_cell * a, basic_cell * b, numeral & c) { scoped_mpq r(qm()); qm().sub(basic_value(a), basic_value(b), r); set(c, r); normalize(c); } template void add(algebraic_cell * a, basic_cell * b, numeral & c) { TRACE("algebraic", tout << "adding algebraic and basic cells:\n"; tout << "a: "; upm().display(tout, a->m_p_sz, a->m_p); tout << " "; bqim().display(tout, a->m_interval); tout << "\n"; tout << "b: "; qm().display(tout, b->m_value); tout << "\n";); scoped_mpq nbv(qm()); qm().set(nbv, b->m_value); if (IsAdd) qm().neg(nbv); m_add_tmp.reset(); upm().translate_q(a->m_p_sz, a->m_p, nbv, m_add_tmp); mpbqi const & i = a->m_interval; scoped_mpbq l(bqm()); scoped_mpbq u(bqm()); qm().neg(nbv); if (bqm().to_mpbq(nbv, l)) { bqm().add(i.upper(), l, u); bqm().add(i.lower(), l, l); } else { // failed to convert to binary rational scoped_mpq il(qm()); scoped_mpq iu(qm()); to_mpq(qm(), i.lower(), il); to_mpq(qm(), i.upper(), iu); TRACE("algebraic", tout << "nbv: " << nbv << "\n"; tout << "il: " << il << ", iu: " << iu << "\n";); qm().add(il, nbv, il); qm().add(iu, nbv, iu); // (il, iu) is an isolating refinable (rational) interval for the new polynomial. upm().convert_q2bq_interval(m_add_tmp.size(), m_add_tmp.c_ptr(), il, iu, bqm(), l, u); } TRACE("algebraic", upm().display(tout, m_add_tmp.size(), m_add_tmp.c_ptr()); tout << ", l: " << l << ", u: " << u << "\n"; tout << "l_sign: " << upm().eval_sign_at(m_add_tmp.size(), m_add_tmp.c_ptr(), l) << "\n"; tout << "u_sign: " << upm().eval_sign_at(m_add_tmp.size(), m_add_tmp.c_ptr(), u) << "\n"; ); set(c, m_add_tmp.size(), m_add_tmp.c_ptr(), l, u, a->m_minimal /* minimality is preserved */); normalize(c); } void add(numeral & a, numeral & b, numeral & c) { if (is_zero(a)) { set(c, b); return; } if (is_zero(b)) { set(c, a); return; } if (a.is_basic()) { if (b.is_basic()) add(a.to_basic(), b.to_basic(), c); else add(b.to_algebraic(), a.to_basic(), c); } else { if (b.is_basic()) add(a.to_algebraic(), b.to_basic(), c); else mk_binary(a, b, c, mk_add_polynomial(*this), add_interval_proc(*this), add_proc(*this)); } } void sub(numeral & a, numeral & b, numeral & c) { if (is_zero(a)) { set(c, b); neg(c); return; } if (is_zero(b)) { set(c, a); return; } if (a.is_basic()) { if (b.is_basic()) sub(a.to_basic(), b.to_basic(), c); else { // c <- b - a add(b.to_algebraic(), a.to_basic(), c); // c <- -c = a - b neg(c); } } else { if (b.is_basic()) add(a.to_algebraic(), b.to_basic(), c); else mk_binary(a, b, c, mk_add_polynomial(*this), add_interval_proc(*this), sub_proc(*this)); } } void mul(basic_cell * a, basic_cell * b, numeral & c) { scoped_mpq r(qm()); qm().mul(basic_value(a), basic_value(b), r); set(c, r); normalize(c); } void mul(algebraic_cell * a, basic_cell * b, numeral & c) { TRACE("algebraic", tout << "mult algebraic and basic cells:\n"; tout << "a: "; upm().display(tout, a->m_p_sz, a->m_p); tout << " "; bqim().display(tout, a->m_interval); tout << "\n"; tout << "b: "; qm().display(tout, b->m_value); tout << "\n";); SASSERT(upm().eval_sign_at(a->m_p_sz, a->m_p, lower(a)) == -upm().eval_sign_at(a->m_p_sz, a->m_p, upper(a))); scoped_mpq nbv(qm()); qm().set(nbv, b->m_value); qm().inv(nbv); scoped_upoly & mulp = m_add_tmp; upm().set(a->m_p_sz, a->m_p, mulp); upm().compose_p_q_x(mulp.size(), mulp.c_ptr(), nbv); mpbqi const & i = a->m_interval; scoped_mpbq l(bqm()); scoped_mpbq u(bqm()); qm().inv(nbv); bool is_neg = qm().is_neg(nbv); if (bqm().to_mpbq(nbv, l)) { bqm().mul(i.upper(), l, u); bqm().mul(i.lower(), l, l); if (is_neg) bqm().swap(l, u); } else { // failed to convert to binary rational scoped_mpq il(qm()); scoped_mpq iu(qm()); to_mpq(qm(), i.lower(), il); to_mpq(qm(), i.upper(), iu); TRACE("algebraic", tout << "nbv: " << nbv << "\n"; tout << "il: " << il << ", iu: " << iu << "\n";); qm().mul(il, nbv, il); qm().mul(iu, nbv, iu); if (is_neg) qm().swap(il, iu); // (il, iu) is an isolating refinable (rational) interval for the new polynomial. upm().convert_q2bq_interval(mulp.size(), mulp.c_ptr(), il, iu, bqm(), l, u); } TRACE("algebraic", upm().display(tout, mulp.size(), mulp.c_ptr()); tout << ", l: " << l << ", u: " << u << "\n"; tout << "l_sign: " << upm().eval_sign_at(mulp.size(), mulp.c_ptr(), l) << "\n"; tout << "u_sign: " << upm().eval_sign_at(mulp.size(), mulp.c_ptr(), u) << "\n"; ); set(c, mulp.size(), mulp.c_ptr(), l, u, a->m_minimal /* minimality is preserved */); normalize(c); } void mul(numeral & a, numeral & b, numeral & c) { if (is_zero(a) || is_zero(b)) { reset(c); return; } if (a.is_basic()) { if (b.is_basic()) mul(a.to_basic(), b.to_basic(), c); else mul(b.to_algebraic(), a.to_basic(), c); } else { if (b.is_basic()) mul(a.to_algebraic(), b.to_basic(), c); else mk_binary(a, b, c, mk_mul_polynomial(*this), mul_interval_proc(*this), mul_proc(*this)); } } void neg(numeral & a) { if (is_zero(a)) return; if (a.is_basic()) { qm().neg(a.to_basic()->m_value); } else { algebraic_cell * c = a.to_algebraic(); upm().p_minus_x(c->m_p_sz, c->m_p); bqim().neg(c->m_interval); update_sign_lower(c); } } /** Make sure lower != 0 and upper != 0 if a is non-basic algebraic number. */ void refine_nz_bound(numeral & a) { if (a.is_basic()) return; algebraic_cell * cell_a = a.to_algebraic(); mpbq & lower = cell_a->m_interval.lower(); mpbq & upper = cell_a->m_interval.upper(); if (!bqm().is_zero(lower) && !bqm().is_zero(upper)) return; int sign_l = sign_lower(cell_a); SASSERT(sign_l != 0); int sign_u = -sign_l; #define REFINE_LOOP(BOUND, TARGET_SIGN) \ while (true) { \ bqm().div2(BOUND); \ int new_sign = upm().eval_sign_at(cell_a->m_p_sz, cell_a->m_p, BOUND); \ if (new_sign == 0) { \ /* found actual root */ \ scoped_mpq r(qm()); \ to_mpq(qm(), BOUND, r); \ set(a, r); \ return; \ } \ if (new_sign == TARGET_SIGN) \ return; \ } if (bqm().is_zero(lower)) { bqm().set(lower, upper); REFINE_LOOP(lower, sign_l); } else { SASSERT(bqm().is_zero(upper)); bqm().set(upper, lower); REFINE_LOOP(upper, sign_u); } } void inv(numeral & a) { if (is_zero(a)) { UNREACHABLE(); throw algebraic_exception("division by zero"); } refine_nz_bound(a); if (a.is_basic()) { qm().inv(a.to_basic()->m_value); } else { TRACE("algebraic_bug", tout << "before inv: "; display_root(tout, a); tout << "\n"; display_interval(tout, a); tout << "\n";); algebraic_cell * cell_a = a.to_algebraic(); upm().p_1_div_x(cell_a->m_p_sz, cell_a->m_p); // convert binary rational bounds into rational bounds scoped_mpq inv_lower(qm()), inv_upper(qm()); to_mpq(qm(), lower(cell_a), inv_lower); to_mpq(qm(), upper(cell_a), inv_upper); // (1/upper, 1/lower) is an isolating interval for the new polynomial qm().inv(inv_lower); qm().inv(inv_upper); qm().swap(inv_lower, inv_upper); TRACE("algebraic_bug", tout << "inv new_bounds: " << qm().to_string(inv_lower) << ", " << qm().to_string(inv_upper) << "\n";); // convert isolating interval back as a binary rational bound upm().convert_q2bq_interval(cell_a->m_p_sz, cell_a->m_p, inv_lower, inv_upper, bqm(), lower(cell_a), upper(cell_a)); TRACE("algebraic_bug", tout << "after inv: "; display_root(tout, a); tout << "\n"; display_interval(tout, a); tout << "\n";); } } void div(numeral & a, numeral & b, numeral & c) { if (is_zero(b)) { UNREACHABLE(); throw algebraic_exception("division by zero"); } // div is not used by the nonlinear procedure. // I implemented just to make sure that all field operations are available in the algebraic number module. // It is also useful to allow users to evaluate expressions containing algebraic numbers. // // We can avoid computing invb, by having a procedure similar to mul // that uses // Resultant(pa(xy), pb(y), y) instead of // Resultant(y^n * pa(x/y), pb(y), y) // scoped_anum invb(m_wrapper); set(invb, b); inv(invb); mul(a, invb, c); } // Todo: move to MPQ int compare(mpq const & a, mpq const & b) { if (qm().eq(a, b)) return 0; return qm().lt(a, b) ? -1 : 1; } /** Comparing algebraic_cells with rationals Given an algebraic cell c with isolating interval (l, u) for p and a rational b Then, u <= b implies c < b b <= l implies c > b Otherwise, l < b < u, and p(b) < 0 --> If p(l) < 0 then c > b else c < b p(b) == 0 --> c = b p(b) > 0 --> if p(l) < 0 then c < b else c > b We can simplify the rules above as: p(b) == 0 then c == b (p(b) < 0) == (p(l) < 0) then c > b else c < b */ int compare(algebraic_cell * c, mpq const & b) { mpbq const & l = lower(c); mpbq const & u = upper(c); if (bqm().le(u, b)) return -1; if (bqm().ge(l, b)) return 1; // b is in the isolating interval (l, u) int sign_b = upm().eval_sign_at(c->m_p_sz, c->m_p, b); if (sign_b == 0) return 0; return sign_b == sign_lower(c) ? 1 : -1; } // Return true if the polynomials of cell_a and cell_b are the same. bool compare_p(algebraic_cell const * cell_a, algebraic_cell const * cell_b) { return upm().eq(cell_a->m_p_sz, cell_a->m_p, cell_b->m_p_sz, cell_b->m_p); } int compare_core(numeral & a, numeral & b) { SASSERT(!a.is_basic() && !b.is_basic()); algebraic_cell * cell_a = a.to_algebraic(); algebraic_cell * cell_b = b.to_algebraic(); mpbq const & a_lower = lower(cell_a); mpbq const & a_upper = upper(cell_a); mpbq const & b_lower = lower(cell_b); mpbq const & b_upper = upper(cell_b); #define COMPARE_INTERVAL() \ if (bqm().le(a_upper, b_lower)) { \ m_compare_cheap++; \ return -1; \ } \ if (bqm().ge(a_lower, b_upper)) { \ m_compare_cheap++; \ return 1; \ } COMPARE_INTERVAL(); // if cell_a and cell_b, contain the same polynomial, // and the intervals are overlaping, then they are // the same root. if (compare_p(cell_a, cell_b)) { m_compare_poly_eq++; return 0; } TRACE("algebraic", tout << "comparing\n"; tout << "a: "; upm().display(tout, cell_a->m_p_sz, cell_a->m_p); tout << "\n"; bqim().display(tout, cell_a->m_interval); tout << "\ncell_a->m_minimal: " << cell_a->m_minimal << "\n"; tout << "b: "; upm().display(tout, cell_b->m_p_sz, cell_b->m_p); tout << "\n"; bqim().display(tout, cell_b->m_interval); tout << "\ncell_b->m_minimal: " << cell_b->m_minimal << "\n";); if (cell_a->m_minimal && cell_b->m_minimal) { // Minimal polynomial special case. // This branch is only executed when polynomial // factorization is turned on. // If a and b are defined by minimal distinct polynomials, // then they MUST BE DIFFERENT. // Thus, if we keep refining the interval of a and b, // eventually they will not overlap while (true) { checkpoint(); refine(a); refine(b); m_compare_refine++; // refine can't reduce a and b to rationals, // since the polynomial is minimal and it is not linear. // So, the roots are NOT rational. SASSERT(!a.is_basic()); SASSERT(!b.is_basic()); COMPARE_INTERVAL(); } } // make sure that intervals of a and b have the same magnitude int a_m = magnitude(a_lower, a_upper); int b_m = magnitude(b_lower, b_upper); int target_m = std::max(m_min_magnitude, std::min(a_m, b_m)); if (b_m > target_m) { if (!refine(b, b_m - target_m)) return compare(a, b); m_compare_refine += b_m - target_m; COMPARE_INTERVAL(); } if (a_m > target_m) { if (!refine(a, a_m - target_m)) return compare(a, b); m_compare_refine += a_m - target_m; COMPARE_INTERVAL(); } if (target_m > m_min_magnitude) { int num_refinements = target_m - m_min_magnitude; for (int i = 0; i < num_refinements; i++) { if (!refine(a) || !refine(b)) return compare(a, b); m_compare_refine++; COMPARE_INTERVAL(); } } // EXPENSIVE CASE // Let seq be the Sturm-Tarski sequence for // p_a, p_a' * p_b // Let s_l be the number of sign variations at a_lower. // Let s_u be the number of sign variations at a_upper. // By Sturm-Tarski Theorem, we have that // V = s_l - s_u = #(p_b(r) > 0) - #(p_b(r) < 0) at roots r of p_a // Since there is only one root of p_a in (a_lower, b_lower), // we are evaluating the sign of p_b at a. // That is V is the sign of p_b at a. // // We have // V < 0 -> p_b(a) < 0 -> if p_b(b_lower) < 0 then b > a else b < a // V == 0 -> p_b(a) == 0 -> a = b // V > 0 -> p_b(a) > 0 -> if p_b(b_lower) > 0 then b > a else b < a // Simplifying we have: // V == 0 --> a = b // if (V < 0) == (p_b(b_lower) < 0) then b > a else b < a // m_compare_sturm++; upolynomial::scoped_upolynomial_sequence seq(upm()); upm().sturm_tarski_seq(cell_a->m_p_sz, cell_a->m_p, cell_b->m_p_sz, cell_b->m_p, seq); int V = upm().sign_variations_at(seq, a_lower) - upm().sign_variations_at(seq, a_upper); TRACE("algebraic", tout << "comparing using sturm\n"; display_interval(tout, a); tout << "\n"; display_interval(tout, b); tout << "\n"; tout << "V: " << V << ", sign_lower(a): " << sign_lower(cell_a) << ", sign_lower(b): " << sign_lower(cell_b) << "\n";); if (V == 0) return 0; if ((V < 0) == (sign_lower(cell_b) < 0)) return -1; else return 1; // Here is an unexplored option for comparing numbers. // // The isolating intervals of a and b are still overlaping // Then we compute // r(x) = Resultant(x - y1 + y2, p1(y1), p2(y2)) // where p1(y1) and p2(y2) are the polynomials defining a and b. // Remarks: // 1) The resultant r(x) must not be the zero polynomial, // since the polynomial x - y1 + y2 does not vanish in any of the roots of p1(y1) and p2(y2) // // 2) By resultant calculus, If alpha, beta1, beta2 are roots of x - y1 + y2, p1(y1), p2(y2) // then alpha is a root of r(x). // Thus, we have that a - b is a root of r(x) // // 3) If 0 is not a root of r(x), then a != b (by remark 2) // // 4) Let (l1, u1) and (l2, u2) be the intervals of a and b. // Then, a - b must be in (l1 - u2, u1 - l2) // // 5) Assume a != b, then if we keep refining the isolating intervals for a and b, // then eventually, (l1, u1) and (l2, u2) will not overlap. // Thus, if 0 is not a root of r(x), we can keep refining until // the intervals do not overlap. // // 6) If 0 is a root of r(x), we have two possibilities: // a) Isolate roots of r(x) in the interval (l1 - u2, u1 - l2), // and then keep refining (l1, u1) and (l2, u2) until they // (l1 - u2, u1 - l2) "convers" only one root. // // b) Compute the sturm sequence for r(x), // keep refining the (l1, u1) and (l2, u2) until // (l1 - u2, u1 - l2) contains only one root of r(x) } int compare(numeral & a, numeral & b) { TRACE("algebraic", tout << "comparing: "; display_interval(tout, a); tout << " "; display_interval(tout, b); tout << "\n";); if (a.is_basic()) { if (b.is_basic()) return compare(basic_value(a), basic_value(b)); else return -compare(b.to_algebraic(), basic_value(a)); } else { if (b.is_basic()) return compare(a.to_algebraic(), basic_value(b)); else return compare_core(a, b); } } bool eq(numeral & a, numeral & b) { return compare(a, b) == 0; } bool eq(numeral & a, mpq const & b) { if (a.is_basic()) return qm().eq(basic_value(a), b); else return compare(a.to_algebraic(), b) == 0; } bool lt(numeral & a, numeral & b) { return compare(a, b) < 0; } bool lt(numeral & a, mpq const & b) { if (a.is_basic()) return qm().lt(basic_value(a), b); else return compare(a.to_algebraic(), b) < 0; } bool gt(numeral & a, mpq const & b) { if (a.is_basic()) return qm().gt(basic_value(a), b); else return compare(a.to_algebraic(), b) > 0; } void get_polynomial(numeral const & a, svector & r) { if (a.is_basic()) { r.reserve(2); if (is_zero(a)) { qm().set(r[0], 0); qm().set(r[1], 1); } else { mpq const & v = basic_value(a); qm().set(r[0], v.numerator()); qm().set(r[1], v.denominator()); qm().neg(r[0]); } upm().set_size(2, r); } else { algebraic_cell * c = a.to_algebraic(); upm().set(c->m_p_sz, c->m_p, r); } } /** \brief "Optimistic" mapping: it assumes all variables are mapped to basic_values (rationals). Throws an exception if that is not the case. */ struct opt_var2basic : public polynomial::var2mpq { struct failed {}; imp & m_imp; polynomial::var2anum const & m_x2v; opt_var2basic(imp & i, polynomial::var2anum const & x2v):m_imp(i), m_x2v(x2v) {} virtual unsynch_mpq_manager & m() const { return m_imp.qm(); } virtual bool contains(polynomial::var x) const { return m_x2v.contains(x); } virtual mpq const & operator()(polynomial::var x) const { anum const & v = m_x2v(x); if (!v.is_basic()) throw failed(); return m_imp.basic_value(v); } }; /** \brief Reduced mapping which contains only the rational values */ struct var2basic : public polynomial::var2mpq { imp & m_imp; polynomial::var2anum const & m_x2v; var2basic(imp & i, polynomial::var2anum const & x2v):m_imp(i), m_x2v(x2v) {} virtual unsynch_mpq_manager & m() const { return m_imp.qm(); } virtual bool contains(polynomial::var x) const { return m_x2v.contains(x) && m_x2v(x).is_basic(); } virtual mpq const & operator()(polynomial::var x) const { anum const & v = m_x2v(x); SASSERT(v.is_basic()); TRACE("var2basic", tout << "getting value of x" << x << " -> " << m().to_string(m_imp.basic_value(v)) << "\n";); return m_imp.basic_value(v); } }; /** \brief Reduced mapping which contains only the non-basic values as intervals */ struct var2interval : public polynomial::var2mpbqi { imp & m_imp; polynomial::var2anum const & m_x2v; var2interval(imp & i, polynomial::var2anum const & x2v):m_imp(i), m_x2v(x2v) {} virtual mpbqi_manager & m() const { return m_imp.bqim(); } virtual bool contains(polynomial::var x) const { return m_x2v.contains(x) && !m_x2v(x).is_basic(); } virtual mpbqi const & operator()(polynomial::var x) const { anum const & v = m_x2v(x); SASSERT(!v.is_basic()); return v.to_algebraic()->m_interval; } }; polynomial::var_vector m_eval_sign_vars; int eval_sign_at(polynomial_ref const & p, polynomial::var2anum const & x2v) { polynomial::manager & ext_pm = p.m(); TRACE("anum_eval_sign", tout << "evaluating sign of: " << p << "\n";); while (true) { bool restart = false; // Optimistic: maybe x2v contains only rational values try { opt_var2basic x2v_basic(*this, x2v); scoped_mpq r(qm()); ext_pm.eval(p, x2v_basic, r); TRACE("anum_eval_sign", tout << "all variables are assigned to rationals, value of p: " << r << "\n";); return qm().sign(r); } catch (opt_var2basic::failed) { // continue } // Eliminate rational values from p polynomial_ref p_prime(ext_pm); var2basic x2v_basic(*this, x2v); p_prime = ext_pm.substitute(p, x2v_basic); TRACE("anum_eval_sign", tout << "p after eliminating rationals: " << p_prime << "\n";); if (ext_pm.is_zero(p_prime)) { // polynomial vanished after substituting rational values. return 0; } if (is_const(p_prime)) { // polynomial became the constant polynomial after substitution. SASSERT(size(p_prime) == 1); return ext_pm.m().sign(ext_pm.coeff(p_prime, 0)); } // Try to find sign using intervals polynomial::var_vector & xs = m_eval_sign_vars; xs.reset(); ext_pm.vars(p_prime, xs); SASSERT(!xs.empty()); var2interval x2v_interval(*this, x2v); scoped_mpbqi ri(bqim()); while (true) { checkpoint(); ext_pm.eval(p_prime, x2v_interval, ri); TRACE("anum_eval_sign", tout << "evaluating using intervals: " << ri << "\n";); if (!bqim().contains_zero(ri)) { return bqim().is_pos(ri) ? 1 : -1; } // refine intervals if magnitude > m_min_magnitude bool refined = false; for (unsigned i = 0; i < xs.size(); i++) { polynomial::var x = xs[i]; SASSERT(x2v.contains(x)); anum const & v = x2v(x); SASSERT(!v.is_basic()); algebraic_cell * c = v.to_algebraic(); int m = magnitude(c); if (m > m_min_magnitude || (m_zero_accuracy > 0 && m > m_zero_accuracy)) { if (!refine(const_cast(v))) { // v became a basic value restart = true; break; } TRACE("anum_eval_sign", tout << "refined algebraic interval\n";); SASSERT(!v.is_basic()); refined = true; } } if (!refined || restart) { // Stop if no interval was refined OR some algebraic cell became basic break; } } if (restart) { // Some non-basic value became basic. // So, restarting the whole process TRACE("anum_eval_sign", tout << "restarting some algebraic_cell became basic\n";); continue; } // At this point, we are almost sure that p is zero at x2n // That is, rin is probably a very small interval that contains zero. // Remark: m_zero_accuracy == 0 means use precise computation. if (m_zero_accuracy > 0) { // assuming the value is 0, since the result is in (-1/2^k, 1/2^k), where m_zero_accuracy = k return 0; } #if 0 // Evaluating sign using algebraic arithmetic scoped_anum ra(m_wrapper); ext_pm.eval(p_prime, x2v, ra); TRACE("anum_eval_sign", tout << "value of p as algebraic number " << ra << "\n";); if (is_zero(ra)) return 0; return is_pos(ra) ? 1 : -1; #else // Evaluating the sign using Resultants // Basic idea: // We want to evaluate the sign of // p(x_1, ..., x_n) // at x_1 -> v_1, ..., x_n -> v_n // // Let v be p(v_1, ..., v_n). // We want to know the sign of v. // // Assume v_i's are defined by the polynomials q_i(x_i) // Then, we have that // the polynomials // y - p(x_1, ..., x_n), q_1(x_1), ..., q_n(x_n) // are zero at y -> v, x_1 -> v_1, ..., x_n -> v_n // // Thus, by resultant theory, v is also a root of // R(y) = Resultant(p(x_1, ..., x_n), q_1(x_1), ..., q_n(x_n)) // Remark: R(y) is not the zero polynomial, since // the coefficient of y in the polynomial y - p(x_1, ..., x_n) // is (the constant polynomial) one. // // Now, let L be a lower bound on the nonzero roots of R(y). // Thus, any root alpha of R(y) is zero or |alpha| > L // // Therefore, we have that |v| > L // Now, using L, we can keep refining the interval ri which contains v. // Eventually, ri will not contain zero (and consequently v != 0), // or ri is in (-L, L), and consequently v = 0. polynomial::var y = get_max_var(xs) + 1; ensure_num_vars(y+1); // we create all polynomials in the local polynomial manager because we need an extra variable y polynomial_ref yp(pm()); yp = pm().mk_polynomial(y); polynomial_ref p_prime_aux(pm()); p_prime_aux = convert(ext_pm, p_prime, pm()); polynomial_ref R(pm()); R = yp - p_prime_aux; // compute the resultants polynomial_ref q_i(pm()); std::stable_sort(xs.begin(), xs.end(), var_degree_lt(*this, x2v)); // std::cout << "R: " << R << "\n"; for (unsigned i = 0; i < xs.size(); i++) { checkpoint(); polynomial::var x_i = xs[i]; SASSERT(x2v.contains(x_i)); anum const & v_i = x2v(x_i); SASSERT(!v_i.is_basic()); algebraic_cell * c = v_i.to_algebraic(); q_i = pm().to_polynomial(c->m_p_sz, c->m_p, x_i); // std::cout << "q_i: " << q_i << std::endl; pm().resultant(R, q_i, x_i, R); SASSERT(!pm().is_zero(R)); } SASSERT(pm().is_univariate(R)); scoped_upoly & _R = m_eval_sign_tmp; upm().to_numeral_vector(R, _R); unsigned k = upm().nonzero_root_lower_bound(_R.size(), _R.c_ptr()); TRACE("anum_eval_sign", tout << "R: " << R << "\nk: " << k << "\nri: "<< ri << "\n";); // std::cout << "R: " << R << "\n"; scoped_mpbq mL(bqm()), L(bqm()); bqm().set(mL, -1); bqm().set(L, 1); bqm().div2k(mL, k); bqm().div2k(L, k); if (bqm().lt(mL, ri.lower()) && bqm().lt(ri.upper(), L)) return 0; // keep refining ri until ri is inside (-L, L) or // ri does not contain zero. // The invervals (for the values of the variables in xs) are going to get too small. // So, we save them before refining... scoped_ptr_vector saved_intervals; for (unsigned i = 0; i < xs.size(); i++) { polynomial::var x_i = xs[i]; SASSERT(x2v.contains(x_i)); anum const & v_i = x2v(x_i); SASSERT(!v_i.is_basic()); saved_intervals.push_back(alloc(save_intervals, *this, v_i)); } // Actual refinement loop restart = false; while (!restart) { checkpoint(); ext_pm.eval(p_prime, x2v_interval, ri); TRACE("anum_eval_sign", tout << "evaluating using intervals: " << ri << "\n"; tout << "zero lower bound is: " << L << "\n";); if (!bqim().contains_zero(ri)) { return bqim().is_pos(ri) ? 1 : -1; } if (bqm().lt(mL, ri.lower()) && bqm().lt(ri.upper(), L)) return 0; for (unsigned i = 0; i < xs.size(); i++) { polynomial::var x = xs[i]; SASSERT(x2v.contains(x)); anum const & v = x2v(x); SASSERT(!v.is_basic()); if (!refine(const_cast(v))) { // v became a basic value restart = true; break; } TRACE("anum_eval_sign", tout << "refined algebraic interval\n";); SASSERT(!v.is_basic()); } } #endif } } // Functor used to sort variables by the degree of the polynomial used to represent their value. struct var_degree_lt { imp & m_imp; polynomial::var2anum const & m_x2v; var_degree_lt(imp & i, polynomial::var2anum const & x2v): m_imp(i), m_x2v(x2v) { } unsigned degree(polynomial::var x) const { if (!m_x2v.contains(x)) return UINT_MAX; return m_imp.degree(m_x2v(x)); } bool operator()(polynomial::var x1, polynomial::var x2) const { return degree(x1) < degree(x2); } }; // Add entry x->v to existing mapping struct ext_var2num : public polynomial::var2anum { manager & m_am; polynomial::var2anum const & m_x2v; polynomial::var m_x; anum const & m_v; ext_var2num(manager & am, polynomial::var2anum const & x2v, polynomial::var x, anum const & v): m_am(am), m_x2v(x2v), m_x(x), m_v(v) { } virtual manager & m() const { return m_am; } virtual bool contains(polynomial::var x) const { return x == m_x || m_x2v.contains(x); } virtual anum const & operator()(polynomial::var x) const { if (x == m_x) return m_v; else return m_x2v(x); } }; // Remove from roots any solution r such that p does not evaluate to 0 at x2v extended with x->r. void filter_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, polynomial::var x, numeral_vector & roots) { TRACE("isolate_roots", tout << "before filtering roots, x: x" << x << "\n"; for (unsigned i = 0; i < roots.size(); i++) { display_root(tout, roots[i]); tout << "\n"; }); unsigned sz = roots.size(); unsigned j = 0; // std::cout << "p: " << p << "\n"; // std::cout << "sz: " << sz << "\n"; for (unsigned i = 0; i < sz; i++) { checkpoint(); // display_root(std::cout, roots[i]); std::cout << std::endl; ext_var2num ext_x2v(m_wrapper, x2v, x, roots[i]); TRACE("isolate_roots", tout << "filter_roots i: " << i << ", ext_x2v: x" << x << " -> "; display_root(tout, roots[i]); tout << "\n";); int sign = eval_sign_at(p, ext_x2v); TRACE("isolate_roots", tout << "filter_roots i: " << i << ", result sign: " << sign << "\n";); if (sign != 0) continue; // display_decimal(std::cout, roots[i], 10); std::cout << " is root" << std::endl; if (i != j) set(roots[j], roots[i]); j++; } for (unsigned i = j; i < sz; i++) del(roots[i]); roots.shrink(j); TRACE("isolate_roots", tout << "after filtering roots:\n"; for (unsigned i = 0; i < roots.size(); i++) { display_root(tout, roots[i]); tout << "\n"; }); } // Return the maximal variable in xs. static polynomial::var get_max_var(polynomial::var_vector const & xs) { SASSERT(!xs.empty()); polynomial::var x = xs[0]; for (unsigned i = 1; i < xs.size(); i++) { if (xs[i] > x) x = xs[i]; } return x; } // Ensure that local polynomial manager has at least sz vars void ensure_num_vars(unsigned sz) { while (sz > pm().num_vars()) pm().mk_var(); SASSERT(pm().num_vars() >= sz); } polynomial::var_vector m_isolate_roots_vars; void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, bool nested_call = false) { TRACE("isolate_roots", tout << "isolating roots of: " << p << "\n";); SASSERT(roots.empty()); polynomial::manager & ext_pm = p.m(); if (ext_pm.is_zero(p) || ext_pm.is_const(p)) { TRACE("isolate_roots", tout << "p is zero or the constant polynomial\n";); return; } if (ext_pm.is_univariate(p)) { TRACE("isolate_roots", tout << "p is univariate, using univariate procedure\n";); isolate_roots(p, roots); return; } // eliminate rationals polynomial_ref p_prime(ext_pm); var2basic x2v_basic(*this, x2v); p_prime = ext_pm.substitute(p, x2v_basic); TRACE("isolate_roots", tout << "p after applying (rational fragment of) x2v:\n" << p_prime << "\n";); if (ext_pm.is_zero(p_prime) || ext_pm.is_const(p_prime)) { TRACE("isolate_roots", tout << "p is zero or the constant polynomial after applying (rational fragment of) x2v\n";); return; } if (ext_pm.is_univariate(p_prime)) { polynomial::var x = ext_pm.max_var(p_prime); if (x2v.contains(x)) { // The remaining variable is assigned, the actual unassigned variable vanished when we replaced rational values. // So, the polynomial does not have any roots return; } TRACE("isolate_roots", tout << "p is univariate after applying (rational fragment of) x2v... using univariate procedure\n";); isolate_roots(p_prime, roots); return; } polynomial::var_vector & xs = m_isolate_roots_vars; xs.reset(); ext_pm.vars(p_prime, xs); SASSERT(xs.size() > 1); // sort variables by the degree of the values std::stable_sort(xs.begin(), xs.end(), var_degree_lt(*this, x2v)); TRACE("isolate_roots", tout << "there are " << (xs.size() - 1) << " variables assigned to nonbasic numbers...\n";); // last variables is the one not assigned by x2v, or the unassigned variable vanished polynomial::var x = xs.back(); if (x2v.contains(x)) { // all remaining variables are assigned. // the unassigned variable vanished when we replaced the rational values. DEBUG_CODE({ for (unsigned i = 0; i < xs.size(); i++) { SASSERT(x2v.contains(xs[i])); } }); return; } // construct univariate polynomial q which contains all roots of p_prime at x2v. polynomial_ref q(ext_pm); q = p_prime; polynomial_ref p_y(ext_pm); for (unsigned i = 0; i < xs.size() - 1; i++) { checkpoint(); polynomial::var y = xs[i]; SASSERT(x2v.contains(y)); anum const & v = x2v(y); SASSERT(!v.is_basic()); algebraic_cell * c = v.to_algebraic(); p_y = ext_pm.to_polynomial(c->m_p_sz, c->m_p, y); ext_pm.resultant(q, p_y, y, q); TRACE("isolate_roots", tout << "resultant loop i: " << i << ", y: x" << y << "\np_y: " << p_y << "\n"; tout << "q: " << q << "\n";); if (ext_pm.is_zero(q)) { SASSERT(!nested_call); break; } } if (ext_pm.is_zero(q)) { TRACE("isolate_roots", tout << "q vanished\n";); // q may vanish at some of the other roots of the polynomial defining the values. // To decide if p_prime vanishes at x2v or not, we start evaluating each coefficient of p_prime at x2v // until we find one that is not zero at x2v. // In the process we will copy p_prime to the local polynomial manager, since we will need to create // an auxiliary variable. SASSERT(!nested_call); unsigned n = ext_pm.degree(p_prime, x); SASSERT(n > 0); if (n == 1) { // p_prime is linear on p, so we just evaluate the coefficients... TRACE("isolate_roots", tout << "p is linear after applying (rational fragment) of x2v\n";); polynomial_ref c0(ext_pm); polynomial_ref c1(ext_pm); c0 = ext_pm.coeff(p_prime, x, 0); c1 = ext_pm.coeff(p_prime, x, 1); scoped_anum a0(m_wrapper); scoped_anum a1(m_wrapper); ext_pm.eval(c0, x2v, a0); ext_pm.eval(c1, x2v, a1); // the root must be - a0/a1 if a1 != 0 if (is_zero(a1)) { TRACE("isolate_roots", tout << "coefficient of degree 1 vanished, so p does not have roots at x2v\n";); // p_prime does not have any root return; } roots.push_back(anum()); div(a0, a1, roots[0]); neg(roots[0]); TRACE("isolate_roots", tout << "after trivial solving p has only one root:\n"; display_root(tout, roots[0]); tout << "\n";); } else { polynomial_ref c(ext_pm); scoped_anum a(m_wrapper); int i = n; for (; i >= 1; i--) { c = ext_pm.coeff(p_prime, x, i); ext_pm.eval(c, x2v, a); if (!is_zero(a)) break; } if (i == 0) { // all coefficients of x vanished, so // the polynomial has no roots TRACE("isolate_roots", tout << "all coefficients vanished... polynomial does not have roots\n";); return; } SASSERT(!is_zero(a)); polynomial::var z = get_max_var(xs) + 1; ensure_num_vars(z+1); // create polynomial q2 in the local manager // z * x^i + c_{i-1} * x^{i-1} + ... + c_1 * x + c_0 // where c's are the coefficients of p_prime. // Then we invoke isolate_roots with q2 and x2v extended with z->a. // The resultant will not vanish again because // 0 is not a root of the polynomial defining a. polynomial_ref q2(pm()); polynomial_ref z_p(pm()); // z poly polynomial_ref xi_p(pm()); // x^i poly polynomial_ref zxi_p(pm()); // z*x^i SASSERT(i >= 1); q2 = convert(ext_pm, p_prime, pm(), x, static_cast(i-1)); xi_p = pm().mk_polynomial(x, i); z_p = pm().mk_polynomial(z); q2 = z_p*xi_p + q2; TRACE("isolate_roots", tout << "invoking isolate_roots with q2:\n" << q2 << "\n"; tout << "z: x" << z << " -> "; display_root(tout, a); tout << "\n";); // extend x2p with z->a ext_var2num ext_x2v(m_wrapper, x2v, z, a); isolate_roots(q2, ext_x2v, roots, true /* nested call */); } } else if (ext_pm.is_const(q)) { // q does not have any roots, so p_prime also does not have roots at x2v. TRACE("isolate_roots", tout << "q is the constant polynomial, so p does not contain any roots at x2v\n";); } else { SASSERT(is_univariate(q)); isolate_roots(q, roots); // some roots of q may not be roots of p_prime filter_roots(p_prime, x2v, x, roots); } } int eval_at_mpbq(polynomial_ref const & p, polynomial::var2anum const & x2v, polynomial::var x, mpbq const & v) { scoped_mpq qv(qm()); to_mpq(qm(), v, qv); scoped_anum av(m_wrapper); set(av, qv); ext_var2num ext_x2v(m_wrapper, x2v, x, av); return eval_sign_at(p, ext_x2v); } // Make sure that lower and upper of prev and curr don't touch each other void separate(numeral & prev, numeral & curr) { SASSERT(lt(prev, curr)); if (prev.is_basic()) { if (curr.is_basic()) { // do nothing } else { while (bqm().le(lower(curr.to_algebraic()), basic_value(prev))) { refine(curr); if (curr.is_basic()) break; // curr became basic } } } else { if (curr.is_basic()) { while (bqm().ge(upper(prev.to_algebraic()), basic_value(curr))) { refine(prev); if (prev.is_basic()) break; } } else { while (bqm().ge(upper(prev.to_algebraic()), lower(curr.to_algebraic()))) { refine(prev); refine(curr); if (prev.is_basic() || curr.is_basic()) break; } } } } void int_lt(numeral const & a, numeral & b) { scoped_mpz v(qm()); if (a.is_basic()) { qm().floor(basic_value(a), v); qm().dec(v); } else { bqm().floor(qm(), lower(a.to_algebraic()), v); } m_wrapper.set(b, v); } void int_gt(numeral const & a, numeral & b) { scoped_mpz v(qm()); if (a.is_basic()) { qm().ceil(basic_value(a), v); qm().inc(v); } else { bqm().ceil(qm(), upper(a.to_algebraic()), v); } m_wrapper.set(b, v); } // Select a numeral between prev and curr. // Pre: prev < curr void select(numeral & prev, numeral & curr, numeral & result) { TRACE("algebraic_select", tout << "prev: "; display_interval(tout, prev); tout << "\n"; tout << "curr: "; display_interval(tout, curr); tout << "\n";); SASSERT(lt(prev, curr)); separate(prev, curr); scoped_mpbq w(bqm()); if (prev.is_basic()) { if (curr.is_basic()) bqm().select_small_core(qm(), basic_value(prev), basic_value(curr), w); else bqm().select_small_core(qm(), basic_value(prev), lower(curr.to_algebraic()), w); } else { if (curr.is_basic()) bqm().select_small_core(qm(), upper(prev.to_algebraic()), basic_value(curr), w); else bqm().select_small_core(upper(prev.to_algebraic()), lower(curr.to_algebraic()), w); } scoped_mpq qw(qm()); to_mpq(qm(), w, qw); set(result, qw); } // Similar to ext_var2num but all variables that are not mapped by x2v are mapped to the same value. struct ext2_var2num : public polynomial::var2anum { manager & m_am; polynomial::var2anum const & m_x2v; anum const & m_v; ext2_var2num(manager & am, polynomial::var2anum const & x2v, anum const & v): m_am(am), m_x2v(x2v), m_v(v) { } virtual manager & m() const { return m_am; } virtual bool contains(polynomial::var x) const { return true; } virtual anum const & operator()(polynomial::var x) const { if (m_x2v.contains(x)) return m_x2v(x); else return m_v; } }; #define DEFAULT_PRECISION 2 void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector & signs) { isolate_roots(p, x2v, roots); unsigned num_roots = roots.size(); if (num_roots == 0) { anum zero; ext2_var2num ext_x2v(m_wrapper, x2v, zero); int s = eval_sign_at(p, ext_x2v); signs.push_back(s); } else { TRACE("isolate_roots_bug", tout << "p: " << p << "\n"; polynomial::var_vector xs; p.m().vars(p, xs); for (unsigned i = 0; i < xs.size(); i++) { if (x2v.contains(xs[i])) { tout << "x" << xs[i] << " -> "; display_root(tout, x2v(xs[i])); tout << " "; display_interval(tout, x2v(xs[i])); tout << "\n"; } } for (unsigned i = 0; i < roots.size(); i++) { tout << "root[i]: "; display_root(tout, roots[i]); tout << "\n"; }); for (unsigned i = 0; i < num_roots; i++) refine_until_prec(roots[i], DEFAULT_PRECISION); scoped_anum w(m_wrapper); int_lt(roots[0], w); TRACE("isolate_roots_bug", tout << "w: "; display_root(tout, w); tout << "\n";); { ext2_var2num ext_x2v(m_wrapper, x2v, w); int s = eval_sign_at(p, ext_x2v); SASSERT(s != 0); signs.push_back(s); } for (unsigned i = 1; i < num_roots; i++) { numeral & prev = roots[i-1]; numeral & curr = roots[i]; select(prev, curr, w); ext2_var2num ext_x2v(m_wrapper, x2v, w); int s = eval_sign_at(p, ext_x2v); SASSERT(s != 0); signs.push_back(s); } int_gt(roots[num_roots - 1], w); { ext2_var2num ext_x2v(m_wrapper, x2v, w); int s = eval_sign_at(p, ext_x2v); SASSERT(s != 0); signs.push_back(s); } } } void display_root(std::ostream & out, numeral const & a) { if (is_zero(a)) { out << "(#, 1)"; // first root of polynomial # } else if (a.is_basic()) { mpq const & v = basic_value(a); scoped_mpz neg_n(qm()); qm().set(neg_n, v.numerator()); qm().neg(neg_n); mpz const coeffs[2] = { neg_n.get(), v.denominator() }; out << "("; upm().display(out, 2, coeffs, "#"); out << ", 1)"; // first root of the polynomial d*# - n } else { algebraic_cell * c = a.to_algebraic(); out << "("; upm().display(out, c->m_p_sz, c->m_p, "#"); if (c->m_i == 0) { // undefined c->m_i = upm().get_root_id(c->m_p_sz, c->m_p, lower(c)) + 1; } SASSERT(c->m_i > 0); out << ", " << c->m_i; out << ")"; } } void display_mathematica(std::ostream & out, numeral const & a) { if (a.is_basic()) { qm().display(out, basic_value(a)); } else { algebraic_cell * c = a.to_algebraic(); out << "Root["; upm().display(out, c->m_p_sz, c->m_p, "#1", true); if (c->m_i == 0) { // undefined c->m_i = upm().get_root_id(c->m_p_sz, c->m_p, lower(c)) + 1; } SASSERT(c->m_i > 0); out << " &, " << c->m_i << "]"; } } void display_root_smt2(std::ostream & out, numeral const & a) { if (is_zero(a)) { out << "(root-obj x 1)"; } else if (a.is_basic()) { mpq const & v = basic_value(a); scoped_mpz neg_n(qm()); qm().set(neg_n, v.numerator()); qm().neg(neg_n); mpz const coeffs[2] = { neg_n.get(), v.denominator() }; out << "(root-obj "; upm().display_smt2(out, 2, coeffs, "x"); out << " 1)"; // first root of the polynomial d*# - n } else { algebraic_cell * c = a.to_algebraic(); out << "(root-obj "; upm().display_smt2(out, c->m_p_sz, c->m_p, "x"); if (c->m_i == 0) { // undefined c->m_i = upm().get_root_id(c->m_p_sz, c->m_p, lower(c)) + 1; } SASSERT(c->m_i > 0); out << " " << c->m_i; out << ")"; } } void display_interval(std::ostream & out, numeral const & a) { if (a.is_basic()) { out << "["; qm().display(out, basic_value(a)); out << ", "; qm().display(out, basic_value(a)); out << "]"; } else { bqim().display(out, a.to_algebraic()->m_interval); } } bool get_interval(numeral const & a, mpbq & l, mpbq & u, unsigned precision) { SASSERT(!a.is_basic()); algebraic_cell * c = a.to_algebraic(); mpbqi const & i = c->m_interval; bqm().set(l, i.lower()); bqm().set(u, i.upper()); // the precision on refine is base 2 return upm().refine(c->m_p_sz, c->m_p, bqm(), l, u, precision * 4); } void display_decimal(std::ostream & out, numeral const & a, unsigned precision) { if (a.is_basic()) { qm().display_decimal(out, basic_value(a), precision); } else { scoped_mpbq l(bqm()), u(bqm()); if (get_interval(a, l, u, precision)) { // this is a hack... fix it bqm().display_decimal(out, u, precision); } else { // actual root was found bqm().display_decimal(out, l, precision); } } } void get_lower(numeral const & a, mpq & l, unsigned precision) { if (a.is_basic()) { qm().set(l, basic_value(a)); } else { scoped_mpbq _l(bqm()), _u(bqm()); get_interval(a, _l, _u, precision); to_mpq(qm(), _l, l); } } void get_upper(numeral const & a, mpq & u, unsigned precision) { if (a.is_basic()) { qm().set(u, basic_value(a)); } else { scoped_mpbq _l(bqm()), _u(bqm()); get_interval(a, _l, _u, precision); to_mpq(qm(), _u, u); } } }; manager::manager(unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { m_own_allocator = false; m_allocator = a; if (m_allocator == 0) { m_own_allocator = true; m_allocator = alloc(small_object_allocator, "algebraic"); } m_imp = alloc(imp, *this, m, p, *m_allocator); } manager::~manager() { dealloc(m_imp); if (m_own_allocator) dealloc(m_allocator); } void manager::updt_params(params_ref const & p) { } void manager::set_cancel(bool f) { m_imp->set_cancel(f); } unsynch_mpq_manager & manager::qm() const { return m_imp->qm(); } mpbq_manager & manager::bqm() const { return m_imp->bqm(); } void manager::del(numeral & a) { return m_imp->del(a); } void manager::reset(numeral & a) { return m_imp->reset(a); } bool manager::is_zero(numeral const & a) { return m_imp->is_zero(const_cast(a)); } bool manager::is_pos(numeral const & a) { return m_imp->is_pos(const_cast(a)); } bool manager::is_neg(numeral const & a) { return m_imp->is_neg(const_cast(a)); } bool manager::is_rational(numeral const & a) { return m_imp->is_rational(const_cast(a)); } bool manager::is_int(numeral const & a) { return m_imp->is_int(const_cast(a)); } unsigned manager::degree(numeral const & a) { return m_imp->degree(const_cast(a)); } void manager::to_rational(numeral const & a, mpq & r) { return m_imp->to_rational(const_cast(a), r); } void manager::to_rational(numeral const & a, rational & r) { return m_imp->to_rational(const_cast(a), r); } void manager::swap(numeral & a, numeral & b) { return m_imp->swap(a, b); } void manager::int_lt(numeral const & a, numeral & b) { m_imp->int_lt(const_cast(a), b); } void manager::int_gt(numeral const & a, numeral & b) { m_imp->int_gt(const_cast(a), b); } void manager::select(numeral const & prev, numeral const & curr, numeral & result) { m_imp->select(const_cast(prev), const_cast(curr), result); } void manager::set(numeral & a, int n) { scoped_mpq _n(qm()); qm().set(_n, n); set(a, _n); } void manager::set(numeral & a, mpz const & n) { scoped_mpq _n(qm()); qm().set(_n, n); set(a, _n); } void manager::set(numeral & a, mpq const & n) { m_imp->set(a, n); } void manager::set(numeral & a, numeral const & n) { m_imp->set(a, n); } void manager::isolate_roots(polynomial_ref const & p, numeral_vector & roots) { m_imp->isolate_roots(p, roots); } void manager::isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots) { m_imp->isolate_roots(p, x2v, roots); } void manager::isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector & signs) { m_imp->isolate_roots(p, x2v, roots, signs); } void manager::mk_root(polynomial_ref const & p, unsigned i, numeral & r) { m_imp->mk_root(p, i, r); } void manager::mk_root(sexpr const * p, unsigned i, numeral & r) { m_imp->mk_root(p, i, r); } void manager::root(numeral const & a, unsigned k, numeral & b) { m_imp->root(const_cast(a), k, b); } void manager::power(numeral const & a, unsigned k, numeral & b) { TRACE("anum_detail", display_root(tout, a); tout << "^" << k << "\n";); m_imp->power(const_cast(a), k, b); TRACE("anum_detail", tout << "^ result: "; display_root(tout, b); tout << "\n";); } void manager::add(numeral const & a, numeral const & b, numeral & c) { TRACE("anum_detail", display_root(tout, a); tout << " + "; display_root(tout, b); tout << "\n";); m_imp->add(const_cast(a), const_cast(b), c); TRACE("anum_detail", tout << "+ result: "; display_root(tout, c); tout << "\n";); } void manager::add(numeral const & a, mpz const & b, numeral & c) { scoped_anum tmp(*this); set(tmp, b); add(a, tmp, c); } void manager::sub(numeral const & a, numeral const & b, numeral & c) { TRACE("anum_detail", display_root(tout, a); tout << " - "; display_root(tout, b); tout << "\n";); m_imp->sub(const_cast(a), const_cast(b), c); TRACE("anum_detail", tout << "- result: "; display_root(tout, c); tout << "\n";); } void manager::mul(numeral const & a, numeral const & b, numeral & c) { TRACE("anum_detail", display_root(tout, a); tout << " * "; display_root(tout, b); tout << "\n";); m_imp->mul(const_cast(a), const_cast(b), c); TRACE("anum_detail", tout << "* result: "; display_root(tout, c); tout << "\n";); } void manager::div(numeral const & a, numeral const & b, numeral & c) { m_imp->div(const_cast(a), const_cast(b), c); } void manager::neg(numeral & a) { m_imp->neg(a); } void manager::inv(numeral & a) { m_imp->inv(a); } int manager::compare(numeral const & a, numeral const & b) { return m_imp->compare(const_cast(a), const_cast(b)); } bool manager::eq(numeral const & a, numeral const & b) { return m_imp->eq(const_cast(a), const_cast(b)); } bool manager::eq(numeral const & a, mpq const & b) { return m_imp->eq(const_cast(a), b); } bool manager::eq(numeral const & a, mpz const & b) { scoped_mpq _b(qm()); qm().set(_b, b); return eq(const_cast(a), _b); } bool manager::lt(numeral const & a, numeral const & b) { return m_imp->lt(const_cast(a), const_cast(b)); } bool manager::lt(numeral const & a, mpq const & b) { return m_imp->lt(const_cast(a), b); } bool manager::lt(numeral const & a, mpz const & b) { scoped_mpq _b(qm()); qm().set(_b, b); return lt(const_cast(a), _b); } bool manager::gt(numeral const & a, mpq const & b) { return m_imp->gt(const_cast(a), b); } bool manager::gt(numeral const & a, mpz const & b) { scoped_mpq _b(qm()); qm().set(_b, b); return gt(const_cast(a), _b); } void manager::get_polynomial(numeral const & a, svector & r) { m_imp->get_polynomial(a, r); } void manager::get_lower(numeral const & a, mpbq & l) { SASSERT(!is_rational(a)); bqm().set(l, a.to_algebraic()->m_interval.lower()); } void manager::get_lower(numeral const & a, mpq & l) { scoped_mpbq bq(bqm()); get_lower(a, bq); to_mpq(qm(), bq, l); } void manager::get_lower(numeral const & a, rational & l) { scoped_mpq q(m_imp->qm()); get_lower(a, q); l = rational(q); } void manager::get_lower(numeral const & a, mpq & l, unsigned precision) { m_imp->get_lower(a, l, precision); } void manager::get_lower(numeral const & a, rational & l, unsigned precision) { scoped_mpq _l(qm()); m_imp->get_lower(a, _l, precision); l = rational(_l); } void manager::get_upper(numeral const & a, mpbq & u) { SASSERT(!is_rational(a)); bqm().set(u, a.to_algebraic()->m_interval.upper()); } void manager::get_upper(numeral const & a, mpq & u) { scoped_mpbq bq(bqm()); get_upper(a, bq); to_mpq(qm(), bq, u); } void manager::get_upper(numeral const & a, rational & u) { scoped_mpq q(m_imp->qm()); get_upper(a, q); u = rational(q); } void manager::get_upper(numeral const & a, mpq & l, unsigned precision) { m_imp->get_upper(a, l, precision); } void manager::get_upper(numeral const & a, rational & l, unsigned precision) { scoped_mpq _l(qm()); m_imp->get_upper(a, _l, precision); l = rational(_l); } int manager::eval_sign_at(polynomial_ref const & p, polynomial::var2anum const & x2v) { SASSERT(&(x2v.m()) == this); return m_imp->eval_sign_at(p, x2v); } void manager::display_interval(std::ostream & out, numeral const & a) const { m_imp->display_interval(out, a); } void manager::display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { m_imp->display_decimal(out, a, precision); } void manager::display_root(std::ostream & out, numeral const & a) const { m_imp->display_root(out, a); } void manager::display_mathematica(std::ostream & out, numeral const & a) const { m_imp->display_mathematica(out, a); } void manager::display_root_smt2(std::ostream & out, numeral const & a) const { m_imp->display_root_smt2(out, a); } void manager::reset_statistics() { m_imp->reset_statistics(); } void manager::collect_statistics(statistics & st) const { m_imp->collect_statistics(st); } }; z3-z3-4.4.1/src/math/polynomial/algebraic_numbers.h000066400000000000000000000367011260446376700221540ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: algebraic_numbers.h Abstract: Real Algebraic Numbers Author: Leonardo (leonardo) 2011-11-22 Notes: --*/ #ifndef ALGEBRAIC_NUMBERS_H_ #define ALGEBRAIC_NUMBERS_H_ #include"rational.h" #include"mpq.h" #include"polynomial.h" #include"z3_exception.h" #include"scoped_numeral.h" #include"scoped_numeral_vector.h" #include"tptr.h" #include"statistics.h" #include"params.h" class small_object_allocator; class mpbq_manager; class mpbq; namespace algebraic_numbers { class anum; class manager; class algebraic_exception : public default_exception { public: algebraic_exception(char const * msg):default_exception(msg) {} }; class manager { public: struct imp; private: imp * m_imp; small_object_allocator * m_allocator; bool m_own_allocator; public: static bool precise() { return true; } static bool field() { return true; } typedef anum numeral; typedef svector numeral_vector; typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; manager(unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0); ~manager(); static void get_param_descrs(param_descrs & r); static void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } void set_cancel(bool f); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } void updt_params(params_ref const & p); unsynch_mpq_manager & qm() const; mpbq_manager & bqm() const; void del(numeral & a); /** \brief a <- 0 */ void reset(numeral & a); /** \brief Return true if a is zero. */ bool is_zero(numeral const & a); /** \brief Return true if a is positive. */ bool is_pos(numeral const & a); /** \brief Return true if a is negative. */ bool is_neg(numeral const & a); /** \brief Return true if a is a rational number. */ bool is_rational(numeral const & a); /** \brief Return true if a is an integer. */ bool is_int(numeral const & a); /** \brief Degree of the algebraic number. That is, degree of the polynomial that is used to encode \c a. */ unsigned degree(numeral const & a); /** \brief Convert a into a rational number. \pre is_rational(a) */ void to_rational(numeral const & a, mpq & r); /** \brief Convert a into a rational number. \pre is_rational(a) */ void to_rational(numeral const & a, rational & r); /** \brief a <- n */ void set(numeral & a, int n); void set(numeral & a, mpz const & n); void set(numeral & a, mpq const & n); void set(numeral & a, numeral const & n); void swap(numeral & a, numeral & b); /** \brief Store in b an integer value smaller than 'a'. Remark: this is not the floor, but b <= floor(a) */ void int_lt(numeral const & a, numeral & b); /** \brief Store in b an integer value bigger than 'a' Remark: this is not the ceil, but b >= ceil(a) */ void int_gt(numeral const & a, numeral & b); /** \brief Store in result a value in the interval (prev, next) \pre lt(pre,v next) */ void select(numeral const & prev, numeral const & curr, numeral & result); /** \brief Isolate the roots of (an univariate polynomial) p, and store them as algebraic numbers in \c root. That is, p is in Z[x]. */ void isolate_roots(polynomial_ref const & p, numeral_vector & roots); /** \brief Isolate the roots of a multivariate polynomial p such that all but one variable of p is fixed by x2v, and store them as algebraic numbers in \c root. That is, we are viewing p as a polynomial in Z[y_1, ..., y_n][x]: q_n(y_1, ..., y_n)x^n + ... + q_1(y_1, ..., y_n)*x + q_0 And we are returning the roots of q_n(x2v(y_1), ..., x2v(y_n))x^n + ... + q_1(x2v(y_1), ..., x2v(y_n))*x + q_0 */ void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots); /** \brief Isolate the roots of the given polynomial, and compute its sign between them. */ void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector & signs); /** \brief Store in r the i-th root of p. This method throws an exception if p does not have at least i roots. This method is not really used in the nonlinear procedure. It is mainly used for debugging purposes, and creating regression tests \pre i > 0 */ void mk_root(polynomial_ref const & p, unsigned i, numeral & r); /** \brief Store in r the i-th root of p. This method throws an exception if the s-expression p does not represent an univariate polynomial, of if p does not have at least i roots. This method is not really used in the nonlinear procedure. It is mainly used for debugging purposes, and "reading" root objects in the SMT 2.0 front-end. \pre i > 0 */ void mk_root(sexpr const * p, unsigned i, numeral & r); /** \brief Return a^{1/k} Throws an exception if the result is not a real. That is, (a is negative and k is even) or (k is zero). */ void root(numeral const & a, unsigned k, numeral & b); /** \brief Return a^k Throws an exception if 0^0. */ void power(numeral const & a, unsigned k, numeral & b); /** \brief c <- a + b */ void add(numeral const & a, numeral const & b, numeral & c); void add(numeral const & a, mpz const & b, numeral & c); /** \brief c <- a - b */ void sub(numeral const & a, numeral const & b, numeral & c); /** \brief c <- a * b */ void mul(numeral const & a, numeral const & b, numeral & c); /** \brief a <- -a */ void neg(numeral & a); /** \brief a <- 1/a if a != 0 */ void inv(numeral & a); /** \brief c <- a/b if b != 0 */ void div(numeral const & a, numeral const & b, numeral & c); /** Return -1 if a < b Return 0 if a == b Return 1 if a > b */ int compare(numeral const & a, numeral const & b); /** \brief a == b */ bool eq(numeral const & a, numeral const & b); bool eq(numeral const & a, mpq const & b); bool eq(numeral const & a, mpz const & b); /** \brief a != b */ bool neq(numeral const & a, numeral const & b) { return !eq(a, b); } bool neq(numeral const & a, mpq const & b) { return !eq(a, b); } bool neq(numeral const & a, mpz const & b) { return !eq(a, b); } /** \brief a < b */ bool lt(numeral const & a, numeral const & b); bool lt(numeral const & a, mpq const & b); bool lt(numeral const & a, mpz const & b); /** \brief a > b */ bool gt(numeral const & a, numeral const & b) { return lt(b, a); } bool gt(numeral const & a, mpq const & b); bool gt(numeral const & a, mpz const & b); /** \brief a <= b */ bool le(numeral const & a, numeral const & b) { return !gt(a, b); } bool le(numeral const & a, mpq const & b) { return !gt(a, b); } bool le(numeral const & a, mpz const & b) { return !gt(a, b); } /** \brief a >= b */ bool ge(numeral const & a, numeral const & b) { return !lt(a, b); } bool ge(numeral const & a, mpq const & b) { return !lt(a, b); } bool ge(numeral const & a, mpz const & b) { return !lt(a, b); } /** \brief Evaluate the sign of a multivariate polynomial p(x_1, ..., x_n) at assignment x2v: [x_1 -> alpha_1, ..., x_n -> alpha_n]. \remark forall variable x in p, we have that x2v.contains(x) is true Return negative number if p(alpha_1, ..., alpha_n) < 0 Return 0 if p(alpha_1, ..., alpha_n) == 0 Return positive number if p(alpha_1, ..., alpha_n) > 0 */ int eval_sign_at(polynomial_ref const & p, polynomial::var2anum const & x2v); void get_polynomial(numeral const & a, svector & r); // Procedures for getting lower and upper bounds for irrational numbers void get_lower(numeral const & a, mpbq & l); void get_lower(numeral const & a, mpq & l); void get_lower(numeral const & a, rational & l); void get_lower(numeral const & a, mpq & l, unsigned precision); void get_lower(numeral const & a, rational & l, unsigned precision); void get_upper(numeral const & a, mpbq & u); void get_upper(numeral const & a, mpq & u); void get_upper(numeral const & a, rational & u); void get_upper(numeral const & a, mpq & l, unsigned precision); void get_upper(numeral const & a, rational & l, unsigned precision); /** \brief Display algebraic number as a rational if is_rational(n) Otherwise, display it as an interval. */ void display_interval(std::ostream & out, numeral const & a) const; /** \brief Display algebraic number in decimal notation. A question mark is added based on the precision requested. */ void display_decimal(std::ostream & out, numeral const & a, unsigned precision = 10) const; /** \brief Display algebraic number as a root object: (p, i) That is, 'a' is the i-th root of p. */ void display_root(std::ostream & out, numeral const & a) const; /** \brief Display algebraic number as a root object in SMT 2.0 style: (root-obj p i) That is, 'a' is the i-th root of p. */ void display_root_smt2(std::ostream & out, numeral const & a) const; /** \brief Display algebraic number in Mathematica format. */ void display_mathematica(std::ostream & out, numeral const & a) const; void display(std::ostream & out, numeral const & a) { return display_decimal(out, a); } void reset_statistics(); void collect_statistics(statistics & st) const; }; struct basic_cell; struct algebraic_cell; enum anum_kind { BASIC = 0, ROOT }; class anum { friend struct manager::imp; friend class manager; void * m_cell; anum(basic_cell * cell):m_cell(TAG(void*, cell, BASIC)) {} anum(algebraic_cell * cell):m_cell(TAG(void*, cell, ROOT)) {} bool is_basic() const { return GET_TAG(m_cell) == BASIC; } basic_cell * to_basic() const { SASSERT(is_basic()); return UNTAG(basic_cell*, m_cell); } algebraic_cell * to_algebraic() const { SASSERT(!is_basic()); return UNTAG(algebraic_cell*, m_cell); } public: anum():m_cell(0) {} }; }; typedef algebraic_numbers::manager anum_manager; typedef algebraic_numbers::manager::numeral anum; typedef algebraic_numbers::manager::numeral_vector anum_vector; typedef algebraic_numbers::manager::scoped_numeral scoped_anum; typedef algebraic_numbers::manager::scoped_numeral_vector scoped_anum_vector; #define AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \ inline bool EXTERNAL(scoped_anum const & a, TYPE const & b) { \ anum_manager & m = a.m(); \ scoped_anum _b(m); \ m.set(_b, b); \ return m.INTERNAL(a, _b); \ } #define AN_MK_COMPARISON(EXTERNAL, INTERNAL) \ AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \ AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \ AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpq) AN_MK_COMPARISON(operator==, eq); AN_MK_COMPARISON(operator!=, neq); AN_MK_COMPARISON(operator<, lt); AN_MK_COMPARISON(operator<=, le); AN_MK_COMPARISON(operator>, gt); AN_MK_COMPARISON(operator>=, ge); #undef AN_MK_COMPARISON #undef AN_MK_COMPARISON_CORE #define AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \ inline scoped_anum EXTERNAL(scoped_anum const & a, TYPE const & b) { \ anum_manager & m = a.m(); \ scoped_anum _b(m); \ m.set(_b, b); \ scoped_anum r(m); \ m.INTERNAL(a, _b, r); \ return r; \ } #define AN_MK_BINARY(EXTERNAL, INTERNAL) \ AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \ AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \ AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpq) AN_MK_BINARY(operator+, add) AN_MK_BINARY(operator-, sub) AN_MK_BINARY(operator*, mul) AN_MK_BINARY(operator/, div) #undef AN_MK_BINARY #undef AN_MK_BINARY_CORE inline scoped_anum root(scoped_anum const & a, unsigned k) { scoped_anum r(a.m()); a.m().root(a, k, r); return r; } inline scoped_anum power(scoped_anum const & a, unsigned k) { scoped_anum r(a.m()); a.m().power(a, k, r); return r; } inline scoped_anum operator^(scoped_anum const & a, unsigned k) { return power(a, k); } inline bool is_int(scoped_anum const & a) { return a.m().is_int(a); } inline bool is_rational(scoped_anum const & a) { return a.m().is_rational(a); } struct root_obj_pp { anum_manager & m; anum const & n; root_obj_pp(anum_manager & _m, anum const & _n):m(_m), n(_n) {} root_obj_pp(scoped_anum const & _n):m(_n.m()), n(_n.get()) {} }; inline std::ostream & operator<<(std::ostream & out, root_obj_pp const & n) { n.m.display_root(out, n.n); return out; } struct decimal_pp { anum_manager & m; anum const & n; unsigned prec; decimal_pp(anum_manager & _m, anum const & _n, unsigned p):m(_m), n(_n), prec(p) {} decimal_pp(scoped_anum const & _n, unsigned p):m(_n.m()), n(_n.get()), prec(p) {} }; inline std::ostream & operator<<(std::ostream & out, decimal_pp const & n) { n.m.display_decimal(out, n.n, n.prec); return out; } struct interval_pp { anum_manager & m; anum const & n; interval_pp(anum_manager & _m, anum const & _n):m(_m), n(_n) {} interval_pp(scoped_anum const & _n):m(_n.m()), n(_n.get()) {} }; inline std::ostream & operator<<(std::ostream & out, interval_pp const & n) { n.m.display_interval(out, n.n); return out; } #endif z3-z3-4.4.1/src/math/polynomial/linear_eq_solver.h000066400000000000000000000106321260446376700220340ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: linear_eq_solver.h Abstract: Simple equational solver template for any number field. No special optimization, just the basics for solving small systems. It is a solver target to dense system of equations. Main client: Sparse Modular GCD algorithm. Author: Leonardo (leonardo) 2012-01-22 Notes: --*/ #ifndef LINEAR_EQ_SOLVER_H_ #define LINEAR_EQ_SOLVER_H_ template class linear_eq_solver { typedef typename numeral_manager::numeral numeral; numeral_manager & m; unsigned n; // number of variables vector > A; svector b; public: linear_eq_solver(numeral_manager & _m):m(_m), n(0) { SASSERT(m.field()); } ~linear_eq_solver() { flush(); } void flush() { SASSERT(b.size() == A.size()); unsigned sz = A.size(); for (unsigned i = 0; i < sz; i++) { svector & as = A[i]; m.del(b[i]); SASSERT(as.size() == n); for (unsigned j = 0; j < n; j++) m.del(as[j]); } A.reset(); b.reset(); n = 0; } void resize(unsigned _n) { if (n != _n) { flush(); n = _n; for (unsigned i = 0; i < n; i++) { A.push_back(svector()); svector & as = A.back(); for (unsigned j = 0; j < n; j++) { as.push_back(numeral()); } b.push_back(numeral()); } } } void reset() { for (unsigned i = 0; i < n; i++) { svector & A_i = A[i]; for (unsigned j = 0; j < n; j++) { m.set(A_i[j], 0); } m.set(b[i], 0); } } // Set row i with _as[0]*x_0 + ... + _as[n-1]*x_{n-1} = b void add(unsigned i, numeral const * _as, numeral const & _b) { SASSERT(i < n); m.set(b[i], _b); svector & A_i = A[i]; for (unsigned j = 0; j < n; j++) { m.set(A_i[j], _as[j]); } } // Return true if the system of equations has a solution. // Return false if the matrix is singular bool solve(numeral * xs) { for (unsigned k = 0; k < n; k++) { TRACE("linear_eq_solver", tout << "iteration " << k << "\n"; display(tout);); // find pivot unsigned i = k; for (; i < n; i++) { if (!m.is_zero(A[i][k])) break; } if (i == n) return false; // matrix is singular A[k].swap(A[i]); // swap rows svector & A_k = A[k]; numeral & A_k_k = A_k[k]; SASSERT(!m.is_zero(A_k_k)); // normalize row for (unsigned i = k+1; i < n; i++) m.div(A_k[i], A_k_k, A_k[i]); m.div(b[k], A_k_k, b[k]); m.set(A_k_k, 1); // check if first k-1 positions are zero DEBUG_CODE({ for (unsigned i = 0; i < k; i++) { SASSERT(m.is_zero(A_k[i])); } }); // for all rows below pivot for (unsigned i = k+1; i < n; i++) { svector & A_i = A[i]; numeral & A_i_k = A_i[k]; for (unsigned j = k+1; j < n; j++) { m.submul(A_i[j], A_i_k, A_k[j], A_i[j]); } m.submul(b[i], A_i_k, b[k], b[i]); m.set(A_i_k, 0); } } unsigned k = n; while (k > 0) { --k; TRACE("linear_eq_solver", tout << "iteration " << k << "\n"; display(tout);); SASSERT(m.is_one(A[k][k])); // save result m.set(xs[k], b[k]); // back substitute unsigned i = k; while (i > 0) { --i; m.submul(b[i], A[i][k], b[k], b[i]); m.set(A[i][k], 0); } } return true; } void display(std::ostream & out) const { for (unsigned i = 0; i < A.size(); i++) { SASSERT(A[i].size() == n); for (unsigned j = 0; j < n; j++) { m.display(out, A[i][j]); out << " "; } m.display(out, b[i]); out << "\n"; } } }; #endif z3-z3-4.4.1/src/math/polynomial/polynomial.cpp000066400000000000000000010543711260446376700212320ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial.cpp Abstract: Goodies for creating and handling polynomials. Author: Leonardo (leonardo) 2011-11-15 Notes: --*/ #include"polynomial.h" #include"vector.h" #include"chashtable.h" #include"small_object_allocator.h" #include"id_gen.h" #include"buffer.h" #include"scoped_ptr_vector.h" #include"cooperate.h" #include"upolynomial_factorization.h" #include"polynomial_factorization.h" #include"polynomial_primes.h" #include"permutation.h" #include"algebraic_numbers.h" #include"mpzzp.h" #include"timeit.h" #include"linear_eq_solver.h" #include"scoped_numeral_buffer.h" #include"ref_buffer.h" namespace polynomial { factor_params::factor_params(): m_max_p(UINT_MAX), m_p_trials(1), m_max_search_size(UINT_MAX) { } factor_params::factor_params(unsigned max_p, unsigned p_trials, unsigned max_search_size): m_max_p(max_p), m_p_trials(p_trials), m_max_search_size(max_search_size) { } void factor_params::updt_params(params_ref const & p) { m_max_p = p.get_uint("max_prime", UINT_MAX); m_p_trials = p.get_uint("num_primes", 1); m_max_search_size = p.get_uint("max_search_size", UINT_MAX); } void factor_params::get_param_descrs(param_descrs & r) { r.insert("max_search_size", CPK_UINT, "(default: infty) Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter can be used to limit the search space."); r.insert("max_prime", CPK_UINT, "(default: infty) Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter limits the maximum prime number p to be used in the first step."); r.insert("num_primes", CPK_UINT, "(default: 1) Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. The search space may be reduced by factoring the polynomial in different GF(p)'s. This parameter specify the maximum number of finite factorizations to be considered, before lifiting and searching."); } typedef ptr_vector monomial_vector; void var2degree::display(std::ostream & out) const { bool first = true; out << "["; for (unsigned i = 0; i < m_var2degree.size(); ++ i) { if (m_var2degree.size() > 0) { if (!first) { out << ","; } out << "x" << i << "^" << m_var2degree[i]; if (first) { first = false; } } } out << "]"; } // ----------------------------------- // // Monomials // // ----------------------------------- /** \brief power: var + exponent */ class power : public std::pair { public: power():std::pair() {} power(var v, unsigned d):std::pair(v, d) {} var get_var() const { return first; } unsigned degree() const { return second; } unsigned & degree() { return second; } void set_var(var x) { first = x; } struct lt_var { bool operator()(power const & p1, power const & p2) { // CMW: The assertion below does not hold on OSX, because // their implementation of std::sort will try to compare // two items at the same index instead of comparing // the indices directly. I suspect that the purpose of // this assertion was to make sure that there are // no duplicates, so I replaced it with a new assertion at // the end of var_degrees(...). // SASSERT(p1.get_var() != p2.get_var()); return p1.get_var() < p2.get_var(); } }; struct lt_degree { bool operator()(power const & p1, power const & p2) { return p1.degree() < p2.degree(); } }; }; std::ostream & operator<<(std::ostream & out, power const & p) { out << "x" << p.get_var(); if (p.degree() != 1) out << "^" << p.degree(); return out; } /** \brief Return true if the variables in pws are sorted in increasing order and are distinct. */ bool is_valid_power_product(unsigned sz, power const * pws) { for (unsigned i = 1; i < sz; i++) { if (pws[i-1].get_var() >= pws[i].get_var()) return false; } return true; } /** \brief Return total degree of the given power product. */ unsigned power_product_total_degree(unsigned sz, power const * pws) { unsigned r = 0; for (unsigned i = 0; i < sz; i++) r += pws[i].degree(); return r; } /** \brief Monomials (power products) */ class monomial { unsigned m_ref_count; unsigned m_id; //!< unique id unsigned m_total_degree; //!< total degree of the monomial unsigned m_size; //!< number of powers unsigned m_hash; power m_powers[0]; friend class tmp_monomial; void sort() { std::sort(m_powers, m_powers + m_size, power::lt_var()); } public: static unsigned hash_core(unsigned sz, power const * pws) { return string_hash(reinterpret_cast(const_cast(pws)), sz*sizeof(power), 11); } struct hash_proc { unsigned operator()(monomial const * m) const { return m->m_hash; } }; struct eq_proc { bool operator()(monomial const * m1, monomial const * m2) const { if (m1->size() != m2->size() || m1->hash() != m2->hash()) return false; // m_total_degree must not be used as a filter, because it is not updated in temporary monomials. for (unsigned i = 0; i < m1->m_size; i++) { if (m1->get_power(i) != m2->get_power(i)) return false; } return true; } }; static unsigned get_obj_size(unsigned sz) { return sizeof(monomial) + sz * sizeof(power); } monomial(unsigned id, unsigned sz, power const * pws, unsigned h): m_ref_count(0), m_id(id), m_total_degree(0), m_size(sz), m_hash(h) { for (unsigned i = 0; i < sz; i ++) { power const & pw = pws[i]; m_powers[i] = pw; SASSERT(i == 0 || get_var(i) > get_var(i-1)); SASSERT(degree(i) > 0); m_total_degree += degree(i); } } unsigned hash() const { return m_hash; } 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--; } bool is_valid() const { return is_valid_power_product(m_size, m_powers) && power_product_total_degree(m_size, m_powers) == m_total_degree; } unsigned id() const { return m_id; } unsigned size() const { return m_size; } unsigned total_degree() const { return m_total_degree; } power const & get_power(unsigned idx) const { SASSERT(idx < size()); return m_powers[idx]; } power const * get_powers() const { return m_powers; } var get_var(unsigned idx) const { return get_power(idx).get_var(); } unsigned degree(unsigned idx) const { return get_power(idx).degree(); } var max_var() const { if (m_size == 0) return null_var; return get_var(m_size - 1); } unsigned max_var_degree() const { if (m_size == 0) return 0; return degree(m_size - 1); } #define SMALL_MONOMIAL 8 unsigned index_of(var x) const { if (m_size == 0) return UINT_MAX; unsigned last = m_size - 1; if (get_var(last) == x) return last; if (m_size < SMALL_MONOMIAL) { // use linear search for small monomials // search backwards since we usually ask for the degree of "big" variables unsigned i = last; while (i > 0) { --i; if (get_var(i) == x) return i; } return UINT_MAX; } else { // use binary search for big monomials int low = 0; int high = last; while (true) { int mid = low + ((high - low)/2); var x_mid = get_var(mid); if (x > x_mid) { low = mid + 1; } else if (x < x_mid) { high = mid - 1; } else { return mid; } if (low > high) return UINT_MAX; } } } unsigned degree_of(var x) const { unsigned pos = index_of(x); if (pos == UINT_MAX) return 0; return degree(pos); } // Given the subset S of variables that are smaller than x, // then return the maximal one. var max_smaller_than_core(var x) const { if (m_size == 0) return null_var; if (m_size < SMALL_MONOMIAL) { // use linear search for small monomials // search backwards since we usually ask for the degree of "big" variables unsigned i = m_size; while (i > 0) { --i; if (get_var(i) < x) return get_var(i); } return null_var; } else { // use binary search for big monomials int low = 0; int high = m_size-1; if (x <= get_var(low)) { return null_var; } if (x > get_var(high)) { return get_var(high); } if (x == get_var(high)) { SASSERT(high > 0); return get_var(high-1); } while (true) { SASSERT(0 <= low && high < static_cast(m_size)); SASSERT(get_var(low) < x); SASSERT(x < get_var(high)); SASSERT(low < high); if (high == low + 1) { SASSERT(get_var(low) < x); SASSERT(x < get_var(low+1)); return get_var(low); } SASSERT(high > low + 1); int mid = low + ((high - low)/2); SASSERT(low < mid && mid < high); var x_mid = get_var(mid); if (x_mid == x) { SASSERT(low < mid && mid < high && high < static_cast(m_size)); SASSERT(get_var(mid-1) < x && x == get_var(mid)); return get_var(mid-1); } if (x < x_mid) { high = mid; } else { SASSERT(x_mid < x); low = mid; } } } } var max_smaller_than(var x) const { SASSERT(x != null_var); var y = max_smaller_than_core(x); DEBUG_CODE({ bool found = false; for (unsigned i = 0; i < m_size; i++) { if (get_var(i) < x) { CTRACE("poly_bug", !(y != null_var && get_var(i) <= y), tout << "m: "; display(tout); tout << "\n"; tout << "x: " << x << "\n"; tout << "y: " << y << "\n"; tout << "i: " << i << "\n"; tout << "get_var(i): " << get_var(i) << "\n";); SASSERT(y != null_var && get_var(i) <= y); } if (get_var(i) == y) found = true; } SASSERT(y == null_var || (y < x && found)); }); return y; } void display(std::ostream & out, display_var_proc const & proc = display_var_proc(), bool use_star = false) const { if (m_size == 0) { out << "1"; return; } for (unsigned i = 0; i < m_size; i++) { if (i > 0) { if (use_star) out << "*"; else out << " "; } proc(out, get_var(i)); if (degree(i) > 1) out << "^" << degree(i); } } void display_smt2(std::ostream & out, display_var_proc const & proc = display_var_proc()) const { if (m_size == 0) { out << "1"; } else if (m_size == 1 && degree(0) == 1) { proc(out, get_var(0)); } else { out << "(*"; for (unsigned i = 0; i < m_size; i++) { var x = get_var(i); unsigned k = degree(i); SASSERT(k > 0); for (unsigned j = 0; j < k; j++) { out << " "; proc(out, x); } } } out << ")"; } bool is_unit() const { return m_size == 0; } /** \brief Return true if the degree of every variable is even. */ bool is_power_of_two() const { for (unsigned i = 0; i < m_size; i++) { if (degree(i) % 2 == 1) return false; } return true; } bool is_square() const { for (unsigned i = 0; i < m_size; i++) { if (degree(i) % 2 != 0) return false; } return true; } void rename(unsigned sz, var const * xs) { for (unsigned i = 0; i < m_size; i++) { power & pw = m_powers[i]; pw.set_var(xs[pw.get_var()]); } sort(); m_hash = hash_core(m_size, m_powers); } }; inline void swap(monomial * & m1, monomial * & m2) { std::swap(m1, m2); } typedef chashtable monomial_table; /** \brief Mapping from monomials to positions. */ class monomial2pos { unsigned_vector m_m2pos; public: unsigned get(monomial const * m) { unsigned id = m->id(); m_m2pos.reserve(id+1, UINT_MAX); return m_m2pos[id]; } void reset(monomial const * m) { unsigned id = m->id(); SASSERT(id < m_m2pos.size()); m_m2pos[id] = UINT_MAX; } void set(monomial const * m, unsigned pos) { unsigned id = m->id(); m_m2pos.reserve(id+1, UINT_MAX); SASSERT(m_m2pos[id] == UINT_MAX); m_m2pos[id] = pos; } /** \brief Save the position of the monomials in p. */ template void set(Poly const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { set(p->m(i), i); } } /** \brief Undo the effects of save_pos. */ template void reset(Poly const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { reset(p->m(i)); } } }; #define TMP_INITIAL_CAPACITY 128 /** \brief Wrapper for temporary monomials. */ class tmp_monomial { monomial * m_ptr; unsigned m_capacity; //!< maximum number of arguments supported by m_ptr; monomial * allocate(unsigned capacity) { void * mem = memory::allocate(monomial::get_obj_size(capacity)); return new (mem) monomial(UINT_MAX, 0, 0, 0); } void deallocate(monomial * ptr, unsigned capacity) { memory::deallocate(ptr); } void increase_capacity(unsigned new_capacity) { SASSERT(new_capacity > m_capacity); deallocate(m_ptr, m_capacity); m_ptr = allocate(new_capacity); m_capacity = new_capacity; } void expand_capacity(unsigned new_capacity) { SASSERT(new_capacity > m_capacity); monomial * new_ptr = allocate(new_capacity); new_ptr->m_size = m_ptr->m_size; memcpy(new_ptr->m_powers, m_ptr->m_powers, sizeof(power)*m_ptr->m_size); deallocate(m_ptr, m_capacity); m_ptr = new_ptr; m_capacity = new_capacity; } public: tmp_monomial(): m_ptr(allocate(TMP_INITIAL_CAPACITY)), m_capacity(TMP_INITIAL_CAPACITY) { } ~tmp_monomial() { deallocate(m_ptr, m_capacity); } void init(unsigned sz, power const * pws) { if (sz > m_capacity) increase_capacity(sz * 2); SASSERT(sz < m_capacity); m_ptr->m_size = sz; if (sz == 0) return; memcpy(m_ptr->m_powers, pws, sizeof(power) * sz); } void reset() { m_ptr->m_size = 0; } unsigned size() const { return m_ptr->m_size; } void push_back(power const & pw) { if (m_ptr->m_size >= m_capacity) expand_capacity(m_ptr->m_size * 2); m_ptr->m_powers[m_ptr->m_size] = pw; m_ptr->m_size++; } monomial * get_ptr() { unsigned sz = m_ptr->m_size; m_ptr->m_hash = monomial::hash_core(sz, m_ptr->m_powers); return m_ptr; } void reserve(unsigned capacity) { if (capacity > m_capacity) increase_capacity(capacity * 2); } void set_size(unsigned sz) { SASSERT(sz <= m_capacity); m_ptr->m_size = sz; } void set_power(unsigned idx, power const & pw) { SASSERT(idx < m_capacity); m_ptr->m_powers[idx] = pw; } power const & get_power(unsigned idx) const { return m_ptr->m_powers[idx]; } power const * get_powers() const { return m_ptr->m_powers; } }; /** \brief Compare m1 and m2 using a lexicographical order Return - -1 if m1 <_lex m2, - 0 if m1 = m2, - 1 if m1 >_lex m2 The biggest variable dominates x3^3 > x3^2 x1^2 > x3 x2^2 x_1 > x1^3 Remark: in out representation the biggest variable is in the last position. */ int lex_compare(monomial const * m1, monomial const * m2) { if (m1 == m2) return 0; int sz1 = m1->size(); int sz2 = m2->size(); int idx1 = sz1 - 1; int idx2 = sz2 - 1; while (idx1 >= 0 && idx2 >= 0) { power const & pw1 = m1->get_power(idx1); power const & pw2 = m2->get_power(idx2); if (pw1.get_var() == pw2.get_var()) { if (pw1.degree() == pw2.degree()) { idx1--; idx2--; continue; } return pw1.degree() < pw2.degree() ? -1 : 1; } return pw1.get_var() > pw2.get_var() ? 1 : -1; } SASSERT(idx1 >= 0 || idx2 >= 0); SASSERT(idx1 < 0 || idx2 < 0); return idx1 < 0 ? -1 : 1; } /** Similar to lex_compare, but min_var is assumed to be the minimal variable. */ int lex_compare2(monomial const * m1, monomial const * m2, var min_var) { if (m1 == m2) return 0; int sz1 = m1->size(); int sz2 = m2->size(); int idx1 = sz1 - 1; int idx2 = sz2 - 1; unsigned min_var_degree1 = 0; unsigned min_var_degree2 = 0; while (idx1 >= 0 && idx2 >= 0) { power const & pw1 = m1->get_power(idx1); power const & pw2 = m2->get_power(idx2); if (pw1.get_var() == min_var) { min_var_degree1 = pw1.degree(); idx1--; if (pw2.get_var() == min_var) { min_var_degree2 = pw2.degree(); idx2--; } continue; } if (pw2.get_var() == min_var) { min_var_degree2 = pw2.degree(); idx2--; continue; } if (pw1.get_var() == pw2.get_var()) { if (pw1.degree() == pw2.degree()) { idx1--; idx2--; continue; } return pw1.degree() < pw2.degree() ? -1 : 1; } return pw1.get_var() > pw2.get_var() ? 1 : -1; } if (idx1 == idx2) { SASSERT(min_var_degree1 != min_var_degree2); return min_var_degree1 < min_var_degree2 ? -1 : 1; } return idx1 < 0 ? -1 : 1; } struct lex_lt2 { var m_min; lex_lt2(var m):m_min(m) {} bool operator()(monomial * m1, monomial * m2) const { TRACE("lex_bug", tout << "min: x" << m_min << "\n"; m1->display(tout); tout << "\n"; m2->display(tout); tout << "\n";); return lex_compare2(m1, m2, m_min) < 0; } }; /** \brief Compare m1 and m2 using a graded lexicographical order \see lex_compare */ int graded_lex_compare(monomial const * m1, monomial const * m2) { unsigned t1 = m1->total_degree(); unsigned t2 = m2->total_degree(); if (t1 == t2) return lex_compare(m1, m2); else return t1 < t2 ? -1 : 1; } /** \brief Compare submonomials m1[start1, end1) and m2[start2, end2) using reverse lexicographical order */ int rev_lex_compare(monomial const * m1, unsigned start1, unsigned end1, monomial const * m2, unsigned start2, unsigned end2) { SASSERT(end1 >= start1); SASSERT(end2 >= start2); unsigned idx1 = end1; unsigned idx2 = end2; while(idx1 > start1 && idx2 > start2) { --idx1; --idx2; power const & pw1 = m1->get_power(idx1); power const & pw2 = m2->get_power(idx2); if (pw1.get_var() == pw2.get_var()) { if (pw1.degree() == pw2.degree()) { // Remark: the submonomials have the same total degree, but they are not equal. So, idx1 > 0 and idx2 > 0. SASSERT(idx1 > start1 && idx2 > start2); continue; } return pw1.degree() > pw2.degree() ? -1 : 1; } return pw1.get_var() > pw2.get_var() ? -1 : 1; } SASSERT(idx1 == start1 || idx2 == start2); if (idx1 == start1) return idx2 == start2 ? 0 : -1; SASSERT(idx2 == start2 && idx1 != start1); return 1; } /** \brief Compare m1 and m2 using reverse lexicographical order. \see lex_compare */ int rev_lex_compare(monomial const * m1, monomial const * m2) { if (m1 == m2) return 0; return rev_lex_compare(m1, 0, m1->size(), m2, 0, m2->size()); } /** \brief Compare m1 and m2 using graded reverse lexicographical order. \see lex_compare */ int graded_rev_lex_compare(monomial const * m1, monomial const * m2) { unsigned t1 = m1->total_degree(); unsigned t2 = m2->total_degree(); if (t1 == t2) return rev_lex_compare(m1, m2); else return t1 < t2 ? -1 : 1; } struct graded_lex_gt { bool operator()(monomial const * m1, monomial const * m2) { return graded_lex_compare(m1, m2) < 0; } }; /** \brief */ class monomial_manager { unsigned m_ref_count; small_object_allocator * m_allocator; bool m_own_allocator; monomial_table m_monomials; id_gen m_mid_gen; // id generator for monomials unsigned m_next_var; monomial * m_unit; tmp_monomial m_mk_tmp; tmp_monomial m_tmp1; tmp_monomial m_tmp2; tmp_monomial m_tmp3; svector m_powers_tmp; public: monomial_manager(small_object_allocator * a = 0) { m_ref_count = 0; m_next_var = 0; if (a == 0) { m_allocator = alloc(small_object_allocator, "polynomial"); m_own_allocator = true; } else { m_allocator = a; m_own_allocator = false; } m_unit = mk_monomial(0, static_cast(0)); inc_ref(m_unit); } ~monomial_manager() { dec_ref(m_unit); CTRACE("polynomial", !m_monomials.empty(), tout << "monomials leaked\n"; monomial_table::iterator it = m_monomials.begin(); monomial_table::iterator end = m_monomials.end(); for (; it != end; ++it) { (*it)->display(tout); tout << "\n"; }); SASSERT(m_monomials.empty()); if (m_own_allocator) dealloc(m_allocator); } void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } small_object_allocator & allocator() { return *m_allocator; } var mk_var() { var r = m_next_var; m_next_var++; return r; } unsigned num_vars() const { return m_next_var; } bool is_valid(var x) const { return x < m_next_var; } void del(monomial * m) { unsigned obj_sz = monomial::get_obj_size(m->size()); m_monomials.erase(m); m_mid_gen.recycle(m->id()); m_allocator->deallocate(obj_sz, m); } void inc_ref(monomial * m) { m->inc_ref(); } void dec_ref(monomial * m) { m->dec_ref(); if (m->ref_count() == 0) del(m); } monomial * mk_unit() { return m_unit; } monomial * mk_monomial(tmp_monomial & tmp) { monomial * tmp_ptr = tmp.get_ptr(); monomial * & m = m_monomials.insert_if_not_there(tmp_ptr); if (m != tmp_ptr) return m; void * mem = m_allocator->allocate(monomial::get_obj_size(tmp_ptr->size())); unsigned id = m_mid_gen.mk(); monomial * r = new (mem) monomial(id, tmp_ptr->size(), tmp_ptr->get_powers(), tmp_ptr->hash()); m = r; SASSERT(m_monomials.contains(r)); SASSERT(*(m_monomials.find_core(r)) == r); return r; } monomial * mk_monomial(unsigned sz, power const * pws) { SASSERT(is_valid_power_product(sz, pws)); m_mk_tmp.init(sz, pws); return mk_monomial(m_mk_tmp); } monomial * convert(monomial const * src) { unsigned sz = src->size(); for (unsigned i = 0; i < sz; i++) { var x = src->get_var(i); while (x >= num_vars()) { mk_var(); } SASSERT(x < num_vars()); } return mk_monomial(src->size(), src->get_powers()); } monomial * mk_monomial(var x) { SASSERT(is_valid(x)); power pw(x, 1); return mk_monomial(1, &pw); } monomial * mk_monomial(var x, unsigned k) { if (k == 0) return m_unit; SASSERT(is_valid(x)); power pw(x, k); return mk_monomial(1, &pw); } monomial * mk_monomial(unsigned sz, var * xs) { if (sz == 0) return m_unit; if (sz == 1) return mk_monomial(xs[0]); m_powers_tmp.reset(); std::sort(xs, xs+sz); SASSERT(is_valid(xs[0])); m_powers_tmp.push_back(power(xs[0], 1)); for (unsigned i = 1; i < sz; i++) { var x = xs[i]; SASSERT(is_valid(x)); power & last = m_powers_tmp.back(); if (x == last.get_var()) last.degree()++; else m_powers_tmp.push_back(power(x, 1)); } return mk_monomial(m_powers_tmp.size(), m_powers_tmp.c_ptr()); } monomial * mul(unsigned sz1, power const * pws1, unsigned sz2, power const * pws2) { SASSERT(is_valid_power_product(sz1, pws1)); SASSERT(is_valid_power_product(sz2, pws2)); tmp_monomial & product_tmp = m_tmp1; product_tmp.reserve(sz1 + sz2); // product has at most sz1 + sz2 powers unsigned i1 = 0, i2 = 0; unsigned j = 0; while (true) { if (i1 == sz1) { // copy 2 for (; i2 < sz2; i2++, j++) product_tmp.set_power(j, pws2[i2]); break; } if (i2 == sz2) { // copy 1 for (; i1 < sz1; i1++, j++) product_tmp.set_power(j, pws1[i1]); break; } power const & pw1 = pws1[i1]; power const & pw2 = pws2[i2]; unsigned v1 = pw1.get_var(); unsigned v2 = pw2.get_var(); if (v1 == v2) { product_tmp.set_power(j, power(v1, pw1.degree() + pw2.degree())); i1++; i2++; } else if (v1 < v2) { product_tmp.set_power(j, pw1); i1++; } else { SASSERT(v1 > v2); product_tmp.set_power(j, pw2); i2++; } j++; } product_tmp.set_size(j); TRACE("monomial_mul_bug", tout << "before mk_monomial\n"; tout << "pws1: "; for (unsigned i = 0; i < sz1; i++) tout << pws1[i] << " "; tout << "\n"; tout << "pws2: "; for (unsigned i = 0; i < sz2; i++) tout << pws2[i] << " "; tout << "\n"; tout << "product_tmp: "; for (unsigned i = 0; i < product_tmp.size(); i++) tout << product_tmp.get_power(i) << " "; tout << "\n";); monomial * r = mk_monomial(product_tmp); TRACE("monomial_mul_bug", tout << "j: " << j << "\n"; tout << "r: "; r->display(tout); tout << "\n"; tout << "pws1: "; for (unsigned i = 0; i < sz1; i++) tout << pws1[i] << " "; tout << "\n"; tout << "pws2: "; for (unsigned i = 0; i < sz2; i++) tout << pws2[i] << " "; tout << "\n"; tout << "product_tmp: "; for (unsigned i = 0; i < product_tmp.size(); i++) tout << product_tmp.get_power(i) << " "; tout << "\n";); SASSERT(r->is_valid()); SASSERT(r->total_degree() == power_product_total_degree(sz1, pws1) + power_product_total_degree(sz2, pws2)); return r; } monomial * mul(monomial const * m1, monomial const * m2) { if (m1 == m_unit) return const_cast(m2); if (m2 == m_unit) return const_cast(m1); return mul(m1->size(), m1->get_powers(), m2->size(), m2->get_powers()); } template bool div_core(unsigned sz1, power const * pws1, unsigned sz2, power const * pws2, tmp_monomial & r) { if (STORE_RESULT) r.reserve(sz1); // r has at most sz1 arguments. unsigned i1 = 0; unsigned i2 = 0; unsigned j = 0; if (sz1 < sz2) return false; // pws2 does not divide pws1 while (true) { if (i2 == sz2) { if (STORE_RESULT) { for (; i1 < sz1; i1++, j++) r.set_power(j, pws1[i1]); r.set_size(j); } return true; } if (i1 == sz1) return false; // pws2 does not divide pws1 power const & pw1 = pws1[i1]; power const & pw2 = pws2[i2]; unsigned v1 = pw1.get_var(); unsigned v2 = pw2.get_var(); if (v1 == v2) { unsigned d1 = pw1.degree(); unsigned d2 = pw2.degree(); if (d1 < d2) return false; // pws2 does not divide pws1 if (STORE_RESULT) { if (d1 > d2) { r.set_power(j, power(v1, d1 - d2)); j++; } } i1++; i2++; } else if (v1 < v2) { if (STORE_RESULT) { r.set_power(j, pw1); j++; } i1++; } else { SASSERT(v1 > v2); return false; // pws2 does not divide pws1 } } } bool div(monomial const * m1, monomial const * m2) { if (m1->total_degree() < m2->total_degree()) return false; if (m1 == m2) return true; return div_core(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), m_tmp1); } bool div(monomial const * m1, monomial const * m2, monomial * & r) { if (m1->total_degree() < m2->total_degree()) return false; if (m1 == m2) { r = m_unit; return true; } tmp_monomial & div_tmp = m_tmp1; if (div_core(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), div_tmp)) { r = mk_monomial(div_tmp); return true; } return false; } /** \brief Compute the gcd of pws1 and pws2, store it in g, and pws1/g in r1, and pws2/g in r2 Return true if the gcd is not 1. If the result is false, then g, r1 and r2 should not be used. */ bool gcd_core(unsigned sz1, power const * pws1, unsigned sz2, power const * pws2, tmp_monomial & g, tmp_monomial & r1, tmp_monomial & r2) { g.reserve(std::min(sz1, sz2)); r1.reserve(sz2); // r1 has at most num_args2 arguments r2.reserve(sz1); // r2 has at most num_args1 arguments bool found = false; unsigned i1 = 0; unsigned i2 = 0; unsigned j1 = 0; unsigned j2 = 0; unsigned j3 = 0; while (true) { if (i1 == sz1) { if (found) { for (; i2 < sz2; i2++, j2++) r2.set_power(j2, pws2[i2]); r1.set_size(j1); r2.set_size(j2); g.set_size(j3); return true; } return false; } if (i2 == sz2) { if (found) { for (; i1 < sz1; i1++, j1++) r1.set_power(j1, pws1[i1]); r1.set_size(j1); r2.set_size(j2); g.set_size(j3); return true; } return false; } power const & pw1 = pws1[i1]; power const & pw2 = pws2[i2]; unsigned v1 = pw1.get_var(); unsigned v2 = pw2.get_var(); if (v1 == v2) { found = true; unsigned d1 = pw1.degree(); unsigned d2 = pw2.degree(); if (d1 > d2) { r1.set_power(j1, power(v1, d1 - d2)); g.set_power(j3, pw2); j1++; j3++; } else if (d2 > d1) { r2.set_power(j2, power(v2, d2 - d1)); g.set_power(j3, pw1); j2++; j3++; } else { SASSERT(d1 == d2); g.set_power(j3, pw1); j3++; } i1++; i2++; } else if (v1 < v2) { r1.set_power(j1, pw1); j1++; i1++; } else { SASSERT(v1 > v2); r2.set_power(j2, pw2); j2++; i2++; } } } monomial * gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { if (gcd_core(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), m_tmp1, m_tmp2, m_tmp3)) { q1 = mk_monomial(m_tmp2); q2 = mk_monomial(m_tmp3); return mk_monomial(m_tmp1); } else { // gcd is one q1 = const_cast(m2); q2 = const_cast(m1); return m_unit; } } bool unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { if (gcd_core(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), m_tmp1, m_tmp2, m_tmp3)) { q1 = mk_monomial(m_tmp2); q2 = mk_monomial(m_tmp3); return true; } return false; } monomial * pw(monomial const * m, unsigned k) { if (k == 0) return m_unit; if (k == 1) return const_cast(m); unsigned sz = m->size(); tmp_monomial & pw_tmp = m_tmp1; pw_tmp.reserve(sz); for (unsigned i = 0; i < sz; i++) pw_tmp.set_power(i, power(m->get_var(i), m->degree(i)*k)); pw_tmp.set_size(sz); return mk_monomial(pw_tmp); } monomial * sqrt(monomial const * m) { SASSERT(m_unit != 0); if (m == m_unit) return m_unit; unsigned sz = m->size(); tmp_monomial & sqrt_tmp = m_tmp1; sqrt_tmp.reserve(sz); for (unsigned i = 0; i < sz; i++) { if (m->degree(i) % 2 == 1) return 0; sqrt_tmp.set_power(i, power(m->get_var(i), m->degree(i) / 2)); } sqrt_tmp.set_size(sz); return mk_monomial(sqrt_tmp); } /** \brief Return m/x^k */ monomial * div_x_k(monomial const * m, var x, unsigned k) { SASSERT(is_valid(x)); unsigned sz = m->size(); tmp_monomial & elim_tmp = m_tmp1; elim_tmp.reserve(sz); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { power const & pw = m->get_power(i); var y = pw.get_var(); if (x != y) { elim_tmp.set_power(j, pw); j++; } else { SASSERT(k <= pw.degree()); unsigned d = pw.degree(); if (k < d) { elim_tmp.set_power(j, power(y, d - k)); j++; } } } elim_tmp.set_size(j); return mk_monomial(elim_tmp); } /** \brief Return m/x^n where n == m->degree_of(x) */ monomial * div_x(monomial const * m, var x) { SASSERT(is_valid(x)); unsigned sz = m->size(); tmp_monomial & elim_tmp = m_tmp1; elim_tmp.reserve(sz); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { power const & pw = m->get_power(i); var y = pw.get_var(); if (x != y) { elim_tmp.set_power(j, pw); j++; } } elim_tmp.set_size(j); return mk_monomial(elim_tmp); } monomial * derivative(monomial const * m, var x) { SASSERT(is_valid(x)); unsigned sz = m->size(); tmp_monomial & derivative_tmp = m_tmp1; derivative_tmp.reserve(sz); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { power const & pw = m->get_power(i); var y = pw.get_var(); if (x == y) { unsigned d = pw.degree(); if (d > 1) { derivative_tmp.set_power(j, power(y, d-1)); j++; } } else { derivative_tmp.set_power(j, pw); j++; } } derivative_tmp.set_size(j); return mk_monomial(derivative_tmp); } void rename(unsigned sz, var const * xs) { SASSERT(m_ref_count <= 1); SASSERT(sz == num_vars()); DEBUG_CODE({ // check whether xs is really a permutation svector found; found.resize(num_vars(), false); for (unsigned i = 0; i < sz; i++) { SASSERT(xs[i] < num_vars()); SASSERT(!found[xs[i]]); found[xs[i]] = true; } }); monomial_table new_table; monomial_table::iterator it = m_monomials.begin(); monomial_table::iterator end = m_monomials.end(); for (; it != end; ++it) { monomial * m = *it; m->rename(sz, xs); SASSERT(!new_table.contains(m)); new_table.insert(m); } m_monomials.swap(new_table); } }; /** We maintain the following invariant: The first monomial m of every non-zero polynomial p contains: 1) the maximal variable x of p, 2) and the degree of x in m is maximal in p. */ class polynomial { public: typedef manager::numeral numeral; private: unsigned m_ref_count; unsigned m_id:31; unsigned m_lex_sorted:1; unsigned m_size; numeral * m_as; monomial ** m_ms; void lex_sort(unsigned start, unsigned end, var x, vector & buckets, unsigned_vector & p) { SASSERT(end > start); unsigned max_degree = 0; for (unsigned i = start, j = 0; i < end; i++, j++) { monomial * m = m_ms[i]; unsigned d = m->degree_of(x); buckets.reserve(d+1); buckets[d].push_back(j); if (d > max_degree) max_degree = d; } p.reset(); unsigned i = max_degree + 1; while (i > 0) { --i; p.append(buckets[i]); buckets[i].reset(); } SASSERT(p.size() == end - start); apply_permutation(p.size(), m_as + start, p.c_ptr()); apply_permutation_core(p.size(), m_ms + start, p.c_ptr()); // p is not needed anymore after this command i = start; while (i < end) { monomial * m = m_ms[i]; unsigned d = m->degree_of(x); if (d == 0) { // x does not occur in m // since we sorted, x should not in the rest // we should find the maximal variable variable smaller than x in [i, end) var y = max_smaller_than(i, end, x); if (y != null_var) lex_sort(i, end, y, buckets, p); return; } unsigned j = i + 1; for (; j < end; j++) { unsigned d_j = m_ms[j]->degree_of(x); SASSERT(d_j <= d); // it is sorted if (d_j < d) break; } SASSERT(j == end || m_ms[j]->degree_of(x) < d); // sort interval [i, j) using the maximal variable y smaller than x if (j > i + 1) { // only need to sort if the interval has more than one element. var y = max_smaller_than(i, j, x); if (y != null_var) lex_sort(i, j, y, buckets, p); } i = j; } } public: 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--; } static unsigned get_obj_size(unsigned n) { return sizeof(polynomial) + n * (sizeof(numeral) + sizeof(monomial*)); } /** \brief Partial order used to implement the polynomial invariant that guarantees that the first monomial contains the maximal variable in the polynomial, and it occurs with maximal degree. Return true if m1 > m2 in this partial order. */ static bool po_gt(monomial const * m1, monomial const * m2) { if (m1->size() == 0) return false; if (m2->size() == 0) return true; if (m1->max_var() < m2->max_var()) return false; if (m1->max_var() > m2->max_var()) return true; SASSERT(m1->max_var() == m2->max_var()); return m1->max_var_degree() > m2->max_var_degree(); } // swap monomials at positions 0 and pos void swap_0_pos(unsigned pos) { if (pos != 0) { swap(m_as[0], m_as[pos]); std::swap(m_ms[0], m_ms[pos]); } } polynomial(mpzzp_manager & nm, unsigned id, unsigned sz, numeral * as, monomial * const * ms, numeral * as_mem, monomial ** ms_mem): m_ref_count(0), m_id(id), m_lex_sorted(false), m_size(sz), m_as(as_mem), m_ms(ms_mem) { if (sz > 0) { unsigned max_pos = 0; for (unsigned i = 0; i < sz; i++) { new (m_as + i) numeral(); // initialize the big number at m_as[i] swap(m_as[i], as[i]); SASSERT(ms[i]->ref_count() > 0); m_ms[i] = ms[i]; if (i > 0 && po_gt(m_ms[i], m_ms[max_pos])) max_pos = i; } swap_0_pos(max_pos); } } // Return the maximal variable y occuring in [m_ms + start, m_ms + end) that is smaller than x var max_smaller_than(unsigned start, unsigned end, var x) { var max = null_var; for (unsigned i = start; i < end; i++) { var y = m_ms[i]->max_smaller_than(x); if (y != null_var && (max == null_var || y > max)) max = y; } return max; } bool lex_sorted() const { return m_lex_sorted; } // Put monomials in lexicographical order void lex_sort(vector & buckets, unsigned_vector & p, mpzzp_manager & nm) { if (m_lex_sorted) return; if (size() <= 1) { m_lex_sorted = true; return; } lex_sort(0, size(), m(0)->max_var(), buckets, p); m_lex_sorted = true; DEBUG_CODE({ for (unsigned i = 0; i < m_size - 1; i++) { CTRACE("poly_bug", lex_compare(m_ms[i], m_ms[i+1]) <= 0, tout << "i: " << i << "\npoly: "; display(tout, nm); tout << "\n";); SASSERT(lex_compare(m_ms[i], m_ms[i+1]) > 0); } }); } /** \brief Make sure that the first monomial contains the maximal variable x occuring in the polynomial, and x occurs with maximal degree. */ void make_first_maximal() { if (m_size <= 1) return; unsigned max_pos = 0; for (unsigned i = 1; i < m_size; i++) { if (po_gt(m_ms[i], m_ms[max_pos])) max_pos = i; } swap_0_pos(max_pos); m_lex_sorted = false; } /** \brief Return the position of the maximal monomial with respect to graded lexicographical order. Return UINT_MAX if polynomial is zero. */ unsigned graded_lex_max_pos() const { if (m_size == 0) return UINT_MAX; unsigned max_pos = 0; for (unsigned i = 1; i < m_size; i++) { if (graded_lex_compare(m_ms[i], m_ms[max_pos]) > 0) max_pos = i; } return max_pos; } /** \brief Return the position of the minimal monomial with respect to graded lexicographical order. Return UINT_MAX if polynomial is zero. */ unsigned graded_lex_min_pos() const { if (m_size == 0) return UINT_MAX; unsigned min_pos = 0; for (unsigned i = 1; i < m_size; i++) { if (graded_lex_compare(m_ms[i], m_ms[min_pos]) < 0) min_pos = i; } return min_pos; } unsigned id() const { return m_id; } unsigned size() const { return m_size; } monomial * m(unsigned idx) const { SASSERT(idx < size()); return m_ms[idx]; } numeral const & a(unsigned idx) const { SASSERT(idx < size()); return m_as[idx]; } numeral & a(unsigned idx) { SASSERT(idx < size()); return m_as[idx]; } numeral const * as() const { return m_as; } bool is_zero() const { return m_size == 0; } void display(std::ostream & out, mpzzp_manager & nm, display_var_proc const & proc = display_var_proc(), bool use_star = false) const { if (is_zero()) { out << "0"; return; } for (unsigned i = 0; i < m_size; i++) { numeral const & a_i = a(i); _scoped_numeral abs_a_i(nm); nm.set(abs_a_i, a_i); nm.abs(abs_a_i); numeral const & a_prime = abs_a_i; if (i > 0) { if (nm.is_neg(a_i)) out << " - "; else out << " + "; } else { if (nm.is_neg(a_i)) out << "- "; } if (m(i)->is_unit()) { out << nm.to_string(a_prime); } else if (nm.is_one(a_prime)) { m(i)->display(out, proc, use_star); } else { out << nm.to_string(a_prime); if (use_star) out << "*"; else out << " "; m(i)->display(out, proc, use_star); } } } static void display_num_smt2(std::ostream & out, mpzzp_manager & nm, numeral const & a) { if (nm.is_neg(a)) { out << "(- "; _scoped_numeral abs_a(nm); nm.set(abs_a, a); nm.neg(abs_a); nm.display(out, abs_a); out << ")"; } else { nm.display(out, a); } } void display_mon_smt2(std::ostream & out, mpzzp_manager & nm, display_var_proc const & proc, unsigned i) const { SASSERT(i < m_size); monomial const * m_i = m(i); numeral const & a_i = a(i); if (m_i->size() == 0) { display_num_smt2(out, nm, a_i); } else if (nm.is_one(a_i)) { m_i->display(out, proc); } else { out << "(* "; display_num_smt2(out, nm, a_i); m_i->display(out, proc); out << ")"; } } void display_smt2(std::ostream & out, mpzzp_manager & nm, display_var_proc const & proc = display_var_proc()) const { if (m_size == 0) { out << "0"; } else if (m_size == 1) { display_mon_smt2(out, nm, proc, 0); } else { out << "(+"; for (unsigned i = 0; i < m_size; i++) { out << " "; display_mon_smt2(out, nm, proc, i); } out << ")"; } } void display(std::ostream & out, mpzzp_manager & nm, bool use_star) const { display(out, nm, display_var_proc(), use_star); } }; manager::factors::factors(manager & _m):m_manager(_m), m_total_factors(0) { m().m().set(m_constant, 1); } manager::factors::~factors() { reset(); m().m().del(m_constant); } void manager::factors::reset() { for (unsigned i = 0; i < m_factors.size(); ++ i) { m().dec_ref(m_factors[i]); } m_factors.reset(); m_degrees.reset(); m_total_factors = 0; m().m().set(m_constant, 1); } void manager::factors::push_back(polynomial * p, unsigned degree) { SASSERT(p != 0 && degree > 0); m_factors.push_back(p); m_degrees.push_back(degree); m_total_factors += degree; m().inc_ref(p); } void manager::factors::multiply(polynomial_ref & out) const { if (m_factors.empty()) { out = m().mk_const(m_constant); } else { // multiply the factors for (unsigned i = 0; i < m_factors.size(); ++ i) { polynomial_ref current(m_factors[i], m()); if (m_degrees[i] > 1) { m().pw(current, m_degrees[i], current); } if (i == 0) { out = current; } else { out = m().mul(out, current); } } // multiply the constant out = m().mul(m_constant, out); } } void manager::factors::display(std::ostream & out) const { out << m().m().to_string(get_constant()); for (unsigned i = 0; i < m_factors.size(); ++ i) { out << " * ("; m_manager.display(out, m_factors[i]); out << ")^" << m_degrees[i]; } } void manager::factors::set_constant(numeral const & constant) { m().m().set(m_constant, constant); } void manager::factors::set_degree(unsigned i, unsigned degree) { SASSERT(i > 0); m_total_factors -= m_degrees[i]; m_total_factors += m_degrees[i] = degree; } polynomial_ref manager::factors::operator[](unsigned i) const { return polynomial_ref(m_factors[i], m()); } unsigned manager::id(monomial const * m) { return m->id(); } unsigned manager::id(polynomial const * p) { return p->id(); } bool manager::is_unit(monomial const * m) { return m->size() == 0; } bool manager::is_zero(polynomial const * p) { return p->size() == 0; } bool manager::is_const(polynomial const * p) { return is_zero(p) || (p->size() == 1 && is_unit(p->m(0))); } bool manager::is_univariate(monomial const * m) { return m->size() <= 1; } bool manager::is_univariate(polynomial const * p) { unsigned sz = p->size(); if (is_const(p)) return true; monomial * m = p->m(0); var x = max_var(p); for (unsigned i = 0; i < sz; i++) { m = p->m(i); if (m->size() == 1 && m->get_var(0) == x) continue; if (m->size() == 0) continue; return false; } return true; } unsigned manager::size(polynomial const * p) { return p->size(); } polynomial::numeral const & manager::coeff(polynomial const * p, unsigned i) { return p->a(i); } polynomial::numeral const & manager::univ_coeff(polynomial const * p, unsigned k) { static numeral zero(0); SASSERT(is_univariate(p)); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (p->m(i)->total_degree() == k) return p->a(i); } return zero; } monomial * manager::get_monomial(polynomial const * p, unsigned i) { return p->m(i); } unsigned manager::total_degree(monomial const * m) { return m->total_degree(); } unsigned manager::size(monomial const * m) { return m->size(); } var manager::get_var(monomial const * m, unsigned i) { return m->get_var(i); } unsigned manager::degree(monomial const * m, unsigned i) { return m->degree(i); } unsigned manager::degree_of(monomial const * m, var x) { return m->degree_of(x); } bool manager::is_linear(monomial const * m) { return m->size() == 0 || (m->size() == 1 && m->degree(0) == 1); } bool manager::is_linear(polynomial const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (!is_linear(p->m(0))) return false; } return true; } unsigned manager::degree(polynomial const * p, var x) { unsigned sz = p->size(); if (sz == 0) return 0; monomial * m = p->m(0); unsigned msz = m->size(); if (msz == 0) return 0; // see polynomial invariant. if (m->get_var(msz - 1) == x) { // x is the maximal variable in p return m->degree(msz - 1); } unsigned r = 0; // use slow (linear) scan. for (unsigned i = 0; i < sz; i++) { unsigned d = p->m(i)->degree_of(x); if (d > r) r = d; } return r; } var manager::max_var(polynomial const * p) { if (p->size() == 0) return null_var; monomial * m = p->m(0); return m->max_var(); } unsigned manager::total_degree(polynomial const * p) { // use linear scan... if it turns out to be too slow, I should cache total_degree in polynomial unsigned r = 0; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { unsigned t = p->m(i)->total_degree(); if (t > r) r = t; } return r; } struct manager::imp { typedef upolynomial::manager up_manager; typedef mpzzp_manager numeral_manager; // refine numeral_manager typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; manager & m_wrapper; numeral_manager m_manager; up_manager m_upm; monomial_manager * m_monomial_manager; polynomial_vector m_polynomials; id_gen m_pid_gen; // id generator for polynomials del_eh * m_del_eh; polynomial * m_zero; numeral m_zero_numeral; polynomial * m_unit_poly; monomial2pos m_m2pos; tmp_monomial m_tmp1; numeral_vector m_rat2numeral; numeral_vector m_tmp_linear_as; monomial_vector m_tmp_linear_ms; unsigned_vector m_degree2pos; bool m_use_sparse_gcd; bool m_use_prs_gcd; volatile bool m_cancel; // Debugging method: check if the coefficients of p are in the numeral_manager. bool consistent_coeffs(polynomial const * p) { scoped_numeral a(m_manager); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { m_manager.set(a, p->a(i)); SASSERT(m_manager.eq(a, p->a(i))); } return true; } /** \brief Divide as by the GCD of as. Return true, if the GCD is not 1. */ static bool normalize_numerals(numeral_manager & m, numeral_vector & as) { unsigned sz = as.size(); if (sz == 0) return false; scoped_numeral g(m); m.gcd(as.size(), as.c_ptr(), g); if (m.is_one(g)) return false; SASSERT(m.is_pos(g)); for (unsigned i = 0; i < sz; i++) { m.div(as[i], g, as[i]); } return true; } /** \brief Som-of-monomials buffer. This a temporary datastructure for building polynomials. The following idiom should be used: Invoke add(...), addmul(...) several times, and then invoke mk() to obtain the final polynomial. */ class som_buffer { imp * m_owner; monomial2pos m_m2pos; numeral_vector m_tmp_as; monomial_vector m_tmp_ms; /** \brief Remove zeros from m_tmp_as & m_tmp_ms. The reference counters of eliminated m_tmp_ms are decremented. m_m2pos is reset. That is for every m in m_tmp_ms, m_m2pos[m->id()] == UINT_MAX */ void remove_zeros(bool normalize) { numeral_manager & mng = m_owner->m_manager; SASSERT(m_tmp_ms.size() == m_tmp_as.size()); unsigned sz = m_tmp_ms.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { monomial * m = m_tmp_ms[i]; m_m2pos.reset(m); if (mng.is_zero(m_tmp_as[i])) { mng.reset(m_tmp_as[i]); m_owner->dec_ref(m_tmp_ms[i]); } else { if (i != j) { SASSERT(m_tmp_ms[j] != m); m_tmp_ms[j] = m; swap(m_tmp_as[j], m_tmp_as[i]); } j++; } } DEBUG_CODE({ for (unsigned i = j; i < sz; i++) { SASSERT(mng.is_zero(m_tmp_as[i])); } }); m_tmp_as.shrink(j); m_tmp_ms.shrink(j); if (normalize) { normalize_numerals(mng, m_tmp_as); } } public: som_buffer():m_owner(0) {} void reset() { if (empty()) return; numeral_manager & mng = m_owner->m_manager; SASSERT(m_tmp_ms.size() == m_tmp_as.size()); unsigned sz = m_tmp_ms.size(); for (unsigned i = 0; i < sz; i++) { monomial * m = m_tmp_ms[i]; m_m2pos.reset(m); mng.reset(m_tmp_as[i]); m_owner->dec_ref(m_tmp_ms[i]); } m_tmp_as.reset(); m_tmp_ms.reset(); } void set_owner(imp * o) { m_owner = o; } unsigned size() const { return m_tmp_ms.size(); } bool empty() const { return m_tmp_ms.empty(); } monomial * m(unsigned i) const { return m_tmp_ms[i]; } numeral const & a(unsigned i) const { return m_tmp_as[i]; } /** \brief Return the position of the maximal monomial with respect to graded lexicographical order. Return UINT_MAX if empty. */ unsigned graded_lex_max_pos() const { numeral_manager & mng = m_owner->m_manager; unsigned max_pos = UINT_MAX; unsigned sz = m_tmp_as.size(); for (unsigned i = 0; i < sz; i++) { if (!mng.is_zero(m_tmp_as[i])) { if (max_pos == UINT_MAX) { max_pos = i; } else { if (graded_lex_compare(m_tmp_ms[i], m_tmp_ms[max_pos]) > 0) max_pos = i; } } } return max_pos; } /** \brief Store a*m*p into the buffer. m_m2pos is updated with the position of the monomials in m_tmp_ms. The reference counter of new monomials added into the buffer is increased. */ template void addmul_core(numeral const & a, monomial const * m, PolyType const * p) { numeral_manager & mng = m_owner->m_manager; if (mng.is_zero(a)) return; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (CheckZeros && mng.is_zero(p->a(i))) continue; monomial * m2 = p->m(i); m2 = m_owner->mul(m, m2); unsigned pos = m_m2pos.get(m2); if (pos == UINT_MAX) { m_m2pos.set(m2, m_tmp_ms.size()); m_tmp_ms.push_back(m2); m_owner->inc_ref(m2); m_tmp_as.push_back(numeral()); mng.mul(a, p->a(i), m_tmp_as.back()); } else { mng.addmul(m_tmp_as[pos], a, p->a(i), m_tmp_as[pos]); } } } void addmul(numeral const & a, monomial const * m, polynomial const * p) { return addmul_core(a, m, p); } void addmul(numeral const & a, monomial const * m, som_buffer const * p) { return addmul_core(a, m, p); } void addmul(numeral const & a, monomial const * m, som_buffer const & p) { return addmul(a, m, &p); } void addmul(numeral const & a, som_buffer const * p) { return addmul(a, m_owner->mk_unit(), p); } void addmul(numeral const & a, som_buffer const & p) { return addmul(a, &p); } void addmul(monomial const * m, som_buffer const * p) { numeral one(1); return addmul(one, m, p); } void addmul(monomial const * m, som_buffer const & p) { return addmul(m, &p); } /** \brief Store p into the buffer. m_m2pos is updated with the position of the monomials in m_tmp_ms. The reference counter of new monomials added into the buffer is increased. */ void add(polynomial const * p) { numeral_manager & mng = m_owner->m_manager; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m2 = p->m(i); unsigned pos = m_m2pos.get(m2); if (pos == UINT_MAX) { m_m2pos.set(m2, m_tmp_ms.size()); m_tmp_ms.push_back(m2); m_owner->inc_ref(m2); m_tmp_as.push_back(numeral()); mng.set(m_tmp_as.back(), p->a(i)); } else { mng.add(m_tmp_as[pos], p->a(i), m_tmp_as[pos]); } } } /** \brief Add 'a*m' into m_tmp_as and m_tmp_ms. m_m2pos is updated with the position of the monomials in m_tmp_ms. The reference counter of m is increased. */ void add(numeral const & a, monomial * m) { numeral_manager & mng = m_owner->m_manager; if (mng.is_zero(a)) return; unsigned pos = m_m2pos.get(m); if (pos == UINT_MAX) { m_m2pos.set(m, m_tmp_ms.size()); m_owner->inc_ref(m); m_tmp_ms.push_back(m); m_tmp_as.push_back(numeral()); mng.set(m_tmp_as.back(), a); } else { mng.add(m_tmp_as[pos], a, m_tmp_as[pos]); } } /** \brief Add 'a' (that is, a*m_unit) into m_tmp_as and m_tmp_ms. m_m2pos is updated with the position of the monomials in m_tmp_ms. The reference counter of m_unit is increased. */ void add(numeral const & a) { add(a, m_owner->mk_unit()); } void sort_graded_lex() { std::sort(m_tmp_ms.begin(), m_tmp_ms.end(), graded_lex_gt()); numeral_vector new_as; unsigned sz = m_tmp_ms.size(); for (unsigned i = 0; i < sz; i++) { monomial * m = m_tmp_ms[i]; unsigned pos = m_m2pos.get(m); new_as.push_back(numeral()); swap(new_as.back(), m_tmp_as[pos]); m_m2pos.reset(m); m_m2pos.set(m, i); } m_tmp_as.swap(new_as); } // For each monomial m // If m contains x^k and k >= x2d[x] and x2d[x] != 0, then set coefficient of m to 0. void mod_d(var2degree const & x2d) { numeral_manager & mng = m_owner->m_manager; unsigned sz = m_tmp_ms.size(); for (unsigned i = 0; i < sz; i++) { if (mng.is_zero(m_tmp_as[i])) continue; monomial * m = m_tmp_ms[i]; unsigned msz = m->size(); unsigned j; for (j = 0; j < msz; j++) { var x = m->get_var(j); unsigned dx = x2d.degree(x); if (dx == 0) continue; if (m->degree(j) >= dx) break; } if (j < msz) { mng.reset(m_tmp_as[i]); } } } polynomial * mk(bool normalize = false) { remove_zeros(normalize); polynomial * p = m_owner->mk_polynomial_core(m_tmp_as.size(), m_tmp_as.c_ptr(), m_tmp_ms.c_ptr()); m_tmp_as.reset(); m_tmp_ms.reset(); return p; } void display(std::ostream & out) const { SASSERT(m_tmp_ms.size() == m_tmp_as.size()); numeral_manager & mng = m_owner->m_manager; for (unsigned i = 0; i < m_tmp_as.size(); i++) { if (i > 0) out << " + "; out << mng.to_string(m_tmp_as[i]) << "*"; m_tmp_ms[i]->display(out); } out << "\n"; } }; class som_buffer_vector { imp * m_owner; ptr_vector m_buffers; void ensure_capacity(unsigned sz) { unsigned old_sz = m_buffers.size(); for (unsigned i = old_sz; i < sz; i++) { som_buffer * new_buffer = alloc(som_buffer); if (m_owner) new_buffer->set_owner(m_owner); m_buffers.push_back(new_buffer); } SASSERT(m_buffers.size() >= sz); } public: som_buffer_vector() { m_owner = 0; } ~som_buffer_vector() { unsigned sz = m_buffers.size(); for (unsigned i = 0; i < sz; i++) { dealloc(m_buffers[i]); } } void set_owner(imp * owner) { SASSERT(m_owner == owner || m_owner == 0); if (m_owner == 0) { m_owner = owner; unsigned sz = m_buffers.size(); for (unsigned i = 0; i < sz; i++) { m_buffers[i]->set_owner(m_owner); } } } som_buffer * operator[](unsigned idx) { ensure_capacity(idx+1); return m_buffers[idx]; } void reset(unsigned sz) { if (sz > m_buffers.size()) sz = m_buffers.size(); for (unsigned i = 0; i < sz; i++) { m_buffers[i]->reset(); } } void reset() { reset(m_buffers.size()); } }; /** \brief Cheap version of som_buffer. In this buffer, each monomial can be added at most once. */ class cheap_som_buffer { imp * m_owner; numeral_vector m_tmp_as; monomial_vector m_tmp_ms; public: cheap_som_buffer():m_owner(0) {} void set_owner(imp * o) { m_owner = o; } bool empty() const { return m_tmp_ms.empty(); } /** \brief Add a*m to the buffer, the content of a is reset. */ void add_reset(numeral & a, monomial * m) { SASSERT(std::find(m_tmp_ms.begin(), m_tmp_ms.end(), m) == m_tmp_ms.end()); numeral_manager & mng = m_owner->m_manager; if (mng.is_zero(a)) return; m_tmp_as.push_back(numeral()); swap(m_tmp_as.back(), a); m_owner->inc_ref(m); m_tmp_ms.push_back(m); } /** \brief Add a*m to the buffer. */ void add(numeral const & a, monomial * m) { SASSERT(std::find(m_tmp_ms.begin(), m_tmp_ms.end(), m) == m_tmp_ms.end()); numeral_manager & mng = m_owner->m_manager; if (mng.is_zero(a)) return; m_tmp_as.push_back(numeral()); mng.set(m_tmp_as.back(), a); m_owner->inc_ref(m); m_tmp_ms.push_back(m); } /** \brief Add a*m*p to the buffer. */ void addmul(numeral const & a, monomial const * m, polynomial const * p) { numeral_manager & mng = m_owner->m_manager; if (mng.is_zero(a)) return; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m2 = p->m(i); m2 = m_owner->mul(m, m2); // m2 is not in m_tmp_ms SASSERT(std::find(m_tmp_ms.begin(), m_tmp_ms.end(), m2) == m_tmp_ms.end()); m_owner->inc_ref(m2); m_tmp_ms.push_back(m2); m_tmp_as.push_back(numeral()); mng.mul(a, p->a(i), m_tmp_as.back()); } } bool normalize() { return normalize_numerals(m_owner->m_manager, m_tmp_as); } void reset() { if (empty()) return; numeral_manager & mng = m_owner->m_manager; unsigned sz = m_tmp_ms.size(); for (unsigned i = 0; i < sz; i++) { mng.del(m_tmp_as[i]); m_owner->dec_ref(m_tmp_ms[i]); } m_tmp_as.reset(); m_tmp_ms.reset(); } polynomial * mk() { polynomial * new_p = m_owner->mk_polynomial_core(m_tmp_as.size(), m_tmp_as.c_ptr(), m_tmp_ms.c_ptr()); m_tmp_as.reset(); m_tmp_ms.reset(); return new_p; } }; som_buffer m_som_buffer; som_buffer m_som_buffer2; cheap_som_buffer m_cheap_som_buffer; cheap_som_buffer m_cheap_som_buffer2; void init() { m_del_eh = 0; m_som_buffer.set_owner(this); m_som_buffer2.set_owner(this); m_cheap_som_buffer.set_owner(this); m_cheap_som_buffer2.set_owner(this); m_zero = mk_polynomial_core(0, 0, 0); m().set(m_zero_numeral, 0); inc_ref(m_zero); numeral one(1); m_unit_poly = mk_const_core(one); inc_ref(m_unit_poly); m_use_sparse_gcd = true; m_use_prs_gcd = false; m_cancel = false; } imp(manager & w, unsynch_mpz_manager & m, monomial_manager * mm): m_wrapper(w), m_manager(m), m_upm(m) { if (mm == 0) mm = alloc(monomial_manager); m_monomial_manager = mm; m_monomial_manager->inc_ref(); init(); } imp(manager & w, unsynch_mpz_manager & m, small_object_allocator * a): m_wrapper(w), m_manager(m), m_upm(m) { m_monomial_manager = alloc(monomial_manager, a); m_monomial_manager->inc_ref(); init(); } ~imp() { dec_ref(m_zero); dec_ref(m_unit_poly); m_som_buffer.reset(); m_som_buffer2.reset(); m_cheap_som_buffer.reset(); m_cheap_som_buffer2.reset(); m_manager.del(m_zero_numeral); m_mgcd_iterpolators.flush(); m_mgcd_skeletons.reset(); DEBUG_CODE({ TRACE("polynomial", tout << "leaked polynomials\n"; for (unsigned i = 0; i < m_polynomials.size(); i++) { if (m_polynomials[i] != 0) { m_polynomials[i]->display(tout, m_manager); tout << "\n"; } }); m_polynomials.reset(); }); SASSERT(m_polynomials.empty()); m_monomial_manager->dec_ref(); } void set_cancel(bool f) { m_cancel = f; m_upm.set_cancel(f); } void checkpoint() { if (m_cancel) { throw polynomial_exception("canceled"); } cooperate("polynomial"); } mpzzp_manager & m() const { return const_cast(this)->m_manager; } manager & pm() const { return m_wrapper; } up_manager & upm() { return m_upm; } monomial_manager & mm() const { return *m_monomial_manager; } var mk_var() { return mm().mk_var(); } unsigned num_vars() const { return mm().num_vars(); } bool is_valid(var x) const { return mm().is_valid(x); } void add_del_eh(del_eh * eh) { eh->m_next = m_del_eh; m_del_eh = eh; } void remove_del_eh(del_eh * eh) { SASSERT(eh != 0); SASSERT(m_del_eh != 0); if (m_del_eh == eh) { m_del_eh = m_del_eh->m_next; } else { del_eh * curr = m_del_eh; while (curr) { if (curr->m_next == eh) { curr->m_next = curr->m_next->m_next; return; } curr = curr->m_next; } UNREACHABLE(); } } void del(polynomial * p) { TRACE("polynomial", tout << "deleting: "; p->display(tout, m_manager); tout << "\n";); if (m_del_eh != 0) { del_eh * curr = m_del_eh; do { (*curr)(p); curr = curr->m_next; } while (curr != 0); } unsigned sz = p->size(); unsigned obj_sz = polynomial::get_obj_size(sz); for (unsigned i = 0; i < sz; i++) { m_manager.del(p->a(i)); dec_ref(p->m(i)); } unsigned id = p->id(); m_pid_gen.recycle(id); m_polynomials[id] = 0; mm().allocator().deallocate(obj_sz, p); } void inc_ref(monomial * m) { mm().inc_ref(m); } void dec_ref(monomial * m) { mm().dec_ref(m); } void inc_ref(polynomial * p) { p->inc_ref(); } void dec_ref(polynomial * p) { p->dec_ref(); if (p->ref_count() == 0) del(p); } vector m_lex_sort_buckets; unsigned_vector m_lex_sort_permutation; void lex_sort(polynomial const * p) { const_cast(p)->lex_sort(m_lex_sort_buckets, m_lex_sort_permutation, m_manager); } struct poly_khasher { unsigned operator()(polynomial const * p) const { return 17; } }; struct poly_chasher { unsigned operator()(polynomial const * p, unsigned idx) const { return hash_u_u(p->m(idx)->hash(), numeral_manager::hash(p->a(idx))); } }; unsigned hash(polynomial const * p) { if (p->size() == 0) return 31; lex_sort(const_cast(p)); return get_composite_hash(p, p->size(), poly_khasher(), poly_chasher()); } polynomial * mk_polynomial_core(unsigned sz, numeral * as, monomial * const * ms) { unsigned obj_sz = polynomial::get_obj_size(sz); void * mem = mm().allocator().allocate(obj_sz); void * as_mem = static_cast(mem) + sizeof(polynomial); void * ms_mem = static_cast(as_mem) + sizeof(numeral)*sz; unsigned id = m_pid_gen.mk(); polynomial * p = new (mem) polynomial(m_manager, id, sz, as, ms, static_cast(as_mem), static_cast(ms_mem)); m_polynomials.reserve(id+1); SASSERT(m_polynomials[id] == 0); m_polynomials[id] = p; return p; } polynomial * mk_zero() { return m_zero; } polynomial * mk_one() { return m_unit_poly; } monomial * mk_unit() { return mm().mk_unit(); } monomial * mk_monomial(tmp_monomial & tmp) { return mm().mk_monomial(tmp); } monomial * mk_monomial(var x) { return mm().mk_monomial(x); } monomial * mk_monomial(var x, unsigned k) { return mm().mk_monomial(x, k); } monomial * mk_monomial(unsigned sz, var * xs) { return mm().mk_monomial(sz, xs); } monomial * mk_monomial(unsigned sz, power const * pws) { return mm().mk_monomial(sz, pws); } monomial * convert(monomial const * src) { return mm().convert(src); } polynomial * mk_const_core(numeral & a) { monomial * u = mk_unit(); inc_ref(u); return mk_polynomial_core(1, &a, &u); } polynomial * mk_const(numeral & a) { if (m_manager.is_zero(a)) return mk_zero(); if (m_manager.is_one(a)) return mk_one(); return mk_const_core(a); } polynomial * mk_const(rational const & a) { SASSERT(a.is_int()); scoped_numeral tmp(m_manager); m_manager.set(tmp, a.to_mpq().numerator()); polynomial * p = mk_const(tmp); return p; } polynomial * mk_polynomial(var x, unsigned k) { SASSERT(is_valid(x)); numeral one(1); monomial * m = mk_monomial(x, k); inc_ref(m); return mk_polynomial_core(1, &one, &m); } polynomial * mk_polynomial(unsigned sz, numeral * as, monomial * const * ms) { m_som_buffer.reset(); for (unsigned i = 0; i < sz; i++) { m_som_buffer.add(as[i], ms[i]); } return m_som_buffer.mk(); } /** \brief Convert rationals into numerals at m_rat2numeral */ void rational2numeral(unsigned sz, rational const * as) { SASSERT(m_rat2numeral.empty()); for (unsigned i = 0; i < sz; i++) { SASSERT(as[i].is_int()); m_rat2numeral.push_back(numeral()); m_manager.set(m_rat2numeral.back(), as[i].to_mpq().numerator()); } } void reset_tmp_as2() { DEBUG_CODE({ for (unsigned i = 0; i < m_rat2numeral.size(); i++) { SASSERT(m_manager.is_zero(m_rat2numeral[i])); } }); m_rat2numeral.reset(); } polynomial * mk_polynomial(unsigned sz, rational const * as, monomial * const * ms) { rational2numeral(sz, as); polynomial * p = mk_polynomial(sz, m_rat2numeral.c_ptr(), ms); reset_tmp_as2(); return p; } polynomial * mk_univariate(var x, unsigned n, numeral * as) { SASSERT(m_cheap_som_buffer.empty()); unsigned k = n+1; while (k > 0) { --k; if (m_manager.is_zero(as[k])) { m_manager.del(as[k]); continue; } m_cheap_som_buffer.add_reset(as[k], mk_monomial(x, k)); } return m_cheap_som_buffer.mk(); } polynomial * mk_univariate(var x, unsigned n, rational const * as) { SASSERT(is_valid(x)); rational2numeral(n+1, as); polynomial * p = mk_univariate(x, n, m_rat2numeral.c_ptr()); reset_tmp_as2(); return p; } polynomial * mk_linear(unsigned sz, numeral * as, var const * xs, numeral & c) { SASSERT(m_tmp_linear_as.empty()); SASSERT(m_tmp_linear_ms.empty()); for (unsigned i = 0; i < sz; i++) { if (m_manager.is_zero(as[i])) continue; m_tmp_linear_as.push_back(numeral()); swap(m_tmp_linear_as.back(), as[i]); m_tmp_linear_ms.push_back(mk_monomial(xs[i])); } if (!m_manager.is_zero(c)) { m_tmp_linear_as.push_back(numeral()); swap(m_tmp_linear_as.back(), c); m_tmp_linear_ms.push_back(mk_unit()); } polynomial * p = mk_polynomial(m_tmp_linear_as.size(), m_tmp_linear_as.c_ptr(), m_tmp_linear_ms.c_ptr()); m_tmp_linear_as.reset(); m_tmp_linear_ms.reset(); return p; } polynomial * mk_linear(unsigned sz, rational const * as, var const * xs, rational const & c) { SASSERT(c.is_int()); rational2numeral(sz, as); numeral tmp_c; m_manager.set(tmp_c, c.to_mpq().numerator()); polynomial * p = mk_linear(sz, m_rat2numeral.c_ptr(), xs, tmp_c); SASSERT(m_manager.is_zero(tmp_c)); reset_tmp_as2(); return p; } monomial * mul(monomial const * m1, monomial const * m2) { return mm().mul(m1, m2); } bool div(monomial const * m1, monomial const * m2) { return mm().div(m1, m2); } bool div(monomial const * m1, monomial const * m2, monomial * & r) { return mm().div(m1, m2, r); } monomial * gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { return mm().gcd(m1, m2, q1, q2); } bool unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { return mm().unify(m1, m2, q1, q2); } monomial * pw(monomial const * m, unsigned k) { return mm().pw(m, k); } monomial * sqrt(monomial const * m) { return mm().sqrt(m); } polynomial * addmul(numeral const & a1, monomial const * m1, polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) { m_som_buffer.reset(); m_som_buffer.addmul(a1, m1, p1); m_som_buffer.addmul(a2, m2, p2); return m_som_buffer.mk(); } polynomial * addmul(polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) { numeral one(1); return addmul(one, mk_unit(), p1, a2, m2, p2); } polynomial * addmul(polynomial const * p1, numeral const & a2, polynomial const * p2) { return addmul(p1, a2, mk_unit(), p2); } polynomial * add(polynomial const * p1, polynomial const * p2) { numeral one(1); return addmul(one, mk_unit(), p1, one, mk_unit(), p2); } polynomial * sub(polynomial const * p1, polynomial const * p2) { numeral one(1); numeral minus_one; // It is incorrect to initialize with -1 when numeral_manager is GF_2 m_manager.set(minus_one, -1); return addmul(one, mk_unit(), p1, minus_one, mk_unit(), p2); } /** \brief Return p1*p2 + a */ polynomial * muladd(polynomial const * p1, polynomial const * p2, numeral const & a) { if (is_zero(p1) || is_zero(p2)) { return mk_const(a); } m_som_buffer.reset(); unsigned sz1 = p1->size(); for (unsigned i = 0; i < sz1; i++) { checkpoint(); numeral const & a1 = p1->a(i); monomial * m1 = p1->m(i); m_som_buffer.addmul(a1, m1, p2); } m_som_buffer.add(a); return m_som_buffer.mk(); } polynomial * mul(polynomial const * p1, polynomial const * p2) { numeral zero(0); return muladd(p1, p2, zero); } polynomial * mul(numeral const & a, monomial const * m, polynomial const * p) { if (m_manager.is_zero(a)) return m_zero; if (m_manager.is_one(a) && m == mk_unit()) return const_cast(p); SASSERT(m_cheap_som_buffer.empty()); m_cheap_som_buffer.addmul(a, m, p); return m_cheap_som_buffer.mk(); } polynomial * mul(monomial const * m, polynomial const * p) { numeral one(1); return mul(one, m, p); } polynomial * mul(numeral const & a, polynomial const * p) { return mul(a, mk_unit(), p); } /** \brief Return a*p1*p2 */ polynomial * mul(numeral const & a, polynomial const * p1, polynomial const * p2) { if (m_manager.is_zero(a) || is_zero(p1) || is_zero(p2)) return mk_zero(); scoped_numeral new_a1(m_manager); m_som_buffer.reset(); unsigned sz1 = p1->size(); for (unsigned i = 0; i < sz1; i++) { checkpoint(); numeral const & a1 = p1->a(i); m_manager.mul(a, a1, new_a1); monomial * m1 = p1->m(i); m_som_buffer.addmul(new_a1, m1, p2); } return m_som_buffer.mk(); } // Divide coefficients of p by d. // This methods assumes that all coefficients of p are divisible by d. polynomial * div(polynomial * p, numeral const & d) { SASSERT(m_cheap_som_buffer.empty()); unsigned sz = p->size(); scoped_numeral a(m_manager); for (unsigned i = 0; i < sz; i++) { m_manager.div(p->a(i), d, a); m_cheap_som_buffer.add(a, p->m(i)); } return m_cheap_som_buffer.mk(); } polynomial * mul(rational const & a, polynomial const * p) { SASSERT(a.is_int()); scoped_numeral tmp(m_manager); m_manager.set(tmp, a.to_mpq().numerator()); polynomial * new_p = mul(tmp, p); return new_p; } /** \brief Return m/x^k */ monomial * div_x_k(monomial const * m, var x, unsigned k) { return mm().div_x_k(m, x, k); } /** \brief Return m/x^n where n == m->degree_of(x) */ monomial * div_x(monomial const * m, var x) { return mm().div_x(m, x); } bool is_p_normalized(polynomial const * p) const { for (unsigned i = 0; i < p->size(); i++) { SASSERT(m().is_p_normalized(p->a(i))); } return true; } /** \brief (Incremental) Newton interpolation for multivariate polynomials. Creates a polynomial on x of degree at most d with coefficients in Z[y1, ..., ym], using d+1 sample points. Sample points are provided using the method add, and the interpolating polynomial is created using mk() method. \pre manager must be configured in Zp (modular) mode. We need this requeriment because we use the inverse operation. */ class newton_interpolator { imp & pm; scoped_numeral_vector m_inputs; scoped_numeral_vector m_invs; polynomial_ref_vector m_vs; mpzzp_manager & m() const { return pm.m(); } public: newton_interpolator(imp & _pm):pm(_pm), m_inputs(m()), m_invs(m()), m_vs(pm.m_wrapper) { m_invs.push_back(numeral(0)); } void reset() { m_inputs.reset(); m_invs.shrink(1); m_vs.reset(); SASSERT(m().is_zero(m_invs[0])); } scoped_numeral_vector const & inputs() const { return m_inputs; } unsigned num_sample_points() const { return m_inputs.size(); } /** \brief Add a new datapoint */ void add(numeral const & input, polynomial const * output) { TRACE("newton", tout << m().to_string(input) << " -> "; output->display(tout, m()); tout << "\n";); SASSERT(m().modular()); unsigned sz = num_sample_points(); if (sz > 0) { unsigned k = sz; // add new inverse scoped_numeral product(m()); scoped_numeral aux(m()); SASSERT(!m().eq(input, m_inputs[0])); m().sub(input, m_inputs[0], product); for (unsigned i = 1; i <= k - 1; i++) { SASSERT(!m().eq(input, m_inputs[i])); m().sub(input, m_inputs[i], aux); m().mul(product, aux, product); } m().inv(product); m_inputs.push_back(input); m_invs.push_back(product); TRACE("newton", tout << "invs[" << k << "]: " << product << "\n";); SASSERT(m().eq(m_inputs[k], input)); // Compute newton's coefficient polynomial_ref temp(pm.m_wrapper); polynomial_ref aux_poly(pm.m_wrapper); temp = m_vs.get(k-1); for (int j = k - 2; j >= 0; j--) { // temp <- temp*(input - m_inputs[j]) + vs[j] m().sub(input, m_inputs[j], aux); SASSERT(m().is_p_normalized(aux)); aux_poly = pm.mul(aux, temp); temp = pm.add(aux_poly, m_vs.get(j)); SASSERT(pm.is_p_normalized(temp)); } // new vs <- (output - temp)*invs[sz] aux_poly = pm.sub(output, temp); SASSERT(pm.is_p_normalized(aux_poly)); aux_poly = pm.mul(m_invs[sz], aux_poly); SASSERT(pm.is_p_normalized(aux_poly)); m_vs.push_back(aux_poly); TRACE("newton", tout << "vs[" << k << "]: " << aux_poly << "\n";); } else { m_inputs.push_back(input); m_vs.push_back(const_cast(output)); } } // Convert newton form to standard form void mk(var x, polynomial_ref & r) { SASSERT(m().modular()); polynomial_ref u(pm.m_wrapper); polynomial_ref aux_poly(pm.m_wrapper); int num = num_sample_points(); int d = num - 1; SASSERT(num > 0); u = m_vs.get(d); scoped_numeral c(m()); for (int k = d - 1; k >= 0; k--) { TRACE("newton", tout << "u: " << u << "\n";); // create polynomial (x - inputs[k]) m().set(c, m_inputs[k]); m().neg(c); numeral one(1); aux_poly = pm.mk_linear(1, &one, &x, c); TRACE("newton", tout << "(x - inputs[k]): " << aux_poly << "\n";); // u <- u * (x - inputs[k]) + vs[k] aux_poly = pm.mul(u, aux_poly); u = pm.add(aux_poly, m_vs.get(k)); } TRACE("newton", tout << "result u: " << u << "\n";); r = u; } }; /** \brief Newton interpolation for multivariate polynomials. Creates a polynomial on x of degree at most d with coefficients in Z[y1, ..., ym], using d+1 sample points. The sample points are store in the vectors inputs and outputs. Both must have size d+1. \pre manager must be configured in Zp (modular) mode. We need this requeriment because we use the inverse operation. */ void newton_interpolation(var x, unsigned d, numeral const * inputs, polynomial * const * outputs, polynomial_ref & r) { SASSERT(m().modular()); newton_interpolator interpolator(*this); for (unsigned i = 0; i <= d; i++) interpolator.add(inputs[i], outputs[i]); interpolator.mk(x, r); } class newton_interpolator_vector { imp * m_imp; ptr_vector m_data; public: newton_interpolator_vector():m_imp(0) {} ~newton_interpolator_vector() { flush(); } void flush() { unsigned sz = m_data.size(); for (unsigned i = 0; i < sz; i++) dealloc(m_data[i]); m_data.reset(); } void set_owner(imp * owner) { SASSERT(m_imp == 0 || m_imp == owner); m_imp = owner; } newton_interpolator & operator[](unsigned idx) { SASSERT(m_imp); while (idx >= m_data.size()) { m_data.push_back(alloc(newton_interpolator, *m_imp)); } return *(m_data[idx]); } }; /** \brief Represents a polynomial skeleton of a multivariate polynomial Z[Y1, ..., Yn] with coefficients in Z[X] */ struct skeleton { struct entry { monomial * m_monomial; // a monomial in Z[Y1, ..., Y1] unsigned m_first_power_idx; // position (in m_powers) of the powers of X that are the coefficient of this monomial unsigned m_num_powers; // size of the coefficient of this monomial. entry(monomial * m, unsigned first_idx): m_monomial(m), m_first_power_idx(first_idx), m_num_powers(1) { } unsigned num_powers() const { return m_num_powers; } monomial * m() const { return m_monomial; } }; imp & pm; var m_x; svector m_entries; unsigned_vector m_powers; ptr_vector m_orig_monomials; unsigned m_max_powers; // maximal number of powers associated with an entry skeleton(imp & _pm, polynomial * p, var x):pm(_pm), m_x(x) { m_max_powers = 0; ptr_buffer ms; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { ms.push_back(p->m(i)); } std::sort(ms.begin(), ms.end(), lex_lt2(x)); monomial * prev = 0; for (unsigned i = 0; i < sz; i++) { monomial * orig_m = ms[i]; monomial * m; unsigned k = orig_m->degree_of(x); if (k > 0) m = pm.div_x(orig_m, x); else m = orig_m; if (m == prev) { unsigned & num_powers = m_entries.back().m_num_powers; num_powers++; if (num_powers > m_max_powers) m_max_powers = num_powers; } else { prev = m; pm.inc_ref(m); m_entries.push_back(entry(m, m_powers.size())); if (m_max_powers == 0) m_max_powers = 1; } pm.inc_ref(orig_m); m_orig_monomials.push_back(orig_m); m_powers.push_back(k); } TRACE("skeleton", tout << "x: x" << m_x << "\n"; tout << "max: " << m_max_powers << "\n"; tout << "p: "; p->display(tout, pm.m()); tout << "\n"; tout << "skeleton: "; display(tout); tout << "\n";); DEBUG_CODE({ unsigned sz = m_entries.size(); for (unsigned i = 1; i < sz; i++) { SASSERT(lex_compare(m_entries[i-1].m_monomial, m_entries[i].m_monomial) < 0); } }); } ~skeleton() { unsigned sz = m_entries.size(); for (unsigned i = 0; i < sz; i++) { pm.dec_ref(m_entries[i].m_monomial); } sz = m_orig_monomials.size(); for (unsigned i = 0; i < sz; i++) { pm.dec_ref(m_orig_monomials[i]); } } unsigned get_entry_idx(monomial * m) { unsigned sz = m_entries.size(); for (unsigned i = 0; i < sz; i++) { if (m_entries[i].m_monomial == m) return i; } return UINT_MAX; } unsigned num_entries() const { return m_entries.size(); } entry const & operator[](unsigned idx) const { return m_entries[idx]; } unsigned ith_power(entry const & e, unsigned i) const { SASSERT(i < e.m_num_powers); return m_powers[e.m_first_power_idx + i]; } monomial * ith_orig_monomial(entry const & e, unsigned i) const { SASSERT(i < e.m_num_powers); return m_orig_monomials[e.m_first_power_idx + i]; } unsigned max_num_powers() const { return m_max_powers; } void display(std::ostream & out) { unsigned sz = m_entries.size(); for (unsigned i = 0; i < sz; i++) { entry & e = m_entries[i]; if (i > 0) out << " "; out << "("; for (unsigned j = 0; j < e.m_num_powers; j++) { if (j > 0) out << " "; out << "x" << m_x << "^"; out << m_powers[e.m_first_power_idx + j]; } out << ")*"; e.m_monomial->display(out); } } }; class sparse_interpolator { skeleton * m_skeleton; numeral_vector m_inputs; numeral_vector m_outputs; public: sparse_interpolator(skeleton * sk):m_skeleton(sk) { // reserve space output values associated with each entry if (sk) { unsigned sz = sk->num_entries(); for (unsigned i = 0; i < sz; i++) { unsigned num_powers = (*sk)[i].num_powers(); for (unsigned j = 0; j < num_powers; j++) { m_outputs.push_back(numeral()); } } } } ~sparse_interpolator() { if (m_skeleton) { numeral_manager & m = m_skeleton->pm.m(); for (unsigned i = 0; i < m_inputs.size(); i++) m.del(m_inputs[i]); for (unsigned i = 0; i < m_outputs.size(); i++) m.del(m_outputs[i]); } } void reset() { numeral_manager & m = m_skeleton->pm.m(); for (unsigned i = 0; i < m_inputs.size(); i++) { m.del(m_inputs[i]); } m_inputs.reset(); } bool ready() const { return m_inputs.size() == m_skeleton->max_num_powers(); } bool add(numeral const & in, polynomial const * q) { SASSERT(m_skeleton); SASSERT(m_inputs.size() < m_skeleton->max_num_powers()); numeral_manager & m = m_skeleton->pm.m(); unsigned input_idx = m_inputs.size(); m_inputs.push_back(numeral()); m.set(m_inputs.back(), in); unsigned sz = q->size(); for (unsigned i = 0; i < sz; i++) { monomial * mon = q->m(i); unsigned entry_idx = m_skeleton->get_entry_idx(mon); if (entry_idx == UINT_MAX) return false; skeleton::entry const & e = (*m_skeleton)[entry_idx]; if (input_idx >= e.num_powers()) continue; unsigned output_idx = e.m_first_power_idx + input_idx; m.set(m_outputs[output_idx], q->a(i)); } return true; } bool mk(polynomial_ref & r) { SASSERT(m_skeleton); numeral_manager & m = m_skeleton->pm.m(); scoped_numeral_vector cs(m); scoped_numeral_vector new_as(m); scoped_numeral_vector as(m); ptr_buffer mons; scoped_numeral aux(m); linear_eq_solver solver(m); unsigned sz = m_skeleton->num_entries(); for (unsigned k = 0; k < sz; k++) { skeleton::entry const & e = (*m_skeleton)[k]; unsigned num_pws = e.num_powers(); solver.resize(num_pws); new_as.resize(num_pws); for (unsigned i = 0; i < num_pws; i++) { numeral & in = m_inputs[i]; cs.reset(); for (unsigned j = 0; j < num_pws; j++) { m.power(in, m_skeleton->ith_power(e, j), aux); cs.push_back(aux); } unsigned output_idx = e.m_first_power_idx + i; TRACE("sparse_interpolator", tout << "adding new equation:\n"; for (unsigned i = 0; i < num_pws; i++) { tout << m.to_string(cs[i]) << " "; } tout << "\n";); solver.add(i, cs.c_ptr(), m_outputs[output_idx]); } TRACE("sparse_interpolator", tout << "find coefficients of:\n"; for (unsigned i = 0; i < num_pws; i++) { m_skeleton->ith_orig_monomial(e, i)->display(tout); tout << "\n"; } tout << "system of equations:\n"; solver.display(tout);); if (!solver.solve(new_as.c_ptr())) return false; for (unsigned i = 0; i < num_pws; i++) { if (!m.is_zero(new_as[i])) { as.push_back(new_as[i]); mons.push_back(m_skeleton->ith_orig_monomial(e, i)); } } } r = m_skeleton->pm.mk_polynomial(as.size(), as.c_ptr(), mons.c_ptr()); return true; } }; svector m_found_vars; void vars(polynomial const * p, var_vector & xs) { xs.reset(); m_found_vars.reserve(num_vars(), false); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); if (!m_found_vars[x]) { m_found_vars[x] = true; xs.push_back(x); } } } // reset m_found_vars sz = xs.size(); for (unsigned i = 0; i < sz; i++) m_found_vars[xs[i]] = false; } typedef sbuffer power_buffer; typedef sbuffer unsigned_buffer; typedef sbuffer var_buffer; /** Store in pws the variables occuring in p and their (minimal or maximal) degrees. */ unsigned_vector m_var_degrees_tmp; template void var_degrees(polynomial const * p, power_buffer & pws) { pws.reset(); unsigned_vector & var2pos = m_var_degrees_tmp; var2pos.reserve(num_vars(), UINT_MAX); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); unsigned k = m->degree(j); unsigned pos = var2pos[x]; if (pos == UINT_MAX) { var2pos[x] = pws.size(); pws.push_back(power(x, k)); } else if (Max && k > pws[pos].degree()) { pws[pos].degree() = k; } else if (!Max && k < pws[pos].degree()) { pws[pos].degree() = k; } } } sz = pws.size(); for (unsigned i = 0; i < sz; i++) { SASSERT(var2pos[pws[i].get_var()] != UINT_MAX); var2pos[pws[i].get_var()] = UINT_MAX; } DEBUG_CODE({ for (unsigned i = 0; i < pws.size(); i++) { for (unsigned j = i + 1; j < pws.size(); j++) SASSERT(pws[i].first != pws[j].first); } }); } void var_max_degrees(polynomial const * p, power_buffer & pws) { var_degrees(p, pws); } void var_min_degrees(polynomial const * p, power_buffer & pws) { var_degrees(p, pws); } polynomial * coeff(polynomial const * p, var x, unsigned k) { SASSERT(is_valid(x)); SASSERT(m_cheap_som_buffer.empty()); TRACE("coeff_bug", tout << "p: "; p->display(tout, m_manager); tout << "\nx: " << x << ", k: " << k << "\n";); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned d = m->degree_of(x); if (d == k) m_cheap_som_buffer.add(p->a(i), div_x(m, x)); } return m_cheap_som_buffer.mk(); } /** Let p be of the form q_k(yvec)*x^k + ...+ q_0(yvec) Store the polynomials q_k(yvec), ..., q_0(yvec) in the som_buffer_vector. */ void coeffs(polynomial const * p, var x, som_buffer_vector & cs) { cs.set_owner(this); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned d = m->degree_of(x); som_buffer * c = cs[d]; c->add(p->a(i), div_x(m, x)); } } /** \brief Return a polynomial h that is the coefficient of x^k in p. Store the reduct (p - h x^k) into \c reduct. */ polynomial * coeff(polynomial const * p, var x, unsigned k, polynomial_ref & reduct) { SASSERT(is_valid(x)); SASSERT(m_cheap_som_buffer.empty()); SASSERT(m_cheap_som_buffer2.empty()); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned d = m->degree_of(x); if (d == k) m_cheap_som_buffer.add(p->a(i), div_x(m, x)); else m_cheap_som_buffer2.add(p->a(i), m); } reduct = m_cheap_som_buffer2.mk(); return m_cheap_som_buffer.mk(); } /** \brief Return true if the coefficient of x^k is just a constant. Store it in c. */ bool const_coeff(polynomial const * p, var x, unsigned k, numeral & c) { SASSERT(is_valid(x)); m_manager.reset(c); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned d = m->degree_of(x); if (d == k) { unsigned msz = m->size(); if ((k > 0 && msz > 1) || (k == 0 && msz > 0)) return false; m_manager.set(c, p->a(i)); } } return true; } bool nonzero_const_coeff(polynomial const * p, var x, unsigned k) { scoped_numeral c(m_manager); return const_coeff(p, x, k, c) && !m_manager.is_zero(c); } /** \brief Extract the integer content of p. */ void ic(polynomial const * p, numeral & a) { if (is_zero(p)) { m_manager.reset(a); return; } if (is_const(p)) { m_manager.set(a, p->a(0)); return; } m_manager.set(a, p->a(0)); unsigned sz = p->size(); for (unsigned i = 1; i < sz; i++) { if (m_manager.is_one(a)) return; m_manager.gcd(a, p->a(i), a); } } /** \brief Sum of the absolute values of the coefficients. */ void abs_norm(polynomial const * p, numeral & norm) { m_manager.reset(norm); scoped_numeral tmp(m_manager); unsigned sz = p->size(); for (unsigned i = 0; i < sz; ++ i) { m_manager.set(tmp, p->a(i)); m_manager.abs(tmp); m_manager.add(norm, tmp, norm); } } /** \brief Arbitrary leading integer coefficient. */ numeral const & numeral_lc(polynomial const * p, var x) { int sz = p->size(); if (sz == 0) { return m_zero_numeral; } else { return p->a(0); } } numeral const & numeral_tc(polynomial const * p) { int sz = p->size(); if (sz == 0) { return m_zero_numeral; } else { monomial * u = mk_unit(); for (int i = 0; i < sz; ++ i) { if (p->m(i) == u) return p->a(i); } return m_zero_numeral; } } /** \brief Extract the integer content of p. p = a*c s.t. the GCD of the coefficients of c is one. */ void ic(polynomial const * p, numeral & a, polynomial_ref & c) { if (is_zero(p)) { m_manager.reset(a); c = const_cast(p); return; } if (is_const(p)) { m_manager.set(a, p->a(0)); c = mk_one(); return; } unsigned sz = p->size(); m_manager.gcd(sz, p->as(), a); if (m_manager.is_one(a)) { c = const_cast(p); return; } m_cheap_som_buffer.reset(); scoped_numeral ai(m_manager); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); m_manager.div(p->a(i), a, ai); m_cheap_som_buffer.add_reset(ai, m); } c = m_cheap_som_buffer.mk(); } // Flip the sign of p, if the leading monomial is negative polynomial * flip_sign_if_lm_neg_core(polynomial const * p) { if (is_zero(p)) return const_cast(p); unsigned glex_max_pos = p->graded_lex_max_pos(); SASSERT(glex_max_pos != UINT_MAX); if (m_manager.is_neg(p->a(glex_max_pos))) return neg(p); else return const_cast(p); } void flip_sign_if_lm_neg(polynomial_ref & p) { p = flip_sign_if_lm_neg_core(p); } /** \brief Extract the integer content, content and primitive part of p with respect to variable x. */ void iccp(polynomial const * p, var x, numeral & i, polynomial_ref & c, polynomial_ref & pp) { TRACE("polynomial", tout << "iccp x" << x << "\n"; p->display(tout, m_manager); tout << "\n";); if (is_zero(p)) { m_manager.set(i, 0); c = mk_one(); pp = const_cast(p); return; } if (is_const(p)) { m_manager.set(i, p->a(0)); c = mk_one(); pp = mk_one(); return; } unsigned d = degree(p, x); if (d == 0) { ic(p, i, c); pp = mk_one(); return; } // Apply filter and collect powers of x occuring in p // The quick filter is the following: // If p contains a monomial x^k and no monomial of the form m*x^k m != 1, then // c = m_unit_poly // To detect that we use a map (iccp_powers) from k to counters. // We traverse p and update the map using the following rules: // - found monomial x^k then iccp_powers[k]++; // - found monomial m*x^k then iccp_powers[k]+=2; // If after traversing p, there is a k s.t. iccp_powers[k] == 1, we know c == 1 // We store iccp_powers the powers of x occuring in p. sbuffer iccp_filter; sbuffer iccp_powers; iccp_filter.resize(d+1, 0); iccp_powers.reset(); for (unsigned j = 0; j <= d; j++) iccp_filter[j] = 0; unsigned sz = p->size(); for (unsigned j = 0; j < sz; j++) { monomial * m = p->m(j); unsigned k = m->degree_of(x); TRACE("polynomial", tout << "degree of x" << x << " at "; m->display(tout); tout << " is " << k << "\n";); if (iccp_filter[k] == 0) iccp_powers.push_back(k); if (m->size() == (k > 0 ? 1 : 0)) iccp_filter[k]++; else iccp_filter[k]+=2; } SASSERT(!iccp_powers.empty()); unsigned num_powers = iccp_powers.size(); for (unsigned j = 0; j < num_powers; j++) { SASSERT(iccp_filter[iccp_powers[j]] > 0); if (iccp_filter[iccp_powers[j]] == 1) { ic(p, i, pp); c = mk_one(); return; } } // Extract integer content ic(p, i, pp); TRACE("polynomial", tout << "p: "; p->display(tout, m_manager); tout << "\ni: " << m_manager.to_string(i) << "\npp: " << pp << "\n";); // Compute c using the gcd of coeffs of x^k for k's in iccp_powers polynomial_ref ci(pm()); c = coeff(pp, x, iccp_powers[0]); for (unsigned j = 1; j < num_powers; j++) { ci = coeff(pp, x, iccp_powers[j]); gcd(c, ci, c); if (is_const(c)) { c = mk_one(); return; } } SASSERT(!is_const(c)); // make sure the sign of the leading monomial is positive flip_sign_if_lm_neg(c); TRACE("polynomial", tout << "pp: " << pp << "\nc: " << c << "\n";); pp = exact_div(pp, c); } void iccp(polynomial const * p, numeral & i, polynomial_ref & c, polynomial_ref & pp) { iccp(p, max_var(p), i, c, pp); } void pp(polynomial const * p, var x, polynomial_ref & pp) { scoped_numeral i(m_manager); polynomial_ref c(pm()); iccp(p, x, i, c, pp); } bool is_primitive(polynomial const * p, var x) { scoped_numeral i(m_manager); polynomial_ref c(pm()); polynomial_ref pp(pm()); iccp(p, x, i, c, pp); return eq(p, pp); } polynomial * lc(polynomial const * p, var x) { return coeff(p, x, degree(p, x)); } void gcd_prs(polynomial const * u, polynomial const * v, var x, polynomial_ref & r) { TRACE("polynomial_gcd", tout << "gcd prs with x" << x << " for\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\n";); if (degree(u, x) < degree(v, x)) std::swap(u, v); scoped_numeral i_u(m_manager), i_v(m_manager); polynomial_ref c_u(pm()), c_v(pm()); polynomial_ref pp_u(pm()), pp_v(pm()); scoped_numeral d_a(m_manager); polynomial_ref d_r(pm()); polynomial_ref g(pm()), h(pm()), rem(pm()), new_h(pm()); iccp(u, x, i_u, c_u, pp_u); iccp(v, x, i_v, c_v, pp_v); gcd(c_u, c_v, d_r); m_manager.gcd(i_u, i_v, d_a); TRACE("polynomial_gcd_detail", tout << "After GCD of the content\n"; tout << "u: "; u->display(tout, m_manager); tout << "\n"; tout << "v: "; v->display(tout, m_manager); tout << "\n"; tout << "i_u: " << i_u << "\n"; tout << "i_v: " << i_v << "\n"; tout << "c_u: " << c_u << "\n"; tout << "c_v: " << c_v << "\n"; tout << "pp_u: " << pp_u << "\n"; tout << "pp_v: " << pp_v << "\n"; tout << "d_r: " << d_r << "\nd_a: " << d_a << "\n";); g = mk_one(); h = mk_one(); unsigned counter = 0; while (true) { SASSERT(degree(pp_u, x) >= degree(pp_v, x)); unsigned delta = degree(pp_u, x) - degree(pp_v, x); TRACE("polynomial_gcd_detail", tout << "iteration: " << counter << "\n"; tout << "gcd loop\npp_u: " << pp_u << "\npp_v: " << pp_v << "\ndelta: " << delta << "\n";); counter++; exact_pseudo_remainder(pp_u, pp_v, x, rem); if (is_zero(rem)) { TRACE("polynomial", tout << "rem is zero...\npp_v: " << pp_v << "\n";); flip_sign_if_lm_neg(pp_v); pp(pp_v, x, r); r = mul(d_a, d_r, r); return; } if (is_const(rem)) { TRACE("polynomial", tout << "rem is a constant: " << rem << "\nr: " << d_r << "\nd_a: " << d_a << "\n";); r = mul(d_a, d_r); return; } pp_u = pp_v; // pp_v <- rem/g*h^{delta} pp_v = exact_div(rem, g); // delta is usually a small number, so I do not compute h^delta for (unsigned i = 0; i < delta; i++) pp_v = exact_div(pp_v, h); g = lc(pp_u, x); // h <- h^{1-delta}*g^{delta} new_h = mk_one(); for (unsigned i = 0; i < delta; i++) new_h = mul(new_h, g); if (delta > 1) { for (unsigned i = 0; i < delta - 1; i++) new_h = exact_div(new_h, h); } h = new_h; } } // Store in r <- gcd(content(u, x), v) void gcd_content(polynomial const * u, var x, polynomial const * v, polynomial_ref & r) { scoped_numeral i_u(m_manager); polynomial_ref c_u(pm()); polynomial_ref pp_u(pm()); iccp(u, x, i_u, c_u, pp_u); c_u = mul(i_u, c_u); gcd(c_u, v, r); } // TODO: implement euclid algorithm when m_manager in Zp mode void euclid_gcd(polynomial const * u, polynomial const * v, polynomial_ref & r) { SASSERT(m().modular()); CTRACE("mgcd", !is_univariate(u) || !is_univariate(v), tout << "euclid_gcd, polynomials are not univariate\n"; u->display(tout, m()); tout << "\n"; v->display(tout, m()); tout << "\n";); SASSERT(is_univariate(u)); SASSERT(is_univariate(v)); if (is_zero(u)) { r = const_cast(v); flip_sign_if_lm_neg(r); return; } if (is_zero(v)) { r = const_cast(u); flip_sign_if_lm_neg(r); return; } if (u == v) { r = const_cast(u); flip_sign_if_lm_neg(r); return; } if (is_const(u) || is_const(v)) { scoped_numeral i_u(m_manager), i_v(m_manager); ic(v, i_v); ic(u, i_u); scoped_numeral a(m_manager); m_manager.gcd(i_v, i_u, a); r = mk_const(a); return; } // Maybe map it to univariate case gcd_prs(u, v, max_var(u), r); } // Combine two different modular images using Chinese Remainder theorem // The new bound is stored in b2 void CRA_combine_images(polynomial const * C1, scoped_numeral const & b1, polynomial const * C2, scoped_numeral & b2, polynomial_ref & r) { lex_sort(C1); lex_sort(C2); TRACE("CRA", tout << "C1: "; C1->display(tout, m()); tout << "\nC2: "; C2->display(tout, m()); tout << "\n";); SASSERT(m_cheap_som_buffer.empty()); SASSERT(!m().m().is_even(b1)); SASSERT(!m().m().is_even(b2)); cheap_som_buffer & R = m_cheap_som_buffer; scoped_numeral inv1(m()); scoped_numeral inv2(m()); scoped_numeral g(m()); m().gcd(b1, b2, inv1, inv2, g); SASSERT(m().is_one(g)); TRACE("CRA", tout << "b1: " << b1 << ", b2: " << b2 << ", inv1: " << inv1 << ", inv2: " << inv2 << "\n";); // b1*inv1 + b2.inv2 = 1 // inv1 is the inverse of b1 mod b2 // inv2 is the inverse of b2 mod b1 m().m().mod(inv1, b2, inv1); m().m().mod(inv2, b1, inv2); TRACE("CRA", tout << "inv1: " << inv1 << ", inv2: " << inv2 << "\n";); scoped_numeral a1(m()); scoped_numeral a2(m()); m().mul(b2, inv2, a1); // a1 is the multiplicator for coefficients of C1 m().mul(b1, inv1, a2); // a2 is the multiplicator for coefficients of C2 TRACE("CRA", tout << "a1: " << a1 << ", a2: " << a2 << "\n";); // new bound scoped_numeral new_bound(m()); m().mul(b1, b2, new_bound); scoped_numeral lower(m()); scoped_numeral upper(m()); scoped_numeral new_a(m()), tmp1(m()), tmp2(m()), tmp3(m()); m().div(new_bound, 2, upper); m().set(lower, upper); m().neg(lower); TRACE("CRA", tout << "lower: " << lower << ", upper: " << upper << "\n";); #define ADD(A1, A2, M) { \ m().mul(A1, a1, tmp1); \ m().mul(A2, a2, tmp2); \ m().add(tmp1, tmp2, tmp3); \ m().m().mod(tmp3, new_bound, new_a); \ if (m().gt(new_a, upper)) \ m().sub(new_a, new_bound, new_a); \ R.add(new_a, M); \ } numeral zero(0); unsigned i1 = 0; unsigned i2 = 0; unsigned sz1 = C1->size(); unsigned sz2 = C2->size(); while (true) { if (i1 == sz1) { while (i2 < sz2) { TRACE("CRA", tout << "adding C2 rest\n";); ADD(zero, C2->a(i2), C2->m(i2)); i2++; } break; } if (i2 == sz2) { while (i1 < sz1) { TRACE("CRA", tout << "adding C1 rest\n";); ADD(C1->a(i1), zero, C1->m(i1)); i1++; } break; } monomial * m1 = C1->m(i1); monomial * m2 = C2->m(i2); int s = lex_compare(m1, m2); if (s == 0) { ADD(C1->a(i1), C2->a(i2), m1); TRACE("CRA", tout << "C1->a(i1): " << m().to_string(C1->a(i1)) << ", C2->a(i2): " << m().to_string(C2->a(i2)) << ", new_a: " << new_a << "\n"; tout << "tmp1: " << tmp1 << ", tmp2: " << tmp2 << ", tmp3: " << tmp3 << "\n";); i1++; i2++; } else if (s > 0) { TRACE("CRA", tout << "C1 mon biggerr, adding it...\n";); ADD(C1->a(i1), zero, m1); i1++; } else { TRACE("CRA", tout << "C2 mon bigger, adding it...\n";); ADD(zero, C2->a(i2), m2); i2++; } } m().set(b2, new_bound); r = R.mk(); } void uni_mod_gcd(polynomial const * u, polynomial const * v, polynomial_ref & r) { TRACE("mgcd", tout << "univ_modular_gcd\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\n";); SASSERT(!m().modular()); SASSERT(is_univariate(u)); SASSERT(!is_const(u) && !is_const(v)); SASSERT(max_var(u) == max_var(v)); var x = max_var(u); scoped_numeral c_u(m()), c_v(m()); polynomial_ref pp_u(pm()), pp_v(pm()); ic(u, c_u, pp_u); ic(v, c_v, pp_v); scoped_numeral d_a(m()); m_manager.gcd(c_u, c_v, d_a); scoped_numeral lc_u(m()); scoped_numeral lc_v(m()); unsigned d_u = degree(pp_u, x); unsigned d_v = degree(pp_v, x); lc_u = univ_coeff(pp_u, d_u); lc_v = univ_coeff(pp_v, d_v); scoped_numeral lc_g(m()); m().gcd(lc_u, lc_v, lc_g); polynomial_ref u_Zp(m_wrapper); polynomial_ref v_Zp(m_wrapper); polynomial_ref C_star(m_wrapper); scoped_numeral bound(m()); polynomial_ref q(m_wrapper); polynomial_ref candidate(m_wrapper); scoped_numeral p(m()); for (unsigned i = 0; i < NUM_BIG_PRIMES; i++) { m().set(p, g_big_primes[i]); TRACE("mgcd", tout << "trying prime: " << p << "\n";); { scoped_set_zp setZp(m_wrapper, p); u_Zp = normalize(pp_u); v_Zp = normalize(pp_v); if (degree(u_Zp, x) < d_u) { TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";); continue; // bad prime } if (degree(v_Zp, x) < d_v) { TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";); continue; // bad prime } euclid_gcd(u_Zp, v_Zp, q); // normalize so that lc_g is leading coefficient of q q = mk_glex_monic(q); scoped_numeral c(m()); m().set(c, lc_g); q = mul(c, q); } TRACE("mgcd", tout << "new q:\n" << q << "\n";); if (is_const(q)) { TRACE("mgcd", tout << "done, modular gcd is one\n";); if (m().is_one(d_a)) r = q; // GCD is one else r = mk_const(d_a); return; } if (C_star.get() == 0) { C_star = q; m().set(bound, p); } else { if (degree(q, x) < degree(C_star, x)) { // discard accumulated image, it was affected by unlucky primes TRACE("mgcd", tout << "discarding image\n";); C_star = q; m().set(bound, p); } else { CRA_combine_images(q, p, C_star, bound, C_star); TRACE("mgcd", tout << "new combined:\n" << C_star << "\n";); } } pp(C_star, x, candidate); TRACE("mgcd", tout << "candidate:\n" << candidate << "\n";); scoped_numeral lc_candidate(m()); lc_candidate = univ_coeff(candidate, degree(candidate, x)); if (m().divides(lc_candidate, lc_g) && divides(candidate, pp_u) && divides(candidate, pp_v)) { TRACE("mgcd", tout << "found GCD\n";); r = mul(d_a, candidate); flip_sign_if_lm_neg(r); TRACE("mgcd", tout << "r: " << r << "\n";); return; } } // Oops, modular GCD failed, not enough primes // fallback to prs gcd_prs(u, v, x, r); } typedef ref_buffer polynomial_ref_buffer; /** Compute the content and primitive parts of p, when p is viewed as a multivariate polynomial Zp[y_1, ..., y_n] with coefficients in Zp[x]. */ som_buffer_vector m_iccp_ZpX_buffers; void iccp_ZpX(polynomial const * p, var x, numeral & ci, polynomial_ref & c, polynomial_ref & pp) { SASSERT(m().modular()); TRACE("mgcd_detail", tout << "iccp_ZpX, p: "; p->display(tout, m()); tout << "\nvar x" << x << "\n";); if (is_zero(p)) { TRACE("mgcd_detail", tout << "iccp_ZpX, p is zero\n";); m_manager.set(ci, 0); c = mk_one(); pp = const_cast(p); return; } if (is_const(p)) { TRACE("mgcd_detail", tout << "iccp_ZpX, p is constant\n";); m_manager.set(ci, p->a(0)); c = mk_one(); pp = mk_one(); return; } unsigned d = degree(p, x); if (d == 0) { TRACE("mgcd_detail", tout << "iccp_ZpX, degree(p, x) == 0\n";); ic(p, ci, pp); c = mk_one(); return; } // 1) traverse monomials of p, and mark the monomials that contain p, also compute the minimal degree of x in p. ref_buffer no_x_ms(m_wrapper); // monomials that do not contains x unsigned min_degree = UINT_MAX; // min degree of x in p unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned k = m->degree_of(x); if (k == 0) { // if m is not marked if (m_m2pos.get(m) == UINT_MAX) { no_x_ms.push_back(m); m_m2pos.set(m, 1); // it is just a mark } } if (k < min_degree) min_degree = k; } SASSERT(min_degree == 0 || no_x_ms.empty()); if (min_degree > 0) { SASSERT(no_x_ms.empty()); // nothing was marked. // divide by x^min_degree TRACE("mgcd_detail", tout << "iccp_ZpX, all monomials contain x" << x << ", dividing by x" << x << "^" << min_degree << "\n";); polynomial_ref xmin(m_wrapper); polynomial_ref new_p(m_wrapper); xmin = mk_polynomial(x, min_degree); new_p = exact_div(p, xmin); iccp_ZpX(new_p, x, ci, c, pp); c = mul(xmin, c); return; } // 2) if for some marked monomial m (i.e., the ones that do not contain x), there is no monomial m*x^k in p, // then c = 1 unsigned num_marked = no_x_ms.size(); unsigned num_unmarked = 0; monomial_ref tmp_m(m_wrapper); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned k = m->degree_of(x); if (k == 0) continue; tmp_m = div_x(m, x); SASSERT(tmp_m != m); // since x is in m, but not in tmp_m if (m_m2pos.get(tmp_m) == 1) { num_unmarked++; m_m2pos.reset(tmp_m); SASSERT(m_m2pos.get(tmp_m) == UINT_MAX); } } SASSERT(num_unmarked <= num_marked); if (num_unmarked < num_marked) { // reset remaining marks for (unsigned i = 0; i < num_marked; i++) m_m2pos.reset(no_x_ms[i]); TRACE("mgcd_detail", tout << "iccp_ZpX, cheap case... invoking ic\n";); ic(p, ci, pp); c = mk_one(); return; } // 3) expensive case // Basic idea: separate a*m*x^k into a*x^k and m, put a*x^k into the som_buffer associated with m. // The mapping between m is som_buffers is given by m_m2pos // Extract integer content ic(p, ci, pp); no_x_ms.reset(); som_buffer_vector & som_buffers = m_iccp_ZpX_buffers; som_buffers.set_owner(this); for (unsigned i = 0; i < sz; i++) { monomial * m = pp->m(i); unsigned k = m->degree_of(x); if (k != 0) { tmp_m = div_x(m, x); m = tmp_m.get(); } unsigned pos = m_m2pos.get(m); if (pos == UINT_MAX) { pos = no_x_ms.size(); no_x_ms.push_back(m); m_m2pos.set(m, pos); } som_buffer * som = som_buffers[pos]; som->add(pp->a(i), mk_monomial(x, k)); } unsigned num_ms = no_x_ms.size(); for (unsigned i = 0; i < num_ms; i++) m_m2pos.reset(no_x_ms[i]); SASSERT(num_ms > 0); // Compute GCD of all som_buffers polynomial_ref g(m_wrapper); polynomial_ref new_g(m_wrapper); g = som_buffers[0]->mk(); for (unsigned i = 1; i < num_ms; i++) { polynomial_ref a(m_wrapper); a = som_buffers[i]->mk(); SASSERT(is_univariate(a)); euclid_gcd(g, a, new_g); g = new_g; if (is_const(g)) break; } if (!is_const(g)) { CTRACE("content_bug", !divides(g, pp), tout << "GF(" << m().m().to_string(m().p()) << ")\n"; tout << "pp: "; pp->display(tout, m()); tout << "\n"; tout << "var: x" << x << "\n"; tout << "content: " << g << "\n";); c = g; pp = exact_div(pp, c); } else { c = mk_one(); } som_buffers.reset(num_ms); } // Return the leading coefficient (with respect to glex) of p when // p is viewed as a multivariate polynomial Zp[y_1, ..., y_n] with coefficients in Zp[x]. polynomial * lc_glex_ZpX(polynomial const * p, var x) { // collect a*x^k of maximal monomial with respect to glex m_som_buffer.reset(); monomial_ref max_m(m_wrapper); monomial_ref tmp_m(m_wrapper); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned k = m->degree_of(x); if (k != 0) { tmp_m = div_x(m, x); m = tmp_m.get(); } if (max_m == 0 || graded_lex_compare(m, max_m) > 0) { // found new maximal monomial m_som_buffer.reset(); max_m = m; m_som_buffer.add(p->a(i), mk_monomial(x, k)); } else if (max_m == m) { // found another a*x^k of max_m m_som_buffer.add(p->a(i), mk_monomial(x, k)); } } SASSERT(!m_som_buffer.empty()); TRACE("mgcd_detail", tout << "maximal monomial: "; max_m->display(tout); tout << "\n";); return m_som_buffer.mk(); } // Wrapper for iccp_ZpX void primitive_ZpX(polynomial const * p, var x, polynomial_ref & pp) { scoped_numeral ci(m()); polynomial_ref c(m_wrapper); iccp_ZpX(p, x, ci, c, pp); } // select a new random value in GF(p) that is not in vals, and store it in r void peek_fresh(scoped_numeral_vector const & vals, unsigned p, scoped_numeral & r) { SASSERT(vals.size() < p); // otherwise we cant keep the fresh value unsigned sz = vals.size(); while (true) { m().set(r, rand() % p); // check if fresh value... unsigned k = 0; for (; k < sz; k++) { if (m().eq(vals[k], r)) break; } if (k == sz) return; // value is fresh } } newton_interpolator_vector m_mgcd_iterpolators; scoped_ptr_vector m_mgcd_skeletons; struct sparse_mgcd_failed {}; // Auxiliary recursive function used in multivariate modular GCD void mod_gcd_rec(polynomial const * u, polynomial const * v, unsigned p, unsigned idx, var_buffer const & vars, polynomial_ref & r) { TRACE("mgcd", tout << "mod_gcd_rec\nu: "; u->display(tout, m_manager, true); tout << "\nv: "; v->display(tout, m_manager, true); tout << "\n";); unsigned num_vars = vars.size(); SASSERT(num_vars > 0); if (idx == num_vars - 1) { SASSERT(is_univariate(u)); SASSERT(is_univariate(v)); euclid_gcd(u, v, r); TRACE("mgcd", tout << "mod_gcd_rec result: "; r->display(tout, m_manager, true); tout << "\n";); return; } var x = vars[idx]; scoped_numeral ci_u(m()), ci_v(m()); polynomial_ref c_u(m_wrapper), pp_u(m_wrapper), lc_u(m_wrapper); polynomial_ref c_v(m_wrapper), pp_v(m_wrapper), lc_v(m_wrapper); iccp_ZpX(u, x, ci_u, c_u, pp_u); iccp_ZpX(v, x, ci_v, c_v, pp_v); lc_u = lc_glex_ZpX(pp_u, x); lc_v = lc_glex_ZpX(pp_v, x); scoped_numeral ci_g(m()); polynomial_ref c_g(m_wrapper); polynomial_ref lc_g(m_wrapper); TRACE("mgcd_detail", tout << "idx: " << idx << "\n"; tout << "x" << x << "\n"; tout << "pp_u = "; pp_u->display(tout, m_manager, true); tout << "\n"; tout << "pp_v = "; pp_v->display(tout, m_manager, true); tout << "\n"; tout << "c_u = "; c_u->display(tout, m_manager, true); tout << "\n"; tout << "c_v = "; c_v->display(tout, m_manager, true); tout << "\n"; tout << "lc_u = "; lc_u->display(tout, m_manager, true); tout << "\n"; tout << "lc_v = "; lc_v->display(tout, m_manager, true); tout << "\n"; tout << "ci_u = " << ci_u << "\n"; tout << "ci_v = " << ci_v << "\n";); m().gcd(ci_u, ci_v, ci_g); euclid_gcd(c_u, c_v, c_g); euclid_gcd(lc_u, lc_v, lc_g); TRACE("mgcd_detail", tout << "c_g = "; c_g->display(tout, m_manager, true); tout << "\n"; tout << "lc_g = "; lc_g->display(tout, m_manager, true); tout << "\n"; tout << "ci_g = " << ci_g << "\n";); skeleton * sk = m_mgcd_skeletons[idx]; // use dense interpolation if skeleton is not available newton_interpolator & interpolator = m_mgcd_iterpolators[idx]; sparse_interpolator sinterpolator(sk); polynomial_ref u1(m_wrapper), v1(m_wrapper), q(m_wrapper); scoped_numeral val(m()); scoped_numeral lc_g_val(m()); polynomial_ref H(m_wrapper), C(m_wrapper); polynomial_ref lc_H(m_wrapper); unsigned min_deg_q = UINT_MAX; unsigned counter = 0; for (;; counter++) { while (true) { peek_fresh(interpolator.inputs(), p, val); // the selected value must satisfy lc_g(val) != 0 univ_eval(lc_g, x, val, lc_g_val); if (!m().is_zero(lc_g_val)) break; } TRACE("mgcd", tout << "x" << x << " -> " << val << "\n";); u1 = substitute(pp_u, 1, &x, &(val.get())); v1 = substitute(pp_v, 1, &x, &(val.get())); mod_gcd_rec(u1, v1, p, idx+1, vars, q); q = mk_glex_monic(q); q = mul(lc_g_val, q); var q_var = max_var(q); unsigned deg_q = q_var == null_var ? 0 : degree(q, q_var); TRACE("mgcd_detail", tout << "counter: " << counter << "\nidx: " << idx << "\nq: " << q << "\ndeg_q: " << deg_q << "\nmin_deg_q: " << min_deg_q << "\nnext_x: x" << vars[idx+1] << "\nmax_var(q): " << q_var << "\n";); if (deg_q < min_deg_q) { TRACE("mgcd_detail", tout << "reseting...\n";); counter = 0; min_deg_q = deg_q; // start from scratch if (sk == 0) { interpolator.reset(); interpolator.add(val, q); } else { sinterpolator.reset(); if (!sinterpolator.add(val, q)) throw sparse_mgcd_failed(); } } else if (deg_q == min_deg_q) { TRACE("mgcd_detail", tout << "adding sample point...\n";); if (sk == 0) { interpolator.add(val, q); } else { if (!sinterpolator.add(val, q)) throw sparse_mgcd_failed(); } } else { TRACE("mgcd", tout << "skipping q...\n";); continue; } bool found_candidate = false; if (sk == 0) { SASSERT(interpolator.num_sample_points() > 0); interpolator.mk(x, H); TRACE("mgcd_detail", tout << "idx: " << idx << "\ncandidate H: " << H << "\n";); lc_H = lc_glex_ZpX(H, x); TRACE("mgcd_detail", tout << "idx: " << idx << "\nlc_H: " << lc_H << "\nlc_g: " << lc_g << "\n";); if (eq(lc_H, lc_g)) { found_candidate = true; } } else { if (sinterpolator.ready()) { if (!sinterpolator.mk(H)) throw sparse_mgcd_failed(); found_candidate = true; } } bool done = false; if (found_candidate) { if (degree(H, x) > 0) primitive_ZpX(H, x, C); else C = normalize(H); TRACE("mgcd_detail", tout << "C: " << C << "\npp_u: " << pp_u << "\npp_v: " << pp_v << "\ndivides(C, pp_u): " << divides(C, pp_u) << "\ndivides(C, pp_v): " << divides(C, pp_v) << "\n";); if (divides(C, pp_u) && divides(C, pp_v)) { r = mul(c_g, C); r = mul(ci_g, r); done = true; } else if (min_deg_q == 0) { r = c_g; r = mul(ci_g, r); done = true; } else if (sk != 0) { throw sparse_mgcd_failed(); } } if (done) { TRACE("mgcd", tout << "idx: " << idx << "\nresult: " << r << "\n";); if (sk == 0 && m_use_sparse_gcd) { // save skeleton skeleton * new_sk = alloc(skeleton, *this, H, x); m_mgcd_skeletons.set(idx, new_sk); } return; } } } // Multivariate modular GCD algorithm void mod_gcd(polynomial const * u, polynomial const * v, power_buffer const & u_var_degrees, power_buffer const & v_var_degrees, polynomial_ref & r) { m_mgcd_iterpolators.set_owner(this); TRACE("mgcd", tout << "mod_gcd\nu: "; u->display(tout, m_manager, true); tout << "\nv: "; v->display(tout, m_manager, true); tout << "\n";); TRACE("mgcd_call", tout << "mod_gcd\nu: "; u->display(tout, m_manager, true); tout << "\nv: "; v->display(tout, m_manager, true); tout << "\n";); SASSERT(!m().modular()); // u and v contain the same set of variables SASSERT(u_var_degrees.size() == v_var_degrees.size()); unsigned num_vars = u_var_degrees.size(); SASSERT(num_vars > 1); // should use uni_mod_gcd if univariate var_buffer vars; power_buffer var_min_degrees; for (unsigned i = 0; i < num_vars; i++) { SASSERT(u_var_degrees[i].get_var() == v_var_degrees[i].get_var()); var x = u_var_degrees[i].get_var(); unsigned d = std::min(u_var_degrees[i].degree(), v_var_degrees[i].degree()); var_min_degrees.push_back(power(x, d)); } std::sort(var_min_degrees.begin(), var_min_degrees.end(), power::lt_degree()); m_mgcd_skeletons.reset(); for (unsigned i = 0; i < num_vars; i++) { vars.push_back(var_min_degrees[i].get_var()); m_mgcd_skeletons.push_back(0); } scoped_numeral c_u(m()), c_v(m()); polynomial_ref pp_u(pm()), pp_v(pm()); ic(u, c_u, pp_u); ic(v, c_v, pp_v); scoped_numeral d_a(m()); m_manager.gcd(c_u, c_v, d_a); unsigned mm_u_pos = pp_u->graded_lex_max_pos(); // position of the maximal monomial in u unsigned mm_v_pos = pp_v->graded_lex_max_pos(); // position of the maximal monomial in v scoped_numeral lc_u(m()); scoped_numeral lc_v(m()); lc_u = pp_u->a(mm_u_pos); lc_v = pp_v->a(mm_v_pos); scoped_numeral lc_g(m()); m().gcd(lc_u, lc_v, lc_g); polynomial_ref u_Zp(m_wrapper); polynomial_ref v_Zp(m_wrapper); polynomial_ref C_star(m_wrapper); scoped_numeral bound(m()); polynomial_ref q(m_wrapper); polynomial_ref candidate(m_wrapper); scoped_numeral p(m()); for (unsigned i = 0; i < NUM_BIG_PRIMES; i++) { m().set(p, g_big_primes[i]); TRACE("mgcd", tout << "trying prime: " << p << "\n";); { scoped_set_zp setZp(m_wrapper, p); u_Zp = normalize(pp_u); if (u_Zp->size() != pp_u->size()) { TRACE("mgcd", tout << "bad prime, coefficient(s) vanished\n";); continue; // bad prime some monomial vanished } v_Zp = normalize(pp_v); if (v_Zp->size() != pp_v->size()) { TRACE("mgcd", tout << "bad prime, coefficient(s) vanished\n";); continue; // bad prime some monomial vanished } TRACE("mgcd", tout << "u_Zp: " << u_Zp << "\nv_Zp: " << v_Zp << "\n";); mod_gcd_rec(u_Zp, v_Zp, g_big_primes[i], 0, vars, q); q = mk_glex_monic(q); scoped_numeral c(m()); m().set(c, lc_g); q = mul(c, q); } TRACE("mgcd", tout << "new q:\n" << q << "\n";); if (is_const(q)) { TRACE("mgcd", tout << "done, modular gcd is one\n";); r = mk_const(d_a); return; } if (C_star.get() == 0) { C_star = q; m().set(bound, p); } else { monomial * max_C_star = C_star->m(C_star->graded_lex_max_pos()); monomial * max_q = q->m(q->graded_lex_max_pos()); if (graded_lex_compare(max_q, max_C_star) < 0) { // Discard accumulated image, it was affected by unlucky primes // maximal monomial of q is smaller than maximal monomial of C_star TRACE("mgcd", tout << "discarding image\n";); C_star = q; m().set(bound, p); } else { CRA_combine_images(q, p, C_star, bound, C_star); TRACE("mgcd", tout << "new combined:\n" << C_star << "\n";); } } candidate = normalize(C_star); TRACE("mgcd", tout << "candidate:\n" << candidate << "\n";); scoped_numeral lc_candidate(m()); lc_candidate = candidate->a(candidate->graded_lex_max_pos()); if (m().divides(lc_candidate, lc_g) && divides(candidate, pp_u) && divides(candidate, pp_v)) { TRACE("mgcd", tout << "found GCD\n";); r = mul(d_a, candidate); flip_sign_if_lm_neg(r); TRACE("mgcd", tout << "r: " << r << "\n";); return; } } // Oops, modular GCD failed, not enough primes // fallback to prs gcd_prs(u, v, max_var(u), r); } void gcd(polynomial const * u, polynomial const * v, polynomial_ref & r) { power_buffer u_var_degrees; power_buffer v_var_degrees; TRACE("gcd_calls", tout << "gcd\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\n";); TRACE("polynomial_gcd", tout << "gcd\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\nis_zero(u): " << is_zero(u) << ", is_const(u): " << is_const(u) << "\n"; tout << "is_zero(v): " << is_zero(v) << ", is_const(v): " << is_const(v) << "\n"; tout << "modular: " << m().modular() << "\n";); if (is_zero(u)) { r = const_cast(v); flip_sign_if_lm_neg(r); return; } if (is_zero(v)) { r = const_cast(u); flip_sign_if_lm_neg(r); return; } if (u == v) { r = const_cast(u); flip_sign_if_lm_neg(r); return; } if (is_const(u) || is_const(v)) { scoped_numeral i_u(m_manager), i_v(m_manager); ic(v, i_v); ic(u, i_u); scoped_numeral a(m_manager); m_manager.gcd(i_v, i_u, a); r = mk_const(a); return; } // Search for a variable x that occurs only in u or v. var_max_degrees(u, u_var_degrees); std::sort(u_var_degrees.begin(), u_var_degrees.end(), power::lt_var()); var_max_degrees(v, v_var_degrees); std::sort(v_var_degrees.begin(), v_var_degrees.end(), power::lt_var()); TRACE("polynomial_gcd", tout << "u var info\n"; for (unsigned i = 0; i < u_var_degrees.size(); i++) tout << u_var_degrees[i] << " "; tout << "\n"; tout << "v var info\n"; for (unsigned i = 0; i < v_var_degrees.size(); i++) tout << v_var_degrees[i] << " "; tout << "\n";); var x = null_var; bool u_found = false; bool v_found = false; unsigned i = 0; unsigned u_sz = u_var_degrees.size(); unsigned v_sz = v_var_degrees.size(); unsigned sz = std::min(u_sz, v_sz); for (; i < sz; i++) { var xu = u_var_degrees[i].get_var(); var xv = v_var_degrees[i].get_var(); if (xu < xv) { x = xu; u_found = true; break; } if (xu > xv) { x = xv; v_found = true; break; } } if (!u_found && !v_found && i < u_sz) { x = u_var_degrees[i].get_var(); u_found = true; } if (!u_found && !v_found && i < v_sz) { x = v_var_degrees[i].get_var(); v_found = true; } if (u_found) { // u has a variable x that v doesn't have. // Thus, gcd(u, v) = gcd(content(u, x), v) gcd_content(u, x, v, r); return; } if (v_found) { // v has a variable x that u doesn't have. // Thus, gcd(u, v) = gcd(u, content(v, x)) gcd_content(v, x, u, r); return; } // TODO: // Try to find a variable x that occurs linearly in u or v // In this case, the GCD is linear or constant in x. // Assume x occurs linearly in u. Then, // gcd(u, v) = gcd(content(u, x), content(v, x)) if pp(u, x) does not divide pp(v, x) // gcd(u, v) = gcd(content(u, x), content(v, x))*pp(u,x) if pp(u, x) divides pp(v, x) // // select variable with minimal degree x = u_var_degrees[sz - 1].get_var(); // give preference to maximal variable SASSERT(u_var_degrees[sz - 1].get_var() == v_var_degrees[sz - 1].get_var()); SASSERT(max_var(u) == max_var(v)); SASSERT(max_var(u) == x); #if 0 unsigned min_k = std::max(m_u_var_degrees[sz - 1].degree(), m_v_var_degrees[sz - 1].degree()); unsigned max_var_bias = 2; // the basic procedures are optimized for operating on the maximal variable. So, we have a bias to select the maximal one if (min_k > max_var_bias) { min_k -= max_var_bias; i = sz - 1; while (i > 0) { --i; SASSERT(m_u_var_degrees[i].get_var() == m_v_var_degrees[i].get_var()); unsigned k = std::max(m_u_var_degrees[i].degree(), m_v_var_degrees[i].degree()); if (k < min_k) { x = m_u_var_degrees[i].get_var(); min_k = k; } } } #endif if (!m().modular() && !m_use_prs_gcd) { SASSERT(max_var(u) == max_var(v)); if (is_univariate(u)) { SASSERT(is_univariate(v)); uni_mod_gcd(u, v, r); } else { try { #ifdef Z3DEBUG polynomial_ref orig_u(m_wrapper); polynomial_ref orig_v(m_wrapper); if (is_debug_enabled("mgcd_check")) { orig_u = const_cast(u); orig_v = const_cast(v); } #endif mod_gcd(u, v, u_var_degrees, v_var_degrees, r); #ifdef Z3DEBUG if (is_debug_enabled("mgcd_check")) { polynomial_ref r2(m_wrapper); flet use_prs(m_use_prs_gcd, false); gcd_prs(orig_u, orig_v, x, r2); CTRACE("mgcd_bug", !eq(r, r2), tout << "u: " << orig_u << "\nv: " << orig_v << "\nr1: " << r << "\nr2: " << r2 << "\n";); SASSERT(eq(r, r2)); } #endif } catch (sparse_mgcd_failed) { flet use_prs(m_use_prs_gcd, false); gcd_prs(u, v, x, r); } } } else { gcd_prs(u, v, x, r); } } monomial * derivative(monomial const * m, var x) { return mm().derivative(m, x); } polynomial * derivative(polynomial const * p, var x) { SASSERT(is_valid(x)); SASSERT(m_cheap_som_buffer.empty()); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned d = m->degree_of(x); TRACE("polynomial", m->display(tout); tout << " degree_of x" << x << ": " << d << "\n";); if (d > 0) { scoped_numeral n(m_manager); m_manager.set(n, d); scoped_numeral a(m_manager); m_manager.mul(p->a(i), n, a); m_cheap_som_buffer.add_reset(a, derivative(m, x)); } } return m_cheap_som_buffer.mk(); } void square_free(polynomial const * p, polynomial_ref & r) { if (is_zero(p)) { r = mk_zero(); return; } if (is_const(p)) { r = const_cast(p); return; } var x = max_var(p); scoped_numeral i(m_manager); polynomial_ref c(pm()), pp(pm()); iccp(p, x, i, c, pp); polynomial_ref sqf_c(pm()); square_free(c, sqf_c); polynomial_ref pp_prime(pm()); pp_prime = derivative(pp, x); polynomial_ref g(pm()); gcd(pp, pp_prime, g); if (is_const(g)) { if (eq(sqf_c, c)) { r = const_cast(p); return; } } else { pp = exact_div(pp, g); } r = mul(i, sqf_c); r = mul(r, pp); } bool is_square_free(polynomial const * p) { polynomial_ref r(pm()); square_free(p, r); SASSERT(!eq(p, r) || p == r.get()); // this is a property of the square_free procedure return p == r.get(); } void square_free(polynomial const * p, var x, polynomial_ref & r) { if (is_zero(p)) { r = mk_zero(); return; } if (is_const(p)) { r = const_cast(p); return; } polynomial_ref p_prime(pm()); p_prime = derivative(p, x); polynomial_ref g(pm()); gcd(p, p_prime, g); if (is_const(g)) { r = const_cast(p); } else { r = exact_div(p, g); } } bool is_square_free(polynomial const * p, var x) { polynomial_ref r(pm()); square_free(p, x, r); SASSERT(!eq(p, r) || p == r.get()); // this is a property of the square_free procedure return p == r.get(); } void pw(polynomial const * p, unsigned k, polynomial_ref & r) { if (k == 0) { r = mk_one(); return; } if (k == 1) { r = const_cast(p); return; } polynomial_ref result(pm()); result = const_cast(p); for (unsigned i = 1; i < k; i++) result = mul(result, const_cast(p)); r = result; #if 0 SASSERT(k >= 2); unsigned mask = 1; polynomial_ref p2(pm()); p2 = const_cast(p); r = mk_one(); while (true) { if (mask & k) r = mul(r, p2); mask = mask << 1; if (mask > k) return; p2 = mul(p2, p2); } #endif } bool eq(polynomial const * p1, polynomial const * p2) { if (p1 == p2) return true; unsigned sz1 = p1->size(); unsigned sz2 = p2->size(); if (sz1 != sz2) return false; if (sz1 == 0) return true; if (max_var(p1) != max_var(p2)) return false; m_m2pos.set(p1); for (unsigned i = 0; i < sz2; i++) { unsigned pos1 = m_m2pos.get(p2->m(i)); if (pos1 == UINT_MAX || !m_manager.eq(p1->a(pos1), p2->a(i))) { m_m2pos.reset(p1); return false; } } m_m2pos.reset(p1); return true; } polynomial * compose_1_div_x(polynomial const * p) { SASSERT(is_univariate(p)); if (is_const(p)) return const_cast(p); SASSERT(m_cheap_som_buffer.empty()); var x = max_var(p); unsigned n = degree(p, x); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); SASSERT(m->size() <= 1); monomial * new_m = mk_monomial(x, n - m->degree_of(x)); m_cheap_som_buffer.add(p->a(i), new_m); } return m_cheap_som_buffer.mk(); } void push_power(sbuffer & pws, var x, unsigned d) { if (d > 0) pws.push_back(power(x, d)); } polynomial * compose_x_div_y(polynomial const * p, var y) { SASSERT(is_univariate(p)); SASSERT(max_var(p) != y); if (is_const(p)) return const_cast(p); SASSERT(m_cheap_som_buffer.empty()); var x = max_var(p); unsigned n = degree(p, x); unsigned sz = p->size(); sbuffer pws; for (unsigned i = 0; i < sz; i++) { unsigned k = p->m(i)->degree_of(x); pws.reset(); if (x < y) { push_power(pws, x, k); push_power(pws, y, n - k); } else { push_power(pws, y, n - k); push_power(pws, x, k); } monomial * new_m = mk_monomial(pws.size(), pws.c_ptr()); m_cheap_som_buffer.add(p->a(i), new_m); } return m_cheap_som_buffer.mk(); } polynomial * compose_y(polynomial const * p, var y) { SASSERT(is_valid(y)); SASSERT(is_univariate(p)); if (y == max_var(p) || is_const(p)) return const_cast(p); SASSERT(m_cheap_som_buffer.empty()); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); SASSERT(m->size() <= 1); monomial * new_m; if (m->size() == 0) new_m = m; else new_m = mk_monomial(y, m->degree(0)); m_cheap_som_buffer.add(p->a(i), new_m); } return m_cheap_som_buffer.mk(); } polynomial * compose_minus_x(polynomial const * p) { SASSERT(is_univariate(p)); if (is_const(p)) return const_cast(p); SASSERT(m_cheap_som_buffer.empty()); scoped_numeral a(m_manager); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); if (m->total_degree() % 2 == 0) { m_cheap_som_buffer.add(p->a(i), p->m(i)); } else { m_manager.set(a, p->a(i)); m_manager.neg(a); m_cheap_som_buffer.add(a, p->m(i)); } } return m_cheap_som_buffer.mk(); } /** \brief Store the positions (in m_degree2pos) of the monomials of an univariate polynomial. */ void save_degree2pos(polynomial const * p) { SASSERT(is_univariate(p)); var x = max_var(p); unsigned n = degree(p, x); m_degree2pos.reserve(n+1, UINT_MAX); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); SASSERT(m->size() <= 1); SASSERT(m_degree2pos[m->total_degree()] == UINT_MAX); m_degree2pos[m->total_degree()] = i; } } /** \brief Undo the modifications in m_degree2pos performed by save_degree2pos. */ void reset_degree2pos(polynomial const * p) { SASSERT(is_univariate(p)); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); SASSERT(m->size() <= 1); SASSERT(m_degree2pos[m->total_degree()] == i); m_degree2pos[m->total_degree()] = UINT_MAX; } } // muladd may throw if the cancel flag is set. // So we wrap the degree2pos set and reset // in a scoped class to ensure the state is clean // on exit. struct scoped_degree2pos { imp& pm; polynomial const* p; scoped_degree2pos(imp& pm, polynomial const* p): pm(pm), p(p) { pm.save_degree2pos(p); } ~scoped_degree2pos() { pm.reset_degree2pos(p); } }; /** \brief Given an univariate polynomial p(x) and a polynomial q(y_1, ..., y_n), return a polynomial r(y_1, ..., y_n) = p(q(y_1, ..., y_n)). */ void compose(polynomial const * p, polynomial const * q, polynomial_ref & r) { SASSERT(is_univariate(p)); if (is_const(p)) { r = const_cast(p); return; } var x = max_var(p); unsigned d = degree(p, x); scoped_degree2pos _sd2pos(*this, p); scoped_numeral a(m()); m_manager.set(a, p->a(m_degree2pos[d])); r = mk_const(a); for (unsigned i = 1; i <= d; i++) { unsigned pos = m_degree2pos[d-i]; if (pos != UINT_MAX) m_manager.set(a, p->a(pos)); else m_manager.reset(a); r = muladd(q, r, a); } } polynomial * mk_x_minus_y(var x, var y) { numeral zero(0); numeral one(1); numeral minus_one; // It is not safe to initialize with -1 when numeral_manager is GF_2 m_manager.set(minus_one, -1); numeral as[2] = { one, minus_one }; var xs[2] = { x, y }; return mk_linear(2, as, xs, zero); } void compose_x_minus_y(polynomial const * p, var y, polynomial_ref & r) { SASSERT(is_valid(y)); SASSERT(is_univariate(p)); var x = max_var(p); if (y == max_var(p)) { r = coeff(p, x, 0); return; } polynomial_ref x_minus_y(pm()); x_minus_y = mk_x_minus_y(x, y); return compose(p, x_minus_y, r); } polynomial * mk_x_plus_y(var x, var y) { numeral zero(0); numeral one(1); numeral as[2] = { one, one }; var xs[2] = { x, y }; return mk_linear(2, as, xs, zero); } void compose_x_plus_y(polynomial const * p, var y, polynomial_ref & r) { SASSERT(is_valid(y)); SASSERT(is_univariate(p)); var x = max_var(p); polynomial_ref x_plus_y(pm()); x_plus_y = mk_x_plus_y(x, y); return compose(p, x_plus_y, r); } // Return the polynomial x - c polynomial * mk_x_minus_c(var x, numeral const & c) { numeral as[2]; m_manager.set(as[0], c); m_manager.set(as[1], 1); m_manager.neg(as[0]); polynomial * p = mk_univariate(x, 1, as); TRACE("polynomial", tout << "x - c: "; p->display(tout, m_manager); tout << "\n";); m_manager.del(as[0]); m_manager.del(as[1]); return p; } void compose_x_minus_c(polynomial const * p, numeral const & c, polynomial_ref & r) { SASSERT(is_univariate(p)); if (m_manager.is_zero(c)) { r = const_cast(p); return; } var x = max_var(p); polynomial_ref x_minus_c(pm()); x_minus_c = mk_x_minus_c(x, c); return compose(p, x_minus_c, r); } /** \brief Template for computing several variations of pseudo division algorithm. If degree(p) < degree(q) --> Q = m_zero, d = 0, R = p The following property is guaranteed by this method l(q)^d * p = Q * q + R where l(q) is coeff(q, x, degree(q, x)) Possible configurations: Exact_d == true, then d = degree(p) - degree(q) + 1. If Exact_d == false, then d <= degree(p) - degree(q) + 1. If Quotient == false, Q is not computed. If ModD == true, then x2d must be different from 0. Moreover, p and q are assumed to be normalized modulo x2d. For all x, x2d->degree(x) > 0 implies degree(p, x) < x2d->degree(x) and degree(q, x) < x2d->degree(x) Moreover, the division algorithm will compute Q and R modulo x2d. */ template void pseudo_division_core(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R, var2degree const * x2d = 0) { SASSERT(is_valid(x)); SASSERT(!ModD || x2d != 0); TRACE("polynomial", tout << "pseudo_division\np: "; p->display(tout, m_manager); tout << "\nq: "; q->display(tout, m_manager); tout << "\nx: " << x << "\n";); polynomial * A = const_cast(p); polynomial * B = const_cast(q); unsigned deg_A = degree(A, x); unsigned deg_B = degree(B, x); if (deg_B == 0) { R = m_zero; if (Quotient) { if (Exact_d) { d = deg_A /* - deg_B */ + 1; // Since degree(B) = 0, lc(B) == B // lc(B)^d * A = Q * B + R --> (using R == 0, lc(B) == B) // B^d * A = Q * B // Thus, Q = A * B^{d-1} if (d == 1) { Q = A; return; } polynomial_ref Bdm1(pm()); pw(B, d - 1, Bdm1); Q = mul(A, Bdm1); if (ModD) Q = mod_d(Q, *x2d); } else { d = 1; Q = A; } } return; } if (deg_B > deg_A) { Q = m_zero; R = A; d = 0; } scoped_numeral minus_a(m_manager); polynomial_ref l_B(pm()); // leading coefficient of B (that is, coefficient of x^(deg_B)) polynomial_ref r_B(pm()); // reduct of B (that is, B without leading coefficient) l_B = coeff(B, x, deg_B, r_B); d = 0; R = A; Q = m_zero; while (true) { checkpoint(); TRACE("polynomial", tout << "A: "; A->display(tout, m_manager); tout << "\n"; tout << "B: "; B->display(tout, m_manager); tout << "\n"; tout << "l_B: "; l_B->display(tout, m_manager); tout << "\n"; tout << "r_B: "; r_B->display(tout, m_manager); tout << "\n"; tout << "Q: "; Q->display(tout, m_manager); tout << "\n"; tout << "R: "; R->display(tout, m_manager); tout << "\n"; tout << "d: " << d << "\n";); unsigned deg_R = degree(R, x); if (deg_B > deg_R) { if (Exact_d) { // Adjust Q and R unsigned exact_d = deg_A - deg_B + 1; SASSERT(d <= exact_d); if (d < exact_d) { unsigned e = exact_d - d; polynomial_ref l_B_e(pm()); pw(l_B, e, l_B_e); TRACE("polynomial", tout << "l_B_e: " << l_B_e << "\n";); if (Quotient) { Q = mul(l_B_e, Q); if (ModD) Q = mod_d(Q, *x2d); } R = mul(l_B_e, R); if (ModD) R = mod_d(R, *x2d); } } return; } // S <- l_R * x^(deg_R - deg_B) // R <- l_B * R - S * B // Note that the goal is to cancel x^deg_R in R. // m_som_buffer will contain the new monomials of R. m_som_buffer.reset(); // We can accomplish that by traversing the current R, and // - For each monomial a * m * x^deg_R --> m_som_buffer.addmul(-a, m * x^(deg_R - deg_B), r_B) // Note that m*x^(deg_R - deg_B) is div_x_k(m*x^deg_R, deg_B) // - For other monomials a*m, ---> m_som_buffer.addmul(a, m, l_B) // Note that, with this trick and don't need to create the temporary polynomials l_B * R and S * B // // If the quotient needs to be computed, we have that // S <- l_R * x^(deg_R - deg_B) // Q <- l_B * Q + S // The new monomials of Q are stored in m_som_buffer2 // When traversing R, for each monomial a*m*x^deg_R we add m_som_buffer2.add(a, m*x^(deg_R - deg_B)) m_som_buffer2.reset(); // unsigned sz = R->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = R->m(i); numeral const & a = R->a(i); if (m->degree_of(x) == deg_R) { monomial_ref m_prime(pm()); m_prime = div_x_k(m, x, deg_B); CTRACE("polynomial", m->degree_of(x) != deg_R - deg_B, tout << "deg_R: " << deg_R << ", deg_B: " << deg_B << ", x: " << x << "\n"; m->display(tout); tout << ", "; m_prime->display(tout); tout << "\n";); SASSERT(m->degree_of(x) == deg_R); SASSERT(m_prime->degree_of(x) == deg_R - deg_B); if (Quotient) { m_som_buffer2.add(a, m_prime); } m_manager.set(minus_a, a); m_manager.neg(minus_a); m_som_buffer.addmul(minus_a, m_prime, r_B); } else { m_som_buffer.addmul(a, m, l_B); } } if (ModD) m_som_buffer.mod_d(*x2d); R = m_som_buffer.mk(); if (Quotient) { // m_som_buffer2 contains new monomials of Q <- l_B Q + S // We have already copied S to m_som_buffer2. // To add l_B * Q, we just traverse Q executing addmul(Q->a(i), Q->m(i), l_B) unsigned sz = Q->size(); for (unsigned i = 0; i < sz; i++) { m_som_buffer2.addmul(Q->a(i), Q->m(i), l_B); } if (ModD) m_som_buffer2.mod_d(*x2d); Q = m_som_buffer2.mk(); } d++; } } void exact_pseudo_remainder(polynomial const * p, polynomial const * q, var x, polynomial_ref & R) { unsigned d; polynomial_ref Q(pm()); pseudo_division_core(p, q, x, d, Q, R); } void pseudo_remainder(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & R) { #ifdef Z3DEBUG polynomial_ref old_p(pm()); polynomial_ref old_q(pm()); old_p = const_cast(p); // R may be aliasing p and q old_q = const_cast(q); polynomial_ref Q(pm()); pseudo_division_core(p, q, x, d, Q, R); // debugging code // check if lc(q)^d * p = Q * q + R polynomial_ref l(pm()); l = coeff(old_q, x, degree(q, x)); polynomial_ref ld(pm()); pw(l, d, ld); polynomial_ref lhs(pm()); lhs = mul(ld, old_p); polynomial_ref rhs(pm()); rhs = mul(Q, old_q); rhs = add(rhs, R); bool is_eq = eq(lhs, rhs); TRACE("pseudo_remainder", tout << "pseudo_division bug\n"; tout << "p: "; old_p->display(tout, m_manager); tout << "\n"; tout << "q: "; old_q->display(tout, m_manager); tout << "\n"; tout << "Q: " << Q << "\nR: " << R << "\n"; tout << "l^d: " << ld << "\nlhs: " << lhs << "\nrhs: " << rhs << "\n";); SASSERT(is_eq); #else polynomial_ref Q(pm()); pseudo_division_core(p, q, x, d, Q, R); #endif } void exact_pseudo_division(polynomial const * p, polynomial const * q, var x, polynomial_ref & Q, polynomial_ref & R) { unsigned d; pseudo_division_core(p, q, x, d, Q, R); } void pseudo_division(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R) { pseudo_division_core(p, q, x, d, Q, R); } polynomial * exact_div(polynomial const * p, numeral const & c) { SASSERT(!m().is_zero(c)); som_buffer & R = m_som_buffer; R.reset(); numeral tmp; unsigned sz = p->size(); for (unsigned i = 0; i < sz; ++ i) { SASSERT(m().divides(c, p->a(i))); m().div(p->a(i), c, tmp); if (!m().is_zero(tmp)) { R.add(tmp, p->m(i)); } } m().del(tmp); return R.mk(); } polynomial * exact_div(polynomial const * p, polynomial const * q) { TRACE("polynomial", tout << "exact division\np: "; p->display(tout, m_manager); tout << "\nq: "; q->display(tout, m_manager); tout << "\n";); if (is_zero(p)) return const_cast(p); SASSERT(!is_zero(q)); m_som_buffer.reset(); m_som_buffer2.reset(); som_buffer & R = m_som_buffer; som_buffer & C = m_som_buffer2; R.add(p); unsigned max_q = q->graded_lex_max_pos(); monomial * m_q = q->m(max_q); numeral const & a_q = q->a(max_q); monomial_ref m_r_q_ref(pm()); scoped_numeral a_r_q(m_manager); while (true) { checkpoint(); unsigned max_R = R.graded_lex_max_pos(); if (max_R == UINT_MAX) { // R is empty R.reset(); return C.mk(); } monomial const * m_r = R.m(max_R); numeral const & a_r = R.a(max_R); monomial * m_r_q = 0; VERIFY(div(m_r, m_q, m_r_q)); TRACE("polynomial", tout << "m_r: "; m_r->display(tout); tout << "\nm_q: "; m_q->display(tout); tout << "\n"; if (m_r_q) { tout << "m_r_q: "; m_r_q->display(tout); tout << "\n"; }); m_r_q_ref = m_r_q; m_manager.div(a_r, a_q, a_r_q); C.add(a_r_q, m_r_q); // C <- C + (a_r/a_q)*(m_r/m_q) m_manager.neg(a_r_q); R.addmul(a_r_q, m_r_q, q); // R <- R - (a_r/a_q)*(m_r/m_q)*Q } } // Return true if q divides p. bool divides(polynomial const * q, polynomial const * p) { TRACE("polynomial", tout << "divides\nq: "; q->display(tout, m_manager); tout << "\np: "; p->display(tout, m_manager); tout << "\n";); TRACE("divides", tout << "divides\nq: "; q->display(tout, m_manager); tout << "\np: "; p->display(tout, m_manager); tout << "\n";); if (is_zero(p)) return true; SASSERT(!is_zero(q)); m_som_buffer.reset(); m_som_buffer2.reset(); som_buffer & R = m_som_buffer; R.add(p); unsigned max_q = q->graded_lex_max_pos(); monomial * m_q = q->m(max_q); numeral const & a_q = q->a(max_q); monomial_ref m_r_q_ref(pm()); scoped_numeral a_r_q(m_manager); while (true) { checkpoint(); unsigned max_R = R.graded_lex_max_pos(); if (max_R == UINT_MAX) { // R is empty return true; } monomial const * m_r = R.m(max_R); numeral const & a_r = R.a(max_R); monomial * m_r_q = 0; bool q_div_r = div(m_r, m_q, m_r_q); m_r_q_ref = m_r_q; TRACE("polynomial", tout << "m_r: "; m_r->display(tout); tout << "\nm_q: "; m_q->display(tout); tout << "\n"; if (m_r_q) { tout << "m_r_q: "; m_r_q->display(tout); tout << "\n"; }); if (!q_div_r) return false; if (!m_manager.divides(a_q, a_r)) return false; m_manager.div(a_r, a_q, a_r_q); m_manager.neg(a_r_q); R.addmul(a_r_q, m_r_q, q); // R <- R - (a_r/a_q)*(m_r/m_q)*Q } } void quasi_resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r) { // For h_0 = p and h_1 = q, we compute the following sequence // using the pseudo remainder procedure // // l(h_1)^d_1 * h_0 = q_1 * h_1 + h_2 // l(h_2)^d_2 * h_1 = q_2 * h_2 + h_3 // l(h_3)^d_3 * h_2 = q_3 * h_3 + h_4 // ... // l(h_n)^d_n * h_{n-1} = q_n * h_n + h_{n+1} // // where // degree(h_i, x) > 0 for all i in [0, n], and // degree(h_{n+1}, x) == 0 // // l(h_i) is the leading coefficient of h_i with respect to variable x. // l(h_i) is in general a polynomial. // For example, l( y*x^2 + x^2 + y^2 x + 1 ) = y + 1 // // d^i is an unsigned integer. It is introduce by the pseudo remainder procedure // because the coefficients of x^k do not form a field. That is, they are elements of a polynomial ring Q[y_1, ..., y_n]. // Their values are irrelevant for the correctness of this procedure. // // Observation 1: // If h_0 and h_1 are polynomials in Q[y_1, ..., y_n, x], // then h_{n+1} is a polynomial in Q[y_1, ..., y_n]. // // Observation 2: // For any (complex values) a_1, ..., a_n, b, // if h_0(a_1, ..., a_n, b) = h_1(a_1, ..., a_n, b) = 0 // then for any h_i in the sequence, h_i(a_1, ..., a_n, b) = 0. // In particular, h_{n+1}(a_1, ..., a_n) = 0 // // Observation 3: // The procedure terminates because degree(h_i, x) > degree(h_{i+1}, x) // for all i >= 1 // // Observation 4: // If h_{n+1} is the zero polynomial, then // For any complex numbers a_1, ..., a_n // the univariate polynomials p(a_1, ..., a_n, x) and q(a_1, ..., a_n, x) in C[x] // have a common root. // Reason: // If h_n(a_1, ..., a_n, x) is not the zero polynomial, then it is the GCD of p(a_1, ..., a_n, x) and q(a_1, ..., a_n, x), // and it contains the common roots. // If h_n(a_1, ..., a_n, x) is the zero polynomial, then // we consider h_{n-1}(a_1, ..., a_n, x). If it is not the zero polynomial then it is the GCD and we are done, // otherwise we consider h_{n-2}(a_1, ..., a_n, x), and we continue the same process. // Thus, eventually we find a h_i(a_1, ..., a_n, x) for i > 1 that is the GCD, or q(a_1, ..., a_n, x) is the zero polynomial, // and any polynomial p(a_1, ..., a_n, x) has a common root with the zero polynomial. // SASSERT(degree(p, x) > 0); SASSERT(degree(q, x) > 0); polynomial_ref h_0(pm()); polynomial_ref h_1(pm()); polynomial_ref h_2(pm()); if (degree(p, x) < degree(q, x)) std::swap(p, q); h_0 = const_cast(p); h_1 = const_cast(q); unsigned d; while (true) { SASSERT(degree(h_1, x) <= degree(h_0, x)); pseudo_remainder(h_0, h_1, x, d, h_2); TRACE("polynomial", tout << "h_0: " << h_0 << "\nh_1: " << h_1 << "\nh_2: " << h_2 << "\n";); SASSERT(degree(h_2, x) < degree(h_1, x)); // We have that // l(h_1)^d h_0 = Q h_1 + h_2. // Q is the quotient of the division. // l(h_1) is the leading coefficient of h_1. // From this equation, we have that any zero of h_0 and h_1 is also a zero of h_2 if (degree(h_2, x) == 0) { r = h_2; return; } h_0 = h_1; h_1 = h_2; // this computation will terminate since the pseudo_remainder guarantees that // degree(h_2, x) < degree(h_1, x) } } // sign = sign * (-1)^(deg_A * deg_B) static void update_sign(unsigned deg_A, unsigned deg_B, bool & sign) { if (deg_A % 2 == 0 || deg_B % 2 == 0) return; sign = !sign; } /** \brief Compute the resultant of p and q with respect to r. The Resultant is usually defined as the determinant of the Sylvester matrix for p and q. This matrix is built using the coefficients of p and q with respect to x. Given p and q polynomials in Q[y_1, ..., y_n, x], r is a polynomial in Q[y_1, ..., y_n]. Property 1) If p and q can be written as p = a_m * (x - alpha_1) * ... * (x - alpha_m) q = b_n * (x - beta_1) * ... * (x - beta_n) Then, Res(p, q, x) = a_m^n * b_n^m * Product_{i in [1,m], j in [1, n]} (alpha_i - beta_j) Remark: if p and q are univariate polynomials, then alpha_i's and beta_j's are the roots of p and q, then Res(p, q, x) is the product of the differences of their roots modulo a constant. Property 2) For any (complex values) a_1, ..., a_n, b, if p(a_1, ..., a_n, b) = q(a_1, ..., a_n, b) = 0, then r(a_1, ..., b_n) = 0 Property 3) There are polynomials U and V in Q[y_1, ..., y_n, x] s.t. p*U + q*V = r s.t. deg(U, x) < deg(q, x) and deg(V, x) < deg(p, x) We use Res(p, q, x) to denote the resultant of p and q. Property 4) (follows from 1) If Res(p, q, x) = 0, then p and q have a common divisor. Resultant Calculus: Let A and B be two polynomials of degree m and n on variable x. Let c and d be numerals. Res(A, B, x) = (-1)^(m*n) * Res(B, A, x) Res(cA, B, x) = c^n * Res(A, B, x) Res(c, B, x) = c^n Res(0, B, x) = 0 if n > 0 Res(c, d, x) = 1 Res(A, B1*B2, x) = Res(A, B1, x)*Res(A, B2, x) Res(A, A*Q + B, x) = coeff(A, x)^(l-n) * Res(A, B, x) where l = deg(A*Q + R) The last equation suggests an approach for computing the Resultant using pseudo-division instead of determinants. Given A and B s.t. degree(A, x) = m >= n = degree(B, x) Then lc^d * A = Q * B + R where lc = coeff(B, x), and pseudo_division(A, B, x, d, Q, R); r = degree(R) Then we have: (lc^d)^n * Res(A, B) = Res(A * l^d, B) = Res(Q * B + R, B) = (-1)^(m*n) * Res(B, Q * B + R) = (-1)^(m*n) * lc^(m-r) * Res(B, R) So, lc^(d*n) * Res(A, B) = (-1)^(m*n) * lc^(m-r) * Res(B, R) From the pseudo-division algorithm, we have that: 1) 1 <= d <= m - n + 1 2) 0 <= r < n <= m d*n >= m >= m-r So, if d*n == m-r Res(A, B) = (-1)^(m*n) * Res(R, B) if d*n > m-r Res(A, B) = (-1)^(m*n) * exact_div(Res(R, B), lc^(d*n - m + r)) if d*n < m-r Res(A, B) = (-1)^(m*n) * mul(Res(R, B), lc^(m - r - d * n)) */ void resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & result) { polynomial_ref A(pm()); polynomial_ref B(pm()); A = const_cast(p); B = const_cast(q); TRACE("resultant", tout << "resultant(A, B, x)\nA: " << A << "\nB: " << B << "\nx: " << x << "\n";); // Res(0, B) = Res(A, 0) = 0 if (is_zero(A) || is_zero(B)) { result = mk_zero(); return; } // Res(c, d, x) = 1 c and d are constants // Res(c, B, x) = c^{deg(B)} if (is_const(A)) { if (is_const(B)) result = mk_one(); else pw(A, degree(B, x), result); return; } // Res(A, d, x) = d^{deg(A)} if (is_const(B)) { pw(B, degree(A, x), result); return; } // decompose A and B into // A = iA*cA*ppA // B = iB*cB*ppB scoped_numeral iA(m_manager), iB(m_manager); polynomial_ref cA(pm()), cB(pm()); polynomial_ref ppA(pm()), ppB(pm()); iccp(A, x, iA, cA, ppA); iccp(B, x, iB, cB, ppB); cA = mul(iA, cA); cB = mul(iB, cB); // At this point, A = cA*ppA and B = cB*ppB, where cA and cB are the content of A and B, and ppA and ppB the primitive polynomials polynomial_ref t(pm()); // t <- cA^{deg(B)}*cB^{deg(A)} pw(cA, degree(B, x), cA); pw(cB, degree(A, x), cB); t = mul(cA, cB); A = ppA; B = ppB; // TRACE("resultant", tout << "resultant(A, B, x) after normalization\nA: " << A << "\nB: " << B << "\nx: " << x << "\n"; tout << "t: " << t << "\n";); int s = 1; unsigned degA = degree(A, x); unsigned degB = degree(B, x); if (degA < degB) { A.swap(B); if (degA % 2 == 1 && degB % 2 == 1) s = -1; } polynomial_ref R(pm()); polynomial_ref g(pm()); polynomial_ref h(pm()); polynomial_ref new_h(pm()); // g <- 1 g = mk_one(); // h <- 1 h = mk_one(); while (true) { TRACE("resultant", tout << "A: " << A << "\nB: " << B << "\n";); degA = degree(A, x); degB = degree(B, x); SASSERT(degA >= degB); unsigned delta = degA - degB; if (degA % 2 == 1 && degB % 2 == 1) s = -s; // lc(B)^delta+1 A = BQ + R exact_pseudo_remainder(A, B, x, R); A = B; // B <- R/g*h^{delta} B = exact_div(R, g); for (unsigned i = 0; i < delta; i++) B = exact_div(B, h); // g <- lc(A) g = lc(A, x); // h <- g^delta * h^{1-delta} new_h = mk_one(); pw(g, delta, new_h); if (delta > 1) { for (unsigned i = 0; i < delta - 1; i++) new_h = exact_div(new_h, h); } h = new_h; if (degree(B, x) == 0) { unsigned degA = degree(A, x); // h <- lc(B)^{deg(A)} * h^{1-deg(A)} new_h = lc(B, x); pw(new_h, degA, new_h); if (degA > 1) { for (unsigned i = 0; i < degA - 1; i++) new_h = exact_div(new_h, h); } h = new_h; // result <- s*t*h result = mul(t, h); if (s < 0) result = neg(result); return; } } } /** \brief Return the discriminant of p with respect to x. Disc(p, x) = (-1)^(m * (m-1)/2) * Resultant(p, dp/dx, x) / coeff(p, x) dp/dx is the derivative of p with respect to x. */ void discriminant(polynomial const * p, var x, polynomial_ref & r) { polynomial_ref p_prime(pm()); unsigned m = degree(p, x); if (m == 0) { r = m_zero; return; } p_prime = derivative(p, x); resultant(p, p_prime, x, r); bool sign = (static_cast(m) * static_cast(m-1))%4 != 0; TRACE("resultant", tout << "discriminant sign: " << sign << "\n";); scoped_numeral lc(m_manager); if (const_coeff(p, x, m, lc)) { if (sign) m_manager.neg(lc); r = div(r, lc); } else { if (sign) r = neg(r); polynomial_ref c(pm()); c = coeff(p, x, m); r = exact_div(r, c); } } void subresultant_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & sRes) { // REMARK: the code does not work if deg_p == deg_q unsigned deg_p = degree(p, x); unsigned deg_q = degree(q, x); if (deg_p < deg_q) { std::swap(p, q); std::swap(deg_p, deg_q); } SASSERT(deg_p > 0); unsigned n = deg_p; sRes.reset(); sRes.resize(n + 1); // the sequence is from 0 ... n sRes.set(n, const_cast(p)); sRes.set(n - 1, const_cast(q)); polynomial_ref R_j_plus_1(pm()); polynomial_ref prem(pm()); polynomial_ref newS(pm()); unsigned j = n - 1; while (j > 0) { SASSERT(degree(sRes.get(j+1), x) == j+1); // sRes_{j+1} is regular if (j == n-1) R_j_plus_1 = mk_one(); // by definition of PSC chain else R_j_plus_1 = coeff(sRes.get(j+1), x, j+1); unsigned r = degree(sRes.get(j), x); if (r == j) { // sRes_j is regular exact_pseudo_remainder(sRes.get(j+1), sRes.get(j), x, prem); TRACE("psc", tout << "j: " << j << "\nsRes_j+1: "; sRes.get(j+1)->display(tout, m_manager); tout << "\nsRes_j: "; sRes.get(j)->display(tout, m_manager); tout << "\nprem: " << prem << "\n";); // sRes_{j-1} = prem/R_j_plus_1^2 newS = exact_div(prem, R_j_plus_1); newS = exact_div(newS, R_j_plus_1); sRes.set(j-1, newS); j--; } else { SASSERT(r < j); // sRes_j is defective // sRes_{j-1} = sRes_{j-2} = ... = sRes_{r+1} = 0 for (int i = j-1; i >= static_cast(r+1); i--) sRes.set(i, mk_zero()); // sRes_r = lc(sRes_j)^{j-r} * sRes_j / R_j_plus_1^{j-r} newS = lc(sRes.get(j), x); pw(newS, j-r, newS); newS = mul(newS, sRes.get(j)); for (unsigned i = 0; i < j-r; i++) newS = exact_div(newS, R_j_plus_1); sRes.set(r, newS); // If r > 0, we also compute sRes_{r-1} if (r > 0) { exact_pseudo_remainder(sRes.get(j+1), sRes.get(j), x, prem); // sRes_{r-1} = prem/(-R_j_plus_1)^{j-r+2} newS = prem; for (unsigned i = 0; i < j-r+2; i++) newS = exact_div(newS, R_j_plus_1); if ((j-r+2)%2 == 1) newS = neg(newS); sRes.set(r-1, newS); j = r - 1; } else { j = 0; } } } } void psc_chain1(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) { subresultant_chain(p, q, x, S); unsigned sz = S.size(); TRACE("psc", tout << "subresultant_chain\n"; for (unsigned i = 0; i < sz; i++) { tout << "i: " << i << " "; S.get(i)->display(tout, m_manager); tout << "\n"; }); for (unsigned i = 0; i < sz - 1; i++) { S.set(i, coeff(S.get(i), x, i)); } S.set(sz-1, mk_one()); } // Store in S a list of the non-zero principal subresultant coefficients of A and B // If i < j then psc_{i}(A,B) precedes psc_{j}(A,B) in S. // The leading coefficients of A and B are not included in S. void psc_chain2(polynomial const * A, polynomial const * B, var x, polynomial_ref_vector & S) { polynomial_ref G1(pm()); polynomial_ref G2(pm()); polynomial_ref G3(pm()); polynomial_ref Gh3(pm()); polynomial_ref g1(pm()), h0(pm()), hs0(pm()), h1(pm()), hs1(pm()); unsigned n1 = degree(A, x); unsigned n2 = degree(B, x); if (n1 > n2) { G1 = const_cast(A); G2 = const_cast(B); } else { G1 = const_cast(B); G2 = const_cast(A); std::swap(n1, n2); } unsigned d0 = 0; unsigned d1 = n1 - n2; unsigned i = 1; unsigned n3; S.reset(); while (true) { // Compute Gh_{i+2} if (!is_zero(G2)) { exact_pseudo_remainder(G1, G2, x, Gh3); n3 = degree(Gh3, x); if (!is_zero(Gh3) && d1%2 == 0) Gh3 = neg(Gh3); } // Compute hi if (i > 1) { g1 = lc(G1, x); pw(g1, d0, h1); if (i > 2) { pw(h0, d0 - 1, hs0); h1 = exact_div(h1, hs0); S.push_back(h1); if (is_zero(G2)) { std::reverse(S.c_ptr(), S.c_ptr() + S.size()); return; } } } // Compute G_{i+2} if (i == 1 || is_zero(Gh3)) { G3 = Gh3; } else { pw(h1, d1, hs1); hs1 = mul(g1, hs1); G3 = exact_div(Gh3, hs1); hs1 = 0; } // prepare for next iteration n1 = n2; n2 = n3; d0 = d1; d1 = n1 - n2; G1 = G2; G2 = G3; if (i > 1) h0 = h1; i = i + 1; } } // Optimized calculation of S_e using "Dichotomous Lazard" void Se_Lazard(unsigned d, polynomial const * lc_S_d, polynomial const * S_d_1, var x, polynomial_ref & S_e) { unsigned n = d - degree(S_d_1, x) - 1; TRACE("Lazard", tout << "lc_S_d: "; lc_S_d->display(tout, m_manager); tout << "\nS_d_1: "; S_d_1->display(tout, m_manager); tout << "\nn: " << n << "\n";); if (n == 0) { S_e = const_cast(S_d_1); return; } polynomial_ref X(pm()); X = lc(S_d_1, x); polynomial const * Y = lc_S_d; unsigned a = 1 << log2(n); TRACE("Lazard", tout << "a: " << a << "\n";); SASSERT(a <= n); SASSERT(n < 2*a); polynomial_ref C(pm()); C = X; n = n - a; while (a != 1) { a = a / 2; // C <- C^2/Y C = mul(C, C); C = exact_div(C, Y); TRACE("Lazard", tout << "loop a: " << a << "\nC : " << C << "\n";); if (n >= a) { // C <- C*X/Y C = mul(C, X); C = exact_div(C, Y); n = n - a; TRACE("Lazard", tout << "if, C: " << C << "\n";); } } TRACE("Lazard", tout << "C: " << C << "\nY: " << Y << "\n";); S_e = mul(C, S_d_1); S_e = exact_div(S_e, Y); } // Optimized calculation of S_{e-1} for optimized psc_chain void optimized_S_e_1(unsigned d, unsigned e, polynomial const * A, polynomial const * S_d_1, polynomial const * S_e, polynomial const * s, var x, polynomial_ref & S_e_1) { SASSERT(d == degree(A, x)); SASSERT(e == degree(S_d_1, x)); polynomial_ref c_d_1(pm()), s_e(pm()), x_j(pm()), tmp(pm()); c_d_1 = lc(S_d_1, x); s_e = lc(S_e, x); polynomial_ref_buffer H(pm()); x_j = mk_one(); for (unsigned j = 0; j <= e - 1; j++) { // H_j <- s_e * x^j x_j = mk_polynomial(x, j); H.push_back(mul(s_e, x_j)); } SASSERT(H.size() == e); // H_e <- s_e * x^e - S_e x_j = mk_polynomial(x, e); x_j = mul(s_e, x_j); H.push_back(sub(x_j, S_e)); SASSERT(H.size() == e+1); polynomial_ref x_pol(pm()), xH(pm()), xHe(pm()); x_pol = mk_polynomial(x, 1); for (unsigned j = e + 1; j <= d - 1; j++) { // H_j <- x H_{j-1} - (coeff(x H_{j-1}, e) * S_{d-1})/c_{d-1} xH = mul(x_pol, H[j-1]); xHe = coeff(xH, x, e); tmp = mul(xHe, S_d_1); tmp = exact_div(tmp, c_d_1); H.push_back(sub(xH, tmp)); } SASSERT(H.size() == d); // D <- (Sum coeff(A,j) * H[j])/lc(A) polynomial_ref D(pm()); D = mk_zero(); for (unsigned j = 0; j < d; j++) { tmp = coeff(A, x, j); tmp = mul(tmp, H[j]); D = add(D, tmp); } polynomial_ref lc_A(pm()); lc_A = lc(A, x); D = exact_div(D, lc_A); // S_e_1 = (-1)^(d-e+1) [c_{d-1} (x H[j-1] + D) - coeff(x H[j-1], e)*S_d-1]/s xH = mul(x_pol, H[d-1]); xHe = coeff(xH, x, e); xHe = mul(xHe, S_d_1); S_e_1 = add(xH, D); S_e_1 = mul(c_d_1, S_e_1); S_e_1 = sub(S_e_1, xHe); S_e_1 = exact_div(S_e_1, s); if ((d-e+1) % 2 == 1) S_e_1 = neg(S_e_1); } void psc_chain_optimized_core(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) { TRACE("psc_chain_classic", tout << "P: "; P->display(tout, m_manager); tout << "\nQ: "; Q->display(tout, m_manager); tout << "\n";); unsigned degP = degree(P, x); unsigned degQ = degree(Q, x); SASSERT(degP >= degQ); polynomial_ref A(pm()), B(pm()), C(pm()), minus_Q(pm()), lc_Q(pm()), ps(pm()); lc_Q = lc(Q, x); polynomial_ref s(pm()); // s <- lc(Q)^(deg(P)-deg(Q)) pw(lc_Q, degP - degQ, s); minus_Q = neg(Q); // A <- Q A = const_cast(Q); // B <- prem(P, -Q) exact_pseudo_remainder(P, minus_Q, x, B); while (true) { unsigned d = degree(A, x); unsigned e = degree(B, x); if (is_zero(B)) return; TRACE("psc_chain_classic", tout << "A: " << A << "\nB: " << B << "\ns: " << s << "\nd: " << d << ", e: " << e << "\n";); // B is S_{d-1} ps = coeff(B, x, d-1); if (!is_zero(ps)) S.push_back(ps); unsigned delta = d - e; if (delta > 1) { // C <- S_e // Optimized S_e calculation // s = lc(S_d) at this point Se_Lazard(d, s, B, x, C); // C is S_e ps = coeff(C, x, e); if (!is_zero(ps)) S.push_back(ps); } else { SASSERT(delta == 0 || delta == 1); C = B; } if (e == 0) return; // B <- optimized S_e_1 optimized_S_e_1(d, e, A, B, C, s, x, B); // A <- C A = C; // s <- lc(A) s = lc(A, x); } } void psc_chain_optimized(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) { SASSERT(degree(P, x) > 0); SASSERT(degree(Q, x) > 0); S.reset(); if (degree(P, x) >= degree(Q, x)) psc_chain_optimized_core(P, Q, x, S); else psc_chain_optimized_core(Q, P, x, S); if (S.empty()) S.push_back(mk_zero()); std::reverse(S.c_ptr(), S.c_ptr() + S.size()); } void psc_chain_classic_core(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) { TRACE("psc_chain_classic", tout << "P: "; P->display(tout, m_manager); tout << "\nQ: "; Q->display(tout, m_manager); tout << "\n";); unsigned degP = degree(P, x); unsigned degQ = degree(Q, x); SASSERT(degP >= degQ); polynomial_ref A(pm()), B(pm()), C(pm()), minus_Q(pm()), lc_Q(pm()), lc_B(pm()), lc_A(pm()); polynomial_ref tmp1(pm()), tmp2(pm()), s_delta(pm()), minus_B(pm()), ps(pm()); lc_Q = lc(Q, x); polynomial_ref s(pm()); // s <- lc(Q)^(deg(P)-deg(Q)) pw(lc_Q, degP - degQ, s); minus_Q = neg(Q); // A <- Q A = const_cast(Q); // B <- prem(P, -Q) exact_pseudo_remainder(P, minus_Q, x, B); while (true) { unsigned d = degree(A, x); unsigned e = degree(B, x); if (is_zero(B)) return; TRACE("psc_chain_classic", tout << "A: " << A << "\nB: " << B << "\ns: " << s << "\nd: " << d << ", e: " << e << "\n";); // B is S_{d-1} ps = coeff(B, x, d-1); if (!is_zero(ps)) S.push_back(ps); unsigned delta = d - e; if (delta > 1) { // C <- S_e // Standard S_e calculation // C <- (lc(B)^(delta-1) B) / s^(delta-1) lc_B = lc(B, x); pw(lc_B, delta-1, lc_B); lc_B = mul(lc_B, B); pw(s, delta - 1, s_delta); // s_delta <- s^(delta-1) C = exact_div(lc_B, s_delta); // s_delta <- s^delta s_delta = mul(s_delta, s); // C is S_e ps = coeff(C, x, e); if (!is_zero(ps)) S.push_back(ps); } else { SASSERT(delta == 0 || delta == 1); C = B; // s_delta <- s^delta pw(s, delta, s_delta); } if (e == 0) return; // B <- prem(A, -B)/(s^delta * lc(A) lc_A = lc(A, x); minus_B = neg(B); exact_pseudo_remainder(A, minus_B, x, tmp1); tmp2 = mul(lc_A, s_delta); B = exact_div(tmp1, tmp2); // A <- C A = C; // s <- lc(A) s = lc(A, x); } } void psc_chain_classic(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) { SASSERT(degree(P, x) > 0); SASSERT(degree(Q, x) > 0); S.reset(); if (degree(P, x) >= degree(Q, x)) psc_chain_classic_core(P, Q, x, S); else psc_chain_classic_core(Q, P, x, S); if (S.empty()) S.push_back(mk_zero()); std::reverse(S.c_ptr(), S.c_ptr() + S.size()); } void psc_chain(polynomial const * A, polynomial const * B, var x, polynomial_ref_vector & S) { // psc_chain1(A, B, x, S); // psc_chain2(A, B, x, S); // psc_chain_classic(A, B, x, S); psc_chain_optimized(A, B, x, S); } polynomial * normalize(polynomial const * p) { if (is_zero(p)) return const_cast(p); unsigned sz = p->size(); if (m().modular()) { unsigned i = 0; for (; i < sz; i++) { if (!m().is_p_normalized(p->a(i))) break; } if (i < sz) { m_cheap_som_buffer.reset(); scoped_numeral a(m_manager); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); m_manager.set(a, p->a(i)); m_cheap_som_buffer.add_reset(a, m); } m_cheap_som_buffer.normalize(); return m_cheap_som_buffer.mk(); } } scoped_numeral g(m_manager); m_manager.gcd(sz, p->as(), g); if (m_manager.is_one(g)) return const_cast(p); m_cheap_som_buffer.reset(); scoped_numeral a(m_manager); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); m_manager.div(p->a(i), g, a); m_cheap_som_buffer.add_reset(a, m); } return m_cheap_som_buffer.mk(); } polynomial * neg(polynomial const * p) { SASSERT(m_cheap_som_buffer.empty()); scoped_numeral minus_a(m_manager); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { m_manager.set(minus_a, p->a(i)); m_manager.neg(minus_a); m_cheap_som_buffer.add(minus_a, p->m(i)); } return m_cheap_som_buffer.mk(); } // Return true if is a(i)*m(i) is a perfect square. // Store sqrt of a(i) in sqrt_a bool is_perfect_square(polynomial const * p, unsigned i, numeral & sqrt_a) { monomial * m = p->m(i); if (!m->is_square()) return false; numeral const & a = p->a(i); if (!m_manager.is_perfect_square(a, sqrt_a)) return false; return true; } bool sqrt(polynomial const * p, polynomial_ref & r) { SASSERT(p != 0); if (is_zero(p)) { r = const_cast(p); return true; } scoped_numeral a(m_manager); TRACE("sqrt_bug", tout << "sqrt: "; p->display(tout, m_manager); tout << "\n"; tout << "min pos: " << p->graded_lex_min_pos() << "\n"; tout << "max pos: " << p->graded_lex_max_pos() << "\n";); // Quick Check: the minimal monomial must be a perfect square unsigned min_pos = p->graded_lex_min_pos(); if (!is_perfect_square(p, min_pos, a)) return false; // Quick Check: the maximal monomial must be a perfect square unsigned max_pos = p->graded_lex_max_pos(); if (!is_perfect_square(p, max_pos, a)) return false; // Compute square root using // (m_1 + ... + m_k)^2 == // (m_1)m_1 // (2m_1 + m_2)m_2 // (2m_1 + 2m_2 + m_3)m_3 // ... // // R <- m1 // C <- p - m1*m1 // while (true) { // if (is_zero(C)) // return true; // m <- biggest monomial in C // if (m is not divisible by 2*m1) // return false; // m' <- m/2m1 // C <- C - 2*R*m' - m'*m' // R <- R + m' // } // // The loop above terminates because total degree of the // maximal monomial in C decreases in each iteration. monomial * m1 = sqrt(p->m(max_pos)); SASSERT(m1 != 0); som_buffer & R = m_som_buffer; som_buffer & C = m_som_buffer2; R.reset(); C.reset(); numeral two; m_manager.set(two, 2); scoped_numeral two_a(m_manager); m_manager.mul(a, two, two_a); // R <- a * m1 R.add(a, m1); // C <- p - m1*m1 unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (i == max_pos) continue; C.add(p->a(i), p->m(i)); } scoped_numeral a_i(m_manager); scoped_numeral aux(m_manager); monomial_ref m_aux(pm()); while (true) { checkpoint(); TRACE("sqrt_bug", tout << "R: "; R.display(tout); tout << "C: "; C.display(tout);); unsigned curr_max = C.graded_lex_max_pos(); if (curr_max == UINT_MAX) { // C is empty C.reset(); r = R.mk(); return true; } monomial * m = C.m(curr_max); monomial * m_i; if (!div(m, m1, m_i)) { // m1 does not divide maximal monomial of C. R.reset(); C.reset(); return false; } // 2*a does not divide maximal coefficient of C if (!m_manager.divides(two_a, C.a(curr_max))) return false; m_manager.div(C.a(curr_max), two_a, a_i); // C <- C - 2*R*a_i*m_i - a_i*a_i*m_i*m_i unsigned R_sz = R.size(); for (unsigned j = 0; j < R_sz; j++) { if (m_manager.is_zero(R.a(j))) continue; m_manager.mul(R.a(j), a_i, aux); m_manager.mul(aux, two, aux); m_manager.neg(aux); m_aux = mul(R.m(j), m_i); C.add(aux, m_aux); } m_manager.mul(a_i, a_i, aux); m_manager.neg(aux); m_aux = mul(m_i, m_i); C.add(aux, m_aux); // R <- R + a_i*m_i R.add(a_i, m_i); } } void rename(unsigned sz, var const * xs) { TRACE("rename", for (unsigned i = 0; i < sz; i++) tout << xs[i] << " "; tout << "\n"; tout << "polynomials before rename\n"; for (unsigned i = 0; i < m_polynomials.size(); i++) { if (m_polynomials[i] == 0) continue; m_polynomials[i]->display(tout, m_manager); tout << "\n"; }); mm().rename(sz, xs); // we must traverse the polynomial vector, and update the first monomial, // since it may not contain anymore the maximal variable with maximal degree. polynomial_vector::iterator it2 = m_polynomials.begin(); polynomial_vector::iterator end2 = m_polynomials.end(); for (; it2 != end2; ++it2) { polynomial * p = *it2; if (p == 0) continue; p->make_first_maximal(); SASSERT(p->size() <= 1 || !p->lex_sorted()); } TRACE("rename", tout << "polynomials after rename\n"; for (unsigned i = 0; i < m_polynomials.size(); i++) { if (m_polynomials[i] == 0) continue; m_polynomials[i]->display(tout, m_manager); tout << "\n"; }); } bool is_pos(polynomial const * p) { bool found_unit = false; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (!p->m(i)->is_power_of_two()) return false; if (p->m(i) == mk_unit()) found_unit = true; if (!m_manager.is_pos(p->a(i))) return false; } return found_unit; } bool is_neg(polynomial const * p) { bool found_unit = false; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (!p->m(i)->is_power_of_two()) return false; if (p->m(i) == mk_unit()) found_unit = true; if (!m_manager.is_neg(p->a(i))) return false; } return found_unit; } bool is_nonpos(polynomial const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (!p->m(i)->is_power_of_two()) return false; if (!m_manager.is_neg(p->a(i))) return false; } return true; } bool is_nonneg(polynomial const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (!p->m(i)->is_power_of_two()) return false; if (!m_manager.is_pos(p->a(i))) return false; } return true; } // Functor used to compute the maximal degree of each variable in a polynomial p. class var_max_degree { unsigned_vector m_max_degree; var_vector m_xs; public: void init(polynomial const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); unsigned k = m->degree(j); unsigned max_k = m_max_degree.get(x, 0); if (k > max_k) { if (max_k == 0) m_xs.push_back(x); m_max_degree.setx(x, m->degree(j), 0); } } } } void reset() { unsigned sz = m_xs.size(); for (unsigned i = 0; i < sz; i++) { m_max_degree[m_xs[i]] = 0; } m_xs.reset(); } unsigned operator()(var x) const { return m_max_degree.get(x, 0); } unsigned num_vars() const { return m_xs.size(); } var const * vars() const { return m_xs.c_ptr(); } }; struct scoped_var_max_degree { var_max_degree & m; scoped_var_max_degree(var_max_degree & _m, polynomial const * p): m(_m) { m.init(p); } ~scoped_var_max_degree() { m.reset(); } unsigned operator()(var x) const { return m(x); } unsigned num_vars() const { return m.num_vars(); } var const * vars() const { return m.vars(); } }; var_max_degree m_var_max_degree; // This method uses the tmp fields: m_found_vars, m_var_max_degree. polynomial * substitute(polynomial const * p, var2mpq const & x2v) { scoped_var_max_degree var2max_degree(m_var_max_degree, p); unsigned xs_sz = var2max_degree.num_vars(); var const * xs = var2max_degree.vars(); bool found = false; for (unsigned i = 0; i < xs_sz; i++) { var x = xs[i]; if (x2v.contains(x) && var2max_degree(x) > 0) { found = true; break; } } if (!found) return const_cast(p); scoped_numeral new_a(m_manager); scoped_numeral tmp(m_manager); m_found_vars.reserve(num_vars(), false); m_som_buffer.reset(); som_buffer & R = m_som_buffer; tmp_monomial & new_m = m_tmp1; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); unsigned new_msz = 0; m_manager.set(new_a, p->a(i)); new_m.reserve(msz); for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); unsigned k = m->degree(j); if (x2v.contains(x)) { unsigned max_k = var2max_degree(x); m_found_vars[x] = true; mpq const & x_value = x2v(x); m_manager.power(x_value.numerator(), k, tmp); m_manager.mul(tmp, new_a, new_a); if (k < max_k) { m_manager.power(x_value.denominator(), max_k - k, tmp); m_manager.mul(tmp, new_a, new_a); } } else { new_m.set_power(new_msz, m->get_power(j)); new_msz++; } } // For each variable x in xs that does not occur in m, I // should include (x2v(x).denominator())^{var2max_degree(x)} to new_a for (unsigned j = 0; j < xs_sz; j++) { var x = xs[j]; if (m_found_vars[x]) continue; if (x2v.contains(x)) { m_manager.power(x2v(x).denominator(), var2max_degree(x), tmp); m_manager.mul(tmp, new_a, new_a); } } // Reset m_found_vars for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); m_found_vars[x] = false; } // Add new_a*new_m to R if (!m_manager.is_zero(new_a)) { new_m.set_size(new_msz); R.add(new_a, mk_monomial(new_m)); } } return R.mk(true); } struct var_pos { unsigned_vector m_pos; void init(unsigned sz, var const * xs) { for (unsigned i = 0; i < sz; i++) { SASSERT(m_pos.get(xs[i], UINT_MAX) == UINT_MAX); m_pos.setx(xs[i], i, UINT_MAX); } } void reset(unsigned sz, var const * xs) { for (unsigned i = 0; i < sz; i++) { SASSERT(m_pos.get(xs[i], UINT_MAX) != UINT_MAX); m_pos[xs[i]] = UINT_MAX; } } unsigned operator()(var x) const { return m_pos.get(x, UINT_MAX); } }; struct scoped_var_pos { var_pos & m; unsigned m_xs_sz; var const * m_xs; scoped_var_pos(var_pos & p, unsigned xs_sz, var const * xs): m(p), m_xs_sz(xs_sz), m_xs(xs) { m.init(m_xs_sz, m_xs); } ~scoped_var_pos() { m.reset(m_xs_sz, m_xs); } unsigned operator()(var x) const { return m(x); } }; var_pos m_var_pos; struct var2mpq_wrapper : public var2mpq { scoped_var_pos m_var_pos; mpq const * m_vs; var2mpq_wrapper(unsigned xs_sz, var const * xs, mpq const * vs, var_pos & buffer): m_var_pos(buffer, xs_sz, xs), m_vs(vs) { } virtual unsynch_mpq_manager & m() const { UNREACHABLE(); static unsynch_mpq_manager m; return m; } virtual bool contains(var x) const { return m_var_pos(x) != UINT_MAX; } virtual mpq const & operator()(var x) const { return m_vs[m_var_pos(x)]; } }; polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, mpq const * vs) { var2mpq_wrapper x2v(xs_sz, xs, vs, m_var_pos); return substitute(p, x2v); } polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs) { TRACE("polynomial", tout << "substitute num_vars: " << xs_sz << "\n"; for (unsigned i = 0; i < xs_sz; i++) { tout << "x" << xs[i] << " -> " << m_manager.to_string(vs[i]) << "\n"; }); scoped_var_pos var2pos(m_var_pos, xs_sz, xs); scoped_numeral new_a(m_manager); scoped_numeral tmp(m_manager); m_som_buffer.reset(); som_buffer & R = m_som_buffer; tmp_monomial & new_m = m_tmp1; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); unsigned new_msz = 0; m_manager.set(new_a, p->a(i)); new_m.reserve(msz); for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); unsigned k = m->degree(j); unsigned pos = var2pos(x); if (pos != UINT_MAX) { m_manager.power(vs[pos], k, tmp); m_manager.mul(tmp, new_a, new_a); } else { SASSERT(var2pos(x) == UINT_MAX); // x is not in xs new_m.set_power(new_msz, m->get_power(j)); new_msz++; } } new_m.set_size(new_msz); TRACE("polynomial", tout << "processing " << m_manager.to_string(p->a(i)) << " "; m->display(tout); tout << "\n"; tout << "new_a: " << m_manager.to_string(new_a) << " "; mk_monomial(new_m)->display(tout); tout << "\n";); R.add(new_a, mk_monomial(new_m)); } return R.mk(); } /** Auxiliary method used to implement t_eval. If evaluates the sub-polynomial p composed of the monomials at positions [start, end) where all variables > x are ignored. var2pos is a mapping from variables to the positions. vs[var2pos(x)] contains the value of x. */ template void t_eval_core(polynomial * p, ValManager & vm, var2value const & x2v, unsigned start, unsigned end, var x, typename ValManager::numeral & r) { TRACE("eval_bug", tout << "p: "; p->display(tout, m()); tout << "\n"; tout << "start: " << start << ", end: " << end << ", x: " << x << "\n";); SASSERT(start < end); SASSERT(end <= p->size()); SASSERT(is_valid(x)); _scoped_numeral aux(vm); if (end == start + 1) { vm.set(r, p->a(start)); monomial * m = p->m(start); SASSERT(m->degree_of(x) > 0); unsigned sz = m->size(); for (unsigned i = 0; i < sz; i++) { var y = m->get_var(i); if (y > x) break; SASSERT(x2v.contains(y)); unsigned d = m->degree(i); vm.power(x2v(y), d, aux); vm.mul(r, aux, r); } } else { SASSERT(x2v.contains(x)); typename ValManager::numeral const & x_value = x2v(x); vm.reset(r); unsigned i = start; while (i < end) { checkpoint(); unsigned d = p->m(i)->degree_of(x); if (d == 0) { var y = p->max_smaller_than(i, end, x); if (y == null_var) { SASSERT(end == i+1); vm.add(r, p->a(i), r); } else { t_eval_core(p, vm, x2v, i, end, y, aux.get()); vm.add(r, aux, r); } break; } unsigned j = i+1; unsigned next_d = 0; for (; j < end; j++) { unsigned d_j = p->m(j)->degree_of(x); SASSERT(d_j <= d); if (d_j < d) { next_d = d_j; break; } } SASSERT(j == end || p->m(j)->degree_of(x) < d); var y = p->max_smaller_than(i, j, x); if (y == null_var) { SASSERT(j == i+1); vm.set(aux, p->a(i)); } else { t_eval_core(p, vm, x2v, i, j, y, aux.get()); } vm.add(r, aux, r); vm.power(x_value, d - next_d, aux); vm.mul(r, aux, r); i = j; } } TRACE("eval_bug", tout << "result for start: " << start << ", end: " << end << ", x: " << x << "\n"; tout << "r: "; vm.display(tout, r); tout << "\n";); } template void t_eval(polynomial * p, var2value const & x2v, typename ValManager::numeral & r) { ValManager & vm = x2v.m(); if (is_zero(p)) { vm.reset(r); return; } if (is_const(p)) { SASSERT(size(p)==1); vm.set(r, p->a(0)); return; } lex_sort(p); // lex_sort just reorders the monomials of p. That is, p still represents the same polynomial t_eval_core(p, vm, x2v, 0, p->size(), max_var(p), r); } class single_var2value : public var2value { numeral_manager & m_manager; var m_x; numeral const & m_val; public: single_var2value(numeral_manager & m, var x, numeral const & val):m_manager(m), m_x(x), m_val(val) {} virtual numeral_manager & m() const { return m_manager; } virtual bool contains(var x) const { return m_x == x; } virtual numeral const & operator()(var x) const { SASSERT(m_x == x); return m_val; } }; void univ_eval(polynomial const * p, var x, numeral const & val, numeral & r) { SASSERT(is_univariate(p)); if (is_zero(p)) m().set(r, 0); else if (is_const(p)) m().set(r, p->a(0)); else t_eval(const_cast(p), single_var2value(m(),x, val), r); } void eval(polynomial const * p, var2mpbqi const & x2v, mpbqi & r) { t_eval(const_cast(p), x2v, r); } void eval(polynomial const * p, var2mpq const & x2v, mpq & r) { t_eval(const_cast(p), x2v, r); } void eval(polynomial const * p, var2anum const & x2v, anum & r) { t_eval(const_cast(p), x2v, r); } // Return the variable with minimal degree in p // That is min_x s.t. forall x in p degree(p, min_x) <= degree(p, x) var get_min_degree_var(polynomial const * p) { SASSERT(!is_const(p)); scoped_var_max_degree var2max_degree(m_var_max_degree, p); unsigned num_vars = var2max_degree.num_vars(); var const * xs = var2max_degree.vars(); var min_x = null_var; unsigned deg_min = UINT_MAX; for (unsigned i = 0; i < num_vars; i++) { var x_i = xs[i]; unsigned deg_x_i = var2max_degree(x_i); if (deg_x_i < deg_min) { min_x = x_i; deg_min = deg_x_i; } } return min_x; } void acc_constant(factors & r, numeral const & c) { TRACE("factor_bug", tout << "acc_constant, c: "; m_manager.display(tout, c); tout << "\n";); scoped_numeral new_c(m_manager); m_manager.mul(r.get_constant(), c, new_c); r.set_constant(new_c); } void flip_sign(factors & r) { scoped_numeral new_c(m_manager); m_manager.set(new_c, r.get_constant()); m_manager.neg(new_c); r.set_constant(new_c); } void factor_1_sqf_pp(polynomial const * p, factors & r, var x, unsigned k) { SASSERT(degree(p, x) == 1); SASSERT(is_primitive(p, x)); SASSERT(is_square_free(p, x)); TRACE("factor", tout << "factor square free (degree == 1):\n"; p->display(tout, m_manager); tout << "\n";); // easy case r.push_back(const_cast(p), k); } void factor_2_sqf_pp(polynomial const * p, factors & r, var x, unsigned k) { SASSERT(degree(p, x) == 2); SASSERT(is_primitive(p, x)); SASSERT(is_square_free(p, x)); TRACE("factor", tout << "factor square free (degree == 2):\n"; p->display(tout, m_manager); tout << "\n";); polynomial_ref a(pm()); polynomial_ref b(pm()); polynomial_ref c(pm()); a = coeff(p, x, 2); b = coeff(p, x, 1); c = coeff(p, x, 0); TRACE("factor", tout << "a: " << a << "\nb: " << b << "\nc: " << c << "\n";); // make sure the leading monomoal of a is positive bool flipped_coeffs = false; SASSERT(!is_zero(a)); unsigned a_glex_max_pos = a->graded_lex_max_pos(); SASSERT(a_glex_max_pos != UINT_MAX); if (m_manager.is_neg(a->a(a_glex_max_pos))) { a = neg(a); b = neg(b); c = neg(c); flipped_coeffs = true; } // Create the discriminant: b^2 - 4*a*c polynomial_ref b2(pm()); b2 = mul(b, b); polynomial_ref ac(pm()); ac = mul(a, c); polynomial_ref disc(pm()); numeral m_four; m_manager.set(m_four, -4); disc = addmul(b2, m_four, mk_unit(), ac); // discriminant must be different from 0, since p is square free SASSERT(!is_zero(disc)); polynomial_ref disc_sqrt(pm()); TRACE("factor", tout << "disc: " << disc << "\n";); if (!sqrt(disc, disc_sqrt)) { // p is irreducible r.push_back(const_cast(p), k); return; } if (flipped_coeffs && k % 2 == 1) { // if k is ODD, and we flipped the coefficients, // we must also flip the sign of r. flip_sign(r); } DEBUG_CODE({ polynomial_ref tmp(pm()); tmp = mul(disc_sqrt, disc_sqrt); SASSERT(eq(disc, tmp)); }); TRACE("factor", tout << "disc_sqrt: " << disc_sqrt << "\n";); // p = cont*(2*a*x + b - disc_sqrt)*(2*a*x + b + disc_sqrt) numeral two; m_manager.set(two, 2); polynomial_ref f1(pm()); polynomial_ref f2(pm()); monomial_ref mx(pm()); mx = mk_monomial(x); polynomial_ref two_ax(pm()); two_ax = mul(two, mx, a); f1 = add(two_ax, b); f2 = f1; f1 = sub(f1, disc_sqrt); f2 = add(f2, disc_sqrt); TRACE("factor", tout << "before pp\nf1: " << f1 << "\nf2: " << f2 << "\n"; polynomial_ref cf1(pm()); m_wrapper.content(f1, x, cf1); polynomial_ref cf2(pm()); m_wrapper.content(f2, x, cf2); tout << "content(f1): " << cf1 << "\ncontent(f2): " << cf2 << "\n";); pp(f1, x, f1); pp(f2, x, f2); TRACE("factor", tout << "f1: " << f1 << "\nf2: " << f2 << "\n";); DEBUG_CODE({ polynomial_ref f1f2(pm()); f1f2 = mul(f1, f2); if (flipped_coeffs) f1f2 = neg(f1f2); SASSERT(eq(f1f2, p)); }); r.push_back(f1, k); r.push_back(f2, k); } void factor_sqf_pp_univ(polynomial const * p, factors & r, unsigned k, factor_params const & params) { SASSERT(is_univariate(p)); SASSERT(is_square_free(p, max_var(p))); SASSERT(is_primitive(p, max_var(p))); SASSERT(!is_zero(p)); TRACE("factor", tout << "factor square free univariate:\n"; p->display(tout, m_manager); tout << "\n";); // Convert polynomial into a upolynomial, and execute univariate factorization. var x = max_var(p); up_manager::scoped_numeral_vector p1(upm().m()); polynomial_ref p_ref(pm()); p_ref = const_cast(p); upm().to_numeral_vector(p_ref, p1); up_manager::factors fs(upm()); upolynomial::factor_square_free(upm(), p1, fs, params); SASSERT(m().is_one(fs.get_constant()) || m().is_minus_one(fs.get_constant())); if (fs.distinct_factors() == 1 && fs.get_degree(0) == 1) { // p is irreducible r.push_back(const_cast(p), k); } else { // Convert factors back into polynomial objects TRACE("factor_bug", tout << "factoring fs constant: " << m().to_string(fs.get_constant()) << "\np:\n"; p->display(tout, m()); tout << "\n";); polynomial_ref f(pm()); unsigned num_factors = fs.distinct_factors(); for (unsigned i = 0; i < num_factors; i++) { numeral_vector const & f1 = fs[i]; unsigned k1 = fs.get_degree(i); f = to_polynomial(f1.size(), f1.c_ptr(), x); TRACE("factor_bug", tout << "uni-factor:\n"; upm().display(tout, f1); tout << "\n"; tout << "factor:\n" << f << "\n";); r.push_back(f, k*k1); } TRACE("factor_bug", tout << "end-factors...\n";); SASSERT(m().is_one(fs.get_constant()) || m().is_minus_one(fs.get_constant())); if (m().is_minus_one(fs.get_constant()) && k % 2 == 1) flip_sign(r); } } void factor_n_sqf_pp(polynomial const * p, factors & r, var x, unsigned k) { SASSERT(degree(p, x) > 2); SASSERT(is_primitive(p, x)); SASSERT(is_square_free(p, x)); TRACE("factor", tout << "factor square free (degree > 2):\n"; p->display(tout, m_manager); tout << "\n";); // TODO: invoke Dejan's procedure r.push_back(const_cast(p), k); } void factor_sqf_pp(polynomial const * p, factors & r, var x, unsigned k, factor_params const & params) { SASSERT(degree(p, x) > 0); SASSERT(is_primitive(p, x)); SASSERT(is_square_free(p, x)); SASSERT(!is_zero(p)); unsigned deg_x = degree(p, x); if (deg_x == 1) factor_1_sqf_pp(p, r, x, k); else if (is_univariate(p)) factor_sqf_pp_univ(p, r, k, params); else if (deg_x == 2) factor_2_sqf_pp(p, r, x, k); else factor_n_sqf_pp(p, r, x, k); } void factor_core(polynomial const * p, factors & r, factor_params const & params) { TRACE("factor", tout << "factor_core\np: "; p->display(tout, m_manager); tout << "\n";); TRACE("factor_bug", tout << "factors r.get_constant(): " << m_manager.to_string(r.get_constant()) << "\n";); SASSERT(!is_zero(p)); if (is_const(p)) { SASSERT(!is_zero(p)); SASSERT(p->size() == 1); acc_constant(r, p->a(0)); return; } var x = get_min_degree_var(p); SASSERT(degree(p, x) > 0); scoped_numeral i(m_manager); polynomial_ref c(pm()), pp(pm()); iccp(p, x, i, c, pp); TRACE("factor", tout << "i: " << i << "\n";); acc_constant(r, i); factor_core(c, r, params); polynomial_ref C(pm()); C = pp; // Let C be a primitive polynomial of the form: P_1^1 * P_2^2 * ... * P_k^k, where each P_i is square free polynomial_ref C_prime(pm()); C_prime = derivative(C, x); polynomial_ref B(pm()), A(pm()), D(pm()); gcd(C, C_prime, B); if (is_const(B)) { // C must be of the form P_1 (square free) SASSERT(degree(C, x) > 0); factor_sqf_pp(C, r, x, 1, params); } else { // B is of the form P_2 * P_3^2 * ... * P_k^{k-1} A = exact_div(C, B); // A is of the form P_1 * P_2 * ... * P_k unsigned j = 1; while (!is_const(A)) { SASSERT(is_primitive(A, x)); SASSERT(is_square_free(A, x)); SASSERT(degree(A, x) > 0); checkpoint(); TRACE("factor", tout << "factor_core main loop j: " << j << "\nA: " << A << "\nB: " << B << "\n";); // A is of the form P_j * P_{j+1} * P_{j+2} * ... * P_k // B is of the form P_{j+1} * P_{j+2}^2 * ... * P_k^{k - j - 2} gcd(A, B, D); // D is of the form P_{j+1} * P_{j+2} * ... * P_k C = exact_div(A, D); // C is of the form P_j if (!is_const(C)) { SASSERT(degree(C, x) > 0); factor_sqf_pp(C, r, x, j, params); } else { TRACE("factor", tout << "const C: " << C << "\n";); SASSERT(C->size() == 1); SASSERT(m_manager.is_one(C->a(0)) || m_manager.is_minus_one(C->a(0))); if (m_manager.is_minus_one(C->a(0)) && j % 2 == 1) flip_sign(r); } B = exact_div(B, D); // B is of the form P_{j+2} * ... * P_k^{k - j - 3} A = D; // D is of the form P_{j+1} * P_{j+2} * ... * P_k j++; } SASSERT(eq(mk_one(), A)); } } void factor(polynomial const * p, factors & r, factor_params const & params) { if (is_zero(p)) { r.set_constant(mpz(0)); return; } factor_core(p, r, params); TRACE("factor_bug", tout << "[factor] end, r.get_constant(): " << m_manager.to_string(r.get_constant()) << "\n";); } polynomial * to_polynomial(unsigned sz, numeral const * p, var x) { if (sz == 0) return mk_zero(); _scoped_numeral_buffer coeffs(m_manager); for (unsigned i = 0; i < sz; i++) { coeffs.push_back(numeral()); m_manager.set(coeffs.back(), p[i]); } return mk_univariate(x, sz-1, coeffs.c_ptr()); } polynomial * mk_glex_monic(polynomial const * p) { SASSERT(m_manager.field()); if (is_zero(p)) return const_cast(p); unsigned pos = p->graded_lex_max_pos(); if (m_manager.is_one(p->a(pos))) return const_cast(p); scoped_numeral inv_c(m()); scoped_numeral new_a(m()); m().set(inv_c, p->a(pos)); m().inv(inv_c); m_cheap_som_buffer.reset(); cheap_som_buffer & R = m_cheap_som_buffer; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { m().set(new_a, p->a(i)); m().mul(new_a, inv_c, new_a); R.add(new_a, p->m(i)); } return R.mk(); } som_buffer_vector m_translate_buffers; polynomial * translate(polynomial const * p, var x, numeral const & v) { unsigned deg_x = degree(p, x); if (deg_x == 0 || m().is_zero(v)) return const_cast(p); som_buffer_vector & as = m_translate_buffers; m_translate_buffers.reset(deg_x+1); coeffs(p, x, as); for (unsigned i = 1; i <= deg_x; i++) { checkpoint(); for (unsigned k = deg_x-i; k <= deg_x-1; k++) { as[k]->addmul(v, as[k+1]); } } monomial_ref xk(pm()); som_buffer & R = m_som_buffer; R.reset(); for (unsigned k = 0; k <= deg_x; k++) { xk = mk_monomial(x, k); R.addmul(xk, as[k]); } m_translate_buffers.reset(deg_x+1); return R.mk(); } void translate(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs, polynomial_ref & r) { r = const_cast(p); if (xs_sz == 0 || is_const(p)) return; for (unsigned i = 0; i < xs_sz; i++) r = translate(r, xs[i], vs[i]); } polynomial * mod_d(polynomial const * p, var2degree const & x2d) { if (is_const(p)) return const_cast(p); cheap_som_buffer & R = m_cheap_som_buffer; R.reset(); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); unsigned j; for (j = 0; j < msz; j++) { var x = m->get_var(j); unsigned dx = x2d.degree(x); if (dx == 0) continue; if (m->degree(j) >= dx) break; } if (j == msz) { R.add(p->a(i), p->m(i)); } } return R.mk(); } void exact_pseudo_division_mod_d(polynomial const * p, polynomial const * q, var x, var2degree const & x2d, polynomial_ref & Q, polynomial_ref & R) { unsigned d; pseudo_division_core(p, q, x, d, Q, R, &x2d); } }; manager::manager(numeral_manager & m, monomial_manager * mm) { m_imp = alloc(imp, *this, m, mm); } manager::manager(numeral_manager & m, small_object_allocator * a) { m_imp = alloc(imp, *this, m, a); } manager::~manager() { dealloc(m_imp); } numeral_manager & manager::m() const { return m_imp->m_manager.m(); } monomial_manager & manager::mm() const { return m_imp->mm(); } bool manager::modular() const { return m_imp->m().modular(); } numeral const & manager::p() const { return m_imp->m().p(); } void manager::set_z() { return m_imp->m().set_z(); } void manager::set_zp(numeral const & p) { return m_imp->m().set_zp(p); } void manager::set_zp(uint64 p) { return m_imp->m().set_zp(p); } small_object_allocator & manager::allocator() const { return m_imp->mm().allocator(); } void manager::set_cancel(bool f) { m_imp->set_cancel(f); } void manager::add_del_eh(del_eh * eh) { m_imp->add_del_eh(eh); } void manager::remove_del_eh(del_eh * eh) { m_imp->remove_del_eh(eh); } var manager::mk_var() { return m_imp->mk_var(); } unsigned manager::num_vars() const { return m_imp->num_vars(); } void manager::inc_ref(monomial * m) { if (m) m->inc_ref(); } void manager::dec_ref(monomial * m) { if (m) m_imp->dec_ref(m); } void manager::inc_ref(polynomial * p) { if (p) p->inc_ref(); } void manager::dec_ref(polynomial * p) { if (p) m_imp->dec_ref(p); } void manager::lex_sort(polynomial * p) { m_imp->lex_sort(p); } unsigned manager::hash(monomial const * m) { return m->hash(); } unsigned manager::hash(polynomial const * p) { return m_imp->hash(p); } polynomial * manager::coeff(polynomial const * p, var x, unsigned k) { return m_imp->coeff(p, x, k); } polynomial * manager::coeff(polynomial const * p, var x, unsigned k, polynomial_ref & reduct) { return m_imp->coeff(p, x, k, reduct); } bool manager::nonzero_const_coeff(polynomial const * p, var x, unsigned k) { return m_imp->nonzero_const_coeff(p, x, k); } bool manager::const_coeff(polynomial const * p, var x, unsigned k, numeral & c) { return m_imp->const_coeff(p, x, k, c); } monomial * manager::mk_unit() { return m_imp->mk_unit(); } monomial * manager::mk_monomial(var x) { return m_imp->mk_monomial(x); } monomial * manager::mk_monomial(var x, unsigned k) { return m_imp->mk_monomial(x, k); } monomial * manager::mk_monomial(unsigned sz, var * xs) { return m_imp->mk_monomial(sz, xs); } monomial * manager::convert(monomial const * src) { return m_imp->convert(src); } monomial * manager::mul(monomial const * m1, monomial const * m2) { return m_imp->mul(m1, m2); } bool manager::div(monomial const * m1, monomial const * m2) { return m_imp->div(m1, m2); } bool manager::div(monomial const * m1, monomial const * m2, monomial * & r) { return m_imp->div(m1, m2, r); } void manager::newton_interpolation(var x, unsigned d, numeral const * inputs, polynomial * const * outputs, polynomial_ref & r) { return m_imp->newton_interpolation(x, d, inputs, outputs, r); } monomial * manager::gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { return m_imp->gcd(m1, m2, q1, q2); } bool manager::unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { return m_imp->unify(m1, m2, q1, q2); } monomial * manager::pw(monomial const * m, unsigned k) { return m_imp->pw(m, k); } polynomial * manager::mk_zero() { return m_imp->mk_zero(); } polynomial * manager::mk_const(numeral & a) { return m_imp->mk_const(a); } polynomial * manager::mk_const(rational const & a) { return m_imp->mk_const(a); } polynomial * manager::mk_polynomial(var x, unsigned k) { return m_imp->mk_polynomial(x, k); } polynomial * manager::mk_polynomial(unsigned sz, numeral * as, monomial * const * ms) { return m_imp->mk_polynomial(sz, as, ms); } polynomial * manager::mk_polynomial(unsigned sz, rational const * as, monomial * const * ms) { return m_imp->mk_polynomial(sz, as, ms); } polynomial * manager::mk_linear(unsigned sz, rational const * as, var const * xs, rational const & c) { return m_imp->mk_linear(sz, as, xs, c); } polynomial * manager::mk_linear(unsigned sz, numeral * as, var const * xs, numeral & c) { return m_imp->mk_linear(sz, as, xs, c); } polynomial * manager::mk_univariate(var x, unsigned n, numeral * as) { return m_imp->mk_univariate(x, n, as); } polynomial * manager::neg(polynomial const * p) { return m_imp->neg(p); } polynomial * manager::add(polynomial const * p1, polynomial const * p2) { return m_imp->add(p1, p2); } polynomial * manager::sub(polynomial const * p1, polynomial const * p2) { return m_imp->sub(p1, p2); } polynomial * manager::addmul(numeral const & a1, monomial const * m1, polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) { return m_imp->addmul(a1, m1, p1, a2, m2, p2); } polynomial * manager::addmul(polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) { return m_imp->addmul(p1, a2, m2, p2); } polynomial * manager::addmul(polynomial const * p1, numeral const & a2, polynomial const * p2) { return m_imp->addmul(p1, a2, p2); } polynomial * manager::mul(numeral const & a, polynomial const * p) { return m_imp->mul(a, p); } polynomial * manager::mul(rational const & a, polynomial const * p) { return m_imp->mul(a, p); } polynomial * manager::mul(polynomial const * p1, polynomial const * p2) { return m_imp->mul(p1, p2); } polynomial * manager::mul(numeral const & a, monomial const * m, polynomial const * p) { return m_imp->mul(a, m, p); } polynomial * manager::mul(monomial const * m, polynomial const * p) { return m_imp->mul(m, p); } void manager::pw(polynomial const * p, unsigned k, polynomial_ref & r) { m_imp->pw(p, k, r); } void manager::int_content(polynomial const * p, numeral & c) { m_imp->ic(p, c); } void manager::abs_norm(polynomial const * p, numeral & norm) { m_imp->abs_norm(p, norm); } polynomial::numeral const & manager::numeral_lc(polynomial const * p, var x) { return m_imp->numeral_lc(p, x); } polynomial::numeral const & manager::numeral_tc(polynomial const * p) { return m_imp->numeral_tc(p); } void manager::content(polynomial const * p, var x, numeral & i, polynomial_ref & c) { polynomial_ref pp(*this); m_imp->iccp(p, x, i, c, pp); } void manager::content(polynomial const * p, var x, polynomial_ref & c) { scoped_numeral i(m_imp->m_manager.m()); content(p, x, i, c); if (!m_imp->m_manager.is_one(i)) { c = mul(i, c); } } void manager::primitive(polynomial const * p, var x, polynomial_ref & pp) { m_imp->pp(p, x, pp); } void manager::icpp(polynomial const * p, var x, numeral & i, polynomial_ref & c, polynomial_ref & pp) { m_imp->iccp(p, x, i, c, pp); } polynomial * manager::flip_sign_if_lm_neg(polynomial const * p) { return m_imp->flip_sign_if_lm_neg_core(p); } void manager::gcd(polynomial const * p, polynomial const * q, polynomial_ref & g) { m_imp->gcd(p, q, g); } polynomial * manager::derivative(polynomial const * p, var x) { return m_imp->derivative(p, x); } void manager::square_free(polynomial const * p, var x, polynomial_ref & r) { m_imp->square_free(p, x, r); } bool manager::is_square_free(polynomial const * p, var x) { return m_imp->is_square_free(p, x); } void manager::square_free(polynomial const * p, polynomial_ref & r) { m_imp->square_free(p, r); } bool manager::is_square_free(polynomial const * p) { return m_imp->is_square_free(p); } bool manager::eq(polynomial const * p1, polynomial const * p2) { return m_imp->eq(p1, p2); } polynomial * manager::compose_y(polynomial const * p, var y) { return m_imp->compose_y(p, y); } polynomial * manager::compose_minus_x(polynomial const * p) { return m_imp->compose_minus_x(p); } polynomial * manager::compose_1_div_x(polynomial const * p) { return m_imp->compose_1_div_x(p); } polynomial * manager::compose_x_div_y(polynomial const * p, var y) { return m_imp->compose_x_div_y(p, y); } void manager::compose(polynomial const * p, polynomial const * q, polynomial_ref & r) { m_imp->compose(p, q, r); } void manager::compose_x_minus_y(polynomial const * p, var y, polynomial_ref & r) { m_imp->compose_x_minus_y(p, y, r); } void manager::compose_x_plus_y(polynomial const * p, var y, polynomial_ref & r) { m_imp->compose_x_plus_y(p, y, r); } void manager::compose_x_minus_c(polynomial const * p, numeral const & c, polynomial_ref & r) { m_imp->compose_x_minus_c(p, c, r); } bool manager::sqrt(polynomial const * p, polynomial_ref & r) { return m_imp->sqrt(p, r); } polynomial * manager::normalize(polynomial const * p) { return m_imp->normalize(p); } void manager::exact_pseudo_remainder(polynomial const * p, polynomial const * q, var x, polynomial_ref & R) { m_imp->exact_pseudo_remainder(p, q, x, R); } void manager::pseudo_remainder(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & R) { m_imp->pseudo_remainder(p, q, x, d, R); } void manager::exact_pseudo_division(polynomial const * p, polynomial const * q, var x, polynomial_ref & Q, polynomial_ref & R) { m_imp->exact_pseudo_division(p, q, x, Q, R); } void manager::pseudo_division(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R) { m_imp->pseudo_division(p, q, x, d, Q, R); } polynomial * manager::exact_div(polynomial const * p, polynomial const * q) { return m_imp->exact_div(p, q); } polynomial * manager::exact_div(polynomial const * p, numeral const & c) { return m_imp->exact_div(p, c); } bool manager::divides(polynomial const * q, polynomial const * p) { return m_imp->divides(q, p); } void manager::quasi_resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r) { m_imp->quasi_resultant(p, q, x, r); } void manager::resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r) { m_imp->resultant(p, q, x, r); } void manager::discriminant(polynomial const * p, var x, polynomial_ref & r) { m_imp->discriminant(p, x, r); } void manager::psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) { m_imp->psc_chain(p, q, x, S); } bool manager::is_pos(polynomial const * p) { return m_imp->is_pos(p); } bool manager::is_neg(polynomial const * p) { return m_imp->is_neg(p); } bool manager::is_nonpos(polynomial const * p) { return m_imp->is_nonpos(p); } bool manager::is_nonneg(polynomial const * p) { return m_imp->is_nonneg(p); } void manager::rename(unsigned sz, var const * xs) { return m_imp->rename(sz, xs); } void manager::vars(polynomial const * p, var_vector & xs) { m_imp->vars(p, xs); } polynomial * manager::substitute(polynomial const * p, var2mpq const & x2v) { return m_imp->substitute(p, x2v); } polynomial * manager::substitute(polynomial const * p, unsigned xs_sz, var const * xs, mpq const * vs) { return m_imp->substitute(p, xs_sz, xs, vs); } polynomial * manager::substitute(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs) { return m_imp->substitute(p, xs_sz, xs, vs); } void manager::factor(polynomial const * p, factors & r, factor_params const & params) { m_imp->factor(p, r, params); } polynomial * manager::to_polynomial(unsigned sz, numeral const * p, var x) { return m_imp->to_polynomial(sz, p, x); } polynomial * manager::mk_glex_monic(polynomial const * p) { return m_imp->mk_glex_monic(p); } polynomial * manager::translate(polynomial const * p, var x, numeral const & v) { return m_imp->translate(p, x, v); } void manager::translate(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs, polynomial_ref & r) { return m_imp->translate(p, xs_sz, xs, vs, r); } polynomial * manager::mod_d(polynomial const * p, var2degree const & x2d) { return m_imp->mod_d(p, x2d); } void manager::exact_pseudo_division_mod_d(polynomial const * p, polynomial const * q, var x, var2degree const & x2d, polynomial_ref & Q, polynomial_ref & R) { m_imp->exact_pseudo_division_mod_d(p, q, x, x2d, Q, R); } void manager::eval(polynomial const * p, var2mpbqi const & x2v, mpbqi & r) { return m_imp->eval(p, x2v, r); } void manager::eval(polynomial const * p, var2mpq const & x2v, mpq & r) { return m_imp->eval(p, x2v, r); } void manager::eval(polynomial const * p, var2anum const & x2v, algebraic_numbers::anum & r) { return m_imp->eval(p, x2v, r); } void manager::display(std::ostream & out, monomial const * m, display_var_proc const & proc, bool user_star) const { m->display(out, proc, user_star); } void manager::display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) const { SASSERT(m_imp->consistent_coeffs(p)); p->display(out, m_imp->m_manager, proc, use_star); } void manager::display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc) const { p->display_smt2(out, m_imp->m_manager, proc); } }; polynomial::polynomial * convert(polynomial::manager & sm, polynomial::polynomial * p, polynomial::manager & tm, polynomial::var x, unsigned max_d) { ptr_buffer ms; polynomial::numeral_manager & nm = tm.m(); _scoped_numeral_buffer as(nm); unsigned sz = sm.size(p); if (&sm == &tm) { // same source and target manager. // So, we just return p return p; } else if (&(sm.mm()) == &(tm.mm())) { // polynomial managers share the same monomial manager. // So, we don't need to convert monomials. for (unsigned i = 0; i < sz; i++) { polynomial::monomial * m = sm.get_monomial(p, i); if (x == polynomial::null_var || sm.degree_of(m, x) <= max_d) { ms.push_back(m); as.push_back(polynomial::numeral()); nm.set(as.back(), sm.coeff(p, i)); } } } else { for (unsigned i = 0; i < sz; i++) { polynomial::monomial * m = sm.get_monomial(p, i); if (x == polynomial::null_var || sm.degree_of(m, x) <= max_d) { ms.push_back(tm.convert(m)); as.push_back(polynomial::numeral()); nm.set(as.back(), sm.coeff(p, i)); } } } return tm.mk_polynomial(as.size(), as.c_ptr(), ms.c_ptr()); } std::ostream & operator<<(std::ostream & out, polynomial_ref_vector const & seq) { unsigned sz = seq.size(); for (unsigned i = 0; i < sz; i++) { seq.m().display(out, seq.get(i)); out << "\n"; } return out; } z3-z3-4.4.1/src/math/polynomial/polynomial.h000066400000000000000000001324721260446376700206750ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial.h Abstract: Goodies for creating and handling polynomials. Author: Leonardo (leonardo) 2011-11-15 Notes: --*/ #ifndef POLYNOMIAL_H_ #define POLYNOMIAL_H_ #include"mpz.h" #include"rational.h" #include"obj_ref.h" #include"ref_vector.h" #include"z3_exception.h" #include"scoped_numeral.h" #include"scoped_numeral_vector.h" #include"params.h" #include"mpbqi.h" class small_object_allocator; namespace algebraic_numbers { class anum; class manager; }; namespace polynomial { typedef unsigned var; const var null_var = UINT_MAX; typedef svector var_vector; class monomial; int lex_compare(monomial const * m1, monomial const * m2); int lex_compare2(monomial const * m1, monomial const * m2, var min_var); int graded_lex_compare(monomial const * m1, monomial const * m2); int rev_lex_compare(monomial const * m1, monomial const * m2); int graded_rev_lex_compare(monomial const * m1, monomial const * m2); // It is used only for signing cancellation. class polynomial_exception : public default_exception { public: polynomial_exception(char const * msg):default_exception(msg) {} }; /** \brief A mapping from variables to degree */ class var2degree { unsigned_vector m_var2degree; public: void set_degree(var x, unsigned d) { m_var2degree.setx(x, d, 0); } unsigned degree(var x) const { return m_var2degree.get(x, 0); } void display(std::ostream & out) const; friend std::ostream & operator<<(std::ostream & out, var2degree const & ideal) { ideal.display(out); return out; } }; template class var2value { public: virtual ValManager & m() const = 0; virtual bool contains(var x) const = 0; virtual Value const & operator()(var x) const = 0; }; typedef var2value var2mpq; typedef var2value var2mpbqi; typedef var2value var2anum; class monomial_manager; /** \brief Parameters for polynomial factorization. */ struct factor_params { unsigned m_max_p; //!< factor in GF_p using primes p <= m_max_p (default UINT_MAX) unsigned m_p_trials; //!< Number of different finite factorizations: G_p1 ... G_pk, where k < m_p_trials unsigned m_max_search_size; //!< Threshold on the search space. factor_params(); factor_params(unsigned max_p, unsigned p_trials, unsigned max_search_size); void updt_params(params_ref const & p); /* REG_MODULE_PARAMS('factor', polynomial::factor_params::get_param_descrs') */ static void get_param_descrs(param_descrs & r); }; struct display_var_proc { virtual void operator()(std::ostream & out, var x) const { out << "x" << x; } }; class polynomial; class manager; typedef obj_ref monomial_ref; typedef obj_ref polynomial_ref; typedef ref_vector polynomial_ref_vector; typedef ptr_vector polynomial_vector; class manager { public: typedef unsynch_mpz_manager numeral_manager; typedef numeral_manager::numeral numeral; typedef svector numeral_vector; typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; /** \brief Contains a factorization of a polynomial of the form c * (f_1)^k_1 * ... (f_n)^k_n */ class factors { vector m_factors; svector m_degrees; manager & m_manager; numeral m_constant; unsigned m_total_factors; public: factors(manager & m); ~factors(); /** \brief Numer of distinct factors (not counting multiplicities). */ unsigned distinct_factors() const { return m_factors.size(); } /** \brief Numer of distinct factors (counting multiplicities). */ unsigned total_factors() const { return m_total_factors; } /** \brief Returns the factor at given position. */ polynomial_ref operator[](unsigned i) const; /** \brief Returns the constant (c above). */ numeral const & get_constant() const { return m_constant; } /** \brief Sets the constant. */ void set_constant(numeral const & constant); /** \brief Returns the degree of a factor (k_i above). */ unsigned get_degree(unsigned i) const { return m_degrees[i]; } /** \brief Sets the degree of a factor. */ void set_degree(unsigned i, unsigned degree); /** \brief Adds a polynomial to the factorization. */ void push_back(polynomial * p, unsigned degree); /** \brief Returns the polynomial that this factorization represents. */ void multiply(polynomial_ref & out) const; manager & m() const { return m_manager; } manager & pm() const { return m_manager; } void display(std::ostream& out) const; void reset(); friend std::ostream & operator<<(std::ostream & out, factors const & f) { f.display(out); return out; } }; struct imp; private: imp * m_imp; public: manager(numeral_manager & m, monomial_manager * mm = 0); manager(numeral_manager & m, small_object_allocator * a); ~manager(); numeral_manager & m() const; monomial_manager & mm() const; small_object_allocator & allocator() const; /** \brief Return true if Z_p[X1, ..., Xn] */ bool modular() const; /** \brief Return p in Z_p[X1, ..., Xn] \pre modular */ numeral const & p() const; /** \brief Set manager as Z[X1, ..., Xn] */ void set_z(); /** \brief Set manager as Z_p[X1, ..., Xn] */ void set_zp(numeral const & p); void set_zp(uint64 p); void set_cancel(bool f); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } /** \brief Abstract event handler. */ class del_eh { friend class manager; del_eh * m_next; public: del_eh():m_next(0) {} virtual void operator()(polynomial * p) = 0; }; /** \brief Install a "delete polynomial" event handler. The even hanlder is not owned by the polynomial manager. If eh = 0, then it uninstall the event handler. */ void add_del_eh(del_eh * eh); void remove_del_eh(del_eh * eh); /** \brief Create a new variable. */ var mk_var(); /** \brief Return the number of variables in the manager. */ unsigned num_vars() const; /** \brief Return true if x is a valid variable in this manager. */ bool is_valid(var x) const { return x < num_vars(); } /** \brief Increment reference counter. */ void inc_ref(polynomial * p); void inc_ref(monomial * m); /** \brief Decrement reference counter. */ void dec_ref(polynomial * p); void dec_ref(monomial * m); /** \brief Return an unique id associated with \c m. This id can be used to implement efficient mappings from monomial to data. */ static unsigned id(monomial const * m); /** \brief Return an unique id associated with \c m. This id can be used to implement efficient mappings from polynomial to data. */ static unsigned id(polynomial const * p); /** \brief Return true if \c m is the unit monomial. */ static bool is_unit(monomial const * m); /** \brief Return true if \c p is the zero polynomial. */ static bool is_zero(polynomial const * p); /** \brief Return true if \c p is the constant polynomial. */ static bool is_const(polynomial const * p); /** \brief Return true if \c m is univariate. */ static bool is_univariate(monomial const * m); /** \brief Return true if \c p is an univariate polynomial. */ static bool is_univariate(polynomial const * p); /** \brief Return true if m is linear (i.e., it is of the form 1 or x). */ static bool is_linear(monomial const * m); /** \brief Return true if all monomials in p are linear. */ static bool is_linear(polynomial const * p); /** \brief Return the degree of variable x in p. */ static unsigned degree(polynomial const * p, var x); /** \brief Return the polynomial total degree. That is, the degree of the monomial of maximal degree in p. */ static unsigned total_degree(polynomial const * p); /** \brief Return the number of monomials in p. */ static unsigned size(polynomial const * p); /** \brief Return the maximal variable occurring in p. Return null_var if p is a constant polynomial. */ static var max_var(polynomial const * p); /** \brief Return the coefficient of the i-th monomial in p. \pre i < size(p) */ static numeral const & coeff(polynomial const * p, unsigned i); /** \brief Given an univariate polynomial, return the coefficient of x_k */ static numeral const & univ_coeff(polynomial const * p, unsigned k); /** \brief Return a polynomial h that is the coefficient of x^k in p. if p does not contain any monomial containing x^k, then return 0. */ polynomial * coeff(polynomial const * p, var x, unsigned k); polynomial * coeff(polynomial const * p, var x, unsigned k, polynomial_ref & reduct); /** \brief Return true if the coefficient of x^k in p is an integer != 0. */ bool nonzero_const_coeff(polynomial const * p, var x, unsigned k); /** \brief Return true if the coefficient of x^k in p is an integer, and store it in c. */ bool const_coeff(polynomial const * p, var x, unsigned k, numeral & c); /** \brief Store in c the integer content of p. */ void int_content(polynomial const * p, numeral & c); /** \brief Returns sum of the absolute value of the coefficients. */ void abs_norm(polynomial const * p, numeral & norm); /** \brief Return the leading integer coefficient (among the loeding power of x, one is picked arbitrary). */ numeral const & numeral_lc(polynomial const * p, var x); /** \brief Return the trailing integer coefficient (i.e. the constant term). */ numeral const & numeral_tc(polynomial const * p); /** \brief Return the content i*c of p with respect to variable x. If p is a polynomial in Z[y_1, ..., y_k, x], then c is a polynomial in Z[y_1, ..., y_k] */ void content(polynomial const * p, var x, numeral & i, polynomial_ref & c); void content(polynomial const * p, var x, polynomial_ref & c); /** \brief Return the primitive polynomial of p with respect to variable x. If p is a polynomial in Z[y_1, ..., y_k, x], then pp is a polynomial in Z[y_1, ..., y_k, x] */ void primitive(polynomial const * p, var x, polynomial_ref & pp); /** \brief Return the integer content, content, and primitive polynomials of p with respect to x. i*c*pp = p If p is a polynomial in Z[y_1, ..., y_k, x], then c is a polynomial in Z[y_1, ..., y_k] pp is a polynomial in Z[y_1, ..., y_k, x] */ void icpp(polynomial const * p, var x, numeral & i, polynomial_ref & c, polynomial_ref & pp); polynomial * flip_sign_if_lm_neg(polynomial const * p); /** \breif Return the gcd g of p and q. */ void gcd(polynomial const * p, polynomial const * q, polynomial_ref & g); /** \brief Return the i-th monomial of p. */ static monomial * get_monomial(polynomial const * p, unsigned i); /** \brief Return the total degree of the given monomial. */ static unsigned total_degree(monomial const * m); /** \brief Return the size (number of variables) of the given monomial. */ static unsigned size(monomial const * m); /** \brief Convert a monomial created in a different manager. */ monomial * convert(monomial const * m); /** \brief Return the i-th variable in the given monomial. \pre i < size(m) */ static var get_var(monomial const * m, unsigned i); /** \brief Return the degree of the i-th variable in the given monomial. \pre i < size(m) */ static unsigned degree(monomial const * m, unsigned i); /** \brief Return the degree of x in the given monomial. */ static unsigned degree_of(monomial const * m, var x); /** \brief Return hash code for the given monomial. */ static unsigned hash(monomial const * m); /** \brief Return hash code for the given polynomial. */ unsigned hash(polynomial const * p); /** \brief Create the unit monomial. That is, the monomial of size zero. */ monomial * mk_unit(); /** \brief Create the zero polynomial. That is, the polynomial of size zero. */ polynomial * mk_zero(); /** \brief Create the constant polynomial \c r. \warning r is a number managed by the numeral_manager in the polynomial manager \warning r is reset. */ polynomial * mk_const(numeral & r); /** \brief Create the constant polynomial \c r. \pre r must be an integer */ polynomial * mk_const(rational const & r); /** \brief Create an univariate monomial. */ monomial * mk_monomial(var x); /** \brief Create an univariate monomial. */ monomial * mk_monomial(var x, unsigned k); /** \brief Create the monomial xs[0]*...*xs[sz-1] Remark: xs may contain duplicate variables. \warning The elements of xs will be reordered. */ monomial * mk_monomial(unsigned sz, var * xs); /** \brief Create the polynomial x^k */ polynomial * mk_polynomial(var x, unsigned k = 1); /** \brief Create the polynomial as[0]*ms[0] + ... + as[sz-1]*ms[sz - 1] \pre as's must be integers */ polynomial * mk_polynomial(unsigned sz, rational const * as, monomial * const * ms); /** \brief Create the polynomial as[0]*ms[0] + ... + as[sz-1]*ms[sz - 1] \warning as's are numbers managed by mpq_manager in the polynomial manager \warning the numerals in as are reset. */ polynomial * mk_polynomial(unsigned sz, numeral * as, monomial * const * ms); /** \brief Create the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1] + c \pre as's must be integers */ polynomial * mk_linear(unsigned sz, rational const * as, var const * xs, rational const & c); /** \brief Create the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1] + c \warning as's are numbers managed by mpq_manager in the polynomial manager \warning the numerals in as are reset. */ polynomial * mk_linear(unsigned sz, numeral * as, var const * xs, numeral & c); /** \brief Create an univariate polynomial of degree n as[0] + as[1]*x + as[2]*x^2 + ... + as[n]*x^n \warning \c as must contain n+1 elements. */ polynomial * mk_univariate(var x, unsigned n, numeral * as); /** \brief Return -p */ polynomial * neg(polynomial const * p); /** \brief Return p1 + p2 */ polynomial * add(polynomial const * p1, polynomial const * p2); /** \brief Return p1 - p2 */ polynomial * sub(polynomial const * p1, polynomial const * p2); /** \brief Return a1*m1*p1 + a2*m2*p2 */ polynomial * addmul(numeral const & a1, monomial const * m1, polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2); /** \brief Return p1 + a2*m2*p2 */ polynomial * addmul(polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2); /** \brief Return p1 + a2*p2 */ polynomial * addmul(polynomial const * p1, numeral const & a2, polynomial const * p2); /** \brief Return a * p */ polynomial * mul(numeral const & a, polynomial const * p); polynomial * mul(rational const & a, polynomial const * p); /** \brief Return p1 * p2 */ polynomial * mul(polynomial const * p1, polynomial const * p2); /** \brief Return m1 * m2 */ monomial * mul(monomial const * m1, monomial const * m2); /** \brief Return a * m * p */ polynomial * mul(numeral const & a, monomial const * m, polynomial const * p); /** \brief Return m * p */ polynomial * mul(monomial const * m, polynomial const * p); /** \brief Return true if m2 divides m1 */ bool div(monomial const * m1, monomial const * m2); /** \brief Return true if m2 divides m1, and store the result in r. */ bool div(monomial const * m1, monomial const * m2, monomial * & r); /** \brief Newton interpolation algorithm for multivariate polynomials. */ void newton_interpolation(var x, unsigned d, numeral const * inputs, polynomial * const * outputs, polynomial_ref & r); /** \brief Return the GCD of m1 and m2. Store in q1 and q2 monomials s.t. m1 = gcd * q1 m2 = gcd * q2 */ monomial * gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2); /** \brief Return true if the gcd of m1 and m2 is not 1. In that case, store in q1 and q2 monomials s.t. m1 = gcd * q1 m2 = gcd * q2 */ bool unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2); /** \brief Return m^k */ monomial * pw(monomial const * m, unsigned k); /** \brief Return the polynomial p^k */ void pw(polynomial const * p, unsigned k, polynomial_ref & r); /** \brief Return dp/dx */ polynomial * derivative(polynomial const * p, var x); /** \brief r := square free part of p with respect to x */ void square_free(polynomial const * p, var x, polynomial_ref & r); /** \brief Return true if p is square free with respect to x */ bool is_square_free(polynomial const * p, var x); /** \brief r := square free part of p */ void square_free(polynomial const * p, polynomial_ref & r); /** \brief Return true if p is square free */ bool is_square_free(polynomial const * p); /** \brief Return true if p1 == p2. */ bool eq(polynomial const * p1, polynomial const * p2); /** \brief Rename variables using the given permutation. sz must be num_vars() */ void rename(unsigned sz, var const * xs); /** \brief Given an univariate polynomial p(x), return the polynomial x^n * p(1/x), where n = degree(p) If u is a nonzero root of p, then 1/u is a root the resultant polynomial. */ polynomial * compose_1_div_x(polynomial const * p); /** \brief Given an univariate polynomial p(x), return the polynomial y^n * p(x/y), where n = degree(p) */ polynomial * compose_x_div_y(polynomial const * p, var y); /** \brief Given an univariate polynomial p(x), return p(-x) */ polynomial * compose_minus_x(polynomial const * p); /** \brief Given an univariate polynomial p(x) and a polynomial q(y_1, ..., y_n), return a polynomial r(y_1, ..., y_n) = p(q(y_1, ..., y_n)). */ void compose(polynomial const * p, polynomial const * q, polynomial_ref & r); /** \brief Given an univariate polynomial p(x), return the polynomial r(y) = p(y) */ polynomial * compose_y(polynomial const * p, var y); /** \brief Given an univariate polynomial p(x), return the polynomial r(x, y) = p(x - y). The result is stored in r. */ void compose_x_minus_y(polynomial const * p, var y, polynomial_ref & r); /** \brief Given an univariate polynomial p(x), return the polynomial r(x, y) = p(x + y). The result is stored in r. */ void compose_x_plus_y(polynomial const * p, var y, polynomial_ref & r); /** \brief Given an univariate polynomial p(x), return the polynomial r(x) = p(x - c). The result is stored in r. */ void compose_x_minus_c(polynomial const * p, numeral const & c, polynomial_ref & r); /** \brief Return the exact pseudo remainder of p by q, assuming x is the maximal variable. See comments at pseudo_division_core at polynomial.cpp for a description of exact pseudo division. */ void exact_pseudo_remainder(polynomial const * p, polynomial const * q, var x, polynomial_ref & R); void exact_pseudo_remainder(polynomial const * p, polynomial const * q, polynomial_ref & R) { exact_pseudo_remainder(p, q, max_var(q), R); } /** \brief Return the pseudo remainder of p by q, assuming x is the maximal variable. See comments at pseudo_division_core at polynomial.cpp for a description of exact pseudo division. */ void pseudo_remainder(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & R); void pseudo_remainder(polynomial const * p, polynomial const * q, unsigned & d, polynomial_ref & R) { pseudo_remainder(p, q, max_var(q), d, R); } /** \brief Return the exact pseudo division quotient and remainder. See comments at pseudo_division_core at polynomial.cpp for a description of exact pseudo division. */ void exact_pseudo_division(polynomial const * p, polynomial const * q, var x, polynomial_ref & Q, polynomial_ref & R); void exact_pseudo_division(polynomial const * p, polynomial const * q, polynomial_ref & Q, polynomial_ref & R) { exact_pseudo_division(p, q, max_var(q), Q, R); } /** \brief Return the pseudo division quotient and remainder. See comments at pseudo_division_core at polynomial.cpp for a description of exact pseudo division. */ void pseudo_division(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R); void pseudo_division(polynomial const * p, polynomial const * q, unsigned & d, polynomial_ref & Q, polynomial_ref & R) { pseudo_division(p, q, max_var(q), d, Q, R); } /** \brief Return p/q if q divides p. \pre q divides p. \remark p and q may be multivariate polynomials. */ polynomial * exact_div(polynomial const * p, polynomial const * q); /** \brief Return true if q divides p. */ bool divides(polynomial const * q, polynomial const * p); /** \brief Return p/c if c divides p. \pre c divides p. \remark p may be multivariate polynomial. */ polynomial * exact_div(polynomial const * p, numeral const & c); /** \brief Store in r the quasi-resultant of p and q with respect to variable x. Assume p and q are polynomials in Q[y_1, ..., y_n, x]. Then r is a polynomial in Q[y_1, ..., y_n]. Moreover, Forall a_1, ..., a_n, b, if p(a_1, ..., a_n, b) = q(a_1, ..., a_n, b) = 0 then r(a_1, ..., a_n) = 0 \pre p and q must contain x. \remark if r is the zero polynomial, then for any complex numbers a_1, ..., a_n, the univariate polynomials p(a_1, ..., a_n, x) and q(a_1, ..., a_n, x) in C[x] have a common root. C is the field of the complex numbers. */ void quasi_resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r); /** \brief Store in r the resultant of p and q with respect to variable x. See comments in polynomial.cpp for more details */ void resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r); /** \brief Stroe in r the discriminant of p with respect to variable x. discriminant(p, x, r) == resultant(p, derivative(p, x), x, r) */ void discriminant(polynomial const * p, var x, polynomial_ref & r); /** \brief Store in S the principal subresultant coefficients for p and q. */ void psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S); /** \brief Make sure the GCD of the coefficients is one. Make sure that the polynomial is a member of the polynomial ring of this manager. If manager is Z_p[X1, ..., Xn], and polynomial is in Z[X1, ..., Xn] return a new one where all coefficients are in Z_p */ polynomial * normalize(polynomial const * p); /** \brief Return true if p is a square, and store its square root in r. */ bool sqrt(polynomial const * p, polynomial_ref & r); /** \brief Return true if p is always positive for any assignment of its variables. This is an incomplete check. This method just check if all monomials are powers of two, and the coefficients are positive. */ bool is_pos(polynomial const * p); /** \brief Return true if p is always negative for any assignment of its variables. This is an incomplete check. */ bool is_neg(polynomial const * p); /** \brief Return true if p is always non-positive for any assignment of its variables. This is an incomplete check. */ bool is_nonpos(polynomial const * p); /** \brief Return true if p is always non-negative for any assignment of its variables. This is an incomplete check. */ bool is_nonneg(polynomial const * p); /** \brief Make sure the monomials in p are sorted using lexicographical order. Remark: the maximal monomial is at position 0. */ void lex_sort(polynomial * p); /** \brief Collect variables that occur in p into xs */ void vars(polynomial const * p, var_vector & xs); /** \brief Evaluate polynomial p using the assignment [x_1 -> v_1, ..., x_n -> v_n]. The result is store in r. All variables occurring in p must be in xs. */ void eval(polynomial const * p, var2mpbqi const & x2v, mpbqi & r); void eval(polynomial const * p, var2mpq const & x2v, mpq & r); void eval(polynomial const * p, var2anum const & x2v, algebraic_numbers::anum & r); /** \brief Apply substitution [x_1 -> v_1, ..., x_n -> v_n]. That is, given p \in Z[x_1, ..., x_n, y_1, ..., y_m] return a polynomial r \in Z[y_1, ..., y_m]. Moreover forall a_1, ..., a_m in Q sign(r(a_1, ..., a_m)) == sign(p(v_1, ..., v_n, a_1, ..., a_m)) */ polynomial * substitute(polynomial const * p, var2mpq const & x2v); polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, mpq const * vs); /** \brief Apply substitution [x_1 -> v_1, ..., x_n -> v_n]. That is, given p \in Z[x_1, ..., x_n, y_1, ..., y_m] return polynomial r(y_1, ..., y_m) = p(v_1, ..., v_n, y_1, ..., y_m) in Z[y_1, ..., y_m]. */ polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs); /** \brief Apply substitution [x -> v]. That is, given p \in Z[x, y_1, ..., y_m] return polynomial r(y_1, ..., y_m) = p(v, y_1, ..., y_m) in Z[y_1, ..., y_m]. */ polynomial * substitute(polynomial const * p, var x, numeral const & v) { return substitute(p, 1, &x, &v); } /** \brief Factorize the given polynomial p and store its factors in r. */ void factor(polynomial const * p, factors & r, factor_params const & params = factor_params()); /** \brief Dense univariate polynomial to sparse polynomial. */ polynomial * to_polynomial(unsigned sz, numeral const * p, var x); polynomial * to_polynomial(numeral_vector const & p, var x) { return to_polynomial(p.size(), p.c_ptr(), x); } /** \brief Make the leading monomial (with respect to graded lexicographical order) monic. \pre numeral_manager must be a field. */ polynomial * mk_glex_monic(polynomial const * p); /** \brief Return p'(y_1, ..., y_n, x) = p(y_1, ..., y_n, x + v) */ polynomial * translate(polynomial const * p, var x, numeral const & v); /** \brief Store p'(y_1, ..., y_n, x_1, ..., x_m) = p(y_1, ..., y_n, x_1 + v_1, ..., x_m + v_m) into r. */ void translate(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs, polynomial_ref & r); void translate(polynomial const * p, var_vector const & xs, numeral_vector const & vs, polynomial_ref & r) { SASSERT(xs.size() == vs.size()); translate(p, xs.size(), xs.c_ptr(), vs.c_ptr(), r); } /** \brief Remove monomials m if it contains x^k and x2d[x] >= k */ polynomial * mod_d(polynomial const * p, var2degree const & x2d); /** \brief (exact) Pseudo division modulo var->degree mapping. */ void exact_pseudo_division_mod_d(polynomial const * p, polynomial const * q, var x, var2degree const & x2d, polynomial_ref & Q, polynomial_ref & R); void display(std::ostream & out, monomial const * m, display_var_proc const & proc = display_var_proc(), bool use_star = true) const; void display(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc(), bool use_star = false) const; void display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc()) const; friend std::ostream & operator<<(std::ostream & out, polynomial_ref const & p) { p.m().display(out, p); return out; } }; typedef manager::factors factors; typedef manager::numeral numeral; typedef manager::numeral_manager numeral_manager; typedef manager::scoped_numeral scoped_numeral; typedef manager::scoped_numeral_vector scoped_numeral_vector; class scoped_set_z { manager & m; bool m_modular; scoped_numeral m_p; public: scoped_set_z(manager & _m):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_z(); } ~scoped_set_z() { if (m_modular) m.set_zp(m_p); } }; class scoped_set_zp { manager & m; bool m_modular; scoped_numeral m_p; public: scoped_set_zp(manager & _m, numeral const & p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } scoped_set_zp(manager & _m, uint64 p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } ~scoped_set_zp() { if (m_modular) m.set_zp(m_p); else m.set_z(); } }; }; typedef polynomial::polynomial_ref polynomial_ref; typedef polynomial::polynomial_ref_vector polynomial_ref_vector; polynomial::polynomial * convert(polynomial::manager & sm, polynomial::polynomial * p, polynomial::manager & tm, polynomial::var x = polynomial::null_var, unsigned max_d = UINT_MAX); inline polynomial::polynomial * convert(polynomial::manager & sm, polynomial_ref const & p, polynomial::manager & tm) { SASSERT(&sm == &(p.m())); return convert(sm, p.get(), tm); } inline polynomial_ref neg(polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.neg(p), m); } inline polynomial_ref operator-(polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.neg(p), m); } inline polynomial_ref operator+(polynomial_ref const & p1, polynomial_ref const & p2) { polynomial::manager & m = p1.m(); return polynomial_ref(m.add(p1, p2), m); } inline polynomial_ref operator+(polynomial_ref const & p1, int n) { polynomial::manager & m = p1.m(); polynomial_ref tmp(m.mk_const(rational(n)), m); return p1 + tmp; } inline polynomial_ref operator+(polynomial_ref const & p1, rational const & n) { polynomial::manager & m = p1.m(); polynomial_ref tmp(m.mk_const(n), m); return p1 + tmp; } inline polynomial_ref operator+(polynomial::numeral const & n, polynomial_ref const & p) { polynomial::manager & m = p.m(); polynomial_ref tmp(m.mk_const(n), m); return p + tmp; } inline polynomial_ref operator+(polynomial_ref const & p, polynomial::numeral const & n) { return operator+(n, p); } inline polynomial_ref operator-(polynomial_ref const & p1, polynomial_ref const & p2) { polynomial::manager & m = p1.m(); return polynomial_ref(m.sub(p1, p2), m); } inline polynomial_ref operator-(polynomial_ref const & p1, int n) { polynomial::manager & m = p1.m(); polynomial_ref tmp(m.mk_const(rational(n)), m); return p1 - tmp; } inline polynomial_ref operator-(polynomial_ref const & p1, rational const & n) { polynomial::manager & m = p1.m(); polynomial_ref tmp(m.mk_const(n), m); return p1 - tmp; } inline polynomial_ref operator-(polynomial::numeral const & n, polynomial_ref const & p) { polynomial::manager & m = p.m(); polynomial_ref tmp(m.mk_const(n), m); return p - tmp; } inline polynomial_ref operator-(polynomial_ref const & p, polynomial::numeral const & n) { return operator-(n, p); } inline polynomial_ref operator*(polynomial_ref const & p1, polynomial_ref const & p2) { polynomial::manager & m = p1.m(); return polynomial_ref(m.mul(p1, p2), m); } inline polynomial_ref operator*(rational const & n, polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.mul(n, p), m); } inline polynomial_ref operator*(int n, polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.mul(rational(n), p), m); } inline polynomial_ref operator*(polynomial::numeral const & n, polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.mul(n, p), m); } inline polynomial_ref operator*(polynomial_ref const & p, polynomial::numeral const & n) { return operator*(n, p); } inline bool eq(polynomial_ref const & p1, polynomial_ref const & p2) { polynomial::manager & m = p1.m(); return m.eq(p1, p2); } inline polynomial_ref operator^(polynomial_ref const & p, int k) { SASSERT(k > 0); polynomial::manager & m = p.m(); polynomial_ref r(m); m.pw(p, k, r); return polynomial_ref(r); } inline polynomial_ref operator^(polynomial_ref const & p, unsigned k) { return operator^(p, static_cast(k)); } inline polynomial_ref derivative(polynomial_ref const & p, polynomial::var x) { polynomial::manager & m = p.m(); return polynomial_ref(m.derivative(p, x), m); } inline bool is_zero(polynomial_ref const & p) { return p.m().is_zero(p); } inline bool is_const(polynomial_ref const & p) { return p.m().is_const(p); } inline bool is_linear(polynomial_ref const & p) { return p.m().is_linear(p); } inline bool is_univariate(polynomial_ref const & p) { return p.m().is_univariate(p); } inline unsigned total_degree(polynomial_ref const & p) { return p.m().total_degree(p); } inline polynomial::var max_var(polynomial_ref const & p) { return p.m().max_var(p); } inline unsigned degree(polynomial_ref const & p, polynomial::var x) { return p.m().degree(p, x); } inline unsigned size(polynomial_ref const & p) { return p.m().size(p); } inline polynomial_ref coeff(polynomial_ref const & p, polynomial::var x, unsigned k) { polynomial::manager & m = p.m(); return polynomial_ref(m.coeff(p, x, k), m); } inline polynomial_ref lc(polynomial_ref const & p, polynomial::var x) { polynomial::manager & m = p.m(); return polynomial_ref(m.coeff(p, x, degree(p, x)), m); } inline polynomial::numeral const & univ_coeff(polynomial_ref const & p, unsigned k) { return p.m().univ_coeff(p, k); } inline polynomial_ref content(polynomial_ref const & p, polynomial::var x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.content(p, x, r); return r; } inline polynomial_ref primitive(polynomial_ref const & p, polynomial::var x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.primitive(p, x, r); return r; } inline polynomial_ref gcd(polynomial_ref const & p, polynomial_ref const & q) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.gcd(p, q, r); return r; } inline bool is_square_free(polynomial_ref const & p, polynomial::var x) { return p.m().is_square_free(p, x); } inline polynomial_ref square_free(polynomial_ref const & p, polynomial::var x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.square_free(p, x, r); return r; } inline bool is_square_free(polynomial_ref const & p) { return p.m().is_square_free(p); } inline polynomial_ref square_free(polynomial_ref const & p) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.square_free(p, r); return r; } inline polynomial_ref compose_y(polynomial_ref const & p, polynomial::var y) { polynomial::manager & m = p.m(); return polynomial_ref(m.compose_y(p, y), m); } inline polynomial_ref compose_1_div_x(polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.compose_1_div_x(p), m); } inline polynomial_ref compose_x_div_y(polynomial_ref const & p, polynomial::var y) { polynomial::manager & m = p.m(); return polynomial_ref(m.compose_x_div_y(p, y), m); } inline polynomial_ref compose_minus_x(polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.compose_minus_x(p), m); } inline polynomial_ref compose(polynomial_ref const & p, polynomial_ref const & g) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.compose(p, g, r); return polynomial_ref(r); } inline polynomial_ref compose_x_minus_y(polynomial_ref const & p, polynomial::var y) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.compose_x_minus_y(p, y, r); return polynomial_ref(r); } inline polynomial_ref compose_x_plus_y(polynomial_ref const & p, polynomial::var y) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.compose_x_plus_y(p, y, r); return polynomial_ref(r); } inline polynomial_ref compose_x_minus_c(polynomial_ref const & p, polynomial::numeral const & c) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.compose_x_minus_c(p, c, r); return polynomial_ref(r); } inline polynomial_ref exact_div(polynomial_ref const & p, polynomial_ref const & q) { polynomial::manager & m = p.m(); return polynomial_ref(m.exact_div(p, q), m); } inline polynomial_ref normalize(polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.normalize(p), m); } inline bool sqrt(polynomial_ref const & p, polynomial_ref & r) { return p.m().sqrt(p, r); } inline polynomial_ref exact_pseudo_remainder(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.exact_pseudo_remainder(p, q, x, r); return polynomial_ref(r); } inline polynomial_ref exact_pseudo_remainder(polynomial_ref const & p, polynomial_ref const & q) { return exact_pseudo_remainder(p, q, max_var(q)); } inline polynomial_ref pseudo_remainder(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x, unsigned & d) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.pseudo_remainder(p, q, x, d, r); return polynomial_ref(r); } inline polynomial_ref pseudo_remainder(polynomial_ref const & p, polynomial_ref const & q, unsigned & d) { return pseudo_remainder(p, q, max_var(q), d); } inline polynomial_ref exact_pseudo_division(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x, polynomial_ref & R) { polynomial::manager & m = p.m(); polynomial_ref Q(m); m.exact_pseudo_division(p, q, x, Q, R); return polynomial_ref(Q); } inline polynomial_ref exact_pseudo_division(polynomial_ref const & p, polynomial_ref const & q, polynomial_ref & R) { return exact_pseudo_division(p, q, max_var(q), R); } inline polynomial_ref pseudo_division(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x, unsigned & d, polynomial_ref & R) { polynomial::manager & m = p.m(); polynomial_ref Q(m); m.pseudo_division(p, q, x, d, Q, R); return polynomial_ref(Q); } inline polynomial_ref pseudo_division(polynomial_ref const & p, polynomial_ref const & q, unsigned & d, polynomial_ref & R) { return pseudo_division(p, q, max_var(q), d, R); } inline polynomial_ref quasi_resultant(polynomial_ref const & p, polynomial_ref const & q, unsigned x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.quasi_resultant(p, q, x, r); return polynomial_ref(r); } inline polynomial_ref resultant(polynomial_ref const & p, polynomial_ref const & q, unsigned x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.resultant(p, q, x, r); return polynomial_ref(r); } inline polynomial_ref discriminant(polynomial_ref const & p, unsigned x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.discriminant(p, x, r); return polynomial_ref(r); } inline bool is_pos(polynomial_ref const & p) { return p.m().is_pos(p); } inline bool is_neg(polynomial_ref const & p) { return p.m().is_neg(p); } inline bool is_nonpos(polynomial_ref const & p) { return p.m().is_nonpos(p); } inline bool is_nonneg(polynomial_ref const & p) { return p.m().is_nonneg(p); } inline void factor(polynomial_ref const & p, polynomial::factors & r, polynomial::factor_params const & params = polynomial::factor_params()) { p.m().factor(p, r, params); } std::ostream & operator<<(std::ostream & out, polynomial_ref_vector const & seq); #endif z3-z3-4.4.1/src/math/polynomial/polynomial_cache.cpp000066400000000000000000000175461260446376700223570ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: polynomial_cache.cpp Abstract: "Hash-consing" for polynomials Author: Leonardo (leonardo) 2012-01-07 Notes: --*/ #include"polynomial_cache.h" #include"chashtable.h" namespace polynomial { struct poly_hash_proc { manager & m; poly_hash_proc(manager & _m):m(_m) {} unsigned operator()(polynomial const * p) const { return m.hash(p); } }; struct poly_eq_proc { manager & m; poly_eq_proc(manager & _m):m(_m) {} bool operator()(polynomial const * p1, polynomial const * p2) const { return m.eq(p1, p2); } }; struct psc_chain_entry { polynomial const * m_p; polynomial const * m_q; var m_x; unsigned m_hash; unsigned m_result_sz; polynomial ** m_result; psc_chain_entry(polynomial const * p, polynomial const * q, var x, unsigned h): m_p(p), m_q(q), m_x(x), m_hash(h), m_result_sz(0), m_result(0) { } struct hash_proc { unsigned operator()(psc_chain_entry const * entry) const { return entry->m_hash; } }; struct eq_proc { bool operator()(psc_chain_entry const * e1, psc_chain_entry const * e2) const { return e1->m_p == e2->m_p && e1->m_q == e2->m_q && e1->m_x == e2->m_x; } }; }; struct factor_entry { polynomial const * m_p; unsigned m_hash; unsigned m_result_sz; polynomial ** m_result; factor_entry(polynomial const * p, unsigned h): m_p(p), m_hash(h), m_result_sz(0), m_result(0) { } struct hash_proc { unsigned operator()(factor_entry const * entry) const { return entry->m_hash; } }; struct eq_proc { bool operator()(factor_entry const * e1, factor_entry const * e2) const { return e1->m_p == e2->m_p; } }; }; typedef chashtable polynomial_table; typedef chashtable psc_chain_cache; typedef chashtable factor_cache; struct cache::imp { manager & m; polynomial_table m_poly_table; psc_chain_cache m_psc_chain_cache; factor_cache m_factor_cache; polynomial_ref_vector m_cached_polys; svector m_in_cache; small_object_allocator & m_allocator; imp(manager & _m):m(_m), m_poly_table(poly_hash_proc(m), poly_eq_proc(m)), m_cached_polys(m), m_allocator(m.allocator()) { } ~imp() { reset_psc_chain_cache(); reset_factor_cache(); } void del_psc_chain_entry(psc_chain_entry * entry) { if (entry->m_result_sz != 0) m_allocator.deallocate(sizeof(polynomial*)*entry->m_result_sz, entry->m_result); entry->~psc_chain_entry(); m_allocator.deallocate(sizeof(psc_chain_entry), entry); } void del_factor_entry(factor_entry * entry) { if (entry->m_result_sz != 0) m_allocator.deallocate(sizeof(polynomial*)*entry->m_result_sz, entry->m_result); entry->~factor_entry(); m_allocator.deallocate(sizeof(factor_entry), entry); } void reset_psc_chain_cache() { psc_chain_cache::iterator it = m_psc_chain_cache.begin(); psc_chain_cache::iterator end = m_psc_chain_cache.end(); for (; it != end; ++it) { del_psc_chain_entry(*it); } m_psc_chain_cache.reset(); } void reset_factor_cache() { factor_cache::iterator it = m_factor_cache.begin(); factor_cache::iterator end = m_factor_cache.end(); for (; it != end; ++it) { del_factor_entry(*it); } m_factor_cache.reset(); } unsigned pid(polynomial * p) const { return m.id(p); } polynomial * mk_unique(polynomial * p) { if (m_in_cache.get(pid(p), false)) return p; polynomial * p_prime = m_poly_table.insert_if_not_there(p); if (p == p_prime) { m_cached_polys.push_back(p_prime); m_in_cache.setx(pid(p_prime), true, false); } return p_prime; } void psc_chain(polynomial * p, polynomial * q, var x, polynomial_ref_vector & S) { p = mk_unique(p); q = mk_unique(q); unsigned h = hash_u_u(pid(p), pid(q)); psc_chain_entry * entry = new (m_allocator.allocate(sizeof(psc_chain_entry))) psc_chain_entry(p, q, x, h); psc_chain_entry * old_entry = m_psc_chain_cache.insert_if_not_there(entry); if (entry != old_entry) { entry->~psc_chain_entry(); m_allocator.deallocate(sizeof(psc_chain_entry), entry); S.reset(); for (unsigned i = 0; i < old_entry->m_result_sz; i++) { S.push_back(old_entry->m_result[i]); } } else { m.psc_chain(p, q, x, S); unsigned sz = S.size(); entry->m_result_sz = sz; entry->m_result = static_cast(m_allocator.allocate(sizeof(polynomial*)*sz)); for (unsigned i = 0; i < sz; i++) { polynomial * h = mk_unique(S.get(i)); S.set(i, h); entry->m_result[i] = h; } } } void factor(polynomial * p, polynomial_ref_vector & distinct_factors) { distinct_factors.reset(); p = mk_unique(p); unsigned h = hash_u(pid(p)); factor_entry * entry = new (m_allocator.allocate(sizeof(factor_entry))) factor_entry(p, h); factor_entry * old_entry = m_factor_cache.insert_if_not_there(entry); if (entry != old_entry) { entry->~factor_entry(); m_allocator.deallocate(sizeof(factor_entry), entry); distinct_factors.reset(); for (unsigned i = 0; i < old_entry->m_result_sz; i++) { distinct_factors.push_back(old_entry->m_result[i]); } } else { factors fs(m); m.factor(p, fs); unsigned sz = fs.distinct_factors(); entry->m_result_sz = sz; entry->m_result = static_cast(m_allocator.allocate(sizeof(polynomial*)*sz)); for (unsigned i = 0; i < sz; i++) { polynomial * h = mk_unique(fs[i]); distinct_factors.push_back(h); entry->m_result[i] = h; } } } }; cache::cache(manager & m) { m_imp = alloc(imp, m); } cache::~cache() { dealloc(m_imp); } manager & cache::m() const { return m_imp->m; } polynomial * cache::mk_unique(polynomial * p) { return m_imp->mk_unique(p); } void cache::psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) { m_imp->psc_chain(const_cast(p), const_cast(q), x, S); } void cache::factor(polynomial const * p, polynomial_ref_vector & distinct_factors) { m_imp->factor(const_cast(p), distinct_factors); } void cache::reset() { manager & _m = m(); dealloc(m_imp); m_imp = alloc(imp, _m); } }; z3-z3-4.4.1/src/math/polynomial/polynomial_cache.h000066400000000000000000000015401260446376700220070ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: polynomial_cache.h Abstract: "Hash-consing" for polynomials Author: Leonardo (leonardo) 2012-01-07 Notes: --*/ #ifndef POLYNOMIAL_CACHE_H_ #define POLYNOMIAL_CACHE_H_ #include"polynomial.h" namespace polynomial { /** \brief Functor for creating unique polynomials and caching results of operations */ class cache { struct imp; imp * m_imp; public: cache(manager & m); ~cache(); manager & m() const; manager & pm() const { return m(); } polynomial * mk_unique(polynomial * p); void psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S); void factor(polynomial const * p, polynomial_ref_vector & distinct_factors); void reset(); }; }; #endif z3-z3-4.4.1/src/math/polynomial/polynomial_factorization.cpp000066400000000000000000001260221260446376700241560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial_factorization.cpp Abstract: Implementation of polynomial factorization. Author: Dejan (t-dejanj) 2011-11-15 Notes: [1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal, 46(8-10):1853-1859, 1967. [2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third edition, 1997. [3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993. --*/ #if 0 // disabled for reorg #include"trace.h" #include"util.h" #include"polynomial_factorization.h" #include"upolynomial_factorization_int.h" #include"prime_generator.h" using namespace std; namespace polynomial { typedef upolynomial::manager::scoped_numeral scoped_numeral; /** Generates a substitution of values for f -> f_univariate in order to reduce the factorization to the univariate case. @param f multivariate polynomial (square-free, primitive, vars(f) > 1) @param x the variable we want to keep as the univarate one @param f_lc the leading coefficient of f in x @param vars the vector of all variables from f witouth x @param size the bound to use for selecting the values, i.e. |a_i| <= size @param a the output values corresponding to vairables in (place place for x should be ignored) @param f_univariate the output substitution */ void generate_substitution_values( polynomial_ref const & f, var x, polynomial_ref const & f_lc, var_vector const & vars, unsigned & size, upolynomial::numeral_vector & a, upolynomial::manager upm, upolynomial::numeral_vector & f_u) { SASSERT(a.size() == vars.size()); TRACE("polynomial::factorization", tout << "polynomial::generate_substitution_values(f = " << f << ", f_lc = " << f_lc << ")";); // f = f_n x^n + ... + f_0, square-free and primitive // this means // f_lc = f_n // since f is primitive, // we are looking for numbers a_i such that // (1) f_lc doesn't vanish // (2) f_u = f(a_0, ..., a_n, x) is square-free manager & pm = f.m(); numeral_manager & nm = pm.m(); // polynomial to use for subtituting into the lc(f) polynomial_ref f_lc_subst(pm); // random generator random_gen generator; // increase the size every once in a while (RETHINK THIS) unsigned inc_size_c = 0; unsigned inc_size_c_max = size; while (true) { // see if we should increase the size of the substitution if ((++ inc_size_c) % inc_size_c_max == 0) { size ++; inc_size_c = 0; inc_size_c_max *= 2; } // the head coefficient we'll substitute in f_lc_subst = f_lc; bool vanished = false; for (unsigned i = 0; i < vars.size() && !vanished; ++ i) { SASSERT(vars[i] != x); // the value for x_i nm.set(a[i], (int)generator(2*size+1) - (int)size); // substitute f_lc_subst = pm.substitute(f_lc_subst, x, a[i]); // did it vanish vanished = pm.is_zero(f_lc_subst); } if (vanished) { // leading coefficient vanished, try again continue; } // substitute into f and get the univariate one polynomial_ref f_subst(pm); f_subst = pm.substitute(f, vars.size(), vars.c_ptr(), a.c_ptr()); upm.to_numeral_vector(f_subst, f_u); // if the result is not square-free we try again if (!upm.is_square_free(f_u)) continue; // found it, break break; } } /** \brief Bound for the coefficients of the factorst of the multivariate polynomial f. R Returns power of p -> p^e that covers the bound We use the gelfond bound here: d_i: degree of x_i in f(x1, ..., x_n) bound = |f| * 2^(\sum d_i - (n-1)/2)) */ void multivariate_factor_coefficient_bound(polynomial_ref const & f, var x, numeral const & p, unsigned & e, numeral & p_e, var2degree & d) { manager & pm = f.m(); numeral_manager & nm = pm.m(); // compute g = lc(f)*f polynomial_ref f_lc(pm), g(pm); f_lc = pm.coeff(f, x, pm.degree(f, x)); g = pm.mul(f, f_lc); // get the norm scoped_numeral g_norm(nm); pm.abs_norm(g, g_norm); // get the variable degrees var_vector vars; pm.vars(f, vars); unsigned power = 0; for (unsigned i = 0; i < vars.size(); ++ i) { unsigned c_d = pm.degree(g, vars[i]); d.set_degree(vars[i], c_d + 1); power += c_d; } power = power - (vars.size()-1)/2; // compute the bound scoped_numeral bound(nm); nm.set(bound, 2); nm.power(bound, power, bound); nm.mul(g_norm, bound, bound); // return the first power of two that is bigger than the norm e = 1; nm.set(p_e, p); while (nm.lt(p_e, bound)) { nm.mul(p_e, p_e, p_e); e *= 2; } } // check that A*S+B*T=C in zp mod ideal bool check_solve(zp_manager & zp_pm, var2degree const & ideal, zp_polynomial_ref const & A, zp_polynomial_ref const & B, zp_polynomial_ref const & C, zp_polynomial_ref const & S, zp_polynomial_ref const & T) { zp_polynomial_ref AS(zp_pm), BT(zp_pm), sum(zp_pm); AS = zp_pm.mul(A, S); AS = zp_pm.mod_d(AS, ideal); BT = zp_pm.mul(B, T); BT = zp_pm.mod_d(BT, ideal); sum = zp_pm.add(AS, BT); TRACE("polynomial::factorization::multivariate", tout << "zp_pm = Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; tout << "ideal = " << ideal << endl; tout << "A = " << A << endl; tout << "B = " << B << endl; tout << "S = " << S << endl; tout << "T = " << T << endl; tout << "C = " << C << endl; tout << "sum = " << sum << endl; ); bool result = zp_pm.eq(sum, C); return result; } /** Solve the equation A*S + B*T = C, given, AU + BV = 1, with deg(T) < deg(A) S = U*C + tB T = V*C - tA we divide VC with A to get (T, t) */ template void solve(zp_manager & zp_pm, var x, var2degree const & ideal, zp_polynomial_ref const & A, zp_polynomial_ref const & U, zp_polynomial_ref const & B, zp_polynomial_ref const & V, zp_polynomial_ref const & C, output_manager & out_pm, typename output_manager::polynomial_ref & S_out, typename output_manager::polynomial_ref & T_out) { TRACE("polynomial::factorization::multivariate", tout << "polynomial::solve(" << endl; tout << "zp_pm = Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; tout << "ideal = " << ideal << endl; tout << "A = " << A << endl; tout << "B = " << B << endl; tout << "U = " << U << endl; tout << "V = " << V << endl; tout << "C = " << C << endl; ); // solution is S = C*U + tB, T = C*V - tA zp_polynomial_ref CV(zp_pm); CV = zp_pm.mul(C, V); CV = zp_pm.mod_d(CV, ideal); zp_polynomial_ref CU(zp_pm); CU = zp_pm.mul(C, U); CU = zp_pm.mod_d(CU, ideal); zp_polynomial_ref t(zp_pm), T(zp_pm); zp_pm.exact_pseudo_division_mod_d(CV, A, x, ideal, t, T); zp_polynomial_ref tB(zp_pm); tB = zp_pm.mul(t, B); tB = zp_pm.mod_d(tB, ideal); zp_polynomial_ref S(zp_pm); S = zp_pm.add(CU, tB); SASSERT(check_solve(zp_pm, ideal, A, B, C, S, T)); // convert to the other manager S_out = convert(zp_pm, S, out_pm); T_out = convert(zp_pm, T, out_pm); TRACE("polynomial::factorization::multivariate", tout << "CU = " << CU << endl; tout << "CV = " << CV << endl; tout << "t = " << t << endl; tout << "--- solution ---" << endl; tout << "S = " << S_out << endl; tout << "T = " << T_out << endl; ); } /** A, B, U, V: multivariate polynomials in Z_p[x, ...], mod ..., also the output polynomials, y is not there C: C = A*B in Z_p[x, ...] mod p, ... ideal: all the vars we care about in the ideal y, the variable we are lifting is not in ideal_vars, we will add it A monic, A*U+B*V = 1 p is not necessary prime, it's a power of a prime we're doing quadratic lifting here output: added y, i.e. * all polynomials in Z_p[x, ..., y] mod (..., y^d) */ void multivariate_hansel_lift_ideal( zp_manager & zp_pm, var x, zp_polynomial_ref const & C, zp_polynomial_ref & A, zp_polynomial_ref & U, zp_polynomial_ref & B, zp_polynomial_ref & V, var2degree & ideal, var y, unsigned d) { numeral_manager & nm = zp_pm.m().m(); TRACE("polynomial::factorization::multivariate", tout << "polynomial::multiratiate_hensel_lift_ideal" << endl; tout << "zp_pm is Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; tout << "x = x" << x << endl; tout << "y = x" << y << endl; tout << "C = " << C << endl; tout << "A = " << A << endl; tout << "B = " << B << endl; tout << "U = " << U << endl; tout << "V = " << V << endl; tout << "ideal = " << ideal << endl; ); // constant 1 scoped_numeral one(nm); nm.set(one, 1); zp_polynomial_ref one_p(zp_pm); one_p = zp_pm.mk_const(one); SASSERT(zp_pm.degree(A, y) == 0 && zp_pm.degree(B, y) == 0 && zp_pm.degree(U, y) == 0 && zp_pm.degree(V, y) == 0); // update the ideal, and start with y ideal.set_degree(y, 1); unsigned current_d = 1; zp_polynomial_ref current_power(zp_pm); current_power = zp_pm.mk_polynomial(y); // lift quadratic until we are over the asked for while (current_d < d) { TRACE("polynomial::factorization::multivariate", tout << "zp_pm = Z_" << nm.to_string(zp_pm.m().p()) << endl; tout << "ideal = " << ideal << endl; tout << "C = " << C << endl; tout << "A = " << A << endl; tout << "B = " << B << endl; ); // again, classic hensel: // since C = A*B mod (p, ideal, y^k) we know that (C - A*B) = 0 mod (p, ideal, y^k) // f = (C - A*B) mod (p, ideal, y^k) and thus divisible by y^current_d = current_power zp_polynomial_ref f(zp_pm); f = zp_pm.mul(A, B); TRACE("polynomial::factorization::multivariate", tout << "zp_pm = Z_" << nm.to_string(zp_pm.m().p()) << endl; tout << "ideal = " << ideal << endl; tout << "C = " << C << endl; tout << "A = " << A << endl; tout << "B = " << B << endl; tout << "f = " << f << endl; ); f = zp_pm.sub(C, f); f = zp_pm.exact_div(f, current_power); f = zp_pm.mod_d(f, ideal); TRACE("polynomial::factorization::multivariate", tout << "A = " << A << endl; tout << "B = " << B << endl; ); // get the S, T, solution to A*S+B*T = f, with d(T, x) < d(A, x) // but we know that S = U*f + Bt, T = V*f - At, so we do division zp_polynomial_ref S(zp_pm), T(zp_pm); solve(zp_pm, x, ideal, A, U, B, V, f, zp_pm, S, T); // now, lets lift A and B // we want A1 = A + T*y^d, B1 = B + S*y^d with A1*B1 = C mod (ideal, y^(2*k)) // hence A*B + y^d*(A*S+B*T) = C S = zp_pm.mul(S, current_power); T = zp_pm.mul(T, current_power); A = zp_pm.add(A, T); B = zp_pm.add(B, S); TRACE("polynomial::factorization::multivariate", tout << "A = " << A << endl; tout << "B = " << B << endl; ); // again, classic quadratic hensel // we need A*U1 + B*V1 = 1 mod (p, ideal, y^2), from above // U1 = U + S*y^d, V1 = V + T*y^d, hence A*U + B*V + y^d(S + T) = 1 mod new ideal^2 // we know that y^d divides (1-UA-BV) so we compute f = (1-UA-BV)/y^d // UA + VB + y^d(SA + TB) = 1 (mod ideal^2) // SA + TB = f (mod ideal) // we solve for S, T again, and do as above zp_polynomial_ref UA(zp_pm), BV(zp_pm); f = zp_pm.mk_const(one); UA = zp_pm.mul(U, A); BV = zp_pm.mul(V, B); f = zp_pm.sub(f, UA); f = zp_pm.sub(f, BV); TRACE("polynomial::factorization::multivariate", tout << "ideal = " << ideal << endl; tout << "current_power = " << current_power << endl; tout << "UA = " << UA << endl; tout << "BV = " << BV << endl; tout << "f = " << f << endl; tout << "x = x" << x << endl; tout << "y = x" << y << endl; ); f = zp_pm.exact_div(f, current_power); f = zp_pm.mod_d(f, ideal); // get the S, T, solution to A*S+B*T = f, with d(T, x) < d(A, x) solve(zp_pm, x, ideal, A, U, B, V, f, zp_pm, S, T); // now, lets lift U and V S = zp_pm.mul(S, current_power); U = zp_pm.add(U, S); T = zp_pm.mul(T, current_power); V = zp_pm.add(V, T); // lift the ideal current_d *= 2; current_power = zp_pm.mul(current_power, current_power); ideal.set_degree(y, current_d); // move, A, B, C, D into the ideal A = zp_pm.mod_d(A, ideal); B = zp_pm.mod_d(B, ideal); S = zp_pm.mod_d(S, ideal); T = zp_pm.mod_d(T, ideal); TRACE("polynomial::factorization::multivariate", tout << "current_d = " << d << endl; tout << "zp_pm is Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; tout << "x = x" << x << endl; tout << "y = x" << y << endl; tout << "C = " << C << endl; tout << "A = " << A << endl; tout << "B = " << B << endl; tout << "U = " << U << endl; tout << "V = " << V << endl; tout << "ideal = " << ideal << endl; ); SASSERT(check_solve(zp_pm, ideal, A, B, one_p, U, V)); } } template bool are_equal_in( manager_to_check pm, typename manager_1::polynomial_ref const & A, typename manager_2::polynomial_ref const & B) { typename manager_to_check::polynomial_ref A_pm(pm), B_pm(pm); A_pm = convert(A.m(), A, pm); B_pm = convert(B.m(), B, pm); bool equal = pm.eq(A_pm, B_pm); return equal; } /** C: target multivariate polynomial mod ideal, p^e, the manager is in p^e x: main variable A, B, U, V: univariate polynomials in Z_p[x] such that U*A+B*V=1 mod ideal, these guys managers are in Z_p output: A_lifted, B_lifted, A = A_lifted mod ideal A_lifted*B_lifted = f mod x_i^d_i, p^e */ void multivariate_hansel_lift_zp( manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, zp_polynomial_ref const & C_pe, var x, unsigned e, zp_polynomial_ref const & A_p, zp_polynomial_ref const & U_p, zp_polynomial_ref const & B_p, zp_polynomial_ref const & V_p, var2degree const & ideal, zp_polynomial_ref & A_lifted, zp_polynomial_ref & B_lifted) { TRACE("polynomial::factorization::multivariate", tout << "polynomial::multiratiate_hensel_lift_zp:" << endl; tout << "zp_pm = Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; tout << "zpe_pm = Z_" << zpe_pm.m().m().to_string(zpe_pm.m().p()) << endl; tout << "x = x" << x << endl; tout << "ideal = " << ideal << endl; tout << "C_pe = " << C_pe << "," << endl; tout << "A_p = " << A_p << "," << endl; tout << "B_p = " << B_p << "," << endl; ); // fixed zpe_pm // upolynomial::zp_numeral_manager & zpe_nm = zpe_pm.m(); // numeral const & pe = zpe_nm.p(); // fixed zp_pm upolynomial::zp_numeral_manager & zp_nm = zp_pm.m(); numeral const & p = zp_nm.p(); // regular numeral manager and mangager numeral_manager & nm = zp_nm.m(); // sliding zpk_pm mod p^k upolynomial::zp_numeral_manager zpk_nm(nm, p); zp_manager zpk_pm(zpk_nm, &zp_pm.mm()); // in the end we copy the result over to zpe unsigned k = 1; upolynomial::scoped_numeral pk(nm); nm.set(pk, zpk_nm.p()); // constant 1 scoped_numeral one(nm); nm.set(one, 1); zp_polynomial_ref one_p(zpk_pm); one_p = zpk_pm.mk_const(one); // lift until you get over the requested power of e zp_polynomial_ref A_pk(zpk_pm), B_pk(zpk_pm), U_pk(zpk_pm), V_pk(zpk_pm); A_pk = convert(zp_pm, A_p, zpk_pm); B_pk = convert(zp_pm, B_p, zpk_pm); U_pk = convert(zp_pm, U_p, zpk_pm); V_pk = convert(zp_pm, V_p, zpk_pm); TRACE("polynomial::factorization::multivariate", tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "A_pk = " << A_pk << endl; tout << "B_pk = " << B_pk << endl; tout << "U_pk = " << U_pk << endl; tout << "V_pk = " << V_pk << endl; ); SASSERT(check_solve(zpk_pm, ideal, A_pk, B_pk, one_p, U_pk, V_pk)); while (k < e) { // standard hensel: // (C - AB) and is divisible by p^k, so we compute f = (C - AB)/p^k mod ideal in Z[...] zp_polynomial_ref f_pk(zpk_pm); polynomial_ref A_pk_in_Z(pm), B_pk_in_Z(pm), AB_in_Z(pm), f_in_Z(pm); f_in_Z = convert(zpe_pm, C_pe, pm); A_pk_in_Z = convert(zpk_pm, A_pk, pm); B_pk_in_Z = convert(zpk_pm, B_pk, pm); AB_in_Z = pm.mul(A_pk_in_Z, B_pk_in_Z); AB_in_Z = pm.mod_d(AB_in_Z, ideal); f_in_Z = pm.sub(f_in_Z, AB_in_Z); f_in_Z = pm.exact_div(f_in_Z, pk); f_in_Z = pm.mod_d(f_in_Z, ideal); f_pk = convert(pm, f_in_Z, zpk_pm); TRACE("polynomial::factorization::multivariate", tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "f_pk = " << f_pk << endl; ); // standard hensel we need to lift to p^(2k) // we have U*A+V*B = 1, C = A*B, so p^k divides C - AB // we want A1 = A + p^k*S, B1 = B + p^k*T, and also // C - (A + p^k*S)*(B + p^k*T) = 0 mod (p^2k) // C - A*B = p^k (T*A + S*B), i.e. // f = (C - A*B)/p^k = (T*A + S*B), so we solve this equation in Z_p^k polynomial_ref S_in_Z(pm), T_in_Z(pm); solve(zpk_pm, x, ideal, A_pk, U_pk, B_pk, V_pk, f_pk, pm, T_in_Z, S_in_Z); TRACE("polynomial::factorization::multivariate", tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "S_in_Z = " << S_in_Z << endl; tout << "T_in_Z = " << T_in_Z << endl; ); // lift A and B to, A = A + p^k*S, B = B + p^k*T polynomial_ref A_next_in_Z(pm), B_next_in_Z(pm); S_in_Z = pm.mul(pk, S_in_Z); S_in_Z = pm.mod_d(S_in_Z, ideal); A_next_in_Z = convert(zpk_pm, A_pk, pm); A_next_in_Z = pm.add(A_next_in_Z, S_in_Z); T_in_Z = pm.mul(pk, T_in_Z); T_in_Z = pm.mod_d(T_in_Z, ideal); B_next_in_Z = convert(zpk_pm, B_pk, pm); B_next_in_Z = pm.add(B_next_in_Z, T_in_Z); TRACE("polynomial::factorization::multivariate", tout << "pk = " << nm.to_string(pk) << endl; tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "S_in_Z = " << S_in_Z << endl; tout << "T_in_Z = " << T_in_Z << endl; tout << "A_pk = " << A_pk << endl; tout << "B_pk = " << B_pk << endl; tout << "A_next_in_Z = " << A_next_in_Z << endl; tout << "B_next_in_Z = " << B_next_in_Z << endl; ); bool eq1 = are_equal_in(zpk_pm, A_next_in_Z, A_pk); SASSERT(eq1); bool eq2 = are_equal_in(zpk_pm, B_next_in_Z, B_pk); SASSERT(eq2); // again, classic quadratic hensel // we need A*U1 + B*V1 = 1 mod p^2k, from above // U1 = U + p^k*S, V1 = V + p^k*T, hence A*U + B*V + p^k*(S + T) = 1 mod (p^2k) // we know that p^k divides (1-UA-BV) so we compute f = (1-UA-BV)/p^k // UA + VB + p^k(SA + TB) = 1 (mod p^k) // SA + TB = f (mod ideal) // we solve for S, T again, and do as above polynomial_ref U_pk_in_Z(pm), V_pk_in_Z(pm), UA_in_Z(pm), BV_in_Z(pm); U_pk_in_Z = convert(zpk_pm, U_pk, pm); V_pk_in_Z = convert(zpk_pm, V_pk, pm); f_in_Z = pm.mk_const(one); UA_in_Z = pm.mul(U_pk_in_Z, A_next_in_Z); UA_in_Z = pm.mod_d(UA_in_Z, ideal); BV_in_Z = pm.mul(V_pk_in_Z, B_next_in_Z); BV_in_Z = pm.mod_d(BV_in_Z, ideal); f_in_Z = pm.sub(f_in_Z, UA_in_Z); f_in_Z = pm.sub(f_in_Z, BV_in_Z); TRACE("polynomial::factorization::multivariate", tout << "pk = " << nm.to_string(pk) << endl; tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "U_pk_in_Z = " << U_pk_in_Z << endl; tout << "V_pk_in_Z = " << V_pk_in_Z << endl; tout << "UA_in_Z = " << UA_in_Z << endl; tout << "BV_in_Z = " << BV_in_Z << endl; tout << "f_in_Z = " << f_in_Z << endl; ); f_in_Z = pm.exact_div(f_in_Z, pk); f_pk = convert(pm, f_in_Z, zpk_pm); // get the S, T, solution to A*S+B*T = f, with d(T, x) < d(A, x) solve(zpk_pm, x, ideal, A_pk, U_pk, B_pk, V_pk, f_pk, pm, S_in_Z, T_in_Z); TRACE("polynomial::factorization::multivariate", tout << "pk = " << nm.to_string(pk) << endl; tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "S_in_Z = " << S_in_Z << endl; tout << "T_in_Z = " << T_in_Z << endl; ); // go to the next zpk scoped_numeral next_pk(nm); nm.mul(pk, pk, next_pk); zpk_nm.set_p(next_pk); TRACE("polynomial::factorization::multivariate", tout << "zp_pk = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; ); // lift U zp_polynomial_ref S_pk(zpk_pm); S_in_Z = pm.mul(pk, S_in_Z); S_pk = convert(pm, S_in_Z, zpk_pm); TRACE("polynomial::factorization::multivariate", tout << "S_pk = " << S_pk << endl; ); U_pk = zpk_pm.add(U_pk, S_pk); // lift V zp_polynomial_ref T_pk(zpk_pm); T_in_Z = pm.mul(pk, T_in_Z); T_pk = convert(pm, T_in_Z, zpk_pm); TRACE("polynomial::factorization::multivariate", tout << "T_pk = " << T_pk << endl; ); V_pk = zpk_pm.add(V_pk, T_pk); // lift A and B TRACE("polynomial::factorization::multivariate", tout << "A_pk_in_Z = " << A_pk_in_Z << endl; tout << "B_pk_in_Z = " << B_pk_in_Z << endl; ); A_pk = convert(pm, A_pk_in_Z, zpk_pm); B_pk = convert(pm, B_pk_in_Z, zpk_pm); // move to the next pk k *= 2; nm.set(pk, next_pk); TRACE("polynomial::factorization::multivariate", tout << "zp_pk = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "A_pk = " << A_pk << endl; tout << "B_pk = " << B_pk << endl; tout << "U_pk = " << U_pk << endl; tout << "V_pk = " << V_pk << endl; tout << "C_pe = " << C_pe << endl; ); SASSERT(check_solve(zpk_pm, ideal, A_pk, B_pk, one_p, U_pk, V_pk)); } // now convert to the non-sliding zpe_manager SASSERT(k == e); A_lifted = convert(zpk_pm, A_pk, zpe_pm); B_lifted = convert(zpk_pm, B_pk, zpe_pm); } /** f: target multivariate polynomial mod x_i^d_i, p^e x: main variable all_vars: all variables (including x) A, B, U, V: univariate polynomials in Z_p[x] such that U*A+B*V=1 from ext gcd output: A_lifted, B_lifted d(A) = d(A_lifted), A = A_lifted mod x_i^d_i, p A_lifted*B_lifted = f mod x_i^d_i, p^e */ void multivariate_hensel_lift( manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, zp_polynomial_ref const & f, var x, unsigned e, var_vector const & all_vars, upolynomial::zp_manager & zp_upm, upolynomial::numeral_vector const & U, upolynomial::numeral_vector const & A, upolynomial::numeral_vector const & V, upolynomial::numeral_vector const & B, var2degree & target_ideal, zp_polynomial_ref & A_lifted, zp_polynomial_ref & B_lifted) { upolynomial::zp_numeral_manager & zp_nm = zp_upm.m(); upolynomial::numeral_manager & nm = zp_nm.m(); TRACE("polynomial::factorization::multivariate", tout << "polynomial::multiratiate_hensel_lift(" << endl; tout << "f = " << f << "," << endl; tout << "x = x" << x << "," << endl; tout << "e = " << e << "," << endl; tout << "U = "; zp_upm.display(tout, U); tout << "," << endl; tout << "A = "; zp_upm.display(tout, A); tout << "," << endl; tout << "V = "; zp_upm.display(tout, V); tout << "," << endl; tout << "B = "; zp_upm.display(tout, B); tout << "," << endl; tout << "target_ideal = " << target_ideal << "," << endl; tout << "p = " << nm.to_string(zp_pm.m().p()) << endl; tout << "pe = " << nm.to_string(zpe_pm.m().p()) << endl; ); // multivariate versions of A, B, U, V that we keep lifting over ideal x_i^d_i zp_polynomial_ref A_m_p(zp_pm), B_m_p(zp_pm), U_m_p(zp_pm), V_m_p(zp_pm); A_m_p = zp_pm.to_polynomial(A, x); B_m_p = zp_pm.to_polynomial(B, x); U_m_p = zp_pm.to_polynomial(U, x); V_m_p = zp_pm.to_polynomial(V, x); TRACE("polynomial::factorization::multivariate", tout << "A_m_p = " << A_m_p << endl; tout << "B_m_p = " << B_m_p << endl; tout << "U_m_p = " << U_m_p << endl; tout << "V_m_p = " << V_m_p << endl; ); // the the target in Z_p[...] zp_polynomial_ref C_m_p(zp_pm); C_m_p = convert(zpe_pm, f, zp_pm); // lift each variable individually var2degree lifted_ideal; unsigned_vector lifted_degs; for (unsigned i = 0; i < all_vars.size(); ++ i) { if (all_vars[i] == x) { // skip the main variable continue; } // current variable and degree we are lifting to, y^(d_y), at least var y = all_vars[i]; // lift to y^(d_y) multivariate_hansel_lift_ideal(zp_pm, x, C_m_p, A_m_p, U_m_p, B_m_p, V_m_p, lifted_ideal, y, target_ideal.degree(y)); } TRACE("polynomial::factorization::multivariate", tout << "A_m_p = " << A_m_p << endl; tout << "B_m_p = " << B_m_p << endl; tout << "U_m_p = " << U_m_p << endl; tout << "V_m_p = " << V_m_p << endl; tout << "lifted_ideal = " << lifted_ideal << endl; ); // now lift it all to p^e multivariate_hansel_lift_zp(pm, zp_pm, zpe_pm, f, x, e, A_m_p, U_m_p, B_m_p, V_m_p, lifted_ideal, A_lifted, B_lifted); } /** f: multivariate polynomial x: main variable all_vars: all variables (including x) f_u: f mod x_1, ..., x_n (excluding mod x), i.e. this is f(0, x), f_u is square_free f_u_zp_factors: monic factors of f_u (mod p), pairvise gcd = 1 we're lifting the factors to mod x_1^d_1, ..., x_n&d_n (excliding x), mod p^e i.e. such that f congruent to the new factors. output goes to f_zpe factors. */ void multivariate_hensel_lift( manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, polynomial_ref const & f, var x, unsigned e, var_vector const & all_vars, upolynomial::manager & upm, upolynomial::numeral_vector const & f_u, upolynomial::zp_factors const & f_u_zp_factors, var2degree & target_ideal, zp_factors & f_zpe_factors) { SASSERT(f_u_zp_factors.distinct_factors() > 1); TRACE("polynomial::factorization::multivariate", tout << "polynomial::multivariate_hensel_lift(" << endl; tout << "f = " << f << "," << endl; tout << "x = x" << x << "," << endl; tout << "e = " << e << "," << endl; tout << "f_u = "; upm.display(tout, f_u); tout << "," << endl; tout << "f_u_zp_factors" << f_u_zp_factors << "," << endl; tout << "target_ideal = " << target_ideal << "," << endl; tout << "f_zpe_factors = " << f_zpe_factors << ")" << endl; ); // managers and all numeral_manager & nm = pm.m(); upolynomial::zp_manager & zp_upm = f_u_zp_factors.upm(); // upolynomial::zp_numeral_manager & zp_nm = zp_upm.m(); upolynomial::zp_numeral_manager & zpe_nm = zpe_pm.m(); upolynomial::zp_manager zpe_upm(zpe_nm); // the targed product we want (mod x_i^d_i, mod p^e) zp_polynomial_ref f_target_zpe(zpe_pm); f_target_zpe = convert(pm, f, zpe_pm); f_target_zpe = zpe_pm.mod_d(f_target_zpe, target_ideal); TRACE("polynomial::factorization::multivariate", tout << "target_ideal = " << target_ideal << endl; tout << "f_target_zpe = " << f_target_zpe << endl; ); // we do the product by doing individual lifting like in the univarate case zp_polynomial_ref B(zp_pm), C_p(zp_pm); zp_polynomial_ref A_lifted(zpe_pm), B_lifted(zpe_pm); upolynomial::scoped_numeral_vector B_u(nm), C_u(nm), tmp_u(nm); upolynomial::scoped_numeral_vector U(nm), V(nm); for (int i = 0, i_end = f_u_zp_factors.distinct_factors() - 1; i < i_end; ++ i) { // get the univarate ones to lift now upolynomial::numeral_vector const & A_u = f_u_zp_factors[i]; // current starting product is f_target_zpe(0, x) in *Z_p* zp_upm.to_numeral_vector(f_target_zpe, x, C_u); // we get the rest into B (mod p) zp_upm.exact_div(C_u, A_u, B_u); TRACE("polynomial::factorization::multivariate", tout << "p = " << nm.to_string(zp_upm.m().p()) << endl; tout << "f_target_zpe = " << f_target_zpe << endl; tout << "A_u = "; upm.display(tout, A_u); tout << endl; tout << "B_u = "; upm.display(tout, B_u); tout << endl; tout << "C_u = "; upm.display(tout, C_u); tout << endl; ); // and get the U, V, such that A*U+B*V = 1 zp_upm.ext_gcd(A_u, B_u, U, V, tmp_u); TRACE("polynomial::factorization::multivariate", tout << "U = "; upm.display(tout, U); tout << endl; tout << "V = "; upm.display(tout, V); tout << endl; tout << "gcd = "; upm.display(tout, tmp_u); tout << endl; ); // do the lifting for this pair multivariate_hensel_lift(pm, zp_pm, zpe_pm, f_target_zpe, x, e, all_vars, zp_upm, U, A_u, V, B_u, target_ideal, A_lifted, B_lifted); // add the lifted A to the output f_zpe_factors.push_back(A_lifted, 1); // move to the new target by dividing with the lifted A f_target_zpe = zpe_pm.exact_div(f_target_zpe, A_lifted); } // add the last f_target f_zpe_factors.push_back(f_target_zpe, 1); } class mfactorization_combination_iterator : public upolynomial::factorization_combination_iterator_base { /** main variable */ var m_x; public: mfactorization_combination_iterator(zp_factors const & factors, var x) : upolynomial::factorization_combination_iterator_base(factors) {} /** \brief Filter the ones not in the degree set. */ bool filter_current() const { return false; } /** \brief Returns the degree of the current selection. */ unsigned current_degree() const { unsigned degree = 0; zp_manager & pm = m_factors.pm(); for (unsigned i = 0; i < left_size(); ++ i) { degree += pm.degree(m_factors[m_current[i]], m_x); } return degree; } void left(zp_polynomial_ref & out) const { SASSERT(m_current_size > 0); zp_manager & zp_pm = m_factors.pm(); out = m_factors[m_current[0]]; for (int i = 1; i < m_current_size; ++ i) { out = zp_pm.mul(out, m_factors[m_current[i]]); } } void get_left_tail_coeff(numeral const & m, numeral & out) { zp_manager & zp_pm = m_factors.pm(); upolynomial::zp_numeral_manager & zp_nm = zp_pm.m(); zp_nm.set(out, m); for (int i = 0; i < m_current_size; ++ i) { zp_nm.mul(out, zp_pm.numeral_tc(m_factors[m_current[i]]), out); } } void get_right_tail_coeff(numeral const & m, numeral & out) { zp_manager & zp_pm = m_factors.pm(); upolynomial::zp_numeral_manager & zp_nm = zp_pm.m(); zp_nm.set(out, m); unsigned current = 0; unsigned selection_i = 0; // selection is ordered, so we just take the ones in between that are not disable while (current < m_factors.distinct_factors()) { if (!m_enabled[current]) { // by skipping the disabled we never skip a selected one current ++; } else { if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) { SASSERT(m_factors.get_degree(current) == 1); zp_nm.mul(out, zp_pm.numeral_tc(m_factors[current]), out); current ++; } else { current ++; selection_i ++; } } } } void right(zp_polynomial_ref & out) const { SASSERT(m_current_size > 0); zp_manager & zp_pm = m_factors.pm(); upolynomial::zp_numeral_manager & zp_nm = zp_pm.m(); unsigned current = 0; unsigned selection_i = 0; numeral one; zp_nm.set(one, 1); out = zp_pm.mk_const(one); // selection is ordered, so we just take the ones in between that are not disable while (current < m_factors.distinct_factors()) { if (!m_enabled[current]) { // by skipping the disabled we never skip a selected one current ++; } else { if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) { SASSERT(m_factors.get_degree(current) == 1); out = zp_pm.mul(out, m_factors[current]); current ++; } else { current ++; selection_i ++; } } } } }; // the multivariate factorization bool factor_square_free_primitive(polynomial_ref const & f, factors & f_factors) { TRACE("polynomial::factorization", tout << "polynomial::factor_square_free_primitive(f = " << f << ", factors = " << f_factors << ")" << endl;); manager & pm = f.m(); numeral_manager & nm = pm.m(); // to start with, maybe this should be part of input var x = pm.max_var(f); // get all the variables var_vector vars, vars_no_x; pm.vars(f, vars); for(unsigned i = 0; i < vars.size(); ++ i) { if (vars[i] != x) { vars_no_x.push_back(vars[i]); } } SASSERT(vars.size() > 1); // degree of the main variable unsigned x_degree = pm.degree(f, x); // the leading coefficient polynomial_ref f_lc(pm); f_lc = pm.coeff(f, x, x_degree); // the vector of values we substitute upolynomial::scoped_numeral_vector a(nm); // the univariate polynomial upolynomial::manager upm(nm); upolynomial::scoped_numeral_vector f_u(upm); // generate the values to substitute and substitute them to get f_u(x) = f(a, x), the univariate version of f unsigned size = 1; a.resize(vars_no_x.size()); for (unsigned i = 0; i < a.size(); ++ i) { nm.reset(a[i]); } generate_substitution_values(f, x, f_lc, vars_no_x, size, a, upm, f_u); TRACE("polynomial::factorization::multivariate", tout << "f_u = "; upm.display(tout, f_u); tout << endl; tout << "substitution:" << endl; for (unsigned i = 0; i < vars_no_x.size(); ++ i) { tout << "x" << vars[i] << " -> " << nm.to_string(a[i]) << endl; }); // the primitive part of f_u scoped_numeral f_u_lc(nm); upolynomial::scoped_numeral_vector f_u_pp(nm); upm.get_primitive_and_content(f_u, f_u_pp, f_u_lc); TRACE("polynomial::factorization::multivariate", tout << "f_u_lc" << nm.to_string(f_u_lc) << endl; tout << "f_u_pp = "; upm.display(tout, f_u_pp); tout << endl; ); // factor the univariate one upolynomial::factors factors_u(upm); upolynomial::factor_square_free(upm, f_u, factors_u); TRACE("polynomial::factorization::multivariate", tout << "factors_u = " << factors_u << endl; ); // if there is no univariate factors, it's irreducible if (factors_u.distinct_factors() == 1) { f_factors.push_back(f, 1); return false; } // translate f with a, so that we work modulo x_i^k and not (x_i - a_i)^k polynomial_ref f_t(pm); // Do the translation, we must have that a[x] = 0 pm.translate(f, vars_no_x, a, f_t); TRACE("polynomial::factorization::multivariate", tout << "f_t = " << f_t << endl; ); // the zp manager stuff, we'll be changing the base upolynomial::zp_numeral_manager zp_nm(nm, 2); upolynomial::zp_manager zp_upm(zp_nm); // get the first prime number p that keeps f square-free scoped_numeral p(nm); prime_iterator prime_it; upolynomial::scoped_numeral_vector f_u_pp_zp(nm); do { // create Z_p with the next prime nm.set(p, prime_it.next()); // translate to Zp[x] zp_nm.set_p(p); upolynomial::to_zp_manager(zp_upm, f_u_pp, f_u_pp_zp); } while (!zp_upm.is_square_free(f_u_pp_zp)); TRACE("polynomial::factorization::multivariate", tout << "p = " << p << endl; tout << "f_t = " << f_t << endl; ); // convert factors of f to factors modulo p (monic ones) upolynomial::zp_factors factors_u_zp(zp_upm); upolynomial::scoped_numeral_vector current_factor(nm); for (unsigned i = 0; i < factors_u.distinct_factors(); ++ i) { upolynomial::to_zp_manager(zp_upm, factors_u[i], current_factor); zp_upm.mk_monic(current_factor); factors_u_zp.push_back_swap(current_factor, 1); } TRACE("polynomial::factorization::multivariate", tout << "factors_u_zp = " << factors_u_zp << endl; ); // compute factor coefficient bound (pover p^e) of the translated f with the leading coefficient added, i.e. // f_t*lc(f_t, x) = f_t*lc(f, x) unsigned e; scoped_numeral p_e(nm); var2degree target_ideal; upolynomial::scoped_numeral f_t_lc(nm); nm.set(f_t_lc, pm.numeral_lc(f_t, x)); polynomial_ref f_t_with_lc(pm); f_t_with_lc = pm.mul(f_t_lc, f_t); multivariate_factor_coefficient_bound(f_t_with_lc, x, p, e, p_e, target_ideal); TRACE("polynomial::factorization::multivariate", tout << "target_ideal = " << target_ideal << endl; ); // do the multivariate lifting of the translated one f_t upolynomial::zp_numeral_manager zpe_nm(nm, p_e); zp_manager zpe_pm(zpe_nm, &pm.mm()); zp_manager zp_pm(zp_nm, &pm.mm()); zp_factors factors_zpe(zpe_pm); multivariate_hensel_lift(pm, zp_pm, zpe_pm, f_t, x, e, vars, upm, f_u_pp_zp, factors_u_zp, target_ideal, factors_zpe); TRACE("polynomial::factorization::multivariate", tout << "factors_zpe = " << factors_zpe << endl; ); // try the factors from the lifted combinations factors f_t_factors(pm); bool remove = false; mfactorization_combination_iterator it(factors_zpe, x); unsigned max_degre = pm.degree(f_t, x) / 2; zp_polynomial_ref zpe_trial_factor(zpe_pm); while (it.next(remove)) { // // our bound ensures we can extract the right factors of degree at most 1/2 of the original // so, if out trial factor has degree bigger than 1/2, we need to take the rest of the factors // but, if we take the rest and it works, it doesn't meen that the rest is factorized, so we still take out // the original factor // bool using_left = it.current_degree() <= max_degre; if (using_left) { // do a quick check first it.left(zpe_trial_factor); } else { // do a quick check first it.right(zpe_trial_factor); } // add the lc(f_pp) to the trial divisor zpe_trial_factor = zpe_pm.mul(f_t_lc, zpe_trial_factor); polynomial_ref trial_factor(pm), trial_factor_quo(pm); trial_factor = convert(zpe_pm, zpe_trial_factor, pm); bool true_factor = true; trial_factor_quo = pm.exact_div(f_t_with_lc, trial_factor); // if division is precise we have a factor if (true_factor) { if (!using_left) { // as noted above, we still use the original factor trial_factor.swap(trial_factor_quo); } // We need to get the content out of the factor upolynomial::scoped_numeral trial_factor_cont(nm); pm.int_content(f_t, trial_factor_cont); trial_factor = pm.exact_div(trial_factor, trial_factor_cont); // add the factor f_t_factors.push_back(trial_factor, 1); // we continue with the int-primitive quotient (with the lc added back) // but we also have to keep lc(f_t)*f_t pm.int_content(trial_factor_quo, f_t_lc); // content trial_factor_quo = pm.exact_div(trial_factor_quo, f_t_lc); nm.set(f_t_lc, pm.numeral_lc(trial_factor_quo, x)); f_t = pm.mul(f_t_lc, trial_factor_quo); // but we also remove it from the iterator remove = true; } else { // don't remove this combination remove = false; } } // translate the factor back for (unsigned i = 0; i < a.size(); ++ i) { nm.neg(a[i]); } for (unsigned i = 0; i < f_t_factors.distinct_factors(); ++ i) { polynomial_ref factor(pm); pm.translate(f_t_factors[i], vars_no_x, a, factor); f_factors.push_back(factor, 1); } TRACE("polynomial::factorization", tout << "polynomial::factor_square_free_primitive(f = " << f << ") => " << f_factors << endl;); return true; } }; // end polynomial namespace #endif z3-z3-4.4.1/src/math/polynomial/polynomial_factorization.h000066400000000000000000000034401260446376700236210ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial_factorization.h Abstract: Methods for factoring polynomials. Author: Dejan (t-dejanj) 2011-11-29 Notes: [1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal, 46(8-10):1853-1859, 1967. [2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third edition, 1997. [3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993. --*/ #pragma once #if 0 // Disabled for reorg #include "polynomial.h" #include "upolynomial.h" #include "bit_vector.h" #include "z3_exception.h" namespace polynomial { /** \brief Something to throw when we are in trouble. */ class factorization_exception : public default_exception { public: factorization_exception(char const * msg) : default_exception(msg) {} }; /** \brief Factor the polynomial f from Z[x1, ..., x_n]. Returns the index of the last factor that is completely factored. I.e., if the method returns m, then f_1, ..., f_m are true irreducible factors, and the rest might be further reducible. */ unsigned factor(polynomial_ref & f, factors & factors); /** \brief Factor the square-free primitive polynomial f from Z[x1, ..., x_n]. Returns true if the factorization was sucesseful, i.e. it was completed and the result is complete. Otherwise the quarantee is that the all but the last factor are irreducible. */ bool factor_square_free_primitive(polynomial_ref const & f, factors & factors); } inline std::ostream & operator<<(std::ostream & out, polynomial::factors & factors) { factors.display(out); return out; } #endif z3-z3-4.4.1/src/math/polynomial/polynomial_primes.h000066400000000000000000000063471260446376700222550ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: polynomial_primes.h Abstract: Some prime numbers for modular computations. Author: Leonardo (leonardo) 2011-12-21 Notes: --*/ #ifndef POLYNOMIAL_PRIMES_H_ #define POLYNOMIAL_PRIMES_H_ namespace polynomial { #define NUM_SMALL_PRIMES 11 const unsigned g_small_primes[NUM_SMALL_PRIMES] = { 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53 }; #if 0 #define NUM_BIG_PRIMES 77 const unsigned g_big_primes[NUM_BIG_PRIMES] = { 16777259, 16777289, 16777291, 16777331, 16777333, 16777337, 16777381, 16777421, 16777441, 16777447, 16777469, 16777499, 16777507, 16777531, 16777571, 16777577, 16777597, 16777601, 16777619, 16777633, 16777639, 16777643, 16777669, 16777679, 16777681, 16777699, 16777711, 16777721, 16777723, 16777729, 16777751, 16777777, 16777781, 16777807, 16777811, 16777823, 16777829, 16777837, 16777853, 16777903, 16777907, 16777949, 16777967, 16777973, 16777987, 16777991, 16778009, 16778023, 16778071, 16778077, 16778089, 16778123, 16778129, 16778137, 16778147, 16778173, 16778227, 16778231, 16778291, 16778309, 16778357, 16778383, 16778401, 16778413, 16778429, 16778441, 16778449, 16778453, 16778497, 16778521, 16778537, 16778543, 16778549, 16778561, 16778603, 16778623, 16778627 }; #else #define NUM_BIG_PRIMES 231 const unsigned g_big_primes[NUM_BIG_PRIMES] = { 39103, 39107, 39113, 39119, 39133, 39139, 39157, 39161, 39163, 39181, 39191, 39199, 39209, 39217, 39227, 39229, 39233, 39239, 39241, 39251, 39293, 39301, 39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397, 39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521, 39541, 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667, 39671, 39679, 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779, 39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869, 39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, 39979, 39983, 39989, 40009, 40013, 40031, 40037, 40039, 40063, 40087, 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, 40163, 40169, 40177, 40189, 40193, 40213, 40231, 40237, 40241, 40253, 40277, 40283, 40289, 40343, 40351, 40357, 40361, 40387, 40423, 40427, 40429, 40433, 40459, 40471, 40483, 40487, 40493, 40499, 40507, 40519, 40529, 40531, 40543, 40559, 40577, 40583, 40591, 40597, 40609, 40627, 40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771, 40787, 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867, 40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, 40993, 41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117, 41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203, 41213, 41221, 41227, 41231, 41233, 41243, 41257, 41263, 41269, 41281, 41299, 41333, 41341, 41351, 41357, 41381, 41387, 41389, 41399, 41411, 41413, 41443, 41453, 41467, 41479, 41491, 41507, 41513, 41519, 41521, 41539, 41543, 41549 }; #endif }; #endif z3-z3-4.4.1/src/math/polynomial/polynomial_var2value.h000066400000000000000000000024611260446376700226560ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: polynomial_var2value.h Abstract: Simple implementations of the var2value interface. Author: Leonardo (leonardo) 2012-01-05 Notes: --*/ #ifndef POLYNOMIAL_VAR2VALUE_H_ #define POLYNOMIAL_VAR2VALUE_H_ #include"polynomial.h" #include"scoped_numeral_vector.h" namespace polynomial { // default implementation used for debugging purposes template class simple_var2value : public var2value { var_vector m_xs; _scoped_numeral_vector m_vs; public: simple_var2value(ValManager & m):m_vs(m) {} void push_back(var x, typename ValManager::numeral const & v) { m_xs.push_back(x); m_vs.push_back(v); } virtual ValManager & m() const { return m_vs.m(); } virtual bool contains(var x) const { return std::find(m_xs.begin(), m_xs.end(), x) != m_xs.end(); } virtual typename ValManager::numeral const & operator()(var x) const { for (unsigned i = 0; i < m_xs.size(); i++) if (m_xs[i] == x) return m_vs[i]; UNREACHABLE(); static typename ValManager::numeral zero; return zero; } }; }; #endif z3-z3-4.4.1/src/math/polynomial/rpolynomial.cpp000066400000000000000000000665151260446376700214160ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: rpolynomial.cpp Abstract: Goodies for creating and handling polynomials in dense recursive representation. Author: Leonardo (leonardo) 2012-06-11 Notes: --*/ #include"rpolynomial.h" #include"tptr.h" #include"buffer.h" namespace rpolynomial { typedef void poly_or_num; inline bool is_poly(poly_or_num * p) { return GET_TAG(p) == 0; } inline bool is_num(poly_or_num * p) { return GET_TAG(p) == 1; } polynomial * to_poly(poly_or_num * p) { SASSERT(is_poly(p)); return UNTAG(polynomial*, p); } manager::numeral * to_num_ptr(poly_or_num * p) { SASSERT(is_num(p)); return (UNTAG(manager::numeral *, p)); } manager::numeral & to_num(poly_or_num * p) { return *to_num_ptr(p); } poly_or_num * to_poly_or_num(polynomial * p) { return TAG(poly_or_num*, p, 0); } poly_or_num * to_poly_or_num(manager::numeral * n) { return TAG(poly_or_num*, n, 1); } class polynomial { friend class manager; friend struct manager::imp; unsigned m_ref_count; var m_var; unsigned m_size; poly_or_num * m_args[0]; public: 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--; } static unsigned get_obj_size(unsigned n) { return sizeof(polynomial) + n*sizeof(void*); } var max_var() const { return m_var; } unsigned size() const { return m_size; } poly_or_num * arg(unsigned i) const { SASSERT(i < size()); return m_args[i]; } }; struct manager::imp { manager & m_wrapper; numeral_manager & m_manager; small_object_allocator * m_allocator; bool m_own_allocator; volatile bool m_cancel; imp(manager & w, numeral_manager & m, small_object_allocator * a): m_wrapper(w), m_manager(m), m_allocator(a), m_own_allocator(a == 0) { if (a == 0) m_allocator = alloc(small_object_allocator, "rpolynomial"); m_cancel = false; } ~imp() { if (m_own_allocator) dealloc(m_allocator); } // Remark: recursive calls should be fine since I do not expect to have polynomials with more than 100 variables. manager & pm() const { return m_wrapper; } numeral * mk_numeral() { void * mem = m_allocator->allocate(sizeof(numeral)); return new (mem) numeral(); } void del_numeral(numeral * n) { m_manager.del(*n); m_allocator->deallocate(sizeof(numeral), n); } static void inc_ref(polynomial * p) { if (p) p->inc_ref(); } static void inc_ref(poly_or_num * p) { if (p && is_poly(p)) inc_ref(to_poly(p)); } void del_poly(polynomial * p) { SASSERT(p != 0); ptr_buffer todo; todo.push_back(p); while (!todo.empty()) { p = todo.back(); todo.pop_back(); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = p->arg(i); if (pn == 0) continue; if (is_num(pn)) { del_numeral(to_num_ptr(pn)); } else { SASSERT(is_poly(p)); polynomial * p_arg = to_poly(p); p_arg->dec_ref(); if (p_arg->ref_count() == 0) { todo.push_back(p_arg); } } } unsigned obj_sz = polynomial::get_obj_size(sz); m_allocator->deallocate(obj_sz, p); } } void dec_ref(polynomial * p) { if (p) { p->dec_ref(); if (p->ref_count() == 0) del_poly(p); } } void dec_ref(poly_or_num * p) { if (p && is_poly(p)) dec_ref(to_poly(p)); } static bool is_const(polynomial const * p) { SASSERT(p == 0 || (p->max_var() == null_var) == (p->size() == 1 && p->arg(0) != 0 && is_num(p->arg(0)))); return p == 0 || p->max_var() == null_var; } bool is_zero(polynomial const * p) { return p == 0; } static bool is_univariate(polynomial const * p) { if (is_const(p)) return false; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = p->arg(i); if (pn == 0) continue; if (is_poly(pn)) return false; } return true; } bool is_monomial(polynomial const * p) { if (is_const(p)) return true; unsigned sz = p->size(); SASSERT(sz > 0); SASSERT(p->arg(sz - 1) != 0); for (unsigned i = 0; i < sz - 1; i++) { if (p->arg(i) != 0) return false; } SASSERT(is_poly(p->arg(sz - 1))); return is_monomial(to_poly(p->arg(sz-1))); } unsigned degree(polynomial const * p) { SASSERT(p != 0); SASSERT(p->size() > 0); return p == 0 ? 0 : p->size() - 1; } bool eq(polynomial const * p1, polynomial const * p2) { if (p1 == p2) return true; if (p1 == 0 || p2 == 0) return false; if (p1->size() != p2->size()) return false; if (p1->max_var() != p2->max_var()) return false; unsigned sz = p1->size(); for (unsigned i = 0; i < sz; i++) { poly_or_num * pn1 = p1->arg(i); poly_or_num * pn2 = p2->arg(i); if (pn1 == 0 && pn2 == 0) continue; if (pn1 == 0 || pn2 == 0) return false; if (is_num(pn1) && is_num(pn2)) { if (!m_manager.eq(to_num(pn1), to_num(pn2))) return false; } else if (is_poly(pn1) && is_poly(pn2)) { if (!eq(to_poly(pn1), to_poly(pn2))) return false; } else { return false; } } return true; } void inc_ref_args(unsigned sz, poly_or_num * const * args) { for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = args[i]; if (pn == 0 || is_num(pn)) continue; inc_ref(to_poly(pn)); } } void dec_ref_args(unsigned sz, poly_or_num * const * args) { for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = args[i]; if (pn == 0 || is_num(pn)) continue; dec_ref(to_poly(pn)); } } unsigned trim(unsigned sz, poly_or_num * const * args) { while (sz > 0) { if (args[sz - 1] != 0) return sz; sz--; } UNREACHABLE(); return UINT_MAX; } polynomial * allocate_poly(unsigned sz, poly_or_num * const * args, var max_var) { SASSERT(sz > 0); SASSERT((max_var == null_var) == (sz == 1 && is_num(args[0]))); unsigned obj_sz = polynomial::get_obj_size(sz); void * mem = m_allocator->allocate(obj_sz); polynomial * new_pol = new (mem) polynomial(); new_pol->m_ref_count = 0; new_pol->m_var = max_var; new_pol->m_size = sz; for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = args[i]; if (is_poly(pn)) { inc_ref(to_poly(pn)); new_pol->m_args[i] = pn; SASSERT(max_var == null_var || to_poly(pn)->max_var() < max_var); } else { SASSERT(!m_manager.is_zero(to_num(pn))); new_pol->m_args[i] = pn; } } return new_pol; } poly_or_num * mk_poly_core(unsigned sz, poly_or_num * const * args, var max_var) { sz = trim(sz, args); SASSERT(sz > 0); if (sz == 1) { poly_or_num * pn0 = args[0]; SASSERT(!is_num(pn0) || !m_manager.is_zero(to_num(pn0))); return pn0; } SASSERT((max_var == null_var) == (sz == 1 && is_num(args[0]))); SASSERT(sz > 1); return to_poly_or_num(allocate_poly(sz, args, max_var)); } polynomial * mk_poly(unsigned sz, poly_or_num * const * args, var max_var) { poly_or_num * _p = mk_poly_core(sz, args, max_var); if (_p == 0) return 0; else if (is_num(_p)) return allocate_poly(1, &_p, null_var); else return to_poly(_p); } polynomial * mk_const(numeral const & n) { if (m_manager.is_zero(n)) return 0; numeral * a = mk_numeral(); m_manager.set(*a, n); poly_or_num * _a = to_poly_or_num(a); return allocate_poly(1, &_a, null_var); } polynomial * mk_const(rational const & a) { SASSERT(a.is_int()); scoped_numeral tmp(m_manager); m_manager.set(tmp, a.to_mpq().numerator()); return mk_const(tmp); } polynomial * mk_polynomial(var x, unsigned k) { SASSERT(x != null_var); if (k == 0) { numeral one; m_manager.set(one, 1); return mk_const(one); } ptr_buffer new_args; for (unsigned i = 0; i < k; i++) new_args.push_back(0); numeral * new_arg = mk_numeral(); m_manager.set(*new_arg, 1); new_args.push_back(to_poly_or_num(new_arg)); return mk_poly(new_args.size(), new_args.c_ptr(), x); } poly_or_num * unpack(polynomial const * p) { if (p == 0) { return 0; } else if (is_const(p)) { SASSERT(p->size() == 1); SASSERT(p->max_var() == null_var); return p->arg(0); } else { return to_poly_or_num(const_cast(p)); } } polynomial * pack(poly_or_num * p) { if (p == 0) return 0; else if (is_num(p)) return mk_poly(1, &p, null_var); else return to_poly(p); } poly_or_num * mul_core(numeral const & c, poly_or_num * p) { if (m_manager.is_zero(c) || p == 0) { return 0; } else if (is_num(p)) { numeral * r = mk_numeral(); m_manager.mul(c, to_num(p), *r); return to_poly_or_num(r); } else { polynomial * _p = to_poly(p); unsigned sz = _p->size(); SASSERT(sz > 1); ptr_buffer new_args; for (unsigned i = 0; i < sz; i++) { new_args.push_back(mul_core(c, _p->arg(i))); } return mk_poly_core(new_args.size(), new_args.c_ptr(), _p->max_var()); } } polynomial * mul(numeral const & c, polynomial const * p) { return pack(mul_core(c, unpack(p))); } polynomial * neg(polynomial const * p) { numeral minus_one; m_manager.set(minus_one, -1); return pack(mul_core(minus_one, unpack(p))); } poly_or_num * add_core(numeral const & c, poly_or_num * p) { if (m_manager.is_zero(c)) { return p; } else if (p == 0) { numeral * r = mk_numeral(); m_manager.set(*r, c); return to_poly_or_num(r); } else if (is_num(p)) { numeral a; m_manager.add(c, to_num(p), a); if (m_manager.is_zero(a)) return 0; numeral * new_arg = mk_numeral(); m_manager.swap(*new_arg, a); return to_poly_or_num(new_arg); } else { polynomial * _p = to_poly(p); unsigned sz = _p->size(); SASSERT(sz > 1); ptr_buffer new_args; new_args.push_back(add_core(c, _p->arg(0))); for (unsigned i = 1; i < sz; i++) new_args.push_back(_p->arg(1)); return mk_poly_core(new_args.size(), new_args.c_ptr(), _p->max_var()); } } polynomial * add(numeral const & c, polynomial const * p) { return pack(add_core(c, unpack(p))); } #if 0 polynomial * add_lt(polynomial const * p1, polynomial const * p2) { // Add non-constant polynomials p1 and p2 when max_var(p1) < max_var(p2) SASSERT(p1->max_var() != null_var); SASSERT(p2->max_var() != null_var); SASSERT(p1->max_var() < p2->max_var()); unsigned sz = p2->size(); ptr_buffer new_args; poly_or_num * pn0 = p2->arg(0); if (pn0 == 0) { new_args.push_back(to_poly_or_num(const_cast(p1))); } else if (is_num(pn0)) { SASSERT(!is_const(p1)); polynomial * new_arg = add(to_num(pn0), p1); SASSERT(!is_zero(new_arg)); SASSERT(!is_const(new_arg)); new_args.push_back(to_poly_or_num(new_arg)); } else { SASSERT(is_poly(pn0)); polynomial * new_arg = add(p1, to_poly(pn0)); new_args.push_back(to_poly_or_num(new_arg)); } for (unsigned i = 1; i < sz; i++) new_args.push_back(p2->arg(i)); return mk_poly(sz, new_args.c_ptr(), p2->max_var()); } polynomial * add(polynomial const * p1, polynomial const * p2) { if (is_zero(p1)) return const_cast(p2); if (is_zero(p2)) return const_cast(p1); var x1 = p1->max_var(); var x2 = p2->max_var(); if (x1 == null_var) { SASSERT(is_const(p1)); return add(to_num(p1->arg(0)), p2); } if (x2 == null_var) { SASSERT(is_const(p2)); return add(to_num(p2->arg(0)), p1); } if (x1 < x2) return add_lt(p1, p2); if (x2 < x1) return add_lt(p2, p1); SASSERT(x1 == x2); unsigned sz1 = p1->size(); unsigned sz2 = p2->size(); unsigned msz = std::min(sz1, sz2); ptr_buffer new_args; for (unsigned i = 0; i < msz; i++) { poly_or_num * pn1 = p1->arg(i); poly_or_num * pn2 = p2->arg(i); if (pn1 == 0) { new_args.push_back(pn2); continue; } if (pn2 == 0) { new_args.push_back(pn1); continue; } SASSERT(pn1 != 0 && pn2 != 0); if (is_num(pn1)) { if (is_num(pn2)) { SASSERT(is_num(pn1) && is_num(pn2)); numeral a; m_manager.add(to_num(pn1), to_num(pn2), a); if (m_manager.is_zero(a)) { new_args.push_back(0); } else { numeral * new_arg = mk_numeral(); m_manager.swap(*new_arg, a); new_args.push_back(to_poly_or_num(new_arg)); } } else { SASSERT(is_num(pn1) && is_poly(pn2)); new_args.push_back(to_poly_or_num(add(to_num(pn1), to_poly(pn2)))); } } else { if (is_num(pn2)) { SASSERT(is_poly(pn1) && is_num(pn2)); new_args.push_back(to_poly_or_num(add(to_num(pn2), to_poly(pn1)))); } else { SASSERT(is_poly(pn1) && is_poly(pn2)); new_args.push_back(to_poly_or_num(add(to_poly(pn1), to_poly(pn2)))); } } } SASSERT(new_args.size() == sz1 || new_args.size() == sz2); for (unsigned i = msz; i < sz1; i++) { new_args.push_back(p1->arg(i)); } for (unsigned i = msz; i < sz2; i++) { new_args.push_back(p2->arg(i)); } SASSERT(new_args.size() == std::max(sz1, sz2)); return mk_poly(new_args.size(), new_args.c_ptr(), x1); } class resetter_mul_buffer; friend class resetter_mul_buffer; class resetter_mul_buffer { imp & m_owner; ptr_buffer m_buffer; public: resetter_mul_buffer(imp & o, ptr_buffer & b):m_owner(o), m_buffer(b) {} ~resetter_mul_buffer() { m_owner.dec_ref_args(m_buffer.size(), m_buffer.c_ptr()); m_buffer.reset(); } }; void acc_mul_xk(ptr_buffer & mul_buffer, unsigned k, polynomial * p) { if (mul_buffer[k] == 0) { mul_buffer[k] = to_poly_or_num(p); inc_ref(p); } else { polynomial * new_p; if (is_num(mul_buffer[k])) new_p = add(to_num(mul_buffer.get(k)), p); else new_p = add(p, to_poly(mul_buffer.get(k))); if (is_zero(new_p)) { dec_ref(mul_buffer[k]); mul_buffer[k] = 0; } else { inc_ref(new_p); dec_ref(mul_buffer[k]); mul_buffer[k] = to_poly_or_num(new_p); } } } void acc_mul_xk(ptr_buffer & mul_buffer, unsigned k, numeral & a) { if (mul_buffer.get(k) == 0) { numeral * new_arg = mk_numeral(); m_manager.swap(*new_arg, a); mul_buffer[k] = to_poly_or_num(new_arg); } else { if (is_num(mul_buffer[k])) { m_manager.add(to_num(mul_buffer[k]), a, to_num(mul_buffer[k])); if (m_manager.is_zero(to_num(mul_buffer[k]))) { del_numeral(to_num_ptr(mul_buffer[k])); mul_buffer[k] = 0; } } else { polynomial * new_p = add(a, to_poly(mul_buffer.get(k))); if (is_zero(new_p)) { dec_ref(mul_buffer[k]); mul_buffer[k] = 0; } else { inc_ref(new_p); dec_ref(mul_buffer[k]); mul_buffer[k] = to_poly_or_num(new_p); } } } } polynomial * mul_lt(polynomial const * p1, polynomial const * p2) { unsigned sz2 = p2->size(); // TODO return 0; } polynomial * mul(polynomial const * p1, polynomial const * p2) { var x1 = p1->max_var(); var x2 = p2->max_var(); if (x1 == null_var) { SASSERT(is_const(p1)); return mul(to_num(p1->arg(0)), p2); } if (x2 == null_var) { SASSERT(is_const(p2)); return mul(to_num(p2->arg(0)), p1); } if (x1 < x2) return mul_lt(p1, p2); if (x2 < x1) return mul_lt(p2, p1); SASSERT(x1 == x2); if (degree(p1) < degree(p2)) std::swap(p1, p2); unsigned sz = degree(p1) * degree(p2) + 1; ptr_buffer mul_buffer; resetter_mul_buffer resetter(*this, mul_buffer); mul_buffer.resize(sz); unsigned sz1 = p1->size(); unsigned sz2 = p2->size(); for (unsigned i1 = 0; i1 < sz1; i1++) { poly_or_num * pn1 = p1->arg(i1); if (pn1 == 0) continue; for (unsigned i2 = 0; i2 < sz2; i2++) { poly_or_num * pn2 = p2->arg(i2); if (pn2 == 0) continue; unsigned i = i1+i2; if (is_num(pn1)) { if (is_num(pn2)) { SASSERT(is_num(pn1) && is_num(pn2)); scoped_numeral a(m_manager); m_manager.mul(to_num(pn1), to_num(pn2), a); acc_mul_xk(mul_buffer, i, a); } else { SASSERT(is_num(pn1) && is_poly(pn2)); polynomial_ref p(pm()); p = mul(to_num(pn1), to_poly(pn2)); acc_mul_xk(mul_buffer, i, p); } } else { if (is_num(pn2)) { SASSERT(is_poly(pn1) && is_num(pn2)); polynomial_ref p(pm()); p = mul(to_num(pn2), to_poly(pn1)); acc_mul_xk(mul_buffer, i, p); } else { SASSERT(is_poly(pn1) && is_poly(pn2)); polynomial_ref p(pm()); p = mul(to_poly(pn2), to_poly(pn1)); acc_mul_xk(mul_buffer, i, p); } } } } return mk_poly(mul_buffer.size(), mul_buffer.c_ptr(), x1); } #endif void display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) { var x = p->max_var(); bool first = true; unsigned i = p->size(); while (i > 0) { --i; poly_or_num * pn = p->arg(i); if (pn == 0) continue; if (first) first = false; else out << " + "; if (is_num(pn)) { numeral & a = to_num(pn); if (i == 0) { m_manager.display(out, a); } else { if (m_manager.is_one(a)) { proc(out, x); if (i > 1) out << "^" << i; } else { m_manager.display(out, a); if (use_star) out << "*"; else out << " "; proc(out, x); if (i > 1) out << "^" << i; } } } else { SASSERT(is_poly(pn)); if (i == 0) { display(out, to_poly(pn), proc, use_star); } else { bool add_paren = false; if (i > 0) add_paren = !is_monomial(to_poly(pn)); if (add_paren) out << "("; display(out, to_poly(pn), proc, use_star); if (add_paren) out << ")"; if (i > 0) { if (use_star) out << "*"; else out << " "; proc(out, x); if (i > 1) out << "^" << i; } } } } } }; manager:: manager(numeral_manager & m, small_object_allocator * a) { m_imp = alloc(imp, *this, m, a); } manager::~manager() { dealloc(m_imp); } bool manager::is_zero(polynomial const * p) { return p == 0; } #if 0 bool manager::is_const(polynomial const * p) { return imp::is_const(p); } bool manager::is_univariate(polynomial const * p) { return imp::is_univariate(p); } bool manager::is_monomial(polynomial const * p) const { return m_imp->is_monomial(p); } bool manager::eq(polynomial const * p1, polynomial const * p2) { return m_imp->eq(p1, p2); } polynomial * manager::mk_zero() { return m_imp->mk_zero(); } polynomial * manager::mk_const(numeral const & r) { return m_imp->mk_const(r); } polynomial * manager::mk_const(rational const & a) { return m_imp->mk_const(a); } polynomial * manager::mk_polynomial(var x, unsigned k) { return m_imp->mk_polynomial(x, k); } polynomial * manager::mul(numeral const & r, polynomial const * p) { return m_imp->mul(r, p); } polynomial * manager::neg(polynomial const * p) { return m_imp->neg(p); } polynomial * manager::add(numeral const & r, polynomial const * p) { return m_imp->add(r, p); } polynomial * manager::add(polynomial const * p1, polynomial const * p2) { return m_imp->add(p1, p2); } var manager::max_var(polynomial const * p) { return p->max_var(); } unsigned manager::size(polynomial const * p) { return p->size(); } void manager::display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) const { return m_imp->display(out, p, proc, use_star); } #endif }; z3-z3-4.4.1/src/math/polynomial/rpolynomial.h000066400000000000000000000134431260446376700210530ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: rpolynomial.h Abstract: Goodies for creating and handling polynomials in dense recursive representation. Author: Leonardo (leonardo) 2012-06-11 Notes: --*/ #ifndef RPOLYNOMIAL_H_ #define RPOLYNOMIAL_H_ #include"mpz.h" #include"rational.h" #include"obj_ref.h" #include"ref_vector.h" #include"z3_exception.h" #include"polynomial.h" namespace rpolynomial { typedef polynomial::var var; const var null_var = polynomial::null_var; typedef polynomial::var_vector var_vector; typedef polynomial::display_var_proc display_var_proc; typedef polynomial::polynomial som_polynomial; class polynomial; class manager; typedef obj_ref polynomial_ref; typedef ref_vector polynomial_ref_vector; typedef ptr_vector polynomial_vector; class manager { public: typedef unsynch_mpz_manager numeral_manager; typedef numeral_manager::numeral numeral; typedef svector numeral_vector; typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; struct imp; private: imp * m_imp; public: manager(numeral_manager & m, small_object_allocator * a = 0); ~manager(); numeral_manager & m() const; small_object_allocator & allocator() const; void set_cancel(bool f); /** \brief Create a new variable. */ var mk_var(); /** \brief Return the number of variables in the manager. */ unsigned num_vars() const; /** \brief Return true if x is a valid variable in this manager. */ bool is_valid(var x) const { return x < num_vars(); } /** \brief Increment reference counter. */ void inc_ref(polynomial * p); /** \brief Decrement reference counter. */ void dec_ref(polynomial * p); /** \brief Return true if \c p is the zero polynomial. */ bool is_zero(polynomial const * p); /** \brief Return true if p1 == p2. */ bool eq(polynomial const * p1, polynomial const * p2); /** \brief Return true if \c p is the constant polynomial. */ static bool is_const(polynomial const * p); /** \brief Return true if \c p is an univariate polynomial. */ static bool is_univariate(polynomial const * p); /** \brief Return true if \c p is a monomial. */ bool is_monomial(polynomial const * p) const; /** \brief Return the maximal variable occurring in p. Return null_var if p is a constant polynomial. */ static var max_var(polynomial const * p); /** \brief Return the size of the polynomail p. It is the degree(p) on max_var(p) + 1. */ static unsigned size(polynomial const * p); /** \brief Return a polynomial h that is the coefficient of max_var(p)^k in p. if p does not contain any monomial containing max_var(p)^k, then return 0. */ polynomial * coeff(polynomial const * p, unsigned k); /** \brief Create the zero polynomial. */ polynomial * mk_zero(); /** \brief Create the constant polynomial \c r. \warning r is a number managed by the numeral_manager in the polynomial manager \warning r is reset. */ polynomial * mk_const(numeral const & r); /** \brief Create the constant polynomial \c r. \pre r must be an integer */ polynomial * mk_const(rational const & r); /** \brief Create the polynomial x^k */ polynomial * mk_polynomial(var x, unsigned k = 1); polynomial * mul(numeral const & r, polynomial const * p); polynomial * neg(polynomial const * p); polynomial * add(numeral const & r, polynomial const * p); polynomial * add(int c, polynomial const * p); polynomial * add(polynomial const * p1, polynomial const * p2); /** \brief Convert the given polynomial in sum-of-monomials form into a polynomial in dense recursive form. */ polynomial * translate(som_polynomial const * p); void display(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc(), bool use_star = false) const; void display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc()) const; friend std::ostream & operator<<(std::ostream & out, polynomial_ref const & p) { p.m().display(out, p); return out; } }; }; typedef rpolynomial::polynomial_ref rpolynomial_ref; typedef rpolynomial::polynomial_ref_vector rpolynomial_ref_vector; inline rpolynomial_ref neg(rpolynomial_ref const & p) { rpolynomial::manager & m = p.m(); return rpolynomial_ref(m.neg(p), m); } inline rpolynomial_ref operator-(rpolynomial_ref const & p) { rpolynomial::manager & m = p.m(); return rpolynomial_ref(m.neg(p), m); } inline rpolynomial_ref operator+(int a, rpolynomial_ref const & p) { rpolynomial::manager & m = p.m(); return rpolynomial_ref(m.add(a, p), m); } inline rpolynomial_ref operator+(rpolynomial_ref const & p, int a) { rpolynomial::manager & m = p.m(); return rpolynomial_ref(m.add(a, p), m); } #endif z3-z3-4.4.1/src/math/polynomial/sexpr2upolynomial.cpp000066400000000000000000000104641260446376700225550ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sexpr2upolynomial.cpp Abstract: sexpr to upolynomial converter Author: Leonardo (leonardo) 2011-12-28 Notes: --*/ #include"sexpr2upolynomial.h" #include"sexpr.h" sexpr2upolynomial_exception::sexpr2upolynomial_exception(char const * msg, sexpr const * s): cmd_exception(msg, s->get_line(), s->get_pos()) { } #define MAX_POLYNOMIAL_DEPTH 1 << 16 // Simple recursive parser void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p, unsigned depth) { if (depth > MAX_POLYNOMIAL_DEPTH) throw sexpr2upolynomial_exception("invalid univariate polynomial, too complex", s); if (s->is_composite()) { unsigned num = s->get_num_children(); if (num == 0) throw sexpr2upolynomial_exception("invalid univariate polynomial, symbol expected", s); sexpr * h = s->get_child(0); if (!h->is_symbol()) throw sexpr2upolynomial_exception("invalid univariate polynomial, symbol expected", s); symbol op = h->get_symbol(); if (op == "+") { if (num <= 1) throw sexpr2upolynomial_exception("invalid univariate polynomial, '+' operator expects at least one argument", s); sexpr2upolynomial(m, s->get_child(1), p, depth+1); upolynomial::scoped_numeral_vector arg(m); for (unsigned i = 2; i < num; i++) { m.reset(arg); sexpr2upolynomial(m, s->get_child(i), arg, depth+1); m.add(arg.size(), arg.c_ptr(), p.size(), p.c_ptr(), p); } } else if (op == "-") { if (num <= 1) throw sexpr2upolynomial_exception("invalid univariate polynomial, '-' operator expects at least one argument", s); sexpr2upolynomial(m, s->get_child(1), p, depth+1); if (num == 2) { m.neg(p); return; } upolynomial::scoped_numeral_vector arg(m); for (unsigned i = 2; i < num; i++) { m.reset(arg); sexpr2upolynomial(m, s->get_child(i), arg, depth+1); m.sub(p.size(), p.c_ptr(), arg.size(), arg.c_ptr(), p); } } else if (op == "*") { if (num <= 1) throw sexpr2upolynomial_exception("invalid univariate polynomial, '*' operator expects at least one argument", s); sexpr2upolynomial(m, s->get_child(1), p, depth+1); upolynomial::scoped_numeral_vector arg(m); for (unsigned i = 2; i < num; i++) { m.reset(arg); sexpr2upolynomial(m, s->get_child(i), arg, depth+1); m.mul(arg.size(), arg.c_ptr(), p.size(), p.c_ptr(), p); } } else if (op == "^") { if (num != 3) throw sexpr2upolynomial_exception("invalid univariate polynomial, '^' operator expects two arguments", s); sexpr2upolynomial(m, s->get_child(1), p, depth+1); sexpr * arg2 = s->get_child(2); if (!arg2->is_numeral() || !arg2->get_numeral().is_unsigned()) throw sexpr2upolynomial_exception("invalid univariate polynomial, exponent must be an unsigned integer", arg2); unsigned k = arg2->get_numeral().get_unsigned(); m.pw(p.size(), p.c_ptr(), k, p); } else { throw sexpr2upolynomial_exception("invalid univariate polynomial, '+', '-', '^' or '*' expected", s); } } else if (s->is_numeral()) { // make constant polynomial rational a = s->get_numeral(); if (!a.is_int()) throw sexpr2upolynomial_exception("invalid univariate polynomial, integer coefficient expected", s); m.set(1, &a, p); } else if (s->is_symbol()) { if (s->get_symbol() != "x") throw sexpr2upolynomial_exception("invalid univariate polynomial, variable 'x' expected", s); // make identity rational as[2] = { rational(0), rational(1) }; m.set(2, as, p); } else { throw sexpr2upolynomial_exception("invalid univariate polynomial, unexpected ", s); } } void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p) { sexpr2upolynomial(m, s, p, 0); } z3-z3-4.4.1/src/math/polynomial/sexpr2upolynomial.h000066400000000000000000000010711260446376700222140ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sexpr2upolynomial.h Abstract: sexpr to upolynomial converter Author: Leonardo (leonardo) 2011-12-28 Notes: --*/ #ifndef SEXPR2UPOLYNOMIAL_H_ #define SEXPR2UPOLYNOMIAL_H_ #include"upolynomial.h" #include"cmd_context_types.h" class sexpr; class sexpr2upolynomial_exception : public cmd_exception { public: sexpr2upolynomial_exception(char const * msg, sexpr const * s); }; void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p); #endif z3-z3-4.4.1/src/math/polynomial/upolynomial.cpp000066400000000000000000003447311260446376700214200ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: upolynomial.cpp Abstract: Goodies for creating and handling univariate polynomials. A dense representation is much better for Root isolation algorithms, encoding algebraic numbers, factorization, etc. We also use integers as the coefficients of univariate polynomials. Author: Leonardo (leonardo) 2011-11-29 Notes: --*/ #include"upolynomial.h" #include"upolynomial_factorization.h" #include"polynomial_primes.h" #include"buffer.h" #include"cooperate.h" namespace upolynomial { core_manager::factors::factors(core_manager & upm): m_upm(upm), m_total_factors(0), m_total_degree(0) { nm().set(m_constant, 1); } core_manager::factors::~factors() { clear(); nm().del(m_constant); } void core_manager::factors::clear() { for (unsigned i = 0; i < m_factors.size(); ++ i) { m_upm.reset(m_factors[i]); } m_factors.reset(); m_degrees.reset(); nm().set(m_constant, 1); m_total_factors = 0; m_total_degree = 0; } void core_manager::factors::push_back(numeral_vector const & p, unsigned degree) { SASSERT(degree > 0); m_factors.push_back(numeral_vector()); m_degrees.push_back(degree); m_upm.set(p.size(), p.c_ptr(), m_factors.back()); m_total_factors += degree; m_total_degree += m_upm.degree(p)*degree; } void core_manager::factors::push_back_swap(numeral_vector & p, unsigned degree) { SASSERT(degree > 0); m_factors.push_back(numeral_vector()); m_degrees.push_back(degree); p.swap(m_factors.back()); m_total_factors += degree; m_total_degree += m_upm.degree(p)*degree; } void core_manager::factors::multiply(numeral_vector & out) const { // set the first one to be just the constant m_upm.reset(out); if (nm().is_zero(m_constant)) { SASSERT(m_factors.empty()); return; } out.push_back(numeral()); nm().set(out.back(), m_constant); // now multiply them all in for (unsigned i = 0; i < m_factors.size(); ++ i) { if (m_degrees[i] > 1) { numeral_vector power; m_upm.pw(m_factors[i].size(), m_factors[i].c_ptr(), m_degrees[i], power); m_upm.mul(out.size(), out.c_ptr(), power.size(), power.c_ptr(), out); m_upm.reset(power); } else { m_upm.mul(out.size(), out.c_ptr(), m_factors[i].size(), m_factors[i].c_ptr(), out); } } } void core_manager::factors::display(std::ostream & out) const { out << nm().to_string(m_constant); if (m_factors.size() > 0) { for (unsigned i = 0; i < m_factors.size(); ++ i) { out << " * ("; m_upm.display(out, m_factors[i]); out << ")^" << m_degrees[i]; } } } numeral_manager & core_manager::factors::nm() const { return m_upm.m(); } void core_manager::factors::set_constant(numeral const & constant) { nm().set(m_constant, constant); } void core_manager::factors::set_degree(unsigned i, unsigned degree) { m_total_degree -= m_upm.degree(m_factors[i])*m_degrees[i]; m_total_factors -= m_degrees[i]; m_degrees[i] = degree; m_total_factors += degree; m_total_degree += m_upm.degree(m_factors[i])*degree; } void core_manager::factors::swap_factor(unsigned i, numeral_vector & p) { m_total_degree -= m_upm.degree(m_factors[i])*m_degrees[i]; m_total_degree += m_upm.degree(p)*m_degrees[i]; m_factors[i].swap(p); } void core_manager::factors::swap(factors & other) { m_factors.swap(other.m_factors); m_degrees.swap(other.m_degrees); nm().swap(m_constant, other.m_constant); std::swap(m_total_factors, other.m_total_factors); std::swap(m_total_degree, other.m_total_degree); } core_manager::core_manager(unsynch_mpz_manager & m): m_manager(m) { m_cancel = false; } core_manager::~core_manager() { reset(m_basic_tmp); reset(m_div_tmp1); reset(m_div_tmp2); reset(m_exact_div_tmp); reset(m_gcd_tmp1); reset(m_gcd_tmp2); reset(m_CRA_tmp); for (unsigned i = 0; i < UPOLYNOMIAL_MGCD_TMPS; i++) reset(m_mgcd_tmp[i]); reset(m_sqf_tmp1); reset(m_sqf_tmp2); reset(m_pw_tmp); } void core_manager::set_cancel(bool f) { m_cancel = f; } void core_manager::checkpoint() { if (m_cancel) throw upolynomial_exception("canceled"); cooperate("upolynomial"); } // Eliminate leading zeros from buffer. void core_manager::trim(numeral_vector & buffer) { unsigned sz = buffer.size(); while (sz > 0 && m().is_zero(buffer[sz - 1])) { m().del(buffer[sz - 1]); // 0 may be a big number when using GMP. sz--; } buffer.shrink(sz); } // Remove old entries from buffer [sz, buffer.size()), shrink size, and remove leading zeros. // buffer must have at least size sz. void core_manager::set_size(unsigned sz, numeral_vector & buffer) { unsigned old_sz = buffer.size(); SASSERT(old_sz >= sz); // delete old entries for (unsigned i = sz; i < old_sz; i++) { m().del(buffer[i]); } buffer.shrink(sz); trim(buffer); } // Set size to zero. void core_manager::reset(numeral_vector & buffer) { set_size(0, buffer); } // Copy elements from p to buffer. void core_manager::set(unsigned sz, numeral const * p, numeral_vector & buffer) { if (p != 0 && buffer.c_ptr() == p) { SASSERT(buffer.size() == sz); return; } buffer.reserve(sz); for (unsigned i = 0; i < sz; i++) { m().set(buffer[i], p[i]); } set_size(sz, buffer); } void core_manager::set(unsigned sz, rational const * p, numeral_vector & buffer) { buffer.reserve(sz); for (unsigned i = 0; i < sz; i++) { SASSERT(p[i].is_int()); m().set(buffer[i], p[i].to_mpq().numerator()); } set_size(sz, buffer); } void core_manager::get_primitive_and_content(unsigned f_sz, numeral const * f, numeral_vector & pp, numeral & cont) { SASSERT(f_sz > 0); m().gcd(f_sz, f, cont); SASSERT(m().is_pos(cont)); if (m().is_one(cont)) { set(f_sz, f, pp); } else { pp.reserve(f_sz); for (unsigned i = 0; i < f_sz; i++) { if (!m().is_zero(f[i])) { m().div(f[i], cont, pp[i]); } else { m().set(pp[i], 0); } } set_size(f_sz, pp); } } // Negate coefficients of p. void core_manager::neg(unsigned sz, numeral * p) { for (unsigned i = 0; i < sz; i++) { m().neg(p[i]); } } // buffer := -p void core_manager::neg_core(unsigned sz, numeral const * p, numeral_vector & buffer) { SASSERT(!is_alias(p, buffer)); buffer.reserve(sz); for (unsigned i = 0; i < sz; i++) { m().set(buffer[i], p[i]); m().neg(buffer[i]); } set_size(sz, buffer); } void core_manager::neg(unsigned sz, numeral const * p, numeral_vector & buffer) { neg_core(sz, p, m_basic_tmp); buffer.swap(m_basic_tmp); } // buffer := p1 + p2 void core_manager::add_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); unsigned min_sz = std::min(sz1, sz2); unsigned max_sz = std::max(sz1, sz2); unsigned i = 0; buffer.reserve(max_sz); for (; i < min_sz; i++) { m().add(p1[i], p2[i], buffer[i]); } for (; i < sz1; i++) { m().set(buffer[i], p1[i]); } for (; i < sz2; i++) { m().set(buffer[i], p2[i]); } set_size(max_sz, buffer); } void core_manager::add(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { add_core(sz1, p1, sz2, p2, m_basic_tmp); buffer.swap(m_basic_tmp); } // buffer := p1 - p2 void core_manager::sub_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); unsigned min_sz = std::min(sz1, sz2); unsigned max_sz = std::max(sz1, sz2); unsigned i = 0; buffer.reserve(max_sz); for (; i < min_sz; i++) { m().sub(p1[i], p2[i], buffer[i]); } for (; i < sz1; i++) { m().set(buffer[i], p1[i]); } for (; i < sz2; i++) { m().set(buffer[i], p2[i]); m().neg(buffer[i]); } set_size(max_sz, buffer); } void core_manager::sub(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { sub_core(sz1, p1, sz2, p2, m_basic_tmp); buffer.swap(m_basic_tmp); } // buffer := p1 * p2 void core_manager::mul_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); if (sz1 == 0) { reset(buffer); } else if (sz2 == 0) { reset(buffer); } else { unsigned new_sz = sz1 + sz2 - 1; buffer.reserve(new_sz); for (unsigned i = 0; i < new_sz; i++) { m().reset(buffer[i]); } if (sz1 < sz2) { std::swap(sz1, sz2); std::swap(p1, p2); } for (unsigned i = 0; i < sz1; i++) { checkpoint(); numeral const & a_i = p1[i]; if (m().is_zero(a_i)) continue; for (unsigned j = 0; j < sz2; j++) { numeral const & b_j = p2[j]; if (m().is_zero(b_j)) continue; m().addmul(buffer[i+j], a_i, b_j, buffer[i+j]); } } set_size(new_sz, buffer); } } void core_manager::mul(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { mul_core(sz1, p1, sz2, p2, m_basic_tmp); buffer.swap(m_basic_tmp); } // buffer := dp/dx void core_manager::derivative(unsigned sz, numeral const * p, numeral_vector & buffer) { if (sz <= 1) { reset(buffer); return; } buffer.reserve(sz - 1); for (unsigned i = 1; i < sz; i++) { numeral d; m().set(d, i); m().mul(p[i], d, buffer[i-1]); } set_size(sz-1, buffer); } // Divide coeffients of p by their GCD void core_manager::normalize(unsigned sz, numeral * p) { if (sz == 0) return; if (sz == 1) { if (m().is_pos(p[0])) m().set(p[0], 1); else m().set(p[0], -1); return; } scoped_numeral g(m()); m().gcd(sz, p, g); if (m().is_one(g)) return; for (unsigned i = 0; i < sz; i++) { #ifdef Z3DEBUG scoped_numeral old_p_i(m()); old_p_i = p[i]; #endif // Actual code m().div(p[i], g, p[i]); #ifdef Z3DEBUG scoped_numeral tmp(m()); m().mul(g, p[i], tmp); CTRACE("div_bug", !m().eq(tmp, old_p_i), tout << "old(p[i]): " << m().to_string(old_p_i) << ", g: " << m().to_string(g) << ", p[i]: " << m().to_string(p[i]) << ", tmp: " << m().to_string(tmp) << "\n";); SASSERT(tmp == old_p_i); #endif } } // Divide coeffients of p by their GCD void core_manager::normalize(numeral_vector & p) { normalize(p.size(), p.c_ptr()); } void core_manager::div(unsigned sz, numeral * p, numeral const & b) { SASSERT(!m().is_zero(b)); if (m().is_one(b)) return; for (unsigned i = 0; i < sz; i++) { CTRACE("upolynomial", !m().divides(b, p[i]), tout << "b: " << m().to_string(b) << ", p[i]: " << m().to_string(p[i]) << "\n";); SASSERT(m().divides(b, p[i])); m().div(p[i], b, p[i]); } } void core_manager::mul(unsigned sz, numeral * p, numeral const & b) { SASSERT(!m().is_zero(b)); if (m().is_one(b)) return; for (unsigned i = 0; i < sz; i++) { m().mul(p[i], b, p[i]); } } void core_manager::mul(numeral_vector & p, numeral const & b) { if (m().is_zero(b)) { reset(p); return; } mul(p.size(), p.c_ptr(), b); } // Pseudo division void core_manager::div_rem_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r) { SASSERT(!is_alias(p1, q)); SASSERT(!is_alias(p2, q)); SASSERT(!is_alias(p1, r)); SASSERT(!is_alias(p2, r)); d = 0; SASSERT(sz2 > 0); if (sz2 == 1) { set(sz1, p1, q); if (field()) { div(q, *p2); } reset(r); return; } reset(q); set(sz1, p1, r); if (sz1 <= 1) return; unsigned qsz; if (sz1 >= sz2) { q.reserve(sz1 - sz2 + 1); qsz = sz1 - sz2 + 1; } else { qsz = 0; } numeral const & b_n = p2[sz2-1]; SASSERT(!m().is_zero(b_n)); scoped_numeral a_m(m()); while (true) { checkpoint(); sz1 = r.size(); if (sz1 < sz2) { set_size(qsz, q); return; } unsigned m_n = sz1 - sz2; // m-n if (field()) { numeral & ratio = a_m; m().div(r[sz1 - 1], b_n, ratio); m().add(q[m_n], ratio, q[m_n]); for (unsigned i = 0; i < sz2 - 1; i++) { m().submul(r[i + m_n], ratio, p2[i], r[i + m_n]); } } else { d++; m().set(a_m, r[sz1 - 1]); for (unsigned i = 0; i < sz1 - 1; i++) { m().mul(r[i], b_n, r[i]); } for (unsigned i = 0; i < qsz; i++) { m().mul(q[i], b_n, q[i]); } m().add(q[m_n], a_m, q[m_n]); for (unsigned i = 0; i < sz2 - 1; i++) { m().submul(r[i + m_n], a_m, p2[i], r[i + m_n]); } } set_size(sz1 - 1, r); } } // Pseudo division void core_manager::div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r) { numeral_vector & _r = m_div_tmp1; numeral_vector & _q = m_div_tmp2; div_rem_core(sz1, p1, sz2, p2, d, _q, _r); r.swap(_r); q.swap(_q); } // Pseudo division void core_manager::div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q) { unsigned d; numeral_vector & _r = m_div_tmp1; numeral_vector & _q = m_div_tmp2; div_rem_core(sz1, p1, sz2, p2, d, _q, _r); reset(_r); q.swap(_q); } // Pseudo remainder void core_manager::rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); d = 0; SASSERT(sz2 > 0); if (sz2 == 1) { reset(buffer); return; } set(sz1, p1, buffer); if (sz1 <= 1) return; numeral const & b_n = p2[sz2-1]; SASSERT(!m().is_zero(b_n)); scoped_numeral a_m(m()); while (true) { checkpoint(); TRACE("rem_bug", tout << "rem loop, p2:\n"; display(tout, sz2, p2); tout << "\nbuffer:\n"; display(tout, buffer); tout << "\n";); sz1 = buffer.size(); if (sz1 < sz2) { TRACE("rem_bug", tout << "finished\n";); return; } unsigned m_n = sz1 - sz2; if (field()) { numeral & ratio = a_m; m().div(buffer[sz1 - 1], b_n, ratio); for (unsigned i = 0; i < sz2 - 1; i++) { m().submul(buffer[i + m_n], ratio, p2[i], buffer[i + m_n]); } } else { // buffer: a_m * x^m + a_{m-1} * x^{m-1} + ... + a_0 // p2: b_n * x^n + b_{n-1} * x^{n-1} + ... + b_0 d++; m().set(a_m, buffer[sz1 - 1]); TRACE("rem_bug", tout << "a_m: " << m().to_string(a_m) << ", b_n: " << m().to_string(b_n) << "\n";); // don't need to update position sz1 - 1, since it will become 0 for (unsigned i = 0; i < sz1 - 1; i++) { m().mul(buffer[i], b_n, buffer[i]); } // buffer: a_m * x^m + b_n * a_{m-1} * x^{m-1} + ... + b_n * a_0 // don't need to process i = sz2 - 1, because buffer[sz1 - 1] will become 0. for (unsigned i = 0; i < sz2 - 1; i++) { m().submul(buffer[i + m_n], a_m, p2[i], buffer[i + m_n]); } } set_size(sz1 - 1, buffer); } } // Signed pseudo remainder void core_manager::srem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); unsigned d; rem(sz1, p1, sz2, p2, d, buffer); // We don't ned to flip the sign if d is odd and leading coefficient of p2 is negative if (d % 2 == 0 || (sz2 > 0 && m().is_pos(p2[sz2-1]))) neg(buffer.size(), buffer.c_ptr()); } // Exact division for univariate polynomials. // Return false if p2 does not divide p1 bool core_manager::divides(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2) { if (sz2 == 0) return false; if (sz1 == 0) return true; if (sz2 > sz1 || !m().divides(p2[sz2-1], p1[sz1-1])) return false; scoped_numeral b(m()); numeral_vector & _p1 = m_div_tmp1; set(sz1, p1, _p1); while (true) { if (sz1 == 0) return true; if (sz2 > sz1 || !m().divides(p2[sz2-1], _p1[sz1-1])) return false; unsigned delta = sz1 - sz2; m().div(_p1[sz1-1], p2[sz2-1], b); for (unsigned i = 0; i < sz2 - 1; i++) { if (!m().is_zero(p2[i])) m().submul(_p1[i+delta], b, p2[i], _p1[i+delta]); } m().reset(_p1[sz1-1]); trim(_p1); sz1 = _p1.size(); } return true; } // Exact division for univariate polynomials. // Return false if p2 does not divide p1 bool core_manager::exact_div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r) { if (sz2 == 0) return false; if (sz1 == 0) { reset(r); return true; } if (sz2 > sz1 || !m().divides(p2[sz2-1], p1[sz1-1]) || !m().divides(p2[0], p1[0])) return false; numeral_vector & _r = m_exact_div_tmp; reset(_r); unsigned deg = sz1 - sz2; _r.reserve(deg+1); numeral_vector & _p1 = m_div_tmp1; // std::cerr << "dividing with "; display(std::cerr, _p1); std::cerr << std::endl; TRACE("factor_bug", tout << "sz1: " << sz1 << " p1: " << p1 << ", _p1.c_ptr(): " << _p1.c_ptr() << ", _p1.size(): " << _p1.size() << "\n";); set(sz1, p1, _p1); SASSERT(_p1.size() == sz1); while (true) { TRACE("upolynomial", tout << "exact_div loop...\n"; display(tout, _p1); tout << "\n"; display(tout, _r); tout << "\n";); // std::cerr << "dividing with "; display(std::cerr, _p1); std::cerr << std::endl; if (sz1 == 0) { set_size(deg+1, _r); r.swap(_r); return true; } if (sz2 > sz1 || !m().divides(p2[sz2-1], _p1[sz1-1]) || !m().divides(p2[0], _p1[0])) { reset(r); return false; } unsigned delta = sz1 - sz2; numeral & a_r = _r[delta]; m().div(_p1[sz1-1], p2[sz2-1], a_r); for (unsigned i = 0; i < sz2 - 1; i++) { if (!m().is_zero(p2[i])) m().submul(_p1[i+delta], a_r, p2[i], _p1[i+delta]); } m().reset(_p1[sz1-1]); trim(_p1); sz1 = _p1.size(); } return true; } void core_manager::flip_sign_if_lm_neg(numeral_vector & buffer) { unsigned sz = buffer.size(); if (sz == 0) return; if (m().is_neg(buffer[sz - 1])) { for (unsigned i = 0; i < sz; i++) m().neg(buffer[i]); } } void core_manager::CRA_combine_images(numeral_vector const & C1, numeral const & b1, numeral_vector & C2, numeral & b2) { SASSERT(!m().m().is_even(b1)); SASSERT(!m().m().is_even(b2)); numeral_vector & R = m_CRA_tmp; reset(R); scoped_numeral inv1(m()); scoped_numeral inv2(m()); scoped_numeral g(m()); m().gcd(b1, b2, inv1, inv2, g); SASSERT(m().is_one(g)); // b1*inv1 + b2.inv2 = 1 // inv1 is the inverse of b1 mod b2 // inv2 is the inverse of b2 mod b1 m().m().mod(inv1, b2, inv1); m().m().mod(inv2, b1, inv2); TRACE("CRA", tout << "inv1: " << inv1 << ", inv2: " << inv2 << "\n";); scoped_numeral a1(m()); scoped_numeral a2(m()); m().mul(b2, inv2, a1); // a1 is the multiplicator for coefficients of C1 m().mul(b1, inv1, a2); // a2 is the multiplicator for coefficients of C2 TRACE("CRA", tout << "a1: " << a1 << ", a2: " << a2 << "\n";); // new bound scoped_numeral new_bound(m()); m().mul(b1, b2, new_bound); scoped_numeral lower(m()); scoped_numeral upper(m()); scoped_numeral new_a(m()), tmp1(m()), tmp2(m()), tmp3(m()); m().div(new_bound, 2, upper); m().set(lower, upper); m().neg(lower); TRACE("CRA", tout << "lower: " << lower << ", upper: " << upper << "\n";); #define ADD(A1, A2) { \ m().mul(A1, a1, tmp1); \ m().mul(A2, a2, tmp2); \ m().add(tmp1, tmp2, tmp3); \ m().m().mod(tmp3, new_bound, new_a); \ if (m().gt(new_a, upper)) \ m().sub(new_a, new_bound, new_a); \ R.push_back(numeral()); \ m().set(R.back(), new_a); \ } numeral zero(0); unsigned i = 0; unsigned sz1 = C1.size(); unsigned sz2 = C2.size(); unsigned sz = std::min(sz1, sz2); for (; i < sz; i++) { ADD(C1[i], C2[i]); } for (; i < sz1; i++) { ADD(C1[i], zero); } for (; i < sz2; i++) { ADD(zero, C2[i]); } m().set(b2, new_bound); R.swap(C2); } void core_manager::mod_gcd(unsigned sz_u, numeral const * u, unsigned sz_v, numeral const * v, numeral_vector & result) { TRACE("mgcd", tout << "u: "; display_star(tout, sz_u, u); tout << "\nv: "; display_star(tout, sz_v, v); tout << "\n";); SASSERT(sz_u > 0 && sz_v > 0); SASSERT(!m().modular()); scoped_numeral c_u(m()), c_v(m()); numeral_vector & pp_u = m_mgcd_tmp[0]; numeral_vector & pp_v = m_mgcd_tmp[1]; get_primitive_and_content(sz_u, u, pp_u, c_u); get_primitive_and_content(sz_v, v, pp_v, c_v); scoped_numeral c_g(m()); m().gcd(c_u, c_v, c_g); unsigned d_u = sz_u - 1; unsigned d_v = sz_v - 1; numeral const & lc_u = pp_u[d_u]; numeral const & lc_v = pp_v[d_v]; scoped_numeral lc_g(m()); m().gcd(lc_u, lc_v, lc_g); scoped_numeral p(m()); scoped_numeral bound(m()); numeral_vector & u_Zp = m_mgcd_tmp[2]; numeral_vector & v_Zp = m_mgcd_tmp[3]; numeral_vector & q = m_mgcd_tmp[4]; numeral_vector & C = m_mgcd_tmp[5]; for (unsigned i = 0; i < NUM_BIG_PRIMES; i++) { m().set(p, polynomial::g_big_primes[i]); TRACE("mgcd", tout << "trying prime: " << p << "\n";); { scoped_set_zp setZp(*this, p); set(pp_u.size(), pp_u.c_ptr(), u_Zp); set(pp_v.size(), pp_v.c_ptr(), v_Zp); if (degree(u_Zp) < d_u) { TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";); continue; // bad prime } if (degree(v_Zp) < d_v) { TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";); continue; // bad prime } euclid_gcd(u_Zp.size(), u_Zp.c_ptr(), v_Zp.size(), v_Zp.c_ptr(), q); // normalize so that lc_g is leading coefficient of q mk_monic(q.size(), q.c_ptr()); scoped_numeral c(m()); m().set(c, lc_g); mul(q, c); } TRACE("mgcd", tout << "new q:\n"; display_star(tout, q); tout << "\n";); if (is_const(q)) { TRACE("mgcd", tout << "done, modular gcd is one\n";); reset(result); result.push_back(numeral()); m().set(result.back(), c_g); return; } if (i == 0) { set(q.size(), q.c_ptr(), C); m().set(bound, p); } else { if (q.size() < C.size()) { // discard accumulated image, it was affected by unlucky primes TRACE("mgcd", tout << "discarding image\n";); set(q.size(), q.c_ptr(), C); m().set(bound, p); } else { CRA_combine_images(q, p, C, bound); TRACE("mgcd", tout << "new combined:\n"; display_star(tout, C); tout << "\n";); } } numeral_vector & candidate = q; get_primitive(C, candidate); TRACE("mgcd", tout << "candidate:\n"; display_star(tout, candidate); tout << "\n";); SASSERT(candidate.size() > 0); numeral const & lc_candidate = candidate[candidate.size() - 1]; if (m().divides(lc_candidate, lc_g) && divides(pp_u, candidate) && divides(pp_v, candidate)) { TRACE("mgcd", tout << "found GCD\n";); mul(candidate, c_g); flip_sign_if_lm_neg(candidate); candidate.swap(result); TRACE("mgcd", tout << "r: "; display_star(tout, result); tout << "\n";); return; } } // Oops, modular GCD failed, not enough primes // fallback to euclid_gcd euclid_gcd(sz_u, u, sz_v, v, result); } void core_manager::euclid_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); if (sz1 == 0) { set(sz2, p2, buffer); flip_sign_if_lm_neg(buffer); return; } if (sz2 == 0) { set(sz1, p1, buffer); flip_sign_if_lm_neg(buffer); return; } bool is_field = field(); numeral_vector & A = m_gcd_tmp1; numeral_vector & B = m_gcd_tmp2; numeral_vector & R = buffer; set(sz1, p1, A); set(sz2, p2, B); TRACE("upolynomial", tout << "sz1: " << sz1 << ", p1: " << p1 << ", sz2: " << sz2 << ", p2: " << p2 << "\nB.size(): " << B.size() << ", B.c_ptr(): " << B.c_ptr() << "\n";); while (true) { TRACE("upolynomial", tout << "A: "; display(tout, A); tout <<"\nB: "; display(tout, B); tout << "\n";); if (B.empty()) { normalize(A); buffer.swap(A); // to be consistent, if in a field, we make gcd monic if (is_field) { mk_monic(buffer.size(), buffer.c_ptr()); } else { flip_sign_if_lm_neg(buffer); } TRACE("upolynomial", tout << "GCD\n"; display(tout, sz1, p1); tout << "\n"; display(tout, sz2, p2); tout << "\n--->\n"; display(tout, buffer); tout << "\n";); return; } rem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), R); normalize(R); A.swap(B); B.swap(R); } } void core_manager::gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); if (sz1 == 0) { set(sz2, p2, buffer); flip_sign_if_lm_neg(buffer); return; } if (sz2 == 0) { set(sz1, p1, buffer); flip_sign_if_lm_neg(buffer); return; } if (!modular()) mod_gcd(sz1, p1, sz2, p2, buffer); else euclid_gcd(sz1, p1, sz2, p2, buffer); } void core_manager::subresultant_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); if (sz1 == 0) { set(sz2, p2, buffer); flip_sign_if_lm_neg(buffer); return; } if (sz2 == 0) { set(sz1, p1, buffer); flip_sign_if_lm_neg(buffer); return; } numeral_vector & A = m_gcd_tmp1; numeral_vector & B = m_gcd_tmp2; numeral_vector & R = buffer; scoped_numeral g(m()); scoped_numeral h(m()); scoped_numeral aux(m()); m().set(g, 1); m().set(h, 1); unsigned d; set(sz1, p1, A); set(sz2, p2, B); if (A.size() < B.size()) A.swap(B); while (true) { SASSERT(A.size() >= B.size()); TRACE("upolynomial", tout << "A: "; display(tout, A); tout <<"\nB: "; display(tout, B); tout << "\n"; tout << "g: " << m().to_string(g) << ", h: " << m().to_string(h) << "\n";); if (B.empty()) { normalize(A); buffer.swap(A); // to be consistent, if in a field, we make gcd monic if (field()) { mk_monic(buffer.size(), buffer.c_ptr()); } else { flip_sign_if_lm_neg(buffer); } TRACE("upolynomial", tout << "subresultant GCD\n"; display(tout, sz1, p1); tout << "\n"; display(tout, sz2, p2); tout << "\n--->\n"; display(tout, buffer); tout << "\n";); return; } rem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), d, R); unsigned pseudo_div_d = A.size() - B.size(); if (d < pseudo_div_d + 1) { // I used a standard subresultant implementation. // TODO: investigate how to avoid the following adjustment. // // adjust R to make sure the following equation holds: // l(B)^{deg(A) - deg(B) + 1) * A = Q * B + R m().power(B[B.size() - 1], pseudo_div_d + 1 - d, aux); mul(R, aux); } d = pseudo_div_d; TRACE("upolynomial", tout << "R: "; display(tout, R); tout << "\nd: " << d << "\n";); // aux <- g*h^d m().power(h, d, aux); m().mul(g, aux, aux); div(R, aux); A.swap(B); B.swap(R); // g <- l(A) m().set(g, A[A.size() - 1]); m().power(g, d, aux); // h <- h^{1-d} * g^d if (d == 0) { // h <- h^1 * g^0 // do nothing } else if (d == 1) { // h <- h^0 * g^1 m().set(h, g); } else { // h <- (g^d)/(h^{d-1}) SASSERT(d > 1); d--; m().power(h, d, h); SASSERT(m().divides(h, aux)); m().div(aux, h, h); } } } // buffer := square-free(p) void core_manager::square_free(unsigned sz, numeral const * p, numeral_vector & buffer) { SASSERT(!is_alias(p, buffer)); if (sz <= 1) { set(sz, p, buffer); return; } numeral_vector & p_prime = m_sqf_tmp1; numeral_vector & g = m_sqf_tmp2; derivative(sz, p, p_prime); gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); // subresultant_gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); if (g.size() <= 1) { set(sz, p, buffer); } else { div(sz, p, g.size(), g.c_ptr(), buffer); normalize(buffer); } } bool core_manager::is_square_free(unsigned sz, numeral const * p) { if (sz <= 1) { return true; } numeral_vector & p_prime = m_sqf_tmp1; numeral_vector & g = m_sqf_tmp2; derivative(sz, p, p_prime); gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); return g.size() <= 1; } void core_manager::pw(unsigned sz, numeral const * p, unsigned k, numeral_vector & r) { if (k == 0) { SASSERT(sz != 0); r.reserve(1); m().set(r[0], 1); set_size(1, r); return; } if (k == 1 || sz == 0 || (sz == 1 && m().is_one(p[0]))) { set(sz, p, r); return; } numeral_vector & result = m_pw_tmp; set(sz, p, result); for (unsigned i = 1; i < k; i++) mul(m_pw_tmp.size(), m_pw_tmp.c_ptr(), sz, p, m_pw_tmp); r.swap(result); #if 0 unsigned mask = 1; numeral_vector & p2 = m_pw_tmp; set(sz, p, p2); reset(r); r.push_back(numeral(1)); while (mask <= k) { if (mask & k) { // r := r * p2 mul(r.size(), r.c_ptr(), p2.size(), p2.c_ptr(), r); } mul(p2.size(), p2.c_ptr(), p2.size(), p2.c_ptr(), p2); mask = mask << 1; } #endif } void core_manager::mk_monic(unsigned sz, numeral * p, numeral & lc, numeral & lc_inv) { m().set(lc, 1); m().set(lc_inv, 1); if (sz > 0 && !m().is_one(p[sz-1])) { int d = sz-1; m().swap(lc, p[d--]); m().set(lc_inv, lc); m().inv(lc_inv); for (; d >= 0; -- d) { m().mul(p[d], lc_inv, p[d]); } } } // Extended GCD void core_manager::ext_gcd(unsigned szA, numeral const * A, unsigned szB, numeral const * B, numeral_vector & U, numeral_vector & V, numeral_vector & D) { SASSERT(!is_alias(A, U)); SASSERT(!is_alias(A, V)); SASSERT(!is_alias(A, D)); SASSERT(!is_alias(B, U)); SASSERT(!is_alias(B, V)); SASSERT(!is_alias(B, D)); SASSERT(field()); scoped_numeral_vector V1(m()), V3(m()), Q(m()), R(m()), T(m()), V1Q(m()); // since we are in a field define gcd(A, B) to be monic // if AU + BV = D and D is not monic we make it monic, and then divide U and V by the same inverse // initialization // U <- 1 reset(U); U.push_back(numeral()); m().set(U.back(), 1); // D <- A set(szA, A, D); mk_monic(szA, D.c_ptr()); // V1 <- 0 reset(V1); // V3 <- B set(szB, B, V3); while (true) { if (V3.empty()) { // V3 is the zero polynomial numeral_vector & AU = V1; numeral_vector & D_AU = V3; // V <- (D - AU)/B mul(szA, A, U.size(), U.c_ptr(), AU); sub(D.size(), D.c_ptr(), AU.size(), AU.c_ptr(), D_AU); div(D_AU.size(), D_AU.c_ptr(), szB, B, V); DEBUG_CODE({ scoped_numeral_vector BV(m()); scoped_numeral_vector expected_D(m()); mul(szB, B, V.size(), V.c_ptr(), BV); add(AU.size(), AU.c_ptr(), BV.size(), BV.c_ptr(), expected_D); SASSERT(eq(expected_D, D)); }); // if D is not monic, make it monic scoped_numeral lc_inv(m()), lc(m()); mk_monic(D.size(), D.c_ptr(), lc, lc_inv); mul(U, lc_inv); mul(V, lc_inv); return; } // D = QV3 + R div_rem(D.size(), D.c_ptr(), V3.size(), V3.c_ptr(), Q, R); // T <- U - V1Q mul(V1.size(), V1.c_ptr(), Q.size(), Q.c_ptr(), V1Q); sub(U.size(), U.c_ptr(), V1Q.size(), V1Q.c_ptr(), T); // U <- V1 U.swap(V1); // D <- V3 D.swap(V3); // V1 <- T V1.swap(T); // V3 <- R V3.swap(R); } } // Display p void core_manager::display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name, bool use_star) const { bool displayed = false; unsigned i = sz; scoped_numeral a(m()); while (i > 0) { --i; if (!m().is_zero(p[i])) { m().set(a, p[i]); if (displayed) { m().abs(a); if (m().is_pos(p[i])) out << " + "; else out << " - "; } displayed = true; if (i == 0) { out << m().to_string(a); } else { SASSERT(i > 0); if (!m().is_one(a)) { out << m().to_string(a); if (use_star) out << "*"; else out << " "; } out << var_name; if (i > 1) out << "^" << i; } } } if (!displayed) out << "0"; } static void display_smt2_mumeral(std::ostream & out, numeral_manager & m, mpz const & n) { if (m.is_neg(n)) { out << "(- "; mpz abs_n; m.set(abs_n, n); m.neg(abs_n); m.display(out, abs_n); m.del(abs_n); out << ")"; } else { m.display(out, n); } } static void display_smt2_monomial(std::ostream & out, numeral_manager & m, mpz const & n, unsigned k, char const * var_name) { if (k == 0) { display_smt2_mumeral(out, m, n); } else if (m.is_one(n)) { if (k == 1) out << var_name; else out << "(^ " << var_name << " " << k << ")"; } else { out << "(* "; display_smt2_mumeral(out, m, n); out << " "; if (k == 1) out << var_name; else out << "(^ " << var_name << " " << k << ")"; out << ")"; } } // Display p as an s-expression void core_manager::display_smt2(std::ostream & out, unsigned sz, numeral const * p, char const * var_name) const { if (sz == 0) { out << "0"; return; } if (sz == 1) { display_smt2_mumeral(out, m(), p[0]); return; } unsigned non_zero_idx = UINT_MAX; unsigned num_non_zeros = 0; for (unsigned i = 0; i < sz; i++) { if (m().is_zero(p[i])) continue; non_zero_idx = i; num_non_zeros ++; } if (num_non_zeros == 1) { SASSERT(non_zero_idx != UINT_MAX && non_zero_idx >= 1); display_smt2_monomial(out, m(), p[non_zero_idx], non_zero_idx, var_name); } out << "(+"; unsigned i = sz; while (i > 0) { --i; if (!m().is_zero(p[i])) { out << " "; display_smt2_monomial(out, m(), p[i], i, var_name); } } out << ")"; } bool core_manager::eq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2) { if (sz1 != sz2) return false; for (unsigned i = 0; i < sz1; i++) { if (!m().eq(p1[i], p2[i])) return false; } return true; } void upolynomial_sequence::push(unsigned sz, numeral * p) { m_begins.push_back(m_seq_coeffs.size()); m_szs.push_back(sz); for (unsigned i = 0; i < sz; i++) { m_seq_coeffs.push_back(numeral()); swap(m_seq_coeffs.back(), p[i]); } } void upolynomial_sequence::push(numeral_manager & m, unsigned sz, numeral const * p) { m_begins.push_back(m_seq_coeffs.size()); m_szs.push_back(sz); for (unsigned i = 0; i < sz; i++) { m_seq_coeffs.push_back(numeral()); m.set(m_seq_coeffs.back(), p[i]); } } scoped_numeral_vector::scoped_numeral_vector(manager & m): _scoped_numeral_vector(m.m()) { } scoped_upolynomial_sequence::~scoped_upolynomial_sequence() { m_manager.reset(*this); } manager::~manager() { reset(m_db_tmp); reset(m_dbab_tmp1); reset(m_dbab_tmp2); reset(m_tr_tmp); reset(m_push_tmp); } void manager::reset(upolynomial_sequence & seq) { reset(seq.m_seq_coeffs); seq.m_szs.reset(); seq.m_begins.reset(); } // Remove zero roots from p void manager::remove_zero_roots(unsigned sz, numeral const * p, numeral_vector & buffer) { SASSERT(sz > 0); if (!m().is_zero(p[0])) { // zero is not a root of p set(sz, p, buffer); return; } unsigned i = 0; while (true) { // p must not be the zero polynomial SASSERT(i < sz); if (!m().is_zero(p[i])) break; i++; } unsigned new_sz = sz - i; buffer.reserve(new_sz); for (unsigned j = 0; j < new_sz; j++) { m().set(buffer[j], p[j + i]); } set_size(new_sz, buffer); } // Evaluate the sign of p(1/2) bool manager::has_one_half_root(unsigned sz, numeral const * p) { // Actually, given b = c/2^k, we compute the sign of 2^n*p(1/2) // Original Horner Sequence // ((a_n * 1/2 + a_{n-1})*1/2 + a_{n-2})*1/2 + a_{n-3} ... // Variation of the Horner Sequence for 2^n*p(1/2) // a_n + a_{n-1}*2 + a_{n-2}*(2^2) + a_{n-3}*(2^3) ... if (sz == 0) return true; if (sz == 1) return false; unsigned k = 1; scoped_numeral r(m()); scoped_numeral ak(m()); m().set(r, p[sz-1]); unsigned i = sz - 1; while (i > 0) { --i; numeral const & a = p[i]; m().mul2k(a, k, ak); m().add(r, ak, r); k++; } return m().is_zero(r); } // Remove 1/2 root from p void manager::remove_one_half_root(unsigned sz, numeral const * p, numeral_vector & buffer) { SASSERT(has_one_half_root(sz, p)); numeral two_x_1[2] = { numeral(-1), numeral(2) }; div(sz, p, 2, two_x_1, buffer); } int manager::sign_of(numeral const & c) { if (m().is_zero(c)) return 0; if (m().is_pos(c)) return 1; else return -1; } // Return the number of sign changes in the coefficients of p unsigned manager::sign_changes(unsigned sz, numeral const * p) { unsigned r = 0; int sign, prev_sign; sign = 0; prev_sign = 0; unsigned i = 0; for (; i < sz; i++) { sign = sign_of(p[i]); if (sign == 0) continue; if (sign != prev_sign && prev_sign != 0) r++; prev_sign = sign; } return r; } // Return the descartes bound for (0, +oo) unsigned manager::descartes_bound(unsigned sz, numeral const * p) { return sign_changes(sz, p); } // Return the descartes bound for the number of roots in the interval (0, 1) unsigned manager::descartes_bound_0_1(unsigned sz, numeral const * p) { if (sz <= 1) return 0; numeral_vector & Q = m_db_tmp; set(sz, p, Q); #if 0 // slow version unsigned n = Q.size() - 1; unsigned i; for (unsigned i = 1; i <= n; i++) { for (unsigned k = i; k >= 1; k--) { m().add(Q[k], Q[k-1], Q[k]); } } return sign_changes(Q.size(), Q.c_ptr()); #endif int prev_sign = 0; unsigned num_vars = 0; // a0 a1 a2 a3 // a0 a0+a1 a0+a1+a2 a0+a1+a2+a3 // a0 2a0+a1 3a0+2a1+a2 // a0 3a0+a1 // a0 for (unsigned i = 0; i < sz; i++) { checkpoint(); unsigned k; for (k = 1; k < sz - i; k++) { m().add(Q[k], Q[k-1], Q[k]); } int sign = sign_of(Q[k-1]); if (sign == 0) continue; if (sign != prev_sign && prev_sign != 0) { num_vars++; if (num_vars > 1) return num_vars; } prev_sign = sign; } return num_vars; } // Return the descartes bound for the number of roots in the interval (a, b) unsigned manager::descartes_bound_a_b(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq const & a, mpbq const & b) { if (bqm.is_nonneg(a)) { // Basic idea: apply descartes_bound_0_1 to p2(x) where // p1(x) = p(x+a) // p2(x) = p1((b-a)*x) TRACE("upolynomial", tout << "pos interval... " << bqm.to_string(a) << ", " << bqm.to_string(b) << "\n"; display(tout, sz, p); tout << "\n";); numeral_vector & p_aux = m_dbab_tmp1; translate_bq(sz, p, a, p_aux); TRACE("upolynomial", tout << "after translation\n"; display(tout, p_aux); tout << "\n";); scoped_mpbq b_a(bqm); bqm.sub(b, a, b_a); compose_p_b_x(p_aux.size(), p_aux.c_ptr(), b_a); TRACE("upolynomial", tout << "after composition: " << bqm.to_string(b_a) << "\n"; display(tout, p_aux); tout << "\n";); unsigned result = descartes_bound_0_1(p_aux.size(), p_aux.c_ptr()); return result; } else if (bqm.is_nonpos(b)) { // Basic idea: apply descartes_bound_a_b to p(-x) with intervals (-b, -a) numeral_vector & p_aux = m_dbab_tmp2; set(sz, p, p_aux); p_minus_x(p_aux.size(), p_aux.c_ptr()); scoped_mpbq mb(bqm); scoped_mpbq ma(bqm); bqm.set(mb, b); bqm.neg(mb); bqm.set(ma, a); bqm.neg(ma); unsigned result = descartes_bound_a_b(p_aux.size(), p_aux.c_ptr(), bqm, mb, ma); return result; } else if (!has_zero_roots(sz, p)) { mpbq zero(0); unsigned r1 = descartes_bound_a_b(sz, p, bqm, a, zero); if (r1 > 1) return r1; // if p has more than 1 root in (a, zero), then we don't even evaluate (zero, b) unsigned r2 = descartes_bound_a_b(sz, p, bqm, zero, b); if (r1 == 0) return r2; if (r2 == 0) return r1; return 2; // p has more than one root in (a, b) } else { // zero is a root of p mpbq zero(0); if (descartes_bound_a_b(sz, p, bqm, a, zero) > 0) return 2; // p has more than one root in (a, b) if (descartes_bound_a_b(sz, p, bqm, zero, b) > 0) return 2; // p has more than one root in (a, b) return 1; // zero is the only root in (a, b) } } // p(x) := p(x+1) void manager::translate(unsigned sz, numeral * p) { if (sz <= 1) return; unsigned n = sz - 1; for (unsigned i = 1; i <= n; i++) { checkpoint(); for (unsigned k = n-i; k <= n-1; k++) m().add(p[k], p[k+1], p[k]); } } // p(x) := p(x+2^k) void manager::translate_k(unsigned sz, numeral * p, unsigned k) { if (sz <= 1) return; scoped_numeral aux(m()); unsigned n = sz - 1; for (unsigned i = 1; i <= n; i++) { checkpoint(); for (unsigned k = n-i; k <= n-1; k++) { m().mul2k(p[k+1], k, aux); m().add(p[k], aux, p[k]); } } } // p(x) := p(x+c) void manager::translate_z(unsigned sz, numeral * p, numeral const & c) { if (sz <= 1) return; unsigned n = sz - 1; for (unsigned i = 1; i <= n; i++) { checkpoint(); for (unsigned k = n-i; k <= n-1; k++) m().addmul(p[k], c, p[k+1], p[k]); } } // Given b of the form c/(2^k) // p(x) := (2^k)^n * p(x + c/(2^k)) where b = c/2^k // // Given p: a_n * x^n + ... + a_0 // // buffer := a_n * (2^k * x + c)^n + a_{n-1} * 2^k * (2^k * x + c)^{n-1} + ... + a_1 * (2^k)^{n-1} * (2^k * x + c) + a_0 * (2^k)^n // // We perform the transformation in two steps: // 1) a_n * x^n + a_{n-1} * 2^k * x^{n-1} + ... + a_1 * (2^k)^{n-1} + a_0 * (2^k)^n // Let d_n, ..., d_0 be the coefficients after this step // Then, we have // d_n * x^n + ... + d_0 // 2) d_n * (2^k * x + c)^n + ... + d_0 // This step is like the translation with integers, but the coefficients must be adjusted by 2^k in each iteration. // // That is, it is a special translation such as: // a_n*(b*x + c)^n + a_{n-1}*(b*x + c)^{n-1} + ... + a_1*(b*x + c) + a_0 // This kind of translation can be computed by applying the following recurrence: // To simplify the notation, we fix n = 4 // Moreover we use a_i[k] to represent the coefficient a_i at iteration k // a_i[0] = a_i // // a_4*(b*x + c)^4 + a_3*(b*x + c)^3 + a_2*(b*x + c)^2 + a_1*(b*x + c) + a_0 // --> // a_4*b*x*(b*x + c)^3 + (a_3 + a_4*c)*(b*x + c)^3 + a_2*(b*x + c)^2 + a_1*(b*x + c) + a_0 // Thus a_4[1] = a_4[0]*b, a_3[1] = a_3[0] + a_4[0]*c, a_2[1] = a_2[0], a_1[1] = a_1[0], a_0[1] = a_0[0] // // a_4[1]*x*(b*x + c)^3 + a_3[1]*(b*x + c)^3 + a_2[1]*(b*x + c)^2 + a_1[1]*(b*x + c) + a_0[1] // --> // a_4[1]*b*x^2*(b*x + c)^2 + (a_3[1]*b + a_4[1]*c)*x*(b*x + c)^2 + (a_2[1] + a_3[1]*c)*(b*x + c)^2 + a_1[1]*(b*x + c) + a_0[1] // Thus a_4[2] = a_4[1]*b, a_3[2] = a_3[1]*b + a_4[1]*c, a_2[2] = a_2[1] + a_3[1]*c, a_1[2] = a_1[1], a_0[2] = a_0[1] // // a_4[2]*x^2*(b*x + c)^2 + a_3[2]*x*(b*x + c)^2 + a_2[2]*(b*x + c)^2 + a_1[2]*(b*x + c) + a_0[2] // --> // a_4[2]*b*x^3*(b*x + c) + (a_3[2]*b + a_4[2]*c)*x^2*(b*x + c) + (a_2[2]*b + a_3[2]*c)*x*(b*x + c) + (a_1[2] + a_2[2]*c)*(b*x + c) + a_0[2] // Thus a_4[3] = a_4[2]*b, a_3[3] = a_3[2]*b + a_4[2]*c, a_2[3] = a_2[2]*b + a_3[2]*c, a_1[3] = a_1[2] + a_2[2]*c, a_0[3] = a_1[3] // // a_4[3]*x^3*(b*x + c) + a_3[3]*x^2*(b*x + c) + a_2[3]*x*(b*x + c) + a_1[3]*(b*x + c) + a_0[3] // ---> // a_4[3]*b*x^4 + (a_3[3]*b + a_4[3]*c)*x^3 + (a_2[3]*b + a_3[3]*c)*x^2 + (a_1[3]*b + a_2[3]*c)*x + (a_0[3] + a_1[3]*c) // void manager::translate_bq(unsigned sz, numeral * p, mpbq const & b) { if (sz <= 1) return; // Step 1 compose_2kn_p_x_div_2k(sz, p, b.k()); TRACE("upolynomial", tout << "after compose 2kn_p_x_div_2k\n"; display(tout, sz, p); tout << "\n";); // Step 2 numeral const & c = b.numerator(); unsigned n = sz - 1; for (unsigned i = 1; i <= n; i++) { checkpoint(); m().addmul(p[n - i], c, p[n - i + 1], p[n - i]); for (unsigned k = n - i + 1; k <= n - 1; k++) { m().mul2k(p[k], b.k()); m().addmul(p[k], c, p[k + 1], p[k]); } m().mul2k(p[n], b.k()); } TRACE("upolynomial", tout << "after special translation\n"; display(tout, sz, p); tout << "\n";); } // Similar to translate_bq but for rationals void manager::translate_q(unsigned sz, numeral * p, mpq const & b) { if (sz <= 1) return; // Step 1 compose_an_p_x_div_a(sz, p, b.denominator()); TRACE("upolynomial", tout << "after compose_an_p_x_div_a\n"; display(tout, sz, p); tout << "\n";); // Step 2 numeral const & c = b.numerator(); unsigned n = sz - 1; for (unsigned i = 1; i <= n; i++) { checkpoint(); m().addmul(p[n - i], c, p[n - i + 1], p[n - i]); for (unsigned k = n - i + 1; k <= n - 1; k++) { m().mul(p[k], b.denominator(), p[k]); m().addmul(p[k], c, p[k + 1], p[k]); } m().mul(p[n], b.denominator(), p[n]); } TRACE("upolynomial", tout << "after special translation\n"; display(tout, sz, p); tout << "\n";); } // p(x) := 2^n*p(x/2) where n = sz-1 void manager::compose_2n_p_x_div_2(unsigned sz, numeral * p) { if (sz <= 1) return; // a_n * x^n + 2 * a_{n-1} * x^{n-1} + ... + (2^n)*a_0 unsigned k = sz-1; // k = n for (unsigned i = 0; i < sz - 1; i++) { m().mul2k(p[i], k); k--; } } // p(x) := p(-x) void manager::p_minus_x(unsigned sz, numeral * p) { for (unsigned i = 0; i < sz; i++) { if (m().is_zero(p[i])) continue; if (i % 2 == 0) continue; m().neg(p[i]); } } // p(x) := x^n * p(1/x) void manager::p_1_div_x(unsigned sz, numeral * p) { if (sz <= 1) return; unsigned i = 0; unsigned j = sz-1; SASSERT(j >= 1); while (true) { swap(p[i], p[j]); i++; j--; if (i >= j) break; } } // p(x) := p(2^k * x) void manager::compose_p_2k_x(unsigned sz, numeral * p, unsigned k) { // (2^k)^n*a_n*x^n + (2^k)^(n-1)*x^{n-1} + ... + a_0 if (sz <= 1) return; unsigned k_i = k; for (unsigned i = 1; i < sz; i++) { m().mul2k(p[i], k_i); k_i += k; } } // p(x) := (2^k)^n * p(x/2^k) // // Let old(p) be of the form: // a_n * x^n + a_{n-1}*x^{n-1} + ... + a_1 * x + a_0 // // Then p is of the form: // a_n * x^n + a_{n-1} * 2^k * x^{n-1} + a_{n-2} * (2^k)^2 * x^{n-2} + ... + a_0 * (2^k)^n void manager::compose_2kn_p_x_div_2k(unsigned sz, numeral * p, unsigned k) { if (sz <= 1) return; unsigned k_i = k*sz; for (unsigned i = 0; i < sz; i++) { k_i -= k; if (!m().is_zero(p[i])) m().mul2k(p[i], k_i); } } // p(x) := a^n * p(x/a) // See compose_2kn_p_x_div_2k void manager::compose_an_p_x_div_a(unsigned sz, numeral * p, numeral const & a) { if (sz <= 1) return; unsigned i = sz-1; scoped_numeral a_i(m()); m().set(a_i, a); while (i > 0) { --i; if (!m().is_zero(p[i])) m().mul(p[i], a_i, p[i]); m().mul(a_i, a, a_i); } } // p(x) := p(b * x) void manager::compose_p_b_x(unsigned sz, numeral * p, numeral const & b) { if (sz <= 1) return; scoped_numeral b_i(m()); m().set(b_i, b); for (unsigned i = 1; i < sz; i++) { if (!m().is_zero(p[i])) m().mul(p[i], b_i, p[i]); m().mul(b_i, b, b_i); } } // Let b be of the form c/(2^k), then this operation is equivalent to: // (2^k)^n*p(c*x/(2^k)) // // Let old(p) be of the form: // a_n * x^n + a_{n-1}*x^{n-1} + ... + a_1 * x + a_0 // // Then p is of the form: // a_n * c^n * x^n + a_{n-1} * c^{n-1} * 2^k * x^{n-1} + ... + a_1 * c * (2^k)^(n-1) * x + a_0 * (2^k)^n void manager::compose_p_b_x(unsigned sz, numeral * p, mpbq const & b) { if (sz <= 1) return; unsigned k = b.k(); numeral const & c = b.numerator(); scoped_numeral c_i(m()); m().set(c_i, 1); unsigned k_i = k*sz; for (unsigned i = 0; i < sz; i++) { k_i -= k; if (!m().is_zero(p[i])) { m().mul2k(p[i], k_i); m().mul(p[i], c_i, p[i]); } m().mul(c_i, c, c_i); } SASSERT(k_i == 0); } // Let q be of the form b/c, then this operation is equivalent to: // p(x) := c^n*p(b*x/c) // // If u is a root of old(p), then u*(c/b) is a root of new p. // // Let old(p) be of the form: // a_n * x^n + a_{n-1}*x^{n-1} + ... + a_1 * x + a_0 // // Then p is of the form: // a_n * b^n * x^n + a_{n-1} * b^{n-1} * c * x^{n-1} + ... + a_1 * b * c^(n-1) * x + a_0 * c^n void manager::compose_p_q_x(unsigned sz, numeral * p, mpq const & q) { if (sz <= 1) return; numeral const & b = q.numerator(); numeral const & c = q.denominator(); scoped_numeral bc(m()); m().power(c, sz-1, bc); // bc = b^n for (unsigned i = 0; i < sz; i++) { if (!m().is_zero(p[i])) m().mul(p[i], bc, p[i]); if (i < sz - 1) { // bc <- (bc/b)*c m().div(bc, c, bc); m().mul(bc, b, bc); } } } // Evaluate the sign of p(b) int manager::eval_sign_at(unsigned sz, numeral const * p, mpbq const & b) { // Actually, given b = c/2^k, we compute the sign of (2^k)^n*p(b) // Original Horner Sequence // ((a_n * b + a_{n-1})*b + a_{n-2})*b + a_{n-3} ... // Variation of the Horner Sequence for (2^k)^n*p(b) // ((a_n * c + a_{n-1}*2_k)*c + a_{n-2}*(2_k)^2)*c + a_{n-3}*(2_k)^3 ... + a_0*(2_k)^n if (sz == 0) return 0; if (sz == 1) return sign_of(p[0]); numeral const & c = b.numerator(); unsigned k = b.k(); unsigned k_i = k; scoped_numeral r(m()); scoped_numeral ak(m()); m().set(r, p[sz-1]); unsigned i = sz-1; while (i > 0) { --i; numeral const & a = p[i]; if (m().is_zero(a)) { m().mul(r, c, r); } else { m().mul2k(a, k_i, ak); m().addmul(ak, r, c, r); } k_i += k; } return sign_of(r); } // Evaluate the sign of p(b) int manager::eval_sign_at(unsigned sz, numeral const * p, mpq const & b) { // Actually, given b = c/d, we compute the sign of (d^n)*p(b) // Original Horner Sequence // ((a_n * b + a_{n-1})*b + a_{n-2})*b + a_{n-3} ... // Variation of the Horner Sequence for (d^n)*p(b) // ((a_n * c + a_{n-1}*d)*c + a_{n-2}*d^2)*c + a_{n-3}*d^3 ... + a_0*d^n if (sz == 0) return 0; if (sz == 1) return sign_of(p[0]); numeral const & c = b.numerator(); numeral const & d = b.denominator(); scoped_numeral d_i(m()); m().set(d_i, d); scoped_numeral r(m()); scoped_numeral ak(m()); m().set(r, p[sz-1]); unsigned i = sz-1; while (i > 0) { --i; numeral const & a = p[i]; if (m().is_zero(a)) { m().mul(r, c, r); } else { m().mul(a, d_i, ak); m().addmul(ak, r, c, r); } m().mul(d_i, d, d_i); } return sign_of(r); } // Evaluate the sign of p(b) int manager::eval_sign_at(unsigned sz, numeral const * p, mpz const & b) { // Using Horner Sequence // ((a_n * b + a_{n-1})*b + a_{n-2})*b + a_{n-3} ... if (sz == 0) return 0; if (sz == 1) return sign_of(p[0]); scoped_numeral r(m()); m().set(r, p[sz-1]); unsigned i = sz-1; while (i > 0) { --i; numeral const & a = p[i]; if (m().is_zero(a)) m().mul(r, b, r); else m().addmul(a, r, b, r); } return sign_of(r); } int manager::eval_sign_at_zero(unsigned sz, numeral const * p) { if (sz == 0) return 0; return sign_of(p[0]); } int manager::eval_sign_at_plus_inf(unsigned sz, numeral const * p) { if (sz == 0) return 0; return sign_of(p[sz-1]); } int manager::eval_sign_at_minus_inf(unsigned sz, numeral const * p) { if (sz == 0) return 0; unsigned degree = sz - 1; if (degree % 2 == 0) return sign_of(p[sz-1]); else return -sign_of(p[sz-1]); } template unsigned manager::sign_variations_at_core(upolynomial_sequence const & seq, mpbq const & b) { unsigned sz = seq.size(); if (sz <= 1) return 0; unsigned r = 0; int sign, prev_sign; sign = 0; prev_sign = 0; unsigned i = 0; for (; i < sz; i++) { // find next nonzero unsigned psz = seq.size(i); numeral const * p = seq.coeffs(i); switch (loc) { case PLUS_INF: sign = eval_sign_at_plus_inf(psz, p); break; case MINUS_INF: sign = eval_sign_at_minus_inf(psz, p); break; case ZERO: sign = eval_sign_at_zero(psz, p); break; case MPBQ: sign = eval_sign_at(psz, p, b); break; default: UNREACHABLE(); break; } if (sign == 0) continue; SASSERT(sign == 1 || sign == -1); // in the first iteration prev_sign == 0, then r is never incremented. if (sign != prev_sign && prev_sign != 0) r++; // move to the next prev_sign = sign; } return r; } unsigned manager::sign_variations_at_minus_inf(upolynomial_sequence const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, dummy); } unsigned manager::sign_variations_at_plus_inf(upolynomial_sequence const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, dummy); } unsigned manager::sign_variations_at_zero(upolynomial_sequence const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, dummy); } unsigned manager::sign_variations_at(upolynomial_sequence const & seq, mpbq const & b) { return sign_variations_at_core(seq, b); } void manager::root_upper_bound(unsigned sz, numeral const * p, numeral & r) { // Using Cauchy's Inequality // We have that any root u of p must satisfy // |u| < (max(p) + min(p))/min(p) // |u| < (max(p) + |a_n|)/|a_n| // where: max(p) is the maximum coefficient in absolute value. // min(p) is the minimum (nonzero) coefficient in absolute value SASSERT(sz > 0); SASSERT(!m().is_zero(p[sz - 1])); bool init = false; scoped_numeral max(m()); scoped_numeral min(m()); scoped_numeral a_n(m()); scoped_numeral r2(m()); m().set(a_n, p[sz - 1]); m().abs(a_n); scoped_numeral c(m()); for (unsigned i = 0; i < sz; i++) { if (m().is_zero(p[i])) continue; m().set(c, p[i]); m().abs(c); if (!init) { m().set(max, c); m().set(min, c); init = true; continue; } if (m().gt(c, max)) m().set(max, c); if (m().lt(c, min)) m().set(min, c); } // first bound m().add(min, max, r); m().div(r, min, r); m().add(r, numeral(1), r); // second bound m().add(a_n, max, r2); m().div(r2, a_n, r2); m().add(r2, numeral(1), r2); // use the best bound if (m().lt(r2, r)) swap(r, r2); SASSERT(m().le(r, r2)); } /** \brief Find positive root upper bound using Knuth's approach. Given p(x) = a_n * x^n + a_{n-1} * x^{n-1} + ... + a_0 If a_n is positive, Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} < 0 }) Then, 2*B is a bound for the positive roots Similarly, if a_n is negative Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} > 0 }) Then, 2*B is a bound for the positive roots This procedure returns a k s.t. 2*B <= 2^k The procedure actually computes max of log2(abs(a_{n-k})) + 1 - log2(abs(a_n))/k Proof Sketch: Let u > 0 be a root of p(x). If u <= B, then we are done. So, let us assume u > B Assume, a_n > 0 (the proof is similar for a_n < 0) Then: a_n * u^n + a_{n-1} * u^{n-1} + ... + a0 = 0 u^n = 1/a_n (-a_{n-1} * u^{n-1} - ... - a_0) Note that, if we remove the monomials s.t. a_i is positive, then we are increasing the value of (-a_{n-1} * u^{n-1} - ... - a_0). Thus, u^n <= 1/a_n(SUM_{a_i < 0, 0 <= i < n} (-a_i * u^i)) Dividing by u_n which is positive, we get 1 <= 1/a_n(SUM_{a_i < 0, 0 <= i < n} (-a_i * u^i)) By replacing, i = n - k we have 1 <= 1/a_n(SUM_{a_{n-k} < 0, n >= k > 0} (-a_{n-k} * u^{n-k})) = 1/a_n(SUM_{a_{n-k} < 0, 1 <= k <= n} (-a_i * u^{n-k})) < Sum_{1 <= k < +oo}(B/u)^k Since u > B, we have that Sum_{1 <= k < +oo}(B/u)^k = B/(u - B) Thus, we have 1 < B/(u - B) and u < 2B */ unsigned manager::knuth_positive_root_upper_bound(unsigned sz, numeral const * p) { if (sz == 0) return 0; unsigned max = 0; unsigned n = sz - 1; bool pos_a_n = m().is_pos(p[n]); unsigned log2_a_n = pos_a_n ? m().log2(p[n]) : m().mlog2(p[n]); for (unsigned k = 1; k <= n; k++) { numeral const & a_n_k = p[n - k]; if (m().is_zero(a_n_k)) continue; bool pos_a_n_k = m().is_pos(a_n_k); if (pos_a_n_k == pos_a_n) continue; // must have oposite signs unsigned log2_a_n_k = pos_a_n_k ? m().log2(a_n_k) : m().mlog2(a_n_k); if (log2_a_n > log2_a_n_k) continue; unsigned curr = log2_a_n_k + 1 - log2_a_n; if (curr % k == 0) { curr /= k; } else { curr /= k; curr ++; } if (curr > max) max = curr; } return max + 1; } /** It is essentially applying knuth_positive_root_upper_bound for p(-x) */ unsigned manager::knuth_negative_root_upper_bound(unsigned sz, numeral const * p) { numeral * _p = const_cast(p); p_minus_x(sz, _p); unsigned r = knuth_positive_root_upper_bound(sz, _p); p_minus_x(sz, _p); return r; } /** Return a lower bound for the nonzero roots of p(x). Let k be the result of this procedure, Then for any nonzero root alpha of p(x), we have that |alpha| > 1/2^k We essentially compute the upper bound for the roots of x^n*p(1/x) where n is the degree of p. Remark: alpha is a nonzero root of p(x) iff 1/alpha is a root of x^n*p(1/x). Thus, if 2^k is upper bound for the root of x^n*p(1/x). Then we have that -2^k < 1/alpha < 2^k and consequently alpha < -1/2^k or 1/2^k < alpha /pre p is not the zero polynomial. */ unsigned manager::nonzero_root_lower_bound(unsigned sz, numeral const * p) { SASSERT(sz > 0); // consume zeros unsigned i = 0; while (true) { SASSERT(i < sz); if (!m().is_zero(p[i])) break; i++; } SASSERT(i < sz); SASSERT(!m().is_zero(p[i])); unsigned nz_sz = sz - i; numeral const * nz_p = p + i; // nz_p does not have zero roots; numeral * _nz_p = const_cast(nz_p); // destructive update for quickly computing x^n*nz_p(1/x) std::reverse(_nz_p, _nz_p + nz_sz); unsigned k1 = knuth_positive_root_upper_bound(nz_sz, _nz_p); unsigned k2 = knuth_negative_root_upper_bound(nz_sz, _nz_p); // undo destructive update std::reverse(_nz_p, _nz_p + nz_sz); return std::max(k1, k2); } // Frames for implementing drs_isolate_0_1_roots. // The frames are used to avoid recursive calls and potential stack overflows. // Each frame has a polynomial associated with it. The polynomials // are stored in a separate stack. struct manager::drs_frame { unsigned m_parent_idx; // position of the parent frame, UINT_MAX if it doesn't have a parent frame unsigned m_size:30; // size of the polynomial associated with this frame unsigned m_first:1; // first time visiting the frame? unsigned m_left:1; // true if the frame is the left child of the parent frame. drs_frame(unsigned pidx, unsigned sz, bool left): m_parent_idx(pidx), m_size(sz), m_first(true), m_left(left) { } }; // Pop the top frame from the frame_stack, remove the coefficients of the polynomial associated with the top frame from p_stack. void manager::pop_top_frame(numeral_vector & p_stack, svector & frame_stack) { SASSERT(!frame_stack.empty()); unsigned sz = frame_stack.back().m_size; SASSERT(sz <= p_stack.size()); for (unsigned i = 0; i < sz; i++) { m().del(p_stack.back()); p_stack.pop_back(); } frame_stack.pop_back(); } // Auxiliar method for isolating the roots of p in the interval (0, 1). // The basic idea is to split the interval in: (0, 1/2) and (1/2, 1). // This is accomplished by analyzing the roots in the interval (0, 1) of the following polynomials. // p1(x) := 2^n * p(x/2) where n = sz-1 // p2(x) := p1(x+1) // We say p1(x) is the left child, and p2 the right child. The coefficients p1 and p2 are stored in p_stack. // A new frame is created for each child. void manager::push_child_frames(unsigned sz, numeral const * p, numeral_vector & p_stack, svector & frame_stack) { // I don't really need the following test, because 0 - 1 == UINT_MAX unsigned parent_idx = frame_stack.empty() ? UINT_MAX : frame_stack.size() - 1; numeral_vector & p_aux = m_push_tmp; // Possible optimization: the coefficients of the parent frame are not needed anymore. // So, we could reuse/steal them. // left child set(sz, p, p_aux); compose_2n_p_x_div_2(p_aux.size(), p_aux.c_ptr()); normalize(p_aux); for (unsigned i = 0; i < sz; i++) { p_stack.push_back(numeral()); m().set(p_stack.back(), p_aux[i]); } frame_stack.push_back(drs_frame(parent_idx, sz, true)); // right child translate(sz, p_stack.end() - sz, p_aux); normalize(p_aux); for (unsigned i = 0; i < sz; i++) { p_stack.push_back(numeral()); swap(p_stack.back(), p_aux[i]); } frame_stack.push_back(drs_frame(parent_idx, sz, false)); } // (0,1) is an isolating interval for the polynomial associated with the top frame. // Apply transformations for obtaining isolating interval for the original polynomial: // We use the following transformations: // Left child: (l, u) -> (l/2, u/2) // Right child: (l, u) -> ((l+1)/2, (u+1)/2) void manager::add_isolating_interval(svector const & frame_stack, mpbq_manager & bqm, mpbq_vector & lowers, mpbq_vector & uppers) { mpbq l(0); mpbq u(1); unsigned idx = frame_stack.size() - 1; while (idx != UINT_MAX) { drs_frame const & fr = frame_stack[idx]; TRACE("upolynomial", tout << "normalizing...\n"; tout << "idx: " << idx << ", left: " << fr.m_left << ", l: " << bqm.to_string(l) << ", u: " << bqm.to_string(u) << "\n";); if (fr.m_left) { bqm.div2(l); bqm.div2(u); } else { bqm.add(l, mpz(1), l); bqm.add(u, mpz(1), u); bqm.div2(l); bqm.div2(u); } idx = fr.m_parent_idx; } TRACE("upolynomial", tout << "adding normalized interval (" << bqm.to_string(l) << ", " << bqm.to_string(u) << ")\n";); lowers.push_back(mpbq()); uppers.push_back(mpbq()); swap(lowers.back(), l); swap(uppers.back(), u); } // 1/2 is a root of the polynomial associated with the top frame. // Apply transformations for obtaining root of the original polynomial: // We use the following transformations: // Left child: u -> u/2 // Right child: u -> (u+1)/2 void manager::add_root(svector const & frame_stack, mpbq_manager & bqm, mpbq_vector & roots) { mpbq u(1,1); unsigned idx = frame_stack.size() - 1; while (idx != UINT_MAX) { drs_frame const & fr = frame_stack[idx]; if (fr.m_left) { bqm.div2(u); } else { bqm.add(u, mpz(1), u); bqm.div2(u); } idx = fr.m_parent_idx; } TRACE("upolynomial", tout << "adding normalized root: " << bqm.to_string(u) << "\n";); roots.push_back(mpbq()); swap(roots.back(), u); } // Isolate roots in the interval (0, 1) void manager::drs_isolate_0_1_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { TRACE("upolynomial", tout << "isolating (0,1) roots of:\n"; display(tout, sz, p); tout << "\n";); unsigned k = descartes_bound_0_1(sz, p); // easy cases... if (k == 0) { TRACE("upolynomial", tout << "polynomial does not have any roots\n";); return; } if (k == 1) { TRACE("upolynomial", tout << "polynomial has one root in (0, 1)\n";); lowers.push_back(mpbq(0)); uppers.push_back(mpbq(1)); return; } TRACE("upolynomial", tout << "polynomial has more than one root in (0, 1), starting search...\n";); scoped_numeral_vector q(m()); scoped_numeral_vector p_stack(m()); svector frame_stack; if (has_one_half_root(sz, p)) { TRACE("upolynomial", tout << "polynomial has a 1/2 root\n";); roots.push_back(mpbq(1, 1)); remove_one_half_root(sz, p, q); push_child_frames(q.size(), q.c_ptr(), p_stack, frame_stack); } else { push_child_frames(sz, p, p_stack, frame_stack); } SASSERT(!frame_stack.empty()); while (!frame_stack.empty()) { checkpoint(); drs_frame & fr = frame_stack.back(); unsigned sz = fr.m_size; SASSERT(sz <= p_stack.size()); numeral const * p = p_stack.end() - sz; TRACE("upolynomial", tout << "processing frame #" << frame_stack.size() - 1 << "\n" << "first: " << fr.m_first << ", left: " << fr.m_left << ", sz: " << fr.m_size << ", parent_idx: "; if (fr.m_parent_idx == UINT_MAX) tout << ""; else tout << fr.m_parent_idx; tout << "\np: "; display(tout, sz, p); tout << "\n";); if (!fr.m_first) { pop_top_frame(p_stack, frame_stack); continue; } fr.m_first = false; unsigned k = descartes_bound_0_1(sz, p); if (k == 0) { TRACE("upolynomial", tout << "(0, 1) does not have roots\n";); pop_top_frame(p_stack, frame_stack); continue; } if (k == 1) { TRACE("upolynomial", tout << "(0, 1) is isolating interval\n";); add_isolating_interval(frame_stack, bqm, lowers, uppers); pop_top_frame(p_stack, frame_stack); continue; } TRACE("upolynomial", tout << "polynomial has more than one root in (0, 1) creating child frames...\n";); if (has_one_half_root(sz, p)) { TRACE("upolynomial", tout << "1/2 is a root\n";); add_root(frame_stack, bqm, roots); remove_one_half_root(sz, p, q); push_child_frames(q.size(), q.c_ptr(), p_stack, frame_stack); } else { push_child_frames(sz, p, p_stack, frame_stack); } } } // Foreach i in [starting_at, v.size()) v[i] := 2^k*v[i] static void adjust_pos(mpbq_manager & bqm, mpbq_vector & v, unsigned starting_at, unsigned k) { unsigned sz = v.size(); for (unsigned i = starting_at; i < sz; i++) bqm.mul2k(v[i], k); } // Foreach i in [starting_at, v.size()) v[i] := -2^k*v[i] static void adjust_neg(mpbq_manager & bqm, mpbq_vector & v, unsigned starting_at, unsigned k) { unsigned sz = v.size(); for (unsigned i = starting_at; i < sz; i++) { bqm.mul2k(v[i], k); bqm.neg(v[i]); } } static void swap_lowers_uppers(unsigned starting_at, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(lowers.size() == uppers.size()); unsigned sz = lowers.size(); for (unsigned i = starting_at; i < sz; i++) { swap(lowers[i], uppers[i]); } } // Isolate roots in an interval (-2^neg_k, 2^pos_k) using an approach based on Descartes rule of signs. void manager::drs_isolate_roots(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { scoped_numeral_vector aux_p(m()); set(sz, p, aux_p); pos_k = std::max(neg_k, pos_k); compose_p_2k_x(sz, aux_p.c_ptr(), pos_k); // p(x) := p(2^{pos_k} * x) // Since the desired positive roots of p(x) are in (0, 2^pos_k), TRACE("upolynomial", tout << "searching at (0, 1)\n";); unsigned old_roots_sz = roots.size(); unsigned old_lowers_sz = lowers.size(); drs_isolate_0_1_roots(sz, aux_p.c_ptr(), bqm, roots, lowers, uppers); SASSERT(lowers.size() == uppers.size()); adjust_pos(bqm, roots, old_roots_sz, pos_k); adjust_pos(bqm, lowers, old_lowers_sz, pos_k); adjust_pos(bqm, uppers, old_lowers_sz, pos_k); // Isolate roots in (-2^{neg_k}, 0) // p(x) := p(-x) p_minus_x(sz, p); compose_p_2k_x(sz, p, neg_k); TRACE("upolynomial", tout << "searching at (-1, 0) using:\n"; display(tout, sz, p); tout << "\n";); old_roots_sz = roots.size(); old_lowers_sz = lowers.size(); drs_isolate_0_1_roots(sz, p, bqm, roots, lowers, uppers); SASSERT(lowers.size() == uppers.size()); adjust_neg(bqm, roots, old_roots_sz, neg_k); adjust_neg(bqm, lowers, old_lowers_sz, neg_k); adjust_neg(bqm, uppers, old_lowers_sz, neg_k); swap_lowers_uppers(old_lowers_sz, lowers, uppers); } // Isolate roots using an approach based on Descartes rule of signs. void manager::drs_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(lowers.size() == uppers.size()); SASSERT(!has_zero_roots(sz, p)); scoped_numeral_vector p1(m()); set(sz, p, p1); normalize(p1); TRACE("upolynomial", scoped_numeral U(m()); root_upper_bound(p1.size(), p1.c_ptr(), U); unsigned U_k = m().log2(U) + 1; tout << "Cauchy U: 2^" << U_k << "\n"; tout << "Knuth pos U: 2^" << knuth_positive_root_upper_bound(sz, p) << "\n"; tout << "Knuth neg U: 2^" << knuth_negative_root_upper_bound(sz, p) << "\n";); #if 1 unsigned pos_k = knuth_positive_root_upper_bound(sz, p); unsigned neg_k = knuth_negative_root_upper_bound(sz, p); #else scoped_numeral U(m()); root_upper_bound(p1.size(), p1.c_ptr(), U); std::cout << "U: " << U << "\n"; unsigned pos_k = m().log2(U) + 1; unsigned neg_k = pos_k; #endif drs_isolate_roots(p1.size(), p1.c_ptr(), neg_k, pos_k, bqm, roots, lowers, uppers); } // Frame for root isolation in sturm_isolate_roots. // It stores (lower, upper, num. sign variations at lower, num. sign variations at upper) struct ss_frame { mpbq m_lower; mpbq m_upper; unsigned m_lower_sv; // number of sign variations in the sturm sequence at the lower bound unsigned m_upper_sv; // number of sign variations in the sturm sequence at the upper bound }; class ss_frame_stack : public svector { mpbq_manager & m; public: ss_frame_stack(mpbq_manager & _m):m(_m) {} ~ss_frame_stack() { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { ss_frame & f = *it; m.del(f.m_lower); m.del(f.m_upper); } } }; inline void pop_ss_frame(mpbq_manager & m, ss_frame_stack & s) { m.del(s.back().m_lower); m.del(s.back().m_upper); s.pop_back(); } void ss_add_isolating_interval(mpbq_manager & m, mpbq const & lower, mpbq const & upper, mpbq_vector & lowers, mpbq_vector & uppers) { lowers.push_back(mpbq()); uppers.push_back(mpbq()); m.set(lowers.back(), lower); m.set(uppers.back(), upper); } // Isolate roots in an interval (-2^neg_k, 2^pos_k) using an approach based on Descartes rule of signs. void manager::sturm_isolate_roots_core(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(!has_zero_roots(sz, p)); scoped_upolynomial_sequence seq(*this); scoped_mpbq mid(bqm); scoped_mpbq curr_lower(bqm); scoped_mpbq curr_upper(bqm); sturm_seq(sz, p, seq); ss_frame_stack s(bqm); TRACE("upolynomial", tout << "p: "; display(tout, sz, p); tout << "\nSturm seq:\n"; display(tout, seq); tout << "\n";); unsigned lower_sv = sign_variations_at_minus_inf(seq); unsigned zero_sv = sign_variations_at_zero(seq); unsigned upper_sv = sign_variations_at_plus_inf(seq); if (upper_sv >= lower_sv) return; // no roots bqm.power(mpbq(2), neg_k, curr_lower); bqm.neg(curr_lower); bqm.power(mpbq(2), pos_k, curr_upper); #define PUSH_SS_FRAME(LOWER_SV, UPPER_SV, LOWER, UPPER) { \ SASSERT(!bqm.eq(LOWER, UPPER)); \ if (LOWER_SV == UPPER_SV) { \ \ } \ else if (LOWER_SV == UPPER_SV + 1) { \ /* Sturm is for half-open intervals (a, b] */ \ /* We must check if upper is the root */ \ if (eval_sign_at(sz, p, UPPER) == 0) { \ /* found precise root */ \ roots.push_back(mpbq()); \ bqm.set(roots.back(), UPPER); \ } \ else { \ ss_add_isolating_interval(bqm, LOWER, UPPER, lowers, uppers); \ } \ } \ else { \ s.push_back(ss_frame()); \ ss_frame & f = s.back(); \ bqm.set(f.m_lower, LOWER); \ bqm.set(f.m_upper, UPPER); \ f.m_lower_sv = LOWER_SV; \ f.m_upper_sv = UPPER_SV; \ } \ } ((void) 0) mpbq zero(0); PUSH_SS_FRAME(lower_sv, zero_sv, curr_lower, zero); PUSH_SS_FRAME(zero_sv, upper_sv, zero, curr_upper); while (!s.empty()) { checkpoint(); ss_frame & f = s.back(); lower_sv = f.m_lower_sv; upper_sv = f.m_upper_sv; bqm.swap(curr_lower, f.m_lower); bqm.swap(curr_upper, f.m_upper); pop_ss_frame(bqm, s); SASSERT(lower_sv > upper_sv + 1); bqm.add(curr_lower, curr_upper, mid); bqm.div2(mid); TRACE("upolynomial", tout << "depth: " << s.size() << "\n"; tout << "lower_sv: " << lower_sv << "\n"; tout << "upper_sv: " << upper_sv << "\n"; tout << "curr_lower: " << curr_lower << "\n"; tout << "curr_upper: " << curr_upper << "\n"; tout << "mid: " << mid << "\n";); unsigned mid_sv = sign_variations_at(seq, mid); PUSH_SS_FRAME(lower_sv, mid_sv, curr_lower, mid); PUSH_SS_FRAME(mid_sv, upper_sv, mid, curr_upper); } } void manager::sturm_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(lowers.size() == uppers.size()); scoped_numeral_vector p1(m()); set(sz, p, p1); normalize(p1); #if 1 unsigned pos_k = knuth_positive_root_upper_bound(sz, p); unsigned neg_k = knuth_negative_root_upper_bound(sz, p); #else scoped_numeral U(m()); root_upper_bound(p1.size(), p1.c_ptr(), U); unsigned pos_k = m().log2(U) + 1; unsigned neg_k = pos_k; #endif sturm_isolate_roots_core(p1.size(), p1.c_ptr(), neg_k, pos_k, bqm, roots, lowers, uppers); } // Isolate roots of a square free polynomial that does not have zero roots void manager::sqf_nz_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(!has_zero_roots(sz, p)); drs_isolate_roots(sz, p, bqm, roots, lowers, uppers); // sturm_isolate_roots(sz, p, bqm, roots, lowers, uppers); } void manager::sqf_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { bqm.reset(roots); bqm.reset(lowers); bqm.reset(uppers); if (has_zero_roots(sz, p)) { roots.push_back(mpbq(0)); scoped_numeral_vector nz_p(m()); remove_zero_roots(sz, p, nz_p); TRACE("upolynomial", tout << "after removing zero root:\n"; display(tout, nz_p); tout << "\n";); SASSERT(!has_zero_roots(nz_p.size(), nz_p.c_ptr())); sqf_nz_isolate_roots(nz_p.size(), nz_p.c_ptr(), bqm, roots, lowers, uppers); } else { sqf_nz_isolate_roots(sz, p, bqm, roots, lowers, uppers); } } void manager::isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(sz > 0); TRACE("upolynomial", tout << "isolating roots of:\n"; display(tout, sz, p); tout << "\n";); scoped_numeral_vector sqf_p(m()); square_free(sz, p, sqf_p); TRACE("upolynomial", tout << "square free part:\n"; display(tout, sqf_p); tout << "\n";); sqf_isolate_roots(sqf_p.size(), sqf_p.c_ptr(), bqm, roots, lowers, uppers); } // Keep expanding the Sturm sequence starting at seq void manager::sturm_seq_core(upolynomial_sequence & seq) { scoped_numeral_vector r(m()); while (true) { unsigned sz = seq.size(); srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); if (is_zero(r)) return; normalize(r); seq.push(r.size(), r.c_ptr()); } } void manager::sturm_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq) { reset(seq); seq.push(m(), sz1, p1); seq.push(m(), sz2, p2); sturm_seq_core(seq); } void manager::sturm_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq) { reset(seq); scoped_numeral_vector p_prime(m()); seq.push(m(), sz, p); derivative(sz, p, p_prime); seq.push(p_prime.size(), p_prime.c_ptr()); sturm_seq_core(seq); } void manager::sturm_tarski_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq) { reset(seq); scoped_numeral_vector p1p2(m()); seq.push(m(), sz1, p1); derivative(sz1, p1, p1p2); mul(p1p2.size(), p1p2.c_ptr(), sz2, p2, p1p2); seq.push(p1p2.size(), p1p2.c_ptr()); sturm_seq_core(seq); } void manager::fourier_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq) { reset(seq); scoped_numeral_vector p_prime(m()); seq.push(m(), sz, p); if (sz == 0) return; unsigned degree = sz - 1; for (unsigned i = 0; i < degree; i++) { unsigned sz = seq.size(); derivative(seq.size(sz-1), seq.coeffs(sz-1), p_prime); normalize(p_prime); seq.push(p_prime.size(), p_prime.c_ptr()); } } /** We say an interval (a, b) of a polynomial p is ISOLATING if p has only one root in the interval (a, b). We say an isolating interval (a, b) of a square free polynomial p is REFINEABLE if sign(p(a)) = -sign(p(b)) Not every isolating interval (a, b) of a square free polynomial p is refineable, because sign(p(a)) or sign(p(b)) may be zero. Refinable intervals of square free polynomials are useful, because we can increase precision ("squeeze" the interval) by just evaluating p at (a+b)/2 This procedure converts an isolating interval of a square free polynomial p, into a refinable interval, or an actual root. The method returns TRUE if it produced a REFINABLE interval (a', b'). The new interval is stored in input variables a and b. The method returns FALSE if it found the root a' in the interval (a, b). The root is stored in the input variable a. \pre p MUST BE SQUARE FREE. \warning This method may loop if p is not square free or if (a,b) is not an isolating interval. */ bool manager::isolating2refinable(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b) { int sign_a = eval_sign_at(sz, p, a); int sign_b = eval_sign_at(sz, p, b); TRACE("upolynomial", tout << "sign_a: " << sign_a << ", sign_b: " << sign_b << "\n";); if (sign_a != 0 && sign_b != 0) { // CASE 1 SASSERT(sign_a == -sign_b); // p is square free return true; } if (sign_a == 0 && sign_b != 0) { // CASE 2 scoped_mpbq new_a(bqm); // new_a <- (a+b)/2 bqm.add(a, b, new_a); bqm.div2(new_a); while (true) { TRACE("upolynomial", tout << "CASE 2, a: " << bqm.to_string(a) << ", b: " << bqm.to_string(b) << ", new_a: " << bqm.to_string(new_a) << "\n";); int sign_new_a = eval_sign_at(sz, p, new_a); if (sign_new_a != sign_b) { swap(new_a, a); SASSERT(sign_new_a == 0 || // found the actual root sign_new_a == -sign_b); // found refinable interval return sign_new_a != 0; } else { SASSERT(sign_new_a == sign_b); // b <- new_a // new_a <- (a+b)/2 swap(b, new_a); bqm.add(b, a, new_a); bqm.div2(new_a); } } } if (sign_a != 0 && sign_b == 0 ) { // CASE 3 scoped_mpbq new_b(bqm); // new_b <- (a+b)/2 bqm.add(a, b, new_b); bqm.div2(new_b); while (true) { TRACE("upolynomial", tout << "CASE 3, a: " << bqm.to_string(a) << ", b: " << bqm.to_string(b) << ", new_b: " << bqm.to_string(new_b) << "\n";); int sign_new_b = eval_sign_at(sz, p, new_b); if (sign_new_b != sign_a) { SASSERT(sign_new_b == 0 || // found the actual root sign_new_b == -sign_a); // found refinable interval if (sign_new_b == 0) swap(new_b, a); // store root in a else swap(new_b, b); return sign_new_b != 0; } else { SASSERT(sign_new_b == sign_a); // a <- new_b // new_b <- (a+b)/2 swap(a, new_b); bqm.add(b, a, new_b); bqm.div2(new_b); } } } SASSERT(sign_a == 0 && sign_b == 0); // CASE 4 // we do cases 3 and 4 in "parallel" mpbq & a1 = a; scoped_mpbq b1(bqm); scoped_mpbq a2(bqm); mpbq & b2 = b; scoped_mpbq new_a1(bqm); scoped_mpbq new_b2(bqm); // b1 <- (a+b)/2 bqm.add(a, b, b1); bqm.div2(b1); // a2 <- (a+b)/2 bqm.set(a2, b1); int sign_b1 = eval_sign_at(sz, p, b1); int sign_a2 = sign_b1; bool result; if (sign_b1 == 0) { // found root swap(b1, a); result = false; goto end; } // new_a1 <- (a1+b1)/2 bqm.add(a1, b1, new_a1); bqm.div2(new_a1); // new_b2 <- (a2+b2)/2 bqm.add(a2, b2, new_b2); bqm.div2(new_b2); while (true) { TRACE("upolynomial", tout << "CASE 4\na1: " << bqm.to_string(a1) << ", b1: " << bqm.to_string(b1) << ", new_a1: " << bqm.to_string(new_a1) << "\n"; tout << "a2: " << bqm.to_string(a2) << ", b2: " << bqm.to_string(b2) << ", new_b2: " << bqm.to_string(new_b2) << "\n";); int sign_new_a1 = eval_sign_at(sz, p, new_a1); if (sign_new_a1 == 0) { // found root swap(new_a1, a); result = false; goto end; } if (sign_new_a1 == -sign_b1) { // found interval swap(new_a1, a); swap(b1, b); result = true; goto end; } int sign_new_b2 = eval_sign_at(sz, p, new_b2); if (sign_new_b2 == 0) { // found root swap(new_b2, a); result = false; goto end; } if (sign_new_b2 == -sign_a2) { // found interval swap(a2, a); swap(new_b2, b); result = true; goto end; } SASSERT(sign_new_a1 == sign_b1); // b1 <- new_a1 // new_a1 <- (a1+b1)/2 swap(b1, new_a1); bqm.add(b1, a1, new_a1); bqm.div2(new_a1); SASSERT(sign_new_b2 == sign_a2); // a2 <- new_b2 // new_b2 <- (a2+b2)/2 swap(a2, new_b2); bqm.add(b2, a2, new_b2); bqm.div2(new_b2); } end: return result; } /** Given a square-free polynomial p, and a refinable interval (a,b), then "squeeze" (a,b). That is, return a new interval (a',b') s.t. b' - a' = (b - a)/2 See isolating2refinable for a definition of refinable interval. Return TRUE, if interval was squeezed, and new interval is stored in (a,b). Return FALSE, if the actual root was found, it is stored in a. The arguments sign_a and sign_b must contain the values returned by eval_sign_at(sz, p, a) and eval_sign_at(sz, p, b). */ bool manager::refine_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b) { SASSERT(sign_a == eval_sign_at(sz, p, a)); int sign_b = -sign_a; SASSERT(sign_b == eval_sign_at(sz, p, b)); SASSERT(sign_a == -sign_b); SASSERT(sign_a != 0 && sign_b != 0); scoped_mpbq mid(bqm); bqm.add(a, b, mid); bqm.div2(mid); int sign_mid = eval_sign_at(sz, p, mid); if (sign_mid == 0) { swap(mid, a); return false; } if (sign_mid == sign_a) { swap(mid, a); return true; } SASSERT(sign_mid == sign_b); swap(mid, b); return true; } // See refine_core bool manager::refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b) { int sign_a = eval_sign_at(sz, p, a); SASSERT(sign_a != 0); return refine_core(sz, p, sign_a, bqm, a, b); } // Keeps reducing the interval until b - a < 1/2^k or a root is found. // See comments in refine_core above. // // Return TRUE, if interval was squeezed, and new interval is stored in (a,b). // Return FALSE, if the actual root was found, it is stored in a. bool manager::refine_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k) { SASSERT(sign_a != 0); SASSERT(sign_a == eval_sign_at(sz, p, a)); SASSERT(-sign_a == eval_sign_at(sz, p, b)); scoped_mpbq w(bqm); while (true) { checkpoint(); bqm.sub(b, a, w); if (bqm.lt_1div2k(w, prec_k)) { return true; } if (!refine_core(sz, p, sign_a, bqm, a, b)) { return false; // found root } } } bool manager::refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k) { int sign_a = eval_sign_at(sz, p, a); int sign_b = -sign_a; SASSERT(eval_sign_at(sz, p, b) == sign_b); SASSERT(sign_a != 0 && sign_b != 0); return refine_core(sz, p, sign_a, bqm, a, b, prec_k); } bool manager::convert_q2bq_interval(unsigned sz, numeral const * p, mpq const & a, mpq const & b, mpbq_manager & bqm, mpbq & c, mpbq & d) { int sign_a = eval_sign_at(sz, p, a); int sign_b = eval_sign_at(sz, p, b); SASSERT(sign_a != 0 && sign_b != 0); SASSERT(sign_a == -sign_b); bool found_d = false; TRACE("convert_bug", tout << "a: " << m().to_string(a.numerator()) << "/" << m().to_string(a.denominator()) << "\n"; tout << "b: " << m().to_string(b.numerator()) << "/" << m().to_string(b.denominator()) << "\n"; tout << "sign_a: " << sign_a << "\n"; tout << "sign_b: " << sign_b << "\n";); scoped_mpbq lower(bqm), upper(bqm); if (bqm.to_mpbq(a, lower)) { TRACE("convert_bug", tout << "found c: " << lower << "\n";); // found c swap(c, lower); SASSERT(bqm.eq(c, a)); } else { // lower = a.numerator()/2^(k+1) where k is log2(a) bqm.set(upper, lower); bqm.mul2(upper); if (m_manager.is_neg(a.numerator())) ::swap(lower, upper); TRACE("convert_bug", tout << "a: "; m().display(tout, a.numerator()); tout << "/"; m().display(tout, a.denominator()); tout << "\n"; tout << "lower: "; bqm.display(tout, lower); tout << ", upper: "; bqm.display(tout, upper); tout << "\n";); SASSERT(bqm.lt(lower, a)); SASSERT(bqm.gt(upper, a)); while (bqm.ge(upper, b)) { bqm.refine_upper(a, lower, upper); } SASSERT(bqm.lt(upper, b)); while (true) { int sign_upper = eval_sign_at(sz, p, upper); if (sign_upper == 0) { // found root bqm.swap(c, upper); bqm.del(lower); bqm.del(upper); return false; } else if (sign_upper == sign_a) { // found c bqm.swap(c, upper); break; } else { SASSERT(sign_upper == sign_b); // found d if (!found_d) { found_d = true; bqm.set(d, upper); } } bqm.refine_upper(a, lower, upper); } } if (!found_d) { if (bqm.to_mpbq(b, lower)) { // found d swap(d, lower); SASSERT(bqm.eq(d, b)); } else { // lower = b.numerator()/2^(k+1) where k is log2(b) bqm.set(upper, lower); bqm.mul2(upper); if (m_manager.is_neg(b.numerator())) ::swap(lower, upper); SASSERT(bqm.gt(upper, b)); SASSERT(bqm.lt(lower, b)); while (bqm.le(lower, c)) { bqm.refine_lower(b, lower, upper); } SASSERT(bqm.lt(c, lower)); SASSERT(bqm.lt(lower, upper)); SASSERT(bqm.lt(lower, b)); while (true) { int sign_lower = eval_sign_at(sz, p, lower); if (sign_lower == 0) { // found root bqm.swap(c, lower); bqm.del(lower); bqm.del(upper); return false; } else if (sign_lower == sign_b) { // found d bqm.swap(d, lower); break; } bqm.refine_lower(b, lower, upper); } } } SASSERT(bqm.lt(c, d)); SASSERT(eval_sign_at(sz, p, c) == -eval_sign_at(sz, p, d)); SASSERT(bqm.ge(c, a)); SASSERT(bqm.le(d, b)); return true; } bool manager::normalize_interval_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & m, mpbq & a, mpbq & b) { if (m.is_neg(a) && m.is_pos(b)) { // See bool manager::normalize_interval(unsigned sz, numeral const * p, mpbq_manager & m, mpbq const & a, mpbq const & b) { if (sign_a == INT_MIN) { sign_a = eval_sign_at(sz, p, a); } else { SASSERT(sign_a == eval_sign_at(sz, p, a)); } int sign_b = -sign_a; SASSERT(sign_b == eval_sign_at(sz, p, b)); SASSERT(sign_a != 0 && sign_b != 0); if (has_zero_roots(sz, p)) { return false; // zero is the root } int sign_zero = eval_sign_at_zero(sz, p); if (sign_a == sign_zero) { m.reset(a); } else { m.reset(b); } SASSERT(eval_sign_at(sz, p, a) == -eval_sign_at(sz, p, b)); } return true; } bool manager::normalize_interval(unsigned sz, numeral const * p, mpbq_manager & m, mpbq & a, mpbq & b) { return normalize_interval_core(sz, p, INT_MIN, m, a, b); } unsigned manager::get_root_id(unsigned sz, numeral const * p, mpbq const & l) { scoped_upolynomial_sequence seq(*this); sturm_seq(sz, p, seq); unsigned s0 = sign_variations_at_minus_inf(seq); unsigned s1 = sign_variations_at(seq, l); SASSERT(s0 >= s1); return s0 - s1; } void manager::flip_sign(factors & r) { scoped_numeral new_c(m_manager); m_manager.set(new_c, r.get_constant()); m_manager.neg(new_c); r.set_constant(new_c); } void manager::factor_2_sqf_pp(numeral_vector & p, factors & r, unsigned k) { SASSERT(p.size() == 3); // p has degree 2 TRACE("factor", tout << "factor square free (degree == 2):\n"; display(tout, p); tout << "\n";); numeral const & a = p[2]; numeral const & b = p[1]; numeral const & c = p[0]; TRACE("factor", tout << "a: " << m().to_string(a) << "\nb: " << m().to_string(b) << "\nc: " << m().to_string(c) << "\n";); // Create the discriminant: b^2 - 4*a*c scoped_numeral b2(m()); scoped_numeral ac(m()); scoped_numeral disc(m()); m().power(b, 2, b2); m().mul(a, c, ac); m().addmul(b2, numeral(-4), ac, disc); // discriminant must be different from 0, since p is square free SASSERT(!m().is_zero(disc)); scoped_numeral disc_sqrt(m()); if (!m().is_perfect_square(disc, disc_sqrt)) { // p is irreducible r.push_back(p, k); return; } TRACE("factor", tout << "disc_sqrt: " << m().to_string(disc_sqrt) << "\n";); // p = cont*(2*a*x + b - disc_sqrt)*(2*a*x + b + disc_sqrt) scoped_numeral_vector f1(m()); scoped_numeral_vector f2(m()); f1.reserve(2); f2.reserve(2); m().sub(b, disc_sqrt, f1[0]); m().add(b, disc_sqrt, f2[0]); m().mul(a, numeral(2), f1[1]); m().mul(a, numeral(2), f2[1]); set_size(2, f1); set_size(2, f2); normalize(f1); normalize(f2); TRACE("factor", tout << "f1: "; display(tout, f1); tout << "\nf2: "; display(tout, f2); tout << "\n";); DEBUG_CODE({ scoped_numeral_vector f1f2(m()); mul(f1, f2, f1f2); SASSERT(eq(f1f2, p)); }); r.push_back(f1, k); r.push_back(f2, k); } bool manager::factor_sqf_pp(numeral_vector & p, factors & r, unsigned k, factor_params const & params) { unsigned sz = p.size(); SASSERT(sz == 0 || m().is_pos(p[sz-1])); if (sz <= 2) { // linear or constant r.push_back(p, k); return true; } else if (sz == 3) { factor_2_sqf_pp(p, r, k); return true; } else { return factor_square_free(*this, p, r, k, params); } } void manager::flip_factor_sign_if_lm_neg(numeral_vector & p, factors & r, unsigned k) { unsigned sz = p.size(); if (sz == 0) return; if (m().is_neg(p[sz - 1])) { for (unsigned i = 0; i < sz; i++) m().neg(p[i]); if (k % 2 == 1) flip_sign(r); } } bool manager::factor_core(unsigned sz, numeral const * p, factors & r, factor_params const & params) { if (sz == 0) { r.set_constant(numeral(0)); return true; } if (sz == 1) { r.set_constant(p[0]); return true; } // extract content & primitive part scoped_numeral content(m()); scoped_numeral_vector pp(m()); get_primitive_and_content(sz, p, pp, content); r.set_constant(content); // scoped_numeral_vector & C = pp; // Let C be a primitive polynomial of the form: P_1^1 * P_2^2 * ... * P_k^k, where each P_i is square free scoped_numeral_vector C_prime(m()); derivative(C, C_prime); scoped_numeral_vector A(m()), B(m()), D(m()); gcd(C, C_prime, B); bool result = true; if (is_const(B)) { // C must be of the form P_1 (square free) SASSERT(!is_const(C)); flip_factor_sign_if_lm_neg(C, r, 1); if (!factor_sqf_pp(C, r, 1, params)) result = false; } else { // B is of the form P_2 * P_3^2 * ... * P_k^{k-1} VERIFY(exact_div(C, B, A)); TRACE("factor_bug", tout << "C: "; display(tout, C); tout << "\nB: "; display(tout, B); tout << "\nA: "; display(tout, A); tout << "\n";); // A is of the form P_1 * P_2 * ... * P_k unsigned j = 1; while (!is_const(A)) { checkpoint(); TRACE("factor", tout << "factor_core main loop j: " << j << "\nA: "; display(tout, A); tout << "\nB: "; display(tout, B); tout << "\n";); // A is of the form P_j * P_{j+1} * P_{j+2} * ... * P_k // B is of the form P_{j+1} * P_{j+2}^2 * ... * P_k^{k - j - 2} gcd(A, B, D); // D is of the form P_{j+1} * P_{j+2} * ... * P_k VERIFY(exact_div(A, D, C)); // C is of the form P_j if (!is_const(C)) { flip_factor_sign_if_lm_neg(C, r, j); if (!factor_sqf_pp(C, r, j, params)) result = false; } else { TRACE("factor", tout << "const C: "; display(tout, C); tout << "\n";); SASSERT(C.size() == 1); SASSERT(m().is_one(C[0]) || m().is_minus_one(C[0])); if (m().is_minus_one(C[0]) && j % 2 == 1) flip_sign(r); } TRACE("factor_bug", tout << "B: "; display(tout, B); tout << "\nD: "; display(tout, D); tout << "\n";); VERIFY(exact_div(B, D, B)); // B is of the form P_{j+2} * ... * P_k^{k - j - 3} A.swap(D); // D is of the form P_{j+1} * P_{j+2} * ... * P_k j++; } TRACE("factor_bug", tout << "A: "; display(tout, A); tout << "\n";); SASSERT(A.size() == 1 && m().is_one(A[0])); } return result; } bool manager::factor(unsigned sz, numeral const * p, factors & r, factor_params const & params) { bool result = factor_core(sz, p, r, params); #ifndef _EXTERNAL_RELEASE IF_VERBOSE(FACTOR_VERBOSE_LVL, verbose_stream() << "(polynomial-factorization :distinct-factors " << r.distinct_factors() << ")" << std::endl;); #endif return result; } void manager::display(std::ostream & out, upolynomial_sequence const & seq, char const * var_name) const { for (unsigned i = 0; i < seq.size(); i++) { display(out, seq.size(i), seq.coeffs(i), var_name); out << "\n"; } } }; z3-z3-4.4.1/src/math/polynomial/upolynomial.h000066400000000000000000001112261260446376700210540ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: upolynomial.h Abstract: Goodies for creating and handling univariate polynomials. A dense representation is much better for Root isolation algorithms, encoding algebraic numbers, factorization, etc. We also use integers as the coefficients of univariate polynomials. Author: Leonardo (leonardo) 2011-11-29 Notes: --*/ #ifndef UPOLYNOMIAL_H_ #define UPOLYNOMIAL_H_ #include"mpzzp.h" #include"rational.h" #include"polynomial.h" #include"z3_exception.h" #include"mpbq.h" #define FACTOR_VERBOSE_LVL 1000 namespace upolynomial { typedef polynomial::factor_params factor_params; // It is used only for signing cancellation. class upolynomial_exception : public default_exception { public: upolynomial_exception(char const * msg):default_exception(msg) {} }; typedef mpz numeral; typedef mpzzp_manager numeral_manager; typedef mpzzp_manager zp_numeral_manager; typedef unsynch_mpz_manager z_numeral_manager; typedef svector numeral_vector; class core_manager { public: typedef _scoped_numeral_vector scoped_numeral_vector; typedef _scoped_numeral scoped_numeral; /** \brief Convenient vector of polynomials that manages its own memory and keeps the degree, of each polynomial. Polynomial is c*f_1^k_1*...*f_n^k_n. */ class factors { private: vector m_factors; svector m_degrees; core_manager & m_upm; numeral m_constant; unsigned m_total_factors; unsigned m_total_degree; public: factors(core_manager & upm); ~factors(); core_manager & upm() const { return m_upm; } core_manager & pm() const { return m_upm; } numeral_manager & nm() const; unsigned distinct_factors() const { return m_factors.size(); } unsigned total_factors() const { return m_total_factors; } void clear(); void reset() { clear(); } numeral_vector const & operator[](unsigned i) const { return m_factors[i]; } numeral const & get_constant() const { return m_constant; } void set_constant(numeral const & constant); unsigned get_degree() const { return m_total_degree; } unsigned get_degree(unsigned i) const { return m_degrees[i]; } void set_degree(unsigned i, unsigned degree); void push_back(numeral_vector const & p, unsigned degree); // push p to vectors and kill it void push_back_swap(numeral_vector & p, unsigned degree); void swap_factor(unsigned i, numeral_vector & p); void swap(factors & other); void multiply(numeral_vector & out) const; void display(std::ostream & out) const; friend std::ostream & operator<<(std::ostream & out, factors const & fs) { fs.display(out); return out; } }; protected: numeral_manager m_manager; numeral_vector m_basic_tmp; numeral_vector m_div_tmp1; numeral_vector m_div_tmp2; numeral_vector m_exact_div_tmp; numeral_vector m_gcd_tmp1; numeral_vector m_gcd_tmp2; numeral_vector m_CRA_tmp; #define UPOLYNOMIAL_MGCD_TMPS 6 numeral_vector m_mgcd_tmp[UPOLYNOMIAL_MGCD_TMPS]; numeral_vector m_sqf_tmp1; numeral_vector m_sqf_tmp2; numeral_vector m_pw_tmp; volatile bool m_cancel; static bool is_alias(numeral const * p, numeral_vector & buffer) { return buffer.c_ptr() != 0 && buffer.c_ptr() == p; } void neg_core(unsigned sz1, numeral const * p1, numeral_vector & buffer); void add_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void sub_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void mul_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void flip_sign_if_lm_neg(numeral_vector & buffer); void mod_gcd(unsigned sz_u, numeral const * u, unsigned sz_v, numeral const * v, numeral_vector & result); void CRA_combine_images(numeral_vector const & q, numeral const & p, numeral_vector & C, numeral & bound); public: core_manager(z_numeral_manager & m); ~core_manager(); z_numeral_manager & zm() const { return m_manager.m(); } numeral_manager & m() const { return const_cast(this)->m_manager; } /** \brief Return true if Z_p[X] */ bool modular() const { return m().modular(); } bool field() const { return m().field(); } /** \brief Return p in Z_p[X] \pre modular */ numeral const & p() const { return m().p(); } /** \brief Set manager as Z[X] */ void set_z() { m().set_z(); } /** \brief Set manager as Z_p[X] */ void set_zp(numeral const & p) { m().set_zp(p); } void set_zp(uint64 p) { m().set_zp(p); } void checkpoint(); void set_cancel(bool f); /** \brief set p size to 0. That is, p is the zero polynomial after this operation. */ void reset(numeral_vector & p); /** \brief Remove zero leading coefficients. After applying this method, we have that p is empty() or p[p.size()-1] is not zero. */ void trim(numeral_vector & p); void set_size(unsigned sz, numeral_vector & buffer); /** \brief Return the actual degree of p. */ unsigned degree(numeral_vector const & p) { unsigned sz = p.size(); return sz == 0 ? 0 : sz - 1; } /** \brief Return true if p is the zero polynomial. */ bool is_zero(numeral_vector & p) { trim(p); return p.empty(); } /** \brief Return true if p is a constant polynomial */ bool is_const(numeral_vector & p) { trim(p); return p.size() <= 1; } /** \brief Copy p to buffer. */ void set(unsigned sz, numeral const * p, numeral_vector & buffer); void set(numeral_vector & target, numeral_vector const & source) { set(source.size(), source.c_ptr(), target); } /** \brief Copy p to buffer. Coefficients of p must be integer. */ void set(unsigned sz, rational const * p, numeral_vector & buffer); /** \brief Compute the primitive part and the content of f (pp can alias f). */ void get_primitive_and_content(unsigned f_sz, numeral const * f, numeral_vector & pp, numeral & cont); void get_primitive_and_content(numeral_vector const & f, numeral_vector & pp, numeral & cont) { get_primitive_and_content(f.size(), f.c_ptr(), pp, cont); } void get_primitive(numeral_vector const & f, numeral_vector & pp) { scoped_numeral cont(m()); get_primitive_and_content(f.size(), f.c_ptr(), pp, cont); } /** \brief p := -p */ void neg(unsigned sz, numeral * p); void neg(numeral_vector & p) { neg(p.size(), p.c_ptr()); } /** \brief buffer := -p */ void neg(unsigned sz, numeral const * p, numeral_vector & buffer); void neg(numeral_vector const & p, numeral_vector & p_neg) { neg(p.size(), p.c_ptr(), p_neg); } /** \brief buffer := p1 + p2 */ void add(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void add(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { add(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); } /** \brief buffer := p1 - p2 */ void sub(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void sub(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { sub(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); } /** \brief buffer := p1 * p2 */ void mul(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void mul(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { mul(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); } /** \brief r := p^k */ void pw(unsigned sz, numeral const * p, unsigned k, numeral_vector & r); /** \brief buffer := dp/dx */ void derivative(unsigned sz1, numeral const * p, numeral_vector & buffer); void derivative(numeral_vector const & p, numeral_vector & d_p) { derivative(p.size(), p.c_ptr(), d_p); } /** \brief Divide coeffients of p by their GCD */ void normalize(unsigned sz, numeral * p); /** \brief Divide coeffients of p by their GCD */ void normalize(numeral_vector & p); /** \brief Divide the coefficients of p by b. This method assumes that every coefficient of p is a multiple of b, and b != 0. */ void div(unsigned sz, numeral * p, numeral const & b); void div(numeral_vector & p, numeral const & b) { div(p.size(), p.c_ptr(), b); } /** \brief Multiply the coefficients of p by b. This method assume b != 0. */ void mul(unsigned sz, numeral * p, numeral const & b); /** \brief Multiply the coefficients of p by b. If b == 0, it is equivalent to a reset() */ void mul(numeral_vector & p, numeral const & b); /** \brief Similar to div_rem but p1 and p2 must not be q and r. */ void div_rem_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r); /** \brief If numeral is a field, then return q and r s.t. p1 = q*p2 + r And degree(r) < degree(p2). If numeral is not a field, then return q and r s.t. (b_m)^d * p1 = q * p2 + r where b_m is the leading coefficient of p2 and d <= sz1 - sz2 + 1 if sz1 >= sz2. The output value d is irrelevant if numeral is a field. */ void div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r); /** \see div_rem */ void div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q, numeral_vector & r) { unsigned d = 0; div_rem(sz1, p1, sz2, p2, d, q, r); } void div_rem(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & q, numeral_vector & r) { div_rem(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), q, r); } /** \see div_rem */ void div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q); /** \see div_rem */ void rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & r); /** \see div_rem */ void rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r) { unsigned d = 0; rem(sz1, p1, sz2, p2, d, r); } /** \brief Signed pseudo-remainder. Alias for rem(sz1, p1, sz2, p2, r); neg(r); */ void srem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r); /** \brief Return true if p2 divides p1. */ bool divides(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2); bool divides(numeral_vector const & p1, numeral_vector const & p2) { return divides(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr()); } /** \brief Return true if p2 divides p1, and store the quotient in q. */ bool exact_div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q); bool exact_div(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & q) { return exact_div(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), q); } /** \brief Assuming that we can, make the polynomial monic by dividing with the leading coefficient. It puts the leading coefficient into lc, and it's inverse into lc_inv. */ void mk_monic(unsigned sz, numeral * p, numeral & lc, numeral & lc_inv); void mk_monic(unsigned sz, numeral * p, numeral & lc) { numeral lc_inv; mk_monic(sz, p, lc, lc_inv); m().del(lc_inv); } void mk_monic(unsigned sz, numeral * p) { numeral lc, lc_inv; mk_monic(sz, p, lc, lc_inv); m().del(lc); m().del(lc_inv); } void mk_monic(numeral_vector & p) { mk_monic(p.size(), p.c_ptr()); } /** \brief g := gcd(p1, p2) If in a field the coefficients don't matter, so we also make sure that D is monic. */ void gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g); void euclid_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g); void subresultant_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g); void gcd(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & g) { gcd(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), g); } void subresultant_gcd(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & g) { subresultant_gcd(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), g); } /** \brief g := square free part of p */ void square_free(unsigned sz, numeral const * p, numeral_vector & g); /** \brief Return true if p is a square-free polynomial. */ bool is_square_free(unsigned sz, numeral const * p); /** \brief Return true if p is a square-free polynomial. */ bool is_square_free(numeral_vector const & p) { return is_square_free(p.size(), p.c_ptr()); } /** \brief Convert a multi-variate polynomial (that is) actually representing an univariate polynomial into a vector of coefficients. */ template void to_numeral_vector(polynomial_ref const & p, numeral_vector & r) { typename polynomial_ref::manager & pm = p.m(); SASSERT(pm.is_univariate(p)); polynomial_ref np(pm); np = pm.normalize(p); unsigned sz = pm.size(p); unsigned deg = pm.total_degree(p); r.reserve(deg+1); for (unsigned i = 0; i <= deg; i++) { m().reset(r[i]); } for (unsigned i = 0; i < sz; i++) { unsigned k = pm.total_degree(pm.get_monomial(p, i)); SASSERT(k <= deg); m().set(r[k], pm.coeff(p, i)); } set_size(deg+1, r); } /** \brief Convert a multi-variate polynomial in [x, y1, ..., yn] to a univariate polynomial in just x by removing everything multivariate. */ template void to_numeral_vector(polynomial_ref const & p, polynomial::var x, numeral_vector & r) { typename polynomial_ref::manager & pm = p.m(); polynomial_ref np(pm); np = pm.normalize(p); unsigned sz = pm.size(p); unsigned deg = pm.degree(p, x); r.reserve(deg+1); for (unsigned i = 0; i <= deg; i++) { m().reset(r[i]); } for (unsigned i = 0; i < sz; i++) { typename polynomial::monomial * mon = pm.get_monomial(p, i); if (pm.size(mon) == 0) { m().set(r[0], pm.coeff(p, i)); } else if (pm.size(mon) == 1 && pm.get_var(mon, 0) == x) { unsigned m_deg_x = pm.degree(mon, 0); m().set(r[m_deg_x], pm.coeff(p, i)); } } set_size(deg+1, r); } /** \brief Extended GCD This method assumes that numeral is a field. It determines U, V, D such that A*U + B*V = D and D is the GCD of A and B. Since in a field the coefficients don't matter, we also make sure that D is monic. */ void ext_gcd(unsigned szA, numeral const * A, unsigned szB, numeral const * B, numeral_vector & U, numeral_vector & V, numeral_vector & D); void ext_gcd(numeral_vector const & A, numeral_vector const & B, numeral_vector & U, numeral_vector & V, numeral_vector & D) { ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D); } bool eq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2); bool eq(numeral_vector const & p1, numeral_vector const & p2) { return eq(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr()); } void display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x", bool use_star = false) const; void display(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const { display(out, p.size(), p.c_ptr(), var_name); } void display_star(std::ostream & out, unsigned sz, numeral const * p) { display(out, sz, p, "x", true); } void display_star(std::ostream & out, numeral_vector const & p) { display_star(out, p.size(), p.c_ptr()); } void display_smt2(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x") const; void display_smt2(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const { return display_smt2(out, p.size(), p.c_ptr(), var_name); } }; class scoped_set_z { core_manager & m; bool m_modular; core_manager::scoped_numeral m_p; public: scoped_set_z(core_manager & _m):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_z(); } ~scoped_set_z() { if (m_modular) m.set_zp(m_p); } }; class scoped_set_zp { core_manager & m; bool m_modular; core_manager::scoped_numeral m_p; public: scoped_set_zp(core_manager & _m, numeral const & p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } scoped_set_zp(core_manager & _m, uint64 p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } ~scoped_set_zp() { if (m_modular) m.set_zp(m_p); else m.set_z(); } }; class manager; typedef core_manager z_manager; typedef core_manager zp_manager; typedef z_manager::factors factors; typedef zp_manager::factors zp_factors; typedef svector numeral_vector; class scoped_numeral_vector : public _scoped_numeral_vector { public: scoped_numeral_vector(numeral_manager & m):_scoped_numeral_vector(m) {} scoped_numeral_vector(manager & m); }; class upolynomial_sequence { numeral_vector m_seq_coeffs; // coefficients of all polynomials in the sequence unsigned_vector m_begins; // start position (in m_seq_coeffs) of each polynomial in the sequence unsigned_vector m_szs; // size of each polynomial in the sequence friend class manager; public: /** \brief Add a new polynomial to the sequence. The contents of p is erased. */ void push(unsigned sz, numeral * p); /** \brief Add a new polynomial to the sequence. The contents of p is preserved. */ void push(numeral_manager & m, unsigned sz, numeral const * p); /** \brief Return the number of polynomials in the sequence. */ unsigned size() const { return m_szs.size(); } /** \brief Return the vector of coefficients for the i-th polynomial in the sequence. */ numeral const * coeffs(unsigned i) const { return m_seq_coeffs.c_ptr() + m_begins[i]; } /** \brief Return the size of the i-th polynomial in the sequence. */ unsigned size(unsigned i) const { return m_szs[i]; } }; class scoped_upolynomial_sequence : public upolynomial_sequence { manager & m_manager; public: scoped_upolynomial_sequence(manager & m):m_manager(m) {} ~scoped_upolynomial_sequence(); }; class manager : public core_manager { numeral_vector m_db_tmp; numeral_vector m_dbab_tmp1; numeral_vector m_dbab_tmp2; numeral_vector m_tr_tmp; numeral_vector m_push_tmp; int sign_of(numeral const & c); struct drs_frame; void pop_top_frame(numeral_vector & p_stack, svector & frame_stack); void push_child_frames(unsigned sz, numeral const * p, numeral_vector & p_stack, svector & frame_stack); void add_isolating_interval(svector const & frame_stack, mpbq_manager & bqm, mpbq_vector & lowers, mpbq_vector & uppers); void add_root(svector const & frame_stack, mpbq_manager & bqm, mpbq_vector & roots); void drs_isolate_0_1_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void drs_isolate_roots(unsigned sz, numeral * p, numeral & U, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void drs_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void sqf_nz_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void sturm_seq_core(upolynomial_sequence & seq); enum location { PLUS_INF, MINUS_INF, ZERO, MPBQ }; template unsigned sign_variations_at_core(upolynomial_sequence const & seq, mpbq const & b); void flip_sign(factors & r); void flip_factor_sign_if_lm_neg(numeral_vector & p, factors & r, unsigned k); void factor_2_sqf_pp(numeral_vector & p, factors & r, unsigned k); bool factor_sqf_pp(numeral_vector & p, factors & r, unsigned k, factor_params const & params); bool factor_core(unsigned sz, numeral const * p, factors & r, factor_params const & params); public: manager(z_numeral_manager & m):core_manager(m) {} ~manager(); void reset(numeral_vector & p) { core_manager::reset(p); } void reset(upolynomial_sequence & seq); /** \brief Return true if 0 is a root of p. */ bool has_zero_roots(unsigned sz, numeral const * p) { SASSERT(sz > 0); return m().is_zero(p[0]); } /** \brief Store in buffer a polynomial that has the same roots of p but the zero roots. We have that: forall u, p(u) = 0 and u != 0 implies buffer(u) = 0 forall u, buffer(u) = 0 implies p(u) = 0 This method assumes p is not the zero polynomial */ void remove_zero_roots(unsigned sz, numeral const * p, numeral_vector & buffer); /** \brief Return true if 1/2 is a root of p. */ bool has_one_half_root(unsigned sz, numeral const * p); /** \brief Store in buffer a polynomial that has the same roots of p, but a 1/2 root is removed. This method assumes that 1/2 is a root of p. */ void remove_one_half_root(unsigned sz, numeral const * p, numeral_vector & buffer); /** \brief Return the number of sign changes in the coefficients of p. Zero coefficients are ignored. */ unsigned sign_changes(unsigned sz, numeral const * p); /** \brief Return the descartes bound for the number of roots of p in the interval (0, +oo) Result: 0 - p has no roots in (0,1) 1 - p has one root in (0,1) >1 - p has more than one root in (0,1) */ unsigned descartes_bound(unsigned sz, numeral const * p); /** \brief Return the descartes bound for the number of roots of p in the interval (0, 1) \see descartes_bound */ unsigned descartes_bound_0_1(unsigned sz, numeral const * p); /** \brief Return the descartes bound for the number of roots of p in the interval (a, b) \see descartes_bound */ unsigned descartes_bound_a_b(unsigned sz, numeral const * p, mpbq_manager & m, mpbq const & a, mpbq const & b); /** \brief p(x) := p(x+1) */ void translate(unsigned sz, numeral * p); void translate(unsigned sz, numeral const * p, numeral_vector & buffer) { set(sz, p, buffer); translate(sz, buffer.c_ptr()); } /** \brief p(x) := p(x+2^k) */ void translate_k(unsigned sz, numeral * p, unsigned k); void translate_k(unsigned sz, numeral const * p, unsigned k, numeral_vector & buffer) { set(sz, p, buffer); translate_k(sz, buffer.c_ptr(), k); } /** \brief p(x) := p(x+c) */ void translate_z(unsigned sz, numeral * p, numeral const & c); void translate_z(unsigned sz, numeral const * p, numeral const & c, numeral_vector & buffer) { set(sz, p, buffer); translate_z(sz, buffer.c_ptr(), c); } /** \brief p(x) := p(x+b) where b = c/2^k buffer := (2^k)^n * p(x + c/(2^k)) */ void translate_bq(unsigned sz, numeral * p, mpbq const & b); void translate_bq(unsigned sz, numeral const * p, mpbq const & b, numeral_vector & buffer) { set(sz, p, buffer); translate_bq(sz, buffer.c_ptr(), b); } /** \brief p(x) := p(x+b) where b = c/d buffer := d^n * p(x + c/d) */ void translate_q(unsigned sz, numeral * p, mpq const & b); void translate_q(unsigned sz, numeral const * p, mpq const & b, numeral_vector & buffer) { set(sz, p, buffer); translate_q(sz, buffer.c_ptr(), b); } /** \brief p(x) := 2^n*p(x/2) where n = sz-1 */ void compose_2n_p_x_div_2(unsigned sz, numeral * p); /** \brief p(x) := (2^k)^n * p(x/(2^k)) */ void compose_2kn_p_x_div_2k(unsigned sz, numeral * p, unsigned k); /** \brief p(x) := p(2^k * x) If u is a root of old(p), then u/2^k is a root of p */ void compose_p_2k_x(unsigned sz, numeral * p, unsigned k); /** \brief p(x) := p(b * x) If u is a root of old(p), then u/b is a root of p */ void compose_p_b_x(unsigned sz, numeral * p, numeral const & b); /** \brief p(x) := p(b * x) If u is a root of old(p), then u/b is a root of p Let b be of the form c/(2^k), then this operation is equivalent to: (2^k)^n*p(c*x/(2^k)) Let old(p) be of the form: a_n * x^n + a_{n-1}*x^{n-1} + ... + a_1 * x + a_0 Then p is of the form: a_n * c^n * x^n + a_{n-1} * c^{n-1} * 2^k * x^{n-1} + ... + a_1 * c * (2^k)^(n-1) * x + a_0 */ void compose_p_b_x(unsigned sz, numeral * p, mpbq const & b); /** \brief p(x) := p(q*x) */ void compose_p_q_x(unsigned sz, numeral * p, mpq const & q); /** \brief p(x) := a^n * p(x/a) */ void compose_an_p_x_div_a(unsigned sz, numeral * p, numeral const & a); /** \brief p(x) := p(-x) */ void p_minus_x(unsigned sz, numeral * p); /** \brief p(x) := x^n * p(1/x) */ void p_1_div_x(unsigned sz, numeral * p); /** \brief Evaluate the sign of p(b) */ int eval_sign_at(unsigned sz, numeral const * p, mpbq const & b); /** \brief Evaluate the sign of p(b) */ int eval_sign_at(unsigned sz, numeral const * p, mpq const & b); /** \brief Evaluate the sign of p(b) */ int eval_sign_at(unsigned sz, numeral const * p, mpz const & b); /** \brief Evaluate the sign of p(0) */ int eval_sign_at_zero(unsigned sz, numeral const * p); /** \brief Evaluate the sign of p(+oo) */ int eval_sign_at_plus_inf(unsigned sz, numeral const * p); /** \brief Evaluate the sign of p(-oo) */ int eval_sign_at_minus_inf(unsigned sz, numeral const * p); /** \brief Evaluate the sign variations in the polynomial sequence at -oo */ unsigned sign_variations_at_minus_inf(upolynomial_sequence const & seq); /** \brief Evaluate the sign variations in the polynomial sequence at +oo */ unsigned sign_variations_at_plus_inf(upolynomial_sequence const & seq); /** \brief Evaluate the sign variations in the polynomial sequence at 0 */ unsigned sign_variations_at_zero(upolynomial_sequence const & seq); /** \brief Evaluate the sign variations in the polynomial sequence at b */ unsigned sign_variations_at(upolynomial_sequence const & seq, mpbq const & b); /** \brief Return an upper bound U for all roots of p. U is a positive value. We have that if u is a root of p, then |u| < U */ void root_upper_bound(unsigned sz, numeral const * p, numeral & U); unsigned knuth_positive_root_upper_bound(unsigned sz, numeral const * p); unsigned knuth_negative_root_upper_bound(unsigned sz, numeral const * p); /** \brief Return k s.t. for any nonzero root alpha of p(x): |alpha| > 1/2^k */ unsigned nonzero_root_lower_bound(unsigned sz, numeral const * p); /** \brief Isolate roots of a square free polynomial p. The result is stored in three vectors: roots, lowers and uppers. The vector roots contains actual roots of p. The vectors lowers and uppers have the same size, and For all i in [0, lowers.size()), we have that there is only and only one root of p in the interval (lowers[i], uppers[i]). Every root of p in roots or in an interval (lowers[i], uppers[i]) The total number of roots of p is roots.size() + lowers.size() \pre p is not the zero polynomial, that is, sz > 0 */ void sqf_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); /** \brief Isolate roots of an arbitrary polynomial p. \see sqf_isolate_roots. \pre p is not the zero polynomial, that is, sz > 0 */ void isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void drs_isolate_roots(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void sturm_isolate_roots_core(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void sturm_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); /** \brief Compute the sturm sequence for p1 and p2. */ void sturm_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq); /** \brief Compute the sturm sequence for p and p'. */ void sturm_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq); /** \brief Compute the sturm tarski sequence for p1 and p1'*p2. */ void sturm_tarski_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq); /** \brief Compute the Fourier sequence for p. */ void fourier_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq); /** \brief Convert an isolating interval into a refinable one. See comments in upolynomial.cpp. */ bool isolating2refinable(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b); // // Interval refinement procedures // They all assume p is square free and (a, b) is a refinable isolating interval. // // Return TRUE, if interval was squeezed, and new interval is stored in (a,b). // Return FALSE, if the actual root was found, it is stored in a. // // See upolynomial.cpp for additional comments bool refine_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b); bool refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b); bool refine_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k); bool refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k); ///////////////////// /** \brief Convert a isolating (refinable) rational interval into a isolating refinable binary rational interval. Return TRUE, if interval was found and the result is stored in (c, d). Return FALSE, if the actual root was found, it is stored in c. */ bool convert_q2bq_interval(unsigned sz, numeral const * p, mpq const & a, mpq const & b, mpbq_manager & bqm, mpbq & c, mpbq & d); /** \brief Given a polynomial p, and a lower bound l. Return the root id i. That is, the first root u > l is the i-th root of p. */ unsigned get_root_id(unsigned sz, numeral const * p, mpbq const & l); /** \brief Make sure that isolating interval (a, b) for p does not contain zero. Return TRUE, if updated (a, b) does not contain zero. Return FALSE, if zero is a root of p */ bool normalize_interval_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & m, mpbq & a, mpbq & b); /** \brief Similar to normalize_interval_core, but sign_a does not need to be provided. */ bool normalize_interval(unsigned sz, numeral const * p, mpbq_manager & m, mpbq & a, mpbq & b); /** \brief Return true if all irreducible factors were found. That is, if the result if false, there is no guarantee that the factors in r are irreducible. This can happen when limits (e.g., on the search space size) are set in params. */ bool factor(unsigned sz, numeral const * p, factors & r, factor_params const & params = factor_params()); bool factor(numeral_vector const & p, factors & r, factor_params const & params = factor_params()) { return factor(p.size(), p.c_ptr(), r, params); } void display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x", bool use_star = false) const { return core_manager::display(out, sz, p, var_name); } void display(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const { return core_manager::display(out, p, var_name); } void display(std::ostream & out, upolynomial_sequence const & seq, char const * var_name = "x") const; }; }; #endif z3-z3-4.4.1/src/math/polynomial/upolynomial_factorization.cpp000066400000000000000000001450041260446376700243440ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial_factorization.cpp Abstract: Implementation of polynomial factorization. Author: Dejan (t-dejanj) 2011-11-15 Notes: [1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal, 46(8-10):1853-1859, 1967. [2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third edition, 1997. [3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993. --*/ #include"trace.h" #include"util.h" #include"upolynomial_factorization_int.h" #include"prime_generator.h" using namespace std; namespace upolynomial { // get the prime as unsigned while throwing exceptions if anything goes bad` unsigned get_p_from_manager(zp_numeral_manager const & zp_nm) { z_numeral_manager & nm = zp_nm.m(); numeral const & p = zp_nm.p(); if (!nm.is_uint64(p)) { throw upolynomial_exception("The prime number attempted in factorization is too big!"); } uint64 p_uint64 = nm.get_uint64(p); unsigned p_uint = static_cast(p_uint64); if (((uint64)p_uint) != p_uint64) { throw upolynomial_exception("The prime number attempted in factorization is too big!"); } return p_uint; } /** \brief The Q-I matrix from Berelkamp's algorithm [1,2]. Given a polynomial f = \sum f_k x^k of degree n, with f_i in Z_p[x], the i-th row of Q is a representation of x^(p*i) modulo f, i.e. x^(p*i) modulo f = \sum_j q[i][j] x^j If f is of degree n, the matrix is square nxn. When the this matrix is constructed we obtain Q - I, because this is what we need in the algorithm. After construction, the null space vectors can be extracted one-by one using the next_null_space_vector method. */ class berlekamp_matrix { zp_manager & m_upm; mpzzp_manager & m_zpm; svector m_matrix; unsigned m_size; unsigned m_null_row; // 0, ..., m_size - 1, state for null vectors svector m_column_pivot; // position of pivots in the columns svector m_row_pivot; // position of pivots in the rows mpz & get(unsigned i, unsigned j) { SASSERT(i < m_size && j < m_size); return m_matrix[i*m_size + j]; } mpz const & get(unsigned i, unsigned j) const { SASSERT(i < m_size && j < m_size); return m_matrix[i*m_size + j]; } public: /** \brief Construct the matrix as explained above, f should be in the Z_p. */ berlekamp_matrix(zp_manager & upm, numeral_vector const & f) : m_upm(upm), m_zpm(upm.m()), m_size(m_upm.degree(f)), m_null_row(1), m_column_pivot(m_size, -1), m_row_pivot(m_size, -1) { unsigned p = get_p_from_manager(m_zpm); TRACE("polynomial::factorization::bughunt", tout << "polynomial::berlekamp_matrix("; m_upm.display(tout, f); tout << ", " << p << ")" << endl;); // the first row is always the vector [1, 0, ..., 0], since x^0 = 0 (modulo f) m_matrix.push_back(1); for (unsigned j = 0; j < m_size; ++ j) { m_matrix.push_back(0); } // the rest of the rows, we can get as follows, given f = x^n + f_{n-1} x^{n-1} + ... + f_1 x + f_0 // if x^k = \sum a_{k,j} x^j (modulo p), hence 0 <= j <= n-1 then // x^{k+1} = a_{k,n-1}(-f_{n-1} x^{n-1} + ... + f_0) + a_{k,n-2} x^{n-1} + ... + a_{k, 0} x // so we can compute a_{k+1,j} = a_{k, j-1} - a_{k,n-1}*f_j // every p-th row we add to the matrix scoped_numeral tmp(m_zpm); unsigned row = 0, previous_row = 0; for (unsigned k = 1; true; previous_row = row, ++ k) { // add another row if we need it if (k % p == 1) { if (++ row >= m_size) { break; } for (unsigned j = 0; j < m_size; ++ j) { m_matrix.push_back(0); } } // the multiplier m_zpm.set(tmp, get(previous_row, m_size - 1)); // go down the row and shift it for (unsigned j = m_size - 1; j > 0; -- j) { m_zpm.submul(get(previous_row, j-1), tmp, f[j], get(row, j)); } // add the 0 element (same formula with a_{k,-1} = 0) m_zpm.mul(f[0], tmp, get(row, 0)); m_zpm.neg(get(row, 0)); } // do Q - I for (unsigned i = 0; i < m_size; ++ i) { m_zpm.dec(get(i, i)); } TRACE("polynomial::factorization::bughunt", tout << "polynomial::berlekamp_matrix():" << endl; display(tout); tout << endl;); } /** \brief Destructor, just removing the numbers */ ~berlekamp_matrix() { for (unsigned k = 0; k < m_matrix.size(); ++ k) { m_zpm.del(m_matrix[k]); } } /** \brief 'Disagonalizes' the matrix using only column operations. The reusling matrix will have -1 at pivot elements. Returns the rank of the null space. */ unsigned diagonalize() { scoped_numeral multiplier(m_zpm); unsigned null_rank = 0; for (unsigned i = 0; i < m_size; ++ i) { // get the first non-zero entry in the m_null_row row bool column_found = false; for (unsigned j = 0; j < m_size; ++ j) { if (m_column_pivot[j] < 0 && !m_zpm.is_zero(get(i, j))) { column_found = true; m_column_pivot[j] = i; m_row_pivot[i] = j; // found a pivot, to make it -1 we compute the multuplier -p^-1 m_zpm.set(multiplier, get(i, j)); m_zpm.inv(multiplier); m_zpm.neg(multiplier); // multiply the pivot column with the multiplier for (unsigned k = m_null_row; k < m_size; ++ k) { m_zpm.mul(get(k, j), multiplier, get(k, j)); } // pivot is -1 so we can add it to the rest of the columns to eliminate the row for (unsigned other_j = 0; other_j < m_size; ++ other_j) { if (j != other_j) { m_zpm.set(multiplier, get(i, other_j)); for (unsigned k = m_null_row; k < m_size; ++ k) { m_zpm.addmul(get(k, other_j), multiplier, get(k, j), get(k, other_j)); } } } } } if (!column_found) { null_rank ++; } } TRACE("polynomial::factorization::bughunt", tout << "polynomial::diagonalize():" << endl; display(tout); tout << endl;); return null_rank; } /** If rank of the matrix is n - r, we are interested in linearly indeprendent vectors v_1, ..., v_r (the basis of the null space), such that v_k A = 0. This method will give one at a time. The method returns true if vector has been computed properly. The first vector [1, 0, ..., 0] is ignored (m_null_row starts from 1). */ bool next_null_space_vector(numeral_vector & v) { SASSERT(v.size() <= m_size); v.resize(m_size); for (; m_null_row < m_size; ++ m_null_row) { if (m_row_pivot[m_null_row] < 0) { // output the vector for (unsigned j = 0; j < m_size; ++ j) { if (m_row_pivot[j] >= 0) { m_zpm.set(v[j], get(m_null_row, m_row_pivot[j])); } else { if (j == m_null_row) { m_zpm.set(v[j], 1); } else { m_zpm.set(v[j], 0); } } } ++ m_null_row; return true; } } // didn't find the vector return false; } /** \brief Display the matrix on the output stream. */ void display(std::ostream & out) const { for (unsigned i = 0; i < m_matrix.size() / m_size; ++ i) { for (unsigned j = 0; j < m_size; ++ j) { out << m_zpm.to_string(get(i, j)) << "\t"; } out << endl; } } }; /** See [3] p. 125. */ void zp_square_free_factor(zp_manager & upm, numeral_vector const & f, zp_factors & sq_free_factors) { zp_numeral_manager & nm = upm.m(); unsigned p = get_p_from_manager(upm.m()); TRACE("polynomial::factorization", tout << "polynomial::square_free_factor("; upm.display(tout, f); tout << ") over Z_" << p << endl;); scoped_numeral_vector div_tmp(nm); // [initialize] T_0 = f, e = 1 // trim and get the make it monic if not already SASSERT(f.size() > 1); scoped_numeral_vector T_0(nm); upm.set(f.size(), f.c_ptr(), T_0); scoped_numeral constant(nm); upm.mk_monic(T_0.size(), T_0.c_ptr(), constant); sq_free_factors.set_constant(constant); TRACE("polynomial::factorization::bughunt", tout << "Initial factors: " << sq_free_factors << endl; tout << "R. = GF(" << p << ")['x']" << endl; tout << "T_0 = "; upm.display(tout, T_0); tout << endl; ); unsigned e = 1; // we repeat until we get a constant scoped_numeral_vector T_0_d(nm); scoped_numeral_vector T(nm); scoped_numeral_vector V(nm); scoped_numeral_vector W(nm); scoped_numeral_vector A_ek(nm); while (T_0.size() > 1) { // [initialize e-loop] T = gcd(T_0, T_0'), V / T_0/T, k = 0 unsigned k = 0; TRACE("polynomial::factorization::bughunt", tout << "k = 0" << endl;); // T_0_d = T_0' upm.derivative(T_0.size(), T_0.c_ptr(), T_0_d); TRACE("polynomial::factorization::bughunt", tout << "T_0_d = T_0.derivative(x)" << endl; tout << "T_0_d == "; upm.display(tout, T_0_d); tout << endl; ); // T = gcd(T_0, T_0') upm.gcd(T_0.size(), T_0.c_ptr(), T_0_d.size(), T_0_d.c_ptr(), T); TRACE("polynomial::factorization::bughunt", tout << "T = T_0.gcd(T_0_d)" << endl; tout << "T == "; upm.display(tout, T); tout << endl; ); // V = T_0 / T upm.div(T_0.size(), T_0.c_ptr(), T.size(), T.c_ptr(), V); TRACE("polynomial::factorization::bughunt", tout << "V = T_0.quo_rem(T)[0]" << endl; tout << "V == "; upm.display(tout, V); tout << endl; ); while (V.size() > 1) { // [special case] if ((++k) % p == 0) { ++ k; // T = T/V upm.div(T.size(), T.c_ptr(), V.size(), V.c_ptr(), T); TRACE("polynomial::factorization::bughunt", tout << "T = T.quo_rem(V)[0]" << endl; tout << "T == "; upm.display(tout, T); tout << endl; ); } // [compute A_ek] // W = gcd(T, V) upm.gcd(T.size(), T.c_ptr(), V.size(), V.c_ptr(), W); TRACE("polynomial::factorization::bughunt", tout << "W = T.gcd(V)" << endl; upm.display(tout, W); tout << endl; ); // A_ek = V/W upm.div(V.size(), V.c_ptr(), W.size(), W.c_ptr(), A_ek); TRACE("polynomial::factorization::bughunt", tout << "A_ek = V.quo_rem(W)[0]" << endl; tout << "A_ek == "; upm.display(tout, A_ek); tout << endl; ); // V = W V.swap(W); TRACE("polynomial::factorization::bughunt", tout << "V = W" << endl; tout << "V == "; upm.display(tout, V); tout << endl; ); // T = T/V upm.div(T.size(), T.c_ptr(), V.size(), V.c_ptr(), T); TRACE("polynomial::factorization::bughunt", tout << "T = T.quo_rem(V)[0]" << endl; tout << "T == "; upm.display(tout, T); tout << endl; ); // if not constant, we output it if (A_ek.size() > 1) { TRACE("polynomial::factorization::bughunt", tout << "Factor: ("; upm.display(tout, A_ek); tout << ")^" << e*k << endl;); sq_free_factors.push_back(A_ek, e*k); } } // [finished e-loop] T_0 = \sum_{p div j} t_j x^j, set T_0 \sum_{p div j} t_j x^{j/p}, e = pe e *= p; T_0.reset(); for (unsigned deg_p = 0; deg_p < T.size(); deg_p += p) { T_0.push_back(numeral()); nm.set(T_0.back(), T[deg_p]); } TRACE("polynomial::factorization::bughunt", tout << "T_0 = "; upm.display(tout, T_0); tout << endl; ); } TRACE("polynomial::factorization", tout << "polynomial::square_free_factor("; upm.display(tout, f); tout << ") => " << sq_free_factors << endl;); } bool zp_factor(zp_manager & upm, numeral_vector const & f, zp_factors & factors) { unsigned p = get_p_from_manager(upm.m()); TRACE("polynomial::factorization", tout << "polynomial::factor("; upm.display(tout, f); tout << ") over Z_" << p << endl;); // get the sq-free parts (all of them will be monic) zp_factors sq_free_factors(upm); zp_square_free_factor(upm, f, sq_free_factors); // factor the sq-free parts individually for (unsigned i = 0; i < sq_free_factors.distinct_factors(); ++ i) { unsigned j = factors.distinct_factors(); if (upm.degree(sq_free_factors[i]) > 1) { zp_factor_square_free(upm, sq_free_factors[i], factors); // monic from aq-free decomposition for (; j < factors.distinct_factors(); ++ j) { factors.set_degree(j, sq_free_factors.get_degree(i)*factors.get_degree(j)); } } else { factors.push_back(sq_free_factors[i], sq_free_factors.get_degree(i)); } } // add the constant factors.set_constant(sq_free_factors.get_constant()); TRACE("polynomial::factorization", tout << "polynomial::factor("; upm.display(tout, f); tout << ") => " << factors << endl;); return factors.total_factors() > 1; } bool zp_factor_square_free(zp_manager & upm, numeral_vector const & f, zp_factors & factors) { return zp_factor_square_free_berlekamp(upm, f, factors, false); } bool zp_factor_square_free_berlekamp(zp_manager & upm, numeral_vector const & f, zp_factors & factors, bool randomized) { SASSERT(upm.degree(f) > 1); mpzzp_manager & zpm = upm.m(); unsigned p = get_p_from_manager(zpm); TRACE("polynomial::factorization", tout << "upolynomial::factor_square_free_berlekamp("; upm.display(tout, f); tout << ", " << p << ")" << endl;); SASSERT(zpm.is_one(f.back())); // construct the berlekamp Q matrix to get the null space berlekamp_matrix Q_I(upm, f); // copy the inital polynomial to factors unsigned first_factor = factors.distinct_factors(); factors.push_back(f, 1); // rank of the null-space (and the number of factors) unsigned r = Q_I.diagonalize(); if (r == 1) { // since r == 1 == number of factors, then f is irreducible TRACE("polynomial::factorization", tout << "upolynomial::factor_square_free_berlekamp("; upm.display(tout, f); tout << ", " << p << ") => " << factors << endl;); return false; } TRACE("polynomial::factorization::bughunt", tout << "upolynomial::factor_square_free_berlekamp(): computing factors, expecting " << r << endl;); scoped_numeral_vector gcd(zpm); scoped_numeral_vector div(zpm); // process the null space vectors (skip first one, it's [1, 0, ..., 0]) while generating the factors unsigned d = upm.degree(f); scoped_numeral_vector v_k(zpm); while (Q_I.next_null_space_vector(v_k)) { TRACE("polynomial::factorization::bughunt", tout << "null vector: "; for(unsigned j = 0; j < d; ++ j) { tout << zpm.to_string(v_k[j]) << " "; } tout << endl; ); upm.trim(v_k); // TRACE("polynomial::factorization", tout << "v_k = "; upm.display(tout, v_k); tout << endl;); unsigned current_factor_end = factors.distinct_factors(); for (unsigned current_factor_i = first_factor; current_factor_i < current_factor_end; ++ current_factor_i) { // we have v such that vQ = v, viewing v as a polynomial, we get that v^n - v = 0 (mod f) // since v^n -v = v*(v-1)*...*(v - p-1) we compute the gcd(v - s, f) to extract the // factors. it also holds that g = \prod gcd(v - s, f), so we just accumulate them // if it's of degree 1, we're done (have to index the array as we are adding to it), as if (factors[current_factor_i].size() == 2) { continue; } for (unsigned s = 0; s < p; ++ s) { numeral_vector const & current_factor = factors[current_factor_i]; // we just take one off v_k each time to get all of them zpm.dec(v_k[0]); // get the gcd upm.gcd(v_k.size(), v_k.c_ptr(), current_factor.size(), current_factor.c_ptr(), gcd); // if the gcd is 1, or the the gcd is f, we just ignroe it if (gcd.size() != 1 && gcd.size() != current_factor.size()) { // get the divisor also (no need to normalize the div, both are monic) upm.div(current_factor.size(), current_factor.c_ptr(), gcd.size(), gcd.c_ptr(), div); TRACE("polynomial::factorization::bughunt", tout << "current_factor = "; upm.display(tout, current_factor); tout << endl; tout << "gcd_norm = "; upm.display(tout, gcd); tout << endl; tout << "div = "; upm.display(tout, div); tout << endl; ); // continue with the rest factors.swap_factor(current_factor_i, div); // add the new factor(s) factors.push_back(gcd, 1); } // at the point where we have all the factors, we are done if (factors.distinct_factors() - first_factor == r) { TRACE("polynomial::factorization", tout << "polynomial::factor("; upm.display(tout, f); tout << ", " << p << ") => " << factors << " of degree " << factors.get_degree() << endl;); return true; } } } } // should never get here SASSERT(false); return true; } /** Check if the hensel lifting was correct, i.e. that C = A*B (mod br). */ bool check_hansel_lift(z_manager & upm, numeral_vector const & C, numeral const & a, numeral const & b, numeral const & r, numeral_vector const & A, numeral_vector const & B, numeral_vector const & A_lifted, numeral_vector const & B_lifted) { z_numeral_manager & nm = upm.zm(); scoped_mpz br(nm); nm.mul(b, r, br); zp_manager br_upm(upm.zm()); br_upm.set_zp(br); if (A_lifted.size() != A.size()) return false; if (B_lifted.size() != B.size()) return false; if (!nm.eq(A.back(), A_lifted.back())) return false; // test1: check that C = A_lifted * B_lifted (mod b*r) scoped_mpz_vector test1(nm); upm.mul(A_lifted.size(), A_lifted.c_ptr(), B_lifted.size(), B_lifted.c_ptr(), test1); upm.sub(C.size(), C.c_ptr(), test1.size(), test1.c_ptr(), test1); to_zp_manager(br_upm, test1); if (test1.size() != 0) { TRACE("polynomial::factorization::bughunt", tout << "sage: R. = ZZ['x']" << endl; tout << "sage: A = "; upm.display(tout, A); tout << endl; tout << "sage: B = "; upm.display(tout, B); tout << endl; tout << "sage: C = "; upm.display(tout, C); tout << endl; tout << "sage: test1 = C - AB" << endl; tout << "sage: print test1.change_ring(GF(" << nm.to_string(br) << "))" << endl; tout << "sage: print 'expected 0'" << endl; ); return false; } zp_manager b_upm(nm); b_upm.set_zp(b); // test2: A_lifted = A (mod b) scoped_mpz_vector test2a(nm), test2b(nm); to_zp_manager(b_upm, A, test2a); to_zp_manager(b_upm, A_lifted, test2b); if (!upm.eq(test2a, test2b)) { return false; } // test3: B_lifted = B (mod b) scoped_mpz_vector test3a(nm), test3b(nm); to_zp_manager(b_upm, B, test3a); to_zp_manager(b_upm, B_lifted, test3b); if (!upm.eq(test3a, test3b)) { return false; } return true; } /** Performs a Hensel lift of A and B in Z_a to Z_b, where p is prime and and a = p^{a_k}, b = p^{b_k}, r = (a, b), with the following assumptions: (1) UA + VB = 1 (mod a) (2) C = A*B (mod b) (3) (l(A), r) = 1 (importand in order to divide by A, i.e. to invert l(A)) (4) deg(A) + deg(B) = deg(C) The output of is two polynomials A1, B1 such that A1 = A (mod b), B1 = B (mod b), l(A1) = l(A), deg(A1) = deg(A), deg(B1) = deg(B) and C = A1 B1 (mod b*r). Such A1, B1 are unique if r is prime. See [3] p. 138. */ void hensel_lift(z_manager & upm, numeral const & a, numeral const & b, numeral const & r, numeral_vector const & U, numeral_vector const & A, numeral_vector const & V, numeral_vector const & B, numeral_vector const & C, numeral_vector & A_lifted, numeral_vector & B_lifted) { z_numeral_manager & nm = upm.zm(); TRACE("polynomial::factorization::bughunt", tout << "polynomial::hensel_lift("; tout << "a = " << nm.to_string(a) << ", "; tout << "b = " << nm.to_string(b) << ", "; tout << "r = " << nm.to_string(r) << ", "; tout << "U = "; upm.display(tout, U); tout << ", "; tout << "A = "; upm.display(tout, A); tout << ", "; tout << "V = "; upm.display(tout, V); tout << ", "; tout << "B = "; upm.display(tout, B); tout << ", "; tout << "C = "; upm.display(tout, C); tout << ")" << endl; ); zp_manager r_upm(nm); r_upm.set_zp(r); SASSERT(upm.degree(C) == upm.degree(A) + upm.degree(B)); SASSERT(upm.degree(U) < upm.degree(B) && upm.degree(V) < upm.degree(A)); // by (2) C = AB (mod b), hence (C - AB) is divisible by b // define thus let f = (C - AB)/b in Z_r scoped_numeral_vector f(upm.m()); upm.mul(A.size(), A.c_ptr(), B.size(), B.c_ptr(), f); upm.sub(C.size(), C.c_ptr(), f.size(), f.c_ptr(), f); upm.div(f, b); to_zp_manager(r_upm, f); TRACE("polynomial::factorization", tout << "f = "; upm.display(tout, f); tout << endl; ); // we need to get A1 = A (mod b), B1 = B (mode b) so we know that we need // A1 = A + b*S, B1 = B + b*T in Z[x] for some S and T with deg(S) <= deg(A), deg(T) <= deg(B) // we also need (mod b*r) C = A1*B1 = (A + b*S)*(B + b*T) = AB + b(AT + BS) + b^2*ST // if we find S and T, we will have found our A1 and B1 // since r divides b, then b^2 contains b*r and we know that it must be that C = AB + b(AT + BS) (mod b*r) // which is equivalent to // (5) f = (C - AB)/b = AT + BS (mod r) // having (1) AU + BV = 1 (mod r) and (5) AT + BS = f (mod r), we know that // A*(fU) + B*(fV) = f (mod r), i.e. T = fU, S = fV is a solution // but we also know that we need an S with deg(S) <= deg(A) so we can do the following // we know that l(A) is invertible so we can find the exact remainder of fV with A, i.e. find the qotient // t in the division and set // A*(fU + tB) + B*(fV - tA) = f // T = fU + tB, S = fU - tA // since l(A) is invertible in Z_r, we can (in Z_r) use exact division to get Vf = At + R with deg(R) < A // we now know that deg(A+bS) = deg(A), but we also know (4) which will guarantee that deg(B+bT) = deg(B) // compute the S, T (compute in Z_r[x]) scoped_numeral_vector Vf(r_upm.m()), t(r_upm.m()), S(r_upm.m()); TRACE("polynomial::factorization::bughunt", tout << "V == "; upm.display(tout, V); tout << endl; ); r_upm.mul(V.size(), V.c_ptr(), f.size(), f.c_ptr(), Vf); TRACE("polynomial::factorization::bughunt", tout << "Vf = V*f" << endl; tout << "Vf == "; upm.display(tout, Vf); tout << endl; ); r_upm.div_rem(Vf.size(), Vf.c_ptr(), A.size(), A.c_ptr(), t, S); TRACE("polynomial::factorization::bughunt", tout << "[t, S] = Vf.quo_rem(A)" << endl; tout << "t == "; upm.display(tout, t); tout << endl; tout << "S == "; upm.display(tout, S); tout << endl; ); scoped_numeral_vector T(r_upm.m()), tmp(r_upm.m()); r_upm.mul(U.size(), U.c_ptr(), f.size(), f.c_ptr(), T); // T = fU TRACE("polynomial::factorization::bughunt", tout << "T == U*f" << endl; tout << "T == "; upm.display(tout, T); tout << endl; ); r_upm.mul(B.size(), B.c_ptr(), t.size(), t.c_ptr(), tmp); // tmp = Bt TRACE("polynomial::factorization::bughunt", tout << "tmp = B*t" << endl; tout << "tmp == "; upm.display(tout, tmp); tout << endl; ); r_upm.add(T.size(), T.c_ptr(), tmp.size(), tmp.c_ptr(), T); // T = Uf + Bt TRACE("polynomial::factorization::bughunt", tout << "T = B*tmp" << endl; tout << "T == "; upm.display(tout, T); tout << endl; ); // set the result, A1 = A + b*S, B1 = B + b*T (now we compute in Z[x]) upm.mul(S, b); upm.mul(T, b); upm.add(A.size(), A.c_ptr(), S.size(), S.c_ptr(), A_lifted); upm.add(B.size(), B.c_ptr(), T.size(), T.c_ptr(), B_lifted); CASSERT("polynomial::factorizatio::bughunt", check_hansel_lift(upm, C, a, b, r, A, B, A_lifted, B_lifted)); } bool check_quadratic_hensel(zp_manager & zpe_upm, numeral_vector const & U, numeral_vector const & A, numeral_vector const & V, numeral_vector const & B) { z_numeral_manager & nm = zpe_upm.zm(); // compute UA+BV expecting to get 1 (in Z_pe[x]) scoped_mpz_vector tmp1(nm); scoped_mpz_vector tmp2(nm); zpe_upm.mul(U.size(), U.c_ptr(), A.size(), A.c_ptr(), tmp1); zpe_upm.mul(V.size(), V.c_ptr(), B.size(), B.c_ptr(), tmp2); scoped_mpz_vector one(nm); zpe_upm.add(tmp1.size(), tmp1.c_ptr(), tmp2.size(), tmp2.c_ptr(), one); if (one.size() != 1 || !nm.is_one(one[0])) { TRACE("polynomial::factorization::bughunt", tout << "sage: R. = Zmod(" << nm.to_string(zpe_upm.m().p()) << ")['x']" << endl; tout << "sage: A = "; zpe_upm.display(tout, A); tout << endl; tout << "sage: B = "; zpe_upm.display(tout, B); tout << endl; tout << "sage: U = "; zpe_upm.display(tout, U); tout << endl; tout << "sage: V = "; zpe_upm.display(tout, V); tout << endl; tout << "sage: print (1 - UA - VB)" << endl; tout << "sage: print 'expected 0'" << endl; ); return false; } return true; } /** Lift C = A*B from Z_p[x] to Z_{p^e}[x] such that: * A = A_lift (mod p) * B = B_lift (mod p) * C = A*B (mod p^e) */ void hensel_lift_quadratic(z_manager& upm, numeral_vector const & C, zp_manager & zpe_upm, numeral_vector & A, numeral_vector & B, unsigned e) { z_numeral_manager & nm = upm.zm(); TRACE("polynomial::factorization::bughunt", tout << "polynomial::hansel_lift_quadratic("; tout << "A = "; upm.display(tout, A); tout << ", "; tout << "B = "; upm.display(tout, B); tout << ", "; tout << "C = "; upm.display(tout, C); tout << ", "; tout << "p = " << nm.to_string(zpe_upm.m().p()) << ", e = " << e << ")" << endl; ); // we create a new Z_p manager, since we'll be changing the input one zp_manager zp_upm(nm); zp_upm.set_zp(zpe_upm.m().p()); // get the U, V, such that A*U + B*V = 1 (mod p) scoped_mpz_vector U(nm), V(nm), D(nm); zp_upm.ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D); SASSERT(D.size() == 1 && zp_upm.m().is_one(D[0])); // we start lifting from (a = p, b = p, r = p) scoped_mpz_vector A_lifted(nm), B_lifted(nm); for (unsigned k = 1; k < e; k *= 2) { upm.checkpoint(); // INVARIANT(a = p^e, b = p^e, r = gcd(a, b) = p^e): // C = AB (mod b), UA + VB = 1 (mod a) // deg(U) < deg(B), dev(V) < deg(A), deg(C) = deg(A) + deg(V) // gcd(l(A), r) = 1 // regular hensel lifting from a to b*r, here from pe -> pk*pk = p^{k*k} numeral const & pe = zpe_upm.m().p(); hensel_lift(upm, pe, pe, pe, U, A, V, B, C, A_lifted, B_lifted); // now we have C = AB (mod b*r) TRACE("polynomial::factorization::bughunt", tout << "A_lifted = "; upm.display(tout, A_lifted); tout << endl; tout << "B_lifted = "; upm.display(tout, B_lifted); tout << endl; tout << "C = "; upm.display(tout, C); tout << endl; ); // we employ similar reasoning as in the regular hansel lemma now // we need to lift UA + VB = 1 (mod a) // since after the lift, we still have UA + VB = 1 (mod a) we know that (1 - UA - VB)/a is in Z[x] // so we can compute g = (1 - UA - VB)/a // we need U1 and V1 such that U1A + V1B = 1 (mod a^2), with U1 = U mod a, V1 = V mod a // hence U1 = U + aS, V1 = V + aT and we need // (U + aS)A + (V + aT)B = 1 (mod a^2) same as // UA + VB + a(SA + TB) = 1 (mod a^2) same as // SA + TB = g (mod a) hence // (gU + tB)A + (gV - tA)B = g (mod a) will be a solution and we pick t such that deg(gV - tA) < deg(A) // compute g scoped_mpz_vector tmp1(nm), g(nm); g.push_back(numeral()); nm.set(g.back(), 1); // g = 1 upm.mul(A_lifted.size(), A_lifted.c_ptr(), U.size(), U.c_ptr(), tmp1); // tmp1 = AU upm.sub(g.size(), g.c_ptr(), tmp1.size(), tmp1.c_ptr(), g); // g = 1 - UA upm.mul(B_lifted.size(), B_lifted.c_ptr(), V.size(), V.c_ptr(), tmp1); // tmp1 = BV upm.sub(g.size(), g.c_ptr(), tmp1.size(), tmp1.c_ptr(), g); // g = 1 - UA - VB upm.div(g, pe); to_zp_manager(zpe_upm, g); TRACE("polynomial::factorization::bughunt", tout << "g = (1 - A_lifted*U - B_lifted*V)/" << nm.to_string(pe) << endl; tout << "g == "; upm.display(tout, g); tout << endl; ); // compute the S, T scoped_mpz_vector S(nm), T(nm), t(nm), tmp2(nm); zpe_upm.mul(g.size(), g.c_ptr(), V.size(), V.c_ptr(), tmp1); // tmp1 = gV zpe_upm.div_rem(tmp1.size(), tmp1.c_ptr(), A.size(), A.c_ptr(), t, T); // T = gV - tA, deg(T) < deg(A) zpe_upm.mul(g.size(), g.c_ptr(), U.size(), U.c_ptr(), tmp1); // tmp1 = gU zpe_upm.mul(t.size(), t.c_ptr(), B.size(), B.c_ptr(), tmp2); // tmp2 = tB zpe_upm.add(tmp1.size(), tmp1.c_ptr(), tmp2.size(), tmp2.c_ptr(), S); // now update U = U + a*S and V = V + a*T upm.mul(S.size(), S.c_ptr(), pe); upm.mul(T.size(), T.c_ptr(), pe); upm.add(U.size(), U.c_ptr(), S.size(), S.c_ptr(), U); upm.add(V.size(), V.c_ptr(), T.size(), T.c_ptr(), V); // deg(V) < deg(A), deg(T) < deg(A) => deg(V') < deg(A) // we go quadratic zpe_upm.m().set_p_sq(); to_zp_manager(zpe_upm, U); to_zp_manager(zpe_upm, V); to_zp_manager(zpe_upm, A_lifted); to_zp_manager(zpe_upm, B_lifted); // at this point we have INVARIANT(a = (p^e)^2, b = (p^e)^2, r = (p^e)^2) A.swap(A_lifted); B.swap(B_lifted); CASSERT("polynomial::factorizatio::bughunt", check_quadratic_hensel(zpe_upm, U, A, V, B)); } } bool check_hensel_lift(z_manager & upm, numeral_vector const & f, zp_factors const & zp_fs, zp_factors const & zpe_fs, unsigned e) { numeral_manager & nm(upm.m()); zp_manager & zp_upm = zp_fs.upm(); zp_manager & zpe_upm = zpe_fs.upm(); numeral const & p = zp_fs.nm().p(); numeral const & pe = zpe_fs.nm().p(); scoped_numeral power(nm); nm.power(p, e, power); if (!nm.ge(pe, power)) { return false; } // check f = lc(f) * zp_fs (mod p) scoped_numeral_vector mult_zp(nm), f_zp(nm); zp_fs.multiply(mult_zp); to_zp_manager(zp_upm, f, f_zp); zp_upm.mul(mult_zp, f_zp.back()); if (!upm.eq(mult_zp, f_zp)) { TRACE("polynomial::factorization::bughunt", tout << "f = "; upm.display(tout, f); tout << endl; tout << "zp_fs = " << zp_fs << endl; tout << "sage: R. = Zmod(" << nm.to_string(p) << ")['x']" << endl; tout << "sage: mult_zp = "; upm.display(tout, mult_zp); tout << endl; tout << "sage: f_zp = "; upm.display(tout, f_zp); tout << endl; tout << "sage: mult_zp == f_zp" << endl; ); return false; } // check individual factors if (zpe_fs.distinct_factors() != zp_fs.distinct_factors()) { return false; } // check f = lc(f) * zpe_fs (mod p^e) scoped_numeral_vector mult_zpe(nm), f_zpe(nm); zpe_fs.multiply(mult_zpe); to_zp_manager(zpe_upm, f, f_zpe); zpe_upm.mul(mult_zpe, f_zpe.back()); if (!upm.eq(mult_zpe, f_zpe)) { TRACE("polynomial::factorization::bughunt", tout << "f = "; upm.display(tout, f); tout << endl; tout << "zpe_fs = " << zpe_fs << endl; tout << "sage: R. = Zmod(" << nm.to_string(pe) << ")['x']" << endl; tout << "sage: mult_zpe = "; upm.display(tout, mult_zpe); tout << endl; tout << "sage: f_zpe = "; upm.display(tout, f_zpe); tout << endl; tout << "sage: mult_zpe == f_zpe" << endl; ); return false; } return true; } bool check_individual_lift(zp_manager & zp_upm, numeral_vector const & A_p, zp_manager & zpe_upm, numeral_vector const & A_pe) { scoped_numeral_vector A_pe_p(zp_upm.m()); to_zp_manager(zp_upm, A_pe, A_pe_p); if (!zp_upm.eq(A_p, A_pe_p)) { return false; } return true; } void hensel_lift(z_manager & upm, numeral_vector const & f, zp_factors const & zp_fs, unsigned e, zp_factors & zpe_fs) { SASSERT(zp_fs.total_factors() > 0); zp_numeral_manager & zp_nm = zp_fs.nm(); zp_manager & zp_upm = zp_fs.upm(); z_numeral_manager & nm = zp_nm.m(); SASSERT(nm.is_one(zp_fs.get_constant())); zp_numeral_manager & zpe_nm = zpe_fs.nm(); zp_manager & zpe_upm = zpe_fs.upm(); zpe_nm.set_zp(zp_nm.p()); TRACE("polynomial::factorization::bughunt", tout << "polynomial::hansel_lift("; upm.display(tout, f); tout << ", " << zp_fs << ")" << endl; ); // lift the factors one by one scoped_mpz_vector A(nm), B(nm), C(nm), f_parts(nm); // these will all be in Z_p // copy of f, that we'll be cutting parts of upm.set(f.size(), f.c_ptr(), f_parts); // F_k are factors mod Z_p, A_k the factors mod p^e // the invariant we keep is that: // (1) f_parts = C = lc(f) * \prod_{k = i}^n F_k, C in Z_p[x] // (2) A_k = F_k (mod p), for k < i // (3) f = (\prod_{k < i} A_k) * f_parts (mod p^e) for (int i = 0, i_end = zp_fs.distinct_factors()-1; i < i_end; ++ i) { SASSERT(zp_fs.get_degree(i) == 1); // p was chosen so that f is square-free // F_i = A (mod Z_p) zp_upm.set(zp_fs[i].size(), zp_fs[i].c_ptr(), A); TRACE("polynomial::factorization::bughunt", tout << "A = "; upm.display(tout, A); tout << endl; ); // C = f_parts (mod Z_p) if (i > 0) { to_zp_manager(zp_upm, f_parts, C); } else { // first time around, we don't have p^e yet, so first time we just compute C zp_fs.multiply(C); scoped_mpz lc(nm); zp_nm.set(lc, f.back()); zp_upm.mul(C, lc); } TRACE("polynomial::factorization::bughunt", tout << "C = "; upm.display(tout, C); tout << endl; ); // we take B to be what's left from C and A zp_upm.div(C.size(), C.c_ptr(), A.size(), A.c_ptr(), B); TRACE("polynomial::factorization::bughunt", tout << "B = "; upm.display(tout, B); tout << endl; ); // lift A and B to p^e (this will change p^e and put it zp_upm) zpe_nm.set_zp(zp_nm.p()); hensel_lift_quadratic(upm, f_parts, zpe_upm, A, B, e); CASSERT("polynomial::factorizatio::bughunt", check_individual_lift(zp_upm, zp_fs[i], zpe_upm, A)); TRACE("polynomial::factorization", tout << "lifted to " << nm.to_string(zpe_upm.m().p()) << endl; tout << "A = "; upm.display(tout, A); tout << endl; tout << "B = "; upm.display(tout, B); tout << endl; ); // if this is the first time round, we also construct f_parts (now we have correct p^e) if (i == 0) { to_zp_manager(zpe_upm, f, f_parts); } // take the lifted A out of f_parts zpe_upm.div(f_parts.size(), f_parts.c_ptr(), A.size(), A.c_ptr(), f_parts); // add the lifted factor (kills A) zpe_fs.push_back_swap(A, 1); } // we have one last factor, but it also contains lc(f), so take it out scoped_mpz lc_inv(nm); zpe_nm.set(lc_inv, f.back()); zpe_nm.inv(lc_inv); zpe_upm.mul(B, lc_inv); zpe_fs.push_back_swap(B, 1); TRACE("polynomial::factorization::bughunt", tout << "polynomial::hansel_lift("; upm.display(tout, f); tout << ", " << zp_fs << ") => " << zpe_fs << endl; ); CASSERT("polynomial::factorizatio::bughunt", check_hensel_lift(upm, f, zp_fs, zpe_fs, e)); } // get a bound on B for the factors of f with degree less or equal to deg(f)/2 // and then choose e to be smallest such that p^e > 2*lc(f)*B, we use the mignotte // // from [3, pg 134] // |p| = sqrt(\sum |p_i|^2). If a = \sum a_i x^i, b = \sum b_j, deg b = n, and b divides a, then for all j // // |b_j| <= (n-1 over j)|a| + (n-1 over j-1)|lc(a)| // // when factoring a polynomial, we find a bound B for a factor of f of degree <= deg(f)/2 // allowing both positive and negative as coefficients of b, we now want a power p^e such that all coefficients // can be represented, i.e. [-B, B] \subset [-\ceil(p^e/2), \ceil(p^e)/2], so we peek e, such that p^e >= 2B static unsigned mignotte_bound(z_manager & upm, numeral_vector const & f, numeral const & p) { numeral_manager & nm = upm.m(); SASSERT(upm.degree(f) >= 2); unsigned n = upm.degree(f)/2; // >= 1 // get the approximation for the norm of a scoped_numeral f_norm(nm); for (unsigned i = 0; i < f.size(); ++ i) { if (!nm.is_zero(f[i])) { nm.addmul(f_norm, f[i], f[i], f_norm); } } nm.root(f_norm, 2); // by above we can pick (n-1 over (n-1/2))|a| + (n-1 over (n-1)/2)lc(a) // we approximate both binomial-coefficients with 2^(n-1), so to get 2B we use 2^n(f_norm + lc(f)) scoped_numeral bound(nm); nm.set(bound, 1); nm.mul2k(bound, n, bound); scoped_numeral tmp(nm); nm.set(tmp, f.back()); nm.abs(tmp); nm.add(f_norm, tmp, f_norm); nm.mul(bound, f_norm, bound); // we need e such that p^e >= B nm.set(tmp, p); unsigned e; for (e = 1; nm.le(tmp, bound); e *= 2) { nm.mul(tmp, tmp, tmp); } return e; } /** \brief Given f from Z[x] that is square free, it factors it. This method also assumes f is primitive. */ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, unsigned k, factor_params const & params) { TRACE("polynomial::factorization::bughunt", tout << "sage: f = "; upm.display(tout, f); tout << endl; tout << "sage: if (not f.is_squarefree()): print 'Error, f is not square-free'" << endl; tout << "sage: print 'Factoring :', f" << endl; tout << "sage: print 'Expected factors: ', f.factor()" << endl; ); numeral_manager & nm = upm.m(); // This method assumes f is primitive. Thus, the content of f must be one DEBUG_CODE({ scoped_numeral f_cont(nm); nm.gcd(f.size(), f.c_ptr(), f_cont); SASSERT(f.size() == 0 || nm.is_one(f_cont)); }); scoped_numeral_vector f_pp(nm); upm.set(f.size(), f.c_ptr(), f_pp); // make sure the leading coefficient is positive if (!f_pp.empty() && nm.is_neg(f_pp[f_pp.size() - 1])) { for (unsigned i = 0; i < f_pp.size(); i++) nm.neg(f_pp[i]); // flip sign constant if k is odd if (k % 2 == 1) { scoped_numeral c(nm); nm.set(c, fs.get_constant()); nm.neg(c); fs.set_constant(c); } } TRACE("polynomial::factorization::bughunt", tout << "sage: f_pp = "; upm.display(tout, f_pp); tout << endl; tout << "sage: if (not (f_pp * 1 == f): print 'Error, content computation wrong'" << endl; ); // the variables we'll be using and updating in Z_p scoped_numeral p(nm); nm.set(p, 2); zp_manager zp_upm(nm.m()); zp_upm.set_zp(p); zp_factors zp_fs(zp_upm); scoped_numeral zp_fs_p(nm); nm.set(zp_fs_p, 2); // we keep all the possible sets of degrees of factors in this set factorization_degree_set degree_set(zp_upm); // we try get some number of factorizations in Z_p, for some primes // get the prime p such that // (1) (f_prim mod p) stays square-free // (2) l(f_prim) mod p doesn't vanish, i.e. we don't get a polynomial of smaller degree prime_iterator prime_it; scoped_numeral gcd_tmp(nm); unsigned trials = 0; while (trials < params.m_p_trials) { upm.checkpoint(); // construct prime to check uint64 next_prime = prime_it.next(); if (next_prime > params.m_max_p) { fs.push_back(f_pp, k); return false; } nm.set(p, next_prime); zp_upm.set_zp(p); // we need gcd(lc(f_pp), p) = 1 nm.gcd(p, f_pp.back(), gcd_tmp); TRACE("polynomial::factorization::bughunt", tout << "sage: if (not (gcd(" << nm.to_string(p) << ", " << nm.to_string(f_pp.back()) << ")) == " << nm.to_string(gcd_tmp) << "): print 'Error, wrong gcd'" << endl; ); if (!nm.is_one(gcd_tmp)) { continue; } // if it's not square free, we also try somehting else scoped_numeral_vector f_pp_zp(nm); to_zp_manager(zp_upm, f_pp, f_pp_zp); TRACE("polynomial::factorization::bughunt", tout << "sage: Rp. = GF(" << nm.to_string(p) << ")['x_p']"; tout << endl; tout << "sage: f_pp_zp = "; zp_upm.display(tout, f_pp_zp, "x_p"); tout << endl; ); if (!zp_upm.is_square_free(f_pp_zp.size(), f_pp_zp.c_ptr())) continue; // we make it monic zp_upm.mk_monic(f_pp_zp.size(), f_pp_zp.c_ptr()); // found a candidate, factorize in Z_p and add back the constant zp_factors current_fs(zp_upm); bool factored = zp_factor_square_free(zp_upm, f_pp_zp, current_fs); if (!factored) { fs.push_back(f_pp, k); return true; } // get the factors degrees set factorization_degree_set current_degree_set(current_fs); if (degree_set.max_degree() == 0) { // first time, initialize degree_set.swap(current_degree_set); } else { degree_set.intersect(current_degree_set); } // if the degree set is trivial, we are done if (degree_set.is_trivial()) { fs.push_back(f_pp, k); return true; } // we found a candidate, lets keep it if it has less factors than the current best trials ++; if (zp_fs.distinct_factors() == 0 || zp_fs.total_factors() > current_fs.total_factors()) { zp_fs.swap(current_fs); nm.set(zp_fs_p, p); TRACE("polynomial::factorization::bughunt", tout << "best zp factorization (Z_" << nm.to_string(zp_fs_p) << "): "; tout << zp_fs << endl; tout << "best degree set: "; degree_set.display(tout); tout << endl; ); } } #ifndef _EXTERNAL_RELEASE IF_VERBOSE(FACTOR_VERBOSE_LVL, verbose_stream() << "(polynomial-factorization :at GF_" << nm.to_string(zp_upm.p()) << ")" << std::endl;); #endif // make sure to set the zp_manager back to modulo zp_fs_p zp_upm.set_zp(zp_fs_p); TRACE("polynomial::factorization::bughunt", tout << "best zp factorization (Z_" << nm.to_string(zp_fs_p) << "): " << zp_fs << endl; tout << "best degree set: "; degree_set.display(tout); tout << endl; ); // get a bound on B for the factors of f_pp with degree less or equal to deg(f)/2 // and then choose e to be smallest such that p^e > 2*lc(f)*B, we use the mignotte unsigned e = mignotte_bound(upm, f_pp, zp_fs_p); TRACE("polynomial::factorization::bughunt", tout << "out p = " << nm.to_string(zp_fs_p) << ", and we'll work p^e for e = " << e << endl; ); // we got a prime factoring, so we do the lifting now zp_manager zpe_upm(nm.m()); zpe_upm.set_zp(zp_fs_p); zp_numeral_manager & zpe_nm = zpe_upm.m(); zp_factors zpe_fs(zpe_upm); // this might give something bigger than p^e, but the lifting proocedure will update the zpe_nm // zp factors are monic, so will be the zpe factors, i.e. f_pp = zpe_fs * lc(f_pp) (mod p^e) hensel_lift(upm, f_pp, zp_fs, e, zpe_fs); #ifndef _EXTERNAL_RELEASE IF_VERBOSE(FACTOR_VERBOSE_LVL, verbose_stream() << "(polynomial-factorization :num-candidate-factors " << zpe_fs.distinct_factors() << ")" << std::endl;); #endif // the leading coefficient of f_pp mod p^e scoped_numeral f_pp_lc(nm); zpe_nm.set(f_pp_lc, f_pp.back()); // we always keep in f_pp the the actual primitive part f_pp*lc(f_pp) upm.mul(f_pp, f_pp_lc); // now we go through the combinations of factors to check construct the factorization ufactorization_combination_iterator it(zpe_fs, degree_set); scoped_numeral_vector trial_factor(nm), trial_factor_quo(nm); scoped_numeral trial_factor_cont(nm); TRACE("polynomial::factorization::bughunt", tout << "STARTING TRIAL DIVISION" << endl; tout << "zpe_fs" << zpe_fs << endl; tout << "degree_set = "; degree_set.display(tout); tout << endl; ); bool result = true; bool remove = false; unsigned counter = 0; while (it.next(remove)) { upm.checkpoint(); counter++; if (counter > params.m_max_search_size) { // stop search result = false; break; } // // our bound ensures we can extract the right factors of degree at most 1/2 of the original // so, if out trial factor has degree bigger than 1/2, we need to take the rest of the factors // but, if we take the rest and it works, it doesn't mean that the rest is factorized, so we still take out // the original factor bool using_left = it.current_degree() <= zp_fs.get_degree()/2; if (using_left) { // do a quick check first scoped_numeral tmp(nm); it.get_left_tail_coeff(f_pp_lc, tmp); if (!nm.divides(tmp, f_pp[0])) { // don't remove this combination remove = false; continue; } it.left(trial_factor); } else { // do a quick check first scoped_numeral tmp(nm); it.get_right_tail_coeff(f_pp_lc, tmp); if (!nm.divides(tmp, f_pp[0])) { // don't remove this combination remove = false; continue; } it.right(trial_factor); } // add the lc(f_pp) to the trial divisor zpe_upm.mul(trial_factor, f_pp_lc); TRACE("polynomial::factorization::bughunt", tout << "f_pp*lc(f_pp) = "; upm.display(tout, f_pp); tout << endl; tout << "trial_factor = "; upm.display(tout, trial_factor); tout << endl; ); bool true_factor = upm.exact_div(f_pp, trial_factor, trial_factor_quo); TRACE("polynomial::factorization::bughunt", tout << "trial_factor = "; upm.display(tout, trial_factor); tout << endl; tout << "trial_factor_quo = "; upm.display(tout, trial_factor_quo); tout << endl; tout << "result = " << (true_factor ? "true" : "false") << endl; ); // if division is precise we have a factor if (true_factor) { if (!using_left) { // as noted above, we still use the original factor trial_factor.swap(trial_factor_quo); } // We need to get the content out of the factor upm.get_primitive_and_content(trial_factor, trial_factor, trial_factor_cont); // add the factor fs.push_back(trial_factor, k); // we continue with the quotient (with the content added back) // but we also have to keep lc(f_pp)*f_pp upm.get_primitive_and_content(trial_factor_quo, f_pp, trial_factor_cont); nm.set(f_pp_lc, f_pp.back()); upm.mul(f_pp, f_pp_lc); // but we also remove it from the iterator remove = true; } else { // don't remove this combination remove = false; } TRACE("polynomial::factorization::bughunt", tout << "factors = " << fs << endl; tout << "f_pp*lc(f_pp) = "; upm.display(tout, f_pp); tout << endl; tout << "lc(f_pp) = " << f_pp_lc << endl; ); } #ifndef _EXTERNAL_RELEASE IF_VERBOSE(FACTOR_VERBOSE_LVL, verbose_stream() << "(polynomial-factorization :search-size " << counter << ")" << std::endl;); #endif // add the what's left to the factors (if not a constant) if (f_pp.size() > 1) { upm.div(f_pp, f_pp_lc); fs.push_back(f_pp, k); } else { // if a constant it must be 1 (it was primitve) SASSERT(f_pp.size() == 1 && nm.is_one(f_pp.back())); } return result; } bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, factor_params const & params) { return factor_square_free(upm, f, fs, 1, params); } }; // end upolynomial namespace z3-z3-4.4.1/src/math/polynomial/upolynomial_factorization.h000066400000000000000000000077561260446376700240240ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: upolynomial_factorization.h Abstract: Methods for factoring polynomials. Author: Dejan (t-dejanj) 2011-11-29 Notes: [1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal, 46(8-10):1853-1859, 1967. [2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third edition, 1997. [3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993. --*/ #ifndef UPOLYNOMIAL_FACTORIZATION_H_ #define UPOLYNOMIAL_FACTORIZATION_H_ #include"upolynomial.h" #include"polynomial.h" #include"bit_vector.h" #include"z3_exception.h" namespace upolynomial { typedef manager::scoped_numeral scoped_numeral; /** \breif Factor f into f = f_1^k_1 * ... * p_n^k_n, such that p_i are square-free and coprime. */ void zp_square_free_factor(zp_manager & zp_upm, numeral_vector const & f, zp_factors & sq_free_factors); /** \brief Factor the monic square-free polynomial f from Z_p[x]. Returns true if factorization was sucesseful, or false if f is an irreducible square-free polynomial in Z_p[x]. */ bool zp_factor_square_free(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors); inline bool zp_factor_square_free(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors, factor_params const & params) { return zp_factor_square_free(zp_upm, f, factors); } /** \brief Factor the monic square-free polynomial f from Z_p[x] using the Berlekamp algorithm. If randomized is true the factor splitting is done randomly [3], otherwise it is done as in the original Berlekamp [1]. */ bool zp_factor_square_free_berlekamp(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors, bool randomized = true); /** \brief Factor the polynomial f from Z_p[x]. Returns true if factorization was sucesseful, or false if f is an irreducible polynomial in Z_p[x] */ bool zp_factor(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors); /** \brief Performs a Hensel lift of A and B in Z_a to Z_b, where p is prime and and a = p^{a_k}, b = p^{b_k}, r = (a, b), with the following assumptions: * UA + VB = 1 (mod a) * C = AB (mod b) * (l(A), r) = 1 (importand in order to divide by A, i.e. to invert l(A)) the output of is two polynomials A1, B1 (replacing A and B) such that A1 = A (mod b), B1 = B (mod b), l(A1) = l(A), deg(A1) = deg(A), deg(B1) = deg(B) and C = A1 B1 (mod b*r). Such A1, B1 are unique if r is prime. See [3] p. 138. The method will also change the zp_manager's module from b to b*r */ void hensel_lift(z_manager & upm, numeral const & a, numeral const & b, numeral const & r, numeral_vector const & U, numeral_vector const & A, numeral_vector const & V, numeral_vector const & B, numeral_vector const & C, numeral_vector & A_lifted, numeral_vector & B_lifted); /** \brief Performs the Hensel lift for the (monic!) factors_p of f in Z_p to Z_{p^e}. */ void hensel_lift(z_manager & upm, numeral_vector const & f, zp_factors const & factors_p, unsigned e, zp_factors & factors_pe); /** \brief Factor the square-free polynomial f from Z[x]. Returns true if factorization was sucesseful, or false if f is an irreducible polynomial in Z[x]. The vector of factors is cleared. */ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, factor_params const & ps = factor_params()); /** Similar to factor_square_free, but it is used to factor the k-th component f^k of a polynomial. That is, the factors of f are inserted as factors of degree k into fs. */ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, unsigned k, factor_params const & ps = factor_params()); }; #endif z3-z3-4.4.1/src/math/polynomial/upolynomial_factorization_int.h000066400000000000000000000356671260446376700247000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: upolynomial_factorization_int.h Abstract: (Internal) header file for univariate polynomial factorization. This classes are exposed for debugging purposes only. Author: Dejan (t-dejanj) 2011-11-29 Notes: [1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal, 46(8-10):1853-1859, 1967. [2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third edition, 1997. [3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993. --*/ #ifndef UPOLYNOMIAL_FACTORIZATION_INT_H_ #define UPOLYNOMIAL_FACTORIZATION_INT_H_ #include"upolynomial_factorization.h" namespace upolynomial { // copy p from some manager to zp_p in Z_p[x] inline void to_zp_manager(zp_manager & zp_upm, numeral_vector & p) { zp_numeral_manager & zp_nm(zp_upm.m()); for (unsigned i = 0; i < p.size(); ++ i) { zp_nm.p_normalize(p[i]); } zp_upm.trim(p); } // copy p from some manager to zp_p in Z_p[x] inline void to_zp_manager(zp_manager & zp_upm, numeral_vector const & p, numeral_vector & zp_p) { zp_numeral_manager & zp_nm(zp_upm.m()); zp_upm.reset(zp_p); for (unsigned i = 0; i < p.size(); ++ i) { numeral p_i; // no need to delete, we keep it pushed in zp_p zp_nm.set(p_i, p[i]); zp_p.push_back(p_i); } zp_upm.trim(zp_p); } /** \brief Contains all possible degrees of a factorization of a polynomial. If p = p1^{k_1} * ... * pn^{k_n} with p_i of degree d_i then it is represents numbers of the for \sum a_i*d_i, where a_i <= k_i. Two numbers always in the set are deg(p) and 0. */ class factorization_degree_set { // the set itself, a (m_max_degree)-binary number bit_vector m_set; public: factorization_degree_set() { } factorization_degree_set(zp_factors const & factors) { zp_manager & upm = factors.upm(); // the set contains only {0} m_set.push_back(true); for (unsigned i = 0; i < factors.distinct_factors(); ++ i) { unsigned degree = upm.degree(factors[i]); unsigned multiplicity = factors.get_degree(i); for (unsigned k = 0; k < multiplicity; ++ k) { bit_vector tmp(m_set); m_set.shift_right(degree); m_set |= tmp; } } SASSERT(in_set(0) && in_set(factors.get_degree())); } unsigned max_degree() const { return m_set.size() - 1; } void swap(factorization_degree_set & other) { m_set.swap(other.m_set); } bool is_trivial() const { // check if set = {0, n} for (int i = 1; i < (int) m_set.size() - 1; ++ i) { if (m_set.get(i)) return false; } return true; } void remove(unsigned k) { m_set.set(k, false); } bool in_set(unsigned k) const { return m_set.get(k); } void intersect(const factorization_degree_set& other) { m_set &= other.m_set; } void display(std::ostream & out) const { out << "[0"; for (unsigned i = 1; i <= max_degree(); ++ i) { if (in_set(i)) { out << ", " << i; } } out << "] represented by " << m_set; } }; /** \brief A to iterate through all combinations of factors. This is only needed for the factorization, and we always iterate through the */ template class factorization_combination_iterator_base { protected: // total size of available factors int m_total_size; // maximal size of the selection int m_max_size; // the factors to select from factors_type const & m_factors; // which factors are enabled svector m_enabled; // the size of the current selection int m_current_size; // the current selection: indices at positions < m_current_size, other values are maxed out svector m_current; /** Assuming a valid selection m_current[0], ..., m_current[position], try to find the next option for m_current[position], i.e. the first bigger one that's enabled. */ int find(int position, int upper_bound) { int current = m_current[position] + 1; while (current < upper_bound && !m_enabled[current]) { current ++; } if (current == upper_bound) { return -1; } else { return current; } } public: factorization_combination_iterator_base(factors_type const & factors) : m_total_size(factors.distinct_factors()), m_max_size(factors.distinct_factors()/2), m_factors(factors) { SASSERT(factors.total_factors() > 1); SASSERT(factors.total_factors() == factors.distinct_factors()); // enable all to start with m_enabled.resize(m_factors.distinct_factors(), true); // max out the m_current so that it always fits m_current.resize(m_factors.distinct_factors()+1, m_factors.distinct_factors()); m_current_size = 0; } /** \brief Returns the factors we are enumerating through. */ factors_type const & get_factors() const { return m_factors; } /** \brief Computes the next combination of factors and returns true if it exists. If remove current is true it will eliminate the current selected elements from any future selection. */ bool next(bool remove_current) { int max_upper_bound = m_factors.distinct_factors(); do { // the index we are currently trying to fix int current_i = m_current_size - 1; // the value we found as plausable (-1 we didn't find anything) int current_value = -1; if (remove_current) { SASSERT(m_current_size > 0); // disable the elements of the current selection from ever appearing again for (current_i = m_current_size - 1; current_i > 0; -- current_i) { SASSERT(m_enabled[m_current[current_i]]); m_enabled[m_current[current_i]] = false; m_current[current_i] = max_upper_bound; } // the last one SASSERT(m_enabled[m_current[0]]); m_enabled[m_current[0]] = false; // not removing current anymore remove_current = false; // out max size is also going down m_total_size -= m_current_size; m_max_size = m_total_size/2; } // we go back to the first one that can be increased (if removing current go all the way) while (current_i >= 0) { current_value = find(current_i, m_current[current_i + 1]); if (current_value >= 0) { // found one m_current[current_i] = current_value; break; } else { // go back some more current_i --; } } do { if (current_value == -1) { // we couldn't find any options, we have to increse size and start from the first one of that size if (m_current_size >= m_max_size) { return false; } else { m_current_size ++; m_current[0] = -1; current_i = 0; current_value = find(current_i, max_upper_bound); // if we didn't find any, we are done if (current_value == -1) { return false; } else { m_current[current_i] = current_value; } } } // ok we have a new selection for the current one for (current_i ++; current_i < m_current_size; ++ current_i) { // start from the previous one m_current[current_i] = m_current[current_i-1]; current_value = find(current_i, max_upper_bound); if (current_value == -1) { // screwed, didn't find the next one, this means we need to increase the size m_current[0] = -1; break; } else { m_current[current_i] = current_value; } } } while (current_value == -1); } while (filter_current()); // found the next one, hurray return true; } /** \brief A function that returns true if the current combination should be ignored. */ virtual bool filter_current() const = 0; /** \brief Returns the size of the current selection (cardinality) */ unsigned left_size() const { return m_current_size; } /** \brief Returns the size of the rest of the current selection (cardinality) */ unsigned right_size() const { return m_total_size - m_current_size; } void display(std::ostream& out) const { out << "[ "; for (unsigned i = 0; i < m_current.size(); ++ i) { out << m_current[i] << " "; } out << "] from [ "; for (unsigned i = 0; i < m_factors.distinct_factors(); ++ i) { if (m_enabled[i]) { out << i << " "; } } out << "]" << std::endl; } }; class ufactorization_combination_iterator : public factorization_combination_iterator_base { // the degree sets to choose from factorization_degree_set const & m_degree_set; public: ufactorization_combination_iterator(zp_factors const & factors, factorization_degree_set const & degree_set) : factorization_combination_iterator_base(factors), m_degree_set(degree_set) {} /** \brief Filter the ones not in the degree set. */ bool filter_current() const { // select only the ones that have degrees in the degree set if (!m_degree_set.in_set(current_degree())) { return true; } return false; } /** \brief Returns the degree of the current selection. */ unsigned current_degree() const { unsigned degree = 0; zp_manager & upm = m_factors.pm(); for (unsigned i = 0; i < left_size(); ++ i) { degree += upm.degree(m_factors[m_current[i]]); } return degree; } void left(numeral_vector & out) const { SASSERT(m_current_size > 0); zp_manager & upm = m_factors.upm(); upm.set(m_factors[m_current[0]].size(), m_factors[m_current[0]].c_ptr(), out); for (int i = 1; i < m_current_size; ++ i) { upm.mul(out.size(), out.c_ptr(), m_factors[m_current[i]].size(), m_factors[m_current[i]].c_ptr(), out); } } void get_left_tail_coeff(numeral const & m, numeral & out) { zp_numeral_manager & nm = m_factors.upm().m(); nm.set(out, m); for (int i = 0; i < m_current_size; ++ i) { nm.mul(out, m_factors[m_current[i]][0], out); } } void get_right_tail_coeff(numeral const & m, numeral & out) { zp_numeral_manager & nm = m_factors.upm().m(); nm.set(out, m); unsigned current = 0; unsigned selection_i = 0; // selection is ordered, so we just take the ones in between that are not disable while (current < m_factors.distinct_factors()) { if (!m_enabled[current]) { // by skipping the disabled we never skip a selected one current ++; } else { if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) { SASSERT(m_factors.get_degree(current) == 1); nm.mul(out, m_factors[current][0], out); current ++; } else { current ++; selection_i ++; } } } } void right(numeral_vector & out) const { SASSERT(m_current_size > 0); zp_manager & upm = m_factors.upm(); upm.reset(out); unsigned current = 0; unsigned selection_i = 0; // selection is ordered, so we just take the ones in between that are not disable while (current < m_factors.distinct_factors()) { if (!m_enabled[current]) { // by skipping the disabled we never skip a selected one current ++; } else { if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) { SASSERT(m_factors.get_degree(current) == 1); if (out.size() == 0) { upm.set(m_factors[current].size(), m_factors[current].c_ptr(), out); } else { upm.mul(out.size(), out.c_ptr(), m_factors[current].size(), m_factors[current].c_ptr(), out); } current ++; } else { current ++; selection_i ++; } } } } }; }; #endif z3-z3-4.4.1/src/math/realclosure/000077500000000000000000000000001260446376700164655ustar00rootroot00000000000000z3-z3-4.4.1/src/math/realclosure/mpz_matrix.cpp000066400000000000000000000316751260446376700213770ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: mpz_matrix.h Abstract: Matrix with integer coefficients. This is not a general purpose module for handling matrices with integer coefficients. Instead, it is a custom package that only contains operations needed to implement Sign Determination (Algorithm 10.11) in the Book: "Algorithms in real algebraic geometry", Basu, Pollack, Roy Design choices: - Dense representation. The matrices in Alg 10.11 are small and dense. - Integer coefficients instead of rational coefficients (it only complicates the solver a little bit). Remark: in Algorithm 10.11, the coefficients of the input matrices are always in {-1, 0, 1}. During solving, bigger coefficients are produced, but they are usually very small. It may be an overkill to use mpz instead of int. We use mpz just to be safe. Remark: We do not use rational arithmetic. The solver is slightly more complicated with integers, but is saves space. Author: Leonardo (leonardo) 2013-01-07 Notes: --*/ #include"mpz_matrix.h" #include"buffer.h" mpz_matrix_manager::mpz_matrix_manager(unsynch_mpz_manager & nm, small_object_allocator & a): m_nm(nm), m_allocator(a) { } mpz_matrix_manager::~mpz_matrix_manager() { } void mpz_matrix_manager::mk(unsigned m, unsigned n, mpz_matrix & A) { SASSERT(m > 0 && n > 0); del(A); A.m = m; A.n = n; void * mem = m_allocator.allocate(sizeof(mpz)*m*n); A.a_ij = new (mem) mpz[m*n]; } void mpz_matrix_manager::del(mpz_matrix & A) { if (A.a_ij != 0) { for (unsigned i = 0; i < A.m; i++) for (unsigned j = 0; j < A.n; j++) nm().del(A(i,j)); unsigned sz = sizeof(mpz) * A.m * A.n; m_allocator.deallocate(sz, A.a_ij); A.m = 0; A.n = 0; A.a_ij = 0; } } void mpz_matrix_manager::set(mpz_matrix & A, mpz_matrix const & B) { if (&A == &B) return; if (A.m != B.m || A.n != B.n) { del(A); mk(B.m, B.n, A); } SASSERT(A.m == B.m && A.n == B.n); for (unsigned i = 0; i < B.m; i++) for (unsigned j = 0; j < B.n; j++) nm().set(A(i, j), B(i, j)); } void mpz_matrix_manager::tensor_product(mpz_matrix const & A, mpz_matrix const & B, mpz_matrix & C) { scoped_mpz_matrix CC(*this); mk(A.m * B.m, A.n * B.n, CC); for (unsigned i = 0; i < CC.m(); i++) for (unsigned j = 0; j < CC.n(); j++) nm().mul(A(i / B.m, j / B.n), B(i % B.m, j % B.n), CC(i, j)); C.swap(CC); } void mpz_matrix_manager::swap_rows(mpz_matrix & A, unsigned i, unsigned j) { if (i != j) { for (unsigned k = 0; k < A.n; k++) ::swap(A(i, k), A(j, k)); } } // If b_i == 0, then method just divides the given row by its GCD // If b_i != 0 // If the GCD of the row divides *b_i // divide the row and *b_i by the GCD // Else // If int_solver == true ==> return false (the system is unsolvable) bool mpz_matrix_manager::normalize_row(mpz * A_i, unsigned n, mpz * b_i, bool int_solver) { scoped_mpz g(nm()); bool first = true; for (unsigned j = 0; j < n; j++) { if (nm().is_zero(A_i[j])) continue; if (first) { nm().set(g, A_i[j]); nm().abs(g); first = false; } else { nm().gcd(g, A_i[j], g); } if (nm().is_one(g)) return true; } if (first) return true; // zero row if (!nm().is_one(g)) { if (b_i) { if (nm().divides(g, *b_i)) { for (unsigned j = 0; j < n; j++) { nm().div(A_i[j], g, A_i[j]); } nm().div(*b_i, g, *b_i); } else { if (int_solver) return false; // system does not have an integer solution } } else { for (unsigned j = 0; j < n; j++) { nm().div(A_i[j], g, A_i[j]); } } } return true; } /* Given a matrix of the form k2 | V X X ... X X ... X 0 X ... X X ... X ... ... X X ... X k1=> 0 0 ... 0 X ... X 0 0 ... 0 X ... X ... ... 0 X ... X 0 0 ... 0 X ... X It will "zero" the elements a_{k1+1, k2} ... a_{m, k2} by addining multiples of the row k1 to multiples of the rows k1+1, ..., m The resultant matrix will look like k2 | V X X ... X X ... X 0 X ... X X ... X ... ... X X ... X k1=> 0 0 ... 0 X ... X 0 0 ... 0 0 ... X ... ... 0 0 ... X 0 0 ... 0 0 ... X If b != 0, then the transformations are also applied to b. If int_solver == true and b != 0, then the method returns false if when performing the transformations it detected that it is impossible to solve the integer system of equations A x = b. */ bool mpz_matrix_manager::eliminate(mpz_matrix & A, mpz * b, unsigned k1, unsigned k2, bool int_solver) { // check if first k2-1 positions of row k1 are 0 DEBUG_CODE(for (unsigned j = 0; j < k2; j++) { SASSERT(nm().is_zero(A(k1, j))); }); mpz & a_kk = A(k1, k2); SASSERT(!nm().is_zero(a_kk)); scoped_mpz t1(nm()), t2(nm()); scoped_mpz a_ik_prime(nm()), a_kk_prime(nm()), lcm_a_kk_a_ik(nm()); // for all rows below pivot for (unsigned i = k1+1; i < A.m; i++) { // check if first k-1 positions of row k are 0 DEBUG_CODE(for (unsigned j = 0; j < k2; j++) { SASSERT(nm().is_zero(A(i, j))); }); mpz & a_ik = A(i, k2); if (!nm().is_zero(a_ik)) { // a_ik' = lcm(a_kk, a_ik)/a_kk // a_kk' = lcm(a_kk, a_ik)/a_ik nm().lcm(a_kk, a_ik, lcm_a_kk_a_ik); nm().div(lcm_a_kk_a_ik, a_kk, a_ik_prime); nm().div(lcm_a_kk_a_ik, a_ik, a_kk_prime); for (unsigned j = k2+1; j < A.n; j++) { // a_ij <- a_kk' * a_ij - a_ik' * a_kj nm().mul(a_ik_prime, A(k1, j), t1); nm().mul(a_kk_prime, A(i, j), t2); nm().sub(t2, t1, A(i, j)); } if (b) { // b_i <- a_kk' * b_i - a_ik' * b_k nm().mul(a_ik_prime, b[k1], t1); nm().mul(a_kk_prime, b[i], t2); nm().sub(t2, t1, b[i]); } // a_ik <- 0 nm().set(A(i, k2), 0); // normalize row i if (!normalize_row(A.row(i), A.n, b ? &(b[i]) : 0, int_solver)) return false; } SASSERT(nm().is_zero(A(i, k2))); } return true; } bool mpz_matrix_manager::solve_core(mpz_matrix const & _A, mpz * b, bool int_solver) { SASSERT(_A.n == _A.m); scoped_mpz_matrix A(*this); set(A, _A); for (unsigned k = 0; k < A.m(); k++) { TRACE("mpz_matrix", tout << "k: " << k << "\n" << A; tout << "b:"; for (unsigned i = 0; i < A.m(); i++) { tout << " "; nm().display(tout, b[i]); } tout << "\n";); // find pivot unsigned i = k; for (; i < A.m(); i++) { if (!nm().is_zero(A(i, k))) break; } if (i == A.m()) return false; // matrix is singular // swap rows k and i swap_rows(A, k, i); swap(b[k], b[i]); // if (!eliminate(A, b, k, k, int_solver)) return false; } // Back substitution unsigned k = A.m(); while (k > 0) { --k; DEBUG_CODE(for (unsigned j = 0; j < A.n(); j++) { SASSERT(j == k || nm().is_zero(A(k, j))); }); SASSERT(!nm().is_zero(A(k, k))); if (nm().divides(A(k, k), b[k])) { nm().div(b[k], A(k, k), b[k]); nm().set(A(k, k), 1); } else { if (int_solver) return false; // no integer solution if (nm().is_neg(A(k, k))) { nm().neg(A(k, k)); nm().neg(b[k]); } } if (!int_solver) { // REMARK: // For the sign determination algorithm, we only use int_solver == true. // // TODO: implement backward substitution when int_solver == false // In this case, A(k, k) may not be 1. NOT_IMPLEMENTED_YET(); } SASSERT(!int_solver || nm().is_one(A(k, k))); // back substitute unsigned i = k; while (i > 0) { --i; // Assuming int_solver == true SASSERT(int_solver); // See comment above // b_i <- b_i - a_ik * b_k nm().submul(b[i], A(i, k), b[k], b[i]); nm().set(A(i, k), 0); } } return true; } bool mpz_matrix_manager::solve(mpz_matrix const & A, mpz * b, mpz const * c) { for (unsigned i = 0; i < A.n; i++) nm().set(b[i], c[i]); return solve_core(A, b, true); } bool mpz_matrix_manager::solve(mpz_matrix const & A, int * b, int const * c) { scoped_mpz_matrix _b(*this); mk(A.n, 1, _b); for (unsigned i = 0; i < A.n; i++) nm().set(_b(i,0), c[i]); bool r = solve_core(A, _b.A.a_ij, true); if (r) { for (unsigned i = 0; i < A.n; i++) b[i] = _b.get_int(i, 0); } return r; } void mpz_matrix_manager::filter_cols(mpz_matrix const & A, unsigned num_cols, unsigned const * cols, mpz_matrix & B) { SASSERT(num_cols <= A.n); // Check pre-condition: // - All elements in cols are smaller than A.n // - cols is sorted // - cols does not contain repeated elements DEBUG_CODE({ for (unsigned i = 0; i < num_cols; i ++) { SASSERT(cols[i] < A.n); SASSERT(i == 0 || cols[i-1] < cols[i]); } }); if (num_cols == A.n) { // keep everything set(B, A); } else { SASSERT(num_cols < A.n); scoped_mpz_matrix C(*this); mk(A.m, num_cols, C); for (unsigned i = 0; i < A.m; i++) for (unsigned j = 0; j < num_cols; j++) nm().set(C(i, j), A(i, cols[j])); B.swap(C); } } void mpz_matrix_manager::permute_rows(mpz_matrix const & A, unsigned const * p, mpz_matrix & B) { // Check if p is really a permutation DEBUG_CODE({ buffer seen; seen.resize(A.m, false); for (unsigned i = 0; i < A.m; i++) { SASSERT(p[i] < A.m); SASSERT(!seen[p[i]]); seen[p[i]] = true; } }); scoped_mpz_matrix C(*this); mk(A.m, A.n, C); for (unsigned i = 0; i < A.m; i++) for (unsigned j = 0; j < A.n; j++) nm().set(C(i, j), A(p[i], j)); B.swap(C); } unsigned mpz_matrix_manager::linear_independent_rows(mpz_matrix const & _A, unsigned * r, mpz_matrix & B) { unsigned r_sz = 0; scoped_mpz_matrix A(*this); scoped_mpz g(nm()); scoped_mpz t1(nm()), t2(nm()); scoped_mpz a_ik_prime(nm()), a_kk_prime(nm()), lcm_a_kk_a_ik(nm()); set(A, _A); sbuffer rows; rows.resize(A.m(), 0); for (unsigned i = 0; i < A.m(); i++) rows[i] = i; for (unsigned k1 = 0, k2 = 0; k1 < A.m(); k1++) { TRACE("mpz_matrix", tout << "k1: " << k1 << ", k2: " << k2 << "\n" << A;); // find pivot unsigned pivot = UINT_MAX; for (unsigned i = k1; i < A.m(); i++) { if (!nm().is_zero(A(i, k2))) { if (pivot == UINT_MAX) { pivot = i; } else { if (rows[i] < rows[pivot]) pivot = i; } } } if (pivot == UINT_MAX) continue; // swap rows k and pivot swap_rows(A, k1, pivot); std::swap(rows[k1], rows[pivot]); // r[r_sz] = rows[k1]; r_sz++; if (r_sz >= A.n()) break; eliminate(A, 0, k1, k2, false); k2++; } std::sort(r, r + r_sz); // Copy linear independent rows to B mpz_matrix & C = A; mk(r_sz, _A.n, C); for (unsigned i = 0; i < r_sz; i++ ) { for (unsigned j = 0; j < _A.n; j++) { nm().set(C(i, j), _A(r[i], j)); } } B.swap(C); return r_sz; } void mpz_matrix_manager::display(std::ostream & out, mpz_matrix const & A, unsigned cell_width) const { out << A.m << " x " << A.n << " Matrix\n"; for (unsigned i = 0; i < A.m; i++) { for (unsigned j = 0; j < A.n; j++) { if (j > 0) out << " "; std::string s = nm().to_string(A(i, j)); if (s.size() < cell_width) { unsigned space = cell_width - static_cast(s.size()); for (unsigned k = 0; k < space; k++) out << " "; } out << s; } out << "\n"; } } z3-z3-4.4.1/src/math/realclosure/mpz_matrix.h000066400000000000000000000132401260446376700210300ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: mpz_matrix.h Abstract: Matrix with integer coefficients. This is not a general purpose module for handling matrices with integer coefficients. Instead, it is a custom package that only contains operations needed to implement Sign Determination (Algorithm 10.11) in the Book: "Algorithms in real algebraic geometry", Basu, Pollack, Roy Design choices: - Dense representation. The matrices in Alg 10.11 are small and dense. - Integer coefficients instead of rational coefficients (it only complicates the solver a little bit). Remark: in Algorithm 10.11, the coefficients of the input matrices are always in {-1, 0, 1}. During solving, bigger coefficients are produced, but they are usually very small. It may be an overkill to use mpz instead of int. We use mpz just to be safe. Remark: We do not use rational arithmetic. The solver is slightly more complicated with integers, but is saves space. Author: Leonardo (leonardo) 2013-01-07 Notes: --*/ #ifndef MPZ_MATRIX_H_ #define MPZ_MATRIX_H_ #include"mpz.h" /** \brief A mxn matrix. Remark: Algorithm 10.11 only uses square matrices, but supporting arbitrary matrices does not increase the complexity of this module. */ class mpz_matrix { friend class mpz_matrix_manager; friend class scoped_mpz_matrix; unsigned m; unsigned n; mpz * a_ij; public: mpz_matrix():m(0), n(0), a_ij(0) {} mpz const & operator()(unsigned i, unsigned j) const { SASSERT(i < m); SASSERT(j < n); return a_ij[i*n + j]; } mpz & operator()(unsigned i, unsigned j) { SASSERT(i < m); SASSERT(j < n); return a_ij[i*n + j]; } void swap(mpz_matrix & B) { std::swap(m, B.m); std::swap(n, B.n); std::swap(a_ij, B.a_ij); } mpz * row(unsigned i) const { SASSERT(i < m); return a_ij + i*n; } }; class mpz_matrix_manager { unsynch_mpz_manager & m_nm; small_object_allocator & m_allocator; static void swap_rows(mpz_matrix & A, unsigned i, unsigned j); bool normalize_row(mpz * A_i, unsigned n, mpz * b_i, bool int_solver); bool eliminate(mpz_matrix & A, mpz * b, unsigned k1, unsigned k2, bool int_solver); bool solve_core(mpz_matrix const & A, mpz * b, bool int_solver); public: mpz_matrix_manager(unsynch_mpz_manager & nm, small_object_allocator & a); ~mpz_matrix_manager(); unsynch_mpz_manager & nm() const { return m_nm; } void mk(unsigned m, unsigned n, mpz_matrix & A); void del(mpz_matrix & r); void set(mpz_matrix & A, mpz_matrix const & B); void tensor_product(mpz_matrix const & A, mpz_matrix const & B, mpz_matrix & C); /** \brief Solve A*b = c Return false if the system does not have an integer solution. \pre A is a square matrix \pre b and c are vectors of size A.n (== A.m) */ bool solve(mpz_matrix const & A, mpz * b, mpz const * c); /** \brief Solve A*b = c Return false if the system does not have an integer solution. \pre A is a square matrix \pre b and c are vectors of size A.n (== A.m) */ bool solve(mpz_matrix const & A, int * b, int const * c); /** \brief Store in B that contains the subset cols of columns of A. \pre num_cols <= A.n \pre Forall i < num_cols, cols[i] < A.n \pre Forall 0 < i < num_cols, cols[i-1] < cols[i] */ void filter_cols(mpz_matrix const & A, unsigned num_cols, unsigned const * cols, mpz_matrix & B); /** \brief Store in B the matrix obtained after applying the given permutation to the rows of A. */ void permute_rows(mpz_matrix const & A, unsigned const * p, mpz_matrix & B); /** \brief Store in r the row (ids) of A that are linear independent. \remark If there is an option between rows i and j, this method will give preference to the row that occurs first. \remark The vector r must have at least A.n() capacity The numer of linear independent rows is returned. Store the new matrix in B. */ unsigned linear_independent_rows(mpz_matrix const & A, unsigned * r, mpz_matrix & B); // method for debugging purposes void display(std::ostream & out, mpz_matrix const & A, unsigned cell_width=4) const; }; class scoped_mpz_matrix { friend class mpz_matrix_manager; mpz_matrix_manager & m_manager; mpz_matrix A; public: scoped_mpz_matrix(mpz_matrix_manager & m):m_manager(m) {} scoped_mpz_matrix(mpz_matrix const & B, mpz_matrix_manager & m):m_manager(m) { m_manager.set(A, B); } ~scoped_mpz_matrix() { m_manager.del(A); } mpz_matrix_manager & mm() const { return m_manager; } unsynch_mpz_manager & nm() const { return mm().nm(); } unsigned m() const { return A.m; } unsigned n() const { return A.n; } mpz * row(unsigned i) const { return A.row(i); } operator mpz_matrix const &() const { return A; } operator mpz_matrix &() { return A; } mpz_matrix const & get() const { return A; } mpz_matrix & get() { return A; } void swap(mpz_matrix & B) { A.swap(B); } void set(unsigned i, unsigned j, mpz const & v) { nm().set(A(i, j), v); } void set(unsigned i, unsigned j, int v) { nm().set(A(i, j), v); } mpz const & operator()(unsigned i, unsigned j) const { return A(i, j); } mpz & operator()(unsigned i, unsigned j) { return A(i, j); } int get_int(unsigned i, unsigned j) const { SASSERT(nm().is_int(A(i, j))); return nm().get_int(A(i, j)); } }; inline std::ostream & operator<<(std::ostream & out, scoped_mpz_matrix const & m) { m.mm().display(out, m); return out; } #endif z3-z3-4.4.1/src/math/realclosure/rcf.pyg000066400000000000000000000022141260446376700177570ustar00rootroot00000000000000def_module_params('rcf', description='real closed fields', export=True, params=(('use_prem', BOOL, True, "use pseudo-remainder instead of remainder when computing GCDs and Sturm-Tarski sequences"), ('clean_denominators', BOOL, True, "clean denominators before root isolation"), ('initial_precision', UINT, 24, "a value k that is the initial interval size (as 1/2^k) when creating transcendentals and approximated division"), ('inf_precision', UINT, 24, "a value k that is the initial interval size (i.e., (0, 1/2^l)) used as an approximation for infinitesimal values"), ('max_precision', UINT, 128, "during sign determination we switch from interval arithmetic to complete methods when the interval size is less than 1/2^k, where k is the max_precision"), ('lazy_algebraic_normalization', BOOL, True, "during sturm-seq and square-free polynomial computations, only normalize algebraic polynomial expressions when the definining polynomial is monic") )) z3-z3-4.4.1/src/math/realclosure/realclosure.cpp000066400000000000000000007442101260446376700215210ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: realclosure.cpp Abstract: Package for computing with elements of the realclosure of a field containing - all rationals - extended with computable transcendental real numbers (e.g., pi and e) - infinitesimals Author: Leonardo (leonardo) 2013-01-02 Notes: --*/ #include"realclosure.h" #include"rcf_params.hpp" #include"array.h" #include"mpbq.h" #include"mpz_matrix.h" #include"interval_def.h" #include"obj_ref.h" #include"ref_vector.h" #include"ref_buffer.h" #include"cooperate.h" #ifndef REALCLOSURE_INI_BUFFER_SIZE #define REALCLOSURE_INI_BUFFER_SIZE 32 #endif #ifndef REALCLOSURE_INI_SEQ_SIZE #define REALCLOSURE_INI_SEQ_SIZE 256 #endif #ifndef REALCLOSURE_INI_DIV_PRECISION #define REALCLOSURE_INI_DIV_PRECISION 24 #endif namespace realclosure { // --------------------------------- // // Intervals with binary rational endpoints // // --------------------------------- struct mpbq_config { struct numeral_manager : public mpbq_manager { // division is not precise static bool precise() { return false; } static bool field() { return true; } unsigned m_div_precision; bool m_to_plus_inf; numeral_manager(unsynch_mpq_manager & qm):mpbq_manager(qm), m_div_precision(REALCLOSURE_INI_DIV_PRECISION), m_to_plus_inf(true) { } void div(mpbq const & a, mpbq const & b, mpbq & c) { approx_div(a, b, c, m_div_precision, m_to_plus_inf); } void inv(mpbq & a) { mpbq one(1); scoped_mpbq r(*this); approx_div(one, a, r, m_div_precision, m_to_plus_inf); swap(a, r); } }; typedef mpbq numeral; numeral_manager & m_manager; struct interval { numeral m_lower; numeral m_upper; unsigned char m_lower_inf; unsigned char m_upper_inf; unsigned char m_lower_open; unsigned char m_upper_open; interval():m_lower_inf(true), m_upper_inf(true), m_lower_open(true), m_upper_open(true) {} interval(numeral & l, numeral & u):m_lower_inf(false), m_upper_inf(false), m_lower_open(true), m_upper_open(true) { swap(m_lower, l); swap(m_upper, u); } numeral & lower() { return m_lower; } numeral & upper() { return m_upper; } void set_lower_is_inf(bool f) { m_lower_inf = f; } void set_upper_is_inf(bool f) { m_upper_inf = f; } void set_lower_is_open(bool f) { m_lower_open = f; } void set_upper_is_open(bool f) { m_upper_open = f; } numeral const & lower() const { return m_lower; } numeral const & upper() const { return m_upper; } bool lower_is_inf() const { return m_lower_inf != 0; } bool upper_is_inf() const { return m_upper_inf != 0; } bool lower_is_open() const { return m_lower_open != 0; } bool upper_is_open() const { return m_upper_open != 0; } }; void set_rounding(bool to_plus_inf) { m_manager.m_to_plus_inf = to_plus_inf; } void round_to_minus_inf() { set_rounding(false); } void round_to_plus_inf() { set_rounding(true); } // Getters numeral const & lower(interval const & a) const { return a.m_lower; } numeral const & upper(interval const & a) const { return a.m_upper; } numeral & lower(interval & a) { return a.m_lower; } numeral & upper(interval & a) { return a.m_upper; } bool lower_is_open(interval const & a) const { return a.lower_is_open(); } bool upper_is_open(interval const & a) const { return a.upper_is_open(); } bool lower_is_inf(interval const & a) const { return a.lower_is_inf(); } bool upper_is_inf(interval const & a) const { return a.upper_is_inf(); } // Setters void set_lower(interval & a, numeral const & n) { m_manager.set(a.m_lower, n); } void set_upper(interval & a, numeral const & n) { m_manager.set(a.m_upper, n); } void set_lower_is_open(interval & a, bool v) { a.m_lower_open = v; } void set_upper_is_open(interval & a, bool v) { a.m_upper_open = v; } void set_lower_is_inf(interval & a, bool v) { a.m_lower_inf = v; } void set_upper_is_inf(interval & a, bool v) { a.m_upper_inf = v; } // Reference to numeral manager numeral_manager & m() const { return m_manager; } mpbq_config(numeral_manager & m):m_manager(m) {} }; typedef interval_manager mpbqi_manager; typedef mpbqi_manager::interval mpbqi; void swap(mpbqi & a, mpbqi & b) { swap(a.m_lower, b.m_lower); swap(a.m_upper, b.m_upper); std::swap(a.m_lower_inf, b.m_lower_inf); std::swap(a.m_upper_inf, b.m_upper_inf); std::swap(a.m_lower_open, b.m_lower_open); std::swap(a.m_upper_open, b.m_upper_open); } // --------------------------------- // // Values are represented as // - arbitrary precision rationals (mpq) // - rational functions on field extensions // // --------------------------------- struct value { unsigned m_ref_count; //!< Reference counter bool m_rational; //!< True if the value is represented as an abitrary precision rational value. mpbqi m_interval; //!< approximation as an interval with binary rational end-points // When performing an operation OP, we may have to make the width (upper - lower) of m_interval very small. // The precision (i.e., a small interval) needed for executing OP is usually unnecessary for subsequent operations, // This unnecessary precision will only slowdown the subsequent operations that do not need it. // To cope with this issue, we cache the value m_interval in m_old_interval whenever the width of m_interval is below // a give threshold. Then, after finishing OP, we restore the old_interval. mpbqi * m_old_interval; value(bool rat):m_ref_count(0), m_rational(rat), m_old_interval(0) {} bool is_rational() const { return m_rational; } mpbqi const & interval() const { return m_interval; } mpbqi & interval() { return m_interval; } }; struct rational_value : public value { mpq m_value; rational_value():value(true) {} }; typedef ptr_array polynomial; struct extension; bool rank_lt(extension * r1, extension * r2); struct rational_function_value : public value { polynomial m_numerator; polynomial m_denominator; // it is only needed if the extension is not algebraic. extension * m_ext; bool m_depends_on_infinitesimals; //!< True if the polynomial expression depends on infinitesimal values. rational_function_value(extension * ext):value(false), m_ext(ext), m_depends_on_infinitesimals(false) {} polynomial const & num() const { return m_numerator; } polynomial & num() { return m_numerator; } polynomial const & den() const { return m_denominator; } polynomial & den() { return m_denominator; } extension * ext() const { return m_ext; } bool depends_on_infinitesimals() const { return m_depends_on_infinitesimals; } void set_depends_on_infinitesimals(bool f) { m_depends_on_infinitesimals = f; } }; // --------------------------------- // // Field Extensions // // --------------------------------- typedef int sign; typedef std::pair p2s; typedef sarray signs; struct extension { enum kind { TRANSCENDENTAL = 0, INFINITESIMAL = 1, ALGEBRAIC = 2 }; unsigned m_ref_count; unsigned m_kind:2; unsigned m_idx:30; mpbqi m_interval; mpbqi * m_old_interval; extension(kind k, unsigned idx):m_ref_count(0), m_kind(k), m_idx(idx), m_old_interval(0) {} unsigned idx() const { return m_idx; } kind knd() const { return static_cast(m_kind); } bool is_algebraic() const { return knd() == ALGEBRAIC; } bool is_infinitesimal() const { return knd() == INFINITESIMAL; } bool is_transcendental() const { return knd() == TRANSCENDENTAL; } mpbqi const & interval() const { return m_interval; } mpbqi & interval() { return m_interval; } }; bool rank_lt(extension * r1, extension * r2) { return r1->knd() < r2->knd() || (r1->knd() == r2->knd() && r1->idx() < r2->idx()); } bool rank_eq(extension * r1, extension * r2) { return r1->knd() == r2->knd() && r1->idx() == r2->idx(); } struct rank_lt_proc { bool operator()(extension * r1, extension * r2) const { return rank_lt(r1, r2); } }; /** \brief Sign condition object, it encodes one conjunct of a sign assignment. If has to keep following m_prev to obtain the whole sign condition */ struct sign_condition { unsigned m_q_idx:31; // Sign condition for the polynomial at position m_q_idx in the field m_qs of sign_det structure unsigned m_mark:1; // auxiliary mark used during deletion int m_sign; // Sign of the polynomial associated with m_q_idx sign_condition * m_prev; // Antecedent sign_condition():m_q_idx(0), m_mark(false), m_sign(0), m_prev(0) {} sign_condition(unsigned qidx, int sign, sign_condition * prev):m_q_idx(qidx), m_mark(false), m_sign(sign), m_prev(prev) {} sign_condition * prev() const { return m_prev; } unsigned qidx() const { return m_q_idx; } int sign() const { return m_sign; } }; struct sign_det { unsigned m_ref_count; // sign_det objects may be shared between different roots of the same polynomial. mpz_matrix M_s; // Matrix used in the sign determination array m_prs; // Polynomials associated with the rows of M array m_taqrs; // Result of the tarski query for each polynomial in m_prs array m_sign_conditions; // Sign conditions associated with the columns of M array m_qs; // Polynomials used in the sign conditions. sign_det():m_ref_count(0) {} array const & qs() const { return m_qs; } sign_condition * sc(unsigned idx) const { return m_sign_conditions[idx]; } unsigned num_roots() const { return m_prs.size(); } array const & taqrs() const { return m_taqrs; } array const & prs() const { return m_prs; } }; struct algebraic : public extension { polynomial m_p; mpbqi m_iso_interval; sign_det * m_sign_det; //!< != 0 if m_iso_interval constains more than one root of m_p. unsigned m_sc_idx; //!< != UINT_MAX if m_sign_det != 0, in this case m_sc_idx < m_sign_det->m_sign_conditions.size() bool m_depends_on_infinitesimals; //!< True if the polynomial p depends on infinitesimal extensions. algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(0), m_sc_idx(0), m_depends_on_infinitesimals(false) {} polynomial const & p() const { return m_p; } bool depends_on_infinitesimals() const { return m_depends_on_infinitesimals; } sign_det * sdt() const { return m_sign_det; } unsigned sc_idx() const { return m_sc_idx; } unsigned num_roots_inside_interval() const { return m_sign_det == 0 ? 1 : m_sign_det->num_roots(); } mpbqi & iso_interval() { return m_iso_interval; } }; struct transcendental : public extension { symbol m_name; symbol m_pp_name; unsigned m_k; mk_interval & m_proc; transcendental(unsigned idx, symbol const & n, symbol const & pp_n, mk_interval & p): extension(TRANSCENDENTAL, idx), m_name(n), m_pp_name(pp_n), m_k(0), m_proc(p) {} void display(std::ostream & out, bool pp = false) const { if (pp) out << m_pp_name; else out << m_name; } }; struct infinitesimal : public extension { symbol m_name; symbol m_pp_name; infinitesimal(unsigned idx, symbol const & n, symbol const & pp_n):extension(INFINITESIMAL, idx), m_name(n), m_pp_name(pp_n) {} void display(std::ostream & out, bool pp = false) const { if (pp) { if (m_pp_name.is_numerical()) out << "ε" << m_pp_name.get_num() << ""; else out << m_pp_name; } else { if (m_name.is_numerical()) out << "eps!" << m_name.get_num(); else out << m_name; } } }; // --------------------------------- // // Predefined transcendental mk_interval procs // // --------------------------------- struct mk_pi_interval : public mk_interval { virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) { im.pi(k, r); } }; struct mk_e_interval : public mk_interval { virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) { im.e(k, r); } }; // --------------------------------- // // Manager // // --------------------------------- struct manager::imp { typedef ref_vector value_ref_vector; typedef ref_buffer value_ref_buffer; typedef obj_ref value_ref; typedef _scoped_interval scoped_mpqi; typedef _scoped_interval scoped_mpbqi; typedef sbuffer int_buffer; typedef sbuffer unsigned_buffer; small_object_allocator * m_allocator; bool m_own_allocator; unsynch_mpq_manager & m_qm; mpz_matrix_manager m_mm; mpbq_config::numeral_manager m_bqm; mpqi_manager m_qim; mpbqi_manager m_bqim; ptr_vector m_extensions[3]; value * m_one; mk_pi_interval m_mk_pi_interval; value * m_pi; mk_e_interval m_mk_e_interval; value * m_e; ptr_vector m_to_restore; //!< Set of values v s.t. v->m_old_interval != 0 ptr_vector m_ex_to_restore; // Parameters bool m_use_prem; //!< use pseudo-remainder when computing sturm sequences bool m_clean_denominators; unsigned m_ini_precision; //!< initial precision for transcendentals, infinitesimals, etc. unsigned m_max_precision; //!< Maximum precision for interval arithmetic techniques, it switches to complete methods after that unsigned m_inf_precision; //!< 2^m_inf_precision is used as the lower bound of oo and -2^m_inf_precision is used as the upper_bound of -oo scoped_mpbq m_plus_inf_approx; // lower bound for binary rational intervals used to approximate an infinite positive value scoped_mpbq m_minus_inf_approx; // upper bound for binary rational intervals used to approximate an infinite negative value bool m_lazy_algebraic_normalization; // Tracing unsigned m_exec_depth; bool m_in_aux_values; // True if we are computing SquareFree polynomials or Sturm sequences. That is, the values being computed will be discarded. volatile bool m_cancel; struct scoped_polynomial_seq { typedef ref_buffer value_seq; value_seq m_seq_coeffs; sbuffer m_begins; // start position (in m_seq_coeffs) of each polynomial in the sequence sbuffer m_szs; // size of each polynomial in the sequence public: scoped_polynomial_seq(imp & m):m_seq_coeffs(m) {} ~scoped_polynomial_seq() { } /** \brief Add a new polynomial to the sequence. The contents of p is erased. */ void push(unsigned sz, value * const * p) { m_begins.push_back(m_seq_coeffs.size()); m_szs.push_back(sz); m_seq_coeffs.append(sz, p); } /** \brief Return the number of polynomials in the sequence. */ unsigned size() const { return m_szs.size(); } /** \brief Return the vector of coefficients for the i-th polynomial in the sequence. */ value * const * coeffs(unsigned i) const { return m_seq_coeffs.c_ptr() + m_begins[i]; } /** \brief Return the size of the i-th polynomial in the sequence. */ unsigned size(unsigned i) const { return m_szs[i]; } void reset() { m_seq_coeffs.reset(); m_begins.reset(); m_szs.reset(); } scoped_polynomial_seq & operator=(scoped_polynomial_seq & s) { if (this == &s) return *this; reset(); m_seq_coeffs.append(s.m_seq_coeffs); m_begins.append(s.m_begins); m_szs.append(s.m_szs); return *this; } }; struct scoped_sign_conditions { imp & m_imp; ptr_buffer m_scs; scoped_sign_conditions(imp & m):m_imp(m) {} ~scoped_sign_conditions() { m_imp.del_sign_conditions(m_scs.size(), m_scs.c_ptr()); } sign_condition * & operator[](unsigned idx) { return m_scs[idx]; } unsigned size() const { return m_scs.size(); } bool empty() const { return m_scs.empty(); } void push_back(sign_condition * sc) { m_scs.push_back(sc); } void release() { // release ownership m_scs.reset(); } void copy_from(scoped_sign_conditions & scs) { SASSERT(this != &scs); release(); m_scs.append(scs.m_scs.size(), scs.m_scs.c_ptr()); scs.release(); } sign_condition * const * c_ptr() { return m_scs.c_ptr(); } }; struct scoped_inc_depth { imp & m_imp; scoped_inc_depth(imp & m):m_imp(m) { m_imp.m_exec_depth++; } ~scoped_inc_depth() { m_imp.m_exec_depth--; } }; #ifdef _TRACE #define INC_DEPTH() scoped_inc_depth __inc(*this) #else #define INC_DEPTH() ((void) 0) #endif imp(unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): m_allocator(a == 0 ? alloc(small_object_allocator, "realclosure") : a), m_own_allocator(a == 0), m_qm(qm), m_mm(m_qm, *m_allocator), m_bqm(m_qm), m_qim(m_qm), m_bqim(m_bqm), m_plus_inf_approx(m_bqm), m_minus_inf_approx(m_bqm) { mpq one(1); m_one = mk_rational(one); inc_ref(m_one); m_pi = 0; m_e = 0; m_exec_depth = 0; m_in_aux_values = false; m_cancel = false; updt_params(p); } ~imp() { restore_saved_intervals(); // to free memory dec_ref(m_one); dec_ref(m_pi); dec_ref(m_e); if (m_own_allocator) dealloc(m_allocator); } // Rational number manager unsynch_mpq_manager & qm() const { return m_qm; } // Binary rational number manager mpbq_config::numeral_manager & bqm() { return m_bqm; } // Rational interval manager mpqi_manager & qim() { return m_qim; } // Binary rational interval manager mpbqi_manager & bqim() { return m_bqim; } mpbqi_manager const & bqim() const { return m_bqim; } // Integer matrix manager mpz_matrix_manager & mm() { return m_mm; } small_object_allocator & allocator() { return *m_allocator; } void checkpoint() { if (m_cancel) throw exception("canceled"); cooperate("rcf"); } value * one() const { return m_one; } /** \brief Return the magnitude of the given interval. The magnitude is an approximation of the size of the interval. */ int magnitude(mpbq const & l, mpbq const & u) { SASSERT(bqm().ge(u, l)); scoped_mpbq w(bqm()); bqm().sub(u, l, w); if (bqm().is_zero(w)) return INT_MIN; SASSERT(bqm().is_pos(w)); return bqm().magnitude_ub(w); } /** \brief Return the magnitude of the given interval. The magnitude is an approximation of the size of the interval. */ int magnitude(mpbqi const & i) { if (i.lower_is_inf() || i.upper_is_inf()) return INT_MAX; else return magnitude(i.lower(), i.upper()); } /** \brief Return the magnitude of the given interval. The magnitude is an approximation of the size of the interval. */ int magnitude(mpq const & l, mpq const & u) { SASSERT(qm().ge(u, l)); scoped_mpq w(qm()); qm().sub(u, l, w); if (qm().is_zero(w)) return INT_MIN; SASSERT(qm().is_pos(w)); return static_cast(qm().log2(w.get().numerator())) + 1 - static_cast(qm().log2(w.get().denominator())); } int magnitude(scoped_mpqi const & i) { SASSERT(!i->m_lower_inf && !i->m_upper_inf); return magnitude(i->m_lower, i->m_upper); } /** \brief Return true if the magnitude of the given interval is less than the parameter m_max_precision. */ bool too_small(mpbqi const & i) { return magnitude(i) < -static_cast(m_max_precision); } #define SMALL_UNSIGNED 1 << 16 static unsigned inc_precision(unsigned prec, unsigned inc) { if (prec < SMALL_UNSIGNED) return prec + inc; else return prec; } struct scoped_set_div_precision { mpbq_config::numeral_manager & m_bqm; unsigned m_old_precision; scoped_set_div_precision(mpbq_config::numeral_manager & bqm, unsigned prec):m_bqm(bqm) { m_old_precision = m_bqm.m_div_precision; m_bqm.m_div_precision = prec; } ~scoped_set_div_precision() { m_bqm.m_div_precision = m_old_precision; } }; /** \brief c <- a/b with precision prec. */ void div(mpbqi const & a, mpbqi const & b, unsigned prec, mpbqi & c) { SASSERT(!contains_zero(a)); SASSERT(!contains_zero(b)); scoped_set_div_precision set(bqm(), prec); bqim().div(a, b, c); SASSERT(!contains_zero(c)); } /** \brief c <- a/b with precision prec. */ void div(mpbqi const & a, mpz const & b, unsigned prec, mpbqi & c) { SASSERT(!contains_zero(a)); SASSERT(!qm().is_zero(b)); scoped_mpbqi bi(bqim()); set_interval(bi, b); scoped_mpbqi r(bqim()); div(a, bi, prec, r); swap(c, r); } /** \brief Save the current interval (i.e., approximation) of the given value or extension. */ template void save_interval(T * v, ptr_vector & to_restore) { if (v->m_old_interval != 0) return; // interval was already saved. to_restore.push_back(v); inc_ref(v); v->m_old_interval = new (allocator()) mpbqi(); set_interval(*(v->m_old_interval), v->m_interval); } void save_interval(value * v) { save_interval(v, m_to_restore); } void save_interval(extension * x) { save_interval(x, m_ex_to_restore); } /** \brief Save the current interval (i.e., approximation) of the given value IF it is too small. */ void save_interval_if_too_small(value * v, unsigned new_prec) { if (new_prec > m_max_precision && !contains_zero(interval(v))) save_interval(v); } /** \brief Save the current interval (i.e., approximation) of the given value IF it is too small. */ void save_interval_if_too_small(extension * x, unsigned new_prec) { if (new_prec > m_max_precision && !contains_zero(x->m_interval)) save_interval(x); } /** \brief Restore the saved intervals (approximations) of RCF values and extensions */ template void restore_saved_intervals(ptr_vector & to_restore) { unsigned sz = to_restore.size(); for (unsigned i = 0; i < sz; i++) { T * v = to_restore[i]; set_interval(v->m_interval, *(v->m_old_interval)); bqim().del(*(v->m_old_interval)); allocator().deallocate(sizeof(mpbqi), v->m_old_interval); v->m_old_interval = 0; dec_ref(v); } to_restore.reset(); } void restore_saved_intervals() { restore_saved_intervals(m_to_restore); restore_saved_intervals(m_ex_to_restore); } void cleanup(extension::kind k) { ptr_vector & exts = m_extensions[k]; // keep removing unused slots while (!exts.empty() && exts.back() == 0) { exts.pop_back(); } } unsigned next_transcendental_idx() { cleanup(extension::TRANSCENDENTAL); return m_extensions[extension::TRANSCENDENTAL].size(); } unsigned next_infinitesimal_idx() { cleanup(extension::INFINITESIMAL); return m_extensions[extension::INFINITESIMAL].size(); } unsigned next_algebraic_idx() { cleanup(extension::ALGEBRAIC); return m_extensions[extension::ALGEBRAIC].size(); } void set_cancel(bool f) { m_cancel = f; } void updt_params(params_ref const & _p) { rcf_params p(_p); m_use_prem = p.use_prem(); m_clean_denominators = p.clean_denominators(); m_ini_precision = p.initial_precision(); m_inf_precision = p.inf_precision(); m_max_precision = p.max_precision(); m_lazy_algebraic_normalization = p.lazy_algebraic_normalization(); bqm().power(mpbq(2), m_inf_precision, m_plus_inf_approx); bqm().set(m_minus_inf_approx, m_plus_inf_approx); bqm().neg(m_minus_inf_approx); } /** \brief Reset the given polynomial. That is, after the call p is the 0 polynomial. */ void reset_p(polynomial & p) { dec_ref(p.size(), p.c_ptr()); p.finalize(allocator()); } void del_rational(rational_value * v) { bqim().del(v->m_interval); qm().del(v->m_value); allocator().deallocate(sizeof(rational_value), v); } void del_rational_function(rational_function_value * v) { bqim().del(v->m_interval); reset_p(v->num()); reset_p(v->den()); dec_ref(v->ext()); allocator().deallocate(sizeof(rational_function_value), v); } void del_value(value * v) { if (v->is_rational()) del_rational(static_cast(v)); else del_rational_function(static_cast(v)); } void finalize(array & ps) { for (unsigned i = 0; i < ps.size(); i++) reset_p(ps[i]); ps.finalize(allocator()); } void del_sign_condition(sign_condition * sc) { allocator().deallocate(sizeof(sign_condition), sc); } void del_sign_conditions(unsigned sz, sign_condition * const * to_delete) { ptr_buffer all_to_delete; for (unsigned i = 0; i < sz; i++) { sign_condition * sc = to_delete[i]; while (sc && sc->m_mark == false) { sc->m_mark = true; all_to_delete.push_back(sc); sc = sc->m_prev; } } for (unsigned i = 0; i < all_to_delete.size(); i++) { del_sign_condition(all_to_delete[i]); } } void del_sign_det(sign_det * sd) { mm().del(sd->M_s); del_sign_conditions(sd->m_sign_conditions.size(), sd->m_sign_conditions.c_ptr()); sd->m_sign_conditions.finalize(allocator()); finalize(sd->m_prs); sd->m_taqrs.finalize(allocator()); finalize(sd->m_qs); allocator().deallocate(sizeof(sign_det), sd); } void inc_ref_sign_det(sign_det * sd) { if (sd != 0) sd->m_ref_count++; } void dec_ref_sign_det(sign_det * sd) { if (sd != 0) { sd->m_ref_count--; if (sd->m_ref_count == 0) { del_sign_det(sd); } } } void del_algebraic(algebraic * a) { reset_p(a->m_p); bqim().del(a->m_interval); bqim().del(a->m_iso_interval); dec_ref_sign_det(a->m_sign_det); allocator().deallocate(sizeof(algebraic), a); } void del_transcendental(transcendental * t) { bqim().del(t->m_interval); allocator().deallocate(sizeof(transcendental), t); } void del_infinitesimal(infinitesimal * i) { bqim().del(i->m_interval); allocator().deallocate(sizeof(infinitesimal), i); } void inc_ref(extension * ext) { SASSERT(ext != 0); ext->m_ref_count++; } void dec_ref(extension * ext) { SASSERT(m_extensions[ext->knd()][ext->idx()] == ext); SASSERT(ext->m_ref_count > 0); ext->m_ref_count--; if (ext->m_ref_count == 0) { m_extensions[ext->knd()][ext->idx()] = 0; switch (ext->knd()) { case extension::TRANSCENDENTAL: del_transcendental(static_cast(ext)); break; case extension::INFINITESIMAL: del_infinitesimal(static_cast(ext)); break; case extension::ALGEBRAIC: del_algebraic(static_cast(ext)); break; } } } void inc_ref(value * v) { if (v) v->m_ref_count++; } void inc_ref(unsigned sz, value * const * p) { for (unsigned i = 0; i < sz; i++) inc_ref(p[i]); } void dec_ref(value * v) { if (v) { SASSERT(v->m_ref_count > 0); v->m_ref_count--; if (v->m_ref_count == 0) del_value(v); } } void dec_ref(unsigned sz, value * const * p) { for (unsigned i = 0; i < sz; i++) dec_ref(p[i]); } void del(numeral & a) { dec_ref(a.m_value); a.m_value = 0; } void del(numeral_vector & v) { for (unsigned i = 0; i < v.size(); i++) del(v[i]); } /** \brief Return true if the given interval is smaller than 1/2^k */ bool check_precision(mpbqi const & interval, unsigned k) { if (interval.lower_is_inf() || interval.upper_is_inf()) return false; scoped_mpbq w(bqm()); bqm().sub(interval.upper(), interval.lower(), w); return bqm().lt_1div2k(w, k); } /** \brief Return true if v is zero. */ static bool is_zero(value * v) { return v == 0; } /** \brief Return true if v is represented using a nonzero arbitrary precision rational value. */ static bool is_nz_rational(value * v) { SASSERT(v != 0); return v->is_rational(); } /** \brief Return true if v is represented as rational value one. */ bool is_rational_one(value * v) const { return !is_zero(v) && is_nz_rational(v) && qm().is_one(to_mpq(v)); } /** \brief Return true if v is represented as rational value minus one. */ bool is_rational_minus_one(value * v) const { return !is_zero(v) && is_nz_rational(v) && qm().is_minus_one(to_mpq(v)); } /** \brief Return true if v is the value one; */ bool is_one(value * v) const { return const_cast(this)->compare(v, one()) == 0; } /** \brief Return true if p is the constant polynomial where the coefficient is the rational value 1. \remark This is NOT checking whether p is actually equal to 1. That is, it is just checking the representation. */ bool is_rational_one(polynomial const & p) const { return p.size() == 1 && is_rational_one(p[0]); } bool is_rational_one(value_ref_buffer const & p) const { return p.size() == 1 && is_rational_one(p[0]); } bool is_denominator_one(rational_function_value * v) const { if (v->ext()->is_algebraic()) { SASSERT(v->den().size() == 0); // we do not use denominator for algebraic extensions return true; } else { return is_rational_one(v->den()); } } template bool is_one(polynomial const & p) const { return p.size() == 1 && is_one(p[0]); } /** \brief Return true if v is a represented as a rational function of the set of field extensions. */ static bool is_rational_function(value * v) { SASSERT(v != 0); return !(v->is_rational()); } static rational_value * to_nz_rational(value * v) { SASSERT(is_nz_rational(v)); return static_cast(v); } static rational_function_value * to_rational_function(value * v) { SASSERT(!is_nz_rational(v)); return static_cast(v); } static bool is_zero(numeral const & a) { return is_zero(a.m_value); } static bool is_nz_rational(numeral const & a) { SASSERT(!is_zero(a)); return is_nz_rational(a.m_value); } /** \brief Return true if v is not a shared value. That is, we can perform destructive updates. */ static bool is_unique(value * v) { SASSERT(v); return v->m_ref_count <= 1; } static bool is_unique(numeral const & a) { return is_unique(a.m_value); } static bool is_unique_nz_rational(value * v) { return is_nz_rational(v) && is_unique(v); } static bool is_unique_nz_rational(numeral const & a) { return is_unique_nz_rational(a.m_value); } static rational_value * to_nz_rational(numeral const & a) { SASSERT(is_nz_rational(a)); return to_nz_rational(a.m_value); } static bool is_rational_function(numeral const & a) { return is_rational_function(a.m_value); } static rational_function_value * to_rational_function(numeral const & a) { SASSERT(is_rational_function(a)); return to_rational_function(a.m_value); } static mpq & to_mpq(value * v) { SASSERT(is_nz_rational(v)); return to_nz_rational(v)->m_value; } static mpq & to_mpq(numeral const & a) { SASSERT(is_nz_rational(a)); return to_nz_rational(a)->m_value; } static int compare_rank(value * a, value * b) { SASSERT(a); SASSERT(b); if (is_nz_rational(a)) return is_nz_rational(b) ? 0 : -1; else if (is_nz_rational(b)) { SASSERT(is_rational_function(a)); return 1; } else if (rank_eq(to_rational_function(a)->ext(), to_rational_function(b)->ext())) return 0; else return rank_lt(to_rational_function(a)->ext(), to_rational_function(b)->ext()) ? -1 : 1; } static transcendental * to_transcendental(extension * ext) { SASSERT(ext->is_transcendental()); return static_cast(ext); } static infinitesimal * to_infinitesimal(extension * ext) { SASSERT(ext->is_infinitesimal()); return static_cast(ext); } static algebraic * to_algebraic(extension * ext) { SASSERT(ext->is_algebraic()); return static_cast(ext); } /** \brief Return True if the given extension depends on infinitesimal extensions. If it doesn't, then it is definitely a real value. If it does, then it may or may not be a real value. Example: Assume eps is an infinitesimal, and pi is 3.14... . Assume also that ext is the unique root between (3, 4) of the following polynomial: x^2 - (pi + eps)*x + pi*ext Thus, x is pi, but the system will return true, since its defining polynomial has infinitesimal coefficients. In the future, we should be able to factor the polynomial above as (x - eps)*(x - pi) and then detect that x is actually the root of (x - pi). */ bool depends_on_infinitesimals(extension * ext) { switch (ext->knd()) { case extension::TRANSCENDENTAL: return false; case extension::INFINITESIMAL: return true; case extension::ALGEBRAIC: return to_algebraic(ext)->depends_on_infinitesimals(); default: UNREACHABLE(); return false; } } /** \brief Return true if v is definitely a real value. */ bool depends_on_infinitesimals(value * v) const { if (is_zero(v) || is_nz_rational(v)) return false; else return to_rational_function(v)->depends_on_infinitesimals(); } bool depends_on_infinitesimals(unsigned sz, value * const * p) const { for (unsigned i = 0; i < sz; i++) if (depends_on_infinitesimals(p[i])) return true; return false; } /** \brief Set the polynomial p with the given coefficients as[0], ..., as[n-1] */ void set_p(polynomial & p, unsigned n, value * const * as) { SASSERT(n > 0); SASSERT(!is_zero(as[n - 1])); reset_p(p); p.set(allocator(), n, as); inc_ref(n, as); } /** \brief Return true if a is an open interval. */ static bool is_open_interval(mpbqi const & a) { return a.lower_is_inf() && a.upper_is_inf(); } /** \brief Return true if the interval contains zero. */ bool contains_zero(mpbqi const & a) const { return bqim().contains_zero(a); } /** \brief Set the lower bound of the given interval. */ void set_lower_core(mpbqi & a, mpbq const & k, bool open, bool inf) { bqm().set(a.lower(), k); a.set_lower_is_open(open); a.set_lower_is_inf(inf); } /** \brief a.lower <- k */ void set_lower(mpbqi & a, mpbq const & k, bool open = true) { set_lower_core(a, k, open, false); } /** \brief a.lower <- -oo */ void set_lower_inf(mpbqi & a) { bqm().reset(a.lower()); a.set_lower_is_open(true); a.set_lower_is_inf(true); } /** \brief a.lower <- 0 */ void set_lower_zero(mpbqi & a) { bqm().reset(a.lower()); a.set_lower_is_open(true); a.set_lower_is_inf(false); } /** \brief Set the upper bound of the given interval. */ void set_upper_core(mpbqi & a, mpbq const & k, bool open, bool inf) { bqm().set(a.upper(), k); a.set_upper_is_open(open); a.set_upper_is_inf(inf); } /** \brief a.upper <- k */ void set_upper(mpbqi & a, mpbq const & k, bool open = true) { set_upper_core(a, k, open, false); } /** \brief a.upper <- oo */ void set_upper_inf(mpbqi & a) { bqm().reset(a.upper()); a.set_upper_is_open(true); a.set_upper_is_inf(true); } /** \brief a.upper <- 0 */ void set_upper_zero(mpbqi & a) { bqm().reset(a.upper()); a.set_upper_is_open(true); a.set_upper_is_inf(false); } /** \brief a <- b */ void set_interval(mpbqi & a, mpbqi const & b) { set_lower_core(a, b.lower(), b.lower_is_open(), b.lower_is_inf()); set_upper_core(a, b.upper(), b.upper_is_open(), b.upper_is_inf()); } /** \brief a <- [b, b] */ void set_interval(mpbqi & a, mpbq const & b) { set_lower_core(a, b, false, false); set_upper_core(a, b, false, false); } /** \brief a <- [b, b] */ void set_interval(mpbqi & a, mpz const & b) { scoped_mpbq _b(bqm()); bqm().set(_b, b); set_lower_core(a, _b, false, false); set_upper_core(a, _b, false, false); } sign_condition * mk_sign_condition(unsigned qidx, int sign, sign_condition * prev_sc) { return new (allocator()) sign_condition(qidx, sign, prev_sc); } /** \brief Make a rational_function_value using the given extension, numerator and denominator. This method does not set the interval. It remains (-oo, oo) */ rational_function_value * mk_rational_function_value_core(extension * ext, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { rational_function_value * r = new (allocator()) rational_function_value(ext); inc_ref(ext); set_p(r->num(), num_sz, num); if (ext->is_algebraic()) { // Avoiding wasteful allocation... // We do not use the denominator for algebraic extensions SASSERT(den_sz == 0 || (den_sz == 1 && is_rational_one(den[0]))); SASSERT(r->den().size() == 0); } else { set_p(r->den(), den_sz, den); } r->set_depends_on_infinitesimals(depends_on_infinitesimals(ext) || depends_on_infinitesimals(num_sz, num) || depends_on_infinitesimals(den_sz, den)); return r; } rational_function_value * mk_rational_function_value_core(algebraic * ext, unsigned num_sz, value * const * num) { return mk_rational_function_value_core(ext, num_sz, num, 0, 0); } /** \brief Create a value using the given extension. */ rational_function_value * mk_rational_function_value(extension * ext) { value * num[2] = { 0, one() }; value * den[1] = { one() }; rational_function_value * v = mk_rational_function_value_core(ext, 2, num, 1, den); set_interval(v->interval(), ext->interval()); return v; } /** \brief Create a new infinitesimal. */ void mk_infinitesimal(symbol const & n, symbol const & pp_n, numeral & r) { unsigned idx = next_infinitesimal_idx(); infinitesimal * eps = new (allocator()) infinitesimal(idx, n, pp_n); m_extensions[extension::INFINITESIMAL].push_back(eps); set_lower(eps->interval(), mpbq(0)); set_upper(eps->interval(), mpbq(1, m_ini_precision)); set(r, mk_rational_function_value(eps)); SASSERT(sign(r) > 0); SASSERT(depends_on_infinitesimals(r)); } void mk_infinitesimal(char const * n, char const * pp_n, numeral & r) { mk_infinitesimal(symbol(n), symbol(pp_n), r); } void mk_infinitesimal(numeral & r) { mk_infinitesimal(symbol(next_infinitesimal_idx()+1), symbol(next_infinitesimal_idx()+1), r); } void refine_transcendental_interval(transcendental * t) { scoped_mpqi i(qim()); t->m_k++; t->m_proc(t->m_k, qim(), i); int m = magnitude(i); TRACE("rcf_transcendental", tout << "refine_transcendental_interval rational: " << m << "\nrational interval: "; qim().display(tout, i); tout << std::endl;); unsigned k; if (m >= 0) k = m_ini_precision; else k = inc_precision(-m, 8); scoped_mpbq l(bqm()); mpq_to_mpbqi(i->m_lower, t->interval(), k); // save lower bqm().set(l, t->interval().lower()); mpq_to_mpbqi(i->m_upper, t->interval(), k); bqm().set(t->interval().lower(), l); } void refine_transcendental_interval(transcendental * t, unsigned prec) { while (!check_precision(t->interval(), prec)) { TRACE("rcf_transcendental", tout << "refine_transcendental_interval: " << magnitude(t->interval()) << std::endl;); checkpoint(); save_interval_if_too_small(t, prec); refine_transcendental_interval(t); } } void mk_transcendental(symbol const & n, symbol const & pp_n, mk_interval & proc, numeral & r) { unsigned idx = next_transcendental_idx(); transcendental * t = new (allocator()) transcendental(idx, n, pp_n, proc); m_extensions[extension::TRANSCENDENTAL].push_back(t); while (contains_zero(t->interval())) { checkpoint(); refine_transcendental_interval(t); } set(r, mk_rational_function_value(t)); SASSERT(!depends_on_infinitesimals(r)); } void mk_transcendental(char const * p, char const * pp_n, mk_interval & proc, numeral & r) { mk_transcendental(symbol(p), symbol(pp_n), proc, r); } void mk_transcendental(mk_interval & proc, numeral & r) { mk_transcendental(symbol(next_transcendental_idx()+1), symbol(next_transcendental_idx()+1), proc, r); } void mk_pi(numeral & r) { if (m_pi) { set(r, m_pi); } else { mk_transcendental(symbol("pi"), symbol("π"), m_mk_pi_interval, r); m_pi = r.m_value; inc_ref(m_pi); } } void mk_e(numeral & r) { if (m_e) { set(r, m_e); } else { mk_transcendental(symbol("e"), symbol("e"), m_mk_e_interval, r); m_e = r.m_value; inc_ref(m_e); } } // --------------------------------- // // Root isolation // // --------------------------------- /** \brief r <- magnitude of the lower bound of |i|. That is, 2^r <= |i|.lower() Another way to view it is: 2^r is smaller than the absolute value of any element in the interval i. Return true if succeeded, and false if i contains elements that are infinitely close to 0. \pre !contains_zero(i) */ bool abs_lower_magnitude(mpbqi const & i, int & r) { SASSERT(!contains_zero(i)); if (bqim().is_P(i)) { if (bqm().is_zero(i.lower())) return false; r = bqm().magnitude_lb(i.lower()); return true; } else { SASSERT(bqim().is_N(i)); if (bqm().is_zero(i.upper())) return false; scoped_mpbq tmp(bqm()); tmp = i.upper(); bqm().neg(tmp); r = bqm().magnitude_lb(tmp); return true; } } /** \brief r <- magnitude of the upper bound of |i|. That is, |i|.upper <= 2^r Another way to view it is: 2^r is bigger than the absolute value of any element in the interval i. Return true if succeeded, and false if i is unbounded. \pre !contains_zero(i) */ bool abs_upper_magnitude(mpbqi const & i, int & r) { SASSERT(!contains_zero(i)); if (bqim().is_P(i)) { if (i.upper_is_inf()) return false; r = bqm().magnitude_ub(i.upper()); return true; } else { SASSERT(bqim().is_N(i)); if (i.lower_is_inf()) return false; scoped_mpbq tmp(bqm()); tmp = i.lower(); bqm().neg(tmp); r = bqm().magnitude_ub(tmp); return true; } } /** \brief Find positive root upper bound using Knuth's approach. Given p(x) = a_n * x^n + a_{n-1} * x^{n-1} + ... + a_0 If a_n is positive, Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} < 0 }) Then, 2*B is a bound for the positive roots Similarly, if a_n is negative Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} > 0 }) Then, 2*B is a bound for the positive roots This procedure returns a N s.t. 2*B <= 2^N The computation is performed using the intervals associated with the coefficients of the polynomial. The procedure may fail if the interval for a_n is of the form (l, 0) or (0, u). Similarly, the procedure will fail if one of the a_{n-k} has an interval of the form (l, oo) or (-oo, u). Both cases can only happen if the values of the coefficients depend on infinitesimal values. */ bool pos_root_upper_bound(unsigned n, value * const * p, int & N) { SASSERT(n > 1); SASSERT(!is_zero(p[n-1])); int lc_sign = sign(p[n-1]); SASSERT(lc_sign != 0); int lc_mag; if (!abs_lower_magnitude(interval(p[n-1]), lc_mag)) return false; N = -static_cast(m_ini_precision); for (unsigned k = 2; k <= n; k++) { value * a = p[n - k]; if (!is_zero(a) && sign(a) != lc_sign) { int a_mag; if (!abs_upper_magnitude(interval(a), a_mag)) return false; int C = (a_mag - lc_mag)/static_cast(k) + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; if (C > N) N = C; } } return true; } /** \brief Auxiliary method for creating the intervals of the coefficients of the polynomials p(-x) without actually creating p(-x). 'a' is the interval of the i-th coefficient of a polynomial a_n * x^n + ... + a_0 */ void neg_root_adjust(mpbqi const & a, unsigned i, mpbqi & r) { if (i % 2 == 0) bqim().neg(a, r); else bqim().set(r, a); } /** \brief Find negative root lower bound using Knuth's approach. This is similar to pos_root_upper_bound. In principle, we can use the same algorithm. We just have to adjust the coefficients by using the transformation p(-x). */ bool neg_root_lower_bound(unsigned n, value * const * as, int & N) { SASSERT(n > 1); SASSERT(!is_zero(as[n-1])); scoped_mpbqi aux(bqim()); neg_root_adjust(interval(as[n-1]), n-1, aux); int lc_sign = bqim().is_P(aux) ? 1 : -1; int lc_mag; if (!abs_lower_magnitude(aux, lc_mag)) return false; N = -static_cast(m_ini_precision); for (unsigned k = 2; k <= n; k++) { value * a = as[n - k]; if (!is_zero(a)) { neg_root_adjust(interval(as[n-k]), n-k, aux); int a_sign = bqim().is_P(aux) ? 1 : -1; if (a_sign != lc_sign) { int a_mag; if (!abs_upper_magnitude(aux, a_mag)) return false; int C = (a_mag - lc_mag)/static_cast(k) + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; if (C > N) N = C; } } } return true; } /** \brief q <- x^{n-1}*p(1/x) Given p(x) a_{n-1} * x^{n-1} + ... + a_0, this method stores a_0 * x^{n-1} + ... + a_{n-1} into q. */ void reverse(unsigned n, value * const * p, value_ref_buffer & q) { unsigned i = n; while (i > 0) { --i; q.push_back(p[i]); } } /** \brief To compute the lower bound for positive roots we computer the upper bound for the polynomial q(x) = x^{n-1}*p(1/x). Assume U is an upper bound for roots of q(x), i.e., (r > 0 and q(r) = 0) implies r < U. Note that if r is a root for q(x), then 1/r is a root for p(x) and 1/U is a lower bound for positive roots of p(x). The polynomial q(x) is just p(x) "reversed". */ bool pos_root_lower_bound(unsigned n, value * const * p, int & N) { value_ref_buffer q(*this); reverse(n, p, q); if (pos_root_upper_bound(n, q.c_ptr(), N)) { N = -N; return true; } else { return false; } } /** \brief See comment on pos_root_lower_bound. */ bool neg_root_upper_bound(unsigned n, value * const * p, int & N) { value_ref_buffer q(*this); reverse(n, p, q); if (neg_root_lower_bound(n, q.c_ptr(), N)) { N = -N; return true; } else { return false; } } /** \brief Store in ds all (non-constant) derivatives of p. \post d.size() == n-2 */ void mk_derivatives(unsigned n, value * const * p, scoped_polynomial_seq & ds) { SASSERT(n >= 3); // p is at least quadratic SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); value_ref_buffer p_prime(*this); derivative(n, p, p_prime); ds.push(p_prime.size(), p_prime.c_ptr()); SASSERT(n >= 3); for (unsigned i = 0; i < n - 2; i++) { SASSERT(ds.size() > 0); unsigned prev = ds.size() - 1; n = ds.size(prev); p = ds.coeffs(prev); derivative(n, p, p_prime); ds.push(p_prime.size(), p_prime.c_ptr()); } } /** \brief Auxiliary function for count_signs_at_zeros. (See comments at count_signs_at_zeros). - taq_p_q contains TaQ(p, q; interval) */ void count_signs_at_zeros_core(// Input values int taq_p_q, unsigned p_sz, value * const * p, // polynomial p unsigned q_sz, value * const * q, // polynomial q mpbqi const & interval, int num_roots, // number of roots of p in the given interval // Output values int & q_eq_0, int & q_gt_0, int & q_lt_0, value_ref_buffer & q2) { if (taq_p_q == num_roots) { // q is positive in all roots of p q_eq_0 = 0; q_gt_0 = num_roots; q_lt_0 = 0; } else if (taq_p_q == -num_roots) { // q is negative in all roots of p q_eq_0 = 0; q_gt_0 = 0; q_lt_0 = num_roots; } if (taq_p_q == num_roots - 1) { // The following assignment is the only possibility q_eq_0 = 1; q_gt_0 = num_roots - 1; q_lt_0 = 0; } else if (taq_p_q == -(num_roots - 1)) { // The following assignment is the only possibility q_eq_0 = 1; q_gt_0 = 0; q_lt_0 = num_roots - 1; } else { // Expensive case // q2 <- q^2 mul(q_sz, q, q_sz, q, q2); int taq_p_q2 = TaQ(p_sz, p, q2.size(), q2.c_ptr(), interval); SASSERT(0 <= taq_p_q2 && taq_p_q2 <= num_roots); // taq_p_q2 == q_gt_0 + q_lt_0 SASSERT((taq_p_q2 + taq_p_q) % 2 == 0); SASSERT((taq_p_q2 - taq_p_q) % 2 == 0); q_eq_0 = num_roots - taq_p_q2; q_gt_0 = (taq_p_q2 + taq_p_q)/2; q_lt_0 = (taq_p_q2 - taq_p_q)/2; } SASSERT(q_eq_0 + q_gt_0 + q_lt_0 == num_roots); } /** \brief Given polynomials p and q, and an interval, compute the number of roots of p in the interval such that: - q is zero - q is positive - q is negative \pre num_roots is the number of roots of p in the given interval. \remark num_roots == q_eq_0 + q_gt_0 + q_lt_0 */ void count_signs_at_zeros(// Input values unsigned p_sz, value * const * p, // polynomial p unsigned q_sz, value * const * q, // polynomial q mpbqi const & interval, int num_roots, // number of roots of p in the given interval // Output values int & q_eq_0, int & q_gt_0, int & q_lt_0, value_ref_buffer & q2) { TRACE("rcf_count_signs", tout << "p: "; display_poly(tout, p_sz, p); tout << "\n"; tout << "q: "; display_poly(tout, q_sz, q); tout << "\n";); SASSERT(num_roots > 0); int taq_p_q = TaQ(p_sz, p, q_sz, q, interval); count_signs_at_zeros_core(taq_p_q, p_sz, p, q_sz, q, interval, num_roots, q_eq_0, q_gt_0, q_lt_0, q2); } /** \brief Expand the set of Tarski Queries used in the sign determination algorithm. taqrs contains the results of TaQ(p, prs[i]; interval) We have that taqrs.size() == prs.size() We produce a new_taqrs and new_prs For each pr in new_prs we have pr in new_prs, TaQ(p, pr; interval) in new_taqrs pr*q in new_prs, TaQ(p, pr*q; interval) in new_taqrs if q2_sz != 0, we also have pr*q^2 in new_prs, TaQ(p, pr*q^2; interval) in new_taqrs */ void expand_taqrs(// Input values int_buffer const & taqrs, scoped_polynomial_seq const & prs, unsigned p_sz, value * const * p, unsigned q_sz, value * const * q, bool use_q2, unsigned q2_sz, value * const * q2, mpbqi const & interval, // Output values int_buffer & new_taqrs, scoped_polynomial_seq & new_prs ) { SASSERT(taqrs.size() == prs.size()); new_taqrs.reset(); new_prs.reset(); for (unsigned i = 0; i < taqrs.size(); i++) { // Add prs * 1 new_taqrs.push_back(taqrs[i]); new_prs.push(prs.size(i), prs.coeffs(i)); // Add prs * q value_ref_buffer prq(*this); mul(prs.size(i), prs.coeffs(i), q_sz, q, prq); new_taqrs.push_back(TaQ(p_sz, p, prq.size(), prq.c_ptr(), interval)); new_prs.push(prq.size(), prq.c_ptr()); // If use_q2, // Add prs * q^2 if (use_q2) { value_ref_buffer prq2(*this); mul(prs.size(i), prs.coeffs(i), q2_sz, q2, prq2); new_taqrs.push_back(TaQ(p_sz, p, prq2.size(), prq2.c_ptr(), interval)); new_prs.push(prq2.size(), prq2.c_ptr()); } } SASSERT(new_prs.size() == new_taqrs.size()); SASSERT(use_q2 || new_prs.size() == 2*prs.size()); SASSERT(!use_q2 || new_prs.size() == 3*prs.size()); } /** \brief In the sign determination algorithm main loop, we keep processing polynomials q, and checking whether they discriminate the roots of the target polynomial. The vectors sc_cardinalities contains the cardinalites of the new realizable sign conditions. That is, we started we a sequence of sign conditions sc_1, ..., sc_n, If q2_used is true, then we expanded this sequence as sc1_1 and q == 0, sc_1 and q > 0, sc_1 and q < 0, ..., sc_n and q == 0, sc_n and q > 0, sc_n and q < 0 If q2_used is false, then we considered only two possible signs of q. Thus, q is useful (i.e., it is a discriminator for the roots of p) IF If !q2_used, then There is an i s.t. sc_cardinalities[2*i] > 0 && sc_cardinalities[2*i] > 0 If q2_used, then There is an i s.t. AtLeatTwo(sc_cardinalities[3*i] > 0, sc_cardinalities[3*i+1] > 0, sc_cardinalities[3*i+2] > 0) */ bool keep_new_sc_assignment(unsigned sz, int const * sc_cardinalities, bool q2_used) { SASSERT(q2_used || sz % 2 == 0); SASSERT(!q2_used || sz % 3 == 0); if (q2_used) { for (unsigned i = 0; i < sz; i += 3) { unsigned c = 0; if (sc_cardinalities[i] > 0) c++; if (sc_cardinalities[i+1] > 0) c++; if (sc_cardinalities[i+2] > 0) c++; if (c >= 2) return true; } } else { for (unsigned i = 0; i < sz; i += 2) { if (sc_cardinalities[i] > 0 && sc_cardinalities[i+1] > 0) return true; } } return false; } /** \brief Store the polynomials in prs into the array of polynomials ps. */ void set_array_p(array & ps, scoped_polynomial_seq const & prs) { unsigned sz = prs.size(); ps.set(allocator(), sz, polynomial()); for (unsigned i = 0; i < sz; i++) { unsigned pi_sz = prs.size(i); value * const * pi = prs.coeffs(i); set_p(ps[i], pi_sz, pi); } } /** \brief Create a "sign determination" data-structure for an algebraic extension. The new object will assume the ownership of the elements stored in M and scs. M and scs will be empty after this operation. */ sign_det * mk_sign_det(mpz_matrix & M_s, scoped_polynomial_seq const & prs, int_buffer const & taqrs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) { sign_det * r = new (allocator()) sign_det(); r->M_s.swap(M_s); set_array_p(r->m_prs, prs); r->m_taqrs.set(allocator(), taqrs.size(), taqrs.c_ptr()); set_array_p(r->m_qs, qs); r->m_sign_conditions.set(allocator(), scs.size(), scs.c_ptr()); scs.release(); return r; } /** \brief Create a new algebraic extension */ algebraic * mk_algebraic(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, sign_det * sd, unsigned sc_idx) { unsigned idx = next_algebraic_idx(); algebraic * r = new (allocator()) algebraic(idx); m_extensions[extension::ALGEBRAIC].push_back(r); set_p(r->m_p, p_sz, p); set_interval(r->m_interval, interval); set_interval(r->m_iso_interval, iso_interval); r->m_sign_det = sd; inc_ref_sign_det(sd); r->m_sc_idx = sc_idx; r->m_depends_on_infinitesimals = depends_on_infinitesimals(p_sz, p); return r; } /** \brief Add a new root of p that is isolated by (interval, sd, sc_idx) to roots. */ void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, sign_det * sd, unsigned sc_idx, numeral_vector & roots) { algebraic * a = mk_algebraic(p_sz, p, interval, iso_interval, sd, sc_idx); numeral r; set(r, mk_rational_function_value(a)); roots.push_back(r); } /** \brief Simpler version of add_root that does not use sign_det data-structure. That is, interval contains only one root of p. */ void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, numeral_vector & roots) { add_root(p_sz, p, interval, iso_interval, 0, UINT_MAX, roots); } /** \brief Create (the square) matrix for sign determination of q on the roots of p. It builds matrix based on the number of root of p where q is == 0, > 0 and < 0. The resultant matrix is stored in M. Return false if the sign of q is already determined, that is only one of the q_eq_0, q_gt_0, q_lt_0 is greater than zero. If the return value is true, then the resultant matrix M has size 2x2 or 3x3 - q_eq_0 > 0, q_gt_0 > 0, q_lt_0 == 0 M <- {{1, 1}, {0, 1}} Meaning: M . [ #(q == 0), #(q > 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t [ ... ]^t represents a column matrix. - q_eq_0 > 0, q_gt_0 == 0, q_lt_0 > 0 M <- {{1, 1}, {0, -1}} Meaning: M . [ #(q == 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t - q_eq_0 == 0, q_gt_0 > 0, q_lt_0 > 0 M <- {{1, 1}, {1, -1}} Meaning: M . [ #(q > 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t - q_eq_0 > 0, q_gt_0 > 0, q_lt_0 > 0 M <- {{1, 1, 1}, {0, 1, -1}, {0, 1, 1}} Meaning: M . [ #(q == 0), #(q > 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q), TaQ(p, q^2) ]^t */ bool mk_sign_det_matrix(int q_eq_0, int q_gt_0, int q_lt_0, scoped_mpz_matrix & M) { if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 == 0) { // M <- {{1, 1}, // {0, 1}} mm().mk(2,2,M); M.set(0,0, 1); M.set(0,1, 1); M.set(1,0, 0); M.set(1,1, 1); return true; } else if (q_eq_0 > 0 && q_gt_0 == 0 && q_lt_0 > 0) { // M <- {{1, 1}, // {0, -1}} mm().mk(2,2,M); M.set(0,0, 1); M.set(0,1, 1); M.set(1,0, 0); M.set(1,1, -1); return true; } else if (q_eq_0 == 0 && q_gt_0 > 0 && q_lt_0 > 0) { // M <- {{1, 1}, // {1, -1}} mm().mk(2,2,M); M.set(0,0, 1); M.set(0,1, 1); M.set(1,0, 1); M.set(1,1, -1); return true; } else if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 > 0) { // M <- {{1, 1, 1}, // {0, 1, -1}, // {0, 1, 1}} mm().mk(3,3,M); M.set(0,0, 1); M.set(0,1, 1); M.set(0,2, 1); M.set(1,0, 0); M.set(1,1, 1); M.set(1,2, -1); M.set(2,0, 0); M.set(2,1, 1); M.set(2,2, 1); return true; } else { // Sign of q is already determined. return false; } } /** \brief Isolate roots of p in the given interval using sign conditions to distinguish between them. We need this method when the polynomial contains roots that are infinitesimally close to each other. For example, given an infinitesimal eps, the polynomial (x - 1)(x - 1 - eps) == (1 + eps) + (- 2 - eps)*x + x^2 has two roots 1 and 1+eps, we can't isolate these roots using intervals with binary rational end points. In this case, we use the signs of (some of the) derivatives in the roots. By Thom's lemma, we know we can always use the signs of the derivatives to distinguish between different roots. Remark: the polynomials do not need to be the derivatives of p. We use derivatives because a simple sequential search can be used to find the set of polynomials that can be used to distinguish between the different roots. \pre num_roots is the number of roots in the given interval */ void sign_det_isolate_roots(unsigned p_sz, value * const * p, int num_roots, mpbqi const & interval, mpbqi const & iso_interval, numeral_vector & roots) { SASSERT(num_roots >= 2); scoped_polynomial_seq der_seq(*this); mk_derivatives(p_sz, p, der_seq); CASSERT("rcf_isolate_roots", TaQ_1(p_sz, p, interval) == num_roots); scoped_mpz_matrix M_s(mm()); mm().mk(1, 1, M_s); M_s.set(0, 0, 1); // Sequence of polynomials associated with each row of M_s scoped_polynomial_seq prs(*this); value * one_p[] = { one() }; prs.push(1, one_p); // start with the polynomial one // For i in [0, poly_rows.size()), taqrs[i] == TaQ(prs[i], p; interval) int_buffer taqrs; taqrs.push_back(num_roots); // Sequence of polynomials used to discriminate the roots of p scoped_polynomial_seq qs(*this); // Sequence of sign conditions associated with the columns of M_s // These are sign conditions on the polynomials in qs. scoped_sign_conditions scs(*this); scs.push_back(0); // Starting configuration // // M_s = {{1}} Matrix of size 1x1 containing the value 1 // prs = [1] Sequence of size 1 containing the constant polynomial 1 (one is always the first element of this sequence) // taqrs = [num_roots] Sequence of size 1 containing the integer num_roots // scs = [0] Sequence of size 1 with the empty sign condition (i.e., NULL pointer) // qs = {} Empty set // scoped_mpz_matrix new_M_s(mm()); int_buffer new_taqrs; scoped_polynomial_seq new_prs(*this); scoped_sign_conditions new_scs(*this); int_buffer sc_cardinalities; unsigned_buffer cols_to_keep; unsigned_buffer new_row_idxs; unsigned i = der_seq.size(); // We perform the search backwards because the degrees are in decreasing order. while (i > 0) { --i; checkpoint(); SASSERT(M_s.m() == M_s.n()); SASSERT(M_s.m() == taqrs.size()); SASSERT(M_s.m() == scs.size()); TRACE("rcf_sign_det", tout << M_s; for (unsigned j = 0; j < scs.size(); j++) { display_sign_conditions(tout, scs[j]); tout << " = " << taqrs[j] << "\n"; } tout << "qs:\n"; for (unsigned j = 0; j < qs.size(); j++) { display_poly(tout, qs.size(j), qs.coeffs(j)); tout << "\n"; }); // We keep executing this loop until we have only one root for each sign condition in scs. // When this happens the polynomials in qs are the ones used to discriminate the roots of p. unsigned q_sz = der_seq.size(i); value * const * q = der_seq.coeffs(i); // q is a derivative of p. int q_eq_0, q_gt_0, q_lt_0; value_ref_buffer q2(*this); count_signs_at_zeros(p_sz, p, q_sz, q, iso_interval, num_roots, q_eq_0, q_gt_0, q_lt_0, q2); TRACE("rcf_sign_det", tout << "q: "; display_poly(tout, q_sz, q); tout << "\n"; tout << "#(q == 0): " << q_eq_0 << ", #(q > 0): " << q_gt_0 << ", #(q < 0): " << q_lt_0 << "\n";); scoped_mpz_matrix M(mm()); if (!mk_sign_det_matrix(q_eq_0, q_gt_0, q_lt_0, M)) { // skip q since its sign does not discriminate the roots of p continue; } bool use_q2 = M.n() == 3; mm().tensor_product(M_s, M, new_M_s); expand_taqrs(taqrs, prs, p_sz, p, q_sz, q, use_q2, q2.size(), q2.c_ptr(), iso_interval, // ---> new_taqrs, new_prs); SASSERT(new_M_s.n() == new_M_s.m()); // it is a square matrix SASSERT(new_M_s.m() == new_taqrs.size()); SASSERT(new_M_s.m() == new_prs.size()); // The system must have a solution sc_cardinalities.resize(new_taqrs.size(), 0); // Solve // new_M_s * sc_cardinalities = new_taqrs VERIFY(mm().solve(new_M_s, sc_cardinalities.c_ptr(), new_taqrs.c_ptr())); TRACE("rcf_sign_det", tout << "solution: "; for (unsigned i = 0; i < sc_cardinalities.size(); i++) { tout << sc_cardinalities[i] << " "; } tout << "\n";); // The solution must contain only positive values <= num_roots DEBUG_CODE(for (unsigned j = 0; j < sc_cardinalities.size(); j++) { SASSERT(0 <= sc_cardinalities[j] && sc_cardinalities[j] <= num_roots); }); // We should keep q only if it discriminated something. // That is, // If !use_q2, then There is an i s.t. sc_cardinalities[2*i] > 0 && sc_cardinalities[2*i] > 0 // If use_q2, then There is an i s.t. AtLeatTwo(sc_cardinalities[3*i] > 0, sc_cardinalities[3*i+1] > 0, sc_cardinalities[3*i+2] > 0) if (!keep_new_sc_assignment(sc_cardinalities.size(), sc_cardinalities.c_ptr(), use_q2)) { // skip q since it did not reduced the cardinality of the existing sign conditions. continue; } // keep q unsigned q_idx = qs.size(); qs.push(q_sz, q); // We remove the columns associated with sign conditions that have cardinality zero, // and create new extended sign condition objects for the ones that have cardinality > 0. cols_to_keep.reset(); unsigned j = 0; unsigned k = 0; unsigned step_sz = use_q2 ? 3 : 2; bool all_one = true; while (j < sc_cardinalities.size()) { sign_condition * sc = scs[k]; k++; for (unsigned s = 0; s < step_sz; s++) { // Remark: the second row of M contains the sign for q if (sc_cardinalities[j] > 0) { new_scs.push_back(mk_sign_condition(q_idx, M.get_int(1, s), sc)); cols_to_keep.push_back(j); } if (sc_cardinalities[j] > 1) all_one = false; j++; } } // Update scs with new_scs scs.copy_from(new_scs); SASSERT(new_scs.empty()); // Update M_s mm().filter_cols(new_M_s, cols_to_keep.size(), cols_to_keep.c_ptr(), M_s); SASSERT(M_s.n() == cols_to_keep.size()); new_row_idxs.resize(cols_to_keep.size(), 0); unsigned new_num_rows = mm().linear_independent_rows(M_s, new_row_idxs.c_ptr(), M_s); SASSERT(new_num_rows == cols_to_keep.size()); // Update taqrs and prs prs.reset(); taqrs.reset(); for (unsigned j = 0; j < new_num_rows; j++) { unsigned rid = new_row_idxs[j]; prs.push(new_prs.size(rid), new_prs.coeffs(rid)); taqrs.push_back(new_taqrs[rid]); } if (all_one) { // Stop each remaining sign condition in scs has cardinality one // So, they are discriminating the roots of p. break; } } TRACE("rcf_sign_det", tout << "Final state\n"; display_poly(tout, p_sz, p); tout << "\n"; tout << M_s; for (unsigned j = 0; j < scs.size(); j++) { display_sign_conditions(tout, scs[j]); tout << " = " << taqrs[j] << "\n"; } tout << "qs:\n"; for (unsigned j = 0; j < qs.size(); j++) { display_poly(tout, qs.size(j), qs.coeffs(j)); tout << "\n"; } tout << "prs:\n"; for (unsigned j = 0; j < prs.size(); j++) { display_poly(tout, prs.size(j), prs.coeffs(j)); tout << "\n"; }); SASSERT(M_s.n() == M_s.m()); SASSERT(M_s.n() == static_cast(num_roots)); sign_det * sd = mk_sign_det(M_s, prs, taqrs, qs, scs); for (unsigned idx = 0; idx < static_cast(num_roots); idx++) { add_root(p_sz, p, interval, iso_interval, sd, idx, roots); } } /** \brief Return true if p is a polynomial of the form a_{n-1}*x^{n-1} + a_0 */ bool is_nz_binomial(unsigned n, value * const * p) { SASSERT(n >= 2); SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); for (unsigned i = 1; i < n - 1; i++) { if (!is_zero(p[i])) return false; } return true; } /** \brief magnitude -> mpbq */ void magnitude_to_mpbq(int mag, bool sign, mpbq & r) { if (mag < 0) { bqm().set(r, mpbq(1, -mag)); } else { bqm().set(r, mpbq(2)); bqm().power(r, mag); } if (sign) bqm().neg(r); } /** \brief Convert magnitudes for negative roots lower and upper bounds into an interval. */ void mk_neg_interval(bool has_neg_lower, int neg_lower_N, bool has_neg_upper, int neg_upper_N, mpbqi & r) { scoped_mpbq aux(bqm()); if (!has_neg_lower) { set_lower_inf(r); } else { magnitude_to_mpbq(neg_lower_N, true, aux); set_lower(r, aux); } if (!has_neg_upper) { set_upper_zero(r); } else { magnitude_to_mpbq(neg_upper_N, true, aux); set_upper(r, aux); } } /** \brief Convert magnitudes for negative roots lower and upper bounds into an interval. */ void mk_pos_interval(bool has_pos_lower, int pos_lower_N, bool has_pos_upper, int pos_upper_N, mpbqi & r) { scoped_mpbq aux(bqm()); if (!has_pos_lower) { set_lower_zero(r); } else { magnitude_to_mpbq(pos_lower_N, false, aux); set_lower(r, aux); } if (!has_pos_upper) { set_upper_inf(r); } else { magnitude_to_mpbq(pos_upper_N, false, aux); set_upper(r, aux); } } struct bisect_ctx { unsigned m_p_sz; value * const * m_p; bool m_depends_on_infinitesimals; scoped_polynomial_seq & m_sturm_seq; numeral_vector & m_result_roots; bisect_ctx(unsigned p_sz, value * const * p, bool dinf, scoped_polynomial_seq & seq, numeral_vector & roots): m_p_sz(p_sz), m_p(p), m_depends_on_infinitesimals(dinf), m_sturm_seq(seq), m_result_roots(roots) {} }; void bisect_isolate_roots(mpbqi & interval, mpbqi & iso_interval, int lower_sv, int upper_sv, bisect_ctx & ctx) { SASSERT(lower_sv >= upper_sv); int num_roots = lower_sv - upper_sv; if (num_roots == 0) { // interval does not contain roots } else if (num_roots == 1) { // Sturm sequences are for half-open intervals (a, b] // We must check if upper is the root if (eval_sign_at(ctx.m_p_sz, ctx.m_p, interval.upper()) == 0) { // found precise root numeral r; set(r, mk_rational(interval.upper())); ctx.m_result_roots.push_back(r); } else { // interval is an isolating interval add_root(ctx.m_p_sz, ctx.m_p, interval, iso_interval, ctx.m_result_roots); } } else if (ctx.m_depends_on_infinitesimals && check_precision(interval, m_max_precision)) { // IF // - The polynomial depends on infinitesimals // - The interval contains more than one root // - The size of the interval is less than 1/2^m_max_precision // THEN // - We switch to expensive sign determination procedure, since // the roots may be infinitely close to each other. // sign_det_isolate_roots(ctx.m_p_sz, ctx.m_p, num_roots, interval, iso_interval, ctx.m_result_roots); } else { scoped_mpbq mid(bqm()); bqm().add(interval.lower(), interval.upper(), mid); bqm().div2(mid); int mid_sv = sign_variations_at(ctx.m_sturm_seq, mid); int num_left_roots = lower_sv - mid_sv; int num_right_roots = mid_sv - upper_sv; if (num_left_roots == 0) { scoped_mpbqi right_interval(bqim()); set_lower(right_interval, mid); set_upper(right_interval, interval.upper()); bisect_isolate_roots(right_interval, iso_interval, mid_sv, upper_sv, ctx); } else if (num_right_roots == 0) { scoped_mpbqi left_interval(bqim()); set_lower(left_interval, interval.lower()); set_upper(left_interval, mid); bisect_isolate_roots(left_interval, iso_interval, lower_sv, mid_sv, ctx); } else { scoped_mpbqi left_interval(bqim()); scoped_mpbqi right_interval(bqim()); set_lower(left_interval, interval.lower()); set_upper(left_interval, mid); set_lower(right_interval, mid); set_upper(right_interval, interval.upper()); bisect_isolate_roots(left_interval, left_interval, lower_sv, mid_sv, ctx); bisect_isolate_roots(right_interval, right_interval, mid_sv, upper_sv, ctx); } } } /** \brief Entry point for the root isolation procedure based on bisection. */ void bisect_isolate_roots(// Input values unsigned p_sz, value * const * p, mpbqi & interval, mpbqi & iso_interval, // Extra Input values with already computed information scoped_polynomial_seq & sturm_seq, // sturm sequence for p int lower_sv, // number of sign variations at the lower bound of interval int upper_sv, // number of sign variations at the upper bound of interval // Output values numeral_vector & roots) { bool dinf = depends_on_infinitesimals(p_sz, p); bisect_ctx ctx(p_sz, p, dinf, sturm_seq, roots); bisect_isolate_roots(interval, iso_interval, lower_sv, upper_sv, ctx); } /** \brief Root isolation for polynomials that are - nonlinear (degree > 2) - zero is not a root - square free - nonconstant */ void nl_nz_sqf_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { SASSERT(n > 2); SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); int pos_lower_N, pos_upper_N, neg_lower_N, neg_upper_N; bool has_neg_lower = neg_root_lower_bound(n, p, neg_lower_N); bool has_neg_upper = neg_root_upper_bound(n, p, neg_upper_N); bool has_pos_lower = pos_root_lower_bound(n, p, pos_lower_N); bool has_pos_upper = pos_root_upper_bound(n, p, pos_upper_N); TRACE("rcf_isolate", display_poly(tout, n, p); tout << "\n"; if (has_neg_lower) tout << "-2^" << neg_lower_N; else tout << "-oo"; tout << " < neg-roots < "; if (has_neg_upper) tout << "-2^" << neg_upper_N; else tout << "0"; tout << "\n"; if (has_pos_lower) tout << "2^" << pos_lower_N; else tout << "0"; tout << " < pos-roots < "; if (has_pos_upper) tout << "2^" << pos_upper_N; else tout << "oo"; tout << "\n";); // Compute the number of positive and negative roots scoped_polynomial_seq seq(*this); sturm_seq(n, p, seq); int num_sv_minus_inf = sign_variations_at_minus_inf(seq); int num_sv_zero = sign_variations_at_zero(seq); int num_sv_plus_inf = sign_variations_at_plus_inf(seq); int num_neg_roots = num_sv_minus_inf - num_sv_zero; int num_pos_roots = num_sv_zero - num_sv_plus_inf; TRACE("rcf_isolate", tout << "num_neg_roots: " << num_neg_roots << "\n"; tout << "num_pos_roots: " << num_pos_roots << "\n";); scoped_mpbqi pos_interval(bqim()); scoped_mpbqi neg_interval(bqim()); mk_neg_interval(has_neg_lower, neg_lower_N, has_neg_upper, neg_upper_N, neg_interval); mk_pos_interval(has_pos_lower, pos_lower_N, has_pos_upper, pos_upper_N, pos_interval); scoped_mpbqi minf_zero(bqim()); set_lower_inf(minf_zero); set_upper_zero(minf_zero); scoped_mpbqi zero_inf(bqim()); set_lower_zero(zero_inf); set_upper_inf(zero_inf); if (num_neg_roots > 0) { if (num_neg_roots == 1) { add_root(n, p, neg_interval, minf_zero, 0, UINT_MAX, roots); } else { if (has_neg_lower) { bisect_isolate_roots(n, p, neg_interval, minf_zero, seq, num_sv_minus_inf, num_sv_zero, roots); } else { sign_det_isolate_roots(n, p, num_neg_roots, minf_zero, minf_zero, roots); } } } if (num_pos_roots > 0) { if (num_pos_roots == 1) { add_root(n, p, pos_interval, zero_inf, 0, UINT_MAX, roots); } else { if (has_pos_upper) { bisect_isolate_roots(n, p, pos_interval, zero_inf, seq, num_sv_zero, num_sv_plus_inf, roots); } else { sign_det_isolate_roots(n, p, num_pos_roots, zero_inf, zero_inf, roots); } } } } /** \brief Root isolation for polynomials that are - zero is not a root - square free - nonconstant */ void nz_sqf_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { SASSERT(n > 1); SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); if (n == 2) { // we don't need a field extension for linear polynomials. numeral r; value_ref v(*this); neg(p[0], v); div(v, p[1], v); set(r, v); roots.push_back(r); } else { nl_nz_sqf_isolate_roots(n, p, roots); } } /** \brief Root isolation for polynomials where 0 is not a root, and the denominators have been cleaned when m_clean_denominators == true */ void nz_cd_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { SASSERT(n > 0); SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); SASSERT(!m_clean_denominators || has_clean_denominators(n, p)); if (n == 1) { // constant polynomial return; } value_ref_buffer sqf(*this); square_free(n, p, sqf); nz_sqf_isolate_roots(sqf.size(), sqf.c_ptr(), roots); } /** \brief Root isolation for polynomials where 0 is not a root. */ void nz_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { TRACE("rcf_isolate", tout << "nz_isolate_roots\n"; display_poly(tout, n, p); tout << "\n";); if (m_clean_denominators) { value_ref d(*this); value_ref_buffer norm_p(*this); clean_denominators(n, p, norm_p, d); nz_cd_isolate_roots(norm_p.size(), norm_p.c_ptr(), roots); } else { nz_cd_isolate_roots(n, p, roots); } } /** \brief Root isolation entry point. */ void isolate_roots(unsigned n, numeral const * p, numeral_vector & roots) { TRACE("rcf_isolate_bug", tout << "isolate_roots: "; for (unsigned i = 0; i < n; i++) { display(tout, p[i]); tout << " "; } tout << "\n";); SASSERT(n > 0); SASSERT(!is_zero(p[n-1])); if (n == 1) { // constant polynomial return; } unsigned i = 0; for (; i < n; i++) { if (!is_zero(p[i])) break; } SASSERT(i < n); SASSERT(!is_zero(p[i])); ptr_buffer nz_p; for (; i < n; i++) nz_p.push_back(p[i].m_value); nz_isolate_roots(nz_p.size(), nz_p.c_ptr(), roots); if (nz_p.size() < n) { // zero is a root roots.push_back(numeral()); } } // --------------------------------- // // Basic operations // // --------------------------------- void reset(numeral & a) { del(a); SASSERT(is_zero(a)); } int sign(value * a) { if (is_zero(a)) return 0; else if (is_nz_rational(a)) { return qm().is_pos(to_mpq(a)) ? 1 : -1; } else { SASSERT(!contains_zero(a->interval())); return bqim().is_P(a->interval()) ? 1 : -1; } } int sign(numeral const & a) { return sign(a.m_value); } /** \brief Return true the given rational function value is actually an integer. \pre a is a rational function (algebraic) extension. \remark If a is actually an integer, this method also updates its representation. */ bool is_algebraic_int(numeral const & a) { SASSERT(is_rational_function(a)); SASSERT(to_rational_function(a)->ext()->is_algebraic()); // TODO return false; } /** \brief Return true if a is an integer */ bool is_int(numeral const & a) { if (is_zero(a)) return true; else if (is_nz_rational(a)) return qm().is_int(to_mpq(a)); else { rational_function_value * rf = to_rational_function(a); switch (rf->ext()->knd()) { case extension::TRANSCENDENTAL: return false; case extension::INFINITESIMAL: return false; case extension::ALGEBRAIC: return is_algebraic_int(a); default: UNREACHABLE(); return false; } } } /** \brief Return true if a depends on infinitesimal extensions. */ bool depends_on_infinitesimals(numeral const & a) const { return depends_on_infinitesimals(a.m_value); } static void swap(mpbqi & a, mpbqi & b) { realclosure::swap(a, b); } /** \brief Store in interval an approximation of the rational number q with precision k. interval has binary rational end-points and the width is <= 1/2^k */ void mpq_to_mpbqi(mpq const & q, mpbqi & interval, unsigned k) { interval.set_lower_is_inf(false); interval.set_upper_is_inf(false); if (bqm().to_mpbq(q, interval.lower())) { bqm().set(interval.upper(), interval.lower()); interval.set_lower_is_open(false); interval.set_upper_is_open(false); } else { bqm().set(interval.upper(), interval.lower()); bqm().mul2(interval.upper()); interval.set_lower_is_open(true); interval.set_upper_is_open(true); if (qm().is_neg(q)) { ::swap(interval.lower(), interval.upper()); } while (contains_zero(interval) || !check_precision(interval, k) || bqm().is_zero(interval.lower()) || bqm().is_zero(interval.upper())) { checkpoint(); bqm().refine_lower(q, interval.lower(), interval.upper()); bqm().refine_upper(q, interval.lower(), interval.upper()); } } } void initialize_rational_value_interval(value * a) { // For rational values, we only compute the binary intervals if needed. SASSERT(is_nz_rational(a)); mpq_to_mpbqi(to_mpq(a), a->m_interval, m_ini_precision); } mpbqi & interval(value * a) const { SASSERT(a != 0); if (contains_zero(a->m_interval)) { SASSERT(is_nz_rational(a)); const_cast(this)->initialize_rational_value_interval(a); } return a->m_interval; } rational_value * mk_rational() { return new (allocator()) rational_value(); } /** \brief Make a rational and swap its value with v */ rational_value * mk_rational_and_swap(mpq & v) { SASSERT(!qm().is_zero(v)); rational_value * r = mk_rational(); ::swap(r->m_value, v); return r; } rational_value * mk_rational(mpq const & v) { SASSERT(!qm().is_zero(v)); rational_value * r = mk_rational(); qm().set(r->m_value, v); return r; } rational_value * mk_rational(mpz const & v) { SASSERT(!qm().is_zero(v)); rational_value * r = mk_rational(); qm().set(r->m_value, v); return r; } rational_value * mk_rational(mpbq const & v) { SASSERT(!bqm().is_zero(v)); scoped_mpq v_q(qm()); // v as a rational ::to_mpq(qm(), v, v_q); return mk_rational(v_q); } void reset_interval(value * a) { bqim().reset(a->m_interval); } template void update_mpq_value(value * a, T & v) { SASSERT(is_nz_rational(a)); qm().set(to_mpq(a), v); reset_interval(a); } template void update_mpq_value(numeral & a, T & v) { update_mpq_value(a.m_value, v); } /** \brief a <- n */ void set(numeral & a, int n) { if (n == 0) { reset(a); return; } del(a); a.m_value = mk_rational(); inc_ref(a.m_value); update_mpq_value(a, n); } /** \brief a <- n */ void set(numeral & a, mpz const & n) { if (qm().is_zero(n)) { reset(a); return; } del(a); a.m_value = mk_rational(); inc_ref(a.m_value); update_mpq_value(a, n); } /** \brief a <- n */ void set(numeral & a, mpq const & n) { if (qm().is_zero(n)) { reset(a); return; } del(a); a.m_value = mk_rational(); inc_ref(a.m_value); update_mpq_value(a, n); } /** \brief a <- n */ void set(numeral & a, numeral const & n) { inc_ref(n.m_value); dec_ref(a.m_value); a.m_value = n.m_value; } // --------------------------------- // // Polynomial arithmetic in RCF // // --------------------------------- /** \brief Remove 0s */ void adjust_size(value_ref_buffer & r) { while (!r.empty() && r.back() == 0) { r.pop_back(); } } /** \brief r <- p1 + p2 */ void add(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); r.reset(); value_ref a_i(*this); unsigned min = std::min(sz1, sz2); unsigned i = 0; for (; i < min; i++) { add(p1[i], p2[i], a_i); r.push_back(a_i); } for (; i < sz1; i++) r.push_back(p1[i]); for (; i < sz2; i++) r.push_back(p2[i]); SASSERT(r.size() == std::max(sz1, sz2)); adjust_size(r); } /** \brief r <- p + a */ void add(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { SASSERT(p != r.c_ptr()); SASSERT(sz > 0); r.reset(); value_ref a_0(*this); add(p[0], a, a_0); r.push_back(a_0); r.append(sz - 1, p + 1); adjust_size(r); } /** \brief r <- p1 - p2 */ void sub(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); r.reset(); value_ref a_i(*this); unsigned min = std::min(sz1, sz2); unsigned i = 0; for (; i < min; i++) { sub(p1[i], p2[i], a_i); r.push_back(a_i); } for (; i < sz1; i++) r.push_back(p1[i]); for (; i < sz2; i++) { neg(p2[i], a_i); r.push_back(a_i); } SASSERT(r.size() == std::max(sz1, sz2)); adjust_size(r); } /** \brief r <- p - a */ void sub(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { SASSERT(p != r.c_ptr()); SASSERT(sz > 0); r.reset(); value_ref a_0(*this); sub(p[0], a, a_0); r.push_back(a_0); r.append(sz - 1, p + 1); adjust_size(r); } /** \brief r <- a * p */ void mul(value * a, unsigned sz, value * const * p, value_ref_buffer & r) { SASSERT(p != r.c_ptr()); r.reset(); if (a == 0) return; value_ref a_i(*this); for (unsigned i = 0; i < sz; i++) { mul(a, p[i], a_i); r.push_back(a_i); } } /** \brief r <- p1 * p2 */ void mul(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); r.reset(); unsigned sz = sz1*sz2; r.resize(sz); if (sz1 < sz2) { std::swap(sz1, sz2); std::swap(p1, p2); } value_ref tmp(*this); for (unsigned i = 0; i < sz1; i++) { checkpoint(); if (p1[i] == 0) continue; for (unsigned j = 0; j < sz2; j++) { // r[i+j] <- r[i+j] + p1[i]*p2[j] mul(p1[i], p2[j], tmp); add(r[i+j], tmp, tmp); r.set(i+j, tmp); } } adjust_size(r); } /** \brief p <- p/a */ void div(value_ref_buffer & p, value * a) { SASSERT(!is_zero(a)); if (is_rational_one(a)) return; value_ref a_i(*this); unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { div(p[i], a, a_i); p.set(i, a_i); } } /** \brief q <- quotient(p1, p2); r <- rem(p1, p2) */ void div_rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & q, value_ref_buffer & r) { SASSERT(sz2 > 0); if (sz2 == 1) { q.reset(); q.append(sz1, p1); div(q, *p2); r.reset(); } else { q.reset(); r.reset(); r.append(sz1, p1); if (sz1 > 1) { if (sz1 >= sz2) { q.resize(sz1 - sz2 + 1); } else { SASSERT(q.empty()); } value * b_n = p2[sz2-1]; SASSERT(!is_zero(b_n)); value_ref ratio(*this); value_ref aux(*this); while (true) { checkpoint(); sz1 = r.size(); if (sz1 < sz2) { adjust_size(q); break; } unsigned m_n = sz1 - sz2; // m-n div(r[sz1 - 1], b_n, ratio); // q[m_n] <- q[m_n] + r[sz1 - 1]/b_n add(q[m_n], ratio, aux); q.set(m_n, aux); for (unsigned i = 0; i < sz2 - 1; i++) { // r[i + m_n] <- r[i + m_n] - ratio * p2[i] mul(ratio, p2[i], aux); sub(r[i + m_n], aux, aux); r.set(i + m_n, aux); } r.shrink(sz1 - 1); adjust_size(r); } } } } /** \brief q <- quotient(p1, p2) */ void div(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & q) { value_ref_buffer r(*this); div_rem(sz1, p1, sz2, p2, q, r); } /** \brief r <- p/a */ void div(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { r.reset(); value_ref a_i(*this); for (unsigned i = 0; i < sz; i++) { div(p[i], a, a_i); r.push_back(a_i); } } /** \brief r <- rem(p1, p2) */ void rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); TRACE("rcf_rem", tout << "rem\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); r.reset(); SASSERT(sz2 > 0); if (sz2 == 1) return; r.append(sz1, p1); if (sz1 <= 1) return; // r is p1 value * b_n = p2[sz2 - 1]; value_ref ratio(*this); value_ref new_a(*this); SASSERT(b_n != 0); while (true) { checkpoint(); sz1 = r.size(); if (sz1 < sz2) { TRACE("rcf_rem", tout << "rem result\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } unsigned m_n = sz1 - sz2; div(r[sz1 - 1], b_n, ratio); for (unsigned i = 0; i < sz2 - 1; i++) { mul(ratio, p2[i], new_a); sub(r[i + m_n], new_a, new_a); r.set(i + m_n, new_a); } r.shrink(sz1 - 1); adjust_size(r); } } /** \brief r <- prem(p1, p2) Pseudo-remainder We are working on a field, but it is useful to use the pseudo-remainder algorithm because it does not create rational function values. That is, if has_clean_denominators(p1) and has_clean_denominators(p2) then has_clean_denominators(r). */ void prem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, unsigned & d, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); TRACE("rcf_prem", tout << "prem\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); d = 0; r.reset(); SASSERT(sz2 > 0); if (sz2 == 1) return; r.append(sz1, p1); if (sz1 <= 1) return; // r is p1 value * b_n = p2[sz2 - 1]; SASSERT(b_n != 0); value_ref a_m(*this); value_ref new_a(*this); while (true) { checkpoint(); sz1 = r.size(); if (sz1 < sz2) { TRACE("rcf_prem", tout << "prem result\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } unsigned m_n = sz1 - sz2; // r: a_m * x^m + a_{m-1} * x^{m-1} + ... + a_0 // p2: b_n * x^n + b_{n-1} * x^{n-1} + ... + b_0 d++; a_m = r[sz1 - 1]; // don't need to update position sz1 - 1, since it will become 0 if (!is_rational_one(b_n)) { for (unsigned i = 0; i < sz1 - 1; i++) { mul(r[i], b_n, new_a); r.set(i, new_a); } } // buffer: a_m * x^m + b_n * a_{m-1} * x^{m-1} + ... + b_n * a_0 // don't need to process i = sz2 - 1, because r[sz1 - 1] will become 0. for (unsigned i = 0; i < sz2 - 1; i++) { mul(a_m, p2[i], new_a); sub(r[i + m_n], new_a, new_a); r.set(i + m_n, new_a); } r.shrink(sz1 - 1); adjust_size(r); } } void prem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { unsigned d; prem(sz1, p1, sz2, p2, d, r); } /** \brief r <- -p */ void neg(unsigned sz, value * const * p, value_ref_buffer & r) { SASSERT(p != r.c_ptr()); r.reset(); value_ref a_i(*this); for (unsigned i = 0; i < sz; i++) { neg(p[i], a_i); r.push_back(a_i); } } /** \brief r <- -r */ void neg(value_ref_buffer & r) { value_ref a_i(*this); unsigned sz = r.size(); for (unsigned i = 0; i < sz; i++) { neg(r[i], a_i); r.set(i, a_i); } } /** \brief p <- -p */ void neg(polynomial & p) { value_ref a_i(*this); unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { neg(p[i], a_i); inc_ref(a_i.get()); dec_ref(p[i]); p[i] = a_i.get(); } } /** \brief r <- srem(p1, p2) Signed remainder */ void srem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); rem(sz1, p1, sz2, p2, r); neg(r); } /** \brief r <- sprem(p1, p2) Signed pseudo remainder */ void sprem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); unsigned d; prem(sz1, p1, sz2, p2, d, r); // We should not flip the sign if d is odd and leading coefficient of p2 is negative. if (d % 2 == 0 || (sz2 > 0 && sign(p2[sz2-1]) > 0)) neg(r); } // --------------------------------- // // Structural equality // // --------------------------------- /** \brief Values a and b are said to be "structurally" equal if: - a and b are 0. - a and b are rationals and compare(a, b) == 0 - a and b are rational function values p_a(x)/q_a(x) and p_b(y)/q_b(y) where x and y are field extensions, and * x == y (pointer equality, i.e., they are the same field extension object). * Every coefficient of p_a is structurally equal to every coefficient of p_b * Every coefficient of q_a is structurally equal to every coefficient of q_b Clearly structural equality implies equality, but the reverse is not true. */ bool struct_eq(value * a, value * b) const { if (a == b) return true; else if (a == 0 || b == 0) return false; else if (is_nz_rational(a) && is_nz_rational(b)) return qm().eq(to_mpq(a), to_mpq(b)); else if (is_nz_rational(a) || is_nz_rational(b)) return false; else { SASSERT(is_rational_function(a)); SASSERT(is_rational_function(b)); rational_function_value * rf_a = to_rational_function(a); rational_function_value * rf_b = to_rational_function(b); if (rf_a->ext() != rf_b->ext()) return false; return struct_eq(rf_a->num(), rf_b->num()) && struct_eq(rf_a->den(), rf_b->den()); } } /** Auxiliary method for bool struct_eq(value * a, value * b) */ bool struct_eq(unsigned sz_a, value * const * p_a, unsigned sz_b, value * const * p_b) const { if (sz_a != sz_b) return false; for (unsigned i = 0; i < sz_a; i++) { if (!struct_eq(p_a[i], p_b[i])) return false; } return true; } /** Auxiliary method for bool struct_eq(value * a, value * b) */ bool struct_eq(polynomial const & p_a, polynomial const & p_b) const { return struct_eq(p_a.size(), p_a.c_ptr(), p_b.size(), p_b.c_ptr()); } // --------------------------------- // // Clean denominators // // --------------------------------- /** \brief We say 'a' has "clean" denominators if - a is 0 - a is a rational_value that is an integer - a is a rational_function_value of the form p_a(x)/1 where the coefficients of p_a also have clean denominators. */ bool has_clean_denominators(value * a) const { if (a == 0) return true; else if (is_nz_rational(a)) return qm().is_int(to_mpq(a)); else { rational_function_value * rf_a = to_rational_function(a); return is_denominator_one(rf_a) && has_clean_denominators(rf_a->num()); } } /** \brief See comment at has_clean_denominators(value * a) */ bool has_clean_denominators(unsigned sz, value * const * p) const { for (unsigned i = 0; i < sz; i++) { if (!has_clean_denominators(p[i])) return false; } return true; } /** \brief See comment at has_clean_denominators(value * a) */ bool has_clean_denominators(polynomial const & p) const { return has_clean_denominators(p.size(), p.c_ptr()); } /** \brief "Clean" the denominators of 'a'. That is, return p and q s.t. a == p/q and has_clean_denominators(p) and has_clean_denominators(q) */ void clean_denominators_core(value * a, value_ref & p, value_ref & q) { INC_DEPTH(); TRACE("rcf_clean", tout << "clean_denominators_core [" << m_exec_depth << "]\na: "; display(tout, a, false); tout << "\n";); p.reset(); q.reset(); if (a == 0) { p = a; q = one(); } else if (is_nz_rational(a)) { p = mk_rational(to_mpq(a).numerator()); q = mk_rational(to_mpq(a).denominator()); } else { rational_function_value * rf_a = to_rational_function(a); value_ref_buffer p_num(*this), p_den(*this); value_ref d_num(*this), d_den(*this); clean_denominators_core(rf_a->num(), p_num, d_num); if (is_denominator_one(rf_a)) { p_den.push_back(one()); d_den = one(); } else { clean_denominators_core(rf_a->den(), p_den, d_den); } value_ref x(*this); x = mk_rational_function_value(rf_a->ext()); mk_polynomial_value(p_num.size(), p_num.c_ptr(), x, p); mk_polynomial_value(p_den.size(), p_den.c_ptr(), x, q); if (!struct_eq(d_den, d_num)) { mul(p, d_den, p); mul(q, d_num, q); } if (sign(q) < 0) { // make sure the denominator is positive neg(p, p); neg(q, q); } } } /** \brief Clean the denominators of the polynomial p, it returns clean_p and d s.t. p = clean_p/d and has_clean_denominators(clean_p) && has_clean_denominators(d) */ void clean_denominators_core(unsigned p_sz, value * const * p, value_ref_buffer & norm_p, value_ref & d) { SASSERT(p_sz >= 1); value_ref_buffer nums(*this), dens(*this); value_ref a_n(*this), a_d(*this); bool all_one = true; for (unsigned i = 0; i < p_sz; i++) { if (p[i]) { clean_denominators_core(p[i], a_n, a_d); nums.push_back(a_n); if (!is_rational_one(a_d)) all_one = false; dens.push_back(a_d); } else { nums.push_back(0); dens.push_back(0); } } if (all_one) { norm_p = nums; d = one(); } else { // Compute lcm of the integer elements in dens. // This is a little trick to control the coefficient growth. // We don't compute lcm of the other elements of dens because it is too expensive. scoped_mpq lcm_z(qm()); bool found_z = false; SASSERT(nums.size() == p_sz); SASSERT(dens.size() == p_sz); for (unsigned i = 0; i < p_sz; i++) { if (!dens[i]) continue; if (is_nz_rational(dens[i])) { mpq const & _d = to_mpq(dens[i]); SASSERT(qm().is_int(_d)); if (!found_z) { found_z = true; qm().set(lcm_z, _d); } else { qm().lcm(lcm_z, _d, lcm_z); } } } value_ref lcm(*this); if (found_z) { lcm = mk_rational(lcm_z); } else { lcm = one(); } // Compute the multipliers for nums. // Compute norm_p and d // // We do NOT use GCD to compute the LCM of the denominators of non-rational values. // However, we detect structurally equivalent denominators. // // Thus a/(b+1) + c/(b+1) is converted into a*c/(b+1) instead of (a*(b+1) + c*(b+1))/(b+1)^2 norm_p.reset(); d = lcm; value_ref_buffer multipliers(*this); value_ref m(*this); for (unsigned i = 0; i < p_sz; i++) { if (!nums[i]) { norm_p.push_back(0); } else { SASSERT(dens[i]); bool is_z; if (!is_nz_rational(dens[i])) { m = lcm; is_z = false; } else { scoped_mpq num_z(qm()); qm().div(lcm_z, to_mpq(dens[i]), num_z); SASSERT(qm().is_int(num_z)); m = mk_rational_and_swap(num_z); is_z = true; } bool found_lt_eq = false; for (unsigned j = 0; j < p_sz; j++) { TRACE("rcf_clean_bug", tout << "j: " << j << " "; display(tout, m, false); tout << "\n";); if (!dens[j]) continue; if (i != j && !is_nz_rational(dens[j])) { if (struct_eq(dens[i], dens[j])) { if (j < i) found_lt_eq = true; } else { mul(m, dens[j], m); } } } if (!is_z && !found_lt_eq) { mul(dens[i], d, d); } mul(m, nums[i], m); norm_p.push_back(m); } } } SASSERT(norm_p.size() == p_sz); } void clean_denominators_core(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { clean_denominators_core(p.size(), p.c_ptr(), norm_p, d); } void clean_denominators(value * a, value_ref & p, value_ref & q) { if (has_clean_denominators(a)) { p = a; q = one(); } else { clean_denominators_core(a, p, q); } } void clean_denominators(unsigned sz, value * const * p, value_ref_buffer & norm_p, value_ref & d) { if (has_clean_denominators(sz, p)) { norm_p.append(sz, p); d = one(); } else { clean_denominators_core(sz, p, norm_p, d); } } void clean_denominators(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { clean_denominators(p.size(), p.c_ptr(), norm_p, d); } void clean_denominators(numeral const & a, numeral & p, numeral & q) { value_ref _p(*this), _q(*this); clean_denominators(a.m_value, _p, _q); set(p, _p); set(q, _q); } // --------------------------------- // // GCD of integer coefficients // // --------------------------------- /** \brief If has_clean_denominators(a), then this method store the gcd of the integer coefficients in g. If !has_clean_denominators(a) it returns false. If g != 0, then it will compute the gcd of g and the coefficients in a. */ bool gcd_int_coeffs(value * a, mpz & g) { if (a == 0) { return false; } else if (is_nz_rational(a)) { if (!qm().is_int(to_mpq(a))) return false; else if (qm().is_zero(g)) { qm().set(g, to_mpq(a).numerator()); qm().abs(g); } else { qm().gcd(g, to_mpq(a).numerator(), g); } return true; } else { rational_function_value * rf_a = to_rational_function(a); if (!is_denominator_one(rf_a)) return false; else return gcd_int_coeffs(rf_a->num(), g); } } /** \brief See comment in gcd_int_coeffs(value * a, mpz & g) */ bool gcd_int_coeffs(unsigned p_sz, value * const * p, mpz & g) { if (p_sz == 0) { return false; } else { for (unsigned i = 0; i < p_sz; i++) { if (p[i]) { if (!gcd_int_coeffs(p[i], g)) return false; if (qm().is_one(g)) return true; } } return true; } } /** \brief See comment in gcd_int_coeffs(value * a, mpz & g) */ bool gcd_int_coeffs(polynomial const & p, mpz & g) { return gcd_int_coeffs(p.size(), p.c_ptr(), g); } /** \brief Compute gcd_int_coeffs and divide p by it (if applicable). */ void normalize_int_coeffs(value_ref_buffer & p) { scoped_mpz g(qm()); if (gcd_int_coeffs(p.size(), p.c_ptr(), g) && !qm().is_one(g)) { SASSERT(qm().is_pos(g)); value_ref a(*this); for (unsigned i = 0; i < p.size(); i++) { if (p[i]) { a = p[i]; p.set(i, 0); exact_div_z(a, g); p.set(i, a); } } } } /** \brief a <- a/b where b > 0 Auxiliary function for normalize_int_coeffs. It assumes has_clean_denominators(a), and that b divides all integer coefficients. FUTURE: perform the operation using destructive updates when a is not shared. */ void exact_div_z(value_ref & a, mpz const & b) { if (a == 0) { return; } else if (is_nz_rational(a)) { scoped_mpq r(qm()); SASSERT(qm().is_int(to_mpq(a))); qm().div(to_mpq(a), b, r); a = mk_rational_and_swap(r); } else { rational_function_value * rf = to_rational_function(a); SASSERT(is_denominator_one(rf)); value_ref_buffer new_ais(*this); value_ref ai(*this); polynomial const & p = rf->num(); for (unsigned i = 0; i < p.size(); i++) { if (p[i]) { ai = p[i]; exact_div_z(ai, b); new_ais.push_back(ai); } else { new_ais.push_back(0); } } rational_function_value * r = mk_rational_function_value_core(rf->ext(), new_ais.size(), new_ais.c_ptr(), 1, &m_one); set_interval(r->m_interval, rf->m_interval); a = r; // divide upper and lower by b div(r->m_interval, b, m_ini_precision, r->m_interval); } } // --------------------------------- // // GCD // // --------------------------------- bool is_monic(value_ref_buffer const & p) { return p.size() > 0 && is_rational_one(p[p.size() - 1]); } bool is_monic(polynomial const & p) { return p.size() > 0 && is_rational_one(p[p.size() - 1]); } /** \brief Force the leading coefficient of p to be 1. */ void mk_monic(value_ref_buffer & p) { unsigned sz = p.size(); if (sz > 0) { value_ref a_i(*this); SASSERT(p[sz-1] != 0); if (!is_rational_one(p[sz-1])) { for (unsigned i = 0; i < sz - 1; i++) { div(p[i], p[sz-1], a_i); p.set(i, a_i); } p.set(sz-1, one()); } } } /** \brief r <- gcd(p1, p2) */ void gcd(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { INC_DEPTH(); TRACE("rcf_gcd", tout << "GCD [" << m_exec_depth << "]\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); if (sz1 == 0) { r.append(sz2, p2); mk_monic(r); } else if (sz2 == 0) { r.append(sz1, p1); mk_monic(r); } else { value_ref_buffer A(*this); value_ref_buffer B(*this); value_ref_buffer R(*this); A.append(sz1, p1); B.append(sz2, p2); while (true) { TRACE("rcf_gcd", tout << "A: "; display_poly(tout, A.size(), A.c_ptr()); tout << "\n"; tout << "B: "; display_poly(tout, B.size(), B.c_ptr()); tout << "\n";); if (B.empty()) { mk_monic(A); r = A; TRACE("rcf_gcd", tout << "gcd result: "; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } rem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), R); A = B; B = R; } } } void flip_sign_if_lc_neg(value_ref_buffer & r) { unsigned sz = r.size(); if (sz == 0) return; if (sign(r[sz - 1]) < 0) neg(r); } void prem_gcd(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { INC_DEPTH(); TRACE("rcf_gcd", tout << "prem-GCD [" << m_exec_depth << "]\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); if (sz1 == 0) { r.append(sz2, p2); flip_sign_if_lc_neg(r); } else if (sz2 == 0) { r.append(sz1, p1); flip_sign_if_lc_neg(r); } else { value_ref_buffer A(*this); value_ref_buffer B(*this); value_ref_buffer R(*this); A.append(sz1, p1); B.append(sz2, p2); while (true) { TRACE("rcf_gcd", tout << "A: "; display_poly(tout, A.size(), A.c_ptr()); tout << "\n"; tout << "B: "; display_poly(tout, B.size(), B.c_ptr()); tout << "\n";); if (B.empty()) { normalize_int_coeffs(A); flip_sign_if_lc_neg(A); r = A; TRACE("rcf_gcd", tout << "gcd result: "; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } prem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), R); normalize_int_coeffs(R); A = B; B = R; } } } // --------------------------------- // // Derivatives and Sturm-Tarski Sequences // // --------------------------------- /** \brief r <- dp/dx */ void derivative(unsigned sz, value * const * p, value_ref_buffer & r) { r.reset(); if (sz > 1) { for (unsigned i = 1; i < sz; i++) { mpq i_mpq(i); value_ref a_i(*this); a_i = mk_rational_and_swap(i_mpq); mul(a_i, p[i], a_i); r.push_back(a_i); } adjust_size(r); } } /** \brief r <- squarefree(p) Store in r the square free factors of p. */ void square_free(unsigned sz, value * const * p, value_ref_buffer & r) { flet set(m_in_aux_values, true); if (sz <= 1) { r.append(sz, p); } else { value_ref_buffer p_prime(*this); value_ref_buffer g(*this); derivative(sz, p, p_prime); if (m_use_prem) prem_gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); else gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); if (g.size() <= 1) { r.append(sz, p); } else { div(sz, p, g.size(), g.c_ptr(), r); if (m_use_prem) normalize_int_coeffs(r); } } } /** \brief Keep expanding the Sturm sequence starting at seq */ void sturm_seq_core(scoped_polynomial_seq & seq) { INC_DEPTH(); flet set(m_in_aux_values, true); SASSERT(seq.size() >= 2); TRACE("rcf_sturm_seq", unsigned sz = seq.size(); tout << "sturm_seq_core [" << m_exec_depth << "]\n"; display_poly(tout, seq.size(sz-2), seq.coeffs(sz-2)); tout << "\n"; display_poly(tout, seq.size(sz-1), seq.coeffs(sz-1)); tout << "\n";); value_ref_buffer r(*this); while (true) { unsigned sz = seq.size(); if (m_use_prem) { sprem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); normalize_int_coeffs(r); } else { srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); } TRACE("rcf_sturm_seq", tout << "sturm_seq_core [" << m_exec_depth << "], new polynomial\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); if (r.empty()) return; seq.push(r.size(), r.c_ptr()); } } /** \brief Store in seq the Sturm sequence for (p1; p2) */ void sturm_seq(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, scoped_polynomial_seq & seq) { seq.reset(); seq.push(sz1, p1); seq.push(sz2, p2); sturm_seq_core(seq); } /** \brief Store in seq the Sturm sequence for (p; p') */ void sturm_seq(unsigned sz, value * const * p, scoped_polynomial_seq & seq) { seq.reset(); value_ref_buffer p_prime(*this); seq.push(sz, p); derivative(sz, p, p_prime); seq.push(p_prime.size(), p_prime.c_ptr()); sturm_seq_core(seq); } /** \brief Store in seq the Sturm sequence for (p1; p1' * p2) */ void sturm_tarski_seq(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, scoped_polynomial_seq & seq) { seq.reset(); value_ref_buffer p1_prime(*this); value_ref_buffer p1_prime_p2(*this); seq.push(sz1, p1); derivative(sz1, p1, p1_prime); mul(p1_prime.size(), p1_prime.c_ptr(), sz2, p2, p1_prime_p2); seq.push(p1_prime_p2.size(), p1_prime_p2.c_ptr()); sturm_seq_core(seq); } // --------------------------------- // // Sign evaluation for polynomials // That is, sign of p(x) at b // // --------------------------------- /** \brief Return the sign of p(0) */ int eval_sign_at_zero(unsigned n, value * const * p) { if (n == 0) return 0; return sign(p[0]); } /** \brief Return the sign of p(oo) */ int eval_sign_at_plus_inf(unsigned n, value * const * p) { if (n == 0) return 0; SASSERT(!is_zero(p[n-1])); // p is well formed return sign(p[n-1]); } /** \brief Return the sign of p(-oo) */ int eval_sign_at_minus_inf(unsigned n, value * const * p) { if (n == 0) return 0; SASSERT(!is_zero(p[n-1])); // p is well formed unsigned degree = n - 1; if (degree % 2 == 0) return sign(p[n - 1]); else return -sign(p[n - 1]); } /** \brief Store in r an approximation (as an interval) for the interval p(b). \pre n >= 2 */ void eval_sign_at_approx(unsigned n, value * const * p, mpbq const & b, mpbqi & r) { SASSERT(n >= 2); // We compute r using the Horner Sequence // ((a_{n-1}*b + a_{n-2})*b + a_{n-3})*b + a_{n-4} ... // where a_i's are the intervals associated with coefficients of p. SASSERT(n > 0); SASSERT(p[n - 1] != 0); scoped_mpbqi bi(bqim()); set_interval(bi, b); // bi <- [b, b] // r <- a_n * bi bqim().mul(interval(p[n - 1]), bi, r); unsigned i = n - 1; while (i > 0) { checkpoint(); --i; if (p[i] != 0) bqim().add(r, interval(p[i]), r); if (i > 0) bqim().mul(r, bi, r); } } /** \brief We say a polynomial has "refinable" approximated coefficients if the intervals approximating the coefficients do not have -oo or oo as lower/upper bounds. */ bool has_refineable_approx_coeffs(unsigned n, value * const * p) { for (unsigned i = 0; i < n; i++) { if (p[i] != 0) { mpbqi & a_i = interval(p[i]); if (a_i.lower_is_inf() || a_i.upper_is_inf()) return false; } } return true; } /** \brief r <- p(b) */ void mk_polynomial_value(unsigned n, value * const * p, value * b, value_ref & r) { SASSERT(n > 0); if (n == 1 || b == 0) { r = p[0]; } else { SASSERT(n >= 2); // We compute the result using the Horner Sequence // ((a_{n-1}*b + a_{n-2})*b + a_{n-3})*b + a_{n-4} ... // where a_i's are the coefficients of p. mul(p[n - 1], b, r); // r <- a_{n-1} * b unsigned i = n - 1; while (i > 0) { --i; if (p[i] != 0) add(r, p[i], r); // r <- r + a_i if (i > 0) mul(r, b, r); // r <- r * b } } } /** \brief Evaluate the sign of p(b) by computing a value object. */ int expensive_eval_sign_at(unsigned n, value * const * p, mpbq const & b) { flet set(m_in_aux_values, true); SASSERT(n > 1); SASSERT(p[n - 1] != 0); // Actually, given b = c/2^k, we compute the sign of (2^k)^n*p(c) // Original Horner Sequence // ((a_n * b + a_{n-1})*b + a_{n-2})*b + a_{n-3} ... // Variation of the Horner Sequence for (2^k)^n*p(b) // ((a_n * c + a_{n-1}*2_k)*c + a_{n-2}*(2_k)^2)*c + a_{n-3}*(2_k)^3 ... + a_0*(2_k)^n scoped_mpz mpz_twok(qm()); qm().mul2k(mpz(1), b.k(), mpz_twok); value_ref twok(*this), twok_i(*this); twok = mk_rational(mpz_twok); twok_i = twok; value_ref c(*this); c = mk_rational(b.numerator()); value_ref r(*this), ak(*this), rc(*this); r = p[n-1]; unsigned i = n-1; while (i > 0) { --i; if (is_zero(p[i])) { mul(r, c, r); } else { // ak <- a_i * (2^k)^(n-i) mul(p[i], twok_i, ak); // r <- r * c + a_i * (2^k)^(n-i) mul(r, c, rc); add(ak, rc, r); } mul(twok_i, twok, twok_i); } return sign(r); } /** \brief Find the magnitude of the biggest interval use to approximate coefficients of p. \pre has_refineable_approx_coeffs(n, p) */ int find_biggest_interval_magnitude(unsigned n, value * const * p) { int r = INT_MIN; for (unsigned i = 0; i < n; i++) { if (p[i] != 0) { mpbqi & a_i = interval(p[i]); SASSERT(!a_i.lower_is_inf() && !a_i.upper_is_inf()); int m = magnitude(a_i); if (m > r) r = m; } } return r; } /** \brief Return the sign of p(b) */ int eval_sign_at(unsigned n, value * const * p, mpbq const & b) { if (n == 0) return 0; else if (n == 1) return sign(p[0]); else { scoped_mpbqi r(bqim()); eval_sign_at_approx(n, p, b, r); if (!contains_zero(r)) { // we are done return bqim().is_P(r) ? 1 : -1; } else if (!has_refineable_approx_coeffs(n, p)) { return expensive_eval_sign_at(n, p, b); } else { int m = find_biggest_interval_magnitude(n, p); unsigned prec; if (m >= 0) prec = 1; else prec = -m; SASSERT(prec >= 1); while (prec <= m_max_precision) { checkpoint(); if (!refine_coeffs_interval(n, p, prec)) { // Failed to refine intervals, p must depend on infinitesimal values. // This can happen even if all intervals of coefficients of p are bounded. return expensive_eval_sign_at(n, p, b); } eval_sign_at_approx(n, p, b, r); if (!contains_zero(r)) { // we are done return bqim().is_P(r) ? 1 : -1; } prec++; // increase precision and try again. } return expensive_eval_sign_at(n, p, b); } } } // --------------------------------- // // Sign variations in polynomial sequences. // // --------------------------------- enum location { ZERO, MINUS_INF, PLUS_INF, MPBQ }; /** \brief Compute the number of sign variations at position (loc, b) in the given polynomial sequence. The position (loc, b) should be interpreted in the following way: - (ZERO, *) -> number of sign variations at 0. - (MINUS_INF, *) -> number of sign variations at -oo. - (PLUS_INF, *) -> number of sign variations at oo. - (MPBQ, b) -> number of sign variations at binary rational b. */ unsigned sign_variations_at_core(scoped_polynomial_seq const & seq, location loc, mpbq const & b) { unsigned sz = seq.size(); if (sz <= 1) return 0; unsigned r = 0; int sign, prev_sign; sign = 0; prev_sign = 0; unsigned i = 0; for (; i < sz; i++) { // find next nonzero unsigned psz = seq.size(i); value * const * p = seq.coeffs(i); switch (loc) { case PLUS_INF: sign = eval_sign_at_plus_inf(psz, p); break; case MINUS_INF: sign = eval_sign_at_minus_inf(psz, p); break; case ZERO: sign = eval_sign_at_zero(psz, p); break; case MPBQ: sign = eval_sign_at(psz, p, b); break; default: UNREACHABLE(); break; } if (sign == 0) continue; SASSERT(sign == 1 || sign == -1); // in the first iteration prev_sign == 0, then r is never incremented. if (sign != prev_sign && prev_sign != 0) r++; // move to the next prev_sign = sign; } return r; } unsigned sign_variations_at_minus_inf(scoped_polynomial_seq const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, MINUS_INF, dummy); } unsigned sign_variations_at_plus_inf(scoped_polynomial_seq const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, PLUS_INF, dummy); } unsigned sign_variations_at_zero(scoped_polynomial_seq const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, ZERO, dummy); } unsigned sign_variations_at(scoped_polynomial_seq const & seq, mpbq const & b) { return sign_variations_at_core(seq, MPBQ, b); } int sign_variations_at_lower(scoped_polynomial_seq & seq, mpbqi const & interval) { if (interval.lower_is_inf()) return sign_variations_at_minus_inf(seq); else if (bqm().is_zero(interval.lower())) return sign_variations_at_zero(seq); else return sign_variations_at(seq, interval.lower()); } int sign_variations_at_upper(scoped_polynomial_seq & seq, mpbqi const & interval) { if (interval.upper_is_inf()) return sign_variations_at_plus_inf(seq); else if (bqm().is_zero(interval.upper())) return sign_variations_at_zero(seq); else return sign_variations_at(seq, interval.upper()); } // --------------------------------- // // Tarski-Queries (see BPR book) // // --------------------------------- /** \brief Given a polynomial Sturm sequence seq for (P; P' * Q) and an interval (a, b], it returns TaQ(Q, P; a, b) = #{ x \in (a, b] | P(x) = 0 and Q(x) > 0 } - #{ x \in (a, b] | P(x) = 0 and Q(x) < 0 } \remark This method ignores whether the interval end-points are closed or open. */ int TaQ(scoped_polynomial_seq & seq, mpbqi const & interval) { return sign_variations_at_lower(seq, interval) - sign_variations_at_upper(seq, interval); } /** \brief Return TaQ(Q, P; a, b) = #{ x \in (a, b] | P(x) = 0 and Q(x) > 0 } - #{ x \in (a, b] | P(x) = 0 and Q(x) < 0 } \remark This method ignores whether the interval end-points are closed or open. */ int TaQ(unsigned p_sz, value * const * p, unsigned q_sz, value * const * q, mpbqi const & interval) { INC_DEPTH(); TRACE("rcf_TaQ", tout << "TaQ [" << m_exec_depth << "]\n"; display_poly(tout, p_sz, p); tout << "\n"; display_poly(tout, q_sz, q); tout << "\n";); scoped_polynomial_seq seq(*this); sturm_tarski_seq(p_sz, p, q_sz, q, seq); return TaQ(seq, interval); } /** \brief Return TaQ(1, P; a, b) = #{ x \in (a, b] | P(x) = 0 } \remark This method ignores whether the interval end-points are closed or open. */ int TaQ_1(unsigned p_sz, value * const * p, mpbqi const & interval) { INC_DEPTH(); TRACE("rcf_TaQ", tout << "TaQ_1 [" << m_exec_depth << "]\n"; display_poly(tout, p_sz, p); tout << "\n";); scoped_polynomial_seq seq(*this); sturm_seq(p_sz, p, seq); return TaQ(seq, interval); } // --------------------------------- // // Interval refinement // // --------------------------------- void refine_rational_interval(rational_value * v, unsigned prec) { mpbqi & i = interval(v); if (!i.lower_is_open() && !i.upper_is_open()) { SASSERT(bqm().eq(i.lower(), i.upper())); return; } while (!check_precision(i, prec)) { checkpoint(); bqm().refine_lower(to_mpq(v), i.lower(), i.upper()); bqm().refine_upper(to_mpq(v), i.lower(), i.upper()); } } /** \brief Refine the interval for each coefficient of in the polynomial p. */ bool refine_coeffs_interval(unsigned n, value * const * p, unsigned prec) { for (unsigned i = 0; i < n; i++) { if (p[i] != 0 && !refine_interval(p[i], prec)) return false; } return true; } /** \brief Refine the interval for each coefficient of in the polynomial p. */ bool refine_coeffs_interval(polynomial const & p, unsigned prec) { return refine_coeffs_interval(p.size(), p.c_ptr(), prec); } /** \brief Store in r the interval p(v). */ void polynomial_interval(polynomial const & p, mpbqi const & v, mpbqi & r) { // We compute r using the Horner Sequence // ((a_n * v + a_{n-1})*v + a_{n-2})*v + a_{n-3} ... // where a_i's are the coefficients of p. unsigned sz = p.size(); if (sz == 1) { bqim().set(r, interval(p[0])); } else { SASSERT(sz > 0); SASSERT(p[sz - 1] != 0); // r <- a_n * v bqim().mul(interval(p[sz-1]), v, r); unsigned i = sz - 1; while (i > 0) { --i; if (p[i] != 0) bqim().add(r, interval(p[i]), r); if (i > 0) bqim().mul(r, v, r); } } } /** \brief Update the interval of v by using the intervals of extension and coefficients of the rational function. */ void update_rf_interval(rational_function_value * v, unsigned prec) { if (is_denominator_one(v)) { polynomial_interval(v->num(), v->ext()->interval(), v->interval()); } else { scoped_mpbqi num_i(bqim()), den_i(bqim()); polynomial_interval(v->num(), v->ext()->interval(), num_i); polynomial_interval(v->den(), v->ext()->interval(), den_i); if (!contains_zero(num_i) && !contains_zero(den_i)) { div(num_i, den_i, inc_precision(prec, 2), v->interval()); } } } void refine_transcendental_interval(rational_function_value * v, unsigned prec) { SASSERT(v->ext()->is_transcendental()); polynomial const & n = v->num(); polynomial const & d = v->den(); unsigned _prec = prec; while (true) { VERIFY(refine_coeffs_interval(n, _prec)); // must return true because a transcendental never depends on an infinitesimal VERIFY(refine_coeffs_interval(d, _prec)); // must return true because a transcendental never depends on an infinitesimal refine_transcendental_interval(to_transcendental(v->ext()), _prec); update_rf_interval(v, prec); TRACE("rcf_transcendental", tout << "after update_rf_interval: " << magnitude(v->interval()) << " "; bqim().display(tout, v->interval()); tout << std::endl;); if (check_precision(v->interval(), prec)) return; _prec++; } } bool refine_infinitesimal_interval(rational_function_value * v, unsigned prec) { SASSERT(v->ext()->is_infinitesimal()); polynomial const & numerator = v->num(); polynomial const & denominator = v->den(); unsigned num_idx = first_non_zero(numerator); unsigned den_idx = first_non_zero(denominator); if (num_idx == 0 && den_idx == 0) { unsigned _prec = prec; while (true) { refine_interval(numerator[num_idx], _prec); refine_interval(denominator[num_idx], _prec); mpbqi const & num_i = interval(numerator[num_idx]); mpbqi const & den_i = interval(denominator[num_idx]); SASSERT(!contains_zero(num_i)); SASSERT(!contains_zero(den_i)); if (is_open_interval(num_i) && is_open_interval(den_i)) { // This case is simple because adding/subtracting infinitesimal quantities, will // not change the interval. div(num_i, den_i, inc_precision(prec, 2), v->interval()); } else { // The intervals num_i and den_i may not be open. // Example: numerator[num_idx] or denominator[num_idx] are rationals // that can be precisely represented as binary rationals. scoped_mpbqi new_num_i(bqim()); scoped_mpbqi new_den_i(bqim()); mpbq tiny_value(1, _prec*2); if (numerator.size() > 1) add_infinitesimal(num_i, sign_of_first_non_zero(numerator, 1) > 0, tiny_value, new_num_i); else bqim().set(new_num_i, num_i); if (denominator.size() > 1) add_infinitesimal(den_i, sign_of_first_non_zero(denominator, 1) > 0, tiny_value, new_den_i); else bqim().set(new_den_i, den_i); div(new_num_i, new_den_i, inc_precision(prec, 2), v->interval()); } if (check_precision(v->interval(), prec)) return true; _prec++; } } else { // The following condition must hold because gcd(numerator, denominator) == 1 // If num_idx > 0 and den_idx > 0, eps^{min(num_idx, den_idx)} is a factor of gcd(numerator, denominator) SASSERT(num_idx == 0 || den_idx == 0); int s = sign(numerator[num_idx]) * sign(denominator[den_idx]); // The following must hold since numerator[num_idx] and denominator[den_idx] are not zero. SASSERT(s != 0); if (num_idx == 0) { SASSERT(den_idx > 0); // |v| is bigger than any binary rational // Interval can't be refined. There is no way to isolate an infinity with an interval with binary rational end points. return false; } else { SASSERT(num_idx > 0); SASSERT(den_idx == 0); // |v| is infinitely close to zero. if (s == 1) { // it is close from above set_lower(v->interval(), mpbq(0)); set_upper(v->interval(), mpbq(1, prec)); } else { // it is close from below set_lower(v->interval(), mpbq(-1, prec)); set_upper(v->interval(), mpbq(0)); } return true; } } } bool refine_algebraic_interval(algebraic * a, unsigned prec) { save_interval_if_too_small(a, prec); if (a->sdt() != 0) { // We don't bisect the interval, since it contains more than one root. // To bisect this kind of interval we would have to use Tarski queries. return false; } else { mpbqi & a_i = a->interval(); if (a_i.lower_is_inf() || a_i.upper_is_inf()) { // we can't bisect the infinite intervals return false; } else { mpbqi & a_i = a->interval(); SASSERT(!a_i.lower_is_inf() && !a_i.upper_is_inf()); int lower_sign = INT_MIN; while (!check_precision(a_i, prec)) { checkpoint(); SASSERT(!bqm().eq(a_i.lower(), a_i.upper())); scoped_mpbq m(bqm()); bqm().add(a_i.lower(), a_i.upper(), m); bqm().div2(m); int mid_sign = eval_sign_at(a->p().size(), a->p().c_ptr(), m); if (mid_sign == 0) { // found the actual root // set interval [m, m] set_lower(a_i, m, false); set_upper(a_i, m, false); return true; } else { SASSERT(mid_sign == 1 || mid_sign == -1); if (lower_sign == INT_MIN) { // initialize lower_sign lower_sign = eval_sign_at(a->p().size(), a->p().c_ptr(), a_i.lower()); } SASSERT(lower_sign == 1 || lower_sign == -1); if (mid_sign == lower_sign) { // improved lower bound set_lower(a_i, m); } else { // improved upper bound set_upper(a_i, m); } } } return true; } } } bool refine_algebraic_interval(rational_function_value * v, unsigned prec) { SASSERT(v->ext()->is_algebraic()); polynomial const & n = v->num(); SASSERT(is_denominator_one(v)); unsigned _prec = prec; while (true) { if (!refine_coeffs_interval(n, _prec) || !refine_algebraic_interval(to_algebraic(v->ext()), _prec)) return false; update_rf_interval(v, prec); TRACE("rcf_algebraic", tout << "after update_rf_interval: " << magnitude(v->interval()) << " "; bqim().display(tout, v->interval()); tout << std::endl;); if (check_precision(v->interval(), prec)) return true; _prec++; } } /** \brief Refine the interval of v to the desired precision (1/2^prec). Return false in case of failure. A failure can only happen if v depends on infinitesimal values. */ bool refine_interval(value * v, unsigned prec) { checkpoint(); SASSERT(!is_zero(v)); int m = magnitude(interval(v)); if (m == INT_MIN || (m < 0 && static_cast(-m) > prec)) return true; save_interval_if_too_small(v, prec); if (is_nz_rational(v)) { refine_rational_interval(to_nz_rational(v), prec); return true; } else { rational_function_value * rf = to_rational_function(v); if (rf->ext()->is_transcendental()) { refine_transcendental_interval(rf, prec); return true; } else if (rf->ext()->is_infinitesimal()) return refine_infinitesimal_interval(rf, prec); else return refine_algebraic_interval(rf, prec); } } // --------------------------------- // // Sign determination // // --------------------------------- /** \brief Return the position of the first non-zero coefficient of p. */ static unsigned first_non_zero(polynomial const & p) { unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { if (p[i] != 0) return i; } UNREACHABLE(); return UINT_MAX; } /** \brief Return the sign of the first non zero coefficient starting at position start_idx */ int sign_of_first_non_zero(polynomial const & p, unsigned start_idx) { unsigned sz = p.size(); SASSERT(start_idx < sz); for (unsigned i = start_idx; i < sz; i++) { if (p[i] != 0) return sign(p[i]); } UNREACHABLE(); return 0; } /** out <- in + infinitesimal (if plus_eps == true) out <- in - infinitesimal (if plus_eps == false) We use the following rules for performing the assignment If plus_eps == True If lower(in) == v (closed or open), then lower(out) == v and open If upper(in) == v and open, then upper(out) == v and open If upper(in) == v and closed, then upper(out) == new_v and open where new_v is v + tiny_value / 2^k, where k is the smallest natural such that sign(new_v) == sign(v) If plus_eps == False If lower(in) == v and open, then lower(out) == v and open If lower(in) == v and closed, then lower(out) == new_v and open If upper(in) == v (closed or open), then upper(out) == v and open where new_v is v - tiny_value / 2^k, where k is the smallest natural such that sign(new_v) == sign(v) */ void add_infinitesimal(mpbqi const & in, bool plus_eps, mpbq const & tiny_value, mpbqi & out) { set_interval(out, in); out.set_lower_is_open(true); out.set_upper_is_open(true); if (plus_eps) { if (!in.upper_is_open()) { scoped_mpbq tval(bqm()); tval = tiny_value; while (true) { bqm().add(in.upper(), tval, out.upper()); if (bqm().is_pos(in.upper()) == bqm().is_pos(out.upper())) return; bqm().div2(tval); checkpoint(); } } } else { if (!in.lower_is_open()) { scoped_mpbq tval(bqm()); tval = tiny_value; while (true) { bqm().sub(in.lower(), tval, out.lower()); if (bqm().is_pos(in.lower()) == bqm().is_pos(out.lower())) return; bqm().div2(tval); checkpoint(); } } } } /** \brief Determine the sign of an element of Q(trans_0, ..., trans_n) */ void determine_transcendental_sign(rational_function_value * v) { // Remark: the sign of a rational function value on an transcendental is never zero. // Reason: The transcendental can be the root of a polynomial. SASSERT(v->ext()->is_transcendental()); int m = magnitude(v->interval()); unsigned prec = 1; if (m < 0) prec = static_cast(-m) + 1; while (contains_zero(v->interval())) { refine_transcendental_interval(v, prec); prec++; } } /** \brief Determine the sign of an element of Q(trans_0, ..., trans_n, eps_0, ..., eps_m) */ void determine_infinitesimal_sign(rational_function_value * v) { // Remark: the sign of a rational function value on an infinitesimal is never zero. // Reason: An infinitesimal eps is transcendental in any field K. So, it can't be the root // of a polynomial. SASSERT(v->ext()->is_infinitesimal()); polynomial const & numerator = v->num(); polynomial const & denominator = v->den(); unsigned num_idx = first_non_zero(numerator); unsigned den_idx = first_non_zero(denominator); if (num_idx == 0 && den_idx == 0) { mpbqi const & num_i = interval(numerator[num_idx]); mpbqi const & den_i = interval(denominator[num_idx]); SASSERT(!contains_zero(num_i)); SASSERT(!contains_zero(den_i)); if (is_open_interval(num_i) && is_open_interval(den_i)) { // This case is simple because adding/subtracting infinitesimal quantities, will // not change the interval. div(num_i, den_i, m_ini_precision, v->interval()); } else { // The intervals num_i and den_i may not be open. // Example: numerator[num_idx] or denominator[num_idx] are rationals // that can be precisely represented as binary rationals. scoped_mpbqi new_num_i(bqim()); scoped_mpbqi new_den_i(bqim()); mpbq tiny_value(1, m_ini_precision); // 1/2^{m_ini_precision} if (numerator.size() > 1) add_infinitesimal(num_i, sign_of_first_non_zero(numerator, 1) > 0, tiny_value, new_num_i); else bqim().set(new_num_i, num_i); if (denominator.size() > 1) add_infinitesimal(den_i, sign_of_first_non_zero(denominator, 1) > 0, tiny_value, new_den_i); else bqim().set(new_den_i, den_i); div(new_num_i, new_den_i, m_ini_precision, v->interval()); } } else { // The following condition must hold because gcd(numerator, denominator) == 1 // If num_idx > 0 and den_idx > 0, eps^{min(num_idx, den_idx)} is a factor of gcd(numerator, denominator) SASSERT(num_idx == 0 || den_idx == 0); int s = sign(numerator[num_idx]) * sign(denominator[den_idx]); // The following must hold since numerator[num_idx] and denominator[den_idx] are not zero. SASSERT(s != 0); if (num_idx == 0) { SASSERT(den_idx > 0); // |v| is bigger than any binary rational if (s == 1) { // it is "oo" set_lower(v->interval(), m_plus_inf_approx); set_upper_inf(v->interval()); } else { // it is "-oo" set_lower_inf(v->interval()); set_upper(v->interval(), m_minus_inf_approx); } } else { SASSERT(num_idx > 0); SASSERT(den_idx == 0); // |v| is infinitely close to zero. if (s == 1) { // it is close from above set_lower(v->interval(), mpbq(0)); set_upper(v->interval(), mpbq(1, m_ini_precision)); } else { // it is close from below set_lower(v->interval(), mpbq(-1, m_ini_precision)); set_upper(v->interval(), mpbq(0)); } } } SASSERT(!contains_zero(v->interval())); } /** \brief Return true if x and q depend on infinitesimal values. That is, q(x) does not depend on infinitesimal values. */ bool depends_on_infinitesimals(polynomial const & q, algebraic * x) { return x->depends_on_infinitesimals() || depends_on_infinitesimals(q.size(), q.c_ptr()); } /** \brief This method is invoked when we know that q(x) is not zero and q(x) does not depend on infinitesimal values. The procedure will keep refining the intervals associated with x and coefficients of q until the interval of q(x) is of the form (l > 0, u) OR (l, u < 0) */ void refine_until_sign_determined(polynomial const & q, algebraic * x, mpbqi & r) { SASSERT(!depends_on_infinitesimals(q, x)); // If x and q do not depend on infinitesimals, must make sure that r satisfies our invariant // for intervals of polynomial values that do not depend on infinitesimals. // that is, // Given r == (l, u), l != 0 and u != 0 int m = magnitude(r); unsigned prec; if (m >= 0) prec = m_ini_precision; else prec = -m; while (true) { checkpoint(); VERIFY(refine_coeffs_interval(q, prec)); // can't fail because q does not depend on infinitesimals VERIFY(refine_algebraic_interval(x, prec)); // can't fail because x does not depend on infinitesimals // Update r polynomial_interval(q, x->interval(), r); // Since q and r do not depend on infinitesimals -oo and +oo will never be end-points. SASSERT(!r.lower_is_inf()); SASSERT(!r.upper_is_inf()); if (!contains_zero(r) && !bqm().is_zero(r.lower()) && !bqm().is_zero(r.upper())) return; // the interval r satisfies our requirements. prec++; } } /** \brief If q(x) != 0, return true and store in r an interval that contains the value q(x), but does not contain 0. If q(x) == 0, return false */ bool expensive_algebraic_poly_interval(polynomial const & q, algebraic * x, mpbqi & r) { polynomial_interval(q, x->interval(), r); if (!contains_zero(r)) { if (!depends_on_infinitesimals(q, x) && (bqm().is_zero(r.lower()) || bqm().is_zero(r.upper()))) { // we don't want intervals of the form (l, 0) and (0, u) when // q(x) does not depend on infinitesimals. refine_until_sign_determined(q, x, r); } return true; } int num_roots = x->num_roots_inside_interval(); SASSERT(x->sdt() != 0 || num_roots == 1); polynomial const & p = x->p(); int taq_p_q = TaQ(p.size(), p.c_ptr(), q.size(), q.c_ptr(), x->iso_interval()); if (num_roots == 1 && taq_p_q == 0) return false; // q(x) is zero if (taq_p_q == num_roots) { // q(x) is positive if (!depends_on_infinitesimals(q, x)) refine_until_sign_determined(q, x, r); else set_lower_zero(r); SASSERT(!contains_zero(r)); return true; } else if (taq_p_q == -num_roots) { // q(x) is negative if (!depends_on_infinitesimals(q, x)) refine_until_sign_determined(q, x, r); else set_upper_zero(r); SASSERT(!contains_zero(r)); return true; } else { SASSERT(num_roots > 1); SASSERT(x->sdt() != 0); int q_eq_0, q_gt_0, q_lt_0; value_ref_buffer q2(*this); count_signs_at_zeros_core(taq_p_q, p.size(), p.c_ptr(), q.size(), q.c_ptr(), x->iso_interval(), num_roots, q_eq_0, q_gt_0, q_lt_0, q2); if (q_eq_0 > 0 && q_gt_0 == 0 && q_lt_0 == 0) { // q(x) is zero return false; } else if (q_eq_0 == 0 && q_gt_0 > 0 && q_lt_0 == 0) { // q(x) is positive set_lower_zero(r); return true; } else if (q_eq_0 == 0 && q_gt_0 == 0 && q_lt_0 > 0) { // q(x) is negative set_upper_zero(r); return true; } else { sign_det & sdt = *(x->sdt()); // Remark: // By definition of algebraic and sign_det, we know that // sdt.M_s * [1, ..., 1]^t = sdt.taqrs()^t // That is, // [1, ..., 1]^t = sdt.M_s^-1 * sdt.taqrs()^t // Moreover the number of roots in x->iso_interval() is equal to the number of rows and columns in sdt.M_s. // The column j of std.M_s is associated with the sign condition sdt.m_scs[j]. // The row i of sdt.M_s is associated with the polynomial sdt.prs()[i]. // // The extension x is encoded using the sign condition x->sc_idx() of std.m_scs // scoped_mpz_matrix M(mm()); VERIFY(mk_sign_det_matrix(q_eq_0, q_gt_0, q_lt_0, M)); bool use_q2 = M.n() == 3; scoped_mpz_matrix new_M_s(mm()); mm().tensor_product(sdt.M_s, M, new_M_s); array const & prs = sdt.prs(); // polynomials associated with the rows of M_s array const & taqrs = sdt.taqrs(); // For each i in [0, taqrs.size()) TaQ(p, prs[i]; x->iso_interval()) == taqrs[i] SASSERT(prs.size() == taqrs.size()); int_buffer new_taqrs; value_ref_buffer prq(*this); // fill new_taqrs using taqrs and the new tarski queries containing q (and q^2 when use_q2 == true). for (unsigned i = 0; i < taqrs.size(); i++) { // Add TaQ(p, prs[i] * 1; x->iso_interval()) new_taqrs.push_back(taqrs[i]); // Add TaQ(p, prs[i] * q; x->iso_interval()) mul(prs[i].size(), prs[i].c_ptr(), q.size(), q.c_ptr(), prq); new_taqrs.push_back(TaQ(p.size(), p.c_ptr(), prq.size(), prq.c_ptr(), x->iso_interval())); if (use_q2) { // Add TaQ(p, prs[i] * q^2; x->iso_interval()) mul(prs[i].size(), prs[i].c_ptr(), q2.size(), q2.c_ptr(), prq); new_taqrs.push_back(TaQ(p.size(), p.c_ptr(), prq.size(), prq.c_ptr(), x->iso_interval())); } } int_buffer sc_cardinalities; sc_cardinalities.resize(new_taqrs.size(), 0); // Solve // new_M_s * sc_cardinalities = new_taqrs VERIFY(mm().solve(new_M_s, sc_cardinalities.c_ptr(), new_taqrs.c_ptr())); DEBUG_CODE({ // check if sc_cardinalities has the expected structure // - contains only 0 or 1 // - !use_q2 IMPLIES for all i in [0, taqrs.size()) (sc_cardinalities[2*i] == 1) + (sc_cardinalities[2*i + 1] == 1) == 1 // - use_q2 IMPLIES for all i in [0, taqrs.size()) (sc_cardinalities[3*i] == 1) + (sc_cardinalities[3*i + 1] == 1) + (sc_cardinalities[3*i + 2] == 1) == 1 for (unsigned i = 0; i < sc_cardinalities.size(); i++) { SASSERT(sc_cardinalities[i] == 0 || sc_cardinalities[i] == 1); } if (!use_q2) { for (unsigned i = 0; i < taqrs.size(); i++) { SASSERT((sc_cardinalities[2*i] == 1) + (sc_cardinalities[2*i + 1] == 1) == 1); } } else { for (unsigned i = 0; i < taqrs.size(); i++) { SASSERT((sc_cardinalities[3*i] == 1) + (sc_cardinalities[3*i + 1] == 1) + (sc_cardinalities[3*i + 2] == 1) == 1); } } }); // Remark: // Note that we found the sign of q for every root of p in the interval x->iso_interval() :) unsigned sc_idx = x->sc_idx(); if (use_q2) { if (sc_cardinalities[3*sc_idx] == 1) { // q(x) is zero return false; } else if (sc_cardinalities[3*sc_idx + 1] == 1) { // q(x) is positive set_lower_zero(r); return true; } else { SASSERT(sc_cardinalities[3*sc_idx + 2] == 1); // q(x) is negative set_upper_zero(r); return true; } } else { if (q_eq_0 == 0) { if (sc_cardinalities[2*sc_idx] == 1) { // q(x) is positive set_lower_zero(r); return true; } else { SASSERT(sc_cardinalities[2*sc_idx + 1] == 1); // q(x) is negative set_upper_zero(r); return true; } } else if (q_gt_0 == 0) { if (sc_cardinalities[2*sc_idx] == 1) { // q(x) is zero return false; } else { SASSERT(sc_cardinalities[2*sc_idx + 1] == 1); // q(x) is negative set_upper_zero(r); return true; } } else { SASSERT(q_lt_0 == 0); if (sc_cardinalities[2*sc_idx] == 1) { // q(x) is zero return false; } else { SASSERT(sc_cardinalities[2*sc_idx + 1] == 1); // q(x) is positive set_lower_zero(r); return true; } } } } } } bool expensive_determine_algebraic_sign(rational_function_value * v) { SASSERT(contains_zero(v->interval())); SASSERT(v->ext()->is_algebraic()); TRACE("rcf_algebraic_sign", tout << "expensive_determine_algebraic_sign\n"; display(tout, v, false); tout << "\ninterval: "; bqim().display(tout, v->interval()); tout << "\n";); algebraic * x = to_algebraic(v->ext()); scoped_mpbqi num_interval(bqim()); SASSERT(is_denominator_one(v)); if (!expensive_algebraic_poly_interval(v->num(), x, num_interval)) return false; // it is zero SASSERT(!contains_zero(num_interval)); set_interval(v->interval(), num_interval); SASSERT(!contains_zero(v->interval())); return true; // it is not zero } /** \brief Determine the sign of an rational function value p(x)/q(x) when x is an algebraic extension. */ bool determine_algebraic_sign(rational_function_value * v) { SASSERT(v->ext()->is_algebraic()); mpbqi & interval = v->interval(); if (interval.lower_is_inf() || interval.upper_is_inf()) { return expensive_determine_algebraic_sign(v); } else { int m = magnitude(v->interval()); unsigned prec = 1; if (m < 0) prec = static_cast(-m) + 1; while (contains_zero(v->interval())) { if (!refine_algebraic_interval(v, prec)) return expensive_determine_algebraic_sign(v); prec++; if (prec > m_max_precision) return expensive_determine_algebraic_sign(v); } SASSERT(!contains_zero(v->interval())); return true; } } /** \brief Determine the sign of the new rational function value. The idea is to keep refinining the interval until interval of v does not contain 0. After a couple of steps we switch to expensive sign determination procedure. Return false if v is actually zero. */ bool determine_sign(rational_function_value * v) { if (!contains_zero(v->interval())) return true; bool r; switch (v->ext()->knd()) { case extension::TRANSCENDENTAL: determine_transcendental_sign(v); r = true; break; // it is never zero case extension::INFINITESIMAL: determine_infinitesimal_sign(v); r = true; break; // it is never zero case extension::ALGEBRAIC: r = determine_algebraic_sign(v); break; default: UNREACHABLE(); r = false; } TRACE("rcf_determine_sign_bug", tout << "result: " << r << "\n"; display_compact(tout, v); tout << "\n"; tout << "sign: " << sign(v) << "\n";); return r; } bool determine_sign(value_ref & r) { SASSERT(is_rational_function(r.get())); return determine_sign(to_rational_function(r.get())); } // --------------------------------- // // Arithmetic operations // // --------------------------------- /** \brief Compute polynomials new_p1 and new_p2 s.t. - p1/p2 == new_p1/new_p2, AND - new_p2 is a Monic polynomial, AND - gcd(new_p1, new_p2) == 1 */ void normalize_fraction(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & new_p1, value_ref_buffer & new_p2) { INC_DEPTH(); TRACE("rcf_arith", tout << "normalize [" << m_exec_depth << "]\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); SASSERT(sz1 > 0 && sz2 > 0); if (sz2 == 1) { // - new_p1 <- p1/p2[0]; new_p2 <- one IF sz2 == 1 div(sz1, p1, p2[0], new_p1); new_p2.reset(); new_p2.push_back(one()); } else { value * lc = p2[sz2 - 1]; if (is_rational_one(lc)) { // p2 is monic normalize_num_monic_den(sz1, p1, sz2, p2, new_p1, new_p2); } else { // p2 is not monic value_ref_buffer tmp1(*this); value_ref_buffer tmp2(*this); div(sz1, p1, lc, tmp1); div(sz2, p2, lc, tmp2); normalize_num_monic_den(tmp1.size(), tmp1.c_ptr(), tmp2.size(), tmp2.c_ptr(), new_p1, new_p2); } } TRACE("normalize_fraction_bug", display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n"; tout << "====>\n"; display_poly(tout, new_p1.size(), new_p1.c_ptr()); tout << "\n"; display_poly(tout, new_p2.size(), new_p2.c_ptr()); tout << "\n";); } /** \brief Auxiliary function for normalize_fraction. It produces new_p1 and new_p2 s.t. new_p1/new_p2 == p1/p2 gcd(new_p1, new_p2) == 1 Assumptions: \pre p2 is monic \pre sz2 > 1 */ void normalize_num_monic_den(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & new_p1, value_ref_buffer & new_p2) { SASSERT(sz2 > 1); SASSERT(is_rational_one(p2[sz2-1])); value_ref_buffer g(*this); gcd(sz1, p1, sz2, p2, g); SASSERT(is_monic(g)); if (is_rational_one(g)) { new_p1.append(sz1, p1); new_p2.append(sz2, p2); } else { div(sz1, p1, g.size(), g.c_ptr(), new_p1); div(sz2, p2, g.size(), g.c_ptr(), new_p2); SASSERT(is_monic(new_p2)); } } /** \brief Simplify p1(x) using x's defining polynomial. By definition of polynomial division, we have: new_p1(x) == quotient(p1,p)(x) * p(x) + rem(p1,p)(x) Since p(x) == 0, we have that new_p1(x) = rem(p1,p)(x) */ void normalize_algebraic(algebraic * x, unsigned sz1, value * const * p1, value_ref_buffer & new_p1) { polynomial const & p = x->p(); if (!m_lazy_algebraic_normalization || !m_in_aux_values || is_monic(p)) { rem(sz1, p1, p.size(), p.c_ptr(), new_p1); } else { new_p1.reset(); new_p1.append(sz1, p1); } } /** \brief Create a new value using the a->ext(), and the given numerator and denominator. Use interval(a) + interval(b) as an initial approximation for the interval of the result, and invoke determine_sign() */ void mk_add_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den, value_ref & r) { SASSERT(num_sz > 0); // den_sz may be zero for algebraic extensions. // We do not use denominators for algebraic extensions. if (num_sz == 1 && den_sz <= 1) { // In this case, the normalization rules guarantee that den is one. SASSERT(den_sz == 0 || is_rational_one(den[0])); r = num[0]; } else { scoped_mpbqi ri(bqim()); bqim().add(interval(a), interval(b), ri); r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); swap(r->interval(), ri); if (determine_sign(r)) { SASSERT(!contains_zero(r->interval())); } else { // The new value is 0 r = 0; } } } /** \brief Add a value of 'a' the form n/1 with b where rank(a) > rank(b) */ void add_p_v(rational_function_value * a, value * b, value_ref & r) { SASSERT(is_denominator_one(a)); SASSERT(compare_rank(a, b) > 0); polynomial const & an = a->num(); polynomial const & one = a->den(); SASSERT(an.size() > 1); value_ref_buffer new_num(*this); add(an.size(), an.c_ptr(), b, new_num); SASSERT(new_num.size() == an.size()); mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } /** \brief Add a value 'a' of the form n/d with b where rank(a) > rank(b) */ void add_rf_v(rational_function_value * a, value * b, value_ref & r) { value_ref_buffer b_ad(*this); value_ref_buffer num(*this); polynomial const & an = a->num(); if (is_denominator_one(a)) { add_p_v(a, b, r); } else { SASSERT(!a->ext()->is_algebraic()); polynomial const & ad = a->den(); // b_ad <- b * ad mul(b, ad.size(), ad.c_ptr(), b_ad); // num <- a + b * ad add(an.size(), an.c_ptr(), b_ad.size(), b_ad.c_ptr(), num); if (num.empty()) r = 0; else { value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_fraction(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); SASSERT(!new_num.empty()); mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } } /** \brief Add values 'a' and 'b' of the form n/1 and rank(a) == rank(b) */ void add_p_p(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(is_denominator_one(a)); SASSERT(is_denominator_one(b)); SASSERT(compare_rank(a, b) == 0); polynomial const & an = a->num(); polynomial const & one = a->den(); polynomial const & bn = b->num(); value_ref_buffer new_num(*this); add(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); if (new_num.empty()) r = 0; else { // We don't need to invoke normalize_algebraic even if x (== a->ext()) is algebraic. // Reason: by construction the polynomials a->num() and b->num() are "normalized". // That is, their degrees are < degree of the polynomial defining x. // Moreover, when we add polynomials, the degree can only decrease. // So, degree of new_num must be < degree of x's defining polynomial. mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } } /** \brief Add values 'a' and 'b' of the form n/d and rank(a) == rank(b) */ void add_rf_rf(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(compare_rank(a, b) == 0); polynomial const & an = a->num(); polynomial const & bn = b->num(); if (is_denominator_one(a) && is_denominator_one(b)) { add_p_p(a, b, r); } else { SASSERT(!a->ext()->is_algebraic()); polynomial const & ad = a->den(); polynomial const & bd = b->den(); value_ref_buffer an_bd(*this); value_ref_buffer bn_ad(*this); mul(an.size(), an.c_ptr(), bd.size(), bd.c_ptr(), an_bd); mul(bn.size(), bn.c_ptr(), ad.size(), ad.c_ptr(), bn_ad); value_ref_buffer num(*this); add(an_bd.size(), an_bd.c_ptr(), bn_ad.size(), bn_ad.c_ptr(), num); if (num.empty()) { r = 0; } else { value_ref_buffer den(*this); mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_fraction(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); SASSERT(!new_num.empty()); mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } } void add(value * a, value * b, value_ref & r) { if (a == 0) { r = b; } else if (b == 0) { r = a; } else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().add(to_mpq(a), to_mpq(b), v); if (qm().is_zero(v)) r = 0; else r = mk_rational_and_swap(v); } else { INC_DEPTH(); TRACE("rcf_arith", tout << "add [" << m_exec_depth << "]\n"; display(tout, a, false); tout << "\n"; display(tout, b, false); tout << "\n";); switch (compare_rank(a, b)) { case -1: add_rf_v(to_rational_function(b), a, r); break; case 0: add_rf_rf(to_rational_function(a), to_rational_function(b), r); break; case 1: add_rf_v(to_rational_function(a), b, r); break; default: UNREACHABLE(); } } } void sub(value * a, value * b, value_ref & r) { if (a == 0) { neg(b, r); } else if (b == 0) { r = a; } else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().sub(to_mpq(a), to_mpq(b), v); if (qm().is_zero(v)) r = 0; else r = mk_rational_and_swap(v); } else { value_ref neg_b(*this); neg(b, neg_b); switch (compare_rank(a, neg_b)) { case -1: add_rf_v(to_rational_function(neg_b), a, r); break; case 0: add_rf_rf(to_rational_function(a), to_rational_function(neg_b), r); break; case 1: add_rf_v(to_rational_function(a), neg_b, r); break; default: UNREACHABLE(); } } } void neg_rf(rational_function_value * a, value_ref & r) { polynomial const & an = a->num(); polynomial const & ad = a->den(); value_ref_buffer new_num(*this); neg(an.size(), an.c_ptr(), new_num); scoped_mpbqi ri(bqim()); bqim().neg(interval(a), ri); r = mk_rational_function_value_core(a->ext(), new_num.size(), new_num.c_ptr(), ad.size(), ad.c_ptr()); swap(r->interval(), ri); SASSERT(!contains_zero(r->interval())); } void neg(value * a, value_ref & r) { if (a == 0) { r = 0; } else if (is_nz_rational(a)) { scoped_mpq v(qm()); qm().set(v, to_mpq(a)); qm().neg(v); r = mk_rational_and_swap(v); } else { neg_rf(to_rational_function(a), r); } } /** \brief Create a new value using the a->ext(), and the given numerator and denominator. Use interval(a) * interval(b) as an initial approximation for the interval of the result, and invoke determine_sign() */ void mk_mul_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den, value_ref & r) { SASSERT(num_sz > 0); if (num_sz == 1 && den_sz <= 1) { // den_sz may be zero for algebraic extensions. // We do not use denominators for algebraic extensions. // In this case, the normalization rules guarantee that den is one. SASSERT(den_sz == 0 || is_rational_one(den[0])); r = num[0]; } else { scoped_mpbqi ri(bqim()); bqim().mul(interval(a), interval(b), ri); r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); swap(ri, r->interval()); if (determine_sign(r)) { SASSERT(!contains_zero(r->interval())); } else { // The new value is 0 r = 0; } } } /** \brief Multiply a value of 'a' the form n/1 with b where rank(a) > rank(b) */ void mul_p_v(rational_function_value * a, value * b, value_ref & r) { SASSERT(is_denominator_one(a)); SASSERT(b != 0); SASSERT(compare_rank(a, b) > 0); polynomial const & an = a->num(); polynomial const & one = a->den(); SASSERT(an.size() > 1); value_ref_buffer new_num(*this); mul(b, an.size(), an.c_ptr(), new_num); SASSERT(new_num.size() == an.size()); mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } /** \brief Multiply a value 'a' of the form n/d with b where rank(a) > rank(b) */ void mul_rf_v(rational_function_value * a, value * b, value_ref & r) { polynomial const & an = a->num(); if (is_denominator_one(a)) { mul_p_v(a, b, r); } else { SASSERT(!a->ext()->is_algebraic()); polynomial const & ad = a->den(); value_ref_buffer num(*this); // num <- b * an mul(b, an.size(), an.c_ptr(), num); SASSERT(num.size() == an.size()); value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_fraction(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); SASSERT(!new_num.empty()); mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } /** \brief Multiply values 'a' and 'b' of the form n/1 and rank(a) == rank(b) */ void mul_p_p(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(is_denominator_one(a)); SASSERT(is_denominator_one(b)); SASSERT(compare_rank(a, b) == 0); polynomial const & an = a->num(); polynomial const & one = a->den(); polynomial const & bn = b->num(); value_ref_buffer new_num(*this); mul(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); SASSERT(!new_num.empty()); extension * x = a->ext(); if (x->is_algebraic()) { value_ref_buffer new_num2(*this); normalize_algebraic(to_algebraic(x), new_num.size(), new_num.c_ptr(), new_num2); SASSERT(!new_num.empty()); mk_mul_value(a, b, new_num2.size(), new_num2.c_ptr(), one.size(), one.c_ptr(), r); } else { mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } } /** \brief Multiply values 'a' and 'b' of the form n/d and rank(a) == rank(b) */ void mul_rf_rf(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(compare_rank(a, b) == 0); polynomial const & an = a->num(); polynomial const & bn = b->num(); if (is_denominator_one(a) && is_denominator_one(b)) { mul_p_p(a, b, r); } else { SASSERT(!a->ext()->is_algebraic()); polynomial const & ad = a->den(); polynomial const & bd = b->den(); value_ref_buffer num(*this); value_ref_buffer den(*this); mul(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), num); mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); SASSERT(!num.empty()); SASSERT(!den.empty()); value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_fraction(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); SASSERT(!new_num.empty()); mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } void mul(value * a, value * b, value_ref & r) { if (a == 0 || b == 0) { r = 0; } else if (is_rational_one(a)) { r = b; } else if (is_rational_one(b)) { r = a; } else if (is_rational_minus_one(a)) { neg(b, r); } else if (is_rational_minus_one(b)) { neg(a, r); } else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().mul(to_mpq(a), to_mpq(b), v); r = mk_rational_and_swap(v); } else { INC_DEPTH(); TRACE("rcf_arith", tout << "mul [" << m_exec_depth << "]\n"; display(tout, a, false); tout << "\n"; display(tout, b, false); tout << "\n";); switch (compare_rank(a, b)) { case -1: mul_rf_v(to_rational_function(b), a, r); break; case 0: mul_rf_rf(to_rational_function(a), to_rational_function(b), r); break; case 1: mul_rf_v(to_rational_function(a), b, r); break; default: UNREACHABLE(); } } } void div(value * a, value * b, value_ref & r) { if (a == 0) { r = 0; } else if (b == 0) { throw exception("division by zero"); } else if (is_rational_one(b)) { r = a; } else if (is_rational_one(a)) { inv(b, r); } else if (is_rational_minus_one(b)) { neg(a, r); } else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().div(to_mpq(a), to_mpq(b), v); r = mk_rational_and_swap(v); } else { value_ref inv_b(*this); inv(b, inv_b); switch (compare_rank(a, inv_b)) { case -1: mul_rf_v(to_rational_function(inv_b), a, r); break; case 0: mul_rf_rf(to_rational_function(a), to_rational_function(inv_b), r); break; case 1: mul_rf_v(to_rational_function(a), inv_b, r); break; default: UNREACHABLE(); } } } /** \brief Invert 1/q(alpha) given that p(alpha) = 0. That is, we find h s.t. q(alpha) * h(alpha) = 1 The procedure succeeds (and returns true) if the GCD(q, p) = 1. If the GCD(q, p) != 1, then it returns false, and store the GCD in g. The following procedure is essentially a special case of the extended polynomial GCD algorithm. */ bool inv_algebraic(unsigned q_sz, value * const * q, unsigned p_sz, value * const * p, value_ref_buffer & g, value_ref_buffer & h) { TRACE("inv_algebraic", tout << "q: "; display_poly(tout, q_sz, q); tout << "\n"; tout << "p: "; display_poly(tout, p_sz, p); tout << "\n";); SASSERT(q_sz > 0); SASSERT(q_sz < p_sz); // Q <- q value_ref_buffer Q(*this); Q.append(q_sz, q); // R <- 1 value_ref_buffer R(*this); R.push_back(one()); value_ref_buffer Quo(*this), Rem(*this), aux(*this); // We find h(alpha), by rewriting the equation // q(alpha) * h(alpha) = 1 // until we have // 1 * h(alpha) = R(alpha) while (true) { // In every iteration of the loop we have // Q(alpha) * h(alpha) = R(alpha) TRACE("inv_algebraic", tout << "Q: "; display_poly(tout, Q.size(), Q.c_ptr()); tout << "\n"; tout << "R: "; display_poly(tout, R.size(), R.c_ptr()); tout << "\n";); if (Q.size() == 1) { // If the new Q is the constant polynomial, they we are done. // We just divide R by Q[0]. // h(alpha) = R(alpha) / Q[0] div(R.size(), R.c_ptr(), Q[0], h); TRACE("inv_algebraic", tout << "h: "; display_poly(tout, h.size(), h.c_ptr()); tout << "\n";); // g <- 1 g.reset(); g.push_back(one()); return true; } else { div_rem(p_sz, p, Q.size(), Q.c_ptr(), Quo, Rem); if (Rem.empty()) { // failed // GCD(q, p) != 1 g = Q; mk_monic(g); return false; } else { // By the definition of polynomial division, we have // p == Quo * Q + Rem // Since, we have p(alpha) = 0 // Quo(alpha) * Q(alpha) = -Rem(alpha) (*) // Now, if we multiply the equation // Q(alpha) * h(alpha) = R(alpha) // by Quo(alpha) and apply (*), we get // -Rem(alpha) * h(alpha) = R(alpha) * Quo(alpha) // Thus, we update Q, and R for the next iteration, as // Q <- -REM // R <- R * Quo // Q <- -Rem neg(Rem.size(), Rem.c_ptr(), Q); mul(R.size(), R.c_ptr(), Quo.size(), Quo.c_ptr(), aux); // Moreover since p(alpha) = 0, we can simplify Q, by using // Q(alpha) = REM(Q, p)(alpha) rem(aux.size(), aux.c_ptr(), p_sz, p, R); SASSERT(R.size() < p_sz); // } } } } /** \brief r <- 1/a specialized version when a->ext() is algebraic. It avoids the use of rational functions. */ void inv_algebraic(rational_function_value * a, value_ref & r) { SASSERT(a->ext()->is_algebraic()); SASSERT(is_denominator_one(a)); scoped_mpbqi ri(bqim()); bqim().inv(interval(a), ri); algebraic * alpha = to_algebraic(a->ext()); polynomial const & q = a->num(); polynomial const & p = alpha->p(); value_ref_buffer norm_q(*this); // since p(alpha) = 0, we have that q(alpha) = rem(q, p)(alpha) rem(q.size(), q.c_ptr(), p.size(), p.c_ptr(), norm_q); SASSERT(norm_q.size() < p.size()); value_ref_buffer new_num(*this), g(*this); if (inv_algebraic(norm_q.size(), norm_q.c_ptr(), p.size(), p.c_ptr(), g, new_num)) { if (new_num.size() == 1) { r = new_num[0]; } else { r = mk_rational_function_value_core(alpha, new_num.size(), new_num.c_ptr()); swap(r->interval(), ri); SASSERT(!contains_zero(r->interval())); } } else { // We failed to compute 1/a // because q and p are not co-prime // This can happen because we don't use minimal // polynomials to represent algebraic extensions such // as alpha. // We recover from the failure by refining the defining polynomial of alpha // with p/gcd(p, q) // Remark: g contains the gcd of p, q // And try again :) value_ref_buffer new_p(*this); div(p.size(), p.c_ptr(), g.size(), g.c_ptr(), new_p); if (m_clean_denominators) { value_ref_buffer tmp(*this); value_ref d(*this); clean_denominators(new_p.size(), new_p.c_ptr(), tmp, d); new_p = tmp; } SASSERT(new_p.size() >= 2); if (new_p.size() == 2) { // Easy case: alpha is actually equal to // -new_p[0]/new_p[1] value_ref alpha_val(*this); alpha_val = new_p[0]; neg(alpha_val, alpha_val); div(alpha_val, new_p[1], alpha_val); // Thus, a is equal to q(alpha_val) value_ref new_a(*this); mk_polynomial_value(q.size(), q.c_ptr(), alpha_val, new_a); // Remark new_a does not depend on alpha anymore // r == 1/inv(new_a) inv(new_a, r); } else if (alpha->sdt() == 0) { // Another easy case: we just have to replace // alpha->p() with new_p. // The m_iso_interval for p() is also an isolating interval for new_p, // since the roots of new_p() are a subset of the roots of p reset_p(alpha->m_p); set_p(alpha->m_p, new_p.size(), new_p.c_ptr()); // The new call will succeed because q and new_p are co-prime inv_algebraic(a, r); } else { // Let sdt be alpha->sdt(); // In pricipal, the signs of the polynomials sdt->qs can be used // to discriminate the roots of new_p. The signs of this polynomials // depend only on alpha, and not on the polynomial used to define alpha // So, in principle, we can reuse m_qs and m_sign_conditions. // However, we have to recompute the tarski queries with respect to new_p. // This values will be different, since new_p has less roots than p. // // Instead of trying to reuse the information in sdt, we simply // isolate the roots of new_p, and check the one that is equal to alpha. // and copy all the information from them. SASSERT(new_p.size() > 2); // we can invoke nl_nz_sqf_isolate_roots, because we know // - new_p is not linear // - new_p is square free (it is a factor of the square free polynomial p) // - 0 is not a root of new_p (it is a factor of p, and 0 is not a root of p) numeral_vector roots; nl_nz_sqf_isolate_roots(new_p.size(), new_p.c_ptr(), roots); SASSERT(roots.size() > 0); algebraic * new_alpha; if (roots.size() == 1) { new_alpha = to_algebraic(to_rational_function(roots[0].m_value)->ext()); } else { value_ref alpha_val(*this); alpha_val = mk_rational_function_value(alpha); // search for the root that is equal to alpha unsigned i = 0; for (i = 0; i < roots.size(); i++) { if (compare(alpha_val, roots[i].m_value) == 0) { // found it; break; } } new_alpha = to_algebraic(to_rational_function(roots[i].m_value)->ext()); } SASSERT(new_alpha->p().size() == new_p.size()); // We now that alpha and new_alpha represent the same value. // Thus, we update alpha fields with the fields from new_alpha. // copy new_alpha->m_p reset_p(alpha->m_p); set_p(alpha->m_p, new_alpha->m_p.size(), new_alpha->m_p.c_ptr()); // copy new_alpha->m_sign_det inc_ref_sign_det(new_alpha->m_sign_det); dec_ref_sign_det(alpha->m_sign_det); alpha->m_sign_det = new_alpha->m_sign_det; // copy remaining fields set_interval(alpha->m_iso_interval, new_alpha->m_iso_interval); alpha->m_sc_idx = new_alpha->m_sc_idx; alpha->m_depends_on_infinitesimals = new_alpha->m_depends_on_infinitesimals; // The new call will succeed because q and new_p are co-prime inv_algebraic(a, r); } } } void inv_rf(rational_function_value * a, value_ref & r) { if (a->ext()->is_algebraic()) { inv_algebraic(a, r); } else { SASSERT(!a->ext()->is_algebraic()); polynomial const & an = a->num(); polynomial const & ad = a->den(); scoped_mpbqi ri(bqim()); bqim().inv(interval(a), ri); // The GCD of an and ad is one, we may use a simpler version of normalize value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_fraction(ad.size(), ad.c_ptr(), an.size(), an.c_ptr(), new_num, new_den); r = mk_rational_function_value_core(a->ext(), new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr()); swap(r->interval(), ri); SASSERT(!contains_zero(r->interval())); } } void inv(value * a, value_ref & r) { if (a == 0) { throw exception("division by zero"); } if (is_nz_rational(a)) { scoped_mpq v(qm()); qm().inv(to_mpq(a), v); r = mk_rational_and_swap(v); } else { inv_rf(to_rational_function(a), r); } } void set(numeral & n, value * v) { inc_ref(v); dec_ref(n.m_value); n.m_value = v; } void set(numeral & n, value_ref const & v) { set(n, v.get()); } void neg(numeral & a) { value_ref r(*this); neg(a.m_value, r); set(a, r); } void neg(numeral const & a, numeral & b) { value_ref r(*this); neg(a.m_value, r); set(b, r); } void inv(numeral & a) { value_ref r(*this); inv(a.m_value, r); set(a, r); } void inv(numeral const & a, numeral & b) { value_ref r(*this); inv(a.m_value, r); set(b, r); } void add(numeral const & a, numeral const & b, numeral & c) { value_ref r(*this); add(a.m_value, b.m_value, r); set(c, r); } void sub(numeral const & a, numeral const & b, numeral & c) { value_ref r(*this); sub(a.m_value, b.m_value, r); set(c, r); } void mul(numeral const & a, numeral const & b, numeral & c) { value_ref r(*this); mul(a.m_value, b.m_value, r); set(c, r); } void div(numeral const & a, numeral const & b, numeral & c) { value_ref r(*this); div(a.m_value, b.m_value, r); set(c, r); } /** \brief a <- b^{1/k} */ void root(numeral const & a, unsigned k, numeral & b) { if (k == 0) throw exception("0-th root is indeterminate"); if (k == 1 || is_zero(a)) { set(b, a); return; } if (sign(a) < 0 && k % 2 == 0) throw exception("even root of negative number"); // create the polynomial p of the form x^k - a value_ref_buffer p(*this); value_ref neg_a(*this); neg(a.m_value, neg_a); p.push_back(neg_a); for (unsigned i = 0; i < k - 1; i++) p.push_back(0); p.push_back(one()); numeral_vector roots; nz_isolate_roots(p.size(), p.c_ptr(), roots); SASSERT(roots.size() == 1 || roots.size() == 2); if (roots.size() == 1 || sign(roots[0].m_value) > 0) { set(b, roots[0]); } else { SASSERT(roots.size() == 2); SASSERT(sign(roots[1].m_value) > 0); set(b, roots[1]); } del(roots); } /** \brief a <- b^k */ void power(numeral const & a, unsigned k, numeral & b) { unsigned mask = 1; value_ref power(*this); value_ref _b(*this); power = a.m_value; _b = one(); while (mask <= k) { checkpoint(); if (mask & k) mul(_b, power, _b); mul(power, power, power); mask = mask << 1; } set(b, _b); } // --------------------------------- // // Comparison // // --------------------------------- int compare(value * a, value * b) { if (a == 0) return -sign(b); else if (b == 0) return sign(a); else if (is_nz_rational(a) && is_nz_rational(b)) { if (qm().eq(to_mpq(a), to_mpq(b))) return 0; else return qm().lt(to_mpq(a), to_mpq(b)) ? -1 : 1; } else { // FUTURE: try to refine interval before switching to sub+sign approach if (bqim().before(interval(a), interval(b))) return -1; else if (bqim().before(interval(b), interval(a))) return 1; else { value_ref diff(*this); sub(a, b, diff); return sign(diff); } } } int compare(numeral const & a, numeral const & b) { return compare(a.m_value, b.m_value); } // --------------------------------- // // "Pretty printing" // // --------------------------------- struct collect_algebraic_refs { char_vector m_visited; // Set of visited algebraic extensions. ptr_vector m_found; // vector/list of visited algebraic extensions. void mark(extension * ext) { if (ext->is_algebraic()) { m_visited.reserve(ext->idx() + 1, false); if (!m_visited[ext->idx()]) { m_visited[ext->idx()] = true; algebraic * a = to_algebraic(ext); m_found.push_back(a); mark(a->p()); } } } void mark(polynomial const & p) { for (unsigned i = 0; i < p.size(); i++) { mark(p[i]); } } void mark(value * v) { if (v == 0 || is_nz_rational(v)) return; rational_function_value * rf = to_rational_function(v); mark(rf->ext()); mark(rf->num()); mark(rf->den()); } }; static unsigned num_nz_coeffs(polynomial const & p) { unsigned r = 0; for (unsigned i = 0; i < p.size(); i++) { if (p[i]) r++; } return r; } bool use_parenthesis(value * v) const { if (is_zero(v) || is_nz_rational(v)) return false; rational_function_value * rf = to_rational_function(v); return num_nz_coeffs(rf->num()) > 1 || !is_denominator_one(rf); } template void display_polynomial(std::ostream & out, unsigned sz, value * const * p, DisplayVar const & display_var, bool compact, bool pp) const { if (sz == 0) { out << "0"; return; } unsigned i = sz; bool first = true; while (i > 0) { --i; if (p[i] == 0) continue; if (first) first = false; else out << " + "; if (i == 0) display(out, p[i], compact, pp); else { if (!is_rational_one(p[i])) { if (use_parenthesis(p[i])) { out << "("; display(out, p[i], compact, pp); out << ")"; if (pp) out << " "; else out << "*"; } else { display(out, p[i], compact, pp); if (pp) out << " "; else out << "*"; } } display_var(out, compact, pp); if (i > 1) { if (pp) out << "" << i << ""; else out << "^" << i; } } } } template void display_polynomial(std::ostream & out, polynomial const & p, DisplayVar const & display_var, bool compact, bool pp) const { display_polynomial(out, p.size(), p.c_ptr(), display_var, compact, pp); } struct display_free_var_proc { void operator()(std::ostream & out, bool compact, bool pp) const { out << "x"; } }; struct display_ext_proc { imp const & m; extension * m_ref; display_ext_proc(imp const & _m, extension * r):m(_m), m_ref(r) {} void operator()(std::ostream & out, bool compact, bool pp) const { m.display_ext(out, m_ref, compact, pp); } }; void display_polynomial_expr(std::ostream & out, polynomial const & p, extension * ext, bool compact, bool pp) const { display_polynomial(out, p, display_ext_proc(*this, ext), compact, pp); } static void display_poly_sign(std::ostream & out, int s) { if (s < 0) out << " < 0"; else if (s == 0) out << " = 0"; else out << " > 0"; } void display_sign_conditions(std::ostream & out, sign_condition * sc) const { bool first = true; out << "{"; while (sc) { if (first) first = false; else out << ", "; out << "q(" << sc->qidx() << ")"; display_poly_sign(out, sc->sign()); sc = sc->prev(); } out << "}"; } void display_sign_conditions(std::ostream & out, sign_condition * sc, array const & qs, bool compact, bool pp) const { bool first = true; out << "{"; while (sc) { if (first) first = false; else out << ", "; display_polynomial(out, qs[sc->qidx()], display_free_var_proc(), compact, pp); display_poly_sign(out, sc->sign()); sc = sc->prev(); } out << "}"; } void display_interval(std::ostream & out, mpbqi const & i, bool pp) const { if (pp) bqim().display_pp(out, i); else bqim().display(out, i); } void display_algebraic_def(std::ostream & out, algebraic * a, bool compact, bool pp) const { out << "root("; display_polynomial(out, a->p(), display_free_var_proc(), compact, pp); out << ", "; display_interval(out, a->iso_interval(), pp); out << ", "; if (a->sdt() != 0) display_sign_conditions(out, a->sdt()->sc(a->sc_idx()), a->sdt()->qs(), compact, pp); else out << "{}"; out << ")"; } void display_poly(std::ostream & out, unsigned n, value * const * p) const { collect_algebraic_refs c; for (unsigned i = 0; i < n; i++) c.mark(p[i]); display_polynomial(out, n, p, display_free_var_proc(), true, false); std::sort(c.m_found.begin(), c.m_found.end(), rank_lt_proc()); for (unsigned i = 0; i < c.m_found.size(); i++) { algebraic * ext = c.m_found[i]; out << "\n r!" << ext->idx() << " := "; display_algebraic_def(out, ext, true, false); } } void display_ext(std::ostream & out, extension * r, bool compact, bool pp) const { switch (r->knd()) { case extension::TRANSCENDENTAL: to_transcendental(r)->display(out, pp); break; case extension::INFINITESIMAL: to_infinitesimal(r)->display(out, pp); break; case extension::ALGEBRAIC: if (compact) { if (pp) out << "α" << r->idx() << ""; else out << "r!" << r->idx(); } else { display_algebraic_def(out, to_algebraic(r), compact, pp); } } } void display(std::ostream & out, value * v, bool compact, bool pp=false) const { if (v == 0) out << "0"; else if (is_nz_rational(v)) qm().display(out, to_mpq(v)); else { rational_function_value * rf = to_rational_function(v); if (is_denominator_one(rf)) { display_polynomial_expr(out, rf->num(), rf->ext(), compact, pp); } else if (is_rational_one(rf->num())) { out << "1/("; display_polynomial_expr(out, rf->den(), rf->ext(), compact, pp); out << ")"; } else { out << "("; display_polynomial_expr(out, rf->num(), rf->ext(), compact, pp); out << ")/("; display_polynomial_expr(out, rf->den(), rf->ext(), compact, pp); out << ")"; } } } void display_compact(std::ostream & out, value * a, bool pp=false) const { collect_algebraic_refs c; c.mark(a); if (c.m_found.empty()) { display(out, a, true, pp); } else { std::sort(c.m_found.begin(), c.m_found.end(), rank_lt_proc()); out << "["; display(out, a, true, pp); for (unsigned i = 0; i < c.m_found.size(); i++) { algebraic * ext = c.m_found[i]; if (pp) out << "; α" << ext->idx() << " := "; else out << "; r!" << ext->idx() << " := "; display_algebraic_def(out, ext, true, pp); } out << "]"; } } void display(std::ostream & out, numeral const & a, bool compact=false, bool pp=false) const { if (compact) display_compact(out, a.m_value, pp); else display(out, a.m_value, false, pp); } void display_non_rational_in_decimal(std::ostream & out, numeral const & a, unsigned precision) { SASSERT(!is_zero(a)); SASSERT(!is_nz_rational(a)); mpbqi const & i = interval(a.m_value); if (refine_interval(a.m_value, precision*4)) { // hack if (bqm().is_int(i.lower())) bqm().display_decimal(out, i.upper(), precision); else bqm().display_decimal(out, i.lower(), precision); } else { if (sign(a.m_value) > 0) out << "?"; else out << "-?"; } } void display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { if (is_zero(a)) { out << "0"; } else if (is_nz_rational(a)) { qm().display_decimal(out, to_mpq(a), precision); } else { const_cast(this)->display_non_rational_in_decimal(out, a, precision); } } void display_interval(std::ostream & out, numeral const & a) const { if (is_zero(a)) out << "[0, 0]"; else display_interval(out, interval(a.m_value), false); } }; // Helper object for restoring the value intervals. class save_interval_ctx { manager::imp * m; public: save_interval_ctx(manager const * _this):m(_this->m_imp) { SASSERT (m); } ~save_interval_ctx() { m->restore_saved_intervals(); } }; manager::manager(unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { m_imp = alloc(imp, m, p, a); } manager::~manager() { dealloc(m_imp); } void manager::get_param_descrs(param_descrs & r) { rcf_params::collect_param_descrs(r); } void manager::set_cancel(bool f) { m_imp->set_cancel(f); } void manager::updt_params(params_ref const & p) { m_imp->updt_params(p); } unsynch_mpq_manager & manager::qm() const { return m_imp->m_qm; } void manager::del(numeral & a) { m_imp->del(a); } void manager::mk_infinitesimal(char const * n, char const * pp_n, numeral & r) { m_imp->mk_infinitesimal(n, pp_n, r); } void manager::mk_infinitesimal(numeral & r) { m_imp->mk_infinitesimal(r); } void manager::mk_transcendental(char const * n, char const * pp_n, mk_interval & proc, numeral & r) { m_imp->mk_transcendental(n, pp_n, proc, r); } void manager::mk_transcendental(mk_interval & proc, numeral & r) { m_imp->mk_transcendental(proc, r); } void manager::mk_pi(numeral & r) { m_imp->mk_pi(r); } void manager::mk_e(numeral & r) { m_imp->mk_e(r); } void manager::isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { save_interval_ctx ctx(this); m_imp->isolate_roots(n, as, roots); } void manager::reset(numeral & a) { m_imp->reset(a); } int manager::sign(numeral const & a) { save_interval_ctx ctx(this); return m_imp->sign(a); } bool manager::is_zero(numeral const & a) { return sign(a) == 0; } bool manager::is_pos(numeral const & a) { return sign(a) > 0; } bool manager::is_neg(numeral const & a) { return sign(a) < 0; } bool manager::is_int(numeral const & a) { return m_imp->is_int(a); } bool manager::depends_on_infinitesimals(numeral const & a) { return m_imp->depends_on_infinitesimals(a); } void manager::set(numeral & a, int n) { m_imp->set(a, n); } void manager::set(numeral & a, mpz const & n) { m_imp->set(a, n); } void manager::set(numeral & a, mpq const & n) { m_imp->set(a, n); } void manager::set(numeral & a, numeral const & n) { m_imp->set(a, n); } void manager::swap(numeral & a, numeral & b) { std::swap(a.m_value, b.m_value); } void manager::root(numeral const & a, unsigned k, numeral & b) { save_interval_ctx ctx(this); m_imp->root(a, k, b); } void manager::power(numeral const & a, unsigned k, numeral & b) { save_interval_ctx ctx(this); m_imp->power(a, k, b); } void manager::add(numeral const & a, numeral const & b, numeral & c) { save_interval_ctx ctx(this); m_imp->add(a, b, c); } void manager::add(numeral const & a, mpz const & b, numeral & c) { scoped_numeral _b(*this); set(_b, b); add(a, _b, c); } void manager::sub(numeral const & a, numeral const & b, numeral & c) { save_interval_ctx ctx(this); m_imp->sub(a, b, c); } void manager::mul(numeral const & a, numeral const & b, numeral & c) { save_interval_ctx ctx(this); m_imp->mul(a, b, c); } void manager::neg(numeral & a) { save_interval_ctx ctx(this); m_imp->neg(a); } void manager::neg(numeral const & a, numeral & b) { save_interval_ctx ctx(this); m_imp->neg(a, b); } void manager::inv(numeral & a) { save_interval_ctx ctx(this); m_imp->inv(a); } void manager::inv(numeral const & a, numeral & b) { save_interval_ctx ctx(this); m_imp->inv(a, b); } void manager::div(numeral const & a, numeral const & b, numeral & c) { save_interval_ctx ctx(this); m_imp->div(a, b, c); } int manager::compare(numeral const & a, numeral const & b) { save_interval_ctx ctx(this); return m_imp->compare(a, b); } bool manager::eq(numeral const & a, numeral const & b) { return compare(a, b) == 0; } bool manager::eq(numeral const & a, mpq const & b) { scoped_numeral _b(*this); set(_b, b); return eq(a, _b); } bool manager::eq(numeral const & a, mpz const & b) { scoped_numeral _b(*this); set(_b, b); return eq(a, _b); } bool manager::lt(numeral const & a, numeral const & b) { return compare(a, b) < 0; } bool manager::lt(numeral const & a, mpq const & b) { scoped_numeral _b(*this); set(_b, b); return lt(a, _b); } bool manager::lt(numeral const & a, mpz const & b) { scoped_numeral _b(*this); set(_b, b); return lt(a, _b); } bool manager::gt(numeral const & a, mpq const & b) { scoped_numeral _b(*this); set(_b, b); return gt(a, _b); } bool manager::gt(numeral const & a, mpz const & b) { scoped_numeral _b(*this); set(_b, b); return gt(a, _b); } void manager::display(std::ostream & out, numeral const & a, bool compact, bool pp) const { save_interval_ctx ctx(this); m_imp->display(out, a, compact, pp); } void manager::display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { save_interval_ctx ctx(this); m_imp->display_decimal(out, a, precision); } void manager::display_interval(std::ostream & out, numeral const & a) const { save_interval_ctx ctx(this); m_imp->display_interval(out, a); } void manager::clean_denominators(numeral const & a, numeral & p, numeral & q) { save_interval_ctx ctx(this); m_imp->clean_denominators(a, p, q); } }; void pp(realclosure::manager::imp * imp, realclosure::polynomial const & p, realclosure::extension * ext) { imp->display_polynomial_expr(std::cout, p, ext, false, false); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, realclosure::value * v) { imp->display(std::cout, v, false); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, unsigned sz, realclosure::value * const * p) { for (unsigned i = 0; i < sz; i++) pp(imp, p[i]); } void pp(realclosure::manager::imp * imp, realclosure::manager::imp::value_ref_buffer const & p) { for (unsigned i = 0; i < p.size(); i++) pp(imp, p[i]); } void pp(realclosure::manager::imp * imp, realclosure::manager::imp::value_ref const & v) { pp(imp, v.get()); } void pp(realclosure::manager::imp * imp, realclosure::mpbqi const & i) { imp->bqim().display(std::cout, i); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, realclosure::manager::imp::scoped_mpqi const & i) { imp->qim().display(std::cout, i); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, mpbq const & n) { imp->bqm().display(std::cout, n); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, mpq const & n) { imp->qm().display(std::cout, n); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, realclosure::extension * x) { imp->display_ext(std::cout, x, false, false); std::cout << std::endl; } z3-z3-4.4.1/src/math/realclosure/realclosure.h000066400000000000000000000305361260446376700211650ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: realclosure.h Abstract: Package for computing with elements of the realclosure of a field containing - all rationals - extended with computable transcendental real numbers (e.g., pi and e) - infinitesimals Author: Leonardo (leonardo) 2013-01-02 Notes: --*/ #ifndef REALCLOSURE_H_ #define REALCLOSURE_H_ #include"mpq.h" #include"params.h" #include"scoped_numeral.h" #include"scoped_numeral_vector.h" #include"interval.h" #include"z3_exception.h" namespace realclosure { class num; typedef interval_manager mpqi_manager; typedef default_exception exception; class mk_interval { public: virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) = 0; }; class manager { public: struct imp; private: friend class save_interval_ctx; imp * m_imp; public: manager(unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0); ~manager(); typedef num numeral; typedef svector numeral_vector; typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; static void get_param_descrs(param_descrs & r); static void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } void set_cancel(bool f); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } void updt_params(params_ref const & p); unsynch_mpq_manager & qm() const; void del(numeral & a); /** \brief Add a new infinitesimal to the current field. The new infinitesimal is smaller than any positive element in the field. */ void mk_infinitesimal(char const * name, char const * pp_name, numeral & r); void mk_infinitesimal(numeral & r); /** \brief Add a new transcendental real value to the field. The functor \c mk_interval is used to compute approximations of the transcendental value. This procedure should be used with care, if the value is not really transcendental with respect to the current field, computations with the new numeral may not terminate. Example: we extended the field with Pi. Pi is transcendental with respect to a field that contains only algebraic real numbers. So, this step is fine. Let us call the resultant field F. Then, we extend the field F with 1 - Pi. 1 - Pi is transcendental with respect to algebraic real numbers, but it is NOT transcendental with respect to F, since F contains Pi. */ void mk_transcendental(char const * name, char const * pp_name, mk_interval & proc, numeral & r); void mk_transcendental(mk_interval & proc, numeral & r); /** \brief r <- pi */ void mk_pi(numeral & r); /** \brief r <- e (Euler's constant) */ void mk_e(numeral & r); /** \brief Isolate the roots of the univariate polynomial as[0] + as[1]*x + ... + as[n-1]*x^{n-1} The roots are stored in \c roots. */ void isolate_roots(unsigned n, numeral const * as, numeral_vector & roots); /** \brief a <- 0 */ void reset(numeral & a); /** \brief Return the sign of a. */ int sign(numeral const & a); /** \brief Return true if a is zero. */ bool is_zero(numeral const & a); /** \brief Return true if a is positive. */ bool is_pos(numeral const & a); /** \brief Return true if a is negative. */ bool is_neg(numeral const & a); /** \brief Return true if a is an integer. */ bool is_int(numeral const & a); /** \brief Return true if the representation of \c a depends on infinitesimal extensions. */ bool depends_on_infinitesimals(numeral const & a); /** \brief a <- n */ void set(numeral & a, int n); void set(numeral & a, mpz const & n); void set(numeral & a, mpq const & n); void set(numeral & a, numeral const & n); void swap(numeral & a, numeral & b); /** \brief Return a^{1/k} Throws an exception if (a is negative and k is even) or (k is zero). */ void root(numeral const & a, unsigned k, numeral & b); /** \brief Return a^k Throws an exception if 0^0. */ void power(numeral const & a, unsigned k, numeral & b); /** \brief c <- a + b */ void add(numeral const & a, numeral const & b, numeral & c); void add(numeral const & a, mpz const & b, numeral & c); /** \brief c <- a - b */ void sub(numeral const & a, numeral const & b, numeral & c); /** \brief c <- a * b */ void mul(numeral const & a, numeral const & b, numeral & c); /** \brief a <- -a */ void neg(numeral & a); /** \brief b <- -a */ void neg(numeral const & a, numeral & b); /** \brief a <- 1/a if a != 0 */ void inv(numeral & a); /** \brief b <- 1/a if a != 0 */ void inv(numeral const & a, numeral & b); /** \brief c <- a/b if b != 0 */ void div(numeral const & a, numeral const & b, numeral & c); /** Return -1 if a < b Return 0 if a == b Return 1 if a > b */ int compare(numeral const & a, numeral const & b); /** \brief a == b */ bool eq(numeral const & a, numeral const & b); bool eq(numeral const & a, mpq const & b); bool eq(numeral const & a, mpz const & b); /** \brief a != b */ bool neq(numeral const & a, numeral const & b) { return !eq(a, b); } bool neq(numeral const & a, mpq const & b) { return !eq(a, b); } bool neq(numeral const & a, mpz const & b) { return !eq(a, b); } /** \brief a < b */ bool lt(numeral const & a, numeral const & b); bool lt(numeral const & a, mpq const & b); bool lt(numeral const & a, mpz const & b); /** \brief a > b */ bool gt(numeral const & a, numeral const & b) { return lt(b, a); } bool gt(numeral const & a, mpq const & b); bool gt(numeral const & a, mpz const & b); /** \brief a <= b */ bool le(numeral const & a, numeral const & b) { return !gt(a, b); } bool le(numeral const & a, mpq const & b) { return !gt(a, b); } bool le(numeral const & a, mpz const & b) { return !gt(a, b); } /** \brief a >= b */ bool ge(numeral const & a, numeral const & b) { return !lt(a, b); } bool ge(numeral const & a, mpq const & b) { return !lt(a, b); } bool ge(numeral const & a, mpz const & b) { return !lt(a, b); } void display(std::ostream & out, numeral const & a, bool compact=false, bool pp=false) const; /** \brief Display a real number in decimal notation. A question mark is added based on the precision requested. This procedure throws an exception if the \c a is not a real. */ void display_decimal(std::ostream & out, numeral const & a, unsigned precision = 10) const; void display_interval(std::ostream & out, numeral const & a) const; void clean_denominators(numeral const & a, numeral & p, numeral & q); }; struct value; class num { friend class manager; friend struct manager::imp; value * m_value; public: num():m_value(0) {} // Low level functions for implementing the C API void * c_ptr() { return m_value; } static num mk(void * ptr) { num r; r.m_value = reinterpret_cast(ptr); return r; } }; }; typedef realclosure::manager rcmanager; typedef rcmanager::numeral rcnumeral; typedef rcmanager::numeral_vector rcnumeral_vector; typedef rcmanager::scoped_numeral scoped_rcnumeral; typedef rcmanager::scoped_numeral_vector scoped_rcnumeral_vector; #define RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \ inline bool EXTERNAL(scoped_rcnumeral const & a, TYPE const & b) { \ rcmanager & m = a.m(); \ scoped_rcnumeral _b(m); \ m.set(_b, b); \ return m.INTERNAL(a, _b); \ } #define RCF_MK_COMPARISON(EXTERNAL, INTERNAL) \ RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \ RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \ RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpq) RCF_MK_COMPARISON(operator==, eq); RCF_MK_COMPARISON(operator!=, neq); RCF_MK_COMPARISON(operator<, lt); RCF_MK_COMPARISON(operator<=, le); RCF_MK_COMPARISON(operator>, gt); RCF_MK_COMPARISON(operator>=, ge); #undef RCF_MK_COMPARISON #undef RCF_MK_COMPARISON_CORE #define RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \ inline scoped_rcnumeral EXTERNAL(scoped_rcnumeral const & a, TYPE const & b) { \ rcmanager & m = a.m(); \ scoped_rcnumeral _b(m); \ m.set(_b, b); \ scoped_rcnumeral r(m); \ m.INTERNAL(a, _b, r); \ return r; \ } #define RCF_MK_BINARY(EXTERNAL, INTERNAL) \ RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \ RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \ RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpq) RCF_MK_BINARY(operator+, add) RCF_MK_BINARY(operator-, sub) RCF_MK_BINARY(operator*, mul) RCF_MK_BINARY(operator/, div) #undef RCF_MK_BINARY #undef RCF_MK_BINARY_CORE inline scoped_rcnumeral root(scoped_rcnumeral const & a, unsigned k) { scoped_rcnumeral r(a.m()); a.m().root(a, k, r); return r; } inline scoped_rcnumeral power(scoped_rcnumeral const & a, unsigned k) { scoped_rcnumeral r(a.m()); a.m().power(a, k, r); return r; } inline scoped_rcnumeral operator^(scoped_rcnumeral const & a, unsigned k) { return power(a, k); } inline bool is_int(scoped_rcnumeral const & a) { return a.m().is_int(a); } struct rc_sym_pp { rcmanager & m; rcnumeral const & n; rc_sym_pp(rcmanager & _m, rcnumeral const & _n):m(_m), n(_n) {} rc_sym_pp(scoped_rcnumeral const & _n):m(_n.m()), n(_n.get()) {} }; inline rc_sym_pp sym_pp(scoped_rcnumeral const & _n) { return rc_sym_pp(_n); } inline std::ostream & operator<<(std::ostream & out, rc_sym_pp const & n) { n.m.display(out, n.n); return out; } struct rc_decimal_pp { rcmanager & m; rcnumeral const & n; unsigned prec; rc_decimal_pp(rcmanager & _m, rcnumeral const & _n, unsigned p):m(_m), n(_n), prec(p) {} rc_decimal_pp(scoped_rcnumeral const & _n, unsigned p):m(_n.m()), n(_n.get()), prec(p) {} }; inline std::ostream & operator<<(std::ostream & out, rc_decimal_pp const & n) { n.m.display_decimal(out, n.n, n.prec); return out; } inline rc_decimal_pp decimal_pp(scoped_rcnumeral const & n, unsigned prec = 10) { return rc_decimal_pp(n, prec); } struct rc_interval_pp { rcmanager & m; rcnumeral const & n; rc_interval_pp(rcmanager & _m, rcnumeral const & _n):m(_m), n(_n) {} rc_interval_pp(scoped_rcnumeral const & _n):m(_n.m()), n(_n.get()) {} }; inline std::ostream & operator<<(std::ostream & out, rc_interval_pp const & n) { n.m.display_interval(out, n.n); return out; } inline rc_interval_pp interval_pp(scoped_rcnumeral const & n) { return rc_interval_pp(n); } #endif z3-z3-4.4.1/src/math/simplex/000077500000000000000000000000001260446376700156265ustar00rootroot00000000000000z3-z3-4.4.1/src/math/simplex/network_flow.h000066400000000000000000000154361260446376700205300ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: network_flow.h Abstract: Implements Network Simplex algorithm for min cost flow problem Author: Anh-Dung Phan (t-anphan) 2013-10-24 Notes: This will be used to solve the dual of min cost flow problem i.e. optimization of difference constraint. We need a function to reduce DL constraints to min cost flow problem and another function to convert from min cost flow solution to DL solution. It remains unclear how to convert DL assignment to a basic feasible solution of Network Simplex. A naive approach is to run an algorithm on max flow in order to get a spanning tree. --*/ #ifndef NETWORK_FLOW_H_ #define NETWORK_FLOW_H_ #include"inf_rational.h" #include"diff_logic.h" #include"spanning_tree.h" namespace smt { enum min_flow_result { // Min cost flow problem is infeasible. // Diff logic optimization could be unbounded or infeasible. INFEASIBLE, // Min cost flow and diff logic optimization are both optimal. OPTIMAL, // Min cost flow problem is unbounded. // Diff logic optimization has to be infeasible. UNBOUNDED, }; enum pivot_rule { // First eligible edge pivot rule // Edges are traversed in a wraparound fashion FIRST_ELIGIBLE, // Best eligible edge pivot rule // The best edge is selected in every iteration BEST_ELIGIBLE, // Candidate list pivot rule // Major iterations: candidate list is built from eligible edges (in a wraparound way) // Minor iterations: the best edge is selected from the list CANDIDATE_LIST }; // Solve minimum cost flow problem using Network Simplex algorithm template class network_flow : private Ext { private: enum edge_state { LOWER = 1, BASIS = 0, }; typedef dl_var node; typedef dl_edge edge; typedef dl_graph graph; typedef typename Ext::numeral numeral; typedef typename Ext::fin_numeral fin_numeral; class pivot_rule_impl { protected: graph & m_graph; svector & m_states; vector & m_potentials; edge_id & m_enter_id; bool edge_in_tree(edge_id id) const { return m_states[id] == BASIS; } public: pivot_rule_impl(graph & g, vector & potentials, svector & states, edge_id & enter_id) : m_graph(g), m_potentials(potentials), m_states(states), m_enter_id(enter_id) { } virtual ~pivot_rule_impl() {} virtual bool choose_entering_edge() = 0; virtual pivot_rule rule() const = 0; }; class first_eligible_pivot : public pivot_rule_impl { edge_id m_next_edge; public: first_eligible_pivot(graph & g, vector & potentials, svector & states, edge_id & enter_id) : pivot_rule_impl(g, potentials, states, enter_id), m_next_edge(0) { } virtual bool choose_entering_edge(); virtual pivot_rule rule() const { return FIRST_ELIGIBLE; } }; class best_eligible_pivot : public pivot_rule_impl { public: best_eligible_pivot(graph & g, vector & potentials, svector & states, edge_id & enter_id) : pivot_rule_impl(g, potentials, states, enter_id) { } virtual pivot_rule rule() const { return BEST_ELIGIBLE; } virtual bool choose_entering_edge(); }; class candidate_list_pivot : public pivot_rule_impl { private: edge_id m_next_edge; svector m_candidates; unsigned m_num_candidates; unsigned m_minor_step; unsigned m_current_length; static const unsigned NUM_CANDIDATES = 10; static const unsigned MINOR_STEP_LIMIT = 5; public: candidate_list_pivot(graph & g, vector & potentials, svector & states, edge_id & enter_id) : pivot_rule_impl(g, potentials, states, enter_id), m_next_edge(0), m_minor_step(0), m_current_length(0), m_num_candidates(NUM_CANDIDATES), m_candidates(m_num_candidates) { } virtual pivot_rule rule() const { return CANDIDATE_LIST; } virtual bool choose_entering_edge(); }; graph m_graph; scoped_ptr m_tree; scoped_ptr m_pivot; vector m_balances; // nodes + 1 |-> [b -1b] Denote supply/demand b_i on node i vector m_potentials; // nodes + 1 |-> initial: +/- 1 // Duals of flows which are convenient to compute dual solutions // become solutions to Dual simplex. vector m_flows; // edges + nodes |-> assignemnt Basic feasible flows svector m_states; unsigned m_step; edge_id m_enter_id; edge_id m_leave_id; optional m_delta; // Initialize the network with a feasible spanning tree void initialize(); void update_potentials(); void update_flows(); bool choose_entering_edge(pivot_rule pr); // Send as much flow as possible around the cycle, the first basic edge with flow 0 will leave // Return false if the problem is unbounded bool choose_leaving_edge(); void update_spanning_tree(); numeral get_cost() const; bool edge_in_tree(edge_id id) const; bool is_infeasible(); bool check_well_formed(); bool check_optimal(); void display_primal(std::ofstream & os); void display_dual(std::ofstream & os); void display_spanning_tree(std::ofstream & os); void display_system(std::ofstream & os); public: network_flow(graph & g, vector const & balances); // Minimize cost flows // Return true if found an optimal solution, and return false if unbounded min_flow_result min_cost(pivot_rule pr = FIRST_ELIGIBLE); // Compute the optimal solution numeral get_optimal_solution(vector & result, bool is_dual); }; } #endif z3-z3-4.4.1/src/math/simplex/network_flow_def.h000066400000000000000000000466661260446376700213570ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: network_flow_def.h Abstract: Implements Network Simplex algorithm for min cost flow problem Author: Anh-Dung Phan (t-anphan) 2013-10-24 Notes: --*/ #ifndef NETWORK_FLOW_DEF_H_ #define NETWORK_FLOW_DEF_H_ #include"network_flow.h" #include"uint_set.h" #include"spanning_tree_def.h" namespace smt { template bool network_flow::first_eligible_pivot::choose_entering_edge() { numeral cost = numeral::zero(); int num_edges = m_graph.get_num_edges(); for (int i = m_next_edge; i < m_next_edge + num_edges; ++i) { edge_id id = i % num_edges; if (edge_in_tree(id)) { continue; } node src = m_graph.get_source(id); node tgt = m_graph.get_target(id); cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id); if (cost.is_pos()) { m_next_edge = m_enter_id = id; return true; } } return false; }; template bool network_flow::best_eligible_pivot::choose_entering_edge() { unsigned num_edges = m_graph.get_num_edges(); numeral cost = numeral::zero(); for (unsigned i = 0; i < num_edges; ++i) { node src = m_graph.get_source(i); node tgt = m_graph.get_target(i); if (!edge_in_tree(i)) { numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(i); if (new_cost > cost) { cost = new_cost; m_enter_id = i; } } } return cost.is_pos(); }; template bool network_flow::candidate_list_pivot::choose_entering_edge() { numeral cost = numeral::zero(); if (m_current_length == 0 || m_minor_step == MINOR_STEP_LIMIT) { // Build the candidate list unsigned num_edges = m_graph.get_num_edges(); m_current_length = 0; for (unsigned i = m_next_edge; i < m_next_edge + num_edges; ++i) { edge_id id = (i >= num_edges) ? i - num_edges : i; node src = m_graph.get_source(id); node tgt = m_graph.get_target(id); if (!edge_in_tree(id)) { numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id); if (new_cost.is_pos()) { m_candidates[m_current_length] = id; ++m_current_length; if (new_cost > cost) { cost = new_cost; m_enter_id = id; } } if (m_current_length >= m_num_candidates) break; } } m_next_edge = m_enter_id; m_minor_step = 1; return cost.is_pos(); } ++m_minor_step; for (unsigned i = 0; i < m_current_length; ++i) { edge_id id = m_candidates[i]; node src = m_graph.get_source(id); node tgt = m_graph.get_target(id); if (!edge_in_tree(id)) { numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id); if (new_cost > cost) { cost = new_cost; m_enter_id = id; } // Remove stale candidates if (!new_cost.is_pos()) { --m_current_length; m_candidates[i] = m_candidates[m_current_length]; --i; } } } return cost.is_pos(); }; template network_flow::network_flow(graph & g, vector const & balances) : m_balances(balances) { // Network flow graph has the edges in the reversed order compared to constraint graph // We only take enabled edges from the original graph for (unsigned i = 0; i < g.get_num_nodes(); ++i) { m_graph.init_var(i); } vector const & es = g.get_all_edges(); for (unsigned i = 0; i < es.size(); ++i) { edge const & e = es[i]; if (e.is_enabled()) { m_graph.add_edge(e.get_target(), e.get_source(), e.get_weight(), explanation()); } } TRACE("network_flow", { tout << "Difference logic optimization:" << std::endl; display_dual(tout); tout << "Minimum cost flow:" << std::endl; display_primal(tout); };); m_step = 0; m_tree = alloc(basic_spanning_tree, m_graph); } template void network_flow::initialize() { TRACE("network_flow", tout << "initialize...\n";); // Create an artificial root node to construct initial spanning tree unsigned num_nodes = m_graph.get_num_nodes(); unsigned num_edges = m_graph.get_num_edges(); node root = num_nodes; m_graph.init_var(root); m_potentials.resize(num_nodes + 1); m_potentials[root] = numeral::zero(); m_balances.resize(num_nodes + 1); fin_numeral sum_supply = fin_numeral::zero(); for (unsigned i = 0; i < num_nodes; ++i) { sum_supply += m_balances[i]; } m_balances[root] = -sum_supply; m_flows.resize(num_nodes + num_edges); m_flows.fill(numeral::zero()); m_states.resize(num_nodes + num_edges); m_states.fill(LOWER); // Create artificial edges from/to root node to/from other nodes and initialize the spanning tree svector tree; for (unsigned i = 0; i < num_nodes; ++i) { bool is_forward = !m_balances[i].is_neg(); m_states[num_edges + i] = BASIS; node src = is_forward ? i : root; node tgt = is_forward ? root : i; m_flows[num_edges + i] = is_forward ? m_balances[i] : -m_balances[i]; m_potentials[i] = is_forward ? numeral::one() : -numeral::one(); tree.push_back(m_graph.add_edge(src, tgt, numeral::one(), explanation())); } m_tree->initialize(tree); TRACE("network_flow", tout << pp_vector("Potentials", m_potentials); tout << pp_vector("Flows", m_flows); tout << "Cost: " << get_cost() << "\n"; tout << "Spanning tree:\n"; display_spanning_tree(tout); display_primal(tout);); SASSERT(check_well_formed()); } template void network_flow::update_potentials() { node src = m_graph.get_source(m_enter_id); node tgt = m_graph.get_target(m_enter_id); numeral cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(m_enter_id); numeral change; node start; if (m_tree->in_subtree_t2(tgt)) { change = cost; start = tgt; } else { change = -cost; start = src; } SASSERT(m_tree->in_subtree_t2(start)); TRACE("network_flow", tout << "update_potentials of T_" << start << " with change = " << change << "...\n";); svector descendants; m_tree->get_descendants(start, descendants); SASSERT(descendants.size() >= 1); for (unsigned i = 0; i < descendants.size(); ++i) { node u = descendants[i]; m_potentials[u] += change; } TRACE("network_flow", tout << pp_vector("Potentials", m_potentials);); } template void network_flow::update_flows() { m_flows[m_enter_id] += *m_delta; node src = m_graph.get_source(m_enter_id); node tgt = m_graph.get_target(m_enter_id); svector path; svector against; m_tree->get_path(src, tgt, path, against); SASSERT(path.size() >= 1); for (unsigned i = 0; i < path.size(); ++i) { edge_id e_id = path[i]; m_flows[e_id] += against[i] ? - *m_delta : *m_delta; } TRACE("network_flow", tout << pp_vector("Flows", m_flows);); } template bool network_flow::choose_leaving_edge() { node src = m_graph.get_source(m_enter_id); node tgt = m_graph.get_target(m_enter_id); m_delta.set_invalid(); edge_id leave_id = null_edge_id; svector path; svector against; m_tree->get_path(src, tgt, path, against); SASSERT(path.size() >= 1); for (unsigned i = 0; i < path.size(); ++i) { edge_id e_id = path[i]; if (against[i] && (!m_delta || m_flows[e_id] < *m_delta)) { m_delta = m_flows[e_id]; leave_id = e_id; } } m_leave_id = leave_id; return m_delta; } template void network_flow::update_spanning_tree() { m_tree->update(m_enter_id, m_leave_id); } template bool network_flow::choose_entering_edge(pivot_rule pr) { if (!m_pivot || pr != m_pivot->rule()) { switch (pr) { case FIRST_ELIGIBLE: m_pivot = alloc(first_eligible_pivot, m_graph, m_potentials, m_states, m_enter_id); break; case BEST_ELIGIBLE: m_pivot = alloc(best_eligible_pivot, m_graph, m_potentials, m_states, m_enter_id); break; case CANDIDATE_LIST: m_pivot = alloc(candidate_list_pivot, m_graph, m_potentials, m_states, m_enter_id); break; default: UNREACHABLE(); } } return m_pivot->choose_entering_edge(); } // Minimize cost flows template min_flow_result network_flow::min_cost(pivot_rule pr) { initialize(); while (choose_entering_edge(pr)) { bool bounded = choose_leaving_edge(); if (!bounded) return UNBOUNDED; vectorconst& es = m_graph.get_all_edges(); TRACE("network_flow", { edge const& e_in = es[m_enter_id]; edge const& e_out = es[m_leave_id]; node src_in = e_in.get_source(); node tgt_in = e_in.get_target(); node src_out = e_out.get_source(); node tgt_out = e_out.get_target(); numeral c1 = m_potentials[src_in] - m_potentials[tgt_in] - m_graph.get_weight(m_enter_id); numeral c2 = m_potentials[src_out] - m_potentials[tgt_out] - m_graph.get_weight(m_leave_id); tout << "new base: y_" << src_in << "_" << tgt_in << " cost: " << c1 << " delta: " << *m_delta << "\n"; tout << "old base: y_" << src_out << "_" << tgt_out << " cost: " << c2 << "\n"; } ); update_flows(); if (m_enter_id != m_leave_id) { SASSERT(edge_in_tree(m_leave_id)); SASSERT(!edge_in_tree(m_enter_id)); m_states[m_enter_id] = BASIS; m_states[m_leave_id] = LOWER; update_spanning_tree(); update_potentials(); TRACE("network_flow", tout << "Spanning tree:\n"; display_spanning_tree(tout); tout << "Cost: " << get_cost() << "\n"; display_primal(tout); ); SASSERT(check_well_formed()); } } TRACE("network_flow", tout << "Spanning tree:\n"; display_spanning_tree(tout); tout << "Cost: " << get_cost() << "\n"; display_primal(tout); ); if (is_infeasible()) return INFEASIBLE; TRACE("network_flow", tout << "Found optimal solution.\n";); SASSERT(check_optimal()); return OPTIMAL; } template bool network_flow::is_infeasible() { // Flows of artificial arcs should be zero unsigned num_nodes = m_graph.get_num_nodes(); unsigned num_edges = m_graph.get_num_edges(); SASSERT(m_flows.size() == num_edges); for (unsigned i = 1; i < num_nodes; ++i) { if (m_flows[num_edges - i].is_pos()) return true; } return false; } // Get the optimal solution template typename network_flow::numeral network_flow::get_optimal_solution(vector & result, bool is_dual) { numeral objective_value = get_cost(); result.reset(); if (is_dual) { result.append(m_potentials); } else { result.append(m_flows); } return objective_value; } template typename network_flow::numeral network_flow::get_cost() const { numeral objective_value = numeral::zero(); unsigned num_edges = m_graph.get_num_edges(); for (unsigned i = 0; i < num_edges; ++i) { if (edge_in_tree(i)) { objective_value += m_flows[i].get_rational() * m_graph.get_weight(i); } } return objective_value; } template bool network_flow::edge_in_tree(edge_id id) const { return m_states[id] == BASIS; } template bool network_flow::check_well_formed() { SASSERT(m_tree->check_well_formed()); SASSERT(!m_delta || !(*m_delta).is_neg()); // m_flows are zero on non-basic edges for (unsigned i = 0; i < m_flows.size(); ++i) { SASSERT(!m_flows[i].is_neg()); SASSERT(edge_in_tree(i) || m_flows[i].is_zero()); } unsigned num_edges = m_graph.get_num_edges(); for (unsigned i = 0; i < num_edges; ++i) { if (edge_in_tree(i)) { dl_var src = m_graph.get_source(i); dl_var tgt = m_graph.get_target(i); numeral weight = m_graph.get_weight(i); SASSERT(m_potentials[src] - m_potentials[tgt] == weight); } } return true; } template bool network_flow::check_optimal() { numeral total_cost = get_cost(); unsigned num_edges = m_graph.get_num_edges(); for (unsigned i = 0; i < num_edges; ++i) { dl_var src = m_graph.get_source(i); dl_var tgt = m_graph.get_target(i); numeral weight = m_graph.get_weight(i); SASSERT(m_potentials[src] - m_potentials[tgt] <= weight); } // m_flows are zero on non-basic edges for (unsigned i = 0; i < m_flows.size(); ++i) { SASSERT(edge_in_tree(i) || m_flows[i].is_zero()); } numeral total_balance = numeral::zero(); for (unsigned i = 0; i < m_potentials.size(); ++i) { total_balance += m_balances[i] * m_potentials[i]; } TRACE("network_flow", tout << "Total balance: " << total_balance << ", total cost: " << total_cost << std::endl;); return total_cost == total_balance; } // display methods template void network_flow::display_primal(std::ofstream & os) { vector const & es = m_graph.get_all_edges(); for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { edge const & e = es[i]; os << "(declare-fun y_" << e.get_source() << "_" << e.get_target() << " () Real)" << std::endl; }; for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { edge const & e = es[i]; os << "(assert (>= y_" << e.get_source() << "_" << e.get_target() << " 0))" << std::endl; }; for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) { bool initialized = false; for (unsigned j = 0; j < m_graph.get_num_edges(); ++j) { edge const & e = es[j]; if (e.get_target() == i || e.get_source() == i) { if (!initialized) { os << "(assert (= (+"; } initialized = true; if (e.get_target() == i) { os << " y_" << e.get_source() << "_" << e.get_target(); } else { os << " (- y_" << e.get_source() << "_" << e.get_target() << ")"; } } } if(initialized) { os << " " << m_balances[i] << ") 0))" << std::endl; } } os << "(minimize (+"; for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { edge const & e = es[i]; os << " (* " << e.get_weight() << " y_" << e.get_source() << "_" << e.get_target() << ")"; }; os << "))" << std::endl; os << "(optimize)" << std::endl; if (!m_flows.empty()) { for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { edge const & e = es[i]; os << "; y_" << e.get_source() << "_" << e.get_target() << " = " << m_flows[i] << "\n"; } } } template void network_flow::display_dual(std::ofstream & os) { for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) { os << "(declare-fun v" << i << " () Real)" << std::endl; } vector const & es = m_graph.get_all_edges(); for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { edge const & e = es[i]; os << "(assert (<= (- v" << e.get_source() << " v" << e.get_target() << ") " << e.get_weight() << "))" << std::endl; }; os << "(assert (= v0 0))" << std::endl; os << "(maximize (+"; for (unsigned i = 0; i < m_balances.size(); ++i) { os << " (* " << m_balances[i] << " v" << i << ")"; }; os << "))" << std::endl; os << "(optimize)" << std::endl; } template void network_flow::display_spanning_tree(std::ofstream & os) { ++m_step;; std::string prefix = "T"; prefix.append(std::to_string(m_step)); prefix.append("_"); unsigned root = m_graph.get_num_nodes() - 1; for (unsigned i = 0; i < root; ++i) { os << prefix << i << "[shape=circle,label=\"" << prefix << i << " ["; os << m_potentials[i] << "/" << m_balances[i] << "]\"];\n"; } os << prefix << root << "[shape=doublecircle,label=\"" << prefix << root << " ["; os << m_potentials[root] << "/" << m_balances[root] << "]\"];\n"; unsigned num_edges = m_graph.get_num_edges(); for (unsigned i = 0; i < num_edges; ++i) { os << prefix << m_graph.get_source(i) << " -> " << prefix << m_graph.get_target(i); if (edge_in_tree(i)) { os << "[color=red,penwidth=3.0,label=\"" << m_flows[i] << "/" << m_graph.get_weight(i) << "\"];\n"; } else { os << "[label=\"" << m_flows[i] << "/" << m_graph.get_weight(i) << "\"];\n"; } } os << std::endl; } } #endif z3-z3-4.4.1/src/math/simplex/simplex.cpp000066400000000000000000000005521260446376700200150ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: simplex.h Abstract: Multi-precision simplex tableau. Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: --*/ #include"simplex.h" #include"sparse_matrix_def.h" #include"simplex_def.h" namespace simplex { template class simplex; template class simplex; }; z3-z3-4.4.1/src/math/simplex/simplex.h000066400000000000000000000162161260446376700174660ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: simplex.h Abstract: Multi-precision simplex tableau. - It uses code from theory_arith where applicable. - It is detached from the theory class and ASTs. - It uses non-shared mpz/mpq's avoiding global locks and operations on rationals. - It follows the same sparse tableau layout (no LU yet). - It does not include features for non-linear arithmetic. - Branch/bound/cuts is external. Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: --*/ #ifndef SIMPLEX_H_ #define SIMPLEX_H_ #include "sparse_matrix.h" #include "mpq_inf.h" #include "heap.h" #include "lbool.h" #include "uint_set.h" namespace simplex { template class simplex { typedef unsigned var_t; typedef typename Ext::eps_numeral eps_numeral; typedef typename Ext::numeral numeral; typedef typename Ext::manager manager; typedef typename Ext::eps_manager eps_manager; typedef typename Ext::scoped_numeral scoped_numeral; typedef _scoped_numeral scoped_eps_numeral; typedef _scoped_numeral_vector scoped_eps_numeral_vector; typedef sparse_matrix matrix; struct var_lt { bool operator()(var_t v1, var_t v2) const { return v1 < v2; } }; typedef heap var_heap; struct stats { unsigned m_num_pivots; unsigned m_num_infeasible; unsigned m_num_checks; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; enum pivot_strategy_t { S_BLAND, S_GREATEST_ERROR, S_LEAST_ERROR, S_DEFAULT }; struct var_info { unsigned m_base2row:29; unsigned m_is_base:1; unsigned m_lower_valid:1; unsigned m_upper_valid:1; eps_numeral m_value; eps_numeral m_lower; eps_numeral m_upper; numeral m_base_coeff; var_info(): m_base2row(0), m_is_base(false), m_lower_valid(false), m_upper_valid(false) {} }; static const var_t null_var; mutable manager m; mutable eps_manager em; mutable matrix M; unsigned m_max_iterations; volatile bool m_cancel; var_heap m_to_patch; vector m_vars; svector m_row2base; bool m_bland; unsigned m_blands_rule_threshold; random_gen m_random; uint_set m_left_basis; unsigned m_infeasible_var; unsigned_vector m_base_vars; stats m_stats; public: simplex(): M(m), m_max_iterations(UINT_MAX), m_cancel(false), m_to_patch(1024), m_bland(false), m_blands_rule_threshold(1000) {} typedef typename matrix::row row; typedef typename matrix::row_iterator row_iterator; typedef typename matrix::col_iterator col_iterator; void ensure_var(var_t v); row add_row(var_t base, unsigned num_vars, var_t const* vars, numeral const* coeffs); row get_infeasible_row(); var_t get_base_var(row const& r) const { return m_row2base[r.id()]; } numeral const& get_base_coeff(row const& r) const { return m_vars[m_row2base[r.id()]].m_base_coeff; } void del_row(var_t base_var); void set_lower(var_t var, eps_numeral const& b); void set_upper(var_t var, eps_numeral const& b); void get_lower(var_t var, scoped_eps_numeral& b) const { b = m_vars[var].m_lower; } void get_upper(var_t var, scoped_eps_numeral& b) const { b = m_vars[var].m_upper; } bool above_lower(var_t var, eps_numeral const& b) const; bool below_upper(var_t var, eps_numeral const& b) const; bool below_lower(var_t v) const; bool above_upper(var_t v) const; bool lower_valid(var_t var) const { return m_vars[var].m_lower_valid; } bool upper_valid(var_t var) const { return m_vars[var].m_upper_valid; } void unset_lower(var_t var); void unset_upper(var_t var); void set_value(var_t var, eps_numeral const& b); void set_cancel(bool f) { m_cancel = f; } void set_max_iterations(unsigned n) { m_max_iterations = n; } void reset(); lbool make_feasible(); lbool minimize(var_t var); eps_numeral const& get_value(var_t v); void display(std::ostream& out) const; void display_row(std::ostream& out, row const& r, bool values = true); unsigned get_num_vars() const { return m_vars.size(); } row_iterator row_begin(row const& r) { return M.row_begin(r); } row_iterator row_end(row const& r) { return M.row_end(r); } void collect_statistics(::statistics & st) const; private: void del_row(row const& r); var_t select_var_to_fix(); pivot_strategy_t pivot_strategy(); var_t select_smallest_var() { return m_to_patch.empty()?null_var:m_to_patch.erase_min(); } var_t select_error_var(bool least); void check_blands_rule(var_t v, unsigned& num_repeated); bool make_var_feasible(var_t x_i); void update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value); void update_value(var_t v, eps_numeral const& delta); void update_value_core(var_t v, eps_numeral const& delta); void pivot(var_t x_i, var_t x_j, numeral const& a_ij); void move_to_bound(var_t x, bool to_lower); var_t select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij); var_t select_pivot_blands(var_t x_i, bool is_below, scoped_numeral& out_a_ij); var_t select_pivot_core(var_t x_i, bool is_below, scoped_numeral& out_a_ij); int get_num_non_free_dep_vars(var_t x_j, int best_so_far); var_t pick_var_to_leave(var_t x_j, bool is_pos, scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc); void select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc_x_i, bool& inc_x_j); bool at_lower(var_t v) const; bool at_upper(var_t v) const; bool above_lower(var_t v) const; bool below_upper(var_t v) const; bool outside_bounds(var_t v) const { return below_lower(v) || above_upper(v); } bool is_free(var_t v) const { return !m_vars[v].m_lower_valid && !m_vars[v].m_upper_valid; } bool is_non_free(var_t v) const { return !is_free(v); } bool is_base(var_t x) const { return m_vars[x].m_is_base; } void add_patch(var_t v); bool well_formed() const; bool well_formed_row(row const& r) const; bool is_feasible() const; }; }; #endif z3-z3-4.4.1/src/math/simplex/simplex_def.h000066400000000000000000001061571260446376700203100ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: simplex_def.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: Sign of base variables can vary. Sign could possibly be normalized to positive. Otherwise, sign could be accounted in pivoting. --*/ #ifndef SIMPLEX_DEF_H_ #define SIMPLEX_DEF_H_ namespace simplex { template const typename simplex::var_t simplex::null_var = UINT_MAX; template typename simplex::row simplex::add_row(var_t base_var, unsigned num_vars, var_t const* vars, numeral const* coeffs) { m_base_vars.reset(); row r = M.mk_row(); for (unsigned i = 0; i < num_vars; ++i) { if (!m.is_zero(coeffs[i])) { var_t v = vars[i]; if (is_base(v)) { m_base_vars.push_back(i); } M.add_var(r, coeffs[i], v); } } scoped_numeral mul(m), a(m), b(m), c(m); m.set(mul, 1); for (unsigned i = 0; i < m_base_vars.size(); ++i) { var_t v = vars[m_base_vars[i]]; m.mul(coeffs[m_base_vars[i]], mul, a); m.set(b, m_vars[v].m_base_coeff); m.lcm(a, b, c); TRACE("simplex", m.display(tout << " a: ", a); m.display(tout << " b v" << v << " : ", b); m.display(tout << " c: ", c); tout << "\n"; M.display_row(tout, r); M.display_row(tout, row(m_vars[v].m_base2row)); if (m.is_zero(b)) { display(tout); }); SASSERT(is_base(v)); m.abs(c); m.div(c, a, b); m.div(c, m_vars[v].m_base_coeff, a); m.mul(mul, b, mul); M.mul(r, b); m.neg(a); M.add(r, a, row(m_vars[v].m_base2row)); TRACE("simplex", M.display_row(tout, r);); } scoped_numeral base_coeff(m); scoped_eps_numeral value(em), tmp(em); row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { var_t v = it->m_var; if (v == base_var) { m.set(base_coeff, it->m_coeff); } else { SASSERT(!is_base(v)); em.mul(m_vars[v].m_value, it->m_coeff, tmp); em.add(value, tmp, value); } } SASSERT(!m.is_zero(base_coeff)); TRACE("simplex", for (unsigned i = 0; i < num_vars; ++i) { m.display(tout << "v" << vars[i] << " * ", coeffs[i]); tout << " "; if (i + 1 < num_vars) tout << " + "; } tout << "\n"; row_iterator it2 = M.row_begin(r); bool first = true; for (; it2 != end; ++it2) { if (!first) tout << " + "; tout << "v" << it2->m_var << " * "; m.display(tout, it2->m_coeff); tout << " "; first = false; } tout << "\n"; ); SASSERT(!is_base(base_var)); em.neg(value); em.div(value, base_coeff, value); while (m_row2base.size() <= r.id()) { m_row2base.push_back(null_var); } m_row2base[r.id()] = base_var; m_vars[base_var].m_base2row = r.id(); m_vars[base_var].m_is_base = true; m.set(m_vars[base_var].m_base_coeff, base_coeff); em.set(m_vars[base_var].m_value, value); add_patch(base_var); SASSERT(well_formed_row(r)); SASSERT(well_formed()); return r; } template typename simplex::row simplex::get_infeasible_row() { SASSERT(is_base(m_infeasible_var)); unsigned row_id = m_vars[m_infeasible_var].m_base2row; return row(row_id); } template void simplex::add_patch(var_t v) { SASSERT(is_base(v)); if (outside_bounds(v)) { TRACE("simplex", tout << "Add patch: v" << v << "\n";); m_to_patch.insert(v); } } template void simplex::del_row(row const& r) { var_t var = m_row2base[r.id()]; m_vars[var].m_is_base = false; m_vars[var].m_lower_valid = false; m_vars[var].m_upper_valid = false; m_row2base[r.id()] = null_var; M.del(r); SASSERT(M.col_begin(var) == M.col_end(var)); SASSERT(well_formed()); } template void simplex::del_row(var_t var) { TRACE("simplex", tout << var << "\n";); row r; if (is_base(var)) { r = row(m_vars[var].m_base2row); } else { col_iterator it = M.col_begin(var), end = M.col_end(var); if (it == end) { return; } typename matrix::row_entry const& re = it.get_row_entry(); r = it.get_row(); var_t old_base = m_row2base[r.id()]; scoped_eps_numeral new_value(em); var_info& vi = m_vars[old_base]; if (below_lower(old_base)) { new_value = vi.m_lower; } else if (above_upper(old_base)) { new_value = vi.m_upper; } else { new_value = vi.m_value; } // need to move var such that old_base comes in bound. update_and_pivot(old_base, var, re.m_coeff, new_value); SASSERT(is_base(var)); SASSERT(m_vars[var].m_base2row == r.id()); SASSERT(!below_lower(old_base) && !above_upper(old_base)); } del_row(r); TRACE("simplex", display(tout);); SASSERT(well_formed()); } template bool simplex::above_lower(var_t var, eps_numeral const& b) const { var_info const& vi = m_vars[var]; return !vi.m_lower_valid || em.gt(b, vi.m_lower); } template bool simplex::below_upper(var_t var, eps_numeral const& b) const { var_info const& vi = m_vars[var]; return !vi.m_upper_valid || em.lt(b, vi.m_upper); } template void simplex::set_lower(var_t var, eps_numeral const& b) { var_info& vi = m_vars[var]; em.set(vi.m_lower, b); vi.m_lower_valid = true; TRACE("simplex", em.display(tout << "v" << var << " lower: ", b); em.display(tout << " value: ", vi.m_value);); SASSERT(!vi.m_upper_valid || em.le(b, vi.m_upper)); if (!vi.m_is_base && em.lt(vi.m_value, b)) { scoped_eps_numeral delta(em); em.sub(b, vi.m_value, delta); update_value(var, delta); } else if (vi.m_is_base && em.lt(vi.m_value, b)) { SASSERT(outside_bounds(var)); add_patch(var); } SASSERT(well_formed()); } template void simplex::set_upper(var_t var, eps_numeral const& b) { var_info& vi = m_vars[var]; em.set(vi.m_upper, b); vi.m_upper_valid = true; SASSERT(!vi.m_lower_valid || em.le(vi.m_lower, b)); if (!vi.m_is_base && em.gt(vi.m_value, b)) { scoped_eps_numeral delta(em); em.sub(b, vi.m_value, delta); update_value(var, delta); } else if (vi.m_is_base && em.lt(b, vi.m_value)) { SASSERT(outside_bounds(var)); add_patch(var); } SASSERT(well_formed()); } template void simplex::unset_lower(var_t var) { m_vars[var].m_lower_valid = false; } template void simplex::unset_upper(var_t var) { m_vars[var].m_upper_valid = false; } template void simplex::set_value(var_t var, eps_numeral const& b) { scoped_eps_numeral delta(em); em.sub(b, m_vars[var].m_value, delta); update_value(var, delta); SASSERT(well_formed()); } template typename simplex::eps_numeral const& simplex::get_value(var_t v) { return m_vars[v].m_value; } template void simplex::display(std::ostream& out) const { M.display(out); for (unsigned i = 0; i < m_vars.size(); ++i) { var_info const& vi = m_vars[i]; out << "v" << i << " "; out << em.to_string(vi.m_value); out << " ["; if (vi.m_lower_valid) out << em.to_string(vi.m_lower); else out << "-oo"; out << ":"; if (vi.m_upper_valid) out << em.to_string(vi.m_upper); else out << "oo"; out << "] "; if (vi.m_is_base) out << "b:" << vi.m_base2row << " "; //col_iterator it = M.col_begin(i), end = M.col_end(i); //for (; it != end; ++it) { // out << "r" << it.get_row().id() << " "; //} out << "\n"; } } template void simplex::display_row(std::ostream& out, row const& r, bool values) { row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { m.display(out, it->m_coeff); out << "*v" << it->m_var << " "; if (values) { var_info const& vi = m_vars[it->m_var]; out << em.to_string(vi.m_value); out << " ["; if (vi.m_lower_valid) out << em.to_string(vi.m_lower); else out << "-oo"; out << ":"; if (vi.m_upper_valid) out << em.to_string(vi.m_upper); else out << "oo"; out << "] "; } } out << "\n"; } template void simplex::ensure_var(var_t v) { while (v >= m_vars.size()) { M.ensure_var(m_vars.size()); m_vars.push_back(var_info()); } if (m_to_patch.get_bounds() <= v) { m_to_patch.set_bounds(2*v+1); } } template void simplex::reset() { M.reset(); m_to_patch.reset(); m_vars.reset(); m_row2base.reset(); m_left_basis.reset(); m_base_vars.reset(); } template lbool simplex::make_feasible() { ++m_stats.m_num_checks; m_left_basis.reset(); m_infeasible_var = null_var; unsigned num_iterations = 0; unsigned num_repeated = 0; var_t v = null_var; m_bland = false; SASSERT(well_formed()); while ((v = select_var_to_fix()) != null_var) { TRACE("simplex", display(tout << "v" << v << "\n");); if (m_cancel || num_iterations > m_max_iterations) { return l_undef; } check_blands_rule(v, num_repeated); if (!make_var_feasible(v)) { m_to_patch.insert(v); m_infeasible_var = v; ++m_stats.m_num_infeasible; return l_false; } ++num_iterations; } SASSERT(well_formed()); return l_true; } /** \brief Make x_j the new base variable for row of x_i. x_i is assumed base variable of row r_i. x_j is assumed to have coefficient a_ij in r_i. a_ii*x_i + a_ij*x_j + r_i = 0 current value of x_i is v_i new value of x_i is new_value a_ii*(x_i + new_value - x_i) + a_ij*((x_i - new_value)*a_ii/a_ij + x_j) + r_i = 0 Let r_k be a row where x_j has coefficient x_kj != 0. r_k <- r_k * a_ij - r_i * a_kj */ template void simplex::update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value) { SASSERT(is_base(x_i)); SASSERT(!is_base(x_j)); var_info& x_iI = m_vars[x_i]; scoped_eps_numeral theta(em); theta = x_iI.m_value; theta -= new_value; numeral const& a_ii = x_iI.m_base_coeff; em.mul(theta, a_ii, theta); em.div(theta, a_ij, theta); update_value(x_j, theta); SASSERT(em.eq(x_iI.m_value, new_value)); pivot(x_i, x_j, a_ij); } template void simplex::pivot(var_t x_i, var_t x_j, numeral const& a_ij) { ++m_stats.m_num_pivots; var_info& x_iI = m_vars[x_i]; var_info& x_jI = m_vars[x_j]; unsigned r_i = x_iI.m_base2row; m_row2base[r_i] = x_j; x_jI.m_base2row = r_i; m.set(x_jI.m_base_coeff, a_ij); x_jI.m_is_base = true; x_iI.m_is_base = false; add_patch(x_j); SASSERT(well_formed_row(row(r_i))); col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); scoped_numeral a_kj(m), g(m); for (; it != end; ++it) { row r_k = it.get_row(); if (r_k.id() != r_i) { a_kj = it.get_row_entry().m_coeff; a_kj.neg(); M.mul(r_k, a_ij); M.add(r_k, a_kj, row(r_i)); var_t s = m_row2base[r_k.id()]; numeral& coeff = m_vars[s].m_base_coeff; m.mul(coeff, a_ij, coeff); M.gcd_normalize(r_k, g); if (!m.is_one(g)) { m.div(coeff, g, coeff); } SASSERT(well_formed_row(row(r_k))); } } SASSERT(well_formed()); } template void simplex::update_value(var_t v, eps_numeral const& delta) { if (em.is_zero(delta)) { return; } update_value_core(v, delta); col_iterator it = M.col_begin(v), end = M.col_end(v); // v <- v + delta // s*s_coeff + v*v_coeff + R = 0 // -> // (v + delta)*v_coeff + (s - delta*v_coeff/s_coeff)*v + R = 0 for (; it != end; ++it) { row r = it.get_row(); var_t s = m_row2base[r.id()]; var_info& si = m_vars[s]; scoped_eps_numeral delta2(em); numeral const& coeff = it.get_row_entry().m_coeff; em.mul(delta, coeff, delta2); em.div(delta2, si.m_base_coeff, delta2); delta2.neg(); update_value_core(s, delta2); } } template void simplex::update_value_core(var_t v, eps_numeral const& delta) { eps_numeral& val = m_vars[v].m_value; em.add(val, delta, val); if (is_base(v)) { add_patch(v); } } template bool simplex::below_lower(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_lower_valid && em.lt(vi.m_value, vi.m_lower); } template bool simplex::above_upper(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_upper_valid && em.gt(vi.m_value, vi.m_upper); } template bool simplex::above_lower(var_t v) const { var_info const& vi = m_vars[v]; return !vi.m_lower_valid || em.gt(vi.m_value, vi.m_lower); } template bool simplex::below_upper(var_t v) const { var_info const& vi = m_vars[v]; return !vi.m_upper_valid || em.lt(vi.m_value, vi.m_upper); } template bool simplex::at_lower(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_lower_valid && em.eq(vi.m_value, vi.m_lower); } template bool simplex::at_upper(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_upper_valid && em.eq(vi.m_value, vi.m_upper); } template bool simplex::make_var_feasible(var_t x_i) { scoped_numeral a_ij(m); scoped_eps_numeral value(em); bool is_below; if (below_lower(x_i)) { SASSERT(is_base(x_i)); is_below = m.is_pos(m_vars[x_i].m_base_coeff); value = m_vars[x_i].m_lower; } else if (above_upper(x_i)) { SASSERT(is_base(x_i)); is_below = m.is_neg(m_vars[x_i].m_base_coeff); value = m_vars[x_i].m_upper; } else { // x_i is already feasible return true; } var_t x_j = select_pivot(x_i, is_below, a_ij); if (x_j != null_var) { update_and_pivot(x_i, x_j, a_ij, value); } return x_j != null_var; } /** \brief Wrapper for select_pivot_blands and select_pivot_core */ template typename simplex::var_t simplex::select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij) { if (m_bland) { return select_pivot_blands(x_i, is_below, out_a_ij); } return select_pivot_core(x_i, is_below, out_a_ij); } /** \brief Select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. The argument is_below is true (false) if x_i is below its lower bound (above its upper bound). */ template typename simplex::var_t simplex::select_pivot_core(var_t x_i, bool is_below, scoped_numeral & out_a_ij) { SASSERT(is_base(x_i)); var_t max = get_num_vars(); var_t result = max; row r = row(m_vars[x_i].m_base2row); int n; unsigned best_col_sz = UINT_MAX; int best_so_far = INT_MAX; row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { var_t x_j = it->m_var; if (x_i == x_j) continue; numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? m.is_neg(a_ij) : m.is_pos(a_ij); bool is_pos = !is_neg; bool can_pivot = ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j))); if (can_pivot) { int num = get_num_non_free_dep_vars(x_j, best_so_far); unsigned col_sz = M.column_size(x_j); if (num < best_so_far || (num == best_so_far && col_sz < best_col_sz)) { result = x_j; out_a_ij = a_ij; best_so_far = num; best_col_sz = col_sz; n = 1; } else if (num == best_so_far && col_sz == best_col_sz) { n++; if (m_random()%n == 0) { result = x_j; out_a_ij = a_ij; } } } } return result < max ? result : null_var; } /** \brief Return the number of base variables that are non free and are v dependent. The function adds 1 to the result if v is non free. The function returns with a partial result r if r > best_so_far. This function is used to select the pivot variable. */ template int simplex::get_num_non_free_dep_vars(var_t x_j, int best_so_far) { int result = is_non_free(x_j); col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); for (; it != end; ++it) { var_t s = m_row2base[it.get_row().id()]; result += is_non_free(s); if (result > best_so_far) return result; } return result; } /** \brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. */ template typename simplex::var_t simplex::select_pivot_blands(var_t x_i, bool is_below, scoped_numeral & out_a_ij) { SASSERT(is_base(x_i)); unsigned max = get_num_vars(); var_t result = max; row r(m_vars[x_i].m_base2row); row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { var_t x_j = it->m_var; numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? m.is_neg(a_ij) : m.is_pos(a_ij); if (x_i != x_j && ((!is_neg && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { SASSERT(!is_base(x_j)); if (x_j < result) { result = x_j; out_a_ij = a_ij; } } } return result < max ? result : null_var; } template lbool simplex::minimize(var_t v) { // minimize v, such that tableau is feasible. // Assume there are no bounds on v. // Let k*v + c*x = 0 e.g, maximize c*x over // tableau constraints: // // max { c*x | A*x = 0 and l <= x <= u } // // start with feasible assigment // A*x0 = 0 and l <= x0 <= u // // Identify pivot: i, j: such that x_i is base, // there is a row k1*x_i + k2*x_j + R = 0 // and a delta such that: // // x_i' <- x_i + delta // x_j' <- x_j - delta*k1/k2 // l_i <= x_i' <= u_i // l_j <= x_j' <= u_j // and c*x' > c*x // e.g., c*x := c_i*x_i + c_j*x_j + ... // and c_i*delta > c_j*delta*k1/k2 // and x_i < u_i (if delta > 0), l_i < x_i (if delta < 0) // and l_j < x_j (if delta > 0), x_j < u_j (if delta < 0) // // update all rows, including c*x, using the pivot. // // If there is c_i*x_i in c*x such that c_i > 0 // and upper_i = oo and complementary lower_j = -oo // then the objective is unbounded. // // There is a singularity if there is a pivot such that // c_i*delta == c_j*delta*k1/k2, e.g., nothing is improved, // pivot, but use bland's rule to ensure // convergence in the limit. // SASSERT(is_feasible()); scoped_eps_numeral delta(em); scoped_numeral a_ij(m); var_t x_i, x_j; bool inc_x_i, inc_x_j; while (true) { if (m_cancel) { return l_undef; } select_pivot_primal(v, x_i, x_j, a_ij, inc_x_i, inc_x_j); if (x_j == null_var) { // optimal return l_true; } TRACE("simplex", tout << "x_i: v" << x_i << " x_j: v" << x_j << "\n";); var_info& vj = m_vars[x_j]; if (x_i == null_var) { if (inc_x_j && vj.m_upper_valid) { delta = vj.m_upper; delta -= vj.m_value; update_value(x_j, delta); } else if (!inc_x_j && vj.m_lower_valid) { delta = vj.m_lower; delta -= vj.m_value; update_value(x_j, delta); } else { // unbounded return l_false; } continue; } // TBD: Change the value of x_j directly without pivoting: // // if (!vj.is_fixed() && vj.bounded() && gain >= upper - lower) { // // } // pivot(x_i, x_j, a_ij); TRACE("simplex", display(tout << "after pivot\n");); move_to_bound(x_i, !inc_x_i); SASSERT(well_formed_row(row(m_vars[x_j].m_base2row))); TRACE("simplex", display(tout);); SASSERT(is_feasible()); } return l_true; } template void simplex::move_to_bound(var_t x, bool to_lower) { scoped_eps_numeral delta(em), delta2(em); var_info& vi = m_vars[x]; if (to_lower) { em.sub(vi.m_value, vi.m_lower, delta); } else { em.sub(vi.m_upper, vi.m_value, delta); } TRACE("simplex", tout << "move " << (to_lower?"to_lower":"to_upper") << " v" << x << " delta: " << em.to_string(delta) << "\n";); col_iterator it = M.col_begin(x), end = M.col_end(x); for (; it != end && is_pos(delta); ++it) { // // base_coeff*s + coeff*x + R = 0 // // to_lower coeff > 0 base_coeff > 0 bound(s) // ------------------------------------------------------ // T T T !to_lower // T T F to_lower // T F T to_lower // T F F !to_lower // var_t s = m_row2base[it.get_row().id()]; var_info& vs = m_vars[s]; numeral const& coeff = it.get_row_entry().m_coeff; numeral const& base_coeff = vs.m_base_coeff; SASSERT(!m.is_zero(coeff)); bool base_to_lower = (m.is_pos(coeff) != m.is_pos(base_coeff)) == to_lower; eps_numeral const* bound = 0; if (!base_to_lower && vs.m_upper_valid) { bound = &vs.m_upper; } else if (base_to_lower && vs.m_lower_valid) { bound = &vs.m_lower; } if (bound) { // |delta2*coeff| = |(bound-value)*base_coeff| em.sub(*bound, vs.m_value, delta2); em.mul(delta2, base_coeff, delta2); em.div(delta2, coeff, delta2); em.abs(delta2); TRACE("simplex", tout << "Delta for v" << s << " " << delta2 << "\n";); if (delta2 < delta) { delta = delta2; } } } if (to_lower) { delta.neg(); } update_value(x, delta); } /** \brief Arguments: v - base variable of row(v) to optimize x_i - base variable of row(x_i) to become non-base x_j - variable in row(v) to make a base variable a_ij - coefficient to x_j in row(x_i) inc - whether to increment x_i */ template void simplex::select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc_x_i, bool& inc_x_j) { row r(m_vars[v].m_base2row); row_iterator it = M.row_begin(r), end = M.row_end(r); scoped_eps_numeral gain(em), new_gain(em); scoped_numeral new_a_ij(m); x_i = null_var; x_j = null_var; inc_x_i = false; bool inc_y = false; for (; it != end; ++it) { var_t x = it->m_var; if (x == v) continue; bool inc_x = m.is_pos(it->m_coeff) == m.is_pos(m_vars[v].m_base_coeff); if ((inc_x && at_upper(x)) || (!inc_x && at_lower(x))) { TRACE("simplex", tout << "v" << x << " pos: " << inc_x << " at upper: " << at_upper(x) << " at lower: " << at_lower(x) << "\n";); continue; // variable cannot be used for improving bounds. // TBD check? } var_t y = pick_var_to_leave(x, inc_x, new_gain, new_a_ij, inc_y); if (y == null_var) { // unbounded. x_i = y; x_j = x; inc_x_i = inc_y; inc_x_j = inc_x; a_ij = new_a_ij; break; } bool better = (new_gain > gain) || ((is_zero(new_gain) && is_zero(gain) && (x_i == null_var || y < x_i))); if (better) { TRACE("simplex", em.display(tout << "gain:", gain); em.display(tout << " new gain:", new_gain); tout << " base x_i: " << y << ", new base x_j: " << x << ", inc x_j: " << inc_x << "\n";); x_i = y; x_j = x; inc_x_i = inc_y; inc_x_j = inc_x; gain = new_gain; a_ij = new_a_ij; } } } // // y is a base variable. // v is a base variable. // v*a_v + x*a_x + E = 0 // y*b_y + x*b_x + F = 0 // inc(x) := sign(a_v) == sign(a_x) // sign_eq := sign(b_y) == sign(b_x) // sign_eq => (inc(x) != inc(y)) // !sign_eq => (inc(x) = inc(y)) // -> // inc(y) := sign_eq != inc(x) // template typename simplex::var_t simplex::pick_var_to_leave( var_t x_j, bool inc_x_j, scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc_x_i) { var_t x_i = null_var; gain.reset(); scoped_eps_numeral curr_gain(em); col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); for (; it != end; ++it) { row r = it.get_row(); var_t s = m_row2base[r.id()]; var_info& vi = m_vars[s]; numeral const& a_ij = it.get_row_entry().m_coeff; numeral const& a_ii = vi.m_base_coeff; bool sign_eq = (m.is_pos(a_ii) == m.is_pos(a_ij)); bool inc_s = sign_eq != inc_x_j; TRACE("simplex", tout << "x_j: v" << x_j << ", base x_i: v" << s << ", inc_x_i: " << inc_s << ", inc_x_j: " << inc_x_j << ", upper valid:" << vi.m_upper_valid << ", lower valid:" << vi.m_lower_valid << "\n"; display_row(tout, r);); if ((inc_s && !vi.m_upper_valid) || (!inc_s && !vi.m_lower_valid)) { continue; } // // current gain: (value(x_i)-bound)*a_ii/a_ij // curr_gain = vi.m_value; curr_gain -= inc_s?vi.m_upper:vi.m_lower; em.mul(curr_gain, a_ii, curr_gain); em.div(curr_gain, a_ij, curr_gain); if (is_neg(curr_gain)) { curr_gain.neg(); } if (x_i == null_var || (curr_gain < gain) || (is_zero(gain) && is_zero(curr_gain) && s < x_i)) { x_i = s; gain = curr_gain; new_a_ij = a_ij; inc_x_i = inc_s; TRACE("simplex", tout << "x_j v" << x_j << " x_i v" << x_i << " gain: "; tout << curr_gain << "\n";); } } return x_i; } template void simplex::check_blands_rule(var_t v, unsigned& num_repeated) { if (m_bland) return; if (m_left_basis.contains(v)) { num_repeated++; if (num_repeated > m_blands_rule_threshold) { TRACE("simplex", tout << "using blands rule, " << num_repeated << "\n";); // std::cerr << "BLANDS RULE...\n"; m_bland = true; } } else { m_left_basis.insert(v); } } template typename simplex::pivot_strategy_t simplex::pivot_strategy() { if (m_bland) { return S_BLAND; } return S_DEFAULT; } template typename simplex::var_t simplex::select_var_to_fix() { switch (pivot_strategy()) { case S_BLAND: return select_smallest_var(); case S_GREATEST_ERROR: return select_error_var(false); case S_LEAST_ERROR: return select_error_var(true); default: return select_smallest_var(); } } template typename simplex::var_t simplex::select_error_var(bool least) { var_t best = null_var; scoped_eps_numeral best_error(em); scoped_eps_numeral curr_error(em); typename var_heap::iterator it = m_to_patch.begin(); typename var_heap::iterator end = m_to_patch.end(); for (; it != end; ++it) { var_t v = *it; var_info const& vi = m_vars[v]; if (below_lower(v)) em.sub(vi.m_lower, vi.m_value, curr_error); else if (above_upper(v)) em.sub(vi.m_value, vi.m_lower, curr_error); else continue; SASSERT(is_pos(curr_error)); if ((best == null_var) || (!least && curr_error > best_error) || (least && curr_error < best_error)) { best = v; best_error = curr_error; } } if (best == null_var) m_to_patch.clear(); // all variables are satisfied else m_to_patch.erase(best); return best; } template bool simplex::well_formed() const { SASSERT(M.well_formed()); for (unsigned i = 0; i < m_row2base.size(); ++i) { var_t s = m_row2base[i]; if (s == null_var) continue; SASSERT(i == m_vars[s].m_base2row); VERIFY(well_formed_row(row(i))); } for (unsigned i = 0; i < m_vars.size(); ++i) { if (!is_base(i)) { SASSERT(!above_upper(i)); SASSERT(!below_lower(i)); } } return true; } template bool simplex::is_feasible() const { for (unsigned i = 0; i < m_vars.size(); ++i) { if (below_lower(i) || above_upper(i)) return false; } return true; } template bool simplex::well_formed_row(row const& r) const { var_t s = m_row2base[r.id()]; SASSERT(m_vars[s].m_base2row == r.id()); SASSERT(m_vars[s].m_is_base); // SASSERT(m.is_neg(m_vars[s].m_base_coeff)); row_iterator it = M.row_begin(r), end = M.row_end(r); scoped_eps_numeral sum(em), tmp(em); for (; it != end; ++it) { em.mul(m_vars[it->m_var].m_value, it->m_coeff, tmp); sum += tmp; SASSERT(s != it->m_var || m.eq(m_vars[s].m_base_coeff, it->m_coeff)); } if (!em.is_zero(sum)) { IF_VERBOSE(0, M.display_row(verbose_stream(), r);); TRACE("pb", display(tout << "non-well formed row\n"); M.display_row(tout << "row: ", r);); throw default_exception("non-well formed row"); } return true; } template void simplex::collect_statistics(::statistics & st) const { M.collect_statistics(st); st.update("simplex num pivots", m_stats.m_num_pivots); st.update("simplex num infeasible", m_stats.m_num_infeasible); st.update("simplex num checks", m_stats.m_num_checks); } }; #endif z3-z3-4.4.1/src/math/simplex/sparse_matrix.h000066400000000000000000000226011260446376700206610ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sparse_matrix.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: --*/ #ifndef SPARSE_MATRIX_H_ #define SPARSE_MATRIX_H_ #include "mpq_inf.h" #include "statistics.h" namespace simplex { template class sparse_matrix { public: typedef typename Ext::numeral numeral; typedef typename Ext::scoped_numeral scoped_numeral; typedef typename Ext::manager manager; typedef unsigned var_t; struct row_entry { numeral m_coeff; var_t m_var; row_entry(numeral const& c, var_t v): m_coeff(c), m_var(v) {} }; private: struct stats { unsigned m_add_rows; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; static const unsigned dead_id = UINT_MAX; /** \brief A row_entry is: m_var*m_coeff m_col_idx points to the place in the column where the variable occurs. */ struct _row_entry : public row_entry { union { int m_col_idx; int m_next_free_row_entry_idx; }; _row_entry(numeral const & c, var_t v): row_entry(c, v), m_col_idx(0) {} _row_entry() : row_entry(numeral(), dead_id), m_col_idx(0) {} bool is_dead() const { return row_entry::m_var == dead_id; } }; /** \brief A column entry points to the row and the row_entry within the row that has a non-zero coefficient on the variable associated with the column entry. */ struct col_entry { int m_row_id; union { int m_row_idx; int m_next_free_col_entry_idx; }; col_entry(int r, int i): m_row_id(r), m_row_idx(i) {} col_entry(): m_row_id(0), m_row_idx(0) {} bool is_dead() const { return (unsigned) m_row_id == dead_id; } }; struct column; /** \brief A row contains a base variable and set of row_entries. The base variable must occur in the set of row_entries with coefficient 1. */ struct _row { vector<_row_entry> m_entries; unsigned m_size; // the real size, m_entries contains dead row_entries. int m_first_free_idx; // first available position. _row(); unsigned size() const { return m_size; } unsigned num_entries() const { return m_entries.size(); } void reset(manager& m); _row_entry & add_row_entry(unsigned & pos_idx); void del_row_entry(unsigned idx); void compress(manager& m, vector & cols); void compress_if_needed(manager& _m, vector & cols); void save_var_pos(svector & result_map, unsigned_vector& idxs) const; //bool is_coeff_of(var_t v, numeral const & expected) const; int get_idx_of(var_t v) const; }; /** \brief A column stores in which rows a variable occurs. The column may have free/dead entries. The field m_first_free_idx is a reference to the first free/dead entry. */ struct column { svector m_entries; unsigned m_size; int m_first_free_idx; mutable unsigned m_refs; column():m_size(0), m_first_free_idx(-1), m_refs(0) {} unsigned size() const { return m_size; } unsigned num_entries() const { return m_entries.size(); } void reset(); void compress(vector<_row> & rows); void compress_if_needed(vector<_row> & rows); //void compress_singleton(vector<_row> & rows, unsigned singleton_pos); col_entry const * get_first_col_entry() const; col_entry & add_col_entry(int & pos_idx); void del_col_entry(unsigned idx); }; manager& m; vector<_row> m_rows; svector m_dead_rows; // rows to recycle vector m_columns; // per var svector m_var_pos; // temporary map from variables to positions in row unsigned_vector m_var_pos_idx; // indices in m_var_pos stats m_stats; bool well_formed_row(unsigned row_id) const; bool well_formed_column(unsigned column_id) const; void del_row_entry(_row& r, unsigned pos); public: sparse_matrix(manager& _m): m(_m) {} ~sparse_matrix(); void reset(); class row { unsigned m_id; public: explicit row(unsigned r):m_id(r) {} row():m_id(UINT_MAX) {} bool operator!=(row const& other) const { return m_id != other.m_id; } unsigned id() const { return m_id; } }; void ensure_var(var_t v); row mk_row(); void add_var(row r, numeral const& n, var_t var); void add(row r, numeral const& n, row src); void mul(row r, numeral const& n); void neg(row r); void del(row r); void gcd_normalize(row const& r, scoped_numeral& g); class row_iterator { friend class sparse_matrix; unsigned m_curr; _row & m_row; void move_to_used() { while (m_curr < m_row.num_entries() && m_row.m_entries[m_curr].is_dead()) { ++m_curr; } } row_iterator(_row & r, bool begin): m_curr(0), m_row(r) { if (begin) { move_to_used(); } else { m_curr = m_row.num_entries(); } } public: row_entry & operator*() const { return m_row.m_entries[m_curr]; } row_entry * operator->() const { return &(operator*()); } row_iterator & operator++() { ++m_curr; move_to_used(); return *this; } row_iterator operator++(int) { row_iterator tmp = *this; ++*this; return tmp; } bool operator==(row_iterator const & it) const { return m_curr == it.m_curr; } bool operator!=(row_iterator const & it) const { return m_curr != it.m_curr; } }; row_iterator row_begin(row const& r) { return row_iterator(m_rows[r.id()], true); } row_iterator row_end(row const& r) { return row_iterator(m_rows[r.id()], false); } unsigned column_size(var_t v) const { return m_columns[v].size(); } class col_iterator { friend class sparse_matrix; unsigned m_curr; column const& m_col; vector<_row> const& m_rows; void move_to_used() { while (m_curr < m_col.num_entries() && m_col.m_entries[m_curr].is_dead()) { ++m_curr; } } col_iterator(column const& c, vector<_row> const& r, bool begin): m_curr(0), m_col(c), m_rows(r) { ++m_col.m_refs; if (begin) { move_to_used(); } else { m_curr = m_col.num_entries(); } } public: ~col_iterator() { --m_col.m_refs; } row get_row() { return row(m_col.m_entries[m_curr].m_row_id); } row_entry const& get_row_entry() { col_entry const& c = m_col.m_entries[m_curr]; int row_id = c.m_row_id; return m_rows[row_id].m_entries[c.m_row_idx]; } col_iterator & operator++() { ++m_curr; move_to_used(); return *this; } col_iterator operator++(int) { col_iterator tmp = *this; ++*this; return tmp; } bool operator==(col_iterator const & it) const { return m_curr == it.m_curr; } bool operator!=(col_iterator const & it) const { return m_curr != it.m_curr; } }; col_iterator col_begin(int v) const { return col_iterator(m_columns[v], m_rows, true); } col_iterator col_end(int v) const { return col_iterator(m_columns[v], m_rows, false); } void display(std::ostream& out); void display_row(std::ostream& out, row const& r); bool well_formed() const; void collect_statistics(::statistics & st) const; }; struct mpz_ext { typedef mpz numeral; typedef scoped_mpz scoped_numeral; typedef unsynch_mpz_manager manager; typedef mpq_inf eps_numeral; typedef unsynch_mpq_inf_manager eps_manager; }; struct mpq_ext { typedef mpq numeral; typedef scoped_mpq scoped_numeral; typedef unsynch_mpq_manager manager; typedef mpq_inf eps_numeral; typedef unsynch_mpq_inf_manager eps_manager; }; }; #endif z3-z3-4.4.1/src/math/simplex/sparse_matrix_def.h000066400000000000000000000451101260446376700214770ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sparse_matrix_def.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: mainly hoisted from theory_arith.h and theory_arith_aux.h --*/ #ifndef SPARSE_MATRIX_DEF_H_ #define SPARSE_MATRIX_DEF_H_ #include "sparse_matrix.h" #include "uint_set.h" namespace simplex { // ----------------------------------- // // Rows // // ----------------------------------- template sparse_matrix::_row::_row(): m_size(0), m_first_free_idx(-1) { } template void sparse_matrix::_row::reset(manager& m) { for (unsigned i = 0; i < m_entries.size(); ++i) { m.reset(m_entries[i].m_coeff); } m_entries.reset(); m_size = 0; m_first_free_idx = -1; } /** \brief Add a new row_entry. The result is a reference to the new row_entry. The position of the new row_entry in the row is stored in pos_idx. */ template typename sparse_matrix::_row_entry & sparse_matrix::_row::add_row_entry(unsigned & pos_idx) { m_size++; if (m_first_free_idx == -1) { pos_idx = m_entries.size(); m_entries.push_back(_row_entry()); return m_entries.back(); } else { SASSERT(m_first_free_idx >= 0); pos_idx = static_cast(m_first_free_idx); _row_entry & result = m_entries[pos_idx]; SASSERT(result.is_dead()); m_first_free_idx = result.m_next_free_row_entry_idx; return result; } } /** \brief Delete row_entry at position idx. */ template void sparse_matrix::_row::del_row_entry(unsigned idx) { _row_entry & t = m_entries[idx]; SASSERT(!t.is_dead()); t.m_next_free_row_entry_idx = (unsigned)m_first_free_idx; t.m_var = dead_id; m_size--; m_first_free_idx = idx; SASSERT(t.is_dead()); } /** \brief Remove holes (i.e., dead entries) from the row. */ template void sparse_matrix::_row::compress(manager& m, vector & cols) { unsigned i = 0; unsigned j = 0; unsigned sz = m_entries.size(); for (; i < sz; i++) { _row_entry & t1 = m_entries[i]; if (!t1.is_dead()) { if (i != j) { _row_entry & t2 = m_entries[j]; t2.m_coeff.swap(t1.m_coeff); t2.m_var = t1.m_var; t2.m_col_idx = t1.m_col_idx; SASSERT(!t2.is_dead()); column & col = cols[t2.m_var]; col.m_entries[t2.m_col_idx].m_row_idx = j; } j++; } } SASSERT(j == m_size); // // alternative: update the free-list to point to the // tail and avoid shrinking. // if m.does not allocate memory (for wrapper around // double), also bypass this step. // for (unsigned i = m_size; i < m_entries.size(); ++i) { m.reset(m_entries[i].m_coeff); } m_entries.shrink(m_size); m_first_free_idx = -1; } template void sparse_matrix::_row::compress_if_needed(manager& m, vector & cols) { if (size() *2 < num_entries()) { compress(m, cols); } } /** \brief Fill the map var -> pos/idx */ template inline void sparse_matrix::_row::save_var_pos(svector & result_map, unsigned_vector& idxs) const { typename vector<_row_entry>::const_iterator it = m_entries.begin(); typename vector<_row_entry>::const_iterator end = m_entries.end(); unsigned idx = 0; for (; it != end; ++it, ++idx) { if (!it->is_dead()) { result_map[it->m_var] = idx; idxs.push_back(it->m_var); } } } template int sparse_matrix::_row::get_idx_of(var_t v) const { typename vector<_row_entry>::const_iterator it = m_entries.begin(); typename vector<_row_entry>::const_iterator end = m_entries.end(); for (unsigned idx = 0; it != end; ++it, ++idx) { if (!it->is_dead() && it->m_var == v) return idx; } return -1; } // ----------------------------------- // // Columns // // ----------------------------------- template void sparse_matrix::column::reset() { m_entries.reset(); m_size = 0; m_first_free_idx = -1; } /** \brief Remove holes (i.e., dead entries) from the column. */ template void sparse_matrix::column::compress(vector<_row> & rows) { unsigned i = 0; unsigned j = 0; unsigned sz = m_entries.size(); for (; i < sz; i++) { col_entry & e1 = m_entries[i]; if (!e1.is_dead()) { if (i != j) { m_entries[j] = e1; _row & r = rows[e1.m_row_id]; r.m_entries[e1.m_row_idx].m_col_idx = j; } j++; } } SASSERT(j == m_size); m_entries.shrink(m_size); m_first_free_idx = -1; } /** \brief Invoke compress if the column contains too many holes (i.e., dead entries). */ template inline void sparse_matrix::column::compress_if_needed(vector<_row> & rows) { if (size() * 2 < num_entries() && m_refs == 0) { compress(rows); } } #if 0 /** \brief Special version of compress, that is used when the column contain only one entry located at position singleton_pos. */ template void sparse_matrix::column::compress_singleton(vector<_row> & rows, unsigned singleton_pos) { SASSERT(m_size == 1); if (singleton_pos != 0) { col_entry & s = m_entries[singleton_pos]; m_entries[0] = s; row & r = rows[s.m_row_id]; r[s.m_row_idx].m_col_idx = 0; } m_first_free_idx = -1; m_entries.shrink(1); } #endif template const typename sparse_matrix::col_entry * sparse_matrix::column::get_first_col_entry() const { typename svector::const_iterator it = m_entries.begin(); typename svector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead()) { return it; } } return 0; } template typename sparse_matrix::col_entry & sparse_matrix::column::add_col_entry(int & pos_idx) { m_size++; if (m_first_free_idx == -1) { pos_idx = m_entries.size(); m_entries.push_back(col_entry()); return m_entries.back(); } else { pos_idx = m_first_free_idx; col_entry & result = m_entries[pos_idx]; SASSERT(result.is_dead()); m_first_free_idx = result.m_next_free_col_entry_idx; return result; } } template void sparse_matrix::column::del_col_entry(unsigned idx) { col_entry & c = m_entries[idx]; SASSERT(!c.is_dead()); c.m_row_id = dead_id; c.m_next_free_col_entry_idx = m_first_free_idx; m_first_free_idx = idx; m_size--; } // ----------------------------------- // // Matrix // // ----------------------------------- template sparse_matrix::~sparse_matrix() { for (unsigned i = 0; i < m_rows.size(); ++i) { _row& r = m_rows[i]; for (unsigned j = 0; j < r.m_entries.size(); ++j) { m.reset(r.m_entries[j].m_coeff); } } } template void sparse_matrix::reset() { m_rows.reset(); m_dead_rows.reset(); m_columns.reset(); m_var_pos.reset(); m_var_pos_idx.reset(); } template void sparse_matrix::ensure_var(var_t v) { while (m_columns.size() <= v) { m_columns.push_back(column()); m_var_pos.push_back(-1); } } template typename sparse_matrix::row sparse_matrix::mk_row() { if (m_dead_rows.empty()) { row r(m_rows.size()); m_rows.push_back(_row()); return r; } else { row r(m_dead_rows.back()); m_dead_rows.pop_back(); return r; } } template void sparse_matrix::add_var(row dst, numeral const& n, var_t v) { _row& r = m_rows[dst.id()]; column& c = m_columns[v]; unsigned r_idx; int c_idx; _row_entry & r_entry = r.add_row_entry(r_idx); col_entry& c_entry = c.add_col_entry(c_idx); r_entry.m_var = v; m.set(r_entry.m_coeff, n); r_entry.m_col_idx = c_idx; c_entry.m_row_id = dst.id(); c_entry.m_row_idx = r_idx; } /** \brief Set row1 <- row1 + row2 * n */ template void sparse_matrix::add(row row1, numeral const& n, row row2) { m_stats.m_add_rows++; _row & r1 = m_rows[row1.id()]; r1.save_var_pos(m_var_pos, m_var_pos_idx); // // loop over variables in row2, // add terms in row2 to row1. // #define ADD_ROW(_SET_COEFF_, _ADD_COEFF_) \ row_iterator it = row_begin(row2); \ row_iterator end = row_end(row2); \ for (; it != end; ++it) { \ var_t v = it->m_var; \ int pos = m_var_pos[v]; \ if (pos == -1) { \ /* variable v is not in row1 */ \ unsigned row_idx; \ _row_entry & r_entry = r1.add_row_entry(row_idx); \ r_entry.m_var = v; \ m.set(r_entry.m_coeff, it->m_coeff); \ _SET_COEFF_; \ column & c = m_columns[v]; \ int col_idx; \ col_entry & c_entry = c.add_col_entry(col_idx); \ r_entry.m_col_idx = col_idx; \ c_entry.m_row_id = row1.id(); \ c_entry.m_row_idx = row_idx; \ } \ else { \ /* variable v is in row1 */ \ _row_entry & r_entry = r1.m_entries[pos]; \ SASSERT(r_entry.m_var == v); \ _ADD_COEFF_; \ if (m.is_zero(r_entry.m_coeff)) { \ del_row_entry(r1, pos); \ } \ } \ } \ ((void) 0) if (m.is_one(n)) { ADD_ROW({}, m.add(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff)); } else if (m.is_minus_one(n)) { ADD_ROW(m.neg(r_entry.m_coeff), m.sub(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff)); } else { scoped_numeral tmp(m); ADD_ROW(m.mul(r_entry.m_coeff, n, r_entry.m_coeff), m.mul(it->m_coeff, n, tmp); m.add(r_entry.m_coeff, tmp, r_entry.m_coeff)); } // reset m_var_pos: for (unsigned i = 0; i < m_var_pos_idx.size(); ++i) { m_var_pos[m_var_pos_idx[i]] = -1; } m_var_pos_idx.reset(); r1.compress_if_needed(m, m_columns); } template void sparse_matrix::del_row_entry(_row& r, unsigned pos) { _row_entry & r_entry = r.m_entries[pos]; var_t v = r_entry.m_var; int col_idx = r_entry.m_col_idx; r.del_row_entry(pos); column & c = m_columns[v]; c.del_col_entry(col_idx); c.compress_if_needed(m_rows); } /** \brief Set row <- -row */ template void sparse_matrix::neg(row r) { row_iterator it = row_begin(r); row_iterator end = row_end(r); for (; it != end; ++it) { m.neg(it->m_coeff); } } /** \brief Set row <- n*row */ template void sparse_matrix::mul(row r, numeral const& n) { SASSERT(!m.is_zero(n)); if (m.is_one(n)) { // no op } else if (m.is_minus_one(n)) { neg(r); } else { row_iterator it = row_begin(r); row_iterator end = row_end(r); for (; it != end; ++it) { m.mul(it->m_coeff, n, it->m_coeff); } } } /** \brief Delete row. */ template void sparse_matrix::del(row r) { _row& rw = m_rows[r.id()]; for (unsigned i = 0; i < rw.m_entries.size(); ++i) { _row_entry& e = rw.m_entries[i]; if (!e.is_dead()) { del_row_entry(rw, i); } } SASSERT(rw.m_size == 0); m_dead_rows.push_back(r.id()); } /** \brief normalize coefficients by dividing with they coefficients. return the gcd. */ template void sparse_matrix::gcd_normalize(row const& r, scoped_numeral& g) { g.reset(); row_iterator it = row_begin(r), end = row_end(r); for (; it != end && !m.is_one(g); ++it) { if (m.is_zero(g)) g = it->m_coeff; else m.gcd(g, it->m_coeff, g); } if (m.is_zero(g)) { g = numeral(1); } if (!m.is_one(g)) { row_iterator it2 = row_begin(r); for (; it2 != end; ++it2) { m.div(it2->m_coeff, g, it2->m_coeff); } } } /** \brief well_formed check */ template bool sparse_matrix::well_formed() const { for (unsigned i = 0; i < m_rows.size(); ++i) { well_formed_row(i); } for (unsigned i = 0; i < m_columns.size(); ++i) { well_formed_column(i); } return true; } /** \brief well_formed row check */ template bool sparse_matrix::well_formed_row(unsigned row_id) const { uint_set vars, dead; _row const& r = m_rows[row_id]; for (unsigned i = 0; i < r.num_entries(); ++i) { _row_entry const& e = r.m_entries[i]; if (e.is_dead()) { dead.insert(i); continue; } SASSERT(!vars.contains(e.m_var)); SASSERT(!m.is_zero(e.m_coeff)); SASSERT(e.m_var != dead_id); col_entry const& c = m_columns[e.m_var].m_entries[e.m_col_idx]; SASSERT((unsigned)c.m_row_id == row_id); SASSERT((unsigned)c.m_row_idx == i); vars.insert(e.m_var); } int idx = r.m_first_free_idx; while (idx != -1) { SASSERT(dead.contains(idx)); dead.remove(idx); idx = r.m_entries[idx].m_next_free_row_entry_idx; } SASSERT(dead.empty()); return true; } /** \brief well_formed column check */ template bool sparse_matrix::well_formed_column(var_t v) const { uint_set rows, dead; column const& col = m_columns[v]; for (unsigned i = 0; i < col.num_entries(); ++i) { col_entry const& c = col.m_entries[i]; if (c.is_dead()) { dead.insert(i); continue; } SASSERT(!rows.contains(c.m_row_id)); _row const& row = m_rows[c.m_row_id]; _row_entry const& r = row.m_entries[c.m_row_idx]; SASSERT(r.m_var == v); SASSERT((unsigned)r.m_col_idx == i); rows.insert(c.m_row_id); } int idx = col.m_first_free_idx; while (idx != -1) { SASSERT(dead.contains(idx)); dead.remove(idx); idx = col.m_entries[idx].m_next_free_col_entry_idx; } SASSERT(dead.empty()); return true; } /** \brief statistics */ template void sparse_matrix::collect_statistics(::statistics & st) const { st.update("simplex add rows", m_stats.m_add_rows); } /** \brief display method */ template void sparse_matrix::display(std::ostream& out) { for (unsigned i = 0; i < m_rows.size(); ++i) { if (m_rows[i].size() == 0) continue; display_row(out, row(i)); } } template void sparse_matrix::display_row(std::ostream& out, row const& r) { row_iterator it = row_begin(r), end = row_end(r); for (; it != end; ++it) { m.display(out, it->m_coeff); out << "*v" << it->m_var << " "; } out << "\n"; } }; #endif z3-z3-4.4.1/src/math/subpaving/000077500000000000000000000000001260446376700161435ustar00rootroot00000000000000z3-z3-4.4.1/src/math/subpaving/subpaving.cpp000066400000000000000000000254361260446376700206570ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving.cpp Abstract: Subpaving for non-linear arithmetic. This is a wrapper for the different implementations of the subpaving module. This wrapper is the main interface between Z3 other modules and subpaving. Thus, it assumes that polynomials have precise integer coefficients, and bounds are rationals. If a particular implementation uses floats, then internally the bounds are approximated. Author: Leonardo de Moura (leonardo) 2012-08-07. Revision History: --*/ #include"subpaving.h" #include"subpaving_types.h" #include"subpaving_mpq.h" #include"subpaving_mpf.h" #include"subpaving_hwf.h" #include"subpaving_mpff.h" #include"subpaving_mpfx.h" namespace subpaving { template class context_wrapper : public context { protected: CTX m_ctx; public: context_wrapper(typename CTX::numeral_manager & m, params_ref const & p, small_object_allocator * a):m_ctx(m, p, a) {} virtual ~context_wrapper() {} virtual unsigned num_vars() const { return m_ctx.num_vars(); } virtual var mk_var(bool is_int) { return m_ctx.mk_var(is_int); } virtual bool is_int(var x) const { return m_ctx.is_int(x); } virtual var mk_monomial(unsigned sz, power const * pws) { return m_ctx.mk_monomial(sz, pws); } virtual void inc_ref(ineq * a) { m_ctx.inc_ref(reinterpret_cast(a)); } virtual void dec_ref(ineq * a) { m_ctx.dec_ref(reinterpret_cast(a)); } virtual void add_clause(unsigned sz, ineq * const * atoms) { m_ctx.add_clause(sz, reinterpret_cast(atoms)); } virtual void display_constraints(std::ostream & out, bool use_star) const { m_ctx.display_constraints(out, use_star); } virtual void set_cancel(bool f) { m_ctx.set_cancel(f); } virtual void set_display_proc(display_var_proc * p) { m_ctx.set_display_proc(p); } virtual void reset_statistics() { m_ctx.reset_statistics(); } virtual void collect_statistics(statistics & st) const { m_ctx.collect_statistics(st); } virtual void collect_param_descrs(param_descrs & r) { m_ctx.collect_param_descrs(r); } virtual void updt_params(params_ref const & p) { m_ctx.updt_params(p); } virtual void operator()() { m_ctx(); } virtual void display_bounds(std::ostream & out) const { m_ctx.display_bounds(out); } }; class context_mpq_wrapper : public context_wrapper { scoped_mpq m_c; scoped_mpq_vector m_as; public: context_mpq_wrapper(unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a): context_wrapper(m, p, a), m_c(m), m_as(m) {} virtual ~context_mpq_wrapper() {} virtual unsynch_mpq_manager & qm() const { return m_ctx.nm(); } virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { m_ctx.nm().set(m_as[i], as[i]); } m_ctx.nm().set(m_c, c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) { return reinterpret_cast(m_ctx.mk_ineq(x, k, lower, open)); } }; class context_mpf_wrapper : public context_wrapper { unsynch_mpq_manager & m_qm; scoped_mpf m_c; scoped_mpf_vector m_as; scoped_mpq m_q1, m_q2; // Convert the mpz (integer) into a mpf, and throws an exception if the conversion is not precise. void int2mpf(mpz const & a, mpf & o) { m_qm.set(m_q1, a); m_ctx.nm().set(o, m_q1); m_ctx.nm().m().to_rational(o, m_q2); if (!m_qm.eq(m_q1, m_q2)) throw subpaving::exception(); } public: context_mpf_wrapper(f2n & fm, params_ref const & p, small_object_allocator * a): context_wrapper(fm, p, a), m_qm(fm.m().mpq_manager()), m_c(fm.m()), m_as(fm.m()), m_q1(m_qm), m_q2(m_qm) { } virtual ~context_mpf_wrapper() {} virtual unsynch_mpq_manager & qm() const { return m_qm; } virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) { try { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { int2mpf(as[i], m_as[i]); } int2mpf(c, m_c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } catch (f2n::exception) { throw subpaving::exception(); } } virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) { try { f2n & m = m_ctx.nm(); if (lower) m.round_to_minus_inf(); else m.round_to_plus_inf(); m.set(m_c, k); return reinterpret_cast(m_ctx.mk_ineq(x, m_c, lower, open)); } catch (f2n::exception) { throw subpaving::exception(); } } }; class context_hwf_wrapper : public context_wrapper { unsynch_mpq_manager & m_qm; hwf m_c; svector m_as; // Convert the mpz (integer) into a hwf, and throws an exception if the conversion is not precise. void int2hwf(mpz const & a, hwf & o) { if (!m_qm.is_int64(a)) throw subpaving::exception(); int64 val = m_qm.get_int64(a); double dval = static_cast(val); m_ctx.nm().set(o, dval); double _dval = m_ctx.nm().m().to_double(o); // TODO check the following test if (static_cast(_dval) != val) throw subpaving::exception(); } public: context_hwf_wrapper(f2n & fm, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): context_wrapper(fm, p, a), m_qm(qm) { } virtual ~context_hwf_wrapper() {} virtual unsynch_mpq_manager & qm() const { return m_qm; } virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) { try { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { int2hwf(as[i], m_as[i]); } int2hwf(c, m_c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } catch (f2n::exception) { throw subpaving::exception(); } } virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) { try { f2n & m = m_ctx.nm(); if (lower) m.round_to_minus_inf(); else m.round_to_plus_inf(); m.set(m_c, k); return reinterpret_cast(m_ctx.mk_ineq(x, m_c, lower, open)); } catch (f2n::exception) { throw subpaving::exception(); } } }; template class context_fpoint_wrapper : public context_wrapper { unsynch_mpq_manager & m_qm; _scoped_numeral m_c; _scoped_numeral_vector m_as; scoped_mpz m_z1, m_z2; void int2fpoint(mpz const & a, typename context_fpoint::numeral & o) { m_qm.set(m_z1, a); this->m_ctx.nm().set(o, m_qm, m_z1); this->m_ctx.nm().to_mpz(o, m_qm, m_z2); if (!m_qm.eq(m_z1, m_z2)) throw subpaving::exception(); } public: context_fpoint_wrapper(typename context_fpoint::numeral_manager & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): context_wrapper(m, p, a), m_qm(qm), m_c(m), m_as(m), m_z1(m_qm), m_z2(m_qm) { } virtual ~context_fpoint_wrapper() {} virtual unsynch_mpq_manager & qm() const { return m_qm; } virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) { try { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { int2fpoint(as[i], m_as[i]); } int2fpoint(c, m_c); return this->m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } catch (typename context_fpoint::numeral_manager::exception) { throw subpaving::exception(); } } virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) { try { typename context_fpoint::numeral_manager & m = this->m_ctx.nm(); if (lower) m.round_to_minus_inf(); else m.round_to_plus_inf(); m.set(m_c, m_qm, k); return reinterpret_cast(this->m_ctx.mk_ineq(x, m_c, lower, open)); } catch (typename context_fpoint::numeral_manager::exception) { throw subpaving::exception(); } } }; typedef context_fpoint_wrapper context_mpff_wrapper; typedef context_fpoint_wrapper context_mpfx_wrapper; context * mk_mpq_context(unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { return alloc(context_mpq_wrapper, m, p, a); } context * mk_mpf_context(f2n & m, params_ref const & p, small_object_allocator * a) { return alloc(context_mpf_wrapper, m, p, a); } context * mk_hwf_context(f2n & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a) { return alloc(context_hwf_wrapper, m, qm, p, a); } context * mk_mpff_context(mpff_manager & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a) { return alloc(context_mpff_wrapper, m, qm, p, a); } context * mk_mpfx_context(mpfx_manager & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a) { return alloc(context_mpfx_wrapper, m, qm, p, a); } }; z3-z3-4.4.1/src/math/subpaving/subpaving.h000066400000000000000000000070221260446376700203130ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving.h Abstract: Subpaving for non-linear arithmetic. This is a wrapper for the different implementations of the subpaving module. This wrapper is the main interface between Z3 other modules and subpaving. Thus, it assumes that polynomials have precise integer coefficients, and bounds are rationals. If a particular implementation uses floats, then internally the bounds are approximated. Author: Leonardo de Moura (leonardo) 2012-08-07. Revision History: --*/ #ifndef SUBPAVING_H_ #define SUBPAVING_H_ #include"mpq.h" #include"subpaving_types.h" #include"params.h" #include"statistics.h" template class f2n; class mpf_manager; class hwf_manager; class mpff_manager; class mpfx_manager; namespace subpaving { class context { public: virtual ~context() {} virtual unsynch_mpq_manager & qm() const = 0; /** \brief Return the number of variables in this subpaving object. */ virtual unsigned num_vars() const = 0; /** \brief Create a new variable. */ virtual var mk_var(bool is_int) = 0; /** \brief Return true if \c x is an integer variable. */ virtual bool is_int(var x) const = 0; /** \brief Create the monomial xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1]. The result is a variable y s.t. y = xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1]. \pre for all i \in [0, sz-1] : ks[i] > 0 \pre sz > 0 */ virtual var mk_monomial(unsigned sz, power const * pws) = 0; /** \brief Create the sum c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]. The result is a variable y s.t. y = c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]. \pre sz > 0 \pre for all i \in [0, sz-1] : as[i] != 0 */ virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) = 0; /** \brief Create an inequality. */ virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) = 0; virtual void inc_ref(ineq * a) = 0; virtual void dec_ref(ineq * a) = 0; /** \brief Assert the clause atoms[0] \/ ... \/ atoms[sz-1] \pre sz >= 1 */ virtual void add_clause(unsigned sz, ineq * const * atoms) = 0; /** \brief Display constraints asserted in the subpaving. */ virtual void display_constraints(std::ostream & out, bool use_star = false) const = 0; virtual void set_cancel(bool f) = 0; virtual void collect_param_descrs(param_descrs & r) = 0; virtual void updt_params(params_ref const & p) = 0; virtual void set_display_proc(display_var_proc * p) = 0; virtual void reset_statistics() = 0; virtual void collect_statistics(statistics & st) const = 0; virtual void operator()() = 0; virtual void display_bounds(std::ostream & out) const = 0; }; context * mk_mpq_context(unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0); context * mk_mpf_context(f2n & m, params_ref const & p = params_ref(), small_object_allocator * a = 0); context * mk_hwf_context(f2n & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = 0); context * mk_mpff_context(mpff_manager & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = 0); context * mk_mpfx_context(mpfx_manager & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = 0); }; #endif z3-z3-4.4.1/src/math/subpaving/subpaving_hwf.cpp000066400000000000000000000006011260446376700215060ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_hwf.cpp Abstract: Subpaving for non-linear arithmetic using hardware floats. Author: Leonardo de Moura (leonardo) 2012-08-06. Revision History: --*/ #include"subpaving_hwf.h" #include"subpaving_t_def.h" // force template instantiation template class subpaving::context_t; z3-z3-4.4.1/src/math/subpaving/subpaving_hwf.h000066400000000000000000000022071260446376700211570ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_hwf.h Abstract: Subpaving for non-linear arithmetic using hardware floats. Author: Leonardo de Moura (leonardo) 2012-08-06. Revision History: --*/ #ifndef SUBPAVING_HWF_H_ #define SUBPAVING_HWF_H_ #include"subpaving_t.h" #include"f2n.h" #include"hwf.h" namespace subpaving { struct config_hwf { f2n & m_manager; public: typedef f2n numeral_manager; typedef f2n::exception exception; static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); } static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); } static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); } config_hwf(f2n & m):m_manager(m) {} f2n & m() const { return const_cast &>(m_manager); } }; class context_hwf : public context_t { public: context_hwf(f2n & m, params_ref const & p, small_object_allocator * a):context_t(config_hwf(m), p, a) {} }; }; #endif z3-z3-4.4.1/src/math/subpaving/subpaving_mpf.cpp000066400000000000000000000006101260446376700215040ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpf.cpp Abstract: Subpaving for non-linear arithmetic using multi-precision floats. Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #include"subpaving_mpf.h" #include"subpaving_t_def.h" // force template instantiation template class subpaving::context_t; z3-z3-4.4.1/src/math/subpaving/subpaving_mpf.h000066400000000000000000000022771260446376700211640ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpf.h Abstract: Subpaving for non-linear arithmetic using multi-precision floats. Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #ifndef SUBPAVING_MPF_H_ #define SUBPAVING_MPF_H_ #include"subpaving_t.h" #include"mpf.h" #include"f2n.h" namespace subpaving { struct config_mpf { f2n & m_manager; public: typedef f2n numeral_manager; typedef mpf numeral; typedef f2n::exception exception; static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); } static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); } static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); } config_mpf(f2n & m):m_manager(m) {} f2n & m() const { return const_cast &>(m_manager); } }; class context_mpf : public context_t { public: context_mpf(f2n & m, params_ref const & p, small_object_allocator * a):context_t(config_mpf(m), p, a) {} }; }; #endif z3-z3-4.4.1/src/math/subpaving/subpaving_mpff.cpp000066400000000000000000000006021260446376700216530ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpff.cpp Abstract: Subpaving for non-linear arithmetic using mpff numerals. Author: Leonardo de Moura (leonardo) 2012-09-18. Revision History: --*/ #include"subpaving_mpff.h" #include"subpaving_t_def.h" // force template instantiation template class subpaving::context_t; z3-z3-4.4.1/src/math/subpaving/subpaving_mpff.h000066400000000000000000000016601260446376700213250ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpff.h Abstract: Subpaving for non-linear arithmetic using mpff numerals Author: Leonardo de Moura (leonardo) 2012-09-18. Revision History: --*/ #ifndef SUBPAVING_MPFF_H_ #define SUBPAVING_MPFF_H_ #include"subpaving_t.h" #include"mpff.h" namespace subpaving { struct config_mpff { typedef mpff_manager numeral_manager; typedef mpff_manager::exception exception; static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); } static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); } static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); } numeral_manager & m_manager; config_mpff(numeral_manager & m):m_manager(m) {} numeral_manager & m() const { return m_manager; } }; typedef context_t context_mpff; }; #endif z3-z3-4.4.1/src/math/subpaving/subpaving_mpfx.cpp000066400000000000000000000006021260446376700216750ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpfx.cpp Abstract: Subpaving for non-linear arithmetic using mpfx numerals. Author: Leonardo de Moura (leonardo) 2012-09-18. Revision History: --*/ #include"subpaving_mpfx.h" #include"subpaving_t_def.h" // force template instantiation template class subpaving::context_t; z3-z3-4.4.1/src/math/subpaving/subpaving_mpfx.h000066400000000000000000000016601260446376700213470ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpfx.h Abstract: Subpaving for non-linear arithmetic using mpfx numerals Author: Leonardo de Moura (leonardo) 2012-09-20. Revision History: --*/ #ifndef SUBPAVING_MPFX_H_ #define SUBPAVING_MPFX_H_ #include"subpaving_t.h" #include"mpfx.h" namespace subpaving { struct config_mpfx { typedef mpfx_manager numeral_manager; typedef mpfx_manager::exception exception; static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); } static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); } static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); } numeral_manager & m_manager; config_mpfx(numeral_manager & m):m_manager(m) {} numeral_manager & m() const { return m_manager; } }; typedef context_t context_mpfx; }; #endif z3-z3-4.4.1/src/math/subpaving/subpaving_mpq.cpp000066400000000000000000000005731260446376700215270ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpq.cpp Abstract: Subpaving for non-linear arithmetic using rationals. Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #include"subpaving_mpq.h" #include"subpaving_t_def.h" // force template instantiation template class subpaving::context_t; z3-z3-4.4.1/src/math/subpaving/subpaving_mpq.h000066400000000000000000000014721260446376700211730ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpq.h Abstract: Subpaving for non-linear arithmetic using rationals Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #ifndef SUBPAVING_MPQ_H_ #define SUBPAVING_MPQ_H_ #include"subpaving_t.h" #include"mpq.h" namespace subpaving { struct config_mpq { typedef unsynch_mpq_manager numeral_manager; struct exception {}; static void round_to_minus_inf(numeral_manager & m) {} static void round_to_plus_inf(numeral_manager & m) {} static void set_rounding(numeral_manager & m, bool to_plus_info) {} numeral_manager & m_manager; config_mpq(numeral_manager & m):m_manager(m) {} numeral_manager & m() const { return m_manager; } }; typedef context_t context_mpq; }; #endif z3-z3-4.4.1/src/math/subpaving/subpaving_t.h000066400000000000000000000720651260446376700206470ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_t.h Abstract: Subpaving template for non-linear arithmetic. Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #ifndef SUBPAVING_T_H_ #define SUBPAVING_T_H_ #include #include"tptr.h" #include"small_object_allocator.h" #include"chashtable.h" #include"parray.h" #include"interval.h" #include"scoped_numeral_vector.h" #include"subpaving_types.h" #include"params.h" #include"statistics.h" #include"lbool.h" #include"id_gen.h" #ifdef _MSC_VER #pragma warning(disable : 4200) #pragma warning(disable : 4355) #endif namespace subpaving { template class context_t { public: typedef typename C::numeral_manager numeral_manager; typedef typename numeral_manager::numeral numeral; /** \brief Inequalities used to encode a problem. */ class ineq { friend class context_t; var m_x; numeral m_val; unsigned m_ref_count:30; unsigned m_lower:1; unsigned m_open:1; public: var x() const { return m_x; } numeral const & value() const { return m_val; } bool is_lower() const { return m_lower; } bool is_open() const { return m_open; } void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc()); struct lt_var_proc { bool operator()(ineq const * a, ineq const * b) const { return a->m_x < b->m_x; } }; }; class node; class constraint { public: enum kind { CLAUSE, MONOMIAL, POLYNOMIAL // TODO: add SIN, COS, TAN, ... }; protected: kind m_kind; uint64 m_timestamp; public: constraint(kind k):m_kind(k), m_timestamp(0) {} kind get_kind() const { return m_kind; } // Return the timestamp of the last propagation visit uint64 timestamp() const { return m_timestamp; } // Reset propagation visit time void set_visited(uint64 ts) { m_timestamp = ts; } }; /** \brief Clauses in the problem description and lemmas learned during paving. */ class clause : public constraint { friend class context_t; unsigned m_size; //!< Number of atoms in the clause. unsigned m_lemma:1; //!< True if it is a learned clause. unsigned m_watched:1; //!< True if it we are watching this clause. All non-lemmas are watched. unsigned m_num_jst:30; //!< Number of times it is used to justify some bound. ineq * m_atoms[0]; static unsigned get_obj_size(unsigned sz) { return sizeof(clause) + sz*sizeof(ineq*); } public: clause():constraint(constraint::CLAUSE) {} unsigned size() const { return m_size; } bool watched() const { return m_watched; } ineq * operator[](unsigned i) const { SASSERT(i < size()); return m_atoms[i]; } void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc()); }; class justification { void * m_data; public: enum kind { AXIOM = 0, ASSUMPTION, CLAUSE, VAR_DEF }; justification(bool axiom = true) { m_data = axiom ? reinterpret_cast(static_cast(AXIOM)) : reinterpret_cast(static_cast(ASSUMPTION)); } justification(justification const & source) { m_data = source.m_data; } explicit justification(clause * c) { m_data = TAG(void*, c, CLAUSE); } explicit justification(var x) { m_data = BOXTAGINT(void*, x, VAR_DEF); } kind get_kind() const { return static_cast(GET_TAG(m_data)); } bool is_clause() const { return get_kind() == CLAUSE; } bool is_axiom() const { return get_kind() == AXIOM; } bool is_assumption() const { return get_kind() == ASSUMPTION; } bool is_var_def() const { return get_kind() == VAR_DEF; } clause * get_clause() const { SASSERT(is_clause()); return UNTAG(clause*, m_data); } var get_var() const { SASSERT(is_var_def()); return UNBOXINT(m_data); } bool operator==(justification const & other) const { return m_data == other.m_data; } bool operator!=(justification const & other) const { return !operator==(other); } }; class bound { friend class context_t; numeral m_val; unsigned m_x:29; unsigned m_lower:1; unsigned m_open:1; unsigned m_mark:1; uint64 m_timestamp; bound * m_prev; justification m_jst; void set_timestamp(uint64 ts) { m_timestamp = ts; } public: var x() const { return static_cast(m_x); } numeral const & value() const { return m_val; } numeral & value() { return m_val; } bool is_lower() const { return m_lower; } bool is_open() const { return m_open; } uint64 timestamp() const { return m_timestamp; } bound * prev() const { return m_prev; } justification jst() const { return m_jst; } void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc()); }; struct bound_array_config { typedef context_t value_manager; typedef small_object_allocator allocator; typedef bound * value; static const bool ref_count = false; static const bool preserve_roots = true; static const unsigned max_trail_sz = 16; static const unsigned factor = 2; }; // auxiliary declarations for parray_manager void dec_ref(bound *) {} void inc_ref(bound *) {} typedef parray_manager bound_array_manager; typedef typename bound_array_manager::ref bound_array; /** \brief Node in the context_t. */ class node { bound_array_manager & m_bm; bound_array m_lowers; bound_array m_uppers; var m_conflict; unsigned m_id; unsigned m_depth; bound * m_trail; node * m_parent; //!< parent node node * m_first_child; node * m_next_sibling; // Doubly linked list of leaves to be processed node * m_prev; node * m_next; public: node(context_t & s, unsigned id); node(node * parent, unsigned id); // return unique indentifier. unsigned id() const { return m_id; } bound_array_manager & bm() const { return m_bm; } bound_array & lowers() { return m_lowers; } bound_array & uppers() { return m_uppers; } bool inconsistent() const { return m_conflict != null_var; } void set_conflict(var x) { SASSERT(!inconsistent()); m_conflict = x; } bound * trail_stack() const { return m_trail; } bound * parent_trail_stack() const { return m_parent == 0 ? 0 : m_parent->m_trail; } bound * lower(var x) const { return bm().get(m_lowers, x); } bound * upper(var x) const { return bm().get(m_uppers, x); } node * parent() const { return m_parent; } node * first_child() const { return m_first_child; } node * next_sibling() const { return m_next_sibling; } node * prev() const { return m_prev; } node * next() const { return m_next; } /** \brief Return true if x is unbounded in this node */ bool is_unbounded(var x) const { return lower(x) == 0 && upper(x) == 0; } void push(bound * b); void set_first_child(node * n) { m_first_child = n; } void set_next_sibling(node * n) { m_next_sibling = n; } void set_next(node * n) { m_next = n; } void set_prev(node * n) { m_prev = n; } unsigned depth() const { return m_depth; } }; /** \brief Intervals are just temporary place holders. The pavers maintain bounds. */ struct interval { bool m_constant; // Flag: constant intervals are pairs // constant intervals node * m_node; var m_x; // mutable intervals numeral m_l_val; bool m_l_inf; bool m_l_open; numeral m_u_val; bool m_u_inf; bool m_u_open; interval():m_constant(false) {} void set_constant(node * n, var x) { m_constant = true; m_node = n; m_x = x; } void set_mutable() { m_constant = false; } }; class interval_config { public: typedef typename C::numeral_manager numeral_manager; typedef typename numeral_manager::numeral numeral; typedef typename context_t::interval interval; private: numeral_manager & m_manager; public: interval_config(numeral_manager & m):m_manager(m) {} numeral_manager & m() const { return m_manager; } void round_to_minus_inf() { C::round_to_minus_inf(m()); } void round_to_plus_inf() { C::round_to_plus_inf(m()); } void set_rounding(bool to_plus_inf) { C::set_rounding(m(), to_plus_inf); } numeral const & lower(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->lower(a.m_x); return b == 0 ? a.m_l_val /* don't care */ : b->value(); } return a.m_l_val; } numeral const & upper(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->upper(a.m_x); return b == 0 ? a.m_u_val /* don't care */ : b->value(); } return a.m_u_val; } numeral & lower(interval & a) { SASSERT(!a.m_constant); return a.m_l_val; } numeral & upper(interval & a) { SASSERT(!a.m_constant); return a.m_u_val; } bool lower_is_inf(interval const & a) const { return a.m_constant ? a.m_node->lower(a.m_x) == 0 : a.m_l_inf; } bool upper_is_inf(interval const & a) const { return a.m_constant ? a.m_node->upper(a.m_x) == 0 : a.m_u_inf; } bool lower_is_open(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->lower(a.m_x); return b == 0 || b->is_open(); } return a.m_l_open; } bool upper_is_open(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->upper(a.m_x); return b == 0 || b->is_open(); } return a.m_u_open; } // Setters void set_lower(interval & a, numeral const & n) { SASSERT(!a.m_constant); m().set(a.m_l_val, n); } void set_upper(interval & a, numeral const & n) { SASSERT(!a.m_constant); m().set(a.m_u_val, n); } void set_lower_is_open(interval & a, bool v) { SASSERT(!a.m_constant); a.m_l_open = v; } void set_upper_is_open(interval & a, bool v) { SASSERT(!a.m_constant); a.m_u_open = v; } void set_lower_is_inf(interval & a, bool v) { SASSERT(!a.m_constant); a.m_l_inf = v; } void set_upper_is_inf(interval & a, bool v) { SASSERT(!a.m_constant); a.m_u_inf = v; } }; typedef ::interval_manager interval_manager; class definition : public constraint { public: definition(typename constraint::kind k):constraint(k) {} }; class monomial : public definition { friend class context_t; unsigned m_size; power m_powers[0]; monomial(unsigned sz, power const * pws); static unsigned get_obj_size(unsigned sz) { return sizeof(monomial) + sz*sizeof(power); } public: unsigned size() const { return m_size; } power const & get_power(unsigned idx) const { SASSERT(idx < size()); return m_powers[idx]; } power const * get_powers() const { return m_powers; } var x(unsigned idx) const { return get_power(idx).x(); } unsigned degree(unsigned idx) const { return get_power(idx).degree(); } void display(std::ostream & out, display_var_proc const & proc = display_var_proc(), bool use_star = false) const; }; class polynomial : public definition { friend class context_t; unsigned m_size; numeral m_c; numeral * m_as; var * m_xs; static unsigned get_obj_size(unsigned sz) { return sizeof(polynomial) + sz*sizeof(numeral) + sz*sizeof(var); } public: polynomial():definition(constraint::POLYNOMIAL) {} unsigned size() const { return m_size; } numeral const & a(unsigned i) const { return m_as[i]; } var x(unsigned i) const { return m_xs[i]; } var const * xs() const { return m_xs; } numeral const * as() const { return m_as; } numeral const & c() const { return m_c; } void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc(), bool use_star = false) const; }; /** \brief Watched element (aka occurence) can be: - A clause - A definition (i.e., a variable) Remark: we cannot use the two watched literal approach since we process multiple nodes. */ class watched { public: enum kind { CLAUSE=0, DEFINITION }; private: void * m_data; public: watched():m_data(0) {} explicit watched(var x) { m_data = BOXTAGINT(void*, x, DEFINITION); } explicit watched(clause * c) { m_data = TAG(void*, c, CLAUSE); } kind get_kind() const { return static_cast(GET_TAG(m_data)); } bool is_clause() const { return get_kind() != DEFINITION; } bool is_definition() const { return get_kind() == DEFINITION; } clause * get_clause() const { SASSERT(is_clause()); return UNTAG(clause*, m_data); } var get_var() const { SASSERT(is_definition()); return UNBOXINT(m_data); } bool operator==(watched const & other) const { return m_data == other.m_data; } bool operator!=(watched const & other) const { return !operator==(other); } }; /** \brief Abstract functor for selecting the next leaf node to be explored. */ class node_selector { context_t * m_ctx; public: node_selector(context_t * ctx):m_ctx(ctx) {} virtual ~node_selector() {} context_t * ctx() const { return m_ctx; } // Return the next leaf node to be processed. // Front and back are the first and last nodes in the doubly linked list of // leaf nodes. // Remark: new nodes are always inserted in the front of the list. virtual node * operator()(node * front, node * back) = 0; }; /** \brief Abstract functor for selecting the next variable to branch. */ class var_selector { context_t * m_ctx; public: var_selector(context_t * ctx):m_ctx(ctx) {} virtual ~var_selector() {} context_t * ctx() const { return m_ctx; } // Return the next variable to branch. virtual var operator()(node * n) = 0; // ----------------------------------- // // Event handlers // // ----------------------------------- // Invoked when a new variable is created. virtual void new_var_eh(var x) {} // Invoked when node n is created virtual void new_node_eh(node * n) {} // Invoked before deleting node n. virtual void del_node_eh(node * n) {} // Invoked when variable x is used during conflict resolution. virtual void used_var_eh(node * n, var x) {} }; class node_splitter; friend class node_splitter; /** \brief Abstract functor for creating children for node n by branching on a given variable. */ class node_splitter { context_t * m_ctx; public: node_splitter(context_t * ctx):m_ctx(ctx) {} virtual ~node_splitter() {} context_t * ctx() const { return m_ctx; } node * mk_node(node * p) { return ctx()->mk_node(p); } bound * mk_decided_bound(var x, numeral const & val, bool lower, bool open, node * n) { return ctx()->mk_bound(x, val, lower, open, n, justification()); } /** \brief Create children nodes for n by splitting on x. \pre n is a leaf. The interval for x in n has more than one element. */ virtual void operator()(node * n, var x) = 0; }; /** \brief Return most recent splitting var for node n. */ var splitting_var(node * n) const; /** \brief Return true if x is a definition. */ bool is_definition(var x) const { return m_defs[x] != 0; } typedef svector watch_list; typedef _scoped_numeral_vector scoped_numeral_vector; private: C m_c; bool m_arith_failed; //!< True if the arithmetic module produced an exception. bool m_own_allocator; small_object_allocator * m_allocator; bound_array_manager m_bm; interval_manager m_im; scoped_numeral_vector m_num_buffer; svector m_is_int; ptr_vector m_defs; vector m_wlist; ptr_vector m_unit_clauses; ptr_vector m_clauses; ptr_vector m_lemmas; id_gen m_node_id_gen; uint64 m_timestamp; node * m_root; // m_leaf_head is the head of a doubly linked list of leaf nodes to be processed. node * m_leaf_head; node * m_leaf_tail; var m_conflict; ptr_vector m_queue; unsigned m_qhead; display_var_proc m_default_display_proc; display_var_proc * m_display_proc; scoped_ptr m_node_selector; scoped_ptr m_var_selector; scoped_ptr m_node_splitter; svector m_pws; // Configuration numeral m_epsilon; //!< If upper - lower < epsilon, then new bound is not propagated. bool m_zero_epsilon; numeral m_max_bound; //!< Bounds > m_max and < -m_max are not propagated numeral m_minus_max_bound; //!< -m_max_bound numeral m_nth_root_prec; //!< precision for computing the nth root unsigned m_max_depth; //!< Maximum depth unsigned m_max_nodes; //!< Maximum number of nodes in the tree unsigned long long m_max_memory; // in bytes // Counters unsigned m_num_nodes; // Statistics unsigned m_num_conflicts; unsigned m_num_mk_bounds; unsigned m_num_splits; unsigned m_num_visited; // Temporary numeral m_tmp1, m_tmp2, m_tmp3; interval m_i_tmp1, m_i_tmp2, m_i_tmp3; // Cancel flag volatile bool m_cancel; friend class node; void set_arith_failed() { m_arith_failed = true; } void checkpoint(); bound_array_manager & bm() { return m_bm; } interval_manager & im() { return m_im; } small_object_allocator & allocator() const { return *m_allocator; } bound * mk_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst); void del_bound(bound * b); // Create a new bound and add it to the propagation queue. void propagate_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst); bool is_int(monomial const * m) const; bool is_int(polynomial const * p) const; bool is_monomial(var x) const { return m_defs[x] != 0 && m_defs[x]->get_kind() == constraint::MONOMIAL; } monomial * get_monomial(var x) const { SASSERT(is_monomial(x)); return static_cast(m_defs[x]); } bool is_polynomial(var x) const { return m_defs[x] != 0 && m_defs[x]->get_kind() == constraint::POLYNOMIAL; } polynomial * get_polynomial(var x) const { SASSERT(is_polynomial(x)); return static_cast(m_defs[x]); } static void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc, var x, numeral & k, bool lower, bool open); void display(std::ostream & out, var x) const; void display_definition(std::ostream & out, definition const * d, bool use_star = false) const; void display(std::ostream & out, constraint * a, bool use_star = false) const; void display(std::ostream & out, bound * b) const; void display(std::ostream & out, ineq * a) const; void display_params(std::ostream & out) const; void add_unit_clause(ineq * a, bool axiom); // Remark: Not all lemmas need to be watched. Some of them can be used to justify clauses only. void add_clause_core(unsigned sz, ineq * const * atoms, bool lemma, bool watched); void del_clause(clause * cls); node * mk_node(node * parent = 0); void del_node(node * n); void del_nodes(); void del(interval & a); void del_clauses(ptr_vector & cs); void del_unit_clauses(); void del_clauses(); void del_monomial(monomial * m); void del_sum(polynomial * p); void del_definitions(); /** \brief Insert n in the beginning of the doubly linked list of leaves. \pre n is a leaf, and it is not already in the list. */ void push_front(node * n); /** \brief Insert n in the end of the doubly linked list of leaves. \pre n is a leaf, and it is not already in the list. */ void push_back(node * n); /** \brief Remove n from the doubly linked list of leaves. \pre n is a leaf, and it is in the list. */ void remove_from_leaf_dlist(node * n); /** \brief Remove all nodes from the leaf dlist. */ void reset_leaf_dlist(); /** \brief Add all leaves back to the leaf dlist. */ void rebuild_leaf_dlist(node * n); // ----------------------------------- // // Propagation // // ----------------------------------- /** \brief Return true if the given node is in an inconsistent state. */ bool inconsistent(node * n) const { return n->inconsistent(); } /** \brief Set a conflict produced by the bounds of x at the given node. */ void set_conflict(var x, node * n); /** \brief Return true if bound b may propagate a new bound using constraint c at node n. */ bool may_propagate(bound * b, constraint * c, node * n); /** \brief Normalize bound if x is integer. Examples: x < 2 --> x <= 1 x <= 2.3 --> x <= 2 */ void normalize_bound(var x, numeral & val, bool lower, bool & open); /** \brief Return true if (x, k, lower, open) is a relevant new bound at node n. That is, it improves the current bound, and satisfies m_epsilon and m_max_bound. */ bool relevant_new_bound(var x, numeral const & k, bool lower, bool open, node * n); /** \brief Return true if the lower and upper bounds of x are 0 at node n. */ bool is_zero(var x, node * n) const; /** \brief Return true if upper bound of x is 0 at node n. */ bool is_upper_zero(var x, node * n) const; /** \brief Return true if lower and upper bounds of x are conflicting at node n. That is, upper(x) < lower(x) */ bool conflicting_bounds(var x, node * n) const; /** \brief Return true if x is unbounded at node n. */ bool is_unbounded(var x, node * n) const { return n->is_unbounded(x); } /** \brief Return true if b is the most recent lower/upper bound for variable b->x() at node n. */ bool most_recent(bound * b, node * n) const; /** \brief Add most recent bounds of node n into the propagation queue. That is, all bounds b s.t. b is in the trail of n, but not in the tail of parent(n), and most_recent(b, n). */ void add_recent_bounds(node * n); /** \brief Propagate new bounds at node n using get_monomial(x) \pre is_monomial(x) */ void propagate_monomial(var x, node * n); void propagate_monomial_upward(var x, node * n); void propagate_monomial_downward(var x, node * n, unsigned i); /** \brief Propagate new bounds at node n using get_polynomial(x) \pre is_polynomial(x) */ void propagate_polynomial(var x, node * n); // Propagate a new bound for y using the polynomial associated with x. x may be equal to y. void propagate_polynomial(var x, node * n, var y); /** \brief Propagate new bounds at node n using clause c. */ void propagate_clause(clause * c, node * n); /** \brief Return the truth value of inequaliy t at node n. */ lbool value(ineq * t, node * n); /** \brief Propagate new bounds at node n using the definition of variable x. \pre is_definition(x) */ void propagate_def(var x, node * n); /** \brief Propagate constraints in b->x()'s watch list. */ void propagate(node * n, bound * b); /** \brief Perform bound propagation at node n. */ void propagate(node * n); /** \brief Try to propagate at node n using all definitions. */ void propagate_all_definitions(node * n); // ----------------------------------- // // Main // // ----------------------------------- void init(); /** \brief Assert unit clauses in the node n. */ void assert_units(node * n); // ----------------------------------- // // Debugging support // // ----------------------------------- /** \brief Return true if b is a bound for node n. */ bool is_bound_of(bound * b, node * n) const; /** \brief Check the consistency of the doubly linked list of leaves. */ bool check_leaf_dlist() const; /** \brief Check paving tree structure. */ bool check_tree() const; /** \brief Check main invariants. */ bool check_invariant() const; public: context_t(C const & c, params_ref const & p, small_object_allocator * a); ~context_t(); /** \brief Return true if the arithmetic module failed. */ bool arith_failed() const { return m_arith_failed; } numeral_manager & nm() const { return m_c.m(); } unsigned num_vars() const { return m_is_int.size(); } bool is_int(var x) const { SASSERT(x < num_vars()); return m_is_int[x]; } /** \brief Create a new variable. */ var mk_var(bool is_int); /** \brief Create the monomial xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1]. The result is a variable y s.t. y = xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1]. \pre for all i \in [0, sz-1] : ks[i] > 0 \pre sz > 0 */ var mk_monomial(unsigned sz, power const * pws); /** \brief Create the sum c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]. The result is a variable y s.t. y = c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]. \pre sz > 0 \pre for all i \in [0, sz-1] : as[i] != 0 */ var mk_sum(numeral const & c, unsigned sz, numeral const * as, var const * xs); /** \brief Create an inequality. */ ineq * mk_ineq(var x, numeral const & k, bool lower, bool open); void inc_ref(ineq * a); void dec_ref(ineq * a); /** \brief Assert the clause atoms[0] \/ ... \/ atoms[sz-1] \pre sz > 1 */ void add_clause(unsigned sz, ineq * const * atoms) { add_clause_core(sz, atoms, false, true); } /** \brief Assert a constraint of one of the forms: x < k, x > k, x <= k, x >= k. If axiom == true, then the constraint is not tracked in proofs. */ void add_ineq(var x, numeral const & k, bool lower, bool open, bool axiom); /** \brief Store in the given vector all leaves of the paving tree. */ void collect_leaves(ptr_vector & leaves) const; /** \brief Display constraints asserted in the subpaving. */ void display_constraints(std::ostream & out, bool use_star = false) const; /** \brief Display bounds for each leaf of the tree. */ void display_bounds(std::ostream & out) const; void display_bounds(std::ostream & out, node * n) const; void set_display_proc(display_var_proc * p) { m_display_proc = p; } void set_cancel(bool f) { m_cancel = f; im().set_cancel(f); } void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void reset_statistics(); void collect_statistics(statistics & st) const; void operator()(); }; }; #endif z3-z3-4.4.1/src/math/subpaving/subpaving_t_def.h000066400000000000000000001715001260446376700214570ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_t_def.h Abstract: Subpaving template for non-linear arithmetic. Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #include"subpaving_t.h" #include"interval_def.h" #include"buffer.h" #include"cooperate.h" #include"z3_exception.h" #include"common_msgs.h" namespace subpaving { /** \brief Node selector for breadth-first search. */ template class breadth_first_node_selector : public context_t::node_selector { typedef typename context_t::node node; public: breadth_first_node_selector(context_t * ctx): context_t::node_selector(ctx) { } virtual node * operator()(node * front, node * back) { return back; } }; /** \brief Node selector for depth-first search. */ template class depth_first_node_selector : public context_t::node_selector { typedef typename context_t::node node; public: depth_first_node_selector(context_t * ctx): context_t::node_selector(ctx) { } virtual node * operator()(node * front, node * back) { return front; } }; /** Round robing variable selector. If only_non_def is true, then variable definitions (aka auxiliary variables) are ignored. */ template class round_robing_var_selector : public context_t::var_selector { typedef typename context_t::bound bound; bool m_only_non_def; void next(var & x) const { x++; if (x >= this->ctx()->num_vars()) x = 0; } public: round_robing_var_selector(context_t * ctx, bool only_non_def = true): context_t::var_selector(ctx), m_only_non_def(only_non_def) { } // Return the next variable to branch. virtual var operator()(typename context_t::node * n) { typename context_t::numeral_manager & nm = this->ctx()->nm(); SASSERT(this->ctx()->num_vars() > 0); var x = this->ctx()->splitting_var(n); if (x == null_var) x = 0; else next(x); var start = x; do { if (!m_only_non_def || !this->ctx()->is_definition(x)) { bound * lower = n->lower(x); bound * upper = n->upper(x); if (lower == 0 || upper == 0 || !nm.eq(lower->value(), upper->value())) { return x; } } next(x); } while (x != start); return null_var; } }; /** Selector that uses the following strategy: - If only_non_def is true, then variable definitions (aka auxiliary variables) are ignored. - All variables x s.t. lower(x) == upper(x) are ignored. - If node contains an unbounded variable (-oo, oo), then return the smallest unbounded variable. - Otherwise, select the smallest variable x with the largest width, where width is defined as: If x has lower and upper bound, then width = upper(x) - lower(x). If x has only lower, width = penalty/max(|lower(x)|, 1) If x has only upper, width = penalty/max(|upper(x)|, 1) penaly is a parameter of this class. This strategy guarantees fairness. */ template class largest_interval_var_selector : public context_t::var_selector { unsigned m_penalty; bool m_only_non_def; public: largest_interval_var_selector(context_t * ctx, unsigned unbounded_penalty = 10, bool only_non_def = true): context_t::var_selector(ctx), m_penalty(unbounded_penalty), m_only_non_def(only_non_def) { } // Return the next variable to branch. virtual var operator()(typename context_t::node * n) { var y = null_var; typename context_t::numeral_manager & nm = this->ctx()->nm(); _scoped_numeral::numeral_manager> largest(nm), width(nm), penalty(nm), one(nm); nm.set(penalty, m_penalty); nm.set(one, 1); unsigned num = this->ctx()->num_vars(); for (var x = 0; x < num; x++) { if (m_only_non_def && this->ctx()->is_definition(x)) continue; typename context_t::bound * l = n->lower(x); typename context_t::bound * u = n->upper(x); // variables without lower and upper bounds are selected immediately. if (l == 0 && u == 0) return x; if (l != 0 && u != 0) { // ignore variables s.t. lower(x) == upper(x) if (nm.eq(l->value(), u->value())) continue; // if x has lower and upper bounds, set width to upper(x) - lower(x) C::round_to_plus_inf(nm); nm.sub(u->value(), l->value(), width); } else { // if x does not have lower or upper, then // set width to penalty/|value| where value is the existing bound. if (l != 0) nm.set(width, l->value()); else nm.set(width, u->value()); C::round_to_plus_inf(nm); if (nm.is_neg(width)) nm.neg(width); if (nm.lt(width, one)) nm.set(width, one); nm.mul(penalty, width, width); } if (y == null_var || nm.gt(width, largest)) { y = x; nm.set(largest, width); } } return y; } }; template class midpoint_node_splitter : public context_t::node_splitter { typedef typename context_t::numeral_manager numeral_manager; typedef typename numeral_manager::numeral numeral; typedef typename context_t::node node; typedef typename context_t::bound bound; bool m_left_open; unsigned m_delta; public: midpoint_node_splitter(context_t * ctx, bool left_open = true, unsigned delta = 1): context_t::node_splitter(ctx), m_left_open(left_open), m_delta(delta) { SASSERT(m_delta < INT_MAX); } virtual void operator()(node * n, var x) { SASSERT(!n->inconsistent()); numeral_manager & nm = this->ctx()->nm(); node * left = this->mk_node(n); node * right = this->mk_node(n); bound * lower = n->lower(x); bound * upper = n->upper(x); _scoped_numeral mid(nm); if (lower == 0 && upper == 0) { nm.set(mid, 0); // mid == 0 } else if (lower == 0) { _scoped_numeral delta(nm); SASSERT(upper != 0); nm.set(delta, static_cast(m_delta)); nm.set(mid, upper->value()); C::round_to_minus_inf(nm); nm.sub(mid, delta, mid); // mid == upper - delta } else if (upper == 0) { _scoped_numeral delta(nm); SASSERT(lower != 0); nm.set(delta, static_cast(m_delta)); nm.set(mid, lower->value()); C::round_to_plus_inf(nm); nm.add(mid, delta, mid); // mid == lower + delta } else { _scoped_numeral two(nm); SASSERT(!nm.eq(lower->value(), upper->value())); nm.set(two, 2); nm.add(lower->value(), upper->value(), mid); nm.div(mid, two, mid); if (!(nm.lt(lower->value(), mid) && nm.lt(mid, upper->value()))) throw subpaving::exception(); // mid == (lower + upper)/2 } this->mk_decided_bound(x, mid, false, m_left_open, left); this->mk_decided_bound(x, mid, true, !m_left_open, right); TRACE("subpaving_int_split", tout << "LEFT:\n"; this->ctx()->display_bounds(tout, left); tout << "\nRIGHT:\n"; this->ctx()->display_bounds(tout, right);); } }; /** \brief Auxiliary static method used to diplay a bound specified by (x, k, lower, open). */ template void context_t::display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc, var x, numeral & k, bool lower, bool open) { if (lower) { out << nm.to_rational_string(k) << " <"; if (!open) out << "="; out << " "; proc(out, x); } else { proc(out, x); out << " <"; if (!open) out << "="; out << " " << nm.to_rational_string(k); } } template void context_t::ineq::display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc) { context_t::display(out, nm, proc, m_x, m_val, is_lower(), is_open()); } template void context_t::bound::display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc) { context_t::display(out, nm, proc, m_x, m_val, is_lower(), is_open()); } template void context_t::clause::display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc) { for (unsigned i = 0; i < size(); i++) { if (i > 0) out << " or "; m_atoms[i]->display(out, nm, proc); } } template context_t::node::node(context_t & s, unsigned id): m_bm(s.bm()) { m_id = id; m_depth = 0; unsigned num_vars = s.num_vars(); m_conflict = null_var; m_trail = 0; m_parent = 0; m_first_child = 0; m_next_sibling = 0; m_prev = 0; m_next = 0; bm().mk(m_lowers); bm().mk(m_uppers); for (unsigned i = 0; i < num_vars; i++) { bm().push_back(m_lowers, 0); bm().push_back(m_uppers, 0); } } template context_t::node::node(node * parent, unsigned id): m_bm(parent->m_bm) { m_id = id; m_depth = parent->depth() + 1; bm().copy(parent->m_lowers, m_lowers); bm().copy(parent->m_uppers, m_uppers); m_conflict = parent->m_conflict; m_trail = parent->m_trail; m_parent = parent; m_first_child = 0; m_next_sibling = parent->m_first_child; m_prev = 0; m_next = 0; parent->m_first_child = this; } /** \brief Add a new bound b at this node. */ template void context_t::node::push(bound * b) { SASSERT(b->prev() == m_trail); m_trail = b; if (b->is_lower()) { bm().set(m_lowers, b->x(), b); SASSERT(lower(b->x()) == b); } else { bm().set(m_uppers, b->x(), b); SASSERT(upper(b->x()) == b); } } /** \brief Return the most recent variable that was used for splitting on node n. */ template var context_t::splitting_var(node * n) const { if (n == m_root) return null_var; bound * b = n->trail_stack(); while (b != 0) { if (b->jst().is_axiom()) return b->x(); b = b->prev(); } UNREACHABLE(); return null_var; } template context_t::monomial::monomial(unsigned sz, power const * pws): definition(constraint::MONOMIAL), m_size(sz) { memcpy(m_powers, pws, sz*sizeof(power)); std::sort(m_powers, m_powers+sz, typename power::lt_proc()); DEBUG_CODE({ for (unsigned i = 0; i < sz; i ++) { SASSERT(i == 0 || x(i) > x(i-1)); SASSERT(degree(i) > 0); }}); } template void context_t::monomial::display(std::ostream & out, display_var_proc const & proc, bool use_star) const { SASSERT(m_size > 0); for (unsigned i = 0; i < m_size; i++) { if (i > 0) { if (use_star) out << "*"; else out << " "; } proc(out, x(i)); if (degree(i) > 1) out << "^" << degree(i); } } template void context_t::polynomial::display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc, bool use_star) const { bool first = true; if (!nm.is_zero(m_c)) { out << nm.to_rational_string(m_c); first = false; } for (unsigned i = 0; i < m_size; i++) { if (first) first = false; else out << " + "; if (!nm.is_one(a(i))) { out << nm.to_rational_string(a(i)); if (use_star) out << "*"; else out << " "; } proc(out, x(i)); } } template context_t::context_t(C const & c, params_ref const & p, small_object_allocator * a): m_c(c), m_own_allocator(a == 0), m_allocator(a == 0 ? alloc(small_object_allocator, "subpaving") : a), m_bm(*this, *m_allocator), m_im(interval_config(m_c.m())), m_num_buffer(nm()) { m_arith_failed = false; m_timestamp = 0; m_root = 0; m_leaf_head = 0; m_leaf_tail = 0; m_conflict = null_var; m_qhead = 0; m_display_proc = &m_default_display_proc; m_node_selector = alloc(breadth_first_node_selector, this); m_var_selector = alloc(round_robing_var_selector, this); m_node_splitter = alloc(midpoint_node_splitter, this); m_cancel = false; m_num_nodes = 0; updt_params(p); reset_statistics(); } template context_t::~context_t() { nm().del(m_epsilon); nm().del(m_max_bound); nm().del(m_minus_max_bound); nm().del(m_nth_root_prec); nm().del(m_tmp1); nm().del(m_tmp2); nm().del(m_tmp3); del(m_i_tmp1); del(m_i_tmp2); del(m_i_tmp3); del_nodes(); del_unit_clauses(); del_clauses(); del_definitions(); if (m_own_allocator) dealloc(m_allocator); } template void context_t::checkpoint() { if (m_cancel) throw default_exception("canceled"); if (memory::get_allocation_size() > m_max_memory) throw default_exception(Z3_MAX_MEMORY_MSG); cooperate("subpaving"); } template void context_t::del(interval & a) { nm().del(a.m_l_val); nm().del(a.m_u_val); } template void context_t::updt_params(params_ref const & p) { unsigned epsilon = p.get_uint("epsilon", 20); if (epsilon != 0) { nm().set(m_epsilon, static_cast(epsilon)); nm().inv(m_epsilon); m_zero_epsilon = false; } else { nm().reset(m_epsilon); m_zero_epsilon = true; } unsigned max_power = p.get_uint("max_bound", 10); nm().set(m_max_bound, 10); nm().power(m_max_bound, max_power, m_max_bound); nm().set(m_minus_max_bound, m_max_bound); nm().neg(m_minus_max_bound); m_max_depth = p.get_uint("max_depth", 128); m_max_nodes = p.get_uint("max_nodes", 8192); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); unsigned prec = p.get_uint("nth_root_precision", 8192); if (prec == 0) prec = 1; nm().set(m_nth_root_prec, static_cast(prec)); nm().inv(m_nth_root_prec); } template void context_t::collect_param_descrs(param_descrs & d) { d.insert("max_nodes", CPK_UINT, "(default: 8192) maximum number of nodes in the subpaving tree."); d.insert("max_depth", CPK_UINT, "(default: 128) maximum depth of the subpaving tree."); d.insert("epsilon", CPK_UINT, "(default: 20) value k s.t. a new lower (upper) bound for x is propagated only new-lower(x) > lower(k) + 1/k * max(min(upper(x) - lower(x), |lower|), 1) (new-upper(x) < upper(x) - 1/k * max(min(upper(x) - lower(x), |lower|), 1)). If k = 0, then this restriction is ignored."); d.insert("max_bound", CPK_UINT, "(default 10) value k s.t. a new upper (lower) bound for x is propagated only if upper(x) > -10^k or lower(x) = -oo (lower(x) < 10^k or upper(x) = oo)"); d.insert("nth_root_precision", CPK_UINT, "(default 8192) value k s.t. 1/k is the precision for computing the nth root in the subpaving module."); } template void context_t::display_params(std::ostream & out) const { out << "max_nodes " << m_max_nodes << "\n"; out << "max_depth " << m_max_depth << "\n"; out << "epsilon " << nm().to_rational_string(m_epsilon) << "\n"; out << "max_bound " << nm().to_rational_string(m_max_bound) << "\n"; out << "max_memory " << m_max_memory << "\n"; } template typename context_t::bound * context_t::mk_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst) { SASSERT(!inconsistent(n)); m_num_mk_bounds++; void * mem = allocator().allocate(sizeof(bound)); bound * r = new (mem) bound(); r->m_x = x; if (is_int(x)) { // adjust integer bound if (!nm().is_int(val)) open = false; // performing ceil/floor if (lower) { nm().ceil(val, r->m_val); } else { nm().floor(val, r->m_val); } if (open) { open = false; if (lower) { C::round_to_minus_inf(nm()); nm().inc(r->m_val); } else { C::round_to_plus_inf(nm()); nm().dec(r->m_val); } } } else { nm().set(r->m_val, val); } r->m_lower = lower; r->m_open = open; r->m_mark = false; r->m_timestamp = m_timestamp; r->m_prev = n->trail_stack(); r->m_jst = jst; n->push(r); TRACE("subpaving_mk_bound", tout << "mk_bound: "; display(tout, r); tout << "\ntimestamp: " << r->m_timestamp << "\n";); if (conflicting_bounds(x, n)) { TRACE("subpaving_mk_bound", tout << "conflict\n"; display_bounds(tout, n);); set_conflict(x, n); } m_timestamp++; if (m_timestamp == UINT64_MAX) throw subpaving::exception(); // subpaving failed. return r; } template void context_t::propagate_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst) { bound * b = mk_bound(x, val, lower, open, n, jst); m_queue.push_back(b); SASSERT(!lower || n->lower(x) == b); SASSERT(lower || n->upper(x) == b); SASSERT(is_int(x) || !lower || nm().eq(n->lower(x)->value(), val)); SASSERT(is_int(x) || lower || nm().eq(n->upper(x)->value(), val)); SASSERT(open || !nm().is_int(val) || !lower || nm().eq(n->lower(x)->value(), val)); SASSERT(open || !nm().is_int(val) || lower || nm().eq(n->upper(x)->value(), val)); SASSERT(!lower || nm().ge(n->lower(x)->value(), val)); SASSERT(lower || nm().le(n->upper(x)->value(), val)); } template void context_t::del_bound(bound * b) { nm().del(b->m_val); b->~bound(); allocator().deallocate(sizeof(bound), b); } template void context_t::display(std::ostream & out, var x) const { if (x == null_var) out << "[null]"; else (*m_display_proc)(out, x); } template void context_t::display(std::ostream & out, bound * b) const { b->display(out, nm(), *m_display_proc); } template void context_t::display(std::ostream & out, ineq * a) const { a->display(out, nm(), *m_display_proc); } template void context_t::display_definition(std::ostream & out, definition const * d, bool use_star) const { switch (d->get_kind()) { case constraint::MONOMIAL: static_cast(d)->display(out, *m_display_proc, use_star); break; case constraint::POLYNOMIAL: static_cast(d)->display(out, nm(), *m_display_proc, use_star); break; default: UNREACHABLE(); }; } template void context_t::display(std::ostream & out, constraint * c, bool use_star) const { if (c->get_kind() == constraint::CLAUSE) static_cast(c)->display(out, nm(), *m_display_proc); else display_definition(out, static_cast(c), use_star); } template void context_t::display_bounds(std::ostream & out, node * n) const { unsigned num = num_vars(); for (unsigned x = 0; x < num; x++) { bound * l = n->lower(x); bound * u = n->upper(x); if (l != 0) { display(out, l); out << " "; } if (u != 0) { display(out, u); } if (l != 0 || u != 0) out << "\n"; } } /** \brief Return true if all variables in m are integer. */ template bool context_t::is_int(monomial const * m) const { for (unsigned i = 0; i < m->size(); i++) { if (is_int(m->x(i))) return true; } return false; } /** \brief Return true if all variables in p are integer, and all coefficients in p are integer. */ template bool context_t::is_int(polynomial const * p) const { for (unsigned i = 0; i < p->size(); i++) { if (!is_int(p->x(i)) || !nm().is_int(p->a(i))) { TRACE("subpaving_is_int", tout << "polynomial is not integer due to monomial at i: " << i << "\n"; tout.flush(); display(tout, p->x(i)); tout << " "; nm().display(tout, p->a(i)); tout << "\n";); return false; } } return nm().is_int(p->c()); } template var context_t::mk_var(bool is_int) { var r = static_cast(m_is_int.size()); m_is_int.push_back(is_int); m_defs.push_back(0); m_wlist.push_back(watch_list()); m_var_selector->new_var_eh(r); return r; } template void context_t::del_monomial(monomial * m) { unsigned mem_sz = monomial::get_obj_size(m->size()); m->~monomial(); allocator().deallocate(mem_sz, m); } template var context_t::mk_monomial(unsigned sz, power const * pws) { SASSERT(sz > 0); m_pws.reset(); m_pws.append(sz, pws); std::sort(m_pws.begin(), m_pws.end(), power::lt_proc()); unsigned j = 0; for (unsigned i = 1; i < sz; i++) { if (m_pws[j].x() == m_pws[i].x()) { m_pws[j].degree() += m_pws[i].degree(); } else { j++; SASSERT(j <= i); m_pws[j] = m_pws[i]; } } sz = j + 1; pws = m_pws.c_ptr(); unsigned mem_sz = monomial::get_obj_size(sz); void * mem = allocator().allocate(mem_sz); monomial * r = new (mem) monomial(sz, pws); var new_var = mk_var(is_int(r)); m_defs[new_var] = r; for (unsigned i = 0; i < sz; i++) { var x = pws[i].x(); m_wlist[x].push_back(watched(new_var)); } return new_var; } template void context_t::del_sum(polynomial * p) { unsigned sz = p->size(); unsigned mem_sz = polynomial::get_obj_size(sz); for (unsigned i = 0; i < sz; i++) { nm().del(p->m_as[i]); } nm().del(p->m_c); p->~polynomial(); allocator().deallocate(mem_sz, p); } template var context_t::mk_sum(numeral const & c, unsigned sz, numeral const * as, var const * xs) { m_num_buffer.reserve(num_vars(), numeral()); for (unsigned i = 0; i < sz; i++) { SASSERT(xs[i] < num_vars()); nm().set(m_num_buffer[xs[i]], as[i]); } unsigned mem_sz = polynomial::get_obj_size(sz); void * mem = allocator().allocate(mem_sz); polynomial * p = new (mem) polynomial(); p->m_size = sz; nm().set(p->m_c, c); p->m_as = reinterpret_cast(static_cast(mem) + sizeof(polynomial)); p->m_xs = reinterpret_cast(reinterpret_cast(p->m_as) + sizeof(numeral)*sz); memcpy(p->m_xs, xs, sizeof(var)*sz); std::sort(p->m_xs, p->m_xs+sz); for (unsigned i = 0; i < sz; i++) { numeral * curr = p->m_as + i; new (curr) numeral(); var x = p->m_xs[i]; nm().swap(m_num_buffer[x], *curr); } TRACE("subpaving_mk_sum", tout << "new variable is integer: " << is_int(p) << "\n";); var new_var = mk_var(is_int(p)); for (unsigned i = 0; i < sz; i++) { var x = p->m_xs[i]; m_wlist[x].push_back(watched(new_var)); } m_defs[new_var] = p; return new_var; } template typename context_t::ineq * context_t::mk_ineq(var x, numeral const & k, bool lower, bool open) { void * mem = allocator().allocate(sizeof(ineq)); ineq * r = new (mem) ineq(); r->m_ref_count = 0; r->m_x = x; nm().set(r->m_val, k); r->m_lower = lower; r->m_open = open; return r; } template void context_t::inc_ref(ineq * a) { TRACE("subpaving_ref_count", tout << "inc-ref: " << a << " " << a->m_ref_count << "\n";); if (a) a->m_ref_count++; } template void context_t::dec_ref(ineq * a) { if (a) { TRACE("subpaving_ref_count", tout << "dec-ref: " << a << " " << a->m_ref_count << "\n"; a->display(tout, nm()); tout << "\n";); SASSERT(a->m_ref_count > 0); a->m_ref_count--; if (a->m_ref_count == 0) { nm().del(a->m_val); a->~ineq(); allocator().deallocate(sizeof(ineq), a); } } } template void context_t::add_clause_core(unsigned sz, ineq * const * atoms, bool lemma, bool watch) { SASSERT(lemma || watch); SASSERT(sz > 0); if (sz == 1) { add_unit_clause(atoms[0], true); return; } void * mem = allocator().allocate(clause::get_obj_size(sz)); clause * c = new (mem) clause(); c->m_size = sz; for (unsigned i = 0; i < sz; i++) { inc_ref(atoms[i]); c->m_atoms[i] = atoms[i]; } std::stable_sort(c->m_atoms, c->m_atoms + sz, typename ineq::lt_var_proc()); if (watch) { for (unsigned i = 0; i < sz; i++) { var x = c->m_atoms[i]->x(); if (i == 0 || x != c->m_atoms[i-1]->x()) m_wlist[x].push_back(watched(c)); } } c->m_lemma = lemma; c->m_num_jst = 0; c->m_watched = watch; if (!lemma) { m_clauses.push_back(c); } else if (watch) { m_lemmas.push_back(c); } TRACE("subpaving_clause", tout << "new clause:\n"; display(tout, c); tout << "\n";); } template void context_t::del_clause(clause * c) { SASSERT(c->m_num_jst == 0); // We cannot delete a clause that is being used to justify some bound bool watch = c->watched(); var prev_x = null_var; unsigned sz = c->size(); for (unsigned i = 0; i < sz; i++) { var x = c->m_atoms[i]->x(); if (watch) { if (x != prev_x) m_wlist[x].erase(watched(c)); prev_x = x; } dec_ref((*c)[i]); } unsigned mem_sz = clause::get_obj_size(sz); c->~clause(); allocator().deallocate(mem_sz, c); } template void context_t::add_unit_clause(ineq * a, bool axiom) { TRACE("subpaving", a->display(tout, nm(), *m_display_proc); tout << "\n";); inc_ref(a); m_unit_clauses.push_back(TAG(ineq*, a, axiom)); } template void context_t::add_ineq(var x, numeral const & k, bool lower, bool open, bool axiom) { ineq * unit = mk_ineq(x, k, lower, open); add_unit_clause(unit, axiom); } template typename context_t::node * context_t::mk_node(node * parent) { void * mem = allocator().allocate(sizeof(node)); node * r; if (parent == 0) r = new (mem) node(*this, m_node_id_gen.mk()); else r = new (mem) node(parent, m_node_id_gen.mk()); m_var_selector->new_node_eh(r); // Add node in the leaf dlist push_front(r); m_num_nodes++; return r; } template void context_t::del_node(node * n) { SASSERT(n->first_child() == 0); SASSERT(m_num_nodes > 0); m_num_nodes--; m_var_selector->del_node_eh(n); // recycle id m_node_id_gen.recycle(n->id()); // disconnect n from list of leaves. remove_from_leaf_dlist(n); // disconnect n from parent node * p = n->parent(); bound * b = n->trail_stack(); bound * b_old; if (p != 0) { node * c = p->first_child(); if (c == n) { // n is the first child p->set_first_child(n->next_sibling()); } else { SASSERT(c->next_sibling() != 0); while (c->next_sibling() != n) { c = c->next_sibling(); SASSERT(c->next_sibling() != 0); } SASSERT(c->next_sibling() == n); c->set_next_sibling(n->next_sibling()); } b_old = p->trail_stack(); } else { b_old = 0; } while (b != b_old) { bound * old = b; b = b->prev(); del_bound(old); } bm().del(n->uppers()); bm().del(n->lowers()); n->~node(); allocator().deallocate(sizeof(node), n); } template void context_t::del_nodes() { ptr_buffer todo; if (m_root == 0) return; todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); node * c = n->first_child(); if (c == 0) { del_node(n); todo.pop_back(); } else { while (c != 0) { todo.push_back(c); c = c->next_sibling(); } } } } template void context_t::push_front(node * n) { SASSERT(n->first_child() == 0); SASSERT(n->next() == 0); SASSERT(n->prev() == 0); n->set_next(m_leaf_head); if (m_leaf_head != 0) { SASSERT(m_leaf_head->prev() == 0); m_leaf_head->set_prev(n); } else { SASSERT(m_leaf_head == 0); m_leaf_tail = n; } m_leaf_head = n; } template void context_t::push_back(node * n) { SASSERT(n->first_child() == 0); SASSERT(n->next() == 0); SASSERT(n->prev() == 0); n->set_prev(m_leaf_tail); if (m_leaf_tail != 0) { SASSERT(m_leaf_tail->next() == 0); m_leaf_tail->set_next(n); } else { SASSERT(m_leaf_tail == 0); m_leaf_head = n; } m_leaf_tail = n; } template void context_t::reset_leaf_dlist() { // Remove all nodes from the lead doubly linked list node * n = m_leaf_head; while (n != 0) { node * next = n->next(); n->set_next(0); n->set_prev(0); n = next; } m_leaf_head = 0; m_leaf_tail = 0; } template void context_t::rebuild_leaf_dlist(node * n) { reset_leaf_dlist(); // Reinsert all leaves in the leaf dlist. ptr_buffer todo; if (m_root != 0) todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); node * c = n->first_child(); if (c == 0) { if (!n->inconsistent()) push_front(n); } else { while (c != 0) { SASSERT(c->parent() == n); todo.push_back(c); c = c->next_sibling(); } } } } template void context_t::remove_from_leaf_dlist(node * n) { node * prev = n->prev(); node * next = n->next(); SASSERT(prev == 0 || prev != next); SASSERT(next == 0 || prev != next); SASSERT(prev != n); SASSERT(next != n); if (prev != 0) { SASSERT(m_leaf_head != n); prev->set_next(next); n->set_prev(0); } else if (m_leaf_head == n) { m_leaf_head = next; } if (next != 0) { SASSERT(m_leaf_tail != n); next->set_prev(prev); n->set_next(0); } else if (m_leaf_tail == n) { m_leaf_tail = prev; } SASSERT(n->prev() == 0 && n->next() == 0); } template void context_t::collect_leaves(ptr_vector & leaves) const { // Copy all leaves to the given vector. ptr_buffer todo; if (m_root != 0) todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); node * c = n->first_child(); if (c == 0) { if (!n->inconsistent()) leaves.push_back(n); } else { while (c != 0) { SASSERT(c->parent() == n); todo.push_back(c); c = c->next_sibling(); } } } } template void context_t::del_unit_clauses() { unsigned sz = m_unit_clauses.size(); for (unsigned i = 0; i < sz; i++) dec_ref(UNTAG(ineq*, m_unit_clauses[i])); m_unit_clauses.reset(); } template void context_t::del_clauses(ptr_vector & cs) { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { del_clause(cs[i]); } cs.reset(); } template void context_t::del_clauses() { del_clauses(m_clauses); del_clauses(m_lemmas); } template void context_t::del_definitions() { unsigned sz = num_vars(); for (unsigned i = 0; i < sz; i++) { definition * d = m_defs[i]; if (d == 0) continue; switch (d->get_kind()) { case constraint::MONOMIAL: del_monomial(static_cast(d)); break; case constraint::POLYNOMIAL: del_sum(static_cast(d)); break; default: UNREACHABLE(); break; } } } template void context_t::display_constraints(std::ostream & out, bool use_star) const { // display definitions for (unsigned i = 0; i < num_vars(); i++) { if (is_definition(i)) { (*m_display_proc)(out, i); out << " = "; display_definition(out, m_defs[i], use_star); out << "\n"; } } // display units for (unsigned i = 0; i < m_unit_clauses.size(); i++) { ineq * a = UNTAG(ineq*, m_unit_clauses[i]); a->display(out, nm(), *m_display_proc); out << "\n"; } // display clauses for (unsigned i = 0; i < m_clauses.size(); i++) { m_clauses[i]->display(out, nm(), *m_display_proc); out << "\n"; } } // ----------------------------------- // // Propagation // // ----------------------------------- template void context_t::set_conflict(var x, node * n) { m_num_conflicts++; n->set_conflict(x); remove_from_leaf_dlist(n); } template bool context_t::may_propagate(bound * b, constraint * c, node * n) { SASSERT(b != 0 && c != 0); TRACE("may_propagate_bug", display(tout, b); tout << " | "; display(tout, c); tout << "\nresult: " << (b->timestamp() > c->timestamp()) << ", " << b->timestamp() << ", " << c->timestamp() << "\n";); return b->timestamp() >= c->timestamp(); } // Normalization for integer bounds template void context_t::normalize_bound(var x, numeral & val, bool lower, bool & open) { if (is_int(x)) { // adjust integer bound if (!nm().is_int(val)) open = false; // performing ceil/floor if (lower) { nm().ceil(val, val); } else { nm().floor(val, val); } if (open) { open = false; if (lower) { C::round_to_minus_inf(nm()); nm().inc(val); } else { C::round_to_plus_inf(nm()); nm().dec(val); } } } } template bool context_t::relevant_new_bound(var x, numeral const & k, bool lower, bool open, node * n) { try { bound * curr_lower = n->lower(x); bound * curr_upper = n->upper(x); SASSERT(curr_lower == 0 || curr_lower->x() == x); SASSERT(curr_upper == 0 || curr_upper->x() == x); TRACE("subpaving_relevant_bound", display(tout, x); tout << " " << (lower ? ">" : "<") << (open ? "" : "=") << " "; nm().display(tout, k); tout << "\n"; tout << "existing bounds:\n"; if (curr_lower) { display(tout, curr_lower); tout << "\n"; } if (curr_upper) { display(tout, curr_upper); tout << "\n"; }); if (lower) { // If new bound triggers a conflict, then it is relevant. if (curr_upper && (nm().gt(k, curr_upper->value()) || ((open || curr_upper->is_open()) && nm().eq(k, curr_upper->value())))) { TRACE("subpaving_relevant_bound", tout << "relevant because triggers conflict.\n";); return true; } // If m_epsilon is zero, then bound is relevant only if it improves existing bound. if (m_zero_epsilon && curr_lower != 0 && (nm().lt(k, curr_lower->value()) || ((curr_lower->is_open() || !open) && nm().eq(k, curr_lower->value())))) { // new lower bound does not improve existing bound TRACE("subpaving_relevant_bound", tout << "irrelevant because does not improve existing bound.\n";); return false; } if (curr_upper == 0 && nm().lt(m_max_bound, k)) { // new lower bound exceeds the :max-bound threshold TRACE("subpaving_relevant_bound", tout << "irrelevant because exceeds :max-bound threshold.\n";); return false; } if (!m_zero_epsilon && curr_lower != 0) { // check if: // new-lower > lower + m_epsilon * max(min(upper - lower, |lower|), 1) numeral & min = m_tmp1; numeral & abs_lower = m_tmp2; nm().set(abs_lower, curr_lower->value()); nm().abs(abs_lower); if (curr_upper != 0) { nm().sub(curr_upper->value(), curr_lower->value(), min); if (nm().lt(abs_lower, min)) nm().set(min, abs_lower); } else { nm().set(min, abs_lower); } numeral & delta = m_tmp3; nm().set(delta, 1); if (nm().gt(min, delta)) nm().set(delta, min); nm().mul(delta, m_epsilon, delta); nm().add(curr_lower->value(), delta, delta); TRACE("subpaving_relevant_bound_bug", tout << "k: "; nm().display(tout, k); tout << ", delta: "; nm().display(tout, delta); tout << "\n"; tout << "curr_lower: "; nm().display(tout, curr_lower->value()); tout << ", min: "; nm().display(tout, min); tout << "\n";); if (nm().le(k, delta)) { TRACE("subpaving_relevant_bound", tout << "irrelevant because does not improve existing bound to at least "; nm().display(tout, delta); tout << "\n";); return false; } } } else { // If new bound triggers a conflict, then it is relevant. if (curr_lower && (nm().gt(curr_lower->value(), k) || ((open || curr_lower->is_open()) && nm().eq(k, curr_lower->value())))) { TRACE("subpaving_relevant_bound", tout << "relevant because triggers conflict.\n";); return true; } // If m_epsilon is zero, then bound is relevant only if it improves existing bound. if (m_zero_epsilon && curr_upper != 0 && (nm().lt(curr_upper->value(), k) || ((curr_upper->is_open() || !open) && nm().eq(k, curr_upper->value())))) { // new upper bound does not improve existing bound TRACE("subpaving_relevant_bound", tout << "irrelevant because does not improve existing bound.\n";); return false; } if (curr_lower == 0 && nm().lt(k, m_minus_max_bound)) { // new upper bound exceeds the -:max-bound threshold TRACE("subpaving_relevant_bound", tout << "irrelevant because exceeds -:max-bound threshold.\n";); return false; } if (!m_zero_epsilon && curr_upper != 0) { // check if: // new-upper < upper - m_epsilon * max(min(upper - lower, |upper|), 1) numeral & min = m_tmp1; numeral & abs_upper = m_tmp2; nm().set(abs_upper, curr_upper->value()); nm().abs(abs_upper); if (curr_lower != 0) { nm().sub(curr_upper->value(), curr_lower->value(), min); if (nm().lt(abs_upper, min)) nm().set(min, abs_upper); } else { nm().set(min, abs_upper); } numeral & delta = m_tmp3; nm().set(delta, 1); if (nm().gt(min, delta)) nm().set(delta, min); nm().mul(delta, m_epsilon, delta); nm().sub(curr_upper->value(), delta, delta); if (nm().ge(k, delta)) { TRACE("subpaving_relevant_bound", tout << "irrelevant because does not improve existing bound to at least "; nm().display(tout, delta); tout << "\n";); return false; } } } TRACE("subpaving_relevant_bound", tout << "new bound is relevant\n";); return true; } catch (typename C::exception) { // arithmetic module failed. set_arith_failed(); return false; } } template bool context_t::is_zero(var x, node * n) const { // Return true if lower(x) == upper(x) == 0 at n bound * l = n->lower(x); bound * u = n->upper(x); return l != 0 && u != 0 && nm().is_zero(l->value()) && nm().is_zero(u->value()) && !l->is_open() && !u->is_open(); } template bool context_t::is_upper_zero(var x, node * n) const { // Return true if upper(x) is zero at node n bound * u = n->upper(x); return u != 0 && nm().is_zero(u->value()) && !u->is_open(); } template bool context_t::conflicting_bounds(var x, node * n) const { // Return true if upper(x) < lower(x) at node n bound * l = n->lower(x); bound * u = n->upper(x); return l != 0 && u != 0 && (nm().lt(u->value(), l->value()) || ((l->is_open() || u->is_open()) && nm().eq(u->value(), l->value()))); } /** \brief Return the truth value of the inequality t in node n. The result may be l_true (True), l_false (False), or l_undef(Unknown). */ template lbool context_t::value(ineq * t, node * n) { var x = t->x(); bound * u = n->upper(x); bound * l = n->lower(x); if (u == 0 && l == 0) return l_undef; else if (t->is_lower()) { if (u != 0 && (nm().lt(u->value(), t->value()) || ((u->is_open() || t->is_open()) && nm().eq(u->value(), t->value())))) return l_false; else if (l != 0 && (nm().gt(l->value(), t->value()) || ((l->is_open() || !t->is_open()) && nm().eq(l->value(), t->value())))) return l_true; else return l_undef; } else { if (l != 0 && (nm().gt(l->value(), t->value()) || ((l->is_open() || t->is_open()) && nm().eq(l->value(), t->value())))) return l_false; else if (u != 0 && (nm().lt(u->value(), t->value()) || ((u->is_open() || !t->is_open()) && nm().eq(u->value(), t->value())))) return l_true; else return l_undef; } } template void context_t::propagate_clause(clause * c, node * n) { TRACE("propagate_clause", tout << "propagate using:\n"; display(tout, c); tout << "\n";); m_num_visited++; c->set_visited(m_timestamp); unsigned sz = c->size(); unsigned j = UINT_MAX; for (unsigned i = 0; i < sz; i++) { ineq * atom = (*c)[i]; switch (value(atom, n)) { case l_true: return; // clause was already satisfied at n case l_false: break; case l_undef: if (j != UINT_MAX) return; // clause has more than one unassigned literal j = i; break; } } if (j == UINT_MAX) { // Clause is in conflict, use first atom to trigger inconsistency j = 0; } ineq * a = (*c)[j]; TRACE("propagate_clause", tout << "propagating inequality: "; display(tout, a); tout << "\n";); propagate_bound(a->x(), a->value(), a->is_lower(), a->is_open(), n, justification(c)); // A clause can propagate only once. // So, we can safely set its timestamp again to avoid another useless visit. c->set_visited(m_timestamp); } template void context_t::propagate_polynomial(var x, node * n, var y) { SASSERT(y != null_var); SASSERT(is_polynomial(x)); polynomial * p = get_polynomial(x); unsigned sz = p->size(); interval & r = m_i_tmp1; r.set_mutable(); interval & v = m_i_tmp2; interval & av = m_i_tmp3; av.set_mutable(); if (x == y) { for (unsigned i = 0; i < sz; i++) { var z = p->x(i); v.set_constant(n, z); im().mul(p->a(i), v, av); if (i == 0) im().set(r, av); else im().add(r, av, r); } // r contains the deduced bounds for x == y } else { v.set_constant(n, x); numeral & a = m_tmp1; im().set(r, v); for (unsigned i = 0; i < sz; i++) { var z = p->x(i); if (z != y) { v.set_constant(n, z); im().mul(p->a(i), v, av); im().sub(r, av, r); } else { nm().set(a, p->a(i)); TRACE("propagate_polynomial_bug", tout << "a: "; nm().display(tout, a); tout << "\n";); } } TRACE("propagate_polynomial_bug", tout << "r before mul 1/a: "; im().display(tout, r); tout << "\n";); im().div(r, a, r); TRACE("propagate_polynomial_bug", tout << "r after mul 1/a: "; im().display(tout, r); tout << "\n";); // r contains the deduced bounds for y. } // r contains the deduced bounds for y. if (!r.m_l_inf) { normalize_bound(y, r.m_l_val, true, r.m_l_open); if (relevant_new_bound(y, r.m_l_val, true, r.m_l_open, n)) { propagate_bound(y, r.m_l_val, true, r.m_l_open, n, justification(x)); if (inconsistent(n)) return; } } if (!r.m_u_inf) { normalize_bound(y, r.m_u_val, false, r.m_u_open); if (relevant_new_bound(y, r.m_u_val, false, r.m_u_open, n)) propagate_bound(y, r.m_u_val, false, r.m_u_open, n, justification(x)); } } template void context_t::propagate_polynomial(var x, node * n) { TRACE("propagate_polynomial", tout << "propagate_polynomial: "; display(tout, x); tout << "\n";); TRACE("propagate_polynomial_detail", display_bounds(tout, n);); SASSERT(is_polynomial(x)); polynomial * p = get_polynomial(x); p->set_visited(m_timestamp); var unbounded_var = null_var; if (is_unbounded(x, n)) unbounded_var = x; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { var y = p->x(i); if (is_unbounded(y, n)) { if (unbounded_var != null_var) return; // no propagation is possible. unbounded_var = y; } } TRACE("propagate_polynomial", tout << "unbounded_var: "; display(tout, unbounded_var); tout << "\n";); if (unbounded_var != null_var) { propagate_polynomial(x, n, unbounded_var); } else { propagate_polynomial(x, n, x); for (unsigned i = 0; i < sz; i++) { if (inconsistent(n)) return; propagate_polynomial(x, n, p->x(i)); } } } template void context_t::propagate_monomial(var x, node * n) { TRACE("propagate_monomial", tout << "propagate_monomial: "; display(tout, x); tout << "\n";); SASSERT(is_monomial(x)); SASSERT(!inconsistent(n)); monomial * m = get_monomial(x); m->set_visited(m_timestamp); bool found_unbounded = false; bool found_zero = false; bool x_is_unbounded = false; unsigned sz = m->size(); for (unsigned i = 0; i < sz; i++) { var y = m->x(i); if (is_zero(y, n)) { found_zero = true; } if (m->degree(i) % 2 == 0) { if (is_upper_zero(y, n)) { found_zero = true; } continue; // elements with even power always produce a lower bound } if (is_unbounded(y, n)) { found_unbounded = true; } } TRACE("propagate_monomial", tout << "found_zero: " << found_zero << ", found_unbounded: " << found_unbounded << "\n";); if (found_zero) { if (!is_zero(x, n)) { // x must be zero numeral & zero = m_tmp1; nm().set(zero, 0); propagate_bound(x, zero, true, false, n, justification(x)); if (inconsistent(n)) return; propagate_bound(x, zero, false, false, n, justification(x)); } // no need to downward propagation return; } x_is_unbounded = n->is_unbounded(x); if (!found_unbounded) propagate_monomial_upward(x, n); if (inconsistent(n)) return; if (!x_is_unbounded) { unsigned bad_pos = UINT_MAX; interval & aux = m_i_tmp1; for (unsigned i = 0; i < sz; i++) { aux.set_constant(n, m->x(i)); if (im().contains_zero(aux)) { if (bad_pos != UINT_MAX) return; // there is more than one position that contains zero, so downward propagation is not possible. bad_pos = i; } } if (bad_pos == UINT_MAX) { // we can use all variables for downward propagation. for (unsigned i = 0; i < sz; i++) { if (inconsistent(n)) return; propagate_monomial_downward(x, n, i); } } else { propagate_monomial_downward(x, n, bad_pos); } } } template void context_t::propagate_monomial_upward(var x, node * n) { SASSERT(is_monomial(x)); monomial * m = get_monomial(x); unsigned sz = m->size(); interval & r = m_i_tmp1; r.set_mutable(); interval & y = m_i_tmp2; interval & yk = m_i_tmp3; yk.set_mutable(); for (unsigned i = 0; i < sz; i++) { y.set_constant(n, m->x(i)); im().power(y, m->degree(i), yk); if (i == 0) im().set(r, yk); else im().mul(r, yk, r); } // r contains the new bounds for x if (!r.m_l_inf) { normalize_bound(x, r.m_l_val, true, r.m_l_open); if (relevant_new_bound(x, r.m_l_val, true, r.m_l_open, n)) { propagate_bound(x, r.m_l_val, true, r.m_l_open, n, justification(x)); if (inconsistent(n)) return; } } if (!r.m_u_inf) { normalize_bound(x, r.m_u_val, false, r.m_u_open); if (relevant_new_bound(x, r.m_u_val, false, r.m_u_open, n)) propagate_bound(x, r.m_u_val, false, r.m_u_open, n, justification(x)); } } template void context_t::propagate_monomial_downward(var x, node * n, unsigned j) { TRACE("propagate_monomial", tout << "propagate_monomial_downward: "; display(tout, x); tout << ", j: " << j << "\n"; display(tout, get_monomial(x)); tout << "\n";); SASSERT(is_monomial(x)); monomial * m = get_monomial(x); SASSERT(j < m->size()); unsigned sz = m->size(); interval & r = m_i_tmp3; if (sz > 1) { interval & d = m_i_tmp1; d.set_mutable(); interval & y = m_i_tmp2; interval & yk = m_i_tmp3; yk.set_mutable(); bool first = true; for (unsigned i = 0; i < sz; i++) { if (i == j) continue; y.set_constant(n, m->x(i)); im().power(y, m->degree(i), yk); if (first) im().set(d, yk); else im().mul(d, yk, r); } interval & aux = m_i_tmp2; aux.set_constant(n, x); im().div(aux, d, r); } else { SASSERT(sz == 1); SASSERT(j == 0); interval & aux = m_i_tmp2; aux.set_constant(n, x); im().set(r, aux); } unsigned d = m->degree(j); if (d > 1) { if (d % 2 == 0 && im().lower_is_neg(r)) return; // If d is even, we can't take the nth-root when lower(r) is negative. im().xn_eq_y(r, d, m_nth_root_prec, r); } var y = m->x(j); // r contains the new bounds for y if (!r.m_l_inf) { normalize_bound(y, r.m_l_val, true, r.m_l_open); if (relevant_new_bound(y, r.m_l_val, true, r.m_l_open, n)) { propagate_bound(y, r.m_l_val, true, r.m_l_open, n, justification(x)); if (inconsistent(n)) return; } } if (!r.m_u_inf) { normalize_bound(y, r.m_u_val, false, r.m_u_open); if (relevant_new_bound(y, r.m_u_val, false, r.m_u_open, n)) propagate_bound(y, r.m_u_val, false, r.m_u_open, n, justification(x)); } } template bool context_t::most_recent(bound * b, node * n) const { var x = b->x(); if (b->is_lower()) return n->lower(x) == b; else return n->upper(x) == b; } template void context_t::add_recent_bounds(node * n) { SASSERT(m_queue.empty()); bound * old_b = n->parent_trail_stack(); bound * b = n->trail_stack(); while (b != old_b) { if (most_recent(b, n)) { b->set_timestamp(m_timestamp); m_queue.push_back(b); } b = b->prev(); } } template void context_t::propagate_def(var x, node * n) { SASSERT(is_definition(x)); m_num_visited++; definition * d = m_defs[x]; switch (d->get_kind()) { case constraint::MONOMIAL: propagate_monomial(x, n); break; case constraint::POLYNOMIAL: propagate_polynomial(x, n); break; default: break; } } template void context_t::propagate(node * n, bound * b) { var x = b->x(); TRACE("subpaving_propagate", tout << "propagate: "; display(tout, b); tout << ", timestamp: " << b->timestamp() << "\n";); typename watch_list::const_iterator it = m_wlist[x].begin(); typename watch_list::const_iterator end = m_wlist[x].end(); for (; it != end; ++it) { if (inconsistent(n)) return; watched const & w = *it; try { if (w.is_clause()) { clause * c = w.get_clause(); if (may_propagate(b, c, n)) { propagate_clause(c, n); } } else { var y = w.get_var(); definition * d = m_defs[y]; if (may_propagate(b, d, n)) { propagate_def(y, n); } } } catch (typename C::exception) { // arithmetic module failed, ignore constraint set_arith_failed(); } } if (inconsistent(n)) return; if (is_definition(x)) { definition * d = m_defs[x]; if (may_propagate(b, d, n)) { propagate_def(x, n); } } } template void context_t::propagate(node * n) { while (m_qhead < m_queue.size()) { if (inconsistent(n)) break; checkpoint(); bound * b = m_queue[m_qhead]; SASSERT(is_bound_of(b, n)); m_qhead++; propagate(n, b); } m_queue.reset(); m_qhead = 0; } template void context_t::propagate_all_definitions(node * n) { unsigned num = num_vars(); for (unsigned x = 0; x < num; x++) { if (inconsistent(n)) break; if (is_definition(x)) propagate_def(x, n); } } // ----------------------------------- // // Main // // ----------------------------------- template void context_t::assert_units(node * n) { typename ptr_vector::const_iterator it = m_unit_clauses.begin(); typename ptr_vector::const_iterator end = m_unit_clauses.end(); for (; it != end; ++it) { checkpoint(); ineq * a = UNTAG(ineq*, *it); bool axiom = GET_TAG(*it) != 0; TRACE("subpaving_init", tout << "asserting: "; display(tout, a); tout << ", axiom: " << axiom << "\n";); propagate_bound(a->x(), a->value(), a->is_lower(), a->is_open(), n, justification(axiom)); if (inconsistent(n)) break; } TRACE("subpaving_init", tout << "bounds after init\n"; display_bounds(tout, n);); } template void context_t::init() { SASSERT(m_root == 0); SASSERT(m_leaf_head == 0); SASSERT(m_leaf_tail == 0); m_timestamp = 0; m_root = mk_node(); SASSERT(m_leaf_head == m_root); SASSERT(m_leaf_tail == m_root); TRACE("subpaving_init", display_constraints(tout);); assert_units(m_root); propagate_all_definitions(m_root); propagate(m_root); TRACE("subpaving_init", tout << "root bounds after propagation\n"; display_bounds(tout, m_root);); SASSERT(check_invariant()); } template void context_t::operator()() { if (m_root == 0) init(); TRACE("subpaving_stats", statistics st; collect_statistics(st); tout << "statistics:\n"; st.display_smt2(tout);); TRACE("subpaving_main", display_params(tout);); while (m_leaf_head != 0) { checkpoint(); SASSERT(m_queue.empty()); if (m_num_nodes > m_max_nodes) break; node * n = (*m_node_selector)(m_leaf_head, m_leaf_tail); if (n == 0) break; TRACE("subpaving_main", tout << "selected node: #" << n->id() << ", depth: " << n->depth() << "\n";); remove_from_leaf_dlist(n); if (n != m_root) { add_recent_bounds(n); propagate(n); } TRACE("subpaving_main", tout << "node #" << n->id() << " after propagation\n"; display_bounds(tout, n);); if (n->inconsistent()) { TRACE("subpaving_main", tout << "node #" << n->id() << " is inconsistent.\n";); // TODO: conflict resolution continue; } if (n->depth() >= m_max_depth) { TRACE("subpaving_main", tout << "maximum depth reached, skipping node #" << n->id() << "\n";); continue; } var x = (*m_var_selector)(n); TRACE("subpaving_main", tout << "splitting variable: "; display(tout, x); tout << "\n";); if (x != null_var) { (*m_node_splitter)(n, x); m_num_splits++; // remove inconsistent children } } TRACE("subpaving_stats", statistics st; collect_statistics(st); tout << "statistics:\n"; st.display_smt2(tout);); } template void context_t::display_bounds(std::ostream & out) const { ptr_vector leaves; collect_leaves(leaves); typename ptr_vector::const_iterator it = leaves.begin(); typename ptr_vector::const_iterator end = leaves.end(); for (bool first = true; it != end; ++it) { node * n = *it; if (first) first = false; else out << "=========\n"; display_bounds(out, n); } } // ----------------------------------- // // Statistics // // ----------------------------------- template void context_t::reset_statistics() { m_num_conflicts = 0; m_num_mk_bounds = 0; m_num_splits = 0; m_num_visited = 0; } template void context_t::collect_statistics(statistics & st) const { st.update("conflicts", m_num_conflicts); st.update("new bounds", m_num_mk_bounds); st.update("splits", m_num_splits); st.update("nodes", m_num_nodes); st.update("visited", m_num_visited); } // ----------------------------------- // // Debugging support // // ----------------------------------- template bool context_t::is_bound_of(bound * b, node * n) const { bound * c = n->trail_stack(); while (c != 0) { if (c == b) return true; if (c->timestamp() <= b->timestamp()) return false; c = c->prev(); } return false; } template bool context_t::check_leaf_dlist() const { node * n = m_leaf_head; while (n != 0) { node * next = n->next(); SASSERT(next != 0 || m_leaf_tail == n); SASSERT(next == 0 || next->prev() == n); n = next; } return true; } template bool context_t::check_tree() const { ptr_buffer todo; if (m_root != 0) todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); node * c = n->first_child(); while (c != 0) { SASSERT(c->parent() == n); todo.push_back(c); c = c->next_sibling(); } } return true; } template bool context_t::check_invariant() const { SASSERT(check_tree()); SASSERT(check_leaf_dlist()); return true; } }; z3-z3-4.4.1/src/math/subpaving/subpaving_types.h000066400000000000000000000020231260446376700215330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_types.h Abstract: Subpaving auxiliary types. Author: Leonardo de Moura (leonardo) 2012-08-07. Revision History: --*/ #ifndef SUBPAVING_TYPES_H_ #define SUBPAVING_TYPES_H_ namespace subpaving { class ineq; typedef unsigned var; const var null_var = UINT_MAX; class exception { }; class power : public std::pair { public: power():std::pair() {} power(var v, unsigned d):std::pair(v, d) {} power(power const & p):std::pair(p) {} var x() const { return first; } var get_var() const { return first; } unsigned degree() const { return second; } unsigned & degree() { return second; } void set_var(var x) { first = x; } struct lt_proc { bool operator()(power const & p1, power const & p2) { return p1.get_var() < p2.get_var(); } }; }; struct display_var_proc { virtual void operator()(std::ostream & out, var x) const { out << "x" << x; } }; }; #endif z3-z3-4.4.1/src/math/subpaving/tactic/000077500000000000000000000000001260446376700174125ustar00rootroot00000000000000z3-z3-4.4.1/src/math/subpaving/tactic/expr2subpaving.cpp000066400000000000000000000263451260446376700231070ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: expr2subpaving.cpp Abstract: Translator from Z3 expressions into generic subpaving data-structure. Author: Leonardo (leonardo) 2012-08-08 Notes: --*/ #include"expr2subpaving.h" #include"expr2var.h" #include"ref_util.h" #include"z3_exception.h" #include"cooperate.h" #include"arith_decl_plugin.h" #include"scoped_numeral_buffer.h" struct expr2subpaving::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) {} }; ast_manager & m_manager; subpaving::context & m_subpaving; unsynch_mpq_manager & m_qm; arith_util m_autil; expr2var * m_expr2var; bool m_expr2var_owner; expr_ref_vector m_var2expr; typedef svector var_vector; obj_map m_cache; var_vector m_cached_vars; scoped_mpz_vector m_cached_numerators; scoped_mpz_vector m_cached_denominators; obj_map m_lit_cache; volatile bool m_cancel; imp(ast_manager & m, subpaving::context & s, expr2var * e2v): m_manager(m), m_subpaving(s), m_qm(s.qm()), m_autil(m), m_var2expr(m), m_cached_numerators(m_qm), m_cached_denominators(m_qm) { if (e2v == 0) { m_expr2var = alloc(expr2var, m); m_expr2var_owner = true; } else { m_expr2var = e2v; m_expr2var_owner = false; } m_cancel = false; } ~imp() { reset_cache(); if (m_expr2var_owner) dealloc(m_expr2var); } ast_manager & m() { return m_manager; } subpaving::context & s() { return m_subpaving; } unsynch_mpq_manager & qm() const { return m_qm; } void reset_cache() { dec_ref_map_keys(m(), m_cache); m_cached_vars.reset(); m_cached_numerators.reset(); m_cached_denominators.reset(); dec_ref_map_key_values(m(), s(), m_lit_cache); } void checkpoint() { if (m_cancel) throw default_exception("canceled"); cooperate("expr2subpaving"); } subpaving::var mk_var_for(expr * t) { SASSERT(!m_autil.is_numeral(t)); subpaving::var x = m_expr2var->to_var(t); if (x == subpaving::null_var) { bool is_int = m_autil.is_int(t); x = s().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); } return x; } void found_non_simplified() { throw default_exception("you must apply simplifier before internalizing expressions into the subpaving module."); } 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 cache_result(expr * t, subpaving::var x, mpz const & n, mpz const & d) { SASSERT(!m_cache.contains(t)); SASSERT(m_cached_numerators.size() == m_cached_vars.size()); SASSERT(m_cached_denominators.size() == m_cached_vars.size()); if (t->get_ref_count() <= 1) return; unsigned idx = m_cached_vars.size(); m_cache.insert(t, idx); m().inc_ref(t); m_cached_vars.push_back(x); m_cached_numerators.push_back(n); m_cached_denominators.push_back(d); } subpaving::var process_num(app * t, unsigned depth, mpz & n, mpz & d) { rational k; VERIFY(m_autil.is_numeral(t, k)); qm().set(n, k.to_mpq().numerator()); qm().set(d, k.to_mpq().denominator()); return subpaving::null_var; } // Put t as a^k. void as_power(expr * t, expr * & a, unsigned & k) { if (!m_autil.is_power(t)) { a = t; k = 1; return; } rational _k; if (!m_autil.is_numeral(to_app(t)->get_arg(1), _k) || !_k.is_int() || !_k.is_unsigned()) { a = t; k = 1; return; } a = to_app(t)->get_arg(0); k = _k.get_unsigned(); } subpaving::var process_mul(app * t, unsigned depth, mpz & n, mpz & d) { unsigned num_args = t->get_num_args(); if (num_args <= 1) found_non_simplified(); rational k; expr * m; if (m_autil.is_numeral(t->get_arg(0), k)) { if (num_args != 2) found_non_simplified(); qm().set(n, k.to_mpq().numerator()); qm().set(d, k.to_mpq().denominator()); m = t->get_arg(1); } else { qm().set(n, 1); qm().set(d, 1); m = t; } expr * const * margs; unsigned sz; if (m_autil.is_mul(m)) { margs = to_app(m)->get_args(); sz = to_app(m)->get_num_args(); } else { margs = &m; sz = 1; } scoped_mpz n_arg(qm()); scoped_mpz d_arg(qm()); sbuffer pws; for (unsigned i = 0; i < sz; i++) { expr * arg = margs[i]; unsigned k; as_power(arg, arg, k); subpaving::var x_arg = process(arg, depth+1, n_arg, d_arg); qm().power(n_arg, k, n_arg); qm().power(d_arg, k, d_arg); qm().mul(n, n_arg, n); qm().mul(d, d_arg, d); if (x_arg != subpaving::null_var) pws.push_back(subpaving::power(x_arg, k)); } subpaving::var x; if (pws.empty()) x = subpaving::null_var; else if (pws.size() == 1 && pws[0].degree() == 1) x = pws[0].get_var(); else x = s().mk_monomial(pws.size(), pws.c_ptr()); cache_result(t, x, n, d); return x; } typedef _scoped_numeral_buffer mpz_buffer; typedef sbuffer var_buffer; subpaving::var process_add(app * t, unsigned depth, mpz & n, mpz & d) { unsigned num_args = t->get_num_args(); mpz_buffer ns(qm()), ds(qm()); var_buffer xs; scoped_mpq c(qm()), c_arg(qm()); scoped_mpz n_arg(qm()), d_arg(qm()); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); subpaving::var x_arg = process(arg, depth+1, n_arg, d_arg); if (x_arg == subpaving::null_var) { qm().set(c_arg, n_arg, d_arg); qm().add(c, c_arg, c); } else { xs.push_back(x_arg); ns.push_back(n_arg); ds.push_back(d_arg); } } qm().set(d, c.get().denominator()); unsigned sz = xs.size(); for (unsigned i = 0; i < sz; i++) { qm().lcm(d, ds[i], d); } scoped_mpz & k = d_arg; qm().div(d, c.get().denominator(), k); scoped_mpz sum_c(qm()); qm().mul(c.get().numerator(), k, sum_c); for (unsigned i = 0; i < sz; i++) { qm().div(d, ds[i], k); qm().mul(ns[i], k, ns[i]); } subpaving::var x; if (sz == 0) { qm().set(n, sum_c); x = subpaving::null_var; } else { x = s().mk_sum(sum_c, sz, ns.c_ptr(), xs.c_ptr()); qm().set(n, 1); } cache_result(t, x, n, d); return x; } subpaving::var process_power(app * t, unsigned depth, mpz & n, mpz & d) { rational k; SASSERT(t->get_num_args() == 2); if (!m_autil.is_numeral(t->get_arg(1), k) || !k.is_int() || !k.is_unsigned()) { qm().set(n, 1); qm().set(d, 1); return mk_var_for(t); } unsigned _k = k.get_unsigned(); subpaving::var x = process(t->get_arg(0), depth+1, n, d); if (x != subpaving::null_var) { subpaving::power p(x, _k); x = s().mk_monomial(1, &p); } qm().power(n, _k, n); qm().power(d, _k, d); cache_result(t, x, n, d); return x; } subpaving::var process_arith_app(app * t, unsigned depth, mpz & n, mpz & d) { SASSERT(m_autil.is_arith_expr(t)); switch (t->get_decl_kind()) { case OP_NUM: return process_num(t, depth, n, d); case OP_ADD: return process_add(t, depth, n, d); case OP_MUL: return process_mul(t, depth, n, d); case OP_POWER: return process_power(t, depth, n, d); case OP_TO_REAL: return process(t->get_arg(0), depth+1, n, d); case OP_SUB: case OP_UMINUS: found_non_simplified(); break; case OP_TO_INT: case OP_DIV: case OP_IDIV: case OP_MOD: case OP_REM: case OP_IRRATIONAL_ALGEBRAIC_NUM: throw default_exception("you must apply arithmetic purifier before internalizing expressions into the subpaving module."); case OP_SIN: case OP_COS: case OP_TAN: case OP_ASIN: case OP_ACOS: case OP_ATAN: case OP_SINH: case OP_COSH: case OP_TANH: case OP_ASINH: case OP_ACOSH: case OP_ATANH: // TODO throw default_exception("transcendental and hyperbolic functions are not supported yet."); default: UNREACHABLE(); } return subpaving::null_var; } subpaving::var process(expr * t, unsigned depth, mpz & n, mpz & d) { SASSERT(is_int_real(t)); checkpoint(); if (is_cached(t)) { unsigned idx = m_cache.find(t); qm().set(n, m_cached_numerators[idx]); qm().set(d, m_cached_denominators[idx]); return m_cached_vars[idx]; } SASSERT(!is_quantifier(t)); if (::is_var(t) || !m_autil.is_arith_expr(t)) { qm().set(n, 1); qm().set(d, 1); return mk_var_for(t); } return process_arith_app(to_app(t), depth, n, d); } bool is_var(expr * t) const { return m_expr2var->is_var(t); } void set_cancel(bool f) { m_cancel = f; } subpaving::var internalize_term(expr * t, mpz & n, mpz & d) { return process(t, 0, n, d); } }; expr2subpaving::expr2subpaving(ast_manager & m, subpaving::context & s, expr2var * e2v) { m_imp = alloc(imp, m, s, e2v); } expr2subpaving::~expr2subpaving() { dealloc(m_imp); } ast_manager & expr2subpaving::m() const { return m_imp->m(); } subpaving::context & expr2subpaving::s() const { return m_imp->s(); } bool expr2subpaving::is_var(expr * t) const { return m_imp->is_var(t); } void expr2subpaving::set_cancel(bool f) { m_imp->set_cancel(f); } subpaving::var expr2subpaving::internalize_term(expr * t, mpz & n, mpz & d) { return m_imp->internalize_term(t, n, d); } z3-z3-4.4.1/src/math/subpaving/tactic/expr2subpaving.h000066400000000000000000000022061260446376700225420ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: expr2subpaving.h Abstract: Translator from Z3 expressions into generic subpaving data-structure. Author: Leonardo (leonardo) 2012-08-08 Notes: --*/ #ifndef EXPR2SUBPAVING_H_ #define EXPR2SUBPAVING_H_ #include"ast.h" #include"subpaving.h" class expr2var; class expr2subpaving { struct imp; imp * m_imp; public: expr2subpaving(ast_manager & m, subpaving::context & s, expr2var * e2v = 0); ~expr2subpaving(); ast_manager & m() const; subpaving::context & s() const; /** \brief Return true if t was encoded as a variable by the translator. */ bool is_var(expr * t) const; /** \brief Cancel/Interrupt execution. */ void set_cancel(bool f); /** \brief Internalize a Z3 arithmetical expression into the subpaving data-structure. \remark throws subpaving::exception there is a translation error (when using imprecise representations, i.e. floats, in the subpaving module) */ subpaving::var internalize_term(expr * t, /* out */ mpz & n, /* out */ mpz & d); }; #endif z3-z3-4.4.1/src/math/subpaving/tactic/subpaving_tactic.cpp000066400000000000000000000225601260446376700234500ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_tactic.cpp Abstract: "Fake" tactic used to test subpaving module. Author: Leonardo de Moura (leonardo) 2012-08-07. Revision History: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"expr2subpaving.h" #include"expr2var.h" #include"arith_decl_plugin.h" #include"ast_smt2_pp.h" #include"hwf.h" #include"mpff.h" #include"mpfx.h" #include"f2n.h" class subpaving_tactic : public tactic { struct display_var_proc : public subpaving::display_var_proc { expr_ref_vector m_inv; display_var_proc(expr2var & e2v):m_inv(e2v.m()) { e2v.mk_inv(m_inv); } ast_manager & m() const { return m_inv.get_manager(); } virtual void operator()(std::ostream & out, subpaving::var x) const { expr * t = m_inv.get(x, 0); if (t != 0) out << mk_ismt2_pp(t, m()); else out << "k!" << x; } }; struct imp { enum engine_kind { MPQ, MPF, HWF, MPFF, MPFX, NONE }; ast_manager & m_manager; unsynch_mpq_manager m_qm; mpf_manager m_fm_core; f2n m_fm; hwf_manager m_hm_core; f2n m_hm; mpff_manager m_ffm; mpfx_manager m_fxm; arith_util m_autil; engine_kind m_kind; scoped_ptr m_ctx; scoped_ptr m_proc; expr2var m_e2v; scoped_ptr m_e2s; bool m_display; imp(ast_manager & m, params_ref const & p): m_manager(m), m_fm(m_fm_core), m_hm(m_hm_core), m_autil(m), m_kind(NONE), m_e2v(m) { updt_params(p); } ast_manager & m() const { return m_manager; } void collect_param_descrs(param_descrs & r) { m_ctx->collect_param_descrs(r); // #ifndef _EXTERNAL_RELEASE r.insert("numeral", CPK_SYMBOL, "(default: mpq) options: mpq, mpf, hwf, mpff, mpfx."); r.insert("print_nodes", CPK_BOOL, "(default: false) display subpaving tree leaves."); // #endif } void updt_params(params_ref const & p) { m_display = p.get_bool("print_nodes", false); symbol engine = p.get_sym("numeral", symbol("mpq")); engine_kind new_kind; if (engine == "mpq") new_kind = MPQ; else if (engine == "mpf") new_kind = MPF; else if (engine == "mpff") new_kind = MPFF; else if (engine == "mpfx") new_kind = MPFX; else new_kind = HWF; if (m_kind != new_kind) { m_kind = new_kind; switch (m_kind) { case MPQ: m_ctx = subpaving::mk_mpq_context(m_qm); break; case MPF: m_ctx = subpaving::mk_mpf_context(m_fm); break; case HWF: m_ctx = subpaving::mk_hwf_context(m_hm, m_qm); break; case MPFF: m_ctx = subpaving::mk_mpff_context(m_ffm, m_qm); break; case MPFX: m_ctx = subpaving::mk_mpfx_context(m_fxm, m_qm); break; default: UNREACHABLE(); break; } m_e2s = alloc(expr2subpaving, m_manager, *m_ctx, &m_e2v); } m_ctx->updt_params(p); } void collect_statistics(statistics & st) const { m_ctx->collect_statistics(st); } void reset_statistics() { m_ctx->reset_statistics(); } void set_cancel(bool f) { m_e2s->set_cancel(f); m_ctx->set_cancel(f); } subpaving::ineq * mk_ineq(expr * a) { bool neg = false; while (m().is_not(a, a)) neg = !neg; bool lower; bool open = false; if (m_autil.is_le(a)) { lower = false; } else if (m_autil.is_ge(a)) { lower = true; } else { throw tactic_exception("unsupported atom"); } if (neg) { lower = !lower; open = !open; } rational _k; if (!m_autil.is_numeral(to_app(a)->get_arg(1), _k)) throw tactic_exception("use simplify tactic with option :arith-lhs true"); scoped_mpq k(m_qm); k = _k.to_mpq(); scoped_mpz n(m_qm), d(m_qm); subpaving::var x = m_e2s->internalize_term(to_app(a)->get_arg(0), n, d); m_qm.mul(d, k, k); m_qm.div(k, n, k); if (is_neg(n)) lower = !lower; TRACE("subpaving_tactic", tout << x << " " << k << " " << lower << " " << open << "\n";); return m_ctx->mk_ineq(x, k, lower, open); } void process_clause(expr * c) { expr * const * args = 0; unsigned sz; if (m().is_or(c)) { args = to_app(c)->get_args(); sz = to_app(c)->get_num_args(); } else { args = &c; sz = 1; } ref_buffer ineq_buffer(*m_ctx); for (unsigned i = 0; i < sz; i++) { ineq_buffer.push_back(mk_ineq(args[i])); } m_ctx->add_clause(sz, ineq_buffer.c_ptr()); } void internalize(goal const & g) { try { for (unsigned i = 0; i < g.size(); i++) { process_clause(g.form(i)); } } catch (subpaving::exception) { throw tactic_exception("failed to internalize goal into subpaving module"); } } void process(goal const & g) { internalize(g); m_proc = alloc(display_var_proc, m_e2v); m_ctx->set_display_proc(m_proc.get()); try { (*m_ctx)(); } catch (subpaving::exception) { throw tactic_exception("failed building subpaving tree..."); } if (m_display) { m_ctx->display_constraints(std::cout); std::cout << "bounds at leaves: \n"; m_ctx->display_bounds(std::cout); } } }; imp * m_imp; params_ref m_params; statistics m_stats; public: subpaving_tactic(ast_manager & m, params_ref const & p): m_imp(alloc(imp, m, p)), m_params(p) { } virtual ~subpaving_tactic() { dealloc(m_imp); } virtual tactic * translate(ast_manager & m) { return alloc(subpaving_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 collect_statistics(statistics & st) const { st.copy(m_stats); } virtual void reset_statistics() { m_stats.reset(); } 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->process(*in); m_imp->collect_statistics(m_stats); result.reset(); result.push_back(in.get()); mc = 0; pc = 0; core = 0; } catch (z3_exception & ex) { // convert all Z3 exceptions into tactic exceptions throw tactic_exception(ex.msg()); } } 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); #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_subpaving_tactic_core(ast_manager & m, params_ref const & p) { return alloc(subpaving_tactic, m, p); } tactic * mk_subpaving_tactic(ast_manager & m, params_ref const & p) { params_ref simp_p = p; simp_p.set_bool("arith_lhs", true); simp_p.set_bool("expand_power", true); simp_p.set_uint("max_power", UINT_MAX); simp_p.set_bool("som", true); simp_p.set_bool("eq2ineq", true); simp_p.set_bool("elim_and", true); simp_p.set_bool("blast_distinct", true); params_ref simp2_p = p; simp2_p.set_bool("mul_to_power", true); return and_then(using_params(mk_simplify_tactic(m, p), simp_p), using_params(mk_simplify_tactic(m, p), simp2_p), mk_subpaving_tactic_core(m, p)); } z3-z3-4.4.1/src/math/subpaving/tactic/subpaving_tactic.h000066400000000000000000000010201260446376700231010ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_tactic.h Abstract: "Fake" tactic used to test subpaving module. Author: Leonardo de Moura (leonardo) 2012-08-07. Revision History: --*/ #ifndef SUBPAVING_TACTIC_H_ #define SUBPAVING_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_subpaving_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("subpaving", "tactic for testing subpaving module.", "mk_subpaving_tactic(m, p)") */ #endif z3-z3-4.4.1/src/model/000077500000000000000000000000001260446376700143145ustar00rootroot00000000000000z3-z3-4.4.1/src/model/func_interp.cpp000066400000000000000000000211541260446376700173370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: func_interp.cpp Abstract: See func_interp.h Author: Leonardo de Moura (leonardo) 2010-12-30. Revision History: --*/ #include"func_interp.h" #include"var_subst.h" #include"obj_hashtable.h" #include"ast_pp.h" #include"ast_smt2_pp.h" func_entry::func_entry(ast_manager & m, unsigned arity, expr * const * args, expr * result): m_args_are_values(true), m_result(result) { SASSERT(is_ground(result)); m.inc_ref(result); for (unsigned i = 0; i < arity; i++) { expr * arg = args[i]; SASSERT(is_ground(arg)); if (!m.is_value(arg)) m_args_are_values = false; m.inc_ref(arg); m_args[i] = arg; } } func_entry * func_entry::mk(ast_manager & m, unsigned arity, expr * const * args, expr * result) { small_object_allocator & allocator = m.get_allocator(); unsigned sz = get_obj_size(arity); void * mem = allocator.allocate(sz); return new (mem) func_entry(m, arity, args, result); } void func_entry::set_result(ast_manager & m, expr * r) { m.inc_ref(r); m.dec_ref(m_result); m_result = r; } bool func_entry::eq_args(ast_manager & m, unsigned arity, expr * const * args) const { unsigned i = 0; for (; i < arity; i++) { if (!m.are_equal(m_args[i], args[i])) return false; } return true; } void func_entry::deallocate(ast_manager & m, unsigned arity) { for (unsigned i = 0; i < arity; i++) { m.dec_ref(m_args[i]); } m.dec_ref(m_result); small_object_allocator & allocator = m.get_allocator(); unsigned sz = get_obj_size(arity); allocator.deallocate(sz, this); } func_interp::func_interp(ast_manager & m, unsigned arity): m_manager(m), m_arity(arity), m_else(0), m_args_are_values(true), m_interp(0) { } func_interp::~func_interp() { ptr_vector::iterator it = m_entries.begin(); ptr_vector::iterator end = m_entries.end(); for (; it != end; ++it) { func_entry * curr = *it; curr->deallocate(m_manager, m_arity); } m_manager.dec_ref(m_else); m_manager.dec_ref(m_interp); } func_interp * func_interp::copy() const { func_interp * new_fi = alloc(func_interp, m_manager, m_arity); ptr_vector::const_iterator it = m_entries.begin(); ptr_vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { func_entry * curr = *it; new_fi->insert_new_entry(curr->get_args(), curr->get_result()); } new_fi->set_else(m_else); return new_fi; } void func_interp::reset_interp_cache() { m_manager.dec_ref(m_interp); m_interp = 0; } void func_interp::set_else(expr * e) { reset_interp_cache(); m_manager.inc_ref(e); m_manager.dec_ref(m_else); m_else = e; } /** \brief Return true if the interpretation represents the constant function. */ bool func_interp::is_constant() const { if (is_partial()) return false; if (!is_ground(m_else)) return false; ptr_vector::const_iterator it = m_entries.begin(); ptr_vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { func_entry * curr = *it; if (curr->get_result() != m_else) return false; } return true; } /** \brief Return a func_entry e such that m().are_equal(e.m_args[i], args[i]) for all i in [0, m_arity). If such entry does not exist then return 0, and store set args_are_values to true if for all entries e e.args_are_values() is true. */ func_entry * func_interp::get_entry(expr * const * args) const { ptr_vector::const_iterator it = m_entries.begin(); ptr_vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { func_entry * curr = *it; if (curr->eq_args(m(), m_arity, args)) return curr; } return 0; } void func_interp::insert_entry(expr * const * args, expr * r) { reset_interp_cache(); func_entry * entry = get_entry(args); if (entry != 0) { entry->set_result(m_manager, r); return; } insert_new_entry(args, r); } void func_interp::insert_new_entry(expr * const * args, expr * r) { reset_interp_cache(); CTRACE("func_interp_bug", get_entry(args) != 0, tout << "Old: " << mk_ismt2_pp(get_entry(args)->m_result, m_manager) << "\n"; tout << "Args:"; for (unsigned i = 0; i < m_arity; i++) { tout << mk_ismt2_pp(get_entry(args)->get_arg(i), m_manager) << "\n"; } tout << "New: " << mk_ismt2_pp(r, m_manager) << "\n"; tout << "Args:"; for (unsigned i = 0; i < m_arity; i++) { tout << mk_ismt2_pp(args[i], m_manager) << "\n"; } ); SASSERT(get_entry(args) == 0); func_entry * new_entry = func_entry::mk(m_manager, m_arity, args, r); if (!new_entry->args_are_values()) m_args_are_values = false; m_entries.push_back(new_entry); } bool func_interp::eval_else(expr * const * args, expr_ref & result) const { if (m_else == 0) return false; var_subst s(m_manager, false); SASSERT(!s.std_order()); // (VAR 0) <- args[0], (VAR 1) <- args[1], ... s(m_else, m_arity, args, result); return true; } /** \brief Return the result with the maximal number of occurrencies in m_entries. */ expr * func_interp::get_max_occ_result() const { if (m_entries.empty()) return 0; obj_map num_occs; expr * r_max = 0; unsigned max = 0; ptr_vector::const_iterator it = m_entries.begin(); ptr_vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { func_entry * curr = *it; expr * r = curr->get_result(); unsigned occs = 0; num_occs.find(r, occs); occs++; num_occs.insert(r, occs); if (occs > max) { max = occs; r_max = r; } } return r_max; } /** \brief Remove entries e such that e.get_result() == m_else. */ void func_interp::compress() { if (m_else == 0 || m_entries.empty()) return; // nothing to be done if (!is_ground(m_else)) return; // forall entries e in m_entries e.get_result() is ground unsigned i = 0; unsigned j = 0; unsigned sz = m_entries.size(); m_args_are_values = true; for (; i < sz; i++) { func_entry * curr = m_entries[i]; if (curr->get_result() != m_else) { m_entries[j] = curr; j++; if (!curr->args_are_values()) m_args_are_values = false; } else { curr->deallocate(m_manager, m_arity); } } if (j < sz) { reset_interp_cache(); m_entries.shrink(j); } } expr * func_interp::get_interp_core() const { if (m_else == 0) return 0; expr * r = m_else; ptr_buffer vars; ptr_vector::const_iterator it = m_entries.begin(); ptr_vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { func_entry * curr = *it; if (vars.empty()) { for (unsigned i = 0; i < m_arity; i++) { vars.push_back(m_manager.mk_var(i, m_manager.get_sort(curr->get_arg(i)))); } } ptr_buffer eqs; for (unsigned i = 0; i < m_arity; i++) { eqs.push_back(m_manager.mk_eq(vars[i], curr->get_arg(i))); } SASSERT(eqs.size() == m_arity); expr * cond; if (m_arity == 1) cond = eqs.get(0); else cond = m_manager.mk_and(eqs.size(), eqs.c_ptr()); r = m_manager.mk_ite(cond, curr->get_result(), r); } return r; } expr * func_interp::get_interp() const { if (m_interp != 0) return m_interp; expr * r = get_interp_core(); if (r != 0) { const_cast(this)->m_interp = r; m_manager.inc_ref(m_interp); } return r; } func_interp * func_interp::translate(ast_translation & translator) const { func_interp * new_fi = alloc(func_interp, translator.to(), m_arity); ptr_vector::const_iterator it = m_entries.begin(); ptr_vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { func_entry * curr = *it; ptr_buffer new_args; for (unsigned i=0; iget_arg(i))); new_fi->insert_new_entry(new_args.c_ptr(), translator(curr->get_result())); } new_fi->set_else(translator(m_else)); return new_fi; } z3-z3-4.4.1/src/model/func_interp.h000066400000000000000000000074561260446376700170150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: func_interp.h Abstract: Support for function graphs (aka interpretations for functions). They are used during model construction, and for evaluating expressions modulo a model. Main goal: Remove some code duplication and make the evaluator more efficient. Example of code duplication that existed in Z3: - smt_model_generator was creating func_values that were essentially partial func_interps - smt_model_generator was creating if-then-else (lambda) exprs representing func_values - the model object was converting these lambdas back into func_graphs (a limited version of func_interps). - the smt_model_finder needs to manipulate func_interps, but the func_values in smt_model_generator were private and too restrictive. Author: Leonardo de Moura (leonardo) 2010-12-30. Revision History: --*/ #ifndef FUNC_INTERP_H_ #define FUNC_INTERP_H_ #include"ast.h" #include"ast_translation.h" class func_interp; class func_entry { bool m_args_are_values; //!< true if is_value(m_args[i]) is true for all i in [0, arity) // m_result and m_args[i] must be ground terms. expr * m_result; expr * m_args[]; static unsigned get_obj_size(unsigned arity) { return sizeof(func_entry) + arity * sizeof(expr*); } func_entry(ast_manager & m, unsigned arity, expr * const * args, expr * result); friend class func_interp; void set_result(ast_manager & m, expr * r); public: static func_entry * mk(ast_manager & m, unsigned arity, expr * const * args, expr * result); bool args_are_values() const { return m_args_are_values; } void deallocate(ast_manager & m, unsigned arity); expr * get_result() const { return m_result; } expr * get_arg(unsigned idx) const { return m_args[idx]; } expr * const * get_args() const { return m_args; } /** \brief Return true if m.are_equal(m_args[i], args[i]) for all i in [0, arity) */ bool eq_args(ast_manager & m, unsigned arity, expr * const * args) const; }; class func_interp { ast_manager & m_manager; unsigned m_arity; ptr_vector m_entries; expr * m_else; bool m_args_are_values; //!< true if forall e in m_entries e.args_are_values() == true expr * m_interp; //!< cache for representing the whole interpretation as a single expression (it uses ite terms). void reset_interp_cache(); expr * get_interp_core() const; public: func_interp(ast_manager & m, unsigned arity); ~func_interp(); ast_manager & m () const { return m_manager; } func_interp * copy() const; unsigned get_arity() const { return m_arity; } bool is_partial() const { return m_else == 0; } // A function interpretation is said to be simple if m_else is ground. bool is_simple() const { return is_partial() || is_ground(m_else); } bool is_constant() const; // Return true if all arguments of the function graph are values. bool args_are_values() const { return m_args_are_values; } expr * get_else() const { return m_else; } void set_else(expr * e); void insert_entry(expr * const * args, expr * r); void insert_new_entry(expr * const * args, expr * r); func_entry * get_entry(expr * const * args) const; bool eval_else(expr * const * args, expr_ref & result) const; unsigned num_entries() const { return m_entries.size(); } func_entry const * const * get_entries() const { return m_entries.c_ptr(); } func_entry const * get_entry(unsigned idx) const { return m_entries[idx]; } expr * get_max_occ_result() const; void compress(); expr * get_interp() const; func_interp * translate(ast_translation & translator) const; }; #endif z3-z3-4.4.1/src/model/model.cpp000066400000000000000000000150411260446376700161210ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model.cpp Abstract: Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #include"model.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"var_subst.h" #include"array_decl_plugin.h" #include"well_sorted.h" #include"used_symbols.h" #include"model_evaluator.h" model::model(ast_manager & m): model_core(m) { } model::~model() { decl2expr::iterator it1 = m_interp.begin(); decl2expr::iterator end1 = m_interp.end(); for (; it1 != end1; ++it1) { m_manager.dec_ref(it1->m_key); m_manager.dec_ref(it1->m_value); } decl2finterp::iterator it2 = m_finterp.begin(); decl2finterp::iterator end2 = m_finterp.end(); for (; it2 != end2; ++it2) { m_manager.dec_ref(it2->m_key); dealloc(it2->m_value); } sort2universe::iterator it3 = m_usort2universe.begin(); sort2universe::iterator end3 = m_usort2universe.end(); for (; it3 != end3; ++it3) { m_manager.dec_ref(it3->m_key); m_manager.dec_array_ref(it3->m_value->size(), it3->m_value->c_ptr()); dealloc(it3->m_value); } } void model::register_decl(func_decl * d, expr * v) { SASSERT(d->get_arity() == 0); decl2expr::obj_map_entry * entry = m_interp.insert_if_not_there2(d, 0); if (entry->get_data().m_value == 0) { // new entry m_decls.push_back(d); m_const_decls.push_back(d); m_manager.inc_ref(d); m_manager.inc_ref(v); entry->get_data().m_value = v; } else { // replacing entry m_manager.inc_ref(v); m_manager.dec_ref(entry->get_data().m_value); entry->get_data().m_value = v; } } void model::register_decl(func_decl * d, func_interp * fi) { SASSERT(d->get_arity() > 0); SASSERT(&fi->m() == &m_manager); decl2finterp::obj_map_entry * entry = m_finterp.insert_if_not_there2(d, 0); if (entry->get_data().m_value == 0) { // new entry m_decls.push_back(d); m_func_decls.push_back(d); m_manager.inc_ref(d); entry->get_data().m_value = fi; } else { // replacing entry if (fi != entry->get_data().m_value) dealloc(entry->get_data().m_value); entry->get_data().m_value = fi; } } void model::copy_const_interps(model const & source) { decl2expr::iterator it1 = source.m_interp.begin(); decl2expr::iterator end1 = source.m_interp.end(); for (; it1 != end1; ++it1) { register_decl(it1->m_key, it1->m_value); } } void model::copy_func_interps(model const & source) { decl2finterp::iterator it2 = source.m_finterp.begin(); decl2finterp::iterator end2 = source.m_finterp.end(); for (; it2 != end2; ++it2) { register_decl(it2->m_key, it2->m_value->copy()); } } void model::copy_usort_interps(model const & source) { sort2universe::iterator it3 = source.m_usort2universe.begin(); sort2universe::iterator end3 = source.m_usort2universe.end(); for (; it3 != end3; ++it3) { register_usort(it3->m_key, it3->m_value->size(), it3->m_value->c_ptr()); } } model * model::copy() const { model * m = alloc(model, m_manager); m->copy_const_interps(*this); m->copy_func_interps(*this); m->copy_usort_interps(*this); return m; } // Remark: eval is for backward compatibility. We should use model_evaluator. bool model::eval(expr * e, expr_ref & result, bool model_completion) { model_evaluator ev(*this); ev.set_model_completion(model_completion); try { ev(e, result); return true; } catch (model_evaluator_exception & ex) { (void)ex; TRACE("model_evaluator", tout << ex.msg() << "\n";); return false; } } struct model::value_proc : public some_value_proc { model & m_model; value_proc(model & m):m_model(m) {} virtual expr * operator()(sort * s) { ptr_vector * u = 0; if (m_model.m_usort2universe.find(s, u)) { if (u->size() > 0) return u->get(0); } return 0; } }; expr * model::get_some_value(sort * s) { value_proc p(*this); return m_manager.get_some_value(s, &p); } ptr_vector const & model::get_universe(sort * s) const { ptr_vector * u = 0; m_usort2universe.find(s, u); SASSERT(u != 0); return *u; } bool model::has_uninterpreted_sort(sort * s) const { ptr_vector * u = 0; m_usort2universe.find(s, u); return u != 0; } unsigned model::get_num_uninterpreted_sorts() const { return m_usorts.size(); } sort * model::get_uninterpreted_sort(unsigned idx) const { return m_usorts[idx]; } void model::register_usort(sort * s, unsigned usize, expr * const * universe) { SASSERT(m_manager.is_uninterp(s)); sort2universe::obj_map_entry * entry = m_usort2universe.insert_if_not_there2(s, 0); m_manager.inc_array_ref(usize, universe); if (entry->get_data().m_value == 0) { // new entry m_usorts.push_back(s); m_manager.inc_ref(s); ptr_vector * new_u = alloc(ptr_vector); new_u->append(usize, universe); entry->get_data().m_value = new_u; } else { // updating ptr_vector * u = entry->get_data().m_value; SASSERT(u); m_manager.dec_array_ref(u->size(), u->c_ptr()); u->append(usize, universe); } } model * model::translate(ast_translation & translator) const { model * res = alloc(model, translator.to()); // Translate const interps decl2expr::iterator it1 = m_interp.begin(); decl2expr::iterator end1 = m_interp.end(); for (; it1 != end1; ++it1) { res->register_decl(translator(it1->m_key), translator(it1->m_value)); } // Translate func interps decl2finterp::iterator it2 = m_finterp.begin(); decl2finterp::iterator end2 = m_finterp.end(); for (; it2 != end2; ++it2) { func_interp * fi = it2->m_value; res->register_decl(translator(it2->m_key), fi->translate(translator)); } // Translate usort interps sort2universe::iterator it3 = m_usort2universe.begin(); sort2universe::iterator end3 = m_usort2universe.end(); for (; it3 != end3; ++it3) { ptr_vector new_universe; for (unsigned i=0; im_value->size(); i++) new_universe.push_back(translator(it3->m_value->get(i))); res->register_usort(translator(it3->m_key), new_universe.size(), new_universe.c_ptr()); } return res; } z3-z3-4.4.1/src/model/model.h000066400000000000000000000031021260446376700155610ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model.h Abstract: Model for satisfiable formulas Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #ifndef MODEL_H_ #define MODEL_H_ #include"model_core.h" #include"ref.h" #include"ast_translation.h" class model : public model_core { protected: typedef obj_map*> sort2universe; ptr_vector m_usorts; sort2universe m_usort2universe; struct value_proc; public: model(ast_manager & m); virtual ~model(); void copy_func_interps(model const & source); void copy_const_interps(model const & source); void copy_usort_interps(model const & source); model * copy() const; bool eval(func_decl * f, expr_ref & r) const { return model_core::eval(f, r); } bool eval(expr * e, expr_ref & result, bool model_completion = false); expr * get_some_value(sort * s); virtual ptr_vector const & get_universe(sort * s) const; virtual unsigned get_num_uninterpreted_sorts() const; virtual sort * get_uninterpreted_sort(unsigned idx) const; bool has_uninterpreted_sort(sort * s) const; // // Primitives for building models // void register_decl(func_decl * d, expr * v); void register_decl(func_decl * f, func_interp * fi); void register_usort(sort * s, unsigned usize, expr * const * universe); // Model translation // model * translate(ast_translation & translator) const; }; typedef ref model_ref; #endif /* MODEL_H_ */ z3-z3-4.4.1/src/model/model2expr.cpp000066400000000000000000000110721260446376700171020ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: model2expr.cpp Abstract: Convert model to logical formula that forces it. Author: Nikolaj Bjorner (nbjorner) 2012-09-17 Revision History: --*/ #include "model2expr.h" #include "for_each_ast.h" #include "bool_rewriter.h" #include "var_subst.h" struct for_each_symbol_proc { symbol_set& m_symbols; for_each_symbol_proc(symbol_set& syms): m_symbols(syms) {} void operator()(func_decl* n) { m_symbols.insert(n->get_name()); } void operator()(quantifier* n) { for (unsigned i = 0; i < n->get_num_decls(); ++i) { m_symbols.insert(n->get_decl_name(i)); } } void operator()(var* n) {} void operator()(sort* s) {} void operator()(app* a) {} }; void mk_fresh_name::add(ast* a) { for_each_symbol_proc proc(m_symbols); for_each_ast(proc, a); } symbol mk_fresh_name::next() { for (; ; ++m_num) { for(; m_char <= 'Z'; ++m_char) { std::stringstream _name; _name << m_char; if (m_num > 0) _name << m_num; ++m_char; symbol name(_name.str().c_str()); if (!m_symbols.contains(name)) { return name; } } m_char = 'A'; } } static void mk_entry_cond(unsigned arity, func_entry const* entry, expr_ref& result) { ast_manager& m = result.get_manager(); expr_ref_vector conjs(m); for (unsigned i = 0; i < arity; ++i) { expr* e = entry->get_arg(i); if (is_var(e) && to_var(e)->get_idx() == i) { // no-op } else { conjs.push_back(m.mk_eq(m.mk_var(i, m.get_sort(e)), e)); } } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result); } void model2expr(model& md, expr_ref& result) { ast_manager& m = result.get_manager(); expr_ref_vector conjs(m); expr_ref tmp(m); unsigned sz; sz = md.get_num_constants(); for (unsigned i = 0; i < sz; ++i) { func_decl* c = md.get_constant(i); expr* v = md.get_const_interp(c); conjs.push_back(m.mk_eq(m.mk_const(c), v)); } sz = md.get_num_functions(); for (unsigned i = 0; i < sz; ++i) { func_decl* f = md.get_function(i); func_interp* fi = md.get_func_interp(f); // Register names. mk_fresh_name fresh_name; unsigned num_entries = fi->num_entries(); fresh_name.add(f); for (unsigned j = 0; j < num_entries; ++j) { func_entry const* entry = fi->get_entry(j); fresh_name.add(entry->get_result()); for (unsigned k = 0; k < f->get_arity(); ++k) { fresh_name.add(entry->get_arg(k)); } } expr_ref func(m), cond(m); expr_ref_vector args(m); for (unsigned j = 0; j < f->get_arity(); ++j) { args.push_back(m.mk_var(j, f->get_domain(j))); } func = m.mk_app(f, args.size(), args.c_ptr()); if (fi->is_partial()) { if (num_entries == 0) { continue; } mk_entry_cond(f->get_arity(), fi->get_entry(num_entries-1), cond); tmp = m.mk_implies(cond, m.mk_eq(func, fi->get_entry(num_entries-1)->get_result())); for (unsigned j = num_entries-1; j > 0; ) { --j; mk_entry_cond(f->get_arity(), fi->get_entry(j), cond); tmp = m.mk_ite(cond, m.mk_eq(func, fi->get_entry(j)->get_result()), tmp); } } else { fresh_name.add(fi->get_else()); tmp = fi->get_else(); for (unsigned j = num_entries; j > 0; ) { --j; mk_entry_cond(f->get_arity(), fi->get_entry(j), cond); tmp = m.mk_ite(cond, fi->get_entry(j)->get_result(), tmp); } tmp = m.mk_eq(func, tmp); } ptr_vector sorts; expr_ref_vector rev_vars(m); svector names; unsigned sz = f->get_arity(); for (unsigned j = 0; j < sz; ++j) { sorts.push_back(f->get_domain(j)); rev_vars.push_back(m.mk_var(sz-j-1, f->get_domain(j))); names.push_back(fresh_name.next()); } if (f->get_arity() > 0) { var_subst vs(m, false); vs(tmp, rev_vars.size(), rev_vars.c_ptr(), tmp); tmp = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp); } conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result); } z3-z3-4.4.1/src/model/model2expr.h000066400000000000000000000015341260446376700165510ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: model2expr.h Abstract: Convert model to logical formula that forces it. Author: Nikolaj Bjorner (nbjorner) 2012-09-17 Revision History: --*/ #ifndef MODEL2EXPR_H_ #define MODEL2EXPR_H_ #include"model.h" void model2expr(model& m, expr_ref& result); inline void model2expr(model_ref& md, expr_ref& result) { model2expr(*md.get(), result); } // TODO: move typedef hashtable symbol_set; class mk_fresh_name { symbol_set m_symbols; char m_char; unsigned m_num; public: mk_fresh_name(): m_char('A'), m_num(0) {} void add(ast* a); void add(symbol const& s) { m_symbols.insert(s); } symbol next(); bool contains(symbol const& s) const { return m_symbols.contains(s); } }; #endif /* MODEL2EXPR_H_ */ z3-z3-4.4.1/src/model/model_core.cpp000066400000000000000000000010541260446376700171300ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_core.cpp Abstract: Base class for models. Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #include"model_core.h" bool model_core::eval(func_decl* f, expr_ref & r) const { if (f->get_arity() == 0) { r = get_const_interp(f); return r != 0; } else { func_interp * fi = get_func_interp(f); if (fi != 0) { r = fi->get_interp(); return r != 0; } return false; } } z3-z3-4.4.1/src/model/model_core.h000066400000000000000000000043601260446376700166000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_core.h Abstract: Base class for models. Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #ifndef MODEL_CORE_H_ #define MODEL_CORE_H_ #include"ast.h" #include"obj_hashtable.h" #include"func_interp.h" class model_core { protected: typedef obj_map decl2expr; typedef obj_map decl2finterp; ast_manager & m_manager; unsigned m_ref_count; decl2expr m_interp; //!< interpretation for uninterpreted constants decl2finterp m_finterp; //!< interpretation for uninterpreted functions ptr_vector m_decls; //!< domain of m_interp ptr_vector m_const_decls; ptr_vector m_func_decls; public: model_core(ast_manager & m):m_manager(m), m_ref_count(0) {} virtual ~model_core() {} ast_manager & get_manager() const { return m_manager; } unsigned get_num_decls() const { return m_decls.size(); } func_decl * get_decl(unsigned i) const { return m_decls[i]; } bool has_interpretation(func_decl * d) const { return m_interp.contains(d) || m_finterp.contains(d); } expr * get_const_interp(func_decl * d) const { expr * v; return m_interp.find(d, v) ? v : 0; } func_interp * get_func_interp(func_decl * d) const { func_interp * fi; return m_finterp.find(d, fi) ? fi : 0; } bool eval(func_decl * f, expr_ref & r) const; unsigned get_num_constants() const { return m_const_decls.size(); } unsigned get_num_functions() const { return m_func_decls.size(); } func_decl * get_constant(unsigned i) const { return m_const_decls[i]; } func_decl * get_function(unsigned i) const { return m_func_decls[i]; } virtual ptr_vector const & get_universe(sort * s) const = 0; virtual unsigned get_num_uninterpreted_sorts() const = 0; virtual sort * get_uninterpreted_sort(unsigned idx) const = 0; // // Reference counting // void inc_ref() { ++m_ref_count; } void dec_ref() { --m_ref_count; if (m_ref_count == 0) { dealloc(this); } } }; #endif z3-z3-4.4.1/src/model/model_evaluator.cpp000066400000000000000000000212671260446376700202120ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_evaluator.cpp Abstract: Evaluate expressions in a given model. Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #include"model.h" #include"model_evaluator_params.hpp" #include"rewriter_types.h" #include"model_evaluator.h" #include"bool_rewriter.h" #include"arith_rewriter.h" #include"bv_rewriter.h" #include"pb_rewriter.h" #include"datatype_rewriter.h" #include"array_rewriter.h" #include"fpa_rewriter.h" #include"rewriter_def.h" #include"cooperate.h" struct evaluator_cfg : public default_rewriter_cfg { model & m_model; 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; pb_rewriter m_pb_rw; fpa_rewriter m_f_rw; unsigned long long m_max_memory; unsigned m_max_steps; bool m_model_completion; bool m_cache; evaluator_cfg(ast_manager & m, model & md, params_ref const & p): m_model(md), m_b_rw(m), // We must allow customers to set parameters for arithmetic rewriter/evaluator. // In particular, the maximum degree of algebraic numbers that will be evaluated. m_a_rw(m, p), m_bv_rw(m), // See comment above. We want to allow customers to set :sort-store m_ar_rw(m, p), m_dt_rw(m), m_pb_rw(m), m_f_rw(m) { m_b_rw.set_flat(false); m_a_rw.set_flat(false); m_bv_rw.set_flat(false); m_bv_rw.set_mkbv2num(true); updt_params(p); } void updt_params(params_ref const & _p) { model_evaluator_params p(_p); m_max_memory = megabytes_to_bytes(p.max_memory()); m_max_steps = p.max_steps(); m_model_completion = p.completion(); m_cache = p.cache(); } ast_manager & m() const { return m_model.get_manager(); } // Try to use the entries to quickly evaluate the fi bool eval_fi(func_interp * fi, unsigned num, expr * const * args, expr_ref & result) { if (fi->num_entries() == 0) return false; // let get_macro handle it. SASSERT(fi->get_arity() == num); bool actuals_are_values = true; for (unsigned i = 0; actuals_are_values && i < num; i++) { actuals_are_values = m().is_value(args[i]); } if (!actuals_are_values) return false; // let get_macro handle it func_entry * entry = fi->get_entry(args); if (entry != 0) { result = entry->get_result(); return true; } return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; family_id fid = f->get_family_id(); if (fid == null_family_id) { if (num == 0) { expr * val = m_model.get_const_interp(f); if (val != 0) { result = val; return BR_DONE; } if (m_model_completion) { sort * s = f->get_range(); expr * val = m_model.get_some_value(s); m_model.register_decl(f, val); result = val; return BR_DONE; } return BR_FAILED; } SASSERT(num > 0); func_interp * fi = m_model.get_func_interp(f); if (fi != 0 && eval_fi(fi, num, args, result)) { TRACE("model_evaluator", tout << "reduce_app " << 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 BR_DONE; } } 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(); br_status st = BR_FAILED; 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); 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_pb_rw.get_fid()) return m_pb_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; } bool get_macro(func_decl * f, expr * & def, quantifier * & q, proof * & def_pr) { if (f->get_family_id() == null_family_id) { func_interp * fi = m_model.get_func_interp(f); if (fi != 0) { if (fi->is_partial()) { if (m_model_completion) { sort * s = f->get_range(); expr * val = m_model.get_some_value(s); fi->set_else(val); } else { return false; } } def = fi->get_interp(); SASSERT(def != 0); return true; } if (m_model_completion) { sort * s = f->get_range(); expr * val = m_model.get_some_value(s); func_interp * new_fi = alloc(func_interp, m(), f->get_arity()); new_fi->set_else(val); m_model.register_decl(f, new_fi); def = val; return true; } } return false; } bool max_steps_exceeded(unsigned num_steps) const { cooperate("model evaluator"); if (memory::get_allocation_size() > m_max_memory) throw rewriter_exception(Z3_MAX_MEMORY_MSG); return num_steps > m_max_steps; } bool cache_results() const { return m_cache; } }; template class rewriter_tpl; struct model_evaluator::imp : public rewriter_tpl { evaluator_cfg m_cfg; imp(model & md, params_ref const & p): rewriter_tpl(md.get_manager(), false, // no proofs for evaluator m_cfg), m_cfg(md.get_manager(), md, p) { } }; model_evaluator::model_evaluator(model & md, params_ref const & p) { m_imp = alloc(imp, md, p); } ast_manager & model_evaluator::m() const { return m_imp->m(); } model_evaluator::~model_evaluator() { dealloc(m_imp); } void model_evaluator::updt_params(params_ref const & p) { m_imp->cfg().updt_params(p); } void model_evaluator::get_param_descrs(param_descrs & r) { model_evaluator_params::collect_param_descrs(r); } void model_evaluator::set_model_completion(bool f) { m_imp->cfg().m_model_completion = f; } unsigned model_evaluator::get_num_steps() const { return m_imp->get_num_steps(); } void model_evaluator::set_cancel(bool f) { #pragma omp critical (model_evaluator) { m_imp->set_cancel(f); } } void model_evaluator::cleanup(params_ref const & p) { model & md = m_imp->cfg().m_model; #pragma omp critical (model_evaluator) { dealloc(m_imp); m_imp = alloc(imp, md, p); } } void model_evaluator::reset(params_ref const & p) { m_imp->reset(); updt_params(p); } void model_evaluator::operator()(expr * t, expr_ref & result) { TRACE("model_evaluator", tout << mk_ismt2_pp(t, m()) << "\n";); m_imp->operator()(t, result); } z3-z3-4.4.1/src/model/model_evaluator.h000066400000000000000000000020351260446376700176470ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_evaluator.h Abstract: Evaluate expressions in a given model. Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #ifndef MODEL_EVALUATOR_H_ #define MODEL_EVALUATOR_H_ #include"ast.h" #include"rewriter_types.h" #include"params.h" class model; typedef rewriter_exception model_evaluator_exception; class model_evaluator { struct imp; imp * m_imp; public: model_evaluator(model & m, params_ref const & p = params_ref()); ~model_evaluator(); ast_manager & m () const; void set_model_completion(bool f); void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); void operator()(expr * t, expr_ref & r); void set_cancel(bool f); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } void cleanup(params_ref const & p = params_ref()); void reset(params_ref const & p = params_ref()); unsigned get_num_steps() const; }; #endif z3-z3-4.4.1/src/model/model_evaluator_params.pyg000066400000000000000000000007361260446376700215700ustar00rootroot00000000000000def_module_params('model_evaluator', export=True, params=(max_memory_param(), max_steps_param(), ('completion', BOOL, False, 'assigns an interptetation to symbols that do not have one in the current model, when evaluating expressions in the current model'), ('cache', BOOL, True, 'cache intermediate results in the model evaluator') )) z3-z3-4.4.1/src/model/model_implicant.cpp000066400000000000000000000630501260446376700201640ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_implicant.cpp Abstract: Facility to extract prime implicant from model. Author: Krystof Hoder (t-khoder) 2011-8-19. Revision History: Notes: --*/ #include #include "array_decl_plugin.h" #include "ast_pp.h" #include "bool_rewriter.h" #include "for_each_expr.h" #include "model.h" #include "ref_vector.h" #include "rewriter.h" #include "rewriter_def.h" #include "util.h" #include "model_implicant.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" ///////////////////////// // model_implicant // void model_implicant::assign_value(expr* e, expr* val) { rational r; if (m.is_true(val)) { set_true(e); } else if (m.is_false(val)) { set_false(e); } else if (m_arith.is_numeral(val, r)) { set_number(e, r); } else if (m.is_value(val)) { set_value(e, val); } else { IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";); TRACE("pdr", tout << "Variable is not tracked: " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";); set_x(e); } } void model_implicant::setup_model(model_ref& model) { m_numbers.reset(); m_values.reset(); m_model = model; rational r; unsigned sz = model->get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * d = model->get_constant(i); expr* val = model->get_const_interp(d); expr* e = m.mk_const(d); m_refs.push_back(e); assign_value(e, val); } } void model_implicant::reset() { m1.reset(); m2.reset(); m_values.reset(); m_visited.reset(); m_numbers.reset(); m_refs.reset(); m_model = 0; } expr_ref_vector model_implicant::minimize_model(ptr_vector const & formulas, model_ref& mdl) { setup_model(mdl); TRACE("pdr_verbose", tout << "formulas:\n"; for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; ); expr_ref_vector model = prune_by_cone_of_influence(formulas); TRACE("pdr_verbose", tout << "pruned model:\n"; for (unsigned i = 0; i < model.size(); ++i) tout << mk_pp(model[i].get(), m) << "\n";); reset(); DEBUG_CODE( setup_model(mdl); VERIFY(check_model(formulas)); reset();); return model; } expr_ref_vector model_implicant::minimize_literals(ptr_vector const& formulas, model_ref& mdl) { TRACE("pdr", tout << "formulas:\n"; for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; ); expr_ref_vector result(m); expr_ref tmp(m); ptr_vector tocollect; setup_model(mdl); collect(formulas, tocollect); for (unsigned i = 0; i < tocollect.size(); ++i) { expr* e = tocollect[i]; expr* e1, *e2; SASSERT(m.is_bool(e)); SASSERT(is_true(e) || is_false(e)); if (is_true(e)) { result.push_back(e); } // hack to break disequalities for arithmetic variables. else if (m.is_eq(e, e1, e2) && m_arith.is_int_real(e1)) { if (get_number(e1) < get_number(e2)) { result.push_back(m_arith.mk_lt(e1,e2)); } else { result.push_back(m_arith.mk_lt(e2,e1)); } } else { result.push_back(m.mk_not(e)); } } reset(); TRACE("pdr", tout << "minimized model:\n"; for (unsigned i = 0; i < result.size(); ++i) tout << mk_pp(result[i].get(), m) << "\n"; ); return result; } void model_implicant::process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect) { SASSERT(m.is_bool(e)); SASSERT(is_true(e) || is_false(e)); unsigned v = is_true(e); unsigned sz = e->get_num_args(); expr* const* args = e->get_args(); if (e->get_family_id() == m.get_basic_family_id()) { switch(e->get_decl_kind()) { case OP_TRUE: break; case OP_FALSE: break; case OP_EQ: case OP_IFF: if (args[0] == args[1]) { SASSERT(v); // no-op } else if (m.is_bool(args[0])) { todo.append(sz, args); } else { tocollect.push_back(e); } break; case OP_DISTINCT: tocollect.push_back(e); break; case OP_ITE: if (args[1] == args[2]) { tocollect.push_back(args[1]); } else if (is_true(args[1]) && is_true(args[2])) { todo.append(2, args+1); } else if (is_false(args[1]) && is_false(args[2])) { todo.append(2, args+1); } else if (is_true(args[0])) { todo.append(2, args); } else { SASSERT(is_false(args[0])); todo.push_back(args[0]); todo.push_back(args[2]); } break; case OP_AND: if (v) { todo.append(sz, args); } else { unsigned i = 0; for (; !is_false(args[i]) && i < sz; ++i); if (i == sz) { fatal_error(1); } VERIFY(i < sz); todo.push_back(args[i]); } break; case OP_OR: if (v) { unsigned i = 0; for (; !is_true(args[i]) && i < sz; ++i); if (i == sz) { fatal_error(1); } VERIFY(i < sz); todo.push_back(args[i]); } else { todo.append(sz, args); } break; case OP_XOR: case OP_NOT: todo.append(sz, args); break; case OP_IMPLIES: if (v) { if (is_true(args[1])) { todo.push_back(args[1]); } else if (is_false(args[0])) { todo.push_back(args[0]); } else { IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); } } else { todo.append(sz, args); } break; default: IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); } } else { tocollect.push_back(e); } } void model_implicant::collect(ptr_vector const& formulas, ptr_vector& tocollect) { ptr_vector todo; todo.append(formulas); m_visited.reset(); VERIFY(check_model(formulas)); while (!todo.empty()) { app* e = to_app(todo.back()); todo.pop_back(); if (!m_visited.is_marked(e)) { process_formula(e, todo, tocollect); m_visited.mark(e, true); } } m_visited.reset(); } expr_ref_vector model_implicant::prune_by_cone_of_influence(ptr_vector const & formulas) { ptr_vector tocollect; collect(formulas, tocollect); m1.reset(); m2.reset(); for (unsigned i = 0; i < tocollect.size(); ++i) { TRACE("pdr_verbose", tout << "collect: " << mk_pp(tocollect[i], m) << "\n";); for_each_expr(*this, m_visited, tocollect[i]); } unsigned sz = m_model->get_num_constants(); expr_ref e(m), eq(m), val(m); expr_ref_vector model(m); for (unsigned i = 0; i < sz; i++) { e = m.mk_const(m_model->get_constant(i)); if (m_visited.is_marked(e)) { val = eval(m_model, e); eq = m.mk_eq(e, val); model.push_back(eq); } } m_visited.reset(); TRACE("pdr", tout << sz << " ==> " << model.size() << "\n";); return model; } void model_implicant::eval_arith(app* e) { rational r, r2; #define ARG1 e->get_arg(0) #define ARG2 e->get_arg(1) unsigned arity = e->get_num_args(); for (unsigned i = 0; i < arity; ++i) { expr* arg = e->get_arg(i); if (is_x(arg)) { set_x(e); return; } SASSERT(!is_unknown(arg)); } switch(e->get_decl_kind()) { case OP_NUM: VERIFY(m_arith.is_numeral(e, r)); set_number(e, r); break; case OP_IRRATIONAL_ALGEBRAIC_NUM: set_x(e); break; case OP_LE: set_bool(e, get_number(ARG1) <= get_number(ARG2)); break; case OP_GE: set_bool(e, get_number(ARG1) >= get_number(ARG2)); break; case OP_LT: set_bool(e, get_number(ARG1) < get_number(ARG2)); break; case OP_GT: set_bool(e, get_number(ARG1) > get_number(ARG2)); break; case OP_ADD: r = rational::zero(); for (unsigned i = 0; i < arity; ++i) { r += get_number(e->get_arg(i)); } set_number(e, r); break; case OP_SUB: r = get_number(e->get_arg(0)); for (unsigned i = 1; i < arity; ++i) { r -= get_number(e->get_arg(i)); } set_number(e, r); break; case OP_UMINUS: SASSERT(arity == 1); set_number(e, get_number(e->get_arg(0))); break; case OP_MUL: r = rational::one(); for (unsigned i = 0; i < arity; ++i) { r *= get_number(e->get_arg(i)); } set_number(e, r); break; case OP_DIV: SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { set_number(e, get_number(ARG1) / r); } break; case OP_IDIV: SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { set_number(e, div(get_number(ARG1), r)); } break; case OP_REM: // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2) SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { r2 = mod(get_number(ARG1), r); if (r.is_neg()) r2.neg(); set_number(e, r2); } break; case OP_MOD: SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { set_number(e, mod(get_number(ARG1), r)); } break; case OP_TO_REAL: SASSERT(arity == 1); set_number(e, get_number(ARG1)); break; case OP_TO_INT: SASSERT(arity == 1); set_number(e, floor(get_number(ARG1))); break; case OP_IS_INT: SASSERT(arity == 1); set_bool(e, get_number(ARG1).is_int()); break; case OP_POWER: set_x(e); break; default: IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); break; } } void model_implicant::inherit_value(expr* e, expr* v) { expr* w; SASSERT(!is_unknown(v)); SASSERT(m.get_sort(e) == m.get_sort(v)); if (is_x(v)) { set_x(e); } else if (m.is_bool(e)) { SASSERT(m.is_bool(v)); if (is_true(v)) set_true(e); else if (is_false(v)) set_false(e); else { TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); set_x(e); } } else if (m_arith.is_int_real(e)) { set_number(e, get_number(v)); } else if (m.is_value(v)) { set_value(e, v); } else if (m_values.find(v, w)) { set_value(e, w); } else { TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); set_x(e); } } void model_implicant::eval_exprs(expr_ref_vector& es) { model_ref mr(m_model); for (unsigned j = 0; j < es.size(); ++j) { if (m_array.is_as_array(es[j].get())) { es[j] = eval(mr, es[j].get()); } } } bool model_implicant::extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case) { SASSERT(m_array.is_array(a)); TRACE("pdr", tout << mk_pp(a, m) << "\n";); while (m_array.is_store(a)) { expr_ref_vector store(m); store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1); eval_exprs(store); stores.push_back(store); a = to_app(a)->get_arg(0); } if (m_array.is_const(a)) { else_case = to_app(a)->get_arg(0); return true; } while (m_array.is_as_array(a)) { func_decl* f = m_array.get_as_array_func_decl(to_app(a)); func_interp* g = m_model->get_func_interp(f); unsigned sz = g->num_entries(); unsigned arity = f->get_arity(); for (unsigned i = 0; i < sz; ++i) { expr_ref_vector store(m); func_entry const* fe = g->get_entry(i); store.append(arity, fe->get_args()); store.push_back(fe->get_result()); for (unsigned j = 0; j < store.size(); ++j) { if (!is_ground(store[j].get())) { TRACE("pdr", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";); return false; } } eval_exprs(store); stores.push_back(store); } else_case = g->get_else(); if (!else_case) { TRACE("pdr", tout << "no else case " << mk_pp(a, m) << "\n";); return false; } if (!is_ground(else_case)) { TRACE("pdr", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";); return false; } if (m_array.is_as_array(else_case)) { model_ref mr(m_model); else_case = eval(mr, else_case); } TRACE("pdr", tout << "else case: " << mk_pp(else_case, m) << "\n";); return true; } TRACE("pdr", tout << "no translation: " << mk_pp(a, m) << "\n";); return false; } /** best effort evaluator of extensional array equality. */ void model_implicant::eval_array_eq(app* e, expr* arg1, expr* arg2) { TRACE("pdr", tout << "array equality: " << mk_pp(e, m) << "\n";); expr_ref v1(m), v2(m); m_model->eval(arg1, v1); m_model->eval(arg2, v2); if (v1 == v2) { set_true(e); return; } sort* s = m.get_sort(arg1); sort* r = get_array_range(s); // give up evaluating finite domain/range arrays if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); return; } vector store; expr_ref else1(m), else2(m); if (!extract_array_func_interp(v1, store, else1) || !extract_array_func_interp(v2, store, else2)) { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); return; } if (else1 != else2) { if (m.is_value(else1) && m.is_value(else2)) { TRACE("pdr", tout << "defaults are different: " << mk_pp(e, m) << " " << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";); set_false(e); } else if (m_array.is_array(else1)) { eval_array_eq(e, else1, else2); } else { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); } return; } expr_ref s1(m), s2(m), w1(m), w2(m); expr_ref_vector args1(m), args2(m); args1.push_back(v1); args2.push_back(v2); for (unsigned i = 0; i < store.size(); ++i) { args1.resize(1); args2.resize(1); args1.append(store[i].size()-1, store[i].c_ptr()); args2.append(store[i].size()-1, store[i].c_ptr()); s1 = m_array.mk_select(args1.size(), args1.c_ptr()); s2 = m_array.mk_select(args2.size(), args2.c_ptr()); m_model->eval(s1, w1); m_model->eval(s2, w2); if (w1 == w2) { continue; } if (m.is_value(w1) && m.is_value(w2)) { TRACE("pdr", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n"; tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";); set_false(e); } else if (m_array.is_array(w1)) { eval_array_eq(e, w1, w2); if (is_true(e)) { continue; } } else { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); } return; } set_true(e); } void model_implicant::eval_eq(app* e, expr* arg1, expr* arg2) { if (arg1 == arg2) { set_true(e); } else if (m_array.is_array(arg1)) { eval_array_eq(e, arg1, arg2); } else if (is_x(arg1) || is_x(arg2)) { expr_ref eq(m), vl(m); eq = m.mk_eq(arg1, arg2); m_model->eval(eq, vl); if (m.is_true(vl)) { set_bool(e, true); } else if (m.is_false(vl)) { set_bool(e, false); } else { TRACE("pdr", tout << "cannot evaluate: " << mk_pp(vl, m) << "\n";); set_x(e); } } else if (m.is_bool(arg1)) { bool val = is_true(arg1) == is_true(arg2); SASSERT(val == (is_false(arg1) == is_false(arg2))); if (val) { set_true(e); } else { set_false(e); } } else if (m_arith.is_int_real(arg1)) { set_bool(e, get_number(arg1) == get_number(arg2)); } else { expr* e1 = get_value(arg1); expr* e2 = get_value(arg2); if (m.is_value(e1) && m.is_value(e2)) { set_bool(e, e1 == e2); } else if (e1 == e2) { set_bool(e, true); } else { TRACE("pdr", tout << "not value equal:\n" << mk_pp(e1, m) << "\n" << mk_pp(e2, m) << "\n";); set_x(e); } } } void model_implicant::eval_basic(app* e) { expr* arg1, *arg2; expr *argCond, *argThen, *argElse, *arg; bool has_x = false; unsigned arity = e->get_num_args(); switch(e->get_decl_kind()) { case OP_AND: for (unsigned j = 0; j < arity; ++j) { expr * arg = e->get_arg(j); if (is_false(arg)) { set_false(e); return; } else if (is_x(arg)) { has_x = true; } else { SASSERT(is_true(arg)); } } if (has_x) { set_x(e); } else { set_true(e); } break; case OP_OR: for (unsigned j = 0; j < arity; ++j) { expr * arg = e->get_arg(j); if (is_true(arg)) { set_true(e); return; } else if (is_x(arg)) { has_x = true; } else { SASSERT(is_false(arg)); } } if (has_x) { set_x(e); } else { set_false(e); } break; case OP_NOT: VERIFY(m.is_not(e, arg)); if (is_true(arg)) { set_false(e); } else if (is_false(arg)) { set_true(e); } else { SASSERT(is_x(arg)); set_x(e); } break; case OP_IMPLIES: VERIFY(m.is_implies(e, arg1, arg2)); if (is_false(arg1) || is_true(arg2)) { set_true(e); } else if (arg1 == arg2) { set_true(e); } else if (is_true(arg1) && is_false(arg2)) { set_false(e); } else { SASSERT(is_x(arg1) || is_x(arg2)); set_x(e); } break; case OP_IFF: VERIFY(m.is_iff(e, arg1, arg2)); eval_eq(e, arg1, arg2); break; case OP_ITE: VERIFY(m.is_ite(e, argCond, argThen, argElse)); if (is_true(argCond)) { inherit_value(e, argThen); } else if (is_false(argCond)) { inherit_value(e, argElse); } else if (argThen == argElse) { inherit_value(e, argThen); } else if (m.is_bool(e)) { SASSERT(is_x(argCond)); if (is_x(argThen) || is_x(argElse)) { set_x(e); } else if (is_true(argThen) == is_true(argElse)) { inherit_value(e, argThen); } else { set_x(e); } } else { set_x(e); } break; case OP_TRUE: set_true(e); break; case OP_FALSE: set_false(e); break; case OP_EQ: VERIFY(m.is_eq(e, arg1, arg2)); eval_eq(e, arg1, arg2); break; case OP_DISTINCT: { vector values; for (unsigned i = 0; i < arity; ++i) { expr* arg = e->get_arg(i); if (is_x(arg)) { set_x(e); return; } values.push_back(get_number(arg)); } std::sort(values.begin(), values.end()); for (unsigned i = 0; i + 1 < values.size(); ++i) { if (values[i] == values[i+1]) { set_false(e); return; } } set_true(e); break; } default: IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); } } bool model_implicant::check_model(ptr_vector const& formulas) { ptr_vector todo(formulas); while (!todo.empty()) { expr * curr_e = todo.back(); if (!is_app(curr_e)) { todo.pop_back(); continue; } app * curr = to_app(curr_e); if (!is_unknown(curr)) { todo.pop_back(); continue; } unsigned arity = curr->get_num_args(); for (unsigned i = 0; i < arity; ++i) { if (is_unknown(curr->get_arg(i))) { todo.push_back(curr->get_arg(i)); } } if (todo.back() != curr) { continue; } todo.pop_back(); if (curr->get_family_id() == m_arith.get_family_id()) { eval_arith(curr); } else if (curr->get_family_id() == m.get_basic_family_id()) { eval_basic(curr); } else { expr_ref vl(m); m_model->eval(curr, vl); assign_value(curr, vl); } IF_VERBOSE(35,verbose_stream() << "assigned "<get_arity() == 0); expr_ref result(m); if (m_array.is_array(d->get_range())) { expr_ref e(m); e = m.mk_const(d); result = eval(model, e); } else { result = model->get_const_interp(d); } return result; } expr_ref model_implicant::eval(model_ref& model, expr* e) { expr_ref result(m); m_model = model; VERIFY(m_model->eval(e, result, true)); if (m_array.is_array(e)) { vector stores; expr_ref_vector args(m); expr_ref else_case(m); if (extract_array_func_interp(result, stores, else_case)) { result = m_array.mk_const_array(m.get_sort(e), else_case); while (!stores.empty() && stores.back().back() == else_case) { stores.pop_back(); } for (unsigned i = stores.size(); i > 0; ) { --i; args.resize(1); args[0] = result; args.append(stores[i]); result = m_array.mk_store(args.size(), args.c_ptr()); } return result; } } return result; } z3-z3-4.4.1/src/model/model_implicant.h000066400000000000000000000071561260446376700176360ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_implicant.h Abstract: Facility to extract prime implicant from model. Author: Krystof Hoder (t-khoder) 2011-8-19. Revision History: --*/ #ifndef MODEL_IMPLICANT_H_ #define MODEL_IMPLICANT_H_ #include "ast.h" #include "ast_pp.h" #include "obj_hashtable.h" #include "ref_vector.h" #include "trace.h" #include "vector.h" #include "arith_decl_plugin.h" #include "array_decl_plugin.h" class model; class model_core; class model_implicant { ast_manager& m; arith_util m_arith; array_util m_array; obj_map m_numbers; expr_ref_vector m_refs; obj_map m_values; model_ref m_model; //00 -- non-visited //01 -- X //10 -- false //11 -- true expr_mark m1; expr_mark m2; expr_mark m_visited; void reset(); void setup_model(model_ref& model); void assign_value(expr* e, expr* v); void collect(ptr_vector const& formulas, ptr_vector& tocollect); void process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect); expr_ref_vector prune_by_cone_of_influence(ptr_vector const & formulas); void eval_arith(app* e); void eval_basic(app* e); void eval_eq(app* e, expr* arg1, expr* arg2); void eval_array_eq(app* e, expr* arg1, expr* arg2); void inherit_value(expr* e, expr* v); inline bool is_unknown(expr* x) { return !m1.is_marked(x) && !m2.is_marked(x); } inline void set_unknown(expr* x) { m1.mark(x, false); m2.mark(x, false); } inline bool is_x(expr* x) { return !m1.is_marked(x) && m2.is_marked(x); } inline bool is_false(expr* x) { return m1.is_marked(x) && !m2.is_marked(x); } inline bool is_true(expr* x) { return m1.is_marked(x) && m2.is_marked(x); } inline void set_x(expr* x) { SASSERT(is_unknown(x)); m2.mark(x); } inline void set_v(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } inline void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } inline void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); } inline void set_bool(expr* x, bool v) { if (v) { set_true(x); } else { set_false(x); } } inline rational const& get_number(expr* x) const { return m_numbers.find(x); } inline void set_number(expr* x, rational const& v) { set_v(x); TRACE("pdr_verbose", tout << mk_pp(x,m) << " " << v << "\n";); m_numbers.insert(x,v); } inline expr* get_value(expr* x) { return m_values.find(x); } inline void set_value(expr* x, expr* v) { set_v(x); m_refs.push_back(v); m_values.insert(x, v); } bool check_model(ptr_vector const & formulas); bool extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case); void eval_exprs(expr_ref_vector& es); public: model_implicant(ast_manager& m) : m(m), m_arith(m), m_array(m), m_refs(m) {} /** \brief extract equalities from model that suffice to satisfy formula. \pre model satisfies formulas */ expr_ref_vector minimize_model(ptr_vector const & formulas, model_ref& mdl); /** \brief extract literals from model that satisfy formulas. \pre model satisfies formulas */ expr_ref_vector minimize_literals(ptr_vector const & formulas, model_ref& mdl); /** for_each_expr visitor. */ void operator()(expr* e) {} expr_ref eval(model_ref& mdl, expr* e); expr_ref eval(model_ref& mdl, func_decl* d); }; #endif z3-z3-4.4.1/src/model/model_params.pyg000066400000000000000000000007771260446376700175130ustar00rootroot00000000000000def_module_params('model', export=True, params=(('partial', BOOL, False, 'enable/disable partial function interpretations'), ('v1', BOOL, False, 'use Z3 version 1.x pretty printer'), ('v2', BOOL, False, 'use Z3 version 2.x (x <= 16) pretty printer'), ('compact', BOOL, False, 'try to compact function graph (i.e., function interpretations that are lookup tables)'), )) z3-z3-4.4.1/src/model/model_pp.cpp000066400000000000000000000057621260446376700166310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_pp.cpp Abstract: Pretty printer for models for debugging purposes. Author: Leonardo de Moura (leonardo) Revision History: --*/ #include"model_pp.h" #include"model_core.h" #include"ast_pp.h" #include"ast_smt2_pp.h" #include"used_symbols.h" #include"pp.h" static void display_uninterp_sorts(std::ostream & out, model_core const & md) { ast_manager & m = md.get_manager(); unsigned sz = md.get_num_uninterpreted_sorts(); for (unsigned i = 0; i < sz; i++) { sort * s = md.get_uninterpreted_sort(i); out << "(define-sort " << mk_pp(s, m); ptr_vector const & univ = md.get_universe(s); ptr_vector::const_iterator it = univ.begin(); ptr_vector::const_iterator end = univ.end(); for (; it != end; ++it) { out << " " << mk_ismt2_pp(*it, m); } out << ")\n"; } } static void display_constants(std::ostream & out, model_core const & md) { ast_manager & m = md.get_manager(); unsigned sz = md.get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * c = md.get_constant(i); char const * d = "(define "; std::string n = c->get_name().str(); unsigned indent = static_cast(n.length() + strlen(d) + 1); out << d << n << " " << mk_ismt2_pp(md.get_const_interp(c), m, indent) << ")\n"; } } static void display_functions(std::ostream & out, model_core const & md) { ast_manager & m = md.get_manager(); unsigned sz = md.get_num_functions(); for (unsigned i = 0; i < sz; i++) { func_decl * f = md.get_function(i); out << "(define (" << f->get_name(); unsigned arity = f->get_arity(); func_interp * fi = md.get_func_interp(f); for (unsigned j = 0; j < arity; j++) { out << " " << "x!" << j; } out << ")\n"; unsigned num_entries = fi->num_entries(); for (unsigned j = 0; j < num_entries; j++) { func_entry const * curr = fi->get_entry(j); out << " (if "; if (arity > 1) out << "(and "; for (unsigned j = 0; j < arity; j++) { out << "(= x!" << j << " " << mk_ismt2_pp(curr->get_arg(j), m) << ")"; if (j + 1 < arity) out << " "; } if (arity > 1) out << ")"; out << " " << mk_ismt2_pp(curr->get_result(), m) << "\n"; } if (num_entries > 0) out << " "; if (fi->is_partial()) out << " #unspecified"; else { out << " " << mk_ismt2_pp(fi->get_else(), m, params_ref(), 5, arity, "x"); } for (unsigned j = 0; j < num_entries; j++) out << ")"; out << ")\n"; } } void model_pp(std::ostream & out, model_core const & md) { display_uninterp_sorts(out, md); display_constants(out, md); display_functions(out, md); } z3-z3-4.4.1/src/model/model_pp.h000066400000000000000000000005431260446376700162660ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_pp.h Abstract: Pretty printer for models for debugging purposes. Author: Leonardo de Moura (leonardo) Revision History: --*/ #ifndef MODEL_PP_H_ #define MODEL_PP_H_ #include class model_core; void model_pp(std::ostream & out, model_core const & m); #endif z3-z3-4.4.1/src/model/model_smt2_pp.cpp000066400000000000000000000275571260446376700176040ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_smt2_pp.cpp Abstract: Pretty printer for models using SMT2 format. Author: Leonardo de Moura (leonardo) Revision History: --*/ #include #include"model_smt2_pp.h" #include"ast_smt2_pp.h" #include"func_decl_dependencies.h" #include"pp.h" using namespace format_ns; static void pp_indent(std::ostream & out, unsigned indent) { for (unsigned i = 0; i < indent; i++) out << " "; } static unsigned pp_symbol(std::ostream & out, symbol const & s) { if (is_smt2_quoted_symbol(s)) { std::string str = mk_smt2_quoted_symbol(s); out << str; return static_cast(str.length()); } else if (s.is_numerical()) { std::string str = s.str(); out << str; return static_cast(str.length()); } else { out << s.bare_str(); return static_cast(strlen(s.bare_str())); } } #define TAB_SZ 2 static void pp_uninterp_sorts(std::ostream & out, ast_printer_context & ctx, model_core const & md, unsigned indent) { ast_manager & m = ctx.get_ast_manager(); ptr_buffer f_conds; unsigned num = md.get_num_uninterpreted_sorts(); for (unsigned i = 0; i < num; i++) { sort * s = md.get_uninterpreted_sort(i); ptr_vector const & u = md.get_universe(s); std::ostringstream buffer; buffer << "universe for "; ctx.display(buffer, s, 13); buffer << ":\n"; pp_indent(buffer, TAB_SZ); ptr_vector::const_iterator it = u.begin(); ptr_vector::const_iterator end = u.end(); for (; it != end; ++it) { SASSERT(is_app(*it)); app * c = to_app(*it); pp_symbol(buffer, c->get_decl()->get_name()); buffer << " "; } buffer << "\n-----------"; std::string buffer_str = buffer.str(); unsigned len = static_cast(buffer_str.length()); pp_indent(out, indent); out << ";; "; for (unsigned i = 0; i < len; i++) { char c = buffer_str[i]; if (c == '\n') { out << "\n"; pp_indent(out, indent); out << ";; "; } else { out << c; } } out << "\n"; pp_indent(out, indent); out << ";; definitions for universe elements:\n"; it = u.begin(); for (; it != end; ++it) { app * c = to_app(*it); pp_indent(out, indent); out << "(declare-fun "; unsigned len = pp_symbol(out, c->get_decl()->get_name()); out << " () "; ctx.display(out, c->get_decl()->get_range(), indent + len + 16); out << ")\n"; } pp_indent(out, indent); out << ";; cardinality constraint:\n"; f_conds.reset(); format * var = mk_string(m, "x"); it = u.begin(); for (; it != end; ++it) { app * c = to_app(*it); symbol csym = c->get_decl()->get_name(); std::string cname; if (is_smt2_quoted_symbol(csym)) cname = mk_smt2_quoted_symbol(csym); else cname = csym.str(); format * c_args[2] = { var, mk_string(m, cname.c_str()) }; f_conds.push_back(mk_seq1(m, c_args, c_args+2, f2f(), "=")); } SASSERT(!f_conds.empty()); format * f_cond; if (f_conds.size() > 1) f_cond = mk_seq1(m, f_conds.begin(), f_conds.end(), f2f(), "or"); else f_cond = f_conds[0]; format_ref f_s(fm(m)); ctx.pp(s, f_s); format * f_args[2] = { mk_compose(m, mk_string(m, "((x "), mk_indent(m, 4, mk_compose(m, f_s.get(), mk_string(m, "))")))), f_cond }; format_ref f_card(fm(m)); f_card = mk_indent(m, indent, mk_seq1(m, f_args, f_args+2, f2f(), "forall")); pp_indent(out, indent); pp(out, f_card, m); out << "\n"; pp_indent(out, indent); out << ";; -----------\n"; } } static void pp_consts(std::ostream & out, ast_printer_context & ctx, model_core const & md, unsigned indent) { unsigned num = md.get_num_constants(); for (unsigned i = 0; i < num; i++) { func_decl * c = md.get_constant(i); expr * c_i = md.get_const_interp(c); pp_indent(out, indent); out << "(define-fun "; unsigned len = pp_symbol(out, c->get_name()); out << " () "; ctx.display(out, c->get_range(), indent + len + 16); out << "\n"; pp_indent(out, indent + TAB_SZ); ctx.display(out, c_i); out << ")\n"; } } void sort_fun_decls(ast_manager & m, model_core const & md, ptr_buffer & result) { func_decl_set visited; ptr_vector todo; unsigned sz = md.get_num_functions(); for (unsigned i = 0; i < sz; i++) { func_decl * f = md.get_function(i); if (visited.contains(f)) continue; visited.insert(f); todo.push_back(f); while (!todo.empty()) { func_decl * curr = todo.back(); func_interp * curr_i = md.get_func_interp(curr); SASSERT(curr_i != 0); if (!curr_i->is_partial()) { func_decl_set deps; bool all_visited = true; collect_func_decls(m, curr_i->get_else(), deps); func_decl_set::iterator it2 = deps.begin(); func_decl_set::iterator end2 = deps.end(); for (; it2 != end2; ++it2) { func_decl * d = *it2; if (d->get_arity() > 0 && md.has_interpretation(d) && !visited.contains(d)) { todo.push_back(d); visited.insert(d); all_visited = false; } } if (!all_visited) continue; } todo.pop_back(); result.push_back(curr); } } } static void pp_funs(std::ostream & out, ast_printer_context & ctx, model_core const & md, unsigned indent) { ast_manager & m = ctx.get_ast_manager(); sbuffer var_names; ptr_buffer f_var_names; ptr_buffer f_arg_decls; ptr_buffer f_entries; ptr_buffer f_entry_conds; ptr_buffer func_decls; sort_fun_decls(m, md, func_decls); for (unsigned i = 0; i < func_decls.size(); i++) { func_decl * f = func_decls[i]; func_interp * f_i = md.get_func_interp(f); SASSERT(f->get_arity() == f_i->get_arity()); format_ref body(fm(m)); var_names.reset(); if (f_i->is_partial()) { body = mk_string(m, "#unspecified"); for (unsigned j = 0; j < f->get_arity(); j++) { std::stringstream strm; strm << "x!" << (j+1); var_names.push_back(symbol(strm.str().c_str())); } } else { ctx.pp(f_i->get_else(), f->get_arity(), "x", body, var_names); } TRACE("model_smt2_pp", for (unsigned i = 0; i < var_names.size(); i++) tout << var_names[i] << "\n";); f_var_names.reset(); for (unsigned i = 0; i < f->get_arity(); i++) f_var_names.push_back(mk_string(m, var_names[i].bare_str())); f_arg_decls.reset(); for (unsigned i = 0; i < f->get_arity(); i++) { format_ref f_domain(fm(m)); ctx.pp(f->get_domain(i), f_domain); format * args[2] = { f_var_names[i], f_domain.get() }; f_arg_decls.push_back(mk_seq5(m, args, args+2, f2f())); } format * f_domain = mk_seq5(m, f_arg_decls.begin(), f_arg_decls.end(), f2f()); format_ref f_range(fm(m)); ctx.pp(f->get_range(), f_range); if (f_i->num_entries() > 0) { f_entries.reset(); for (unsigned i = 0; i < f_i->num_entries(); i++) { func_entry const * e = f_i->get_entry(i); f_entry_conds.reset(); for (unsigned j = 0; j < f->get_arity(); j++) { format_ref f_arg(fm(m)); ctx.pp(e->get_arg(j), f_arg); format * eq_args[2] = { f_var_names[j], f_arg.get() }; f_entry_conds.push_back(mk_seq1(m, eq_args, eq_args+2, f2f(), "=")); } format * f_entry_cond; SASSERT(!f_entry_conds.empty()); if (f_entry_conds.size() > 1) f_entry_cond = mk_seq1(m, f_entry_conds.begin(), f_entry_conds.end(), f2f(), "and"); else f_entry_cond = f_entry_conds[0]; format_ref f_result(fm(m)); ctx.pp(e->get_result(), f_result); if (i > 0) f_entries.push_back(mk_line_break(m)); f_entries.push_back(mk_group(m, mk_compose(m, mk_string(m, "(ite "), mk_indent(m, 5, f_entry_cond), mk_indent(m, TAB_SZ, mk_compose(m, mk_line_break(m), f_result.get()))))); } f_entries.push_back(mk_indent(m, TAB_SZ, mk_compose(m, mk_line_break(m), body.get()))); for (unsigned i = 0; i < f_i->num_entries(); i++) f_entries.push_back(mk_string(m, ")")); body = mk_compose(m, f_entries.size(), f_entries.c_ptr()); } format_ref def(fm(m)); std::string fname; if (is_smt2_quoted_symbol(f->get_name())) fname = mk_smt2_quoted_symbol(f->get_name()); else fname = f->get_name().str(); def = mk_indent(m, indent, mk_compose(m, mk_compose(m, mk_string(m, "(define-fun "), mk_string(m, fname.c_str()), mk_string(m, " "), mk_compose(m, f_domain, mk_string(m, " "), f_range)), mk_indent(m, TAB_SZ, mk_compose(m, mk_line_break(m), body.get(), mk_string(m, ")"))))); pp_indent(out, indent); pp(out, def.get(), m); out << "\n"; } } void model_smt2_pp(std::ostream & out, ast_printer_context & ctx, model_core const & m, unsigned indent) { pp_uninterp_sorts(out, ctx, m, indent); pp_consts(out, ctx, m, indent); pp_funs(out, ctx, m, indent); } void model_smt2_pp(std::ostream & out, ast_manager & m, model_core const & md, unsigned indent) { scoped_ptr ctx; ctx = mk_simple_ast_printer_context(m); pp_uninterp_sorts(out, *(ctx.get()), md, indent); pp_consts(out, *(ctx.get()), md, indent); pp_funs(out, *(ctx.get()), md, indent); } z3-z3-4.4.1/src/model/model_smt2_pp.h000066400000000000000000000010111260446376700172220ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_smt2_pp.h Abstract: Pretty printer for models using SMT2 format. Author: Leonardo de Moura (leonardo) Revision History: --*/ #ifndef MODEL_SMT2_PP_H_ #define MODEL_SMT2_PP_H_ #include"ast_printer.h" #include"model_core.h" void model_smt2_pp(std::ostream & out, ast_printer_context & ctx, model_core const & m, unsigned indent); void model_smt2_pp(std::ostream & out, ast_manager & m, model_core const & md, unsigned indent); #endif z3-z3-4.4.1/src/model/model_v2_pp.cpp000066400000000000000000000046401260446376700172320ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_v2_pp.cpp Abstract: V2 Pretty printer for models. (backward compatibility) Author: Leonardo de Moura (leonardo) Revision History: --*/ #include"model_v2_pp.h" #include"model_core.h" #include"ast_pp.h" static void display_function(std::ostream & out, model_core const & md, func_decl * f, bool partial) { ast_manager & m = md.get_manager(); func_interp * g = md.get_func_interp(f); out << f->get_name() << " -> {\n"; unsigned num_entries = g->num_entries(); unsigned arity = g->get_arity(); char const * else_str = num_entries == 0 ? " " : " else -> "; unsigned else_indent = static_cast(strlen(else_str)); for (unsigned i = 0; i < num_entries; i++) { func_entry const * entry = g->get_entry(i); out << " "; for (unsigned j = 0; j < arity; j++) { expr * arg = entry->get_arg(j); out << mk_pp(arg, m); out << " "; } out << "-> "; out << mk_pp(entry->get_result(), m); out << "\n"; } if (partial) { out << else_str << "#unspecified\n"; } else { expr * else_val = g->get_else(); out << else_str; if (else_val) out << mk_pp(else_val, m, else_indent); else out << "#unspecified"; out << "\n"; } out << "}\n"; } static void display_functions(std::ostream & out, model_core const & md, bool partial) { unsigned sz = md.get_num_functions(); for (unsigned i = 0; i < sz; i++) { func_decl * f = md.get_function(i); display_function(out, md, f, partial); } } static void display_constants(std::ostream & out, model_core const & md) { ast_manager & m = md.get_manager(); unsigned sz = md.get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * d = md.get_constant(i); std::string name = d->get_name().str(); const char * arrow = " -> "; out << name << arrow; unsigned indent = static_cast(name.length() + strlen(arrow)); out << mk_pp(md.get_const_interp(d), m, indent) << "\n"; } } void model_v2_pp(std::ostream & out, model_core const & md, bool partial) { display_constants(out, md); display_functions(out, md, partial); } // debugging support void pp(model_core const & md) { model_v2_pp(std::cout, md, false); } z3-z3-4.4.1/src/model/model_v2_pp.h000066400000000000000000000006101260446376700166700ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_v2_pp.h Abstract: V2 Pretty printer for models. (backward compatibility) Author: Leonardo de Moura (leonardo) Revision History: --*/ #ifndef MODEL_V2_PP_H_ #define MODEL_V2_PP_H_ #include class model_core; void model_v2_pp(std::ostream & out, model_core const & m, bool partial = false); #endif z3-z3-4.4.1/src/muz/000077500000000000000000000000001260446376700140275ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/README000066400000000000000000000007301260446376700147070ustar00rootroot00000000000000muZ: 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-z3-4.4.1/src/muz/base/000077500000000000000000000000001260446376700147415ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/base/bind_variables.cpp000066400000000000000000000107121260446376700204120ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/bind_variables.h000066400000000000000000000017271260446376700200650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/dl_boogie_proof.cpp000066400000000000000000000230301260446376700205730ustar00rootroot00000000000000 /*++ 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 "ast_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; } 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-z3-4.4.1/src/muz/base/dl_boogie_proof.h000066400000000000000000000053521260446376700202470ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/muz/base/dl_context.cpp000066400000000000000000001213721260446376700176160ustar00rootroot00000000000000/*++ 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" #include"ast_pp_util.h" 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_coi() const { return m_params->xform_coi(); } 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); } get_memory_statistics(st); get_rlimit_statistics(m.limit(), st); } 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. static void collect_free_funcs(unsigned sz, expr* const* exprs, ast_pp_util& v, mk_fresh_name& fresh_names) { v.collect(sz, exprs); for (unsigned i = 0; i < sz; ++i) { expr* e = exprs[i]; 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(); ast_pp_util visitor(m); 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, visitor, fresh_names); collect_free_funcs(rules.size(), rules.c_ptr(), visitor, fresh_names); collect_free_funcs(queries.size(), queries.c_ptr(), visitor, fresh_names); func_decl_set funcs; unsigned sz = visitor.coll.get_num_decls(); for (unsigned i = 0; i < sz; ++i) { func_decl* f = visitor.coll.get_func_decls()[i]; 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"; } visitor.display_decls(out); func_decl_set::iterator 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-z3-4.4.1/src/muz/base/dl_context.h000066400000000000000000000477561260446376700173000ustar00rootroot00000000000000/*++ 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; bool xform_coi() 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-z3-4.4.1/src/muz/base/dl_costs.cpp000066400000000000000000000074201260446376700172620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/dl_costs.h000066400000000000000000000052451260446376700167320ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/dl_engine_base.h000066400000000000000000000044001260446376700200260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/dl_rule.cpp000066400000000000000000001025131260446376700170750ustar00rootroot00000000000000/*++ 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); 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()); } 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-z3-4.4.1/src/muz/base/dl_rule.h000066400000000000000000000275721260446376700165550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule.h Abstract: 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-z3-4.4.1/src/muz/base/dl_rule_set.cpp000066400000000000000000000657141260446376700177630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/dl_rule_set.h000066400000000000000000000217431260446376700174220ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/dl_rule_subsumption_index.cpp000066400000000000000000000033341260446376700227350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/dl_rule_subsumption_index.h000066400000000000000000000026311260446376700224010ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/dl_rule_transformer.cpp000066400000000000000000000104041260446376700215140ustar00rootroot00000000000000/*++ 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: 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-z3-4.4.1/src/muz/base/dl_util.cpp000066400000000000000000000503431260446376700171060ustar00rootroot00000000000000/*++ 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<(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-z3-4.4.1/src/muz/base/fixedpoint_params.pyg000066400000000000000000000262111260446376700212000ustar00rootroot00000000000000def_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"), ('xform.coi', BOOL, True, "use cone of influence simplificaiton"), ('duality.enable_restarts', BOOL, False, 'DUALITY: enable restarts'), )) z3-z3-4.4.1/src/muz/base/hnf.cpp000066400000000000000000000367571260446376700162420ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: hnf.cpp Abstract: Horn normal form conversion. Author: Nikolaj Bjorner (nbjorner) 3-20-2013 Notes: Convert formula (forall x f(x)) into conjunction (f1 xy) (f2 xy) (f3 xy) such that (forall x f(x)) ~ /\ (forall xy (f_i xy)) modulo definitions that are introduced. Convert proof with asserted (forall xy (f' xy)) To: (forall xy (f' xy)) by mp~ 1, 2 1. asserted/def-intro (forall xy (f xy)) 2. (forall xy (f xy)) ~ (forall xy (f' xy)) by trans, 3, 4 3. (forall xy (f xy)) ~ (forall xy (f1 xy)) by pull quantifiers (rewrite) 4. (forall xy (f1 xy)) ~ (forall xy (f' xy)) by oeq_quant_intro 5 5. f1 xy ~ f' xy by sub-proof. --*/ #include"hnf.h" #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_pp.h" #include"quant_hoist.h" #include"ast_util.h" #include"dl_util.h" #include"for_each_ast.h" #include"for_each_expr.h" class hnf::imp { class contains_predicate_proc { imp const& m; public: struct found {}; contains_predicate_proc(imp const& m): m(m) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app* n) { if (m.is_predicate(n)) throw found(); } }; ast_manager& m; bool m_produce_proofs; volatile bool m_cancel; expr_ref_vector m_todo; proof_ref_vector m_proofs; expr_ref_vector m_refs; symbol m_name; svector 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)) { if (is_var(n1)) { return true; } if (is_quantifier(n1)) { return false; } 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; } 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-z3-4.4.1/src/muz/base/hnf.h000066400000000000000000000020411260446376700156620ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/muz/base/proof_utils.cpp000066400000000000000000000474561260446376700200320ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/muz/base/proof_utils.h000066400000000000000000000016311260446376700174600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/rule_properties.cpp000066400000000000000000000135511260446376700206750ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/base/rule_properties.h000066400000000000000000000031761260446376700203440ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/bmc/000077500000000000000000000000001260446376700145705ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/bmc/dl_bmc_engine.cpp000066400000000000000000001742511260446376700200530ustar00rootroot00000000000000/*++ 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(); rule_set& rules0 = m_ctx.get_rules(); datalog::rule_set old_rules(rules0); rule_manager.mk_query(query, rules0); 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); } const rule_set& rules = m_ctx.get_rules(); if (rules.get_output_predicates().empty()) { return l_false; } m_query_pred = rules.get_output_predicate(); m_rules.replace_rules(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-z3-4.4.1/src/muz/bmc/dl_bmc_engine.h000066400000000000000000000027241260446376700175130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/clp/000077500000000000000000000000001260446376700146055ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/clp/clp_context.cpp000066400000000000000000000164341260446376700176430ustar00rootroot00000000000000/*++ 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); const rule_set& rules = m_ctx.get_rules(); if (rules.get_output_predicates().empty()) { return l_false; } func_decl *head_decl = rules.get_output_predicate(); 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-z3-4.4.1/src/muz/clp/clp_context.h000066400000000000000000000015611260446376700173030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/dataflow/000077500000000000000000000000001260446376700156305ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/dataflow/dataflow.cpp000066400000000000000000000005371260446376700201420ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: dataflow.cpp Abstract: Generic bottom-up and top-down data-flow engine for analysis of rule sets. Author: Henning Guenther (t-hennig) --*/ #include "dataflow.h" #include "reachability.h" namespace datalog { const reachability_info reachability_info::null_fact; } z3-z3-4.4.1/src/muz/dataflow/dataflow.h000066400000000000000000000234721260446376700176120ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: dataflow.h Abstract: Generic bottom-up and top-down data-flow engine for analysis of rule sets. Author: Henning Guenther (t-hennig) --*/ #ifndef DATAFLOW_H_ #define DATAFLOW_H_ #include "dl_rule.h" #include "dl_rule_set.h" #include "hashtable.h" #include "vector.h" namespace datalog { template class fact_reader; template class fact_writer; /* The structure of fact classes: class fact { public: typedef ... ctx_t; // Empty fact static fact null_fact; fact(); -- bottom // Init (Top down) void init_down(ctx_t& ctx, const rule* r); // Init (Bottom up) bool init_up(ctx_t& ctx, const rule* r); // Step (Bottom up) bool propagate_up(ctx_t& ctx, const rule* r, const fact_reader& tail_facts); // Step (Top down) void propagate_down(ctx_t& ctx, const rule* r, fact_writer& tail_facts) const; // Debugging void dump(ctx_t& ctx, std::ostream& outp) const; // Union void join(ctx_t& ctx, const Fact& oth); // Intersection void intersect(ctx_t& ctx, const Fact& oth); }; */ template class dataflow_engine { public: typedef map, ptr_eq > fact_db; typedef hashtable, ptr_eq > todo_set; typedef typename fact_db::iterator iterator; private: const rule_set& m_rules; fact_db m_facts; todo_set m_todo[2]; unsigned m_todo_idx; typename Fact::ctx_t& m_context; rule_set::decl2rules m_body2rules; void init_bottom_up() { for (rule_set::iterator it = m_rules.begin(); it != m_rules.end(); ++it) { rule* cur = *it; for (unsigned i = 0; i < cur->get_uninterpreted_tail_size(); ++i) { func_decl *d = cur->get_decl(i); rule_set::decl2rules::obj_map_entry *e = m_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(cur); } if (cur->get_uninterpreted_tail_size() == 0) { func_decl *sym = cur->get_head()->get_decl(); bool new_info = m_facts.insert_if_not_there2(sym, Fact())->get_data().m_value.init_up(m_context, cur); if (new_info) { m_todo[m_todo_idx].insert(sym); } } } } void init_top_down() { const func_decl_set& output_preds = m_rules.get_output_predicates(); for (func_decl_set::iterator I = output_preds.begin(), E = output_preds.end(); I != E; ++I) { func_decl* sym = *I; const rule_vector& output_rules = m_rules.get_predicate_rules(sym); for (unsigned i = 0; i < output_rules.size(); ++i) { m_facts.insert_if_not_there2(sym, Fact())->get_data().m_value.init_down(m_context, output_rules[i]); m_todo[m_todo_idx].insert(sym); } } } void step_bottom_up() { for(todo_set::iterator I = m_todo[m_todo_idx].begin(), E = m_todo[m_todo_idx].end(); I!=E; ++I) { ptr_vector * rules; if (!m_body2rules.find(*I, rules)) continue; for (ptr_vector::iterator I2 = rules->begin(), E2 = rules->end(); I2 != E2; ++I2) { func_decl* head_sym = (*I2)->get_head()->get_decl(); fact_reader tail_facts(m_facts, *I2); bool new_info = m_facts.insert_if_not_there2(head_sym, Fact())->get_data().m_value.propagate_up(m_context, *I2, tail_facts); if (new_info) { m_todo[!m_todo_idx].insert(head_sym); } } } // Update todo list m_todo[m_todo_idx].reset(); m_todo_idx = !m_todo_idx; } void step_top_down() { for(todo_set::iterator I = m_todo[m_todo_idx].begin(), E = m_todo[m_todo_idx].end(); I!=E; ++I) { func_decl* head_sym = *I; // We can't use a reference here because we are changing the map while using the reference const Fact head_fact = m_facts.get(head_sym, Fact::null_fact); const rule_vector& deps = m_rules.get_predicate_rules(head_sym); const unsigned deps_size = deps.size(); for (unsigned i = 0; i < deps_size; ++i) { rule *trg_rule = deps[i]; fact_writer writer(m_facts, trg_rule, m_todo[!m_todo_idx]); // Generate new facts head_fact.propagate_down(m_context, trg_rule, writer); } } // Update todo list m_todo[m_todo_idx].reset(); m_todo_idx = !m_todo_idx; } bool done() const { return m_todo[m_todo_idx].empty(); } public: dataflow_engine(typename Fact::ctx_t& ctx, const rule_set& rules) : m_rules(rules), m_todo_idx(0), m_context(ctx) {} ~dataflow_engine() { for (rule_set::decl2rules::iterator it = m_body2rules.begin(); it != m_body2rules.end(); ++it) { dealloc(it->m_value); } } void dump(std::ostream& outp) { obj_hashtable visited; for (rule_set::iterator I = m_rules.begin(), E = m_rules.end(); I != E; ++I) { const rule* r = *I; func_decl* head_decl = r->get_decl(); obj_hashtable::entry *dummy; if (visited.insert_if_not_there_core(head_decl, dummy)) { const Fact& fact = m_facts.get(head_decl, Fact::null_fact); outp << head_decl->get_name() << " -> "; fact.dump(m_context, outp); outp << "\n"; } for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { func_decl *tail_decl = r->get_decl(i); if (visited.insert_if_not_there_core(tail_decl, dummy)) { const Fact& fact = m_facts.get(tail_decl, Fact::null_fact); outp << tail_decl->get_name() << " -> "; fact.dump(m_context, outp); outp << "\n"; } } } } void run_bottom_up() { init_bottom_up(); while (!done()) step_bottom_up(); } void run_top_down() { init_top_down(); while (!done()) step_top_down(); } const Fact& get_fact(func_decl* decl) const { return m_facts.get(decl, Fact::null_fact); } iterator begin() const { return m_facts.begin(); } iterator end() const { return m_facts.end(); } void join(const dataflow_engine& oth) { for (typename fact_db::iterator I = oth.m_facts.begin(), E = oth.m_facts.end(); I != E; ++I) { typename fact_db::entry* entry; if (m_facts.insert_if_not_there_core(I->m_key, entry)) { entry->get_data().m_value = I->m_value; } else { entry->get_data().m_value.join(m_context, I->m_value); } } } void intersect(const dataflow_engine& oth) { vector to_delete; for (typename fact_db::iterator I = m_facts.begin(), E = m_facts.end(); I != E; ++I) { if (typename fact_db::entry *entry = oth.m_facts.find_core(I->m_key)) { I->m_value.intersect(m_context, entry->get_data().m_value); } else { to_delete.push_back(I->m_key); } } for (unsigned i = 0; i < to_delete.size(); ++i) { m_facts.erase(to_delete[i]); } } }; // This helper-class is used to look up facts for rule tails template class fact_reader { typedef typename dataflow_engine::fact_db fact_db; const fact_db& m_facts; const rule* m_rule; public: fact_reader(const fact_db& facts, const rule* r) : m_facts(facts), m_rule(r) { } const Fact& get(unsigned idx) const { return m_facts.get(m_rule->get_decl(idx), Fact::null_fact); } unsigned size() const { return m_rule->get_uninterpreted_tail_size(); } }; template class fact_writer { friend class dataflow_engine; typedef typename dataflow_engine::fact_db fact_db; fact_db& m_facts; const rule* m_rule; typename dataflow_engine::todo_set& m_todo; public: fact_writer(fact_db& facts, const rule* r, typename dataflow_engine::todo_set& todo) : m_facts(facts), m_rule(r), m_todo(todo) {} Fact& get(unsigned idx) { func_decl *sym = m_rule->get_decl(idx); return m_facts.insert_if_not_there2(sym, Fact())->get_data().m_value; } void set_changed(unsigned idx) { m_todo.insert(m_rule->get_decl(idx)); } unsigned size() const { return m_rule->get_uninterpreted_tail_size(); } }; } #endif z3-z3-4.4.1/src/muz/dataflow/reachability.h000066400000000000000000000042171260446376700204450ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: reachability.h Abstract: Abstract domain for tracking rule reachability. Author: Henning Guenther (t-hennig) --*/ #ifndef REACHABILITY_H_ #define REACHABILITY_H_ #include "dataflow.h" namespace datalog { class reachability_info { bool m_reachable; reachability_info(bool r) : m_reachable(r) {} public: typedef ast_manager ctx_t; static const reachability_info null_fact; reachability_info() : m_reachable(false) {} void init_down(const ctx_t& m, const rule* r) { m_reachable = true; } bool init_up(const ctx_t& m, const rule* r) { if (m_reachable) return false; else { m_reachable = true; return true; } } void propagate_down(const ctx_t& manager, const rule* r, fact_writer& tail_facts) const { SASSERT(m_reachable); for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { reachability_info& tail_fact = tail_facts.get(i); if (!tail_fact.m_reachable) { tail_fact.m_reachable = true; tail_facts.set_changed(i); } } } bool propagate_up(const ctx_t& manager, const rule* r, const fact_reader& tail_facts) { if (m_reachable) return false; for (unsigned i = 0; i < r->get_positive_tail_size(); ++i) { if (!tail_facts.get(i).m_reachable) { return false; } } m_reachable = true; return true; } void join(const ctx_t& manager, const reachability_info& oth) { m_reachable |= oth.m_reachable; } void dump(const ctx_t& manager, std::ostream& outp) const { outp << (m_reachable ? "reachable" : "unreachable"); } bool is_reachable() const { return m_reachable; } }; typedef dataflow_engine reachability; } #endif z3-z3-4.4.1/src/muz/ddnf/000077500000000000000000000000001260446376700147425ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/ddnf/ddnf.cpp000066400000000000000000000711271260446376700163710ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/ddnf/ddnf.h000066400000000000000000000030711260446376700160270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/duality/000077500000000000000000000000001260446376700155025ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/duality/duality_dl_interface.cpp000077500000000000000000000512371260446376700223730ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/duality/duality_dl_interface.h000066400000000000000000000026301260446376700220260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/fp/000077500000000000000000000000001260446376700144345ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/fp/datalog_parser.cpp000066400000000000000000001347121260446376700201370ustar00rootroot00000000000000 /*++ 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 = (m_file != NULL) && (err == 0); #else m_file = fopen(fname, "rb"); m_ok = (m_file != NULL); #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(default_exception::fmt(), "%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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "number expected on line %d in file %s", m_current_line, m_current_file.c_str()); } if(*ptr!=' ' && *ptr!=0) { throw default_exception(default_exception::fmt(), "' ' 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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "number expected at line %d in file %s", m_current_line, m_current_file.c_str()); } if(*ptr!=' ') { throw default_exception(default_exception::fmt(), "' ' 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-z3-4.4.1/src/muz/fp/datalog_parser.h000066400000000000000000000014641260446376700176010ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/fp/dl_cmds.cpp000066400000000000000000000373431260446376700165570ustar00rootroot00000000000000/*++ 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(); dlctx.collect_statistics(st); st.update("time", ctx.get_seconds()); 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-z3-4.4.1/src/muz/fp/dl_cmds.h000066400000000000000000000011771260446376700162200ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/fp/dl_register_engine.cpp000066400000000000000000000024171260446376700207740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/fp/dl_register_engine.h000066400000000000000000000010741260446376700204370ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/fp/horn_tactic.cpp000066400000000000000000000312221260446376700174350ustar00rootroot00000000000000/*++ 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" #include"ast_util.h" 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; 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-z3-4.4.1/src/muz/fp/horn_tactic.h000066400000000000000000000012331260446376700171010ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/000077500000000000000000000000001260446376700146145ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/pdr/pdr_closure.cpp000066400000000000000000000123001260446376700176350ustar00rootroot00000000000000/*++ 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); 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-z3-4.4.1/src/muz/pdr/pdr_closure.h000066400000000000000000000031371260446376700173120ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_context.cpp000066400000000000000000002501341260446376700176560ustar00rootroot00000000000000/*++ 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); 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)); } 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; 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); 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(); 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-z3-4.4.1/src/muz/pdr/pdr_context.h000066400000000000000000000406711260446376700173260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_dl_interface.cpp000066400000000000000000000146431260446376700206140ustar00rootroot00000000000000/*++ 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& rules0 = m_ctx.get_rules(); datalog::rule_set old_rules(rules0); func_decl_ref query_pred(m); rm.mk_query(query, rules0); 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; } } const datalog::rule_set& rules = m_ctx.get_rules(); if (rules.get_output_predicates().empty()) { m_context->set_unsat(); return l_false; } query_pred = rules.get_output_predicate(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); m_pdr_rules.replace_rules(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-z3-4.4.1/src/muz/pdr/pdr_dl_interface.h000066400000000000000000000030701260446376700202510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_farkas_learner.cpp000066400000000000000000001033271260446376700211520ustar00rootroot00000000000000/*++ 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); 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-z3-4.4.1/src/muz/pdr/pdr_farkas_learner.h000066400000000000000000000065501260446376700206170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_generalizers.cpp000066400000000000000000000704341260446376700206670ustar00rootroot00000000000000/*++ 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); 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(); 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); 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-z3-4.4.1/src/muz/pdr/pdr_generalizers.h000066400000000000000000000076421260446376700203350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_manager.cpp000066400000000000000000000236661260446376700176140ustar00rootroot00000000000000/*++ 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); 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); 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); 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-z3-4.4.1/src/muz/pdr/pdr_manager.h000066400000000000000000000254231260446376700172520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_prop_solver.cpp000066400000000000000000000360171260446376700205460ustar00rootroot00000000000000/*++ 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) { 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-z3-4.4.1/src/muz/pdr/pdr_prop_solver.h000066400000000000000000000102331260446376700202030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_reachable_cache.cpp000066400000000000000000000062361260446376700212250ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_reachable_cache.h000066400000000000000000000030271260446376700206650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_smt_context_manager.cpp000066400000000000000000000113641260446376700222330ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_smt_context_manager.h000066400000000000000000000054151260446376700217000ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_sym_mux.cpp000066400000000000000000000376661260446376700177100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_sym_mux.h000066400000000000000000000163621260446376700173430ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/pdr/pdr_util.cpp000066400000000000000000000404201260446376700171420ustar00rootroot00000000000000/*++ 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, (expr * const *)(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); 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-z3-4.4.1/src/muz/pdr/pdr_util.h000066400000000000000000000037111260446376700166110ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/000077500000000000000000000000001260446376700146115ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/rel/aig_exporter.cpp000066400000000000000000000254761260446376700200230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/aig_exporter.h000066400000000000000000000037601260446376700174600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/check_relation.cpp000066400000000000000000000766411260446376700203050ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "check_relation.h" #include "dl_relation_manager.h" #include "qe_util.h" #include "ast_util.h" #include "smt_kernel.h" #include 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 (res == l_true) { IF_VERBOSE(0, verbose_stream() << "NOT verified " << res << "\n"; verbose_stream() << mk_pp(fml1, m) << "\n"; verbose_stream() << mk_pp(fml2, m) << "\n"; verbose_stream().flush(); ); throw default_exception("operation was not verified"); } } 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-z3-4.4.1/src/muz/rel/check_relation.h000066400000000000000000000156111260446376700177400ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_base.cpp000066400000000000000000000464731260446376700167240ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_base.h000066400000000000000000001451411260446376700163610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_bound_relation.cpp000066400000000000000000000573651260446376700210200ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_bound_relation.h000066400000000000000000000140321260446376700204450ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_check_table.cpp000066400000000000000000000437151260446376700202320ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_check_table.h000066400000000000000000000120311260446376700176620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_compiler.cpp000066400000000000000000001567201260446376700176210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_compiler.h000066400000000000000000000316231260446376700172600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_external_relation.cpp000066400000000000000000000441431260446376700215210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_external_relation.h000066400000000000000000000125431260446376700211650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_finite_product_relation.cpp000066400000000000000000003034551260446376700227210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_finite_product_relation.h000066400000000000000000000353151260446376700223630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_instruction.cpp000066400000000000000000001400261260446376700203600ustar00rootroot00000000000000/*++ 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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "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(default_exception::fmt(), "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-z3-4.4.1/src/muz/rel/dl_instruction.h000066400000000000000000000320361260446376700200260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_interval_relation.cpp000066400000000000000000000561771260446376700215350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_interval_relation.h000066400000000000000000000123331260446376700211640ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_lazy_table.cpp000066400000000000000000000370411260446376700201270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_lazy_table.h000066400000000000000000000266661260446376700176070ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_mk_explanations.cpp000066400000000000000000001014271260446376700211750ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_mk_explanations.h000066400000000000000000000044431260446376700206420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_mk_partial_equiv.cpp000066400000000000000000000107771260446376700213440ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_mk_partial_equiv.h000066400000000000000000000017431260446376700210020ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_mk_similarity_compressor.cpp000066400000000000000000000457351260446376700231430ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_mk_similarity_compressor.h000066400000000000000000000032421260446376700225730ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_mk_simple_joins.cpp000066400000000000000000000727411260446376700211710ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_mk_simple_joins.h000066400000000000000000000025271260446376700206310ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_product_relation.cpp000066400000000000000000001330021260446376700213500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_product_relation.h000066400000000000000000000156651260446376700210330ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_relation_manager.cpp000066400000000000000000002076171260446376700213200ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_relation_manager.h000066400000000000000000000735141260446376700207620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_sieve_relation.cpp000066400000000000000000000666031260446376700210170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_sieve_relation.h000066400000000000000000000172541260446376700204620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_sparse_table.cpp000066400000000000000000001532761260446376700204560ustar00rootroot00000000000000/*++ 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((char *)(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-z3-4.4.1/src/muz/rel/dl_sparse_table.h000066400000000000000000000452201260446376700201100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_table.cpp000066400000000000000000000644051260446376700170740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_table.h000066400000000000000000000205061260446376700165330ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_table_plugin.h000066400000000000000000000123231260446376700201070ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/dl_table_relation.cpp000066400000000000000000000466261260446376700207760ustar00rootroot00000000000000/*++ 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 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-z3-4.4.1/src/muz/rel/dl_vector_relation.h000066400000000000000000000321401260446376700206400ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/doc.cpp000066400000000000000000000522031260446376700160640ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/doc.h000066400000000000000000000277231260446376700155420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/karr_relation.cpp000066400000000000000000000657751260446376700201750ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "karr_relation.h" #include "bool_rewriter.h" #include "ast_util.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); 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-z3-4.4.1/src/muz/rel/karr_relation.h000066400000000000000000000051571260446376700176260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/rel_context.cpp000066400000000000000000000533561260446376700176570ustar00rootroot00000000000000/*++ 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: { const rule_set& rules = m_context.get_rules(); 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 = 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() { const rule_set& all_rules = m_context.get_rules(); rule_set::pred_set_vector const & pred_sets = all_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 = all_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-z3-4.4.1/src/muz/rel/rel_context.h000066400000000000000000000077001260446376700173140ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/tbv.cpp000066400000000000000000000203151260446376700161110ustar00rootroot00000000000000/*++ 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 = static_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-z3-4.4.1/src/muz/rel/tbv.h000066400000000000000000000073351260446376700155650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/rel/udoc_relation.cpp000066400000000000000000001362541260446376700201570ustar00rootroot00000000000000/*++ 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); 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); 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& result, bit_vector const& discard_cols) const { result.push_back(dm.allocateX()); // 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); m_udoc.push_back(dm.allocateX()); t.apply_guard(guard, m_udoc, m_equalities, 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-z3-4.4.1/src/muz/rel/udoc_relation.h000066400000000000000000000155651260446376700176250ustar00rootroot00000000000000/*++ 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, 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-z3-4.4.1/src/muz/tab/000077500000000000000000000000001260446376700145755ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/tab/tab_context.cpp000066400000000000000000001643721260446376700176300ustar00rootroot00000000000000/*++ 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" #include "ast_util.h" 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); 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()); 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); 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-z3-4.4.1/src/muz/tab/tab_context.h000066400000000000000000000015411260446376700172610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/000077500000000000000000000000001260446376700162255ustar00rootroot00000000000000z3-z3-4.4.1/src/muz/transforms/dl_mk_array_blast.cpp000066400000000000000000000236621260446376700224130ustar00rootroot00000000000000/*++ 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 "ast_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); 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)); } 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-z3-4.4.1/src/muz/transforms/dl_mk_array_blast.h000066400000000000000000000033221260446376700220470ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_backwards.cpp000066400000000000000000000042371260446376700220460ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_backwards.h000066400000000000000000000012061260446376700215040ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_bit_blast.cpp000066400000000000000000000261501260446376700220460ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_bit_blast.h000066400000000000000000000011701260446376700215060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_coalesce.cpp000066400000000000000000000150141260446376700216560ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_coalesce.h000066400000000000000000000022521260446376700213230ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_coi_filter.cpp000066400000000000000000000127411260446376700222230ustar00rootroot00000000000000/*++ Copyright (c) 2006-2015 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) Andrey Rybalchenko (rybal) Henning Guenther (t-hennig) --*/ #include "dl_mk_coi_filter.h" #include "dataflow.h" #include "reachability.h" #include "ast_pp.h" #include "extension_model_converter.h" namespace datalog { rule_set * mk_coi_filter::operator()(rule_set const & source) { scoped_ptr result1 = top_down(source); scoped_ptr result2 = bottom_up(result1 ? *result1 : source); return result2 ? result2.detach() : result1.detach(); } rule_set * mk_coi_filter::bottom_up(rule_set const & source) { dataflow_engine engine(source.get_manager(), source); engine.run_bottom_up(); 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 new_tail = false; bool contained = true; for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { if (r->is_neg_tail(i)) { if (!engine.get_fact(r->get_decl(i)).is_reachable()) { if (!new_tail) { for (unsigned j = 0; j < i; ++j) { m_new_tail.push_back(r->get_tail(j)); m_new_tail_neg.push_back(r->is_neg_tail(j)); } new_tail = true; } } else if (new_tail) { m_new_tail.push_back(r->get_tail(i)); m_new_tail_neg.push_back(true); } } else { SASSERT(!new_tail); if (!engine.get_fact(r->get_decl(i)).is_reachable()) { contained = false; break; } } } if (contained) { if (new_tail) { rule* new_r = m_context.get_rule_manager().mk(r->get_head(), m_new_tail.size(), m_new_tail.c_ptr(), m_new_tail_neg.c_ptr(), symbol::null, false); res->add_rule(new_r); } else { res->add_rule(r); } } m_new_tail.reset(); m_new_tail_neg.reset(); } 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 (dataflow_engine::iterator it = engine.begin(); it != engine.end(); it++) { if (!it->m_value.is_reachable()) { mc0->insert(it->m_key, m.mk_false()); } } m_context.add_model_converter(mc0); } CTRACE("dl", 0 != res, res->display(tout);); return res.detach(); } rule_set * mk_coi_filter::top_down(rule_set const & source) { func_decl_set pruned_preds; dataflow_engine engine(source.get_manager(), source); engine.run_top_down(); 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 (engine.get_fact(pred).is_reachable()) { 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-z3-4.4.1/src/muz/transforms/dl_mk_coi_filter.h000066400000000000000000000020361260446376700216640ustar00rootroot00000000000000/*++ Copyright (c) 2006-2015 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) Andrey Rybalchenko (rybal) Henning Guenther (t-hennig) --*/ #ifndef DL_MK_COI_FILTER_H_ #define DL_MK_COI_FILTER_H_ #include "dl_rule_transformer.h" #include "dl_context.h" namespace datalog { class mk_coi_filter : public rule_transformer::plugin { typedef obj_map decl_map; ast_manager & m; context & m_context; vector m_new_tail; svector m_new_tail_neg; 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 z3-z3-4.4.1/src/muz/transforms/dl_mk_different.h000066400000000000000000000013301260446376700215070ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_filter_rules.cpp000066400000000000000000000137671260446376700226140ustar00rootroot00000000000000/*++ 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); filter_cache::obj_map_entry *entry = m_tail2filter.insert_if_not_there2(key, 0); func_decl*& filter_decl = entry->get_data().m_value; if (!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); 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-z3-4.4.1/src/muz/transforms/dl_mk_filter_rules.h000066400000000000000000000046071260446376700222520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp000066400000000000000000000464461260446376700246520ustar00rootroot00000000000000/*++ 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" #include"ast_util.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(); 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-z3-4.4.1/src/muz/transforms/dl_mk_interp_tail_simplifier.h000066400000000000000000000057401260446376700243070ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_karr_invariants.cpp000066400000000000000000000250761260446376700233060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_karr_invariants.h000066400000000000000000000037651260446376700227540ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_loop_counter.cpp000066400000000000000000000130251260446376700226100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_loop_counter.h000066400000000000000000000021441260446376700222550ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_magic_sets.cpp000066400000000000000000000331131260446376700222160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_magic_sets.h000066400000000000000000000102121260446376700216560ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_magic_symbolic.cpp000066400000000000000000000103571260446376700230660ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_magic_symbolic.h000066400000000000000000000014041260446376700225240ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_quantifier_abstraction.cpp000066400000000000000000000315721260446376700246470ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_quantifier_abstraction.h000066400000000000000000000030271260446376700243060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_quantifier_instantiation.cpp000066400000000000000000000226001260446376700252120ustar00rootroot00000000000000/*++ 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)); } 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-z3-4.4.1/src/muz/transforms/dl_mk_quantifier_instantiation.h000066400000000000000000000036471260446376700246710ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_rule_inliner.cpp000066400000000000000000000765761260446376700226130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_rule_inliner.h000066400000000000000000000155661260446376700222500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_scale.cpp000066400000000000000000000202401260446376700211640ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_scale.h000066400000000000000000000022021260446376700206270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_separate_negated_tails.cpp000066400000000000000000000101441260446376700245660ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_separate_negated_tails.h000066400000000000000000000026731260446376700242430ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_slice.cpp000066400000000000000000000744571260446376700212170ustar00rootroot00000000000000/*++ 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 "ast_util.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)); } 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-z3-4.4.1/src/muz/transforms/dl_mk_slice.h000066400000000000000000000054511260446376700206500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_subsumption_checker.cpp000066400000000000000000000302531260446376700241560ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_subsumption_checker.h000066400000000000000000000050531260446376700236230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_unbound_compressor.cpp000066400000000000000000000325671260446376700240420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_unbound_compressor.h000066400000000000000000000052171260446376700234770ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_unfold.cpp000066400000000000000000000035501260446376700213710ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_mk_unfold.h000066400000000000000000000017371260446376700210430ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_transforms.cpp000066400000000000000000000061351260446376700216130ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/muz/transforms/dl_transforms.h000066400000000000000000000006061260446376700212550ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/000077500000000000000000000000001260446376700143355ustar00rootroot00000000000000z3-z3-4.4.1/src/nlsat/nlsat_assignment.h000066400000000000000000000047301260446376700200630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_clause.cpp000066400000000000000000000017471260446376700175270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_clause.h000066400000000000000000000036241260446376700171700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_evaluator.cpp000066400000000000000000000673451260446376700202630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_evaluator.h000066400000000000000000000025611260446376700177150ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_explain.cpp000066400000000000000000001542461260446376700177160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_explain.h000066400000000000000000000034061260446376700173520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_interval_set.cpp000066400000000000000000000720541260446376700207510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_interval_set.h000066400000000000000000000064741260446376700204210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_justification.h000066400000000000000000000063441260446376700205710ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_params.pyg000066400000000000000000000020141260446376700175370ustar00rootroot00000000000000 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-z3-4.4.1/src/nlsat/nlsat_scoped_literal_vector.h000066400000000000000000000046161260446376700222710ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_solver.cpp000066400000000000000000003036231260446376700175630ustar00rootroot00000000000000/*++ 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; reslimit& m_rlimit; 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, reslimit& rlim, params_ref const & p): m_solver(s), m_rlimit(rlim), 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 (!m_rlimit.inc()) throw solver_exception(Z3_MAX_RESOURCE_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(reslimit& rlim, params_ref const & p) { m_imp = alloc(imp, *this, rlim, 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-z3-4.4.1/src/nlsat/nlsat_solver.h000066400000000000000000000111031260446376700172150ustar00rootroot00000000000000/*++ 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" #include"rlimit.h" namespace nlsat { class solver { struct imp; imp * m_imp; public: solver(reslimit& rlim, 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-z3-4.4.1/src/nlsat/nlsat_types.cpp000066400000000000000000000035141260446376700174110ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/nlsat_types.h000066400000000000000000000123601260446376700170550ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/tactic/000077500000000000000000000000001260446376700156045ustar00rootroot00000000000000z3-z3-4.4.1/src/nlsat/tactic/goal2nlsat.cpp000066400000000000000000000224021260446376700203560ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/tactic/goal2nlsat.h000066400000000000000000000032331260446376700200240ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/tactic/nlsat_tactic.cpp000066400000000000000000000173671260446376700207760ustar00rootroot00000000000000/*++ 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(m.limit(), 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-z3-4.4.1/src/nlsat/tactic/nlsat_tactic.h000066400000000000000000000007611260446376700204310ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/tactic/qfnra_nlsat_tactic.cpp000066400000000000000000000041641260446376700221540ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/nlsat/tactic/qfnra_nlsat_tactic.h000066400000000000000000000011601260446376700216120ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/000077500000000000000000000000001260446376700140165ustar00rootroot00000000000000z3-z3-4.4.1/src/opt/bcd2.cpp000066400000000000000000000335331260446376700153430ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/bcd2.h000066400000000000000000000005341260446376700150030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/fu_malik.cpp000066400000000000000000000203221260446376700163100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/fu_malik.h000066400000000000000000000011531260446376700157560ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/hitting_sets.cpp000066400000000000000000001153111260446376700172300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/hitting_sets.h000066400000000000000000000017231260446376700166760ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/maxhs.cpp000066400000000000000000000460331260446376700156500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/maxhs.h000066400000000000000000000006051260446376700153100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/maxres.cpp000066400000000000000000000672051260446376700160330ustar00rootroot00000000000000/*++ 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; model_ref m_csmodel; unsigned m_correction_set_size; 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. bool m_pivot_on_cs; // prefer smaller correction set to core. bool m_dump_benchmarks; // display benchmarks (into wcnf format) std::string m_trace_id; 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_correction_set_size(0), 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), m_pivot_on_cs(true) { switch(st) { case s_primal: m_trace_id = "maxres"; break; case s_primal_dual: m_trace_id = "pd-maxres"; break; } } 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(13, 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); } void trace() { trace_bounds(m_trace_id.c_str()); } lbool mus_solver() { init(); init_local(); trace(); 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_false) { m_lower = m_upper; } if (is_sat == l_undef) { return is_sat; } break; case l_undef: return l_undef; default: break; } } trace(); return l_true; } lbool primal_dual_solver() { init(); init_local(); trace(); exprs cs; while (m_lower < m_upper) { lbool is_sat = check_sat_hill_climb(m_asms); if (m_cancel) { return l_undef; } switch (is_sat) { case l_true: get_current_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_false) { m_lower = m_upper; } if (is_sat == l_undef) { return is_sat; } break; case l_undef: return l_undef; default: break; } } m_lower = m_upper; trace(); 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 = check_sat(index, asms.c_ptr()); } } else { is_sat = check_sat(asms.size(), asms.c_ptr()); } return is_sat; } lbool check_sat(unsigned sz, expr* const* asms) { if (m_st == s_primal_dual && m_c.sat_enabled()) { rational max_weight = m_upper; vector weights; for (unsigned i = 0; i < sz; ++i) { weights.push_back(get_weight(asms[i])); } return inc_sat_check_sat(s(), sz, asms, weights.c_ptr(), max_weight); } else { return s().check_sat(sz, asms); } } void found_optimum() { IF_VERBOSE(1, verbose_stream() << "found optimum\n";); s().get_model(m_model); SASSERT(is_true(m_asms)); 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) { IF_VERBOSE(100, verbose_stream() << "(opt.maxres minimization failed)\n";); break; } if (core.empty()) { IF_VERBOSE(100, verbose_stream() << "(opt.maxres core is empty)\n";); 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) { 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) { ++m_stats.m_num_cs; 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";); m_csmodel = 0; m_correction_set_size = 0; } 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 update_model(expr* def, expr* value) { SASSERT(is_uninterp_const(def)); if (m_csmodel) { expr_ref val(m); SASSERT(m_csmodel.get()); VERIFY(m_csmodel->eval(value, val)); m_csmodel->register_decl(to_app(def)->get_decl(), val); } } void process_unsat(exprs const& core) { IF_VERBOSE(3, verbose_stream() << "(maxres cs model valid: " << (m_csmodel.get() != 0) << " cs size:" << m_correction_set_size << " core: " << core.size() << ")\n";); expr_ref fml(m); remove_core(core); SASSERT(!core.empty()); rational w = split_core(core); TRACE("opt", display_vec(tout << "minimized core: ", core);); IF_VERBOSE(10, display_vec(verbose_stream() << "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; if (m_st == s_primal_dual) { m_lower = std::min(m_lower, m_upper); } if (m_csmodel.get() && m_correction_set_size > 0) { // this estimate can overshoot for weighted soft constraints. --m_correction_set_size; } trace(); if (m_c.num_objectives() == 1 && m_pivot_on_cs && m_csmodel.get() && m_correction_set_size < core.size()) { exprs cs; get_current_correction_set(m_csmodel.get(), cs); m_correction_set_size = cs.size(); if (m_correction_set_size < core.size()) { process_sat(cs); return; } } } 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); } 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); fml = m.mk_and(d, b_i); update_model(dd, fml); d = dd; } asum = mk_fresh_bool("a"); cls = m.mk_or(b_i1, d); fml = m.mk_implies(asum, cls); update_model(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); update_model(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_and(b_i1, cls); update_model(asum, fml); } fml = m.mk_or(m_B.size(), m_B.c_ptr()); s().assert_expr(fml); } void update_assignment(model* mdl) { unsigned correction_set_size = 0; for (unsigned i = 0; i < m_asms.size(); ++i) { if (is_false(mdl, m_asms[i].get())) { ++correction_set_size; } } if (!m_csmodel.get() || correction_set_size < m_correction_set_size) { m_csmodel = mdl; m_correction_set_size = correction_set_size; } 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]); } DEBUG_CODE(verify_assignment();); m_upper = upper; trace(); 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); } bool is_true(expr_ref_vector const& es) { for (unsigned i = 0; i < es.size(); ++i) { if (!is_true(es[i])) return false; } return true; } 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_pivot_on_cs = _p.maxres_pivot_on_correction_set(); m_wmax = _p.maxres_wmax(); m_dump_benchmarks = _p.dump_benchmarks(); } 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(); m_csmodel = 0; m_correction_set_size = 0; } 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-z3-4.4.1/src/opt/maxres.h000066400000000000000000000007711260446376700154730ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/maxsls.cpp000066400000000000000000000032241260446376700160320ustar00rootroot00000000000000/*++ 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" #include "opt_context.h" #include "inc_sat_solver.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(); enable_sls(true); lbool is_sat = check(); 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; } lbool check() { if (m_c.sat_enabled()) { return inc_sat_check_sat( s(), m_soft.size(), m_soft.c_ptr(), m_weights.c_ptr(), m_upper); } else { return s().check_sat(0, 0); } } }; maxsmt_solver_base* mk_sls( maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { return alloc(sls, c, ws, soft); } }; z3-z3-4.4.1/src/opt/maxsls.h000066400000000000000000000007621260446376700155030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/maxsmt.cpp000066400000000000000000000216421260446376700160400ustar00rootroot00000000000000/*++ 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); // p.set_bool("minimize_core_partial", f); s().updt_params(p); } void maxsmt_solver_base::enable_sls(bool force) { m_c.enable_sls(force); } 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-z3-4.4.1/src/opt/maxsmt.h000066400000000000000000000114011260446376700154750ustar00rootroot00000000000000/*++ 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(bool force); 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-z3-4.4.1/src/opt/mss.cpp000066400000000000000000000210571260446376700153310ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/mss.h000066400000000000000000000025631260446376700147770ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/mus.cpp000066400000000000000000000142141260446376700153300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/mus.h000066400000000000000000000021551260446376700147760ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/opt_cmds.cpp000066400000000000000000000225331260446376700163370ustar00rootroot00000000000000/*++ 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_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("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 = 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) { } }; void install_opt_cmds(cmd_context & ctx) { ctx.insert(alloc(assert_soft_cmd)); ctx.insert(alloc(min_maximize_cmd, true)); ctx.insert(alloc(min_maximize_cmd, false)); } #if 0 ctx.insert(alloc(optimize_cmd)); ctx.insert(alloc(assert_weighted_cmd)); 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()); } }; 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) { } }; #endif z3-z3-4.4.1/src/opt/opt_cmds.h000066400000000000000000000005021260446376700157740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/opt_context.cpp000066400000000000000000001331451260446376700170770ustar00rootroot00000000000000/*++ 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 "tactical.h" #include "model_smt2_pp.h" #include "card2bv_tactic.h" #include "eq2bv_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" #include "ast_pp_util.h" #include "inc_sat_solver.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_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()); } display_benchmark(); 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_false) { s.get_model(m_model); } if (is_sat != l_true) { return is_sat; } IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n";); 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; } } /** \brief there is no need to use push/pop when all objectives are maxsat and engine is maxres. */ bool context::scoped_lex() { if (m_maxsat_engine == symbol("maxres")) { for (unsigned i = 0; i < m_objectives.size(); ++i) { if (m_objectives[i].m_type != O_MAXSMT) return true; } return false; } return true; } lbool context::execute_lex() { lbool r = l_true; bool sc = scoped_lex(); 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(), sc && !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; } std::string context::reason_unknown() const { if (m_solver.get()) { return m_solver->reason_unknown(); } return std::string("unknown"); } 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") || (opt_params(m_params).priority() == symbol("lex") && m_objectives.size() > 1)) { 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::enable_sls(bool force) { if ((force || 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), // 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_eq2bv_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(), mk_simplify_tactic(m))); } else { tactic_ref tac1 = and_then(tac0.get(), mk_simplify_tactic(m)); set_simplify(tac1.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)) { purify(tr); m_objectives[index].m_term = tr; } else if (is_minimize(fml, tr, orig_term, index)) { purify(tr); m_objectives[index].m_term = tr; m_objectives[index].m_adjust_value.set_negate(true); } else { m_hard_constraints.push_back(fml); } } } void context::purify(app_ref& term) { filter_model_converter_ref fm; if (m_arith.is_add(term)) { expr_ref_vector args(m); unsigned sz = term->get_num_args(); for (unsigned i = 0; i < sz; ++i) { expr* arg = term->get_arg(i); if (is_mul_const(arg)) { args.push_back(arg); } else { args.push_back(purify(fm, arg)); } } term = m_arith.mk_add(args.size(), args.c_ptr()); } else if (m_arith.is_arith_expr(term) && !is_mul_const(term)) { TRACE("opt", tout << "Purifying " << term << "\n";); term = purify(fm, term); } if (fm) { m_model_converter = concat(m_model_converter.get(), fm.get()); } } bool context::is_mul_const(expr* e) { expr* e1, *e2; return is_uninterp_const(e) || m_arith.is_numeral(e) || (m_arith.is_mul(e, e1, e2) && m_arith.is_numeral(e1) && is_uninterp_const(e2)) || (m_arith.is_mul(e, e2, e1) && m_arith.is_numeral(e1) && is_uninterp_const(e2)); } app* context::purify(filter_model_converter_ref& fm, expr* term) { std::ostringstream out; out << mk_pp(term, m); app* q = m.mk_fresh_const(out.str().c_str(), m.get_sort(term)); if (!fm) fm = alloc(filter_model_converter, m); m_hard_constraints.push_back(m.mk_eq(q, term)); fm->insert(q->get_decl()); return q; } /** 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) { filter_model_converter_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 { terms[i] = purify(fm, p); } } 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_benchmark() { if (opt_params(m_params).dump_benchmarks() && sat_enabled() && m_objectives.size() == 1 && m_objectives[0].m_type == O_MAXSMT ) { objective& o = m_objectives[0]; unsigned sz = o.m_terms.size(); inc_sat_display(verbose_stream(), get_solver(), sz, o.m_terms.c_ptr(), o.m_weights.c_ptr()); } } 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); } get_memory_statistics(stats); get_rlimit_statistics(m.limit(), stats); } 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(); } std::string context::to_string() const { smt2_pp_environment_dbg env(m); ast_pp_util visitor(m); std::ostringstream out; #define PP(_e_) ast_smt2_pp(out, _e_, env); visitor.collect(m_scoped_state.m_hard); 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: visitor.collect(obj.m_terms); break; default: UNREACHABLE(); break; } } visitor.display_decls(out); visitor.display_asserts(out, m_scoped_state.m_hard, m_pp_neat); 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-z3-4.4.1/src/opt/opt_context.h000066400000000000000000000250761260446376700165470ustar00rootroot00000000000000/*++ 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(bool force) = 0; // stochastic local search 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) virtual unsigned num_objectives() = 0; }; /** \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 { 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; 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(bool force); 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(); bool scoped_lex(); 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); void purify(app_ref& term); app* purify(filter_model_converter_ref& fm, expr* e); bool is_mul_const(expr* e); 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(); void display_benchmark(); // 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-z3-4.4.1/src/opt/opt_params.pyg000066400000000000000000000045551260446376700167150ustar00rootroot00000000000000def_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'), ('maxres.pivot_on_correction_set', BOOL, True, 'reduce soft constraints if the current correction set is smaller than current core') )) z3-z3-4.4.1/src/opt/opt_pareto.cpp000066400000000000000000000056101260446376700167000ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/opt_pareto.h000066400000000000000000000054661260446376700163560ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/opt_sls_solver.h000066400000000000000000000172051260446376700172510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/opt_solver.cpp000066400000000000000000000322211260446376700167160ustar00rootroot00000000000000/*++ 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); if (m_params.m_case_split_strategy == CS_ACTIVITY_DELAY_NEW) { 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-z3-4.4.1/src/opt/opt_solver.h000066400000000000000000000111541260446376700163650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/optsmt.cpp000066400000000000000000000343351260446376700160600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/optsmt.h000066400000000000000000000037651260446376700155300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/pb_sls.cpp000066400000000000000000000643571260446376700160230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/pb_sls.h000066400000000000000000000015621260446376700154550ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/wmax.cpp000066400000000000000000000043561260446376700155060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/opt/wmax.h000066400000000000000000000005471260446376700151510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/parsers/000077500000000000000000000000001260446376700146735ustar00rootroot00000000000000z3-z3-4.4.1/src/parsers/smt/000077500000000000000000000000001260446376700154765ustar00rootroot00000000000000z3-z3-4.4.1/src/parsers/smt/smtlib.cpp000066400000000000000000000154251260446376700175030ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include"smtlib.h" #include"ast_pp.h" #include"ast_smt2_pp.h" #ifdef _WINDOWS #ifdef ARRAYSIZE #undef ARRAYSIZE #endif #include #include #endif #include using namespace smtlib; // -------------------------------------------------------------------------- // symtable symtable::~symtable() { reset(); } void symtable::reset() { svector*> range; m_ids.get_range(range); for (unsigned i = 0; i < range.size(); ++i) { ptr_vector const & v = *range[i]; for (unsigned j = 0; j < v.size(); ++j) { m_manager.dec_ref(v[j]); } dealloc(range[i]); } m_ids.reset(); ptr_vector sorts; m_sorts1.get_range(sorts); for (unsigned i = 0; i < sorts.size(); ++i) { m_manager.dec_ref(sorts[i]); } m_sorts1.reset(); ptr_vector sort_builders; m_sorts.get_range(sort_builders); for (unsigned i = 0; i < sort_builders.size(); ++i) { dealloc(sort_builders[i]); } m_sorts.reset(); } void symtable::insert(symbol s, func_decl * d) { ptr_vector* decls = 0; m_manager.inc_ref(d); if (!m_ids.find(s, decls)) { SASSERT(!decls); decls = alloc(ptr_vector); decls->push_back(d); m_ids.insert(s, decls); } else { SASSERT(decls); if ((*decls)[0] != d) { decls->push_back(d); } else { m_manager.dec_ref(d); } } } bool symtable::find1(symbol s, func_decl*& d) { ptr_vector* decls = 0; if (!m_ids.find(s, decls)) { SASSERT(!decls); return false; } SASSERT(decls && !decls->empty()); d = (*decls)[0]; return true; } bool symtable::find_overload(symbol s, ptr_vector const & dom, func_decl * & d) { ptr_vector* decls = 0; d = 0; if (!m_ids.find(s, decls)) { SASSERT(!decls); return false; } SASSERT(decls); for (unsigned i = 0; i < decls->size(); ++i) { func_decl* decl = (*decls)[i]; if (decl->is_associative() && decl->get_arity() > 0) { for (unsigned j = 0; j < dom.size(); ++j) { if (dom[j] != decl->get_domain(0)) { goto try_next; } } d = decl; return true; } if (decl->get_arity() != dom.size()) { goto try_next; } for (unsigned j = 0; j < decl->get_arity(); ++j) { if (decl->get_domain(j) != dom[j]) { goto try_next; } } d = decl; return true; try_next: if (decl->get_family_id() == m_manager.get_basic_family_id() && decl->get_decl_kind() == OP_DISTINCT) { // we skip type checking for 'distinct' d = decl; return true; } } return false; } // Store in result the func_decl that are not attached to any family id. // That is, the uninterpreted constants and function declarations. void symtable::get_func_decls(ptr_vector & result) const { svector*> tmp; m_ids.get_range(tmp); svector*>::const_iterator it = tmp.begin(); svector*>::const_iterator end = tmp.end(); for (; it != end; ++it) { ptr_vector * curr = *it; if (curr) { ptr_vector::const_iterator it2 = curr->begin(); ptr_vector::const_iterator end2 = curr->end(); for (; it2 != end2; ++it2) { func_decl * d = *it2; if (d && d->get_family_id() == null_family_id) { result.push_back(d); } } } } } void symtable::insert(symbol s, sort_builder* sb) { m_sorts.insert(s, sb); } bool symtable::lookup(symbol s, sort_builder*& sb) { return m_sorts.find(s, sb); } void symtable::push_sort(symbol name, sort* srt) { m_sorts.begin_scope(); sort_builder* sb = alloc(basic_sort_builder,srt); m_sorts.insert(name, sb); m_sorts_trail.push_back(sb); } void symtable::pop_sorts(unsigned num_sorts) { while (num_sorts > 0) { dealloc(m_sorts_trail.back()); m_sorts_trail.pop_back(); m_sorts.end_scope(); } } void symtable::get_sorts(ptr_vector& result) const { vector tmp; m_sorts1.get_range(tmp); for (unsigned i = 0; i < tmp.size(); ++i) { if (tmp[i]->get_family_id() == null_family_id) { result.push_back(tmp[i]); } } } // -------------------------------------------------------------------------- // theory func_decl * theory::declare_func(symbol const & id, sort_ref_buffer & domain, sort * range, bool is_assoc, bool is_comm, bool is_inj) { func_decl * decl = m_ast_manager.mk_func_decl(id, domain.size(), domain.c_ptr(), range, is_assoc, is_comm, is_inj); m_symtable.insert(id, decl); m_asts.push_back(decl); return decl; } sort * theory::declare_sort(symbol const & id) { sort * decl = m_ast_manager.mk_uninterpreted_sort(id); m_symtable.insert(id, decl); m_asts.push_back(decl); return decl; } bool theory::get_func_decl(symbol id, func_decl * & decl) { return m_symtable.find1(id, decl); } bool theory::get_sort(symbol id, sort* & s) { return m_symtable.find(id, s); } bool theory::get_const(symbol id, expr * & term) { func_decl* decl = 0; if (!get_func_decl(id,decl)) { return false; } if (decl->get_arity() != 0) { return false; } term = m_ast_manager.mk_const(decl); m_asts.push_back(term); return true; } void benchmark::display_as_smt2(std::ostream & out) const { if (m_logic != symbol::null) out << "(set-logic " << m_logic << ")\n"; out << "(set-info :smt-lib-version 2.0)\n"; out << "(set-info :status "; switch (m_status) { case SAT: out << "sat"; break; case UNSAT: out << "unsat"; break; default: out << "unknown"; break; } out << ")\n"; #if 0 ast_manager & m = m_ast_manager; ptr_vector decls; m_symtable.get_func_decls(decls); ptr_vector::const_iterator it = decls.begin(); ptr_vector::const_iterator end = decls.end(); for (; it != end; ++it) { func_decl * f = *it; out << "(declare-fun " << f->get_name() << " ("; for (unsigned i = 0; i < f->get_arity(); i++) { if (i > 0) out << " "; out << mk_ismt2_pp(f->get_domain(i), m); } out << ") " << mk_ismt2_pp(f->get_range(), m); out << ")\n"; } #endif } z3-z3-4.4.1/src/parsers/smt/smtlib.h000066400000000000000000000131201260446376700171360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smtlib.h Abstract: SMT library utilities Author: Nikolaj Bjorner (nbjorner) 2006-09-29 Revision History: --*/ #ifndef SMTLIB_H_ #define SMTLIB_H_ #include "ast.h" #include "symbol_table.h" #include "map.h" #include "arith_decl_plugin.h" namespace smtlib { class sort_builder { public: virtual ~sort_builder() {} virtual bool apply(unsigned num_params, parameter const* params, sort_ref& result) = 0; virtual char const* error_message() { return ""; } }; class basic_sort_builder : public sort_builder { sort* m_sort; public: basic_sort_builder(sort* s) : m_sort(s) {} virtual bool apply(unsigned np, parameter const*, sort_ref& result) { result = m_sort; return m_sort && np != 0; } }; class symtable { ast_manager& m_manager; symbol_table m_sorts1; symbol_table m_sorts; ptr_vector m_sorts_trail; symbol_table* > m_ids; public: symtable(ast_manager& m): m_manager(m) {} ~symtable(); void reset(); void insert(symbol s, func_decl * d); bool find(symbol s, ptr_vector * & decls) { return m_ids.find(s, decls); } bool find1(symbol s, func_decl * & d); bool find_overload(symbol s, ptr_vector const & dom, func_decl * & d); void insert(symbol s, sort * d) { sort * d2; if (m_sorts1.find(s, d2)) { m_manager.dec_ref(d2); } m_manager.inc_ref(d); m_sorts1.insert(s, d); } bool find(symbol s, sort * & d) { return m_sorts1.find(s, d); } void insert(symbol s, sort_builder* sb); bool lookup(symbol s, sort_builder*& sb); void push_sort(symbol s, sort*); void pop_sorts(unsigned num_sorts); void get_func_decls(ptr_vector & result) const; void get_sorts(ptr_vector& result) const; }; class theory { public: typedef ptr_vector::const_iterator expr_iterator; theory(ast_manager & ast_manager, symbol const& name): m_name(name), m_ast_manager(ast_manager), m_symtable(ast_manager), m_asts(ast_manager) {} virtual ~theory() {} symtable * get_symtable() { return &m_symtable; } void insert(sort * s) { m_symtable.insert(s->get_name(), s); } void insert(func_decl * c) { m_symtable.insert(c->get_name(), c); } func_decl * declare_func(symbol const & id, sort_ref_buffer & domain, sort * range, bool is_assoc, bool is_comm, bool is_inj); sort * declare_sort(symbol const & id); void add_axiom(expr * axiom) { m_asts.push_back(axiom); m_axioms.push_back(axiom); } expr_iterator begin_axioms() const { return m_axioms.begin(); } unsigned get_num_axioms() const { return m_axioms.size(); } expr * const * get_axioms() const { return m_axioms.c_ptr(); } expr_iterator end_axioms() const { return m_axioms.end(); } void add_assumption(expr * axiom) { m_asts.push_back(axiom); m_assumptions.push_back(axiom); } unsigned get_num_assumptions() const { return m_assumptions.size(); } expr * const * get_assumptions() const { return m_assumptions.c_ptr(); } bool get_func_decl(symbol, func_decl*&); bool get_sort(symbol, sort*&); bool get_const(symbol, expr*&); void set_name(symbol const& name) { m_name = name; } symbol const get_name() const { return m_name; } protected: symbol m_name; ast_manager& m_ast_manager; ptr_vector m_axioms; ptr_vector m_assumptions; symtable m_symtable; ast_ref_vector m_asts; private: theory& operator=(theory const&); theory(theory const&); }; class benchmark : public theory { public: enum status { UNKNOWN, SAT, UNSAT }; benchmark(ast_manager & ast_manager, symbol const & name) : theory(ast_manager, name), m_status(UNKNOWN) {} virtual ~benchmark() {} status get_status() const { return m_status; } void set_status(status status) { m_status = status; } symbol get_logic() const { if (m_logic == symbol::null) { return symbol("ALL"); } return m_logic; } void set_logic(symbol const & s) { m_logic = s; } unsigned get_num_formulas() const { return m_formulas.size(); } expr_iterator begin_formulas() const { return m_formulas.begin(); } expr_iterator end_formulas() const { return m_formulas.end(); } void add_formula(expr * formula) { m_asts.push_back(formula); m_formulas.push_back(formula); } void display_as_smt2(std::ostream & out) const; private: status m_status; symbol m_logic; ptr_vector m_formulas; }; }; #endif z3-z3-4.4.1/src/parsers/smt/smtlib_solver.cpp000066400000000000000000000070641260446376700210750ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smtlib_solver.cpp Abstract: SMT based solver. Author: Nikolaj Bjorner (nbjorner) 2006-11-3. Revision History: --*/ #include"smtparser.h" #include"smtlib_solver.h" #include"warning.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"well_sorted.h" #include"model.h" #include"model_v2_pp.h" #include"solver.h" #include"smt_strategic_solver.h" #include"cmd_context.h" #include"model_params.hpp" #include"parser_params.hpp" namespace smtlib { solver::solver(): m_ast_manager(m_params.m_proof ? PGM_FINE : PGM_DISABLED, m_params.m_trace ? m_params.m_trace_file_name.c_str() : 0), m_ctx(0), m_error_code(0) { parser_params ps; m_parser = parser::create(m_ast_manager, ps.ignore_user_patterns()); m_parser->initialize_smtlib(); } solver::~solver() { if (m_ctx) dealloc(m_ctx); } bool solver::solve_smt(char const * benchmark_file) { IF_VERBOSE(100, verbose_stream() << "parsing...\n";); if (!m_parser->parse_file(benchmark_file)) { if (benchmark_file) { warning_msg("could not parse file '%s'.", benchmark_file); } else { warning_msg("could not parse input stream."); } m_error_code = ERR_PARSER; return false; } benchmark * benchmark = m_parser->get_benchmark(); solve_benchmark(*benchmark); return true; } bool solver::solve_smt_string(char const * benchmark_string) { if (!m_parser->parse_string(benchmark_string)) { warning_msg("could not parse string '%s'.", benchmark_string); return false; } benchmark * benchmark = m_parser->get_benchmark(); solve_benchmark(*benchmark); return true; } void solver::display_statistics() { if (m_ctx) m_ctx->display_statistics(); } void solver::solve_benchmark(benchmark & benchmark) { if (benchmark.get_num_formulas() == 0) { // Hack: it seems SMT-LIB allow benchmarks without any :formula benchmark.add_formula(m_ast_manager.mk_true()); } m_ctx = alloc(cmd_context, true, &m_ast_manager, benchmark.get_logic()); m_ctx->set_solver_factory(mk_smt_strategic_solver_factory()); theory::expr_iterator fit = benchmark.begin_formulas(); theory::expr_iterator fend = benchmark.end_formulas(); for (; fit != fend; ++fit) solve_formula(benchmark, *fit); } void solver::solve_formula(benchmark const & benchmark, expr * f) { IF_VERBOSE(100, verbose_stream() << "starting...\n";); m_ctx->reset(); for (unsigned i = 0; i < benchmark.get_num_axioms(); i++) m_ctx->assert_expr(benchmark.get_axioms()[i]); m_ctx->assert_expr(f); m_ctx->check_sat(benchmark.get_num_assumptions(), benchmark.get_assumptions()); check_sat_result * r = m_ctx->get_check_sat_result(); if (r != 0) { proof * pr = r->get_proof(); if (pr != 0 && m_params.m_proof) std::cout << mk_ll_pp(pr, m_ast_manager, false, false); model_ref md; if (r->status() != l_false) r->get_model(md); if (md.get() != 0 && m_params.m_model) { model_params p; model_v2_pp(std::cout, *(md.get()), p.partial()); } } else { m_error_code = ERR_UNKNOWN_RESULT; } } }; z3-z3-4.4.1/src/parsers/smt/smtlib_solver.h000066400000000000000000000017321260446376700205360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smtlib_solver.h Abstract: SMT based solver. Author: Nikolaj Bjorner (nbjorner) 2006-11-3. Revision History: --*/ #ifndef SMTLIB_SOLVER_H_ #define SMTLIB_SOLVER_H_ #include"smtparser.h" #include"context_params.h" #include"lbool.h" class cmd_context; namespace smtlib { class solver { context_params m_params; ast_manager m_ast_manager; cmd_context * m_ctx; scoped_ptr m_parser; unsigned m_error_code; public: solver(); ~solver(); bool solve_smt(char const * benchmark_file); bool solve_smt_string(char const * benchmark_string); void display_statistics(); unsigned get_error_code() const { return m_error_code; } private: void solve_benchmark(benchmark & benchmark); void solve_formula(benchmark const & benchmark, expr * f); }; }; #endif z3-z3-4.4.1/src/parsers/smt/smtparser.cpp000066400000000000000000002676111260446376700202370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smtparser.cpp Abstract: SMT parser into ast. Author: Nikolaj Bjorner (nbjorner) 2006-10-4. Leonardo de Moura (leonardo) Revision History: --*/ #include #include #include #include #include #include"region.h" #include"scanner.h" #include"symbol.h" #include"vector.h" #include"symbol_table.h" #include"smtlib.h" #include"smtparser.h" #include"ast_pp.h" #include"bv_decl_plugin.h" #include"array_decl_plugin.h" #include"warning.h" #include"error_codes.h" #include"pattern_validation.h" #include"var_subst.h" #include"well_sorted.h" #include"str_hashtable.h" #include"stopwatch.h" class id_param_info { symbol m_string; unsigned m_num_params; parameter m_params[0]; public: id_param_info(symbol const& s, unsigned n, parameter const* p) : m_string(s), m_num_params(n) { for (unsigned i = 0; i < n; ++i) { new (&(m_params[i])) parameter(); m_params[i] = p[i]; } } symbol string() const { return m_string; } parameter * params() { return m_params; } unsigned num_params() const { return m_num_params; } }; class proto_region { ptr_vector m_rationals; ptr_vector m_id_infos; region m_region; public: proto_region() { } ~proto_region() { reset(); } rational* allocate(rational const & n) { rational* r = alloc(rational, n); m_rationals.push_back(r); return r; } id_param_info* allocate(vector const& params, symbol const & s) { unsigned size = sizeof(id_param_info) + sizeof(parameter)*(params.size()); id_param_info* r = static_cast(m_region.allocate(size)); new (r) id_param_info(s, params.size(), params.c_ptr()); m_id_infos.push_back(r); return r; } void* allocate(size_t s) { return m_region.allocate(s); } void reset() { for (unsigned i = 0; i < m_rationals.size(); ++i) { dealloc(m_rationals[i]); } for (unsigned i = 0; i < m_id_infos.size(); ++i) { unsigned n = m_id_infos[i]->num_params(); for (unsigned j = 0; j < n; ++j) { m_id_infos[i]->params()[j].~parameter(); } } m_rationals.reset(); m_id_infos.reset(); m_region.reset(); } private: }; inline void * operator new(size_t s, proto_region& r) { return r.allocate(s); } inline void * operator new[](size_t s, proto_region & r) { return r.allocate(s); } inline void operator delete(void*, proto_region& r) {} inline void operator delete[](void *, proto_region& r) {} class proto_expr { public: enum kind_t { ID, STRING, COMMENT, ANNOTATION, INT, FLOAT, CONS }; private: int m_kind:8; int m_line:24; int m_pos; union { id_param_info* m_id_info; rational* m_number; proto_expr** m_children; }; public: symbol string() { if (m_kind == INT || m_kind == FLOAT) { std::string s = m_number->to_string(); return symbol(s.c_str()); } if (m_kind == CONS) { return symbol(""); } SASSERT(m_kind == STRING || m_kind == COMMENT || m_kind == ID || m_kind == ANNOTATION); return m_id_info->string(); } rational const& number() { SASSERT(m_kind == INT || m_kind == FLOAT); return *m_number; } proto_expr* const* children() const { if (m_kind == CONS) { return m_children; } else { return 0; } } int line() { return m_line; } int pos() { return m_pos; } kind_t kind() { return static_cast(m_kind); } unsigned num_params() const { SASSERT(m_kind == ID); return m_id_info->num_params(); } parameter * params() { SASSERT(m_kind == ID); return m_id_info->params(); } proto_expr(proto_region & region, kind_t kind, symbol const & s, vector const & params, int line, int pos): m_kind(kind), m_line(line), m_pos(pos), m_id_info(region.allocate(params, s)) { SASSERT(kind != CONS); SASSERT(kind != INT); SASSERT(kind != FLOAT); } proto_expr(proto_region& region, bool is_int, rational const & n, int line, int pos): m_kind(is_int?INT:FLOAT), m_line(line), m_pos(pos), m_number(region.allocate(n)) {} proto_expr(proto_region& region, ptr_vector& proto_exprs, int line, int pos): m_kind(CONS), m_line(line), m_pos(pos) { // // null terminated list of proto_expression pointers. // unsigned num_children = proto_exprs.size(); m_children = new (region) proto_expr*[num_children+1]; for (unsigned i = 0; i < num_children; ++i) { m_children[i] = proto_exprs[i]; } m_children[num_children] = 0; } ~proto_expr() {} static proto_expr* copy(proto_region& r, proto_expr* e) { switch(e->kind()) { case proto_expr::CONS: { ptr_vector args; proto_expr* const* children = e->children(); while (children && *children) { args.push_back(copy(r, *children)); ++children; } return new (r) proto_expr(r, args, e->line(), e->pos()); } case proto_expr::INT: { return new (r) proto_expr(r, true, e->number(), e->line(), e->pos()); } case proto_expr::FLOAT: { return new (r) proto_expr(r, false, e->number(), e->line(), e->pos()); } case proto_expr::ID: { vector params; for (unsigned i = 0; i < e->num_params(); ++i) { params.push_back(e->params()[i]); } return new (r) proto_expr(r, e->kind(), e->string(), params, e->line(), e->pos()); } default: { vector params; return new (r) proto_expr(r, e->kind(), e->string(), params, e->line(), e->pos()); } } } private: proto_expr(proto_expr const & other); proto_expr& operator=(proto_expr const & other); }; // // build up proto_expr tree from token stream. // class proto_expr_parser { proto_region& m_region; scanner& m_scanner; std::ostream& m_err; bool m_at_eof; public: proto_expr_parser(proto_region& region, scanner& scanner, std::ostream& err): m_region(region), m_scanner(scanner), m_err(err), m_at_eof(false) { } ~proto_expr_parser() {} bool parse(ptr_vector & proto_exprs, bool parse_single_expr = false) { scanner::token token; vector stack; proto_expr* result = 0; stack.push_back(frame(PROTO_EXPRS_PRE)); token = m_scanner.scan(); if (token == scanner::EOF_TOKEN) { proto_exprs.reset(); return true; } while (!stack.empty()) { if (token == scanner::EOF_TOKEN) { break; } if (token == scanner::ERROR_TOKEN) { print_error("unexpected token"); goto done; } switch(stack.back().m_state) { case PROTO_EXPR: switch (token) { case scanner::LEFT_PAREN: stack.back().m_state = PROTO_EXPRS_PRE; token = m_scanner.scan(); break; default: stack.back().m_state = ATOM; break; } break; case ATOM: SASSERT(!result); switch(token) { case scanner::ID_TOKEN: result = new (m_region) proto_expr(m_region, proto_expr::ID, m_scanner.get_id(), m_scanner.get_params(), m_scanner.get_line(), m_scanner.get_pos()); break; case scanner::INT_TOKEN: result = new (m_region) proto_expr(m_region, true, m_scanner.get_number(), m_scanner.get_line(), m_scanner.get_pos()); break; case scanner::FLOAT_TOKEN: result = new (m_region) proto_expr(m_region, false, m_scanner.get_number(), m_scanner.get_line(), m_scanner.get_pos()); break; case scanner::STRING_TOKEN: result = new (m_region) proto_expr(m_region, proto_expr::STRING, m_scanner.get_id(), m_scanner.get_params(), m_scanner.get_line(), m_scanner.get_pos()); break; case scanner::COMMENT_TOKEN: result = new (m_region) proto_expr(m_region, proto_expr::COMMENT, m_scanner.get_id(), m_scanner.get_params(), m_scanner.get_line(), m_scanner.get_pos()); break; case scanner::COLON: token = m_scanner.scan(); if (token == scanner::ID_TOKEN) { result = new (m_region) proto_expr(m_region, proto_expr::ANNOTATION, m_scanner.get_id(), m_scanner.get_params(), m_scanner.get_line(), m_scanner.get_pos()); } else { print_error("unexpected identifier ':'"); token = scanner::ERROR_TOKEN; goto done; } break; default: print_error("unexpected token"); token = scanner::ERROR_TOKEN; goto done; } stack.pop_back(); SASSERT(!stack.empty()); stack.back().m_proto_exprs.push_back(result); result = 0; if (parse_single_expr && stack.size() == 1) { goto done; } token = m_scanner.scan(); break; case PROTO_EXPRS_PRE: SASSERT(!result); switch(token) { case scanner::RIGHT_PAREN: result = new (m_region) proto_expr(m_region, stack.back().m_proto_exprs, m_scanner.get_line(), m_scanner.get_pos()); stack.pop_back(); if (stack.empty()) { print_error("unexpected right parenthesis"); token = scanner::ERROR_TOKEN; result = 0; goto done; } stack.back().m_proto_exprs.push_back(result); if (parse_single_expr && stack.size() == 1) { goto done; } result = 0; token = m_scanner.scan(); break; case scanner::EOF_TOKEN: m_at_eof = true; break; case scanner::ERROR_TOKEN: print_error("could not parse expression"); goto done; default: stack.back().m_state = PROTO_EXPRS_POST; stack.push_back(frame(PROTO_EXPR)); break; } break; case PROTO_EXPRS_POST: stack.back().m_state = PROTO_EXPRS_PRE; break; } } done: if (stack.size() == 1) { for (unsigned i = 0; i < stack.back().m_proto_exprs.size(); ++i) { proto_exprs.push_back(stack.back().m_proto_exprs[i]); } return true; } if (stack.size() == 2) { proto_exprs.push_back(new (m_region) proto_expr(m_region, stack.back().m_proto_exprs, m_scanner.get_line(), m_scanner.get_pos())); return true; } print_error("unexpected nesting of parenthesis: ", stack.size()); return false; } int get_line() { return m_scanner.get_line(); } bool at_eof() const { return m_at_eof; } private: template void print_error(char const* msg1, T msg2) { m_err << "ERROR: line " << m_scanner.get_line() << " column " << m_scanner.get_pos() << ": " << msg1 << msg2 << "\n"; } void print_error(char const* msg) { print_error(msg, ""); } // stack frame: enum frame_state { PROTO_EXPR, PROTO_EXPRS_PRE, PROTO_EXPRS_POST, ATOM }; class frame { public: frame_state m_state; ptr_vector m_proto_exprs; frame(frame_state state): m_state(state){ } frame(frame const & other): m_state(other.m_state), m_proto_exprs(other.m_proto_exprs) { } private: frame& operator=(frame const &); }; }; using namespace smtlib; class idbuilder { public: virtual ~idbuilder() {} virtual bool apply(expr_ref_vector const & args, expr_ref & result) = 0; }; class builtin_sort_builder : public sort_builder { ast_manager& m_manager; family_id m_fid; decl_kind m_kind; public: builtin_sort_builder(ast_manager& m, family_id fid, decl_kind k) : m_manager(m), m_fid(fid), m_kind(k) {} virtual bool apply(unsigned num_params, parameter const* params, sort_ref & result) { result = m_manager.mk_sort(m_fid, m_kind, num_params, params); return result.get() != 0; } }; class array_sort : public builtin_sort_builder { public: array_sort(ast_manager& m) : builtin_sort_builder(m, m.mk_family_id("array"), ARRAY_SORT) {} }; class bv_sort : public builtin_sort_builder { public: bv_sort(ast_manager& m) : builtin_sort_builder(m, m.mk_family_id("bv"), BV_SORT) {} }; class user_sort : public sort_builder { user_sort_plugin * m_plugin; decl_kind m_kind; symbol m_name; unsigned m_num_args; std::string m_error_message; public: user_sort(ast_manager& m, unsigned num_args, symbol name): m_name(name), m_num_args(num_args) { m_plugin = m.get_user_sort_plugin(); m_kind = m_plugin->register_name(name); } ~user_sort() {} virtual bool apply(unsigned num_params, parameter const* params, sort_ref & result) { if (num_params != m_num_args) { std::ostringstream strm; strm << "wrong number of arguments passed to " << m_name << " " << m_num_args << " expected, but " << num_params << " given"; m_error_message = strm.str(); return false; } result = m_plugin->mk_sort(m_kind, num_params, params); return true; } virtual char const* error_message() { return m_error_message.c_str(); } }; class smtparser : public parser { struct builtin_op { family_id m_family_id; decl_kind m_kind; builtin_op() : m_family_id(null_family_id), m_kind(0) {} builtin_op(family_id fid, decl_kind k) : m_family_id(fid), m_kind(k) {} }; class add_plugins { public: add_plugins(ast_manager& m) { #define REGISTER_PLUGIN(NAME, MK) { \ family_id fid = m.mk_family_id(symbol(NAME)); \ if (!m.has_plugin(fid)) { \ m.register_plugin(fid, MK); \ } \ } ((void) 0) REGISTER_PLUGIN("arith", alloc(arith_decl_plugin)); REGISTER_PLUGIN("bv", alloc(bv_decl_plugin)); REGISTER_PLUGIN("array", alloc(array_decl_plugin)); }; }; ast_manager& m_manager; add_plugins m_plugins; arith_util m_anum_util; bv_util m_bvnum_util; pattern_validator m_pattern_validator; bool m_ignore_user_patterns; unsigned m_binding_level; // scope level for bound vars benchmark m_benchmark; // currently parsed benchmark typedef map op_map; op_map m_builtin_ops; op_map m_builtin_sorts; symbol m_let; // commonly used symbols. symbol m_flet; symbol m_forall; symbol m_exists; symbol m_lblneg; symbol m_lblpos; symbol m_associative; symbol m_commutative; symbol m_injective; symbol m_sorts; symbol m_funs; symbol m_preds; symbol m_axioms; symbol m_notes; symbol m_array; symbol m_bang; symbol m_underscore; sort* m_int_sort; sort* m_real_sort; family_id m_bv_fid; family_id m_arith_fid; family_id m_array_fid; family_id m_rel_fid; func_decl * m_sk_hack; std::ostream* m_err; bool m_display_error_for_vs; public: smtparser(ast_manager& m, bool ignore_user_patterns): m_manager(m), m_plugins(m), m_anum_util(m), m_bvnum_util(m), m_pattern_validator(m), m_ignore_user_patterns(ignore_user_patterns), m_binding_level(0), m_benchmark(m_manager, symbol("")), m_let("let"), m_flet("flet"), m_forall("forall"), m_exists("exists"), m_lblneg("lblneg"), m_lblpos("lblpos"), m_associative("assoc"), m_commutative("comm"), m_injective("injective"), m_sorts("sorts"), m_funs("funs"), m_preds("preds"), m_axioms("axioms"), m_notes("notes"), m_array("array"), m_bang("!"), m_underscore("_"), m_err(0), m_display_error_for_vs(false) { family_id bfid = m_manager.get_basic_family_id(); add_builtin_type("bool", bfid, BOOL_SORT); m_benchmark.get_symtable()->insert(symbol("Array"), alloc(array_sort, m)); m_benchmark.get_symtable()->insert(symbol("BitVec"), alloc(bv_sort, m)); add_builtins(bfid); } ~smtparser() { } void set_error_stream(std::ostream& strm) { m_err = &strm; } std::ostream& get_err() { return m_err?(*m_err):std::cerr; } bool ignore_user_patterns() const { return m_ignore_user_patterns; } bool parse_stream(std::istream& stream) { proto_region region; scanner scanner(stream, get_err(), false); proto_expr_parser parser(region, scanner, get_err()); return parse(parser); } bool parse_file(char const * filename) { if (filename != 0) { std::ifstream stream(filename); if (!stream) { get_err() << "ERROR: could not open file '" << filename << "'.\n"; return false; } return parse_stream(stream); } else { return parse_stream(std::cin); } } bool parse_string(char const * str) { std::string s = str; std::istringstream is(s); return parse_stream(is); } void add_builtin_op(char const * s, family_id fid, decl_kind k) { m_builtin_ops.insert(symbol(s), builtin_op(fid, k)); } void add_builtin_type(char const * s, family_id fid, decl_kind k) { m_builtin_sorts.insert(symbol(s), builtin_op(fid, k)); } void initialize_smtlib() { smtlib::symtable* table = m_benchmark.get_symtable(); symbol arith("arith"); family_id afid = m_manager.mk_family_id(arith); m_arith_fid = afid; add_builtin_type("Int", afid, INT_SORT); add_builtin_type("Real", afid, REAL_SORT); add_builtin_type("Bool", m_manager.get_basic_family_id(), BOOL_SORT); m_int_sort = m_manager.mk_sort(m_arith_fid, INT_SORT); m_real_sort = m_manager.mk_sort(m_arith_fid, REAL_SORT); add_builtins(afid); symbol bv("bv"); family_id bfid = m_manager.mk_family_id(bv); m_bv_fid = bfid; add_builtins(bfid); add_builtin_type("BitVec", bfid, BV_SORT); symbol array("array"); afid = m_manager.mk_family_id(array); m_array_fid = afid; add_builtins(afid); sort* a1, *a2; func_decl* store1, *sel1, *store2, *sel2; // Array parameter params0[2] = { parameter(m_int_sort), parameter(m_int_sort) }; a1 = m_manager.mk_sort(afid, ARRAY_SORT, 2, params0); table->insert(symbol("Array"), a1); parameter param0(a1); sort * args0[3] = { a1, m_int_sort, m_int_sort }; store1 = m_manager.mk_func_decl(afid, OP_STORE, 0, 0, 3, args0); table->insert(symbol("store"), store1); sel1 = m_manager.mk_func_decl(afid, OP_SELECT, 0, 0, 2, args0); table->insert(symbol("select"), sel1); // Array1 parameter params1[2] = { parameter(m_int_sort), parameter(m_real_sort) }; a1 = m_manager.mk_sort(afid, ARRAY_SORT, 2, params1); table->insert(symbol("Array1"), a1); parameter param1(a1); sort * args1[3] = { a1, m_int_sort, m_real_sort }; store1 = m_manager.mk_func_decl(afid, OP_STORE, 0, 0, 3, args1); table->insert(symbol("store"), store1); sel1 = m_manager.mk_func_decl(afid, OP_SELECT, 0, 0, 2, args1); table->insert(symbol("select"), sel1); // Array2 parameter params2[2] = { parameter(m_int_sort), parameter(a1) }; a2 = m_manager.mk_sort(afid, ARRAY_SORT, 2, params2); table->insert(symbol("Array2"), a2); parameter param2(a2); sort * args2[3] = { a2, m_int_sort, a1 }; store2 = m_manager.mk_func_decl(afid, OP_STORE, 0, 0, 3, args2); table->insert(symbol("store"), store2); sel2 = m_manager.mk_func_decl(afid, OP_SELECT, 0, 0, 2, args2); table->insert(symbol("select"), sel2); m_benchmark.declare_sort(symbol("U")); sort * bool_sort = m_manager.mk_bool_sort(); m_sk_hack = m_manager.mk_func_decl(symbol("sk_hack"), 1, &bool_sort, bool_sort); table->insert(symbol("sk_hack"), m_sk_hack); } void add_builtins(family_id fid) { decl_plugin * plugin = m_manager.get_plugin(fid); SASSERT(plugin); svector op_names; plugin->get_op_names(op_names); for (unsigned i = 0; i < op_names.size(); ++i) { add_builtin_op(op_names[i].m_name.bare_str(), fid, op_names[i].m_kind); } } smtlib::benchmark* get_benchmark() { return &m_benchmark; } private: bool parse(proto_expr_parser& parser) { symbol benchmark("benchmark"); symbol name(""); proto_expr* const* rest = 0; ptr_vector proto_exprs; bool result = parser.parse(proto_exprs); proto_expr* proto_expr = 0; if (!result) { get_err() << "ERROR: parse error at line " << parser.get_line() << ".\n"; } for (unsigned i = 0; result && i < proto_exprs.size(); ++i) { proto_expr = proto_exprs[i]; if (match_cons(proto_expr, benchmark, name, rest)) { result = make_benchmark(name, rest); } else if (proto_expr && proto_expr->kind() != proto_expr::COMMENT) { set_error("could not match expression to benchmark ", proto_expr); } else { // proto_expr->kind() == proto_expr::COMMENT. } } return result; } void error_prefix(proto_expr* e) { if (m_display_error_for_vs) { if (e) { get_err() << "Z3(" << e->line() << "," << e->pos() << "): ERROR: "; } } else { get_err() << "ERROR: "; if (e) { get_err() << "line " << e->line() << " column " << e->pos() << ": "; } } } void set_error(char const * str, proto_expr* e) { error_prefix(e); if (e->kind() == proto_expr::ID) { get_err() << str << " '" << e->string() << "'.\n"; } else { get_err() << str << ".\n"; } } template void set_error(T1 str1, T2 str2, proto_expr* e) { error_prefix(e); get_err() << str1 << " " << str2 << ".\n"; } template void set_error(T1 str1, T2 str2, T3 str3, proto_expr* e) { error_prefix(e); get_err() << str1 << str2 << str3 << ".\n"; } bool match_cons(proto_expr * e, symbol const & sym, symbol & name, proto_expr* const*& proto_exprs) { if (e && e->kind() == proto_expr::CONS && e->children() && e->children()[0] && e->children()[0]->string() == sym && e->children()[1]) { proto_exprs = e->children() + 2; name = e->children()[1]->string(); return true; } return false; } bool make_benchmark(symbol & name, proto_expr * const* proto_exprs) { symbol extrasorts("extrasorts"); symbol extrafuns("extrafuns"); symbol extrapreds("extrapreds"); symbol assumption("assumption"); symbol assumption_core("assumption-core"); symbol define_sorts_sym("define_sorts"); symbol logic("logic"); symbol formula("formula"); symbol status("status"); symbol sat("sat"); symbol unsat("unsat"); symbol unknown("unknown"); symbol empty(""); symbol source("source"); symbol difficulty("difficulty"); symbol category("category"); bool success = true; push_benchmark(name); while (proto_exprs && *proto_exprs) { proto_expr* e = *proto_exprs; ++proto_exprs; proto_expr* e1 = *proto_exprs; if (logic == e->string() && e1) { name = e1->string(); m_benchmark.set_logic(name); set_default_num_sort(name); if (name == symbol("QF_AX")) { // Hack for supporting new QF_AX theory... sort * index = m_manager.mk_uninterpreted_sort(symbol("Index")); sort * element = m_manager.mk_uninterpreted_sort(symbol("Element")); parameter params[2] = { parameter(index), parameter(element) }; sort * array = m_manager.mk_sort(m_array_fid, ARRAY_SORT, 2, params); smtlib::symtable* table = m_benchmark.get_symtable(); table->insert(symbol("Index"), index); table->insert(symbol("Element"), element); // overwrite Array definition... table->insert(symbol("Array"), array); sort * args[3] = { array, index, element }; func_decl * store = m_manager.mk_func_decl(m_array_fid, OP_STORE, 0, 0, 3, args); table->insert(symbol("store"), store); func_decl * sel = m_manager.mk_func_decl(m_array_fid, OP_SELECT, 0, 0, 2, args); table->insert(symbol("select"), sel); } ++proto_exprs; continue; } if (assumption == e->string() && e1) { expr_ref t(m_manager); if (!make_expression(e1, t) || !push_assumption(t.get())) { return false; } ++proto_exprs; continue; } if (assumption_core == e->string() && e1) { expr_ref t(m_manager); if (!make_expression(e1, t)) return false; m_benchmark.add_assumption(t.get()); ++proto_exprs; continue; } if (define_sorts_sym == e->string() && e1) { if (!define_sorts(e1)) { return false; } ++proto_exprs; continue; } if (formula == e->string() && e1) { expr_ref t(m_manager); if (!make_expression(e1, t) || !push_formula(t.get())) { return false; } ++proto_exprs; continue; } if (status == e->string() && e1) { if (sat == e1->string()) { if (!push_status(smtlib::benchmark::SAT)) { set_error("could not push status ", e1->string(),e1); return false; } } else if (unsat == e1->string()) { if (!push_status(smtlib::benchmark::UNSAT)) { set_error("could not push status ", e1->string(),e1); return false; } } else if (unknown == e1->string()) { if (!push_status(smtlib::benchmark::UNKNOWN)) { set_error("could not push status ", e1->string(),e1); return false; } } else { set_error("could not recognize status ", e1->string(),e1); return false; } ++proto_exprs; continue; } if (extrasorts == e->string() && e1) { if (!declare_sorts(e1)) { return false; } ++proto_exprs; continue; } if (extrafuns == e->string() && e1) { if (!declare_funs(e1)) { return false; } ++proto_exprs; continue; } if (extrapreds == e->string() && e1) { if (!declare_preds(e1)) { return false; } ++proto_exprs; continue; } if (m_notes == e->string() && e1) { ++proto_exprs; continue; } if ((source == e->string() || difficulty == e->string() || category == e->string()) && e1) { ++proto_exprs; continue; } if (e->string() != empty) { set_error("ignoring unknown attribute '", e->string().bare_str(), "'", e); if (e1) { ++proto_exprs; } // do not fail. // success = false; continue; } TRACE("smtparser", tout << "skipping: " << e->string() << " " << e->line() << " " << e->pos() << ".\n";); continue; } return success; } bool is_id_token(proto_expr* expr) { return expr && (expr->kind() == proto_expr::ID || expr->kind() == proto_expr::STRING || expr->kind() == proto_expr::ANNOTATION); } bool check_id(proto_expr* e) { return is_id_token(e); } bool make_expression(proto_expr * e, expr_ref & result) { m_binding_level = 0; symbol_table local_scope; return make_expression(local_scope, e, result); } bool make_func_decl(proto_expr* e, func_decl_ref& result) { func_decl* f; if (m_benchmark.get_symtable()->find1(e->string(), f)) { result = f; return true; } else { return false; } } bool make_bool_expression(symbol_table& local_scope, proto_expr * e, expr_ref & result) { if (!make_expression(local_scope, e, result)) { return false; } if (!m_manager.is_bool(result)) { set_error("expecting Boolean expression", e); return false; } return true; } bool make_bool_expressions(symbol_table& local_scope, proto_expr * const* chs, expr_ref_vector & exprs) { while (chs && *chs) { expr_ref result(m_manager); m_binding_level = 0; if (!make_bool_expression(local_scope, *chs, result)) { return false; } exprs.push_back(result); ++chs; } return true; } bool make_expression(symbol_table& local_scope, proto_expr * e, expr_ref & result) { // // Walk proto_expr by using the zipper. // That is, maintain a stack of what's // . left - already processed. // . right - to be processed. // . up - above the processed node. // region region; expr_ref_vector * left = alloc(expr_ref_vector, m_manager); proto_expr* const* right = 0; ptr_vector up; proto_expr* current = e; bool success = false; idbuilder* builder = 0; while (true) { if (!current && right && *right) { // // pull the current from right. // current = *right; ++right; } if (!current && up.empty()) { // // we are done. // if (left->size() == 0) { // set_error(); set_error("there are no expressions to return", e); goto cleanup; } if (left->size() != 1) { set_error("there are too many expressions to return", e); goto cleanup; } result = left->back(); success = true; goto cleanup; } if (!current && !up.empty()) { // // There is nothing more to process at this level. // // Apply the operator on the stack to the // current 'left' vector. // Adjust the stack by popping the left and right // work-lists. // expr_ref term(m_manager); parse_frame* above = up.back(); // symbol sym = above->get_proto_expr()->string(); if (above->make_term()) { if (!above->make_term()->apply(*left, term)) { set_error("Could not create application", above->get_proto_expr()); success = false; goto cleanup; } } else if (!make_app(above->get_proto_expr(), *left, term)) { success = false; goto cleanup; } dealloc(left); left = above->detach_left(); left->push_back(term.get()); right = above->right(); m_binding_level = above->binding_level(); up.pop_back(); continue; } while (current && current->kind() == proto_expr::CONS && current->children() && current->children()[0] && !current->children()[1]) { current = current->children()[0]; } switch(current->kind()) { case proto_expr::ANNOTATION: // ignore current = 0; break; case proto_expr::ID: { symbol const& id = current->string(); expr_ref term(m_manager); expr * const_term = 0; bool ok = true; if (local_scope.find(id, builder)) { expr_ref_vector tmp(m_manager); if (!builder->apply(tmp, term)) { set_error("identifier supplied with the wrong number of arguments ", id, current); goto cleanup; } } else if (m_benchmark.get_const(id, const_term)) { // found. term = const_term; } else if (is_builtin_const(id, current, current->num_params(), current->params(), ok, term)) { if (!ok) goto cleanup; } else if (is_bvconst(id, current->num_params(), current->params(), term)) { // found } else { set_error("could not locate id ", id, current); goto cleanup; } left->push_back(term.get()); current = 0; break; } case proto_expr::STRING: // // Ignore strings. // current = 0; break; case proto_expr::COMMENT: // // Ignore comments. // current = 0; break; case proto_expr::INT: left->push_back(mk_number(current->number(), true)); current = 0; break; case proto_expr::FLOAT: left->push_back(mk_number(current->number(), false)); current = 0; break; case proto_expr::CONS: if (!current->children() || !current->children()[0]) { set_error("cons does not have children", current); current = 0; goto cleanup; } // // expect the head to be a symbol // which can be used to build a term of // the subterms. // symbol const& head_symbol = current->children()[0]->string(); if (head_symbol == m_underscore) { expr_ref term(m_manager); proto_expr * const* chs = current->children() + 1; symbol const& id = chs[0]->string(); sort_ref_vector sorts(m_manager); vector params; bool ok = true; if (!parse_params(chs+1, params, sorts)) { goto cleanup; } if (is_builtin_const(id, current, params.size(), params.c_ptr(), ok, term)) { if (!ok) goto cleanup; } else if (is_bvconst(id, params.size(), params.c_ptr(), term)) { // ok } else { set_error("Could not parse _ term", current); goto cleanup; } left->push_back(term.get()); current = 0; break; } if ((head_symbol == m_let) || (head_symbol == m_flet)) { if (!current->children()[1] || !current->children()[2]) { set_error("let does not have two arguments", current); goto cleanup; } proto_expr * let_binding = current->children()[1]; proto_expr * const* let_body = current->children()+2; // // Collect bound variables and definitions for the bound variables // into vectors 'vars' and 'bound'. // svector vars; ptr_vector bound_vec; if (is_binary_let_binding(let_binding)) { vars.push_back(let_binding->children()[0]->string()); bound_vec.push_back(let_binding->children()[1]); } else { proto_expr* const* children = let_binding->children(); if (!children) { set_error("let binding does not have two arguments", let_binding); goto cleanup; } while (*children) { proto_expr* ch = *children; if (!is_binary_let_binding(ch)) { set_error("let binding does not have two arguments", ch); goto cleanup; } vars.push_back(ch->children()[0]->string()); bound_vec.push_back(ch->children()[1]); ++children; } } bound_vec.push_back(0); proto_expr** bound = new (region) proto_expr*[bound_vec.size()]; for (unsigned i = 0; i < bound_vec.size(); ++i) { bound[i] = bound_vec[i]; } // // Let's justify the transformation that // pushes push_let and pop_let on the stack. // and how it processes the let declaration. // // walk up left ((let ((v1 x1) (v2 x2)) z)::right) // // = // // walk (up::(pop_let(),left,right)::(bind(v1,v2),[],[z])) [] [x1;x2] // // = (* assume x1 -> y1, x2 -> y2 *) // // walk (up::(pop_let(),left,right)::(bind(v1,v2),[],[z])) [y1;y2] [] // // = (* apply binding *) // // walk (up::(pop_let(),left,right)) [] [z] // // = (* assume z -> u *) // // walk up {left::u] right // // so if pop_let(v) [a,b] has the effect of removing v from the environment // and projecting the second element "b", we obtain the effect of a let-binding. // expr_ref_vector * pinned = alloc(expr_ref_vector, m_manager); pop_let * popl = new (region) pop_let(local_scope, pinned); up.push_back(new (region) parse_frame(let_binding, popl, left, right, m_binding_level)); push_let_and * pushl = new (region) push_let_and(this, region, local_scope, pinned, vars.size(), vars.c_ptr()); expr_ref_vector * tmp = alloc(expr_ref_vector, m_manager); up.push_back(new (region) parse_frame(let_binding, pushl, tmp, let_body, m_binding_level)); left = alloc(expr_ref_vector, m_manager); right = bound; current = 0; break; } if (head_symbol == m_lblneg || head_symbol == m_lblpos) { if (!current->children()[1] || !current->children()[2]) { set_error("labels require two arguments", current); goto cleanup; } bool is_pos = head_symbol == m_lblpos; idbuilder* lbl = new (region) build_label(this, is_pos, current->children()[1]); up.push_back(new (region) parse_frame(current, lbl, left, right, m_binding_level)); // // process the body. // left = alloc(expr_ref_vector, m_manager); right = 0; current = current->children()[2]; break; } if (head_symbol == m_bang) { proto_expr* const* children = current->children(); proto_expr* body = children[1]; proto_expr* lblname = 0; bool is_pos = false; children += 2; while (children[0] && children[0]->kind() == proto_expr::ANNOTATION && children[1]) { symbol id = children[0]->string(); if ((id == m_lblneg) || (id == m_lblpos)) { is_pos = id == m_lblpos; lblname = children[1]; } children += 2; } if (lblname) { idbuilder* lbl = new (region) build_label(this, is_pos, lblname); up.push_back(new (region) parse_frame(current, lbl, left, right, m_binding_level)); left = alloc(expr_ref_vector, m_manager); right = 0; } // // process the body. // current = body; break; } if ((head_symbol == m_forall) || (head_symbol == m_exists)) { expr_ref_buffer patterns(m_manager); expr_ref_buffer no_patterns(m_manager); sort_ref_buffer sorts(m_manager); svector vars; int weight = 1; proto_expr* const* children = current->children(); proto_expr* body = 0; ++children; if (!children[0] || !children[1]) { set_error("quantifier should have at least two arguments", current); goto cleanup; } // // restore 'left' and 'right' working set and m_binding_level. // up.push_back(new (region) parse_frame(current, new (region) identity(), left, right, m_binding_level)); left = alloc(expr_ref_vector, m_manager); // // declare the bound variables. // local_scope.begin_scope(); while (children[0] && children[1] && (children[1]->kind() != proto_expr::ANNOTATION)) { if (!parse_bound(local_scope, region, *children, vars, sorts)) { goto cleanup; } ++children; } body = children[0]; if (is_annotated_cons(body)) { children = body->children()+1; body = body->children()[1]; } ++children; symbol qid = symbol(current->line()); symbol skid = symbol(); read_patterns(vars.size(), local_scope, children, patterns, no_patterns, weight, qid, skid); // // push a parse_frame to undo the scope of the quantifier. // SASSERT(sorts.size() > 0); idbuilder* pop_q = new (region) pop_quantifier(this, (head_symbol == m_forall), weight, qid, skid, patterns, no_patterns, sorts, vars, local_scope); expr_ref_vector * empty_v = alloc(expr_ref_vector, m_manager); up.push_back(new (region) parse_frame(current, pop_q, empty_v, 0, m_binding_level)); // // process the body. // right = 0; current = body; break; } if (is_underscore_op(region, current->children()[0], builder)) { up.push_back(new (region) parse_frame(current, builder, left, right, m_binding_level)); } else if (local_scope.find(head_symbol, builder)) { up.push_back(new (region) parse_frame(current, builder, left, right, m_binding_level)); } else { up.push_back(new (region) parse_frame(current->children()[0], left, right, m_binding_level)); } left = alloc(expr_ref_vector, m_manager); right = current->children() + 1; current = 0; break; } } cleanup: if (success && !is_well_sorted(m_manager, result)) { set_error("expression is not well sorted", e); success = false; } dealloc(left); while (!up.empty()) { dealloc(up.back()->detach_left()); up.pop_back(); } return success; } bool read_patterns(unsigned num_bindings, symbol_table & local_scope, proto_expr * const * & children, expr_ref_buffer & patterns, expr_ref_buffer & no_patterns, int& weight, symbol& qid, symbol& skid) { proto_region region; while (children[0] && children[0]->kind() == proto_expr::ANNOTATION && children[1]) { if (children[0]->string() == symbol("qid") || children[0]->string() == symbol("named")) { qid = children[1]->string(); children += 2; continue; } if (children[0]->string() == symbol("skolemid")) { skid = children[1]->string(); children += 2; continue; } ptr_vector proto_exprs; if (children[1]->kind() == proto_expr::COMMENT) { std::string s = children[1]->string().str(); std::istringstream stream(s); scanner scanner(stream, get_err(), false); proto_expr_parser parser(region, scanner, get_err()); if (!parser.parse(proto_exprs)) { set_error("could not parse expression", children[1]); return false; } } else if (children[1]->kind() == proto_expr::CONS) { for (proto_expr* const* pexpr = children[1]->children(); *pexpr; pexpr++) proto_exprs.push_back(*pexpr); } else { proto_exprs.push_back(children[1]); } expr_ref_buffer ts(m_manager); for (unsigned i = 0; i < proto_exprs.size(); ++i) { expr_ref t(m_manager); if (!make_expression(local_scope, proto_exprs[i], t)) { return false; } ts.push_back(t.get()); } if (children[0]->string() == symbol("pat") || children[0]->string() == symbol("pats") || children[0]->string() == symbol("pattern")) { for (unsigned i = 0; i < ts.size(); ++i) { if (!is_app(ts[i])) { set_error("invalid pattern", children[0]); return false; } } expr * p = m_manager.mk_pattern(ts.size(), (app*const*)(ts.c_ptr())); if (!p || (!ignore_user_patterns() && !m_pattern_validator(num_bindings, p))) { set_error("invalid pattern", children[0]); return false; } patterns.push_back(p); } else if (children[0]->string() == symbol("ex_act") && ts.size() == 1) { app * sk_hack = m_manager.mk_app(m_sk_hack, 1, ts.c_ptr()); expr * p = m_manager.mk_pattern(1, &sk_hack); if (!p || (!ignore_user_patterns() && !m_pattern_validator(num_bindings, p))) { set_error("invalid pattern", children[0]); return false; } patterns.push_back(p); } else if ((children[0]->string() == symbol("nopat") || children[0]->string() == symbol("no-pattern")) && ts.size() == 1) { no_patterns.push_back(ts[0]); } else if (children[0]->string() == symbol("weight") && ts.size() == 1 && proto_exprs[0]->kind() == proto_expr::INT && proto_exprs[0]->number().is_unsigned()) { weight = proto_exprs[0]->number().get_unsigned(); } else { // TODO: this should be a warning, perferably once per unknown kind of annotation set_error("could not understand annotation '", children[0]->string().bare_str(), "'", children[0]); } children += 2; } return true; } void set_default_num_sort(symbol const& name) { if (name == symbol("QF_RDL") || name == symbol("QF_LRA") || name == symbol("LRA") || name == symbol("RDL") || name == symbol("QF_NRA") || name == symbol("QF_UFNRA") || name == symbol("QF_UFLRA")) { m_int_sort = m_real_sort; } } bool get_sort(theory* th, char const * s, sort_ref& sort) { return make_sort(symbol(s), 0, 0, sort); } bool make_sort(symbol const & id, unsigned num_params, parameter const* params, sort_ref& s) { builtin_op info; if (m_builtin_sorts.find(id, info)) { s = m_manager.mk_sort(info.m_family_id, info.m_kind, num_params, params); return true; } if (num_params == 2 && symbol("Array") == id) { // Old HACK to accomodate bit-vector arrays. if (!params[0].is_int()) { throw default_exception("Non-integer parameter to array"); return false; } if (!params[1].is_int()) { throw default_exception("Non-integer parameter to array"); return false; } parameter bv_params0[1] = { parameter(params[0].get_int()) }; parameter bv_params1[1] = { parameter(params[1].get_int()) }; sort * t1 = m_manager.mk_sort(m_bv_fid, BV_SORT, 1, bv_params0); sort * t2 = m_manager.mk_sort(m_bv_fid, BV_SORT, 1, bv_params1); parameter params[2] = { parameter(t1), parameter(t2) }; s = m_manager.mk_sort(m_array_fid, ARRAY_SORT, 2, params); return true; } sort* srt = 0; if (m_benchmark.get_sort(id, srt)) { s = srt; return true; } return false; } bool make_sort(proto_expr * e, sort_ref& s) { SASSERT(can_be_sort(e)); symtable& env = *m_benchmark.get_symtable(); sort_builder* mk_sort; switch(e->kind()) { case proto_expr::ID: { if (make_sort(e->string(), e->num_params(), e->params(), s)) { return true; } if (env.lookup(e->string(), mk_sort)) { if (!mk_sort->apply(e->num_params(), e->params(), s)) { set_error(mk_sort->error_message(), e); return false; } return true; } set_error("could not find sort ", e); return false; } case proto_expr::CONS: { if (!can_be_sort(e)) { set_error("expression cannot be a sort", e); return false; } proto_expr *const* chs = e->children(); if (is_underscore(e)) { ++chs; } symbol name = (*chs)->string(); if (!env.lookup(name, mk_sort)) { set_error("could not find sort symbol '", name.str(), "'", e); return false; } sort_ref_vector sorts(m_manager); vector params; if (!parse_params(chs+1, params, sorts)) { return false; } if (!mk_sort->apply(params.size(), params.c_ptr(), s)) { set_error(mk_sort->error_message(), e); return false; } return true; } default: set_error("could not create sort ", e); return false; } } bool parse_params(proto_expr* const* chs,vector& params, sort_ref_vector& sorts) { while (*chs) { if ((*chs)->kind() == proto_expr::INT) { rational const& num = (*chs)->number(); if (num.is_unsigned()) { params.push_back(parameter(num.get_unsigned())); } else { params.push_back(parameter(num)); } } else { sort_ref s1(m_manager); if (!make_sort(*chs, s1)) { return false; } sorts.push_back(s1); params.push_back(parameter((ast*)s1.get())); } ++chs; } return true; } bool parse_bound( symbol_table& local_scope, region& region, proto_expr* bound, svector& vars, sort_ref_buffer& sorts ) { if (is_cons_list(bound)) { proto_expr *const* children = bound->children(); while (*children) { if (!parse_bound(local_scope, region, *children, vars, sorts)) { return false; } ++children; } return true; } if (!can_be_sorted_var(bound)) { set_error("bound variable should contain a list of pairs", bound); return false; } proto_expr* var = bound->children()[0]; proto_expr* sort_proto_expr = bound->children()[1]; sort_ref sort(m_manager); if (!make_sort(sort_proto_expr, sort)) { return false; } sorts.push_back(sort); vars.push_back(var->string()); local_scope.insert( var->string(), new (region) bound_var(this, sort) ); ++m_binding_level; return true; } bool can_be_sort(proto_expr* e) { if (e && e->kind() == proto_expr::ID) { return true; } if (is_underscore(e)) { return true; } if (e && e->kind() == proto_expr::CONS && e->children() && e->children()[0] && e->children()[1]) { proto_expr* const* ch = e->children(); while(*ch) { if (!can_be_sort(*ch)) { return false; } ++ch; } return true; } return false; } bool declare_sorts(proto_expr* e) { proto_expr* const * children = e->children(); while (children && *children) { proto_expr* ch = *children; switch(ch->kind()) { case proto_expr::ID: m_benchmark.declare_sort(ch->string()); break; case proto_expr::CONS: // // The declaration of type constructors // consists of an identifier together with // a number indicating the arity of the // constructor. // if (ch->children() && ch->children()[0] && ch->children()[0]->kind() == proto_expr::ID && ch->children()[1] && ch->children()[1]->kind() == proto_expr::INT) { // unsigned num = (unsigned) ch->children()[1]->number().get_uint64(); m_benchmark.declare_sort(ch->children()[0]->string()); } break; case proto_expr::ANNOTATION: break; default: set_error("unexpected argument to sorts",ch); return false; } ++children; } return true; } bool define_sorts(proto_expr* e) { proto_expr* const * children = e->children(); while (children && *children) { if (!define_sort(*children)) { return false; } ++children; } return true; } bool define_sort(proto_expr* e) { proto_expr* const * children = e->children(); sort_ref_buffer domain(m_manager); // // First element in list must be an identifier. // there should be just two elements. // if (!children || !children[0] || !(children[0]->kind() == proto_expr::ID) || !children[1] || children[2]) { set_error("unexpected arguments to function declaration", e); return false; } symbol name = children[0]->string(); sort_ref s(m_manager); if (!can_be_sort(children[1]) || !make_sort(children[1], s)) { set_error("unexpected arguments to function declaration", e); return false; } m_benchmark.get_symtable()->insert(name, s); return true; } bool declare_funs(proto_expr* e) { proto_expr* const * children = e->children(); while (children && *children) { if (!declare_fun(*children)) { return false; } ++children; } return true; } class define_sort_cls : public sort_builder { smtparser& m_parser; proto_region m_region; proto_expr* m_expr; svector m_params; symbol m_name; std::string m_error_message; public: define_sort_cls(smtparser& p, symbol const& name, proto_expr* e, unsigned num_params, symbol* params) : m_parser(p), m_name(name) { for (unsigned i = 0; i < num_params; ++i) { m_params.push_back(params[i]); } m_expr = proto_expr::copy(m_region, e); } virtual bool apply(unsigned num_params, parameter const* params, sort_ref & result) { smtlib::symtable * symtable = m_parser.m_benchmark.get_symtable(); if (m_params.size() != num_params) { std::ostringstream strm; strm << "wrong number of arguments passed to " << m_name << " " << m_params.size() << " expected, but " << num_params << " given"; m_error_message = strm.str(); return false; } for (unsigned i = 0; i < num_params; ++i) { parameter p(params[i]); if (!p.is_ast() || !is_sort(p.get_ast())) { symtable->pop_sorts(i); std::ostringstream strm; strm << "argument " << i << " is not a sort"; m_error_message = strm.str(); return false; } symtable->push_sort(m_params[i], to_sort(p.get_ast())); } bool success = m_parser.make_sort(m_expr, result); symtable->pop_sorts(num_params); return success; } virtual char const* error_message() { return m_error_message.c_str(); } }; // (define-sort name (*) ) bool define_sort(proto_expr* id, proto_expr* sorts, proto_expr* srt) { symbol name = id->string(); proto_expr* const * children = sorts->children(); svector names; if (!children) { set_error("Sort definition expects a list of sort symbols",id); return false; } while (children[0]) { id = children[0]; if(id->kind() != proto_expr::ID) { set_error("unexpected argument, expected ID", id); return false; } names.push_back(id->string()); ++children; } m_benchmark.get_symtable()->insert(name, alloc(define_sort_cls, *this, name, srt, names.size(), names.c_ptr())); return true; } bool declare_fun(proto_expr* id, proto_expr* sorts, proto_expr* srt) { proto_expr* const * children = sorts?sorts->children():0; sort_ref_buffer domain(m_manager); symbol name = id->string(); if (sorts && !children) { set_error("Function declaration expects a list of sorts", id); return false; } // // parse domain. // while (sorts && children[0]) { sort_ref s(m_manager); if (!make_sort(children[0], s)) { return false; } domain.push_back(s); ++children; } sort_ref range(m_manager); if (!make_sort(srt, range)) { return false; } bool is_associative = false; bool is_commutative = false; bool is_injective = false; m_benchmark.declare_func(name, domain, range, is_associative, is_commutative, is_injective); return true; } bool declare_fun(proto_expr* e) { proto_expr* const * children = e->children(); sort_ref_buffer domain(m_manager); // // Skip declaration of numbers. // if (children && children[0] && children[0]->kind() == proto_expr::INT) { return true; } // // First element in list must be an identifier. // if (!children || !children[0] || !(children[0]->kind() == proto_expr::ID)) { set_error("unexpected arguments to function declaration", e); return false; } symbol name = children[0]->string(); ++children; if (!can_be_sort(children[0])) { set_error("unexpected arguments to function declaration", e); return false; } // // parse domain. // while (can_be_sort(children[1])) { sort_ref s(m_manager); if (!make_sort(children[0], s)) { return false; } domain.push_back(s); ++children; } // // parse range. // SASSERT(can_be_sort(children[0])); sort_ref range(m_manager); if (!make_sort(children[0], range)) { return false; } ++children; // // parse attributes. // bool is_associative = false; bool is_commutative = false; bool is_injective = false; while(children[0] && children[0]->kind() == proto_expr::ANNOTATION) { if (m_associative == children[0]->string()) { is_associative = true; } else if (m_commutative == children[0]->string()) { is_commutative = true; } else if (m_injective == children[0]->string()) { is_injective = true; } ++children; } m_benchmark.declare_func(name, domain, range, is_associative, is_commutative, is_injective); return true; } bool declare_preds(proto_expr* e) { proto_expr* const * children = e->children(); while (children && *children) { if (!declare_pred(*children)) { return false; } ++children; } return true; } bool declare_pred(proto_expr* e) { proto_expr* const * children = e->children(); if (!children || !children[0] || !(children[0]->kind() == proto_expr::ID)) { set_error("unexpected arguments to predicate declaration", e); return false; } symbol const & name = children[0]->string(); sort_ref_buffer domain(m_manager); sort * bool_sort = m_manager.mk_bool_sort(); ++children; while (can_be_sort(children[0])) { sort_ref s(m_manager); if (!make_sort(children[0], s)) { return false; } domain.push_back(s); ++children; } m_benchmark.declare_func(name, domain, bool_sort, false, false, false); return true; } bool can_be_sorted_var(proto_expr* e) { return e && (e->kind() == proto_expr::CONS) && e->children() && e->children()[0] && (e->children()[0]->kind() == proto_expr::ID) && can_be_sort(e->children()[1]); } bool is_cons_list(proto_expr* e) { return e && (e->kind() == proto_expr::CONS) && e->children() && e->children()[0] && e->children()[0]->kind() == proto_expr::CONS; } bool is_prefixed(proto_expr* e, symbol const& s) { return e && (e->kind() == proto_expr::CONS) && e->children() && e->children()[0] && e->children()[1] && e->children()[0]->string() == s; } bool is_underscore(proto_expr* e) { return is_prefixed(e, m_underscore) && e->children()[1]->kind() == proto_expr::ID; } bool is_annotated_cons(proto_expr* e) { return is_prefixed(e, m_bang); } bool is_builtin_const(symbol const& id, proto_expr* current, unsigned num_params, parameter * params, bool& ok, expr_ref& term) { builtin_op info; ok = true; if (!m_builtin_ops.find(id, info)) { return false; } fix_parameters(num_params, params); func_decl* d = m_manager.mk_func_decl(info.m_family_id, info.m_kind, num_params, params, 0, (expr * const *)0); if (!d) { set_error("could not create a term from constant ", id, current); ok = false; } else if (d->get_arity() != 0) { set_error("identifier expects arguments ", id, current); ok = false; } else { term = m_manager.mk_const(d); } return true; } bool is_underscore_op(region& r, proto_expr* e, idbuilder*& builder) { if (!is_underscore(e)) { return false; } builtin_op info; proto_expr *const* chs = e->children()+1; symbol const& id = (*chs)->string(); sort_ref_vector sorts(m_manager); vector params; if (!m_builtin_ops.find(id, info)) { return false; } if (!parse_params(chs+1, params, sorts)) { return false; } builder = new (r) builtin_builder(this, info.m_family_id, info.m_kind, params); return true; } void fix_parameters(unsigned num_params, parameter* params) { for (unsigned i = 0; i < num_params; ++i) { func_decl* d = 0; sort* s = 0; builtin_op info; if (params[i].is_symbol() && m_benchmark.get_symtable()->find1(params[i].get_symbol(), d)) { params[i] = parameter(d); } else if (params[i].is_symbol() && m_benchmark.get_symtable()->find(params[i].get_symbol(), s)) { params[i] = parameter(s); } else if (params[i].is_symbol() && m_builtin_sorts.find(params[i].get_symbol(), info)) { params[i] = parameter(m_manager.mk_sort(info.m_family_id, info.m_kind, 0, 0)); } } } bool make_app(proto_expr * proto_expr, expr_ref_vector const & args, expr_ref & result) { symbol const& name = proto_expr->string(); ptr_vector sorts; func_decl * d = 0; smtlib::symtable * symtable = m_benchmark.get_symtable(); for (unsigned i = 0; i < args.size(); ++i) { sorts.push_back(m_manager.get_sort(args.get(i))); } if (symtable->find_overload(name, sorts, d)) { result = m_manager.mk_app(d, args.size(), args.c_ptr()); return true; } builtin_op info; if (m_builtin_ops.find(name, info)) { unsigned num_params = proto_expr->num_params(); parameter * params = proto_expr->params(); fix_parameters(num_params, params); d = m_manager.mk_func_decl(info.m_family_id, info.m_kind, num_params, params, args.size(), args.c_ptr()); if (d) { result = m_manager.mk_app(d, args.size(), args.c_ptr()); return true; } } rational arg2_value; bool arg2_is_int; if (name == symbol("store") && args.size() == 3 && m_anum_util.is_numeral(args.get(2), arg2_value, arg2_is_int) && arg2_is_int) { expr_ref_vector new_args(m_manager); new_args.push_back(args.get(0)); new_args.push_back(args.get(1)); new_args.push_back(m_anum_util.mk_numeral(arg2_value, false)); sorts.reset(); for (unsigned i = 0; i < args.size(); ++i) { sorts.push_back(m_manager.get_sort(new_args.get(i))); } if (symtable->find_overload(name, sorts, d)) { result = m_manager.mk_app(d, new_args.size(), new_args.c_ptr()); return true; } } error_prefix(proto_expr); get_err() << "could not find overload for '" << name << "' "; for (unsigned i = 0; i < sorts.size(); ++i) { get_err() << "Argument: " << mk_pp(args.get(i), m_manager) << " has type " << mk_pp(sorts[i], m_manager) << ".\n"; } return false; } class nullary : public idbuilder { expr* m_expr; smtparser* m_parser; unsigned m_decl_level_save; public: nullary(expr* e, smtparser* p) : m_expr(e), m_parser(p), m_decl_level_save(p->m_binding_level) {} virtual bool apply(expr_ref_vector const& args, expr_ref & result) { unsigned decl_level = m_parser->m_binding_level; SASSERT(decl_level >= m_decl_level_save); shift_vars shifty(m_parser->m_manager); shifty(m_expr, decl_level - m_decl_level_save, result); return (args.size() == 0); } }; class identity : public idbuilder { public: identity() {} virtual bool apply(expr_ref_vector const & args, expr_ref & result) { if (args.size() == 1) { result = args.back(); return true; } else { return false; } } }; class parse_frame { public: parse_frame(proto_expr * e, idbuilder * make, expr_ref_vector * left, proto_expr * const* right, unsigned binding_level): m_proto_expr(e), m_make_term(make), m_left(left), m_right(right), m_binding_level(binding_level) { } parse_frame(proto_expr * e, expr_ref_vector * left, proto_expr * const* right, unsigned binding_level): m_proto_expr(e), m_make_term(0), m_left(left), m_right(right), m_binding_level(binding_level) { } expr_ref_vector * detach_left() { expr_ref_vector * result = m_left; SASSERT(m_left); m_left = 0; return result; } unsigned binding_level() const { return m_binding_level; } proto_expr* const * right() const { return m_right; } idbuilder* make_term() { return m_make_term; } proto_expr* get_proto_expr() const { return m_proto_expr; } ~parse_frame() { dealloc(m_left); } private: proto_expr* m_proto_expr; idbuilder* m_make_term; expr_ref_vector * m_left; proto_expr* const * m_right; unsigned m_binding_level; parse_frame & operator=(parse_frame const & other); parse_frame(parse_frame const & other); }; class build_label : public idbuilder { bool m_pos; symbol m_sym; smtparser * m_smt; public: build_label(smtparser * smt, bool is_pos, proto_expr * sym): m_pos(is_pos), m_smt(smt) { switch(sym->kind()) { case proto_expr::ID: case proto_expr::STRING: m_sym = sym->string(); break; case proto_expr::INT: m_sym = symbol(sym->number().to_string().c_str()); break; default: UNREACHABLE(); break; } } virtual bool apply(expr_ref_vector const & args, expr_ref & result) { if (args.size() >= 1) { result = m_smt->m_manager.mk_label(m_pos, m_sym, args.get(0)); return true; } else { return false; } } }; class pop_let : public idbuilder { public: pop_let(symbol_table & local_scope, expr_ref_vector* pinned = 0): m_local_scope(local_scope), m_pinned(pinned) { } virtual ~pop_let() {} virtual bool apply(expr_ref_vector const & args, expr_ref & result) { dealloc(m_pinned); if (args.size() == 2) { m_local_scope.end_scope(); result = args.get(1); return true; } else { return false; } } private: symbol_table & m_local_scope; expr_ref_vector* m_pinned; }; class push_let : public idbuilder { smtparser* m_parser; region & m_region; symbol_table & m_local_scope; symbol m_let_var; public: push_let(smtparser* p, region & region, symbol_table & local_scope, symbol const & let_var): m_parser(p), m_region(region), m_local_scope(local_scope), m_let_var(let_var) { } virtual bool apply(expr_ref_vector const & args, expr_ref & result) { // // . push a scope, // . create a nullary function using the variable/term association. // . return the (first) argument. // // if (args.size() == 1) { m_local_scope.begin_scope(); m_local_scope.insert(m_let_var, new (m_region) nullary(args.back(), m_parser)); result = args.back(); return true; } else { return false; } } }; // push multiple let bound variables. class push_let_and : public idbuilder { smtparser* m_parser; region & m_region; symbol_table & m_local_scope; unsigned m_num_vars; symbol* m_vars; expr_ref_vector* m_pinned; public: push_let_and(smtparser* p, region & region, symbol_table & local_scope, expr_ref_vector* pinned, unsigned num_vars, symbol const* vars): m_parser(p), m_region(region), m_local_scope(local_scope), m_num_vars(num_vars), m_vars(new (region) symbol[num_vars]), m_pinned(pinned) { for (unsigned i = 0; i < num_vars; ++i) { m_vars[i] = vars[i]; } } virtual bool apply(expr_ref_vector const & args, expr_ref & result) { if (args.size() != m_num_vars) { return false; } // // . push a scope, // . create a nullary function using the variable/term association. // . return the last argument (arbitrary). // m_local_scope.begin_scope(); for (unsigned i = 0; i < m_num_vars; ++i) { m_local_scope.insert(m_vars[i], new (m_region) nullary(args[i], m_parser)); m_pinned->push_back(args[i]); } result = args.back(); return true; } }; class bound_var : public idbuilder { public: bound_var(smtparser * smt, sort * sort): m_smt(smt), m_decl_level(smt->m_binding_level), m_sort(sort) { } virtual bool apply(expr_ref_vector const & args, expr_ref & result) { SASSERT(m_smt->m_binding_level > m_decl_level); unsigned idx = m_smt->m_binding_level - m_decl_level - 1; result = m_smt->m_manager.mk_var(idx, m_sort); return args.empty(); } private: smtparser * m_smt; unsigned m_decl_level; sort * m_sort; }; class pop_quantifier : public idbuilder { public: pop_quantifier(smtparser * smt, bool is_forall, int weight, symbol const& qid, symbol const& skid, expr_ref_buffer & patterns, expr_ref_buffer & no_patterns, sort_ref_buffer & sorts, svector& vars, symbol_table & local_scope): m_smt(smt), m_is_forall(is_forall), m_weight(weight), m_qid(qid), m_skid(skid), m_patterns(m_smt->m_manager), m_no_patterns(m_smt->m_manager), m_sorts(m_smt->m_manager), m_local_scope(local_scope) { SASSERT(sorts.size() == vars.size()); m_vars.append(vars); m_sorts.append(sorts); m_patterns.append(patterns); m_no_patterns.append(no_patterns); } virtual bool apply(expr_ref_vector const & args, expr_ref & result) { if (args.size() != 1) { return false; } m_local_scope.end_scope(); expr * body = args.back(); if (m_smt->ignore_user_patterns()) { TRACE("pat_bug", tout << "ignoring user patterns...: " << m_patterns.size() << "\n";); result = m_smt->m_manager.mk_quantifier(m_is_forall, m_sorts.size(), // num_decls m_sorts.c_ptr(), // decl_sorts m_vars.begin(), // decl_names body, m_weight, m_qid, m_skid, 0, 0, 0, 0); } else if (!m_patterns.empty()) { if (!m_no_patterns.empty()) { m_smt->set_error("patterns were provided, ignoring :nopat attribute.", ((proto_expr*)0)); } result = m_smt->m_manager.mk_quantifier(m_is_forall, m_sorts.size(), // num_decls m_sorts.c_ptr(), // decl_sorts m_vars.begin(), // decl_names body, m_weight, m_qid, m_skid, m_patterns.size(), m_patterns.c_ptr(), 0, 0); } else { result = m_smt->m_manager.mk_quantifier(m_is_forall, m_sorts.size(), // num_decls m_sorts.c_ptr(), // decl_sorts m_vars.begin(), // decl_names body, m_weight, m_qid, m_skid, 0, 0, m_no_patterns.size(), m_no_patterns.c_ptr()); } // // reclaim memory resources on application. // m_vars.finalize(); m_sorts.finalize(); m_patterns.finalize(); m_no_patterns.finalize(); return true; } private: smtparser* m_smt; bool m_is_forall; int m_weight; symbol m_qid; symbol m_skid; expr_ref_buffer m_patterns; expr_ref_buffer m_no_patterns; sort_ref_buffer m_sorts; svector m_vars; symbol_table& m_local_scope; }; class builtin_builder : public idbuilder { smtparser* m_smt; family_id m_fid; decl_kind m_kind; vector m_params; public: builtin_builder(smtparser* smt, family_id fid, decl_kind k,vector const& p): m_smt(smt), m_fid(fid), m_kind(k), m_params(p) { } virtual bool apply(expr_ref_vector const& args, expr_ref& result) { ast_manager& m = m_smt->m_manager; func_decl* d = m.mk_func_decl(m_fid, m_kind, m_params.size(), m_params.c_ptr(), args.size(), args.c_ptr()); if (d) { result = m.mk_app(d, args.size(), args.c_ptr()); } m_params.finalize(); return d != 0; } }; bool push_status(smtlib::benchmark::status status) { m_benchmark.set_status( status); return true; } expr * mk_number(rational const & r, bool is_int){ if (m_int_sort == m_real_sort) // integer constants should be mapped to real is_int = false; return m_anum_util.mk_numeral(r, is_int); } void push_benchmark(symbol const & name) { m_benchmark.set_name(name); } bool push_assumption(expr * t) { m_benchmark.add_axiom(t); return true; } bool push_formula(expr * t) { m_benchmark.add_formula(t); return true; } bool is_binary_let_binding(proto_expr* let_binding) { return let_binding && let_binding->children() && let_binding->children()[0] && (let_binding->children()[0]->kind() == proto_expr::ID) && let_binding->children()[1] && !let_binding->children()[2]; } bool is_bvconst(symbol const & fname, unsigned num_params, parameter const* params, expr_ref & term) { rational n; char const * str = fname.bare_str(); unsigned sz = 0; if (strncmp(str, "bvbin", 5) == 0) { str += 5; n = rational(0); while (*str == '1' || *str == '0') { n *= rational(2); n += rational(*str - '0'); ++sz; ++str; } if (sz == 0) { return false; } } else if (strncmp(str, "bvhex", 5) == 0) { n = rational(0); str += 5; while (('0' <= *str && *str <= '9') || ('a' <= *str && *str <= 'f') || ('A' <= *str && *str <= 'F')) { n *= rational(16); if ('0' <= *str && *str <= '9') { n += rational(*str - '0'); } else if ('a' <= *str && *str <= 'f') { n += rational(10); n += rational(*str - 'a'); } else { SASSERT('A' <= *str && *str <= 'F'); n += rational(10); n += rational(*str - 'A'); } sz += 4; ++str; } if (sz == 0) { return false; } } else if (strncmp(str, "bv", 2) == 0 && '0' <= *(str + 2) && *(str + 2) <= '9') { n = rational(0); str += 2; while ('0' <= *str && *str <= '9') { n *= rational(10); n += rational(*str - '0'); ++str; } if (num_params == 1) { sz = params[0].get_int(); } else { sz = 32; } } else { return false; } term = m_bvnum_util.mk_numeral(n, sz); return true; } }; parser * parser::create(ast_manager& ast_manager, bool ignore_user_patterns) { return alloc(smtparser, ast_manager, ignore_user_patterns); } z3-z3-4.4.1/src/parsers/smt/smtparser.h000066400000000000000000000017041260446376700176710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smtparser.h Abstract: SMT parsing utilities Author: Nikolaj Bjorner (nbjorner) 2006-09-25 Revision History: --*/ #ifndef SMT_PARSER_H_ #define SMT_PARSER_H_ #include #include"ast.h" #include"vector.h" #include"smtlib.h" namespace smtlib { class parser { public: static parser * create(ast_manager & ast_manager, bool ignore_user_patterns = false); virtual ~parser() {} virtual void add_builtin_op(char const *, family_id fid, decl_kind kind) = 0; virtual void add_builtin_type(char const *, family_id fid, decl_kind kind) = 0; virtual void initialize_smtlib() = 0; virtual void set_error_stream(std::ostream& strm) = 0; virtual bool parse_file(char const * path) = 0; virtual bool parse_string(char const * string) = 0; virtual benchmark * get_benchmark() = 0; }; }; #endif z3-z3-4.4.1/src/parsers/smt2/000077500000000000000000000000001260446376700155605ustar00rootroot00000000000000z3-z3-4.4.1/src/parsers/smt2/smt2parser.cpp000066400000000000000000003105101260446376700203660ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt2parser.h Abstract: SMT 2.0 parser Author: Leonardo de Moura (leonardo) 2011-03-01 Revision History: --*/ #include"smt2parser.h" #include"smt2scanner.h" #include"stack.h" #include"datatype_decl_plugin.h" #include"bv_decl_plugin.h" #include"arith_decl_plugin.h" #include"ast_pp.h" #include"well_sorted.h" #include"pattern_validation.h" #include"rewriter.h" #include"has_free_vars.h" #include"ast_smt2_pp.h" #include"parser_params.hpp" namespace smt2 { typedef cmd_exception parser_exception; class parser { cmd_context & m_ctx; params_ref m_params; scanner m_scanner; scanner::token m_curr; cmd * m_curr_cmd; stack m_stack; struct local { expr * m_term; unsigned m_level; local():m_term(0), m_level(0) {} local(expr * t, unsigned l):m_term(t), m_level(l) {} }; symbol_table m_env; unsigned m_num_bindings; dictionary m_sort_id2param_idx; dictionary m_dt_name2idx; scoped_ptr m_psort_stack; scoped_ptr m_sort_stack; scoped_ptr m_expr_stack; unsigned m_num_expr_frames; scoped_ptr m_pattern_stack; scoped_ptr m_nopattern_stack; svector m_symbol_stack; vector m_param_stack; scoped_ptr m_sexpr_stack; scoped_ptr m_bv_util; scoped_ptr m_arith_util; scoped_ptr m_pattern_validator; scoped_ptr m_var_shifter; symbol m_let; symbol m_bang; symbol m_forall; symbol m_exists; symbol m_as; symbol m_not; symbol m_root_obj; symbol m_named; symbol m_weight; symbol m_qid; symbol m_skid; symbol m_ex_act; symbol m_pattern; symbol m_nopattern; symbol m_lblneg; symbol m_lblpos; symbol m_assert; symbol m_check_sat; symbol m_define_fun; symbol m_define_const; symbol m_declare_fun; symbol m_declare_const; symbol m_define_sort; symbol m_declare_sort; symbol m_declare_datatypes; symbol m_push; symbol m_pop; symbol m_get_value; symbol m_reset; symbol m_underscore; typedef std::pair named_expr; named_expr m_last_named_expr; ast_manager & m() const { return m_ctx.m(); } pdecl_manager & pm() const { return m_ctx.pm(); } sexpr_manager & sm() const { return m_ctx.sm(); } bool m_ignore_user_patterns; bool m_ignore_bad_patterns; bool m_display_error_for_vs; bool ignore_user_patterns() const { return m_ignore_user_patterns; } bool ignore_bad_patterns() const { return m_ignore_bad_patterns; } bool use_vs_format() const { return m_display_error_for_vs; } struct psort_frame { psort_decl * m_decl; unsigned m_spos; // position of m_psort_stack psort_frame(parser & p, psort_decl * d, unsigned spos): m_decl(d), m_spos(spos) { } }; typedef psort_frame sort_frame; enum expr_frame_kind { EF_APP, EF_LET, EF_LET_DECL, EF_QUANT, EF_ATTR_EXPR, EF_PATTERN }; struct expr_frame { expr_frame_kind m_kind; expr_frame(expr_frame_kind k):m_kind(k) {} }; struct app_frame : public expr_frame { symbol m_f; unsigned m_expr_spos; unsigned m_param_spos; bool m_as_sort; app_frame(symbol const & f, unsigned expr_spos, unsigned param_spos, bool as_sort): expr_frame(EF_APP), m_f(f), m_expr_spos(expr_spos), m_param_spos(param_spos), m_as_sort(as_sort) {} }; struct quant_frame : public expr_frame { bool m_forall; symbol m_qid; symbol m_skid; unsigned m_weight; unsigned m_pat_spos; unsigned m_nopat_spos; unsigned m_sym_spos; unsigned m_sort_spos; unsigned m_expr_spos; quant_frame(bool forall, unsigned pat_spos, unsigned nopat_spos, unsigned sym_spos, unsigned sort_spos, unsigned expr_spos): expr_frame(EF_QUANT), m_forall(forall), m_weight(1), m_pat_spos(pat_spos), m_nopat_spos(nopat_spos), m_sym_spos(sym_spos), m_sort_spos(sort_spos), m_expr_spos(expr_spos) {} }; struct let_frame : public expr_frame { bool m_in_decls; unsigned m_sym_spos; unsigned m_expr_spos; let_frame(unsigned sym_spos, unsigned expr_spos):expr_frame(EF_LET), m_in_decls(true), m_sym_spos(sym_spos), m_expr_spos(expr_spos) {} }; struct let_decl_frame : public expr_frame { let_decl_frame():expr_frame(EF_LET_DECL) {} }; struct attr_expr_frame : public expr_frame { expr_frame * m_prev; unsigned m_sym_spos; unsigned m_expr_spos; symbol m_last_symbol; attr_expr_frame(expr_frame * prev, unsigned sym_spos, unsigned expr_spos): expr_frame(EF_ATTR_EXPR), m_prev(prev), m_sym_spos(sym_spos), m_expr_spos(expr_spos) {} }; struct pattern_frame : public expr_frame { unsigned m_expr_spos; pattern_frame(unsigned expr_spos): expr_frame(EF_PATTERN), m_expr_spos(expr_spos) { } }; struct sexpr_frame { unsigned m_spos; // position of m_sexpr_stack sexpr_frame(unsigned spos): m_spos(spos) { } }; void reset_stack() { m_stack.reset(); } psort_ref_vector & psort_stack() { if (m_psort_stack.get() == 0) m_psort_stack = alloc(psort_ref_vector, pm()); return *(m_psort_stack.get()); } sort_ref_vector & sort_stack() { if (m_sort_stack.get() == 0) m_sort_stack = alloc(sort_ref_vector, m()); return *(m_sort_stack.get()); } expr_ref_vector & expr_stack() { if (m_expr_stack.get() == 0) m_expr_stack = alloc(expr_ref_vector, m()); return *(m_expr_stack.get()); } template static unsigned size(scoped_ptr & v) { return v.get() == 0 ? 0 : v->size(); } template static void shrink(scoped_ptr & v, unsigned old_sz) { if (v.get() == 0) { SASSERT(old_sz == 0); } else { v->shrink(old_sz); } } expr_ref_vector & pattern_stack() { if (m_pattern_stack.get() == 0) m_pattern_stack = alloc(expr_ref_vector, m()); return *(m_pattern_stack.get()); } expr_ref_vector & nopattern_stack() { if (m_nopattern_stack.get() == 0) m_nopattern_stack = alloc(expr_ref_vector, m()); return *(m_nopattern_stack.get()); } svector & symbol_stack() { return m_symbol_stack; } sexpr_ref_vector & sexpr_stack() { if (m_sexpr_stack.get() == 0) m_sexpr_stack = alloc(sexpr_ref_vector, sm()); return *(m_sexpr_stack.get()); } arith_util & autil() { if (m_arith_util.get() == 0) m_arith_util = alloc(arith_util, m()); return *(m_arith_util.get()); } bv_util & butil() { if (m_bv_util.get() == 0) m_bv_util = alloc(bv_util, m()); return *(m_bv_util.get()); } pattern_validator & pat_validator() { if (m_pattern_validator.get() == 0) { m_pattern_validator = alloc(pattern_validator, m()); } return *(m_pattern_validator.get()); } var_shifter & shifter() { if (m_var_shifter.get() == 0) m_var_shifter = alloc(var_shifter, m()); return *(m_var_shifter.get()); } unsigned m_cache_end; vector m_cached_strings; int m_num_open_paren; void scan_core() { m_cache_end = m_scanner.cache_size(); m_curr = m_scanner.scan(); } void scan() { switch (m_curr) { case scanner::LEFT_PAREN: m_num_open_paren++; break; case scanner::RIGHT_PAREN: m_num_open_paren--; break; default: break; } scan_core(); } void next() { if (m_curr != scanner::EOF_TOKEN) scan(); } scanner::token curr() const { return m_curr; } // consume garbage // return true if managed to recover from the error... bool sync_after_error() { while (true) { try { while (curr_is_rparen()) next(); if (m_num_open_paren < 0) m_num_open_paren = 0; if (curr() == scanner::EOF_TOKEN && m_num_open_paren == 0) return true; SASSERT(m_num_open_paren >= 0); while (m_num_open_paren > 0 || !curr_is_lparen()) { TRACE("sync", tout << "sync(): curr: " << curr() << "\n"; tout << "m_num_open_paren: " << m_num_open_paren << ", line: " << m_scanner.get_line() << ", pos: " << m_scanner.get_pos() << "\n";); if (curr() == scanner::EOF_TOKEN) { return false; } SASSERT(m_num_open_paren >= 0); next(); SASSERT(m_num_open_paren >= -1); if (m_num_open_paren < 0) m_num_open_paren = 0; SASSERT(m_num_open_paren >= 0); } return true; } catch (scanner_exception & ex) { SASSERT(ex.has_pos()); error(ex.line(), ex.pos(), ex.msg()); } } } void check_next(scanner::token t, char const * msg) { if (curr() == t) { next(); return; } throw parser_exception(msg); } symbol const & curr_id() const { return m_scanner.get_id(); } rational curr_numeral() const { return m_scanner.get_number(); } bool curr_is_identifier() const { return curr() == scanner::SYMBOL_TOKEN; } bool curr_is_keyword() const { return curr() == scanner::KEYWORD_TOKEN; } bool curr_is_string() const { return curr() == scanner::STRING_TOKEN; } bool curr_is_lparen() const { return curr() == scanner::LEFT_PAREN; } bool curr_is_rparen() const { return curr() == scanner::RIGHT_PAREN; } bool curr_is_int() const { return curr() == scanner::INT_TOKEN; } bool curr_is_float() const { return curr() == scanner::FLOAT_TOKEN; } bool curr_id_is_underscore() const { SASSERT(curr_is_identifier()); return curr_id() == m_underscore; } bool curr_id_is_as() const { SASSERT(curr_is_identifier()); return curr_id() == m_as; } bool curr_id_is_forall() const { SASSERT(curr_is_identifier()); return curr_id() == m_forall; } bool curr_id_is_exists() const { SASSERT(curr_is_identifier()); return curr_id() == m_exists; } bool curr_id_is_bang() const { SASSERT(curr_is_identifier()); return curr_id() == m_bang; } bool curr_id_is_let() const { SASSERT(curr_is_identifier()); return curr_id() == m_let; } bool curr_id_is_root_obj() const { SASSERT(curr_is_identifier()); return curr_id() == m_root_obj; } void check_lparen(char const * msg) { if (!curr_is_lparen()) throw parser_exception(msg); } void check_lparen_next(char const * msg) { check_next(scanner::LEFT_PAREN, msg); } void check_rparen_next(char const * msg) { check_next(scanner::RIGHT_PAREN, msg); } void check_rparen(char const * msg) { if (!curr_is_rparen()) throw parser_exception(msg); } void check_id_next(symbol const & id, char const * msg) { if (!curr_is_identifier() || curr_id() != id) throw parser_exception(msg); next(); } void check_underscore_next(char const * msg) { check_id_next(m_underscore, msg); } void check_as_next(char const * msg) { check_id_next(m_as, msg); } void check_identifier(char const * msg) { if (!curr_is_identifier()) throw parser_exception(msg); } void check_keyword(char const * msg) { if (!curr_is_keyword()) throw parser_exception(msg); } void check_string(char const * msg) { if (!curr_is_string()) throw parser_exception(msg); } void check_int(char const * msg) { if (!curr_is_int()) throw parser_exception(msg); } void check_int_or_float(char const * msg) { if (!curr_is_int() || !curr_is_float()) throw parser_exception(msg); } void check_float(char const * msg) { if (!curr_is_float()) throw parser_exception(msg); } void error(unsigned line, unsigned pos, char const * msg) { m_ctx.reset_cancel(); if (use_vs_format()) { m_ctx.diagnostic_stream() << "Z3(" << line << ", " << pos << "): ERROR: " << msg; if (msg[strlen(msg)-1] != '\n') m_ctx.diagnostic_stream() << std::endl; } else { m_ctx.regular_stream() << "(error \"line " << line << " column " << pos << ": " << escaped(msg, true) << "\")" << std::endl; } if (m_ctx.exit_on_error()) { exit(1); } } void error(char const * msg) { error(m_scanner.get_line(), m_scanner.get_pos(), msg); } void error_wo_pos(char const * msg) { if (use_vs_format()) { m_ctx.diagnostic_stream() << "Z3: ERROR: " << msg; if (msg[strlen(msg)-1] != '\n') m_ctx.diagnostic_stream() << std::endl; } else { m_ctx.regular_stream() << "(error : " << escaped(msg, true) << "\")" << std::endl; } } void unknown_sort(symbol id) { std::string msg = "unknown sort '"; msg += id.str() + "'"; throw parser_exception(msg.c_str()); } void consume_sexpr() { unsigned num_parens = 0; do { switch (curr()) { case scanner::LEFT_PAREN: num_parens++; break; case scanner::RIGHT_PAREN: if (num_parens == 0) throw parser_exception("invalid s-expression, unexpected ')'"); num_parens--; break; case scanner::SYMBOL_TOKEN: case scanner::KEYWORD_TOKEN: case scanner::STRING_TOKEN: case scanner::INT_TOKEN: case scanner::FLOAT_TOKEN: case scanner::BV_TOKEN: break; case scanner::EOF_TOKEN: throw parser_exception("invalid s-expression, unexpected end of file"); break; default: throw parser_exception("invalid s-expression, unexpected input"); break; } next(); } while (num_parens > 0); } void parse_sexpr() { unsigned stack_pos = sexpr_stack().size(); unsigned num_frames = 0; do { unsigned line = m_scanner.get_line(); unsigned pos = m_scanner.get_pos(); switch (curr()) { case scanner::LEFT_PAREN: { void * mem = m_stack.allocate(sizeof(sexpr_frame)); new (mem) sexpr_frame(sexpr_stack().size()); num_frames++; break; } case scanner::RIGHT_PAREN: { if (num_frames == 0) throw parser_exception("invalid s-expression, unexpected ')'"); num_frames--; sexpr_frame * fr = static_cast(m_stack.top()); unsigned spos = fr->m_spos; unsigned epos = sexpr_stack().size(); SASSERT(epos >= spos); unsigned num = epos - spos; if (num == 0) throw parser_exception("invalid empty s-expression"); sexpr * r = sm().mk_composite(num, sexpr_stack().c_ptr() + spos, line, pos); sexpr_stack().shrink(spos); sexpr_stack().push_back(r); m_stack.deallocate(fr); break; } case scanner::SYMBOL_TOKEN: sexpr_stack().push_back(sm().mk_symbol(curr_id(), line, pos)); break; case scanner::KEYWORD_TOKEN: sexpr_stack().push_back(sm().mk_keyword(curr_id(), line, pos)); break; case scanner::STRING_TOKEN: sexpr_stack().push_back(sm().mk_string(m_scanner.get_string(), line, pos)); break; case scanner::INT_TOKEN: case scanner::FLOAT_TOKEN: sexpr_stack().push_back(sm().mk_numeral(curr_numeral(), line, pos)); break; case scanner::BV_TOKEN: sexpr_stack().push_back(sm().mk_bv_numeral(curr_numeral(), m_scanner.get_bv_size(), line, pos)); break; case scanner::EOF_TOKEN: throw parser_exception("invalid s-expression, unexpected end of file"); break; default: throw parser_exception("invalid s-expression, unexpected input"); break; } next(); } while (num_frames > 0); SASSERT(sexpr_stack().size() == stack_pos + 1); } sort * parse_sort_name() { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); if (d == 0) unknown_sort(id); if (!d->has_var_params() && d->get_num_params() != 0) throw parser_exception("sort constructor expects parameters"); sort * r = d->instantiate(pm()); if (r == 0) throw parser_exception("invalid sort application"); next(); return r; } psort * parse_psort_name(bool ignore_unknow_sort = false) { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); if (d != 0) { if (!d->has_var_params() && d->get_num_params() != 0) throw parser_exception("sort constructor expects parameters"); next(); return pm().mk_psort_app(d); } else { int idx = 0; if (m_sort_id2param_idx.find(id, idx)) { next(); return pm().mk_psort_var(m_sort_id2param_idx.size(), idx); } else { if (ignore_unknow_sort) return 0; unknown_sort(id); UNREACHABLE(); return 0; } } } sort * parse_indexed_sort() { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_underscore()); next(); check_identifier("invalid indexed sort, symbol expected"); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); if (d == 0) unknown_sort(id); next(); sbuffer args; while (!curr_is_rparen()) { check_int("invalid indexed sort, integer or ')' expected"); rational n = curr_numeral(); if (!n.is_unsigned()) throw parser_exception("invalid indexed sort, index is too big to fit in an unsigned machine integer"); args.push_back(n.get_unsigned()); next(); } if (args.empty()) throw parser_exception("invalid indexed sort, index expected"); sort * r = d->instantiate(pm(), args.size(), args.c_ptr()); if (r == 0) throw parser_exception("invalid sort application"); next(); return r; } void push_psort_app_frame() { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); if (d == 0) unknown_sort(id); next(); void * mem = m_stack.allocate(sizeof(psort_frame)); new (mem) psort_frame(*this, d, psort_stack().size()); } void pop_psort_app_frame() { SASSERT(curr_is_rparen()); psort_frame * fr = static_cast(m_stack.top()); psort_decl * d = fr->m_decl; unsigned spos = fr->m_spos; unsigned epos = psort_stack().size(); SASSERT(epos >= spos); unsigned num = epos - spos; if (!d->has_var_params() && d->get_num_params() != num) { TRACE("smt2parser", tout << "num: " << num << ", d->get_num_params(): " << d->get_num_params() << "\n";); throw parser_exception("invalid number of parameters to sort constructor"); } psort * r = pm().mk_psort_app(m_sort_id2param_idx.size(), d, num, psort_stack().c_ptr() + spos); psort_stack().shrink(spos); psort_stack().push_back(r); m_stack.deallocate(fr); next(); } void parse_psort() { unsigned stack_pos = psort_stack().size(); unsigned num_frames = 0; do { if (curr_is_identifier()) { psort_stack().push_back(parse_psort_name()); } else if (curr_is_rparen()) { if (num_frames == 0) throw parser_exception("invalid sort, unexpected ')'"); pop_psort_app_frame(); num_frames--; } else { check_lparen_next("invalid sort, symbol, '_' or '(' expected"); if (!curr_is_identifier()) throw parser_exception("invalid sort, symbol or '_' expected"); if (curr_id_is_underscore()) { psort_stack().push_back(pm().mk_psort_cnst(parse_indexed_sort())); } else { push_psort_app_frame(); num_frames++; } } } while (num_frames > 0); SASSERT(psort_stack().size() == stack_pos + 1); } void push_sort_app_frame() { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); if (d == 0) unknown_sort(id); next(); void * mem = m_stack.allocate(sizeof(sort_frame)); new (mem) sort_frame(*this, d, sort_stack().size()); } void pop_sort_app_frame() { SASSERT(curr_is_rparen()); sort_frame * fr = static_cast(m_stack.top()); psort_decl * d = fr->m_decl; unsigned spos = fr->m_spos; unsigned epos = sort_stack().size(); SASSERT(epos >= spos); unsigned num = epos - spos; if (!d->has_var_params() && d->get_num_params() != num) { TRACE("smt2parser", tout << "num: " << num << ", d->get_num_params(): " << d->get_num_params() << "\n";); throw parser_exception("invalid number of parameters to sort constructor"); } sort * r = d->instantiate(pm(), num, sort_stack().c_ptr() + spos); if (r == 0) throw parser_exception("invalid sort application"); sort_stack().shrink(spos); sort_stack().push_back(r); m_stack.deallocate(fr); next(); } void parse_sort() { unsigned stack_pos = sort_stack().size(); unsigned num_frames = 0; do { if (curr_is_identifier()) { sort_stack().push_back(parse_sort_name()); } else if (curr_is_rparen()) { if (num_frames == 0) throw parser_exception("invalid sort, unexpected ')'"); pop_sort_app_frame(); num_frames--; } else { check_lparen_next("invalid sort, symbol, '_' or '(' expected"); if (!curr_is_identifier()) throw parser_exception("invalid sort, symbol or '_' expected"); if (curr_id_is_underscore()) { sort_stack().push_back(parse_indexed_sort()); } else { push_sort_app_frame(); num_frames++; } } } while (num_frames > 0); SASSERT(sort_stack().size() == stack_pos + 1); } unsigned parse_sorts() { unsigned sz = 0; check_lparen_next("invalid list of sorts, '(' expected"); while (!curr_is_rparen()) { parse_sort(); sz++; } next(); return sz; } unsigned parse_symbols() { unsigned sz = 0; check_lparen_next("invalid list of symbols, '(' expected"); while (!curr_is_rparen()) { check_identifier("invalid list of symbols, symbol or ')' expected"); m_symbol_stack.push_back(curr_id()); next(); sz++; } next(); return sz; } // [ '(' identifier sort ')' ]+ void parse_accessor_decls(paccessor_decl_ref_buffer & a_decls) { while (!curr_is_rparen()) { check_lparen_next("invalid datatype declaration, '(' or ')' expected"); check_identifier("invalid accessor declaration, symbol (accessor name) expected"); symbol a_name = curr_id(); next(); if (curr_is_identifier()) { psort * p = parse_psort_name(true); if (p != 0) { a_decls.push_back(pm().mk_paccessor_decl(m_sort_id2param_idx.size(), a_name, ptype(p))); } else { // parse_psort_name failed, identifier was not consumed. int idx; if (m_dt_name2idx.find(curr_id(), idx)) { a_decls.push_back(pm().mk_paccessor_decl(m_sort_id2param_idx.size(), a_name, ptype(idx))); } else { a_decls.push_back(pm().mk_paccessor_decl(m_sort_id2param_idx.size(), a_name, ptype(curr_id()))); } SASSERT(curr_is_identifier()); next(); } } else { parse_psort(); a_decls.push_back(pm().mk_paccessor_decl(m_sort_id2param_idx.size(), a_name, ptype(psort_stack().back()))); psort_stack().pop_back(); } check_rparen_next("invalid accessor declaration, ')' expected"); } } // [ '(' identifier accessors ')' ]+ void parse_constructor_decls(pconstructor_decl_ref_buffer & ct_decls) { while (!curr_is_rparen()) { if (curr_is_identifier()) { symbol ct_name = curr_id(); std::string r_str = "is-"; r_str += curr_id().str(); symbol r_name(r_str.c_str()); next(); TRACE("datatype_parser_bug", tout << ct_name << " " << r_name << "\n";); ct_decls.push_back(pm().mk_pconstructor_decl(m_sort_id2param_idx.size(), ct_name, r_name, 0, 0)); } else { check_lparen_next("invalid datatype declaration, '(' or ')' expected"); check_identifier("invalid constructor declaration, symbol (constructor name) expected"); symbol ct_name = curr_id(); std::string r_str = "is-"; r_str += curr_id().str(); symbol r_name(r_str.c_str()); next(); paccessor_decl_ref_buffer new_a_decls(pm()); parse_accessor_decls(new_a_decls); ct_decls.push_back(pm().mk_pconstructor_decl(m_sort_id2param_idx.size(), ct_name, r_name, new_a_decls.size(), new_a_decls.c_ptr())); check_rparen_next("invalid constructor declaration, ')' expected"); } } if (ct_decls.empty()) throw parser_exception("invalid datatype declaration, datatype does not have any constructors"); } void parse_declare_datatypes() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_declare_datatypes); next(); unsigned line = m_scanner.get_line(); unsigned pos = m_scanner.get_pos(); parse_sort_decl_params(); m_dt_name2idx.reset(); unsigned i = 0; pdatatype_decl_ref_buffer new_dt_decls(pm()); check_lparen_next("invalid datatype declaration, '(' expected"); while (!curr_is_rparen()) { check_lparen_next("invalid datatype declaration, '(' or ')' expected"); check_identifier("invalid datatype declaration, symbol (datatype name) expected"); symbol dt_name = curr_id(); next(); m_dt_name2idx.insert(dt_name, i); pconstructor_decl_ref_buffer new_ct_decls(pm()); parse_constructor_decls(new_ct_decls); new_dt_decls.push_back(pm().mk_pdatatype_decl(m_sort_id2param_idx.size(), dt_name, new_ct_decls.size(), new_ct_decls.c_ptr())); check_rparen_next("invalid datatype declaration, ')' expected"); i++; } next(); check_rparen("invalid datatype declaration"); unsigned sz = new_dt_decls.size(); if (sz == 0) { m_ctx.print_success(); next(); return; } else if (sz == 1) { symbol missing; if (new_dt_decls[0]->has_missing_refs(missing)) { std::string err_msg = "invalid datatype declaration, unknown sort '"; err_msg += missing.str(); err_msg += "'"; throw parser_exception(err_msg, line, pos); } } else { SASSERT(sz > 1); pdatatypes_decl_ref dts(pm()); dts = pm().mk_pdatatypes_decl(m_sort_id2param_idx.size(), sz, new_dt_decls.c_ptr()); symbol missing; if (!pm().fix_missing_refs(dts, missing)) { std::string err_msg = "invalid datatype declaration, unknown sort '"; err_msg += missing.str(); err_msg += "'"; throw parser_exception(err_msg, line, pos); } m_ctx.insert_aux_pdecl(dts.get()); } for (unsigned i = 0; i < sz; i++) { pdatatype_decl * d = new_dt_decls[i]; SASSERT(d != 0); symbol duplicated; if (d->has_duplicate_accessors(duplicated)) { std::string err_msg = "invalid datatype declaration, repeated accessor identifier '"; err_msg += duplicated.str(); err_msg += "'"; throw parser_exception(err_msg, line, pos); } m_ctx.insert(d); if (d->get_num_params() == 0) { // if datatype is not parametric... then force instantiation to register accessor, recognizers and constructors... sort_ref s(m()); s = d->instantiate(pm(), 0, 0); } } TRACE("declare_datatypes", tout << "i: " << i << " new_dt_decls.size(): " << sz << "\n"; for (unsigned i = 0; i < sz; i++) tout << new_dt_decls[i]->get_name() << "\n";); m_ctx.print_success(); next(); } void name_expr(expr * n, symbol const & s) { TRACE("name_expr", tout << "naming: " << s << " ->\n" << mk_pp(n, m()) << "\n";); if (!is_ground(n) && has_free_vars(n)) throw parser_exception("invalid named expression, expression contains free variables"); m_ctx.insert(s, 0, n); m_last_named_expr.first = s; m_last_named_expr.second = n; } bool in_quant_ctx(attr_expr_frame * fr) { return fr != 0 && fr->m_prev != 0 && fr->m_prev->m_kind == EF_QUANT; } void check_in_quant_ctx(attr_expr_frame * fr) { if (!in_quant_ctx(fr)) throw parser_exception("invalid attribute, not in the scope of a quantifier"); } void process_last_symbol(attr_expr_frame * fr) { if (fr->m_last_symbol == symbol::null) return; if (fr->m_last_symbol == m_pattern) { expr * pat = expr_stack().back(); if (pat == 0) { if (!ignore_bad_patterns()) throw parser_exception("invalid empty pattern"); } else { if (!m().is_pattern(pat)) pat = m().mk_pattern(to_app(pat)); // unary pattern SASSERT(m().is_pattern(pat)); pattern_stack().push_back(pat); } expr_stack().pop_back(); } else if (fr->m_last_symbol == m_nopattern) { nopattern_stack().push_back(expr_stack().back()); expr_stack().pop_back(); } else { UNREACHABLE(); } } void store_qid(attr_expr_frame * fr, symbol const & qid) { SASSERT(in_quant_ctx(fr)); static_cast(fr->m_prev)->m_qid = qid; } void store_skid(attr_expr_frame * fr, symbol const & skid) { SASSERT(in_quant_ctx(fr)); static_cast(fr->m_prev)->m_skid = skid; } void store_weight(attr_expr_frame * fr, unsigned w) { SASSERT(in_quant_ctx(fr)); static_cast(fr->m_prev)->m_weight = w; } // parse expression state enum pe_state { PES_EXPR, // expecting PES_DECL, // expecting ( ) PES_PATTERN, PES_CONTINUE }; pe_state consume_attributes(attr_expr_frame * fr) { if (fr->m_expr_spos == expr_stack().size()) return PES_EXPR; // didn't parse the expression yet. process_last_symbol(fr); while (true) { check_keyword("invalid attributed expression, keyword expected"); symbol id = curr_id(); fr->m_last_symbol = symbol::null; TRACE("consume_attributes", tout << "id: " << id << ", expr_stack().size(): " << expr_stack().size() << "\n";); if (id == m_named) { next(); check_identifier("invalid attribute value, symbol expected"); name_expr(expr_stack().back(), curr_id()); next(); } else if (id == m_lblpos || id == m_lblneg) { next(); check_identifier("invalid attribute value, symbol expected"); if (!m().is_bool(expr_stack().back())) throw parser_exception("invalid labeled expression, expression must have Bool sort"); expr * new_expr = m().mk_label(id == m_lblpos, curr_id(), expr_stack().back()); expr_stack().pop_back(); expr_stack().push_back(new_expr); next(); } else if (id == m_weight) { check_in_quant_ctx(fr); next(); check_int("invalid weight attribute, integer expected"); rational n = curr_numeral(); if (!n.is_unsigned()) throw parser_exception("invalid weight attribute, value is too big to fit in an unsigned machine integer"); store_weight(fr, n.get_unsigned()); next(); } else if (id == m_skid) { check_in_quant_ctx(fr); next(); check_identifier("invalid attribute value, symbol expected"); store_skid(fr, curr_id()); next(); } else if (id == m_qid) { check_in_quant_ctx(fr); next(); check_identifier("invalid attribute value, symbol expected"); store_qid(fr, curr_id()); next(); } else if (id == m_pattern) { if (!ignore_user_patterns()) { check_in_quant_ctx(fr); next(); fr->m_last_symbol = id; return PES_PATTERN; } else { // just consume pattern next(); consume_sexpr(); } } else if (id == m_nopattern) { if (!ignore_user_patterns()) { check_in_quant_ctx(fr); next(); fr->m_last_symbol = id; return PES_EXPR; } else { // just consume pattern next(); consume_sexpr(); } } else { std::ostringstream str; str << "unknown attribute " << id; warning_msg(str.str().c_str()); next(); // just consume the consume_sexpr(); } if (curr_is_rparen()) return PES_CONTINUE; } } pe_state parse_expr_state() { if (m_num_expr_frames == 0) return PES_EXPR; expr_frame * fr = static_cast(m_stack.top()); switch (fr->m_kind) { case EF_LET: return static_cast(fr)->m_in_decls ? PES_DECL : PES_EXPR; case EF_ATTR_EXPR: return consume_attributes(static_cast(fr)); default: return PES_EXPR; } } void parse_numeral(bool is_int) { SASSERT(!is_int || curr_is_int()); SASSERT(is_int || curr_is_float()); TRACE("parse_numeral", tout << "curr(): " << curr() << ", curr_numeral(): " << curr_numeral() << ", is_int: " << is_int << "\n";); expr_stack().push_back(autil().mk_numeral(curr_numeral(), is_int && !m_ctx.numeral_as_real())); next(); } void parse_bv_numeral() { SASSERT(curr() == scanner::BV_TOKEN); expr_stack().push_back(butil().mk_numeral(curr_numeral(), m_scanner.get_bv_size())); TRACE("parse_bv_numeral", tout << "new numeral: " << mk_pp(expr_stack().back(), m()) << "\n";); next(); } void push_pattern_frame() { // TODO: It seems the only reliable way to parse patterns is: // Parse as an S-Expr, then try to convert it to an useful pattern. // If it is not possible, then discard pattern. // After this modification, the (PROMOTE) hack below can be removed. if (curr_is_lparen()) { next(); } else { if (!ignore_bad_patterns()) throw parser_exception("invalid pattern, '(' expected"); consume_sexpr(); expr_stack().push_back(0); // empty pattern return; } if (curr_is_lparen()) { // multi-pattern void * mem = m_stack.allocate(sizeof(pattern_frame)); new (mem) pattern_frame(expr_stack().size()); m_num_expr_frames++; } else if (curr_is_rparen()) { next(); expr_stack().push_back(0); // empty pattern } else { // unary pattern // HACK: to consume & discard (PROMOTE)-like patterns that were incorrectly introduced in SMT-LIB 2.0 // when Simplify benchmarks were converted into SMT2 ones. if (curr_is_identifier()) { symbol id = curr_id(); func_decl * f = 0; try { f = m_ctx.find_func_decl(id); } catch (cmd_exception &) { } if (f && f->get_arity() == 0) { if (!ignore_bad_patterns()) throw parser_exception("invalid constant pattern"); while (!curr_is_rparen()) consume_sexpr(); next(); expr_stack().push_back(0); // empty pattern return; // no frame is created } } if (!curr_is_lparen() && !curr_is_identifier()) throw parser_exception("invalid pattern, '(' or identifier expected"); push_app_frame(); } } void push_let_decl_frame() { check_lparen_next("invalid let declaration, '(' expected"); check_identifier("invalid let declaration, symbol expected"); symbol_stack().push_back(curr_id()); next(); void * mem = m_stack.allocate(sizeof(let_decl_frame)); new (mem) let_decl_frame(); m_num_expr_frames++; } unsigned parse_sorted_vars() { unsigned num = 0; unsigned sym_spos = symbol_stack().size(); unsigned sort_spos = sort_stack().size(); TRACE("parse_sorted_vars", tout << "[before] symbol_stack().size(): " << symbol_stack().size() << "\n";); check_lparen_next("invalid list of sorted variables, '(' expected"); m_env.begin_scope(); while (!curr_is_rparen()) { check_lparen_next("invalid sorted variable, '(' expected"); check_identifier("invalid sorted variable, symbol expected"); symbol_stack().push_back(curr_id()); TRACE("parse_sorted_vars", tout << "push_back curr_id(): " << curr_id() << "\n";); next(); parse_sort(); check_rparen_next("invalid sorted variable, ')' expected"); num++; } next(); TRACE("parse_sorted_vars", tout << "[after] symbol_stack().size(): " << symbol_stack().size() << "\n";); symbol const * sym_it = symbol_stack().c_ptr() + sym_spos; sort * const * sort_it = sort_stack().c_ptr() + sort_spos; m_num_bindings += num; unsigned i = num; while (i > 0) { --i; var * v = m().mk_var(i, *sort_it); expr_stack().push_back(v); // prevent v from being deleted TRACE("parse_sorted_vars", tout << "registering " << *sym_it << " -> " << mk_pp(v, m()) << ", num: " << num << ", i: " << i << "\n";); m_env.insert(*sym_it, local(v, m_num_bindings)); SASSERT(m_env.contains(*sym_it)); ++sort_it; ++sym_it; } return num; } void push_quant_frame(bool is_forall) { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_forall() || curr_id_is_exists()); SASSERT(!is_forall || curr_id_is_forall()); SASSERT(is_forall || curr_id_is_exists()); next(); void * mem = m_stack.allocate(sizeof(quant_frame)); new (mem) quant_frame(is_forall, pattern_stack().size(), nopattern_stack().size(), symbol_stack().size(), sort_stack().size(), expr_stack().size()); m_num_expr_frames++; unsigned num_vars = parse_sorted_vars(); if (num_vars == 0) throw parser_exception("invalid quantifier, list of sorted variables is empty"); } symbol parse_indexed_identifier_core() { check_underscore_next("invalid indexed identifier, '_' expected"); check_identifier("invalid indexed identifier, symbol expected"); symbol r = curr_id(); next(); unsigned num_indices = 0; while (!curr_is_rparen()) { if (curr_is_int()) { rational n = curr_numeral(); if (!n.is_unsigned()) throw parser_exception("invalid indexed identifier, index is too big to fit in an unsigned machine integer"); m_param_stack.push_back(parameter(n.get_unsigned())); next(); } else if (curr_is_identifier() || curr_is_lparen()) { m_param_stack.push_back(parameter(parse_func_decl_ref())); } else { throw parser_exception("invalid indexed identifier, integer, identifier or '(' expected"); } num_indices++; } if (num_indices == 0) throw parser_exception("invalid indexed identifier, index expected"); next(); return r; } symbol parse_indexed_identifier() { if (curr_is_identifier()) { symbol r = curr_id(); next(); return r; } check_lparen_next("invalid (indexed) identifier, '(_' or symbol expected"); return parse_indexed_identifier_core(); } // parse: // 'as' ')' // '_' + ')' // 'as' (|)+ ')' ')' symbol parse_qualified_identifier_core(bool & has_as) { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_underscore() || curr_id_is_as()); if (curr_id_is_underscore()) { has_as = false; return parse_indexed_identifier_core(); } else { SASSERT(curr_id_is_as()); has_as = true; next(); symbol r = parse_indexed_identifier(); parse_sort(); check_rparen_next("invalid qualified identifier, ')' expected"); return r; } } // parse: // // '(' 'as' ')' // '(' '_' + ')' // '(' 'as' (|)+ ')' ')' symbol parse_qualified_identifier(bool & has_as) { SASSERT(curr_is_lparen() || curr_is_identifier()); if (curr_is_identifier()) { has_as = false; symbol r = curr_id(); next(); return r; } SASSERT(curr_is_lparen()); next(); if (!curr_is_identifier() || (!curr_id_is_underscore() && !curr_id_is_as())) throw parser_exception("invalid qualified/indexed identifier, '_' or 'as' expected"); return parse_qualified_identifier_core(has_as); } void unknown_var_const_name(symbol id) { std::string msg = "unknown constant/variable '"; msg += id.str() + "'"; throw parser_exception(msg.c_str()); } rational m_last_bv_numeral; // for bv, bvbin, bvhex // return true if *s == [0-9]+ bool is_bv_decimal(char const * s) { TRACE("is_bv_num", tout << "is_bv_decimal: " << s << "\n";); SASSERT('0' <= *s && *s <= '9'); rational & n = m_last_bv_numeral; n = rational(*s - '0'); ++s; while ('0' <= *s && *s <= '9') { n *= rational(10); n += rational(*s - '0'); ++s; } if (*s != 0) return false; return true; } // return true if *s == bin[0-1]+ bool is_bv_binary(char const * s) { SASSERT(*s == 'b'); ++s; if (*s != 'i') return false; ++s; if (*s != 'n') return false; ++s; rational & n = m_last_bv_numeral; unsigned i = 0; n = rational(0); while (*s == '0' || *s == '1') { n *= rational(2); n += rational(*s - '0'); ++s; ++i; } if (*s != 0 || i == 0) return false; return true; } // return true if *s == hex[0-9,a-f,A-F]+ bool is_bv_hex(char const * s) { SASSERT(*s == 'h'); ++s; if (*s != 'e') return false; ++s; if (*s != 'x') return false; ++s; rational & n = m_last_bv_numeral; unsigned i = 0; n = rational(0); while (true) { if ('0' <= *s && *s <= '9') { n *= rational(16); n += rational(*s - '0'); } else if ('a' <= *s && *s <= 'f') { n *= rational(16); n += rational(10 + (*s - 'a')); } else if ('A' <= *s && *s <= 'F') { n *= rational(16); n += rational(10 + (*s - 'A')); } else if (*s == 0) { return i > 0; } else { return false; } ++s; ++i; } } // Return true if // n == bv[0-9]+ OR // n == bvhex[0-9,a-f,A-F]+ OR // n == bvbin[0-1]+ // It store the bit-vector value in m_last_bv_numeral bool is_bv_num(symbol const & n) { char const * s = n.bare_str(); if (*s != 'b') return false; s++; if (*s != 'v') return false; s++; if ('0' <= *s && *s <= '9') return is_bv_decimal(s); else if (*s == 'b') return is_bv_binary(s); else if (*s == 'h') return is_bv_hex(s); else return false; } void push_local(local const & l) { if (is_ground(l.m_term) || l.m_level == m_num_bindings) { expr_stack().push_back(l.m_term); } else { SASSERT(l.m_level <= m_num_bindings); expr_ref new_term(m()); shifter()(l.m_term, m_num_bindings - l.m_level, new_term); expr_stack().push_back(new_term); } } // parse as expression void parse_expr_name() { SASSERT(curr_is_identifier()); symbol n = curr_id(); local l; if (m_env.find(n, l)) { push_local(l); } else { expr_ref t_ref(m()); m_ctx.mk_const(n, t_ref); expr_stack().push_back(t_ref.get()); } next(); } // if has_as == true, then the sort of t must be equal to sort_stack().pop_back() // if that is the case, pop the top of sort_stack() void check_qualifier(expr * t, bool has_as) { if (has_as) { sort * s = sort_stack().back(); if (s != m().get_sort(t)) throw parser_exception("invalid qualified identifier, sort mismatch"); sort_stack().pop_back(); } } // parse // 'as' ')' // '_' + ')' // 'as' '(' (|)+ ')' ')' void parse_qualified_name() { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_as() || curr_id_is_underscore()); TRACE("parse_qualified_name", tout << "parse_qualified_name() curr_id: " << curr_id() << "\n";); unsigned param_spos = m_param_stack.size(); bool has_as; symbol r = parse_qualified_identifier_core(has_as); TRACE("parse_qualified_name", tout << "parse_qualified_name() r: " << r << "\n";); expr * t; local l; if (m_env.find(r, l)) { push_local(l); t = expr_stack().back(); check_qualifier(t, has_as); if (m_param_stack.size() != param_spos) throw parser_exception("invalid indexed identifier, symbol is a local declaration"); return; } unsigned num_indices = m_param_stack.size() - param_spos; if (is_bv_num(r)) { if (num_indices != 1 || !m_param_stack.back().is_int()) throw parser_exception("invalid bit-vector constant, index expected"); unsigned bv_size = m_param_stack.back().get_int(); m_param_stack.pop_back(); t = butil().mk_numeral(m_last_bv_numeral, bv_size); expr_stack().push_back(t); check_qualifier(t, has_as); return; } expr_ref t_ref(m()); m_ctx.mk_app(r, 0, 0, num_indices, m_param_stack.c_ptr() + param_spos, has_as ? sort_stack().back() : 0, t_ref); m_param_stack.shrink(param_spos); expr_stack().push_back(t_ref.get()); if (has_as) { check_qualifier(t_ref.get(), has_as); } } void parse_root_obj() { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_root_obj()); next(); parse_sexpr(); sexpr * p = sexpr_stack().back(); check_int("invalid root-obj, (unsigned) integer expected"); rational idx = curr_numeral(); if (!idx.is_unsigned()) throw parser_exception("invalid root-obj, index must fit in an unsigned machine integer"); unsigned u_idx = idx.get_unsigned(); if (u_idx == 0) throw parser_exception("invalid root-obj, index must be >= 1"); next(); check_rparen_next("invalid root-obj, ')' expected"); expr_stack().push_back(autil().mk_numeral(p, u_idx)); sexpr_stack().pop_back(); } void push_app_frame() { SASSERT(curr_is_lparen() || curr_is_identifier()); unsigned param_spos = m_param_stack.size(); unsigned expr_spos = expr_stack().size(); bool has_as; symbol f = parse_qualified_identifier(has_as); void * mem = m_stack.allocate(sizeof(quant_frame)); new (mem) app_frame(f, expr_spos, param_spos, has_as); m_num_expr_frames++; } // return true if a new frame was created. void push_expr_frame(expr_frame * curr) { SASSERT(curr_is_lparen()); next(); TRACE("push_expr_frame", tout << "push_expr_frame(), curr(): " << m_curr << "\n";); if (curr_is_identifier()) { TRACE("push_expr_frame", tout << "push_expr_frame(), curr_id(): " << curr_id() << "\n";); if (curr_id_is_let()) { next(); check_lparen_next("invalid let declaration, '(' expected"); void * mem = m_stack.allocate(sizeof(let_frame)); new (mem) let_frame(symbol_stack().size(), expr_stack().size()); m_num_expr_frames++; } else if (curr_id_is_forall()) { push_quant_frame(true); } else if (curr_id_is_exists()) { push_quant_frame(false); } else if (curr_id_is_bang()) { TRACE("consume_attributes", tout << "begin bang, expr_stack.size(): " << expr_stack().size() << "\n";); next(); void * mem = m_stack.allocate(sizeof(attr_expr_frame)); new (mem) attr_expr_frame(curr, symbol_stack().size(), expr_stack().size()); m_num_expr_frames++; } else if (curr_id_is_as() || curr_id_is_underscore()) { TRACE("push_expr_frame", tout << "push_expr_frame(): parse_qualified_name\n";); parse_qualified_name(); } else if (curr_id_is_root_obj()) { parse_root_obj(); } else { push_app_frame(); } } else if (curr_is_lparen()) { push_app_frame(); } else { throw parser_exception("invalid expression, '(' or symbol expected"); } } void pop_app_frame(app_frame * fr) { SASSERT(expr_stack().size() >= fr->m_expr_spos); SASSERT(m_param_stack.size() >= fr->m_param_spos); if (expr_stack().size() == fr->m_expr_spos) throw parser_exception("invalid function application, arguments missing"); unsigned num_args = expr_stack().size() - fr->m_expr_spos; unsigned num_indices = m_param_stack.size() - fr->m_param_spos; expr_ref t_ref(m()); m_ctx.mk_app(fr->m_f, num_args, expr_stack().c_ptr() + fr->m_expr_spos, num_indices, m_param_stack.c_ptr() + fr->m_param_spos, fr->m_as_sort ? sort_stack().back() : 0, t_ref); expr_stack().shrink(fr->m_expr_spos); m_param_stack.shrink(fr->m_param_spos); if (fr->m_as_sort) sort_stack().pop_back(); TRACE("pop_app_frame", tout << "new term: " << mk_pp(t_ref, m()) << "\n";); expr_stack().push_back(t_ref.get()); m_stack.deallocate(fr); m_num_expr_frames--; } void pop_let_frame(let_frame * fr) { if (fr->m_in_decls) { m_env.begin_scope(); fr->m_in_decls = false; SASSERT(symbol_stack().size() >= fr->m_sym_spos); SASSERT(expr_stack().size() >= fr->m_expr_spos); SASSERT(symbol_stack().size() - fr->m_sym_spos == expr_stack().size() - fr->m_expr_spos); unsigned num_decls = expr_stack().size() - fr->m_expr_spos; symbol * sym_it = symbol_stack().c_ptr() + fr->m_sym_spos; expr ** expr_it = expr_stack().c_ptr() + fr->m_expr_spos; expr ** expr_end = expr_it + num_decls; for (; expr_it != expr_end; ++expr_it, ++sym_it) { TRACE("let_frame", tout << "declaring: " << *sym_it << " " << mk_pp(*expr_it, m()) << "\n";); m_env.insert(*sym_it, local(*expr_it, m_num_bindings)); } } else { // the resultant expression is on the top of the stack TRACE("let_frame", tout << "let result expr: " << mk_pp(expr_stack().back(), m()) << "\n";); expr_ref r(m()); r = expr_stack().back(); expr_stack().pop_back(); // remove local declarations from the stack symbol_stack().shrink(fr->m_sym_spos); expr_stack().shrink(fr->m_expr_spos); m_env.end_scope(); // put result back on the stack expr_stack().push_back(r.get()); m_stack.deallocate(fr); m_num_expr_frames--; } } void pop_quant_frame(quant_frame * fr) { SASSERT(pattern_stack().size() >= fr->m_pat_spos); SASSERT(nopattern_stack().size() >= fr->m_nopat_spos); SASSERT(symbol_stack().size() >= fr->m_sym_spos); SASSERT(sort_stack().size() >= fr->m_sort_spos); SASSERT(symbol_stack().size() - fr->m_sym_spos == sort_stack().size() - fr->m_sort_spos); SASSERT(expr_stack().size() >= fr->m_expr_spos); unsigned num_decls = sort_stack().size() - fr->m_sort_spos; if (expr_stack().size() - fr->m_expr_spos != num_decls /* variables */ + 1 /* result */) throw parser_exception("invalid quantified expression, syntax error: (forall|exists (( )*) ) expected"); unsigned begin_pats = fr->m_pat_spos; unsigned end_pats = pattern_stack().size(); unsigned j = begin_pats; for (unsigned i = begin_pats; i < end_pats; i++) { expr * pat = pattern_stack().get(i); if (!pat_validator()(num_decls, pat)) { if (!ignore_bad_patterns()) throw parser_exception("invalid pattern"); continue; } pattern_stack().set(j, pat); j++; } end_pats = j; pattern_stack().shrink(end_pats); unsigned num_pats = end_pats - begin_pats; unsigned num_nopats = nopattern_stack().size() - fr->m_nopat_spos; TRACE("parse_quantifier", tout << "weight: " << fr->m_weight << "\n";); TRACE("skid", tout << "fr->m_skid: " << fr->m_skid << "\n";); TRACE("parse_quantifier", tout << "body:\n" << mk_pp(expr_stack().back(), m()) << "\n";); if (fr->m_qid == symbol::null) fr->m_qid = symbol(m_scanner.get_line()); if (!m().is_bool(expr_stack().back())) throw parser_exception("quantifier body must be a Boolean expression"); quantifier * new_q = m().mk_quantifier(fr->m_forall, num_decls, sort_stack().c_ptr() + fr->m_sort_spos, symbol_stack().c_ptr() + fr->m_sym_spos, expr_stack().back(), fr->m_weight, fr->m_qid, fr->m_skid, num_pats, pattern_stack().c_ptr() + fr->m_pat_spos, num_nopats, nopattern_stack().c_ptr() + fr->m_nopat_spos ); TRACE("mk_quantifier", tout << "id: " << new_q->get_id() << "\n" << mk_ismt2_pp(new_q, m()) << "\n";); TRACE("skid", tout << "new_q->skid: " << new_q->get_skid() << "\n";); expr_stack().shrink(fr->m_expr_spos); pattern_stack().shrink(fr->m_pat_spos); nopattern_stack().shrink(fr->m_nopat_spos); symbol_stack().shrink(fr->m_sym_spos); sort_stack().shrink(fr->m_sort_spos); m_env.end_scope(); SASSERT(num_decls <= m_num_bindings); m_num_bindings -= num_decls; expr_stack().push_back(new_q); m_stack.deallocate(fr); m_num_expr_frames--; } void pop_attr_expr_frame(attr_expr_frame * fr) { process_last_symbol(fr); TRACE("consume_attributes", tout << "pop_attr_expr_frame, expr_stack.size(): " << expr_stack().size() << "\n";); // the resultant expression is already on the top of the stack. SASSERT(expr_stack().size() == fr->m_expr_spos + 1); m_stack.deallocate(fr); m_num_expr_frames--; } void pop_pattern_frame(pattern_frame * fr) { SASSERT(expr_stack().size() >= fr->m_expr_spos); if (expr_stack().size() == fr->m_expr_spos) { if (!ignore_bad_patterns()) throw parser_exception("invalid empty pattern"); // ingoring empty pattern expr_stack().shrink(fr->m_expr_spos); } else { unsigned num = expr_stack().size() - fr->m_expr_spos; expr * new_pat = m().mk_pattern(num, reinterpret_cast(expr_stack().c_ptr() + fr->m_expr_spos)); expr_stack().shrink(fr->m_expr_spos); expr_stack().push_back(new_pat); } m_stack.deallocate(fr); m_num_expr_frames--; } void pop_expr_frame() { SASSERT(curr_is_rparen()); expr_frame * fr = static_cast(m_stack.top()); switch (fr->m_kind) { case EF_APP: pop_app_frame(static_cast(fr)); break; case EF_LET: pop_let_frame(static_cast(fr)); break; case EF_LET_DECL: m_stack.deallocate(static_cast(fr)); m_num_expr_frames--; break; case EF_QUANT: pop_quant_frame(static_cast(fr)); break; case EF_ATTR_EXPR: pop_attr_expr_frame(static_cast(fr)); break; case EF_PATTERN: pop_pattern_frame(static_cast(fr)); break; default: UNREACHABLE(); } SASSERT(curr_is_rparen()); next(); // consume ')' } void parse_expr() { m_num_expr_frames = 0; do { TRACE("parse_expr", tout << "curr(): " << curr() << ", m_num_expr_frames: " << m_num_expr_frames << ", expr_stack().size(): " << expr_stack().size() << "\n";); if (curr_is_rparen()) { if (m_num_expr_frames == 0) throw parser_exception("invalid expression, unexpected ')'"); pop_expr_frame(); } else { pe_state st = parse_expr_state(); TRACE("consume_attributes", tout << "parse_expr_state: " << st << ", expr_stack.size(): " << expr_stack().size() << "\n";); switch (st) { case PES_EXPR: switch (curr()) { case scanner::SYMBOL_TOKEN: parse_expr_name(); break; case scanner::INT_TOKEN: parse_numeral(true); break; case scanner::FLOAT_TOKEN: parse_numeral(false); break; case scanner::BV_TOKEN: parse_bv_numeral(); break; case scanner::LEFT_PAREN: push_expr_frame(m_num_expr_frames == 0 ? 0 : static_cast(m_stack.top())); break; case scanner::KEYWORD_TOKEN: throw parser_exception("invalid expression, unexpected keyword"); default: throw parser_exception("invalid expression, unexpected input"); } break; case PES_DECL: push_let_decl_frame(); break; case PES_PATTERN: push_pattern_frame(); break; case PES_CONTINUE: // do nothing break; default: UNREACHABLE(); break; } } } while (m_num_expr_frames > 0 ); SASSERT(!expr_stack().empty()); } unsigned parse_exprs() { unsigned sz = 0; check_lparen_next("invalid list of terms, '(' expected"); while (!curr_is_rparen()) { parse_expr(); sz++; } next(); return sz; } void parse_sort_decl_params() { check_lparen_next("invalid sort declaration, parameters missing"); m_sort_id2param_idx.reset(); unsigned i = 0; while (!curr_is_rparen()) { check_identifier("invalid sort parameter, symbol or ')' expected"); m_sort_id2param_idx.insert(curr_id(), i); i++; next(); } next(); } void parse_declare_sort() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_declare_sort); next(); check_identifier("invalid sort declaration, symbol expected"); symbol id = curr_id(); if (m_ctx.find_psort_decl(id) != 0) throw parser_exception("invalid sort declaration, sort already declared/defined"); next(); if (curr_is_rparen()) { psort_decl * decl = pm().mk_psort_user_decl(0, id, 0); m_ctx.insert(decl); } else { check_int("invalid sort declaration, arity () or ')' expected"); rational n = curr_numeral(); if (!n.is_unsigned()) throw parser_exception("invalid sort declaration, arity is too big to fit in an unsigned machine integer"); psort_decl * decl = pm().mk_psort_user_decl(n.get_unsigned(), id, 0); m_ctx.insert(decl); next(); check_rparen("invalid sort declaration, ')' expected"); } m_ctx.print_success(); next(); } void parse_define_sort() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_define_sort); next(); check_identifier("invalid sort definition, symbol expected"); symbol id = curr_id(); if (m_ctx.find_psort_decl(id) != 0) throw parser_exception("invalid sort definition, sort already declared/defined"); next(); parse_sort_decl_params(); parse_psort(); psort_decl * decl = pm().mk_psort_user_decl(m_sort_id2param_idx.size(), id, psort_stack().back()); psort_stack().pop_back(); m_ctx.insert(decl); check_rparen("invalid sort definition, ')' expected"); m_ctx.print_success(); next(); } void parse_define_fun() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_define_fun); SASSERT(m_num_bindings == 0); next(); check_identifier("invalid function/constant definition, symbol expected"); symbol id = curr_id(); next(); unsigned sym_spos = symbol_stack().size(); unsigned sort_spos = sort_stack().size(); unsigned expr_spos = expr_stack().size(); unsigned num_vars = parse_sorted_vars(); parse_sort(); parse_expr(); if (m().get_sort(expr_stack().back()) != sort_stack().back()) throw parser_exception("invalid function/constant definition, sort mismatch"); m_ctx.insert(id, num_vars, expr_stack().back()); check_rparen("invalid function/constant definition, ')' expected"); // restore stacks & env symbol_stack().shrink(sym_spos); sort_stack().shrink(sort_spos); expr_stack().shrink(expr_spos); m_env.end_scope(); SASSERT(num_vars == m_num_bindings); m_num_bindings = 0; m_ctx.print_success(); next(); } void parse_define_const() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_define_const); SASSERT(m_num_bindings == 0); next(); check_identifier("invalid constant definition, symbol expected"); symbol id = curr_id(); next(); parse_sort(); parse_expr(); if (m().get_sort(expr_stack().back()) != sort_stack().back()) throw parser_exception("invalid constant definition, sort mismatch"); m_ctx.insert(id, 0, expr_stack().back()); check_rparen("invalid constant definition, ')' expected"); expr_stack().pop_back(); sort_stack().pop_back(); m_ctx.print_success(); next(); } void parse_declare_fun() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_declare_fun); next(); check_identifier("invalid function declaration, symbol expected"); symbol id = curr_id(); next(); unsigned spos = sort_stack().size(); unsigned num_params = parse_sorts(); parse_sort(); func_decl_ref f(m()); f = m().mk_func_decl(id, num_params, sort_stack().c_ptr() + spos, sort_stack().back()); sort_stack().shrink(spos); m_ctx.insert(f); check_rparen("invalid function declaration, ')' expected"); m_ctx.print_success(); next(); } void parse_declare_const() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_declare_const); next(); check_identifier("invalid constant declaration, symbol expected"); symbol id = curr_id(); next(); parse_sort(); SASSERT(!sort_stack().empty()); func_decl_ref c(m()); c = m().mk_const_decl(id, sort_stack().back()); TRACE("declare_const", tout << "declaring " << id << " "; pm().display(tout, sort_stack().back()); tout << "\n";); SASSERT(c.get() != 0); sort_stack().pop_back(); m_ctx.insert(c); check_rparen("invalid constant declaration, ')' expected"); m_ctx.print_success(); next(); } unsigned parse_opt_unsigned(unsigned def) { unsigned num; if (!curr_is_rparen()) { check_int("invalid push command, integer expected"); rational n = curr_numeral(); if (n.is_neg()) throw parser_exception("invalid push command, value is negative."); if (!n.is_unsigned()) throw parser_exception("invalid push command, value is too big to fit in an unsigned machine integer"); num = n.get_unsigned(); next(); } else { num = def; } return num; } void parse_push() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_push); next(); unsigned num = parse_opt_unsigned(1); m_ctx.push(num); check_rparen("invalid push command, ')' expected"); m_ctx.print_success(); next(); } void parse_pop() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_pop); next(); unsigned num = parse_opt_unsigned(1); m_ctx.pop(num); check_rparen("invalid pop command, ')' expected"); m_ctx.print_success(); next(); TRACE("after_pop", tout << "expr_stack.size: " << expr_stack().size() << "\n"; m_ctx.dump_assertions(tout);); } std::string m_assert_expr; void parse_assert() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_assert); m_last_named_expr.first = symbol::null; m_last_named_expr.second = 0; if (m_ctx.interactive_mode()) { m_scanner.start_caching(); m_cache_end = 0; } next(); parse_expr(); if (m_ctx.interactive_mode()) { m_assert_expr = m_scanner.cached_str(0, m_cache_end); m_scanner.stop_caching(); } expr * f = expr_stack().back(); if (!m().is_bool(f)) throw cmd_exception("invalid assert command, term is not Boolean"); if (f == m_last_named_expr.second) { m_ctx.assert_expr(m_last_named_expr.first, f); } else { m_ctx.assert_expr(f); } if (m_ctx.interactive_mode()) { m_ctx.push_assert_string(m_assert_expr); } expr_stack().pop_back(); check_rparen("invalid assert command, ')' expected"); m_ctx.print_success(); next(); } void parse_check_sat() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_check_sat); next(); unsigned spos = expr_stack().size(); while (!curr_is_rparen()) { bool sign; expr_ref t_ref(m()); if (curr_is_lparen()) { next(); check_id_next(m_not, "invalid check-sat command, 'not' expected, assumptions must be Boolean literals"); check_identifier("invalid check-sat command, literal expected"); sign = true; } else { check_identifier("invalid check-sat command, literal or ')' expected"); sign = false; } symbol n = curr_id(); next(); m_ctx.mk_const(n, t_ref); if (!m().is_bool(t_ref)) throw parser_exception("invalid check-sat command, argument must be a Boolean literal"); if (sign) { if (!is_uninterp_const(t_ref)) throw parser_exception("invalid check-sat command, argument must be a Boolean literal"); t_ref = m().mk_not(t_ref.get()); } else { expr * arg; if (!is_uninterp_const(t_ref) && !(m().is_not(t_ref, arg) && is_uninterp_const(arg))) throw parser_exception("invalid check-sat command, argument must be a Boolean literal"); } expr_stack().push_back(t_ref.get()); if (sign) check_rparen_next("invalid check-sat command, ')' expected"); } m_ctx.check_sat(expr_stack().size() - spos, expr_stack().c_ptr() + spos); next(); expr_stack().shrink(spos); } void parse_get_value() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_get_value); next(); unsigned spos = expr_stack().size(); unsigned cache_it = 0; m_scanner.start_caching(); m_cache_end = 0; m_cached_strings.reset(); check_lparen_next("invalid get-value command, '(' expected"); while (!curr_is_rparen()) { parse_expr(); if (!is_ground(expr_stack().back())) throw cmd_exception("invalid get-value term, term must be ground and must not contain quantifiers"); m_cached_strings.push_back(m_scanner.cached_str(cache_it, m_cache_end)); cache_it = m_cache_end; } m_scanner.stop_caching(); if (m_cached_strings.empty()) throw cmd_exception("invalid get-value command, empty list of terms"); next(); check_rparen("invalid get-value command, ')' expected"); if (!m_ctx.is_model_available() || m_ctx.get_check_sat_result() == 0) throw cmd_exception("model is not available"); model_ref md; m_ctx.get_check_sat_result()->get_model(md); m_ctx.regular_stream() << "("; expr ** expr_it = expr_stack().c_ptr() + spos; expr ** expr_end = expr_it + m_cached_strings.size(); for (unsigned i = 0; expr_it < expr_end; expr_it++, i++) { expr_ref v(m()); md->eval(*expr_it, v, true); if (i > 0) m_ctx.regular_stream() << "\n "; m_ctx.regular_stream() << "(" << m_cached_strings[i] << " "; m_ctx.display(m_ctx.regular_stream(), v); m_ctx.regular_stream() << ")"; } m_ctx.regular_stream() << ")" << std::endl; expr_stack().shrink(spos); next(); } void parse_reset() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_reset); next(); check_rparen("invalid reset command, ')' expected"); m_ctx.reset(); reset(); m_ctx.print_success(); next(); } void parse_option_value() { switch (curr()) { case scanner::BV_TOKEN: case scanner::INT_TOKEN: case scanner::FLOAT_TOKEN: m_curr_cmd->set_next_arg(m_ctx, m_scanner.get_number()); next(); break; case scanner::SYMBOL_TOKEN: m_curr_cmd->set_next_arg(m_ctx, m_scanner.get_id()); next(); break; case scanner::STRING_TOKEN: m_curr_cmd->set_next_arg(m_ctx, m_scanner.get_string()); next(); break; default: throw parser_exception("invalid option value"); } } // A func_decl reference is of the form: // // | ( (+) sort) // | ((_ +) (+) sort) func_decl * parse_func_decl_ref() { if (curr_is_identifier()) { symbol id = curr_id(); func_decl * d = m_ctx.find_func_decl(id); next(); return d; } else { check_lparen_next("invalid function declaration reference, symbol or '(' expected"); symbol id; sbuffer indices; if (curr_is_identifier()) { id = curr_id(); next(); } else { check_lparen_next("invalid function declaration reference, symbol or '(' expected"); check_underscore_next("invalid indexed function declaration reference, '_' expected"); check_identifier("invalid indexed function declaration reference, symbol expected"); id = curr_id(); next(); while (!curr_is_rparen()) { check_int("invalid indexed function declaration reference, integer or ')' expected"); rational n = curr_numeral(); if (!n.is_unsigned()) throw parser_exception("invalid indexed function declaration reference, index is too big to fit in an unsigned machine integer"); indices.push_back(n.get_unsigned()); next(); } if (indices.empty()) throw parser_exception("invalid indexed function declaration reference, index expected"); next(); } unsigned spos = sort_stack().size(); parse_sorts(); unsigned domain_size = sort_stack().size() - spos; parse_sort(); func_decl * d = m_ctx.find_func_decl(id, indices.size(), indices.c_ptr(), domain_size, sort_stack().c_ptr() + spos, sort_stack().back()); sort_stack().shrink(spos); check_rparen_next("invalid function declaration reference, ')' expected"); return d; } } void parse_func_decl_refs(ptr_buffer & flist) { check_lparen_next("invalid list of function declaration references, '(' expected"); while (!curr_is_rparen()) { flist.push_back(parse_func_decl_ref()); } next(); } void parse_next_cmd_arg() { SASSERT(m_curr_cmd != 0); cmd_arg_kind k = m_curr_cmd->next_arg_kind(m_ctx); switch (k) { case CPK_UINT: { check_int("invalid command argument, unsigned integer expected"); rational n = curr_numeral(); if (!n.is_unsigned()) throw parser_exception("invalid command argument, numeral is too big to fit in an unsigned machine integer"); m_curr_cmd->set_next_arg(m_ctx, n.get_unsigned()); next(); break; } case CPK_BOOL: { check_identifier("invalid command argument, true/false expected"); symbol val = curr_id(); if (val != "true" && val != "false") throw parser_exception("invalid command argument, true/false expected"); m_curr_cmd->set_next_arg(m_ctx, val == "true"); next(); break; } case CPK_NUMERAL: check_int("invalid command argument, numeral expected"); m_curr_cmd->set_next_arg(m_ctx, curr_numeral()); next(); break; case CPK_DECIMAL: check_float("invalid command argument, decimal expected"); m_curr_cmd->set_next_arg(m_ctx, curr_numeral()); next(); break; case CPK_STRING: check_string("invalid command argument, string expected"); m_curr_cmd->set_next_arg(m_ctx, m_scanner.get_string()); next(); break; case CPK_KEYWORD: check_keyword("invalid command argument, keyword expected"); m_curr_cmd->set_next_arg(m_ctx, curr_id()); next(); break; case CPK_OPTION_VALUE: parse_option_value(); break; case CPK_SYMBOL: check_identifier("invalid command argument, symbol expected"); m_curr_cmd->set_next_arg(m_ctx, curr_id()); next(); return; case CPK_SYMBOL_LIST: { unsigned spos = m_symbol_stack.size(); unsigned num = parse_symbols(); m_curr_cmd->set_next_arg(m_ctx, num, m_symbol_stack.c_ptr() + spos); break; } case CPK_SORT: parse_sort(); m_curr_cmd->set_next_arg(m_ctx, sort_stack().back()); return; case CPK_SORT_LIST: { unsigned spos = sort_stack().size(); unsigned num = parse_sorts(); m_curr_cmd->set_next_arg(m_ctx, num, sort_stack().c_ptr() + spos); break; } case CPK_EXPR: parse_expr(); m_curr_cmd->set_next_arg(m_ctx, expr_stack().back()); return; case CPK_EXPR_LIST: { unsigned spos = expr_stack().size(); unsigned num = parse_exprs(); m_curr_cmd->set_next_arg(m_ctx, num, expr_stack().c_ptr() + spos); break; } case CPK_FUNC_DECL: { func_decl * f = parse_func_decl_ref(); m_curr_cmd->set_next_arg(m_ctx, f); return; } case CPK_FUNC_DECL_LIST: { ptr_buffer flist; parse_func_decl_refs(flist); m_curr_cmd->set_next_arg(m_ctx, flist.size(), flist.c_ptr()); return; } case CPK_SORTED_VAR: NOT_IMPLEMENTED_YET(); break; case CPK_SORTED_VAR_LIST: NOT_IMPLEMENTED_YET(); break; case CPK_SEXPR: parse_sexpr(); m_curr_cmd->set_next_arg(m_ctx, sexpr_stack().back()); break; case CPK_INVALID: throw parser_exception("invalid/unexpected argument"); default: throw parser_exception("unexpected argument"); } } void parse_unknown_cmd() { SASSERT(curr_is_identifier()); symbol s = curr_id(); next(); while (!curr_is_rparen()) { consume_sexpr(); } m_ctx.print_unsupported(s); next(); return; } void parse_ext_cmd() { symbol s = curr_id(); m_curr_cmd = m_ctx.find_cmd(s); if (m_curr_cmd == 0) { parse_unknown_cmd(); return; } next(); unsigned arity = m_curr_cmd->get_arity(); unsigned i = 0; unsigned sort_spos = size(m_sort_stack); unsigned expr_spos = size(m_expr_stack); unsigned sexpr_spos = size(m_sexpr_stack); unsigned sym_spos = m_symbol_stack.size(); m_curr_cmd->prepare(m_ctx); while (true) { if (curr_is_rparen()) { if (arity != VAR_ARITY && i < arity) throw parser_exception("invalid command, argument(s) missing"); m_curr_cmd->execute(m_ctx); next(); m_curr_cmd = 0; shrink(m_sort_stack, sort_spos); shrink(m_expr_stack, expr_spos); shrink(m_sexpr_stack, sexpr_spos); m_symbol_stack.shrink(sym_spos); m_num_bindings = 0; // HACK for propagating the update of parser parameters if (norm_param_name(s) == "set_option") { updt_params(); } return; } else { if (arity != VAR_ARITY && i == arity) throw parser_exception("invalid command, too many arguments"); parse_next_cmd_arg(); } i++; } } void parse_cmd() { SASSERT(curr_is_lparen()); next(); check_identifier("invalid command, symbol expected"); symbol s = curr_id(); if (s == m_assert) { parse_assert(); return; } if (s == m_declare_fun) { parse_declare_fun(); return; } if (s == m_declare_const) { parse_declare_const(); return; } if (s == m_check_sat) { parse_check_sat(); return; } if (s == m_push) { parse_push(); return; } if (s == m_pop) { parse_pop(); return; } if (s == m_define_fun) { parse_define_fun(); return; } if (s == m_define_const) { parse_define_const(); return; } if (s == m_define_sort) { parse_define_sort(); return; } if (s == m_declare_sort) { parse_declare_sort(); return; } if (s == m_declare_datatypes) { parse_declare_datatypes(); return; } if (s == m_get_value) { parse_get_value(); return; } if (s == m_reset) { parse_reset(); return; } parse_ext_cmd(); } public: parser(cmd_context & ctx, std::istream & is, bool interactive, params_ref const & p): m_ctx(ctx), m_params(p), m_scanner(ctx, is, interactive), m_curr(scanner::NULL_TOKEN), m_curr_cmd(0), m_num_bindings(0), m_let("let"), m_bang("!"), m_forall("forall"), m_exists("exists"), m_as("as"), m_not("not"), m_root_obj("root-obj"), m_named(":named"), m_weight(":weight"), m_qid(":qid"), m_skid(":skolemid"), m_ex_act(":ex-act"), m_pattern(":pattern"), m_nopattern(":no-pattern"), m_lblneg(":lblneg"), m_lblpos(":lblpos"), m_assert("assert"), m_check_sat("check-sat"), m_define_fun("define-fun"), m_define_const("define-const"), m_declare_fun("declare-fun"), m_declare_const("declare-const"), m_define_sort("define-sort"), m_declare_sort("declare-sort"), m_declare_datatypes("declare-datatypes"), m_push("push"), m_pop("pop"), m_get_value("get-value"), m_reset("reset"), m_underscore("_"), m_num_open_paren(0) { // the following assertion does not hold if ctx was already attached to an AST manager before the parser object is created. // SASSERT(!m_ctx.has_manager()); updt_params(); } ~parser() { reset_stack(); } void updt_params() { parser_params p(m_params); m_ignore_user_patterns = p.ignore_user_patterns(); m_ignore_bad_patterns = p.ignore_bad_patterns(); m_display_error_for_vs = p.error_for_visual_studio(); } void reset() { reset_stack(); m_num_bindings = 0; m_psort_stack = 0; m_sort_stack = 0; m_expr_stack = 0; m_pattern_stack = 0; m_nopattern_stack = 0; m_sexpr_stack = 0; m_symbol_stack .reset(); m_param_stack .reset(); m_env .reset(); m_sort_id2param_idx .reset(); m_dt_name2idx .reset(); m_bv_util = 0; m_arith_util = 0; m_pattern_validator = 0; m_var_shifter = 0; } bool operator()() { m_num_bindings = 0; bool found_errors = false; try { scan_core(); } catch (scanner_exception & ex) { error(ex.msg()); if (!sync_after_error()) return false; found_errors = true; } while (true) { try { m_num_open_paren = 0; while (true) { switch (curr()) { case scanner::LEFT_PAREN: parse_cmd(); break; case scanner::EOF_TOKEN: return !found_errors; default: throw parser_exception("invalid command, '(' expected"); break; } } } catch (z3_error & ex) { // Can't invoke error(...) when out of memory. // Reason: escaped() string builder needs memory m_ctx.regular_stream() << "(error \"line " << m_scanner.get_line() << " column " << m_scanner.get_pos() << ": " << ex.msg() << "\")" << std::endl; exit(ex.error_code()); } catch (stop_parser_exception) { m_scanner.stop_caching(); return !found_errors; } catch (parser_exception & ex) { if (ex.has_pos()) error(ex.line(), ex.pos(), ex.msg()); else error(ex.msg()); } catch (ast_exception & ex) { error(ex.msg()); } catch (z3_exception & ex) { error(ex.msg()); } m_scanner.stop_caching(); if (m_curr_cmd) m_curr_cmd->failure_cleanup(m_ctx); reset(); found_errors = true; if (!sync_after_error()) return false; TRACE("parser_error", tout << "after sync: " << curr() << "\n";); SASSERT(m_num_open_paren == 0); } } }; }; bool parse_smt2_commands(cmd_context & ctx, std::istream & is, bool interactive, params_ref const & ps) { smt2::parser p(ctx, is, interactive, ps); return p(); } z3-z3-4.4.1/src/parsers/smt2/smt2parser.h000066400000000000000000000006131260446376700200330ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt2parser.h Abstract: SMT 2.0 parser Author: Leonardo de Moura (leonardo) 2011-03-01 Revision History: --*/ #ifndef SMT2_PARSER_H_ #define SMT2_PARSER_H_ #include"cmd_context.h" bool parse_smt2_commands(cmd_context & ctx, std::istream & is, bool interactive = false, params_ref const & p = params_ref()); #endif z3-z3-4.4.1/src/parsers/smt2/smt2scanner.cpp000066400000000000000000000247601260446376700205340ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt2scanner.cpp Abstract: Author: Leonardo de Moura (leonardo) 2011-03-09. Revision History: --*/ #include"smt2scanner.h" #include"parser_params.hpp" namespace smt2 { void scanner::next() { if (m_cache_input) m_cache.push_back(m_curr); SASSERT(m_curr != EOF); if (m_interactive) { m_curr = m_stream.get(); } else if (m_bpos < m_bend) { m_curr = m_buffer[m_bpos]; m_bpos++; } else { m_stream.read(m_buffer, SCANNER_BUFFER_SIZE); m_bend = static_cast(m_stream.gcount()); m_bpos = 0; if (m_bpos == m_bend) { m_curr = EOF; } else { m_curr = m_buffer[m_bpos]; m_bpos++; } } m_spos++; } void scanner::read_comment() { SASSERT(curr() == ';'); next(); while (true) { char c = curr(); if (c == EOF) return; if (c == '\n') { new_line(); next(); return; } next(); } } scanner::token scanner::read_quoted_symbol() { SASSERT(curr() == '|'); bool escape = false; m_string.reset(); next(); while (true) { char c = curr(); if (c == EOF) { throw scanner_exception("unexpected end of quoted symbol", m_line, m_spos); } else if (c == '\n') { new_line(); } else if (c == '|' && !escape) { next(); m_string.push_back(0); m_id = m_string.begin(); TRACE("scanner", tout << "new quoted symbol: " << m_id << "\n";); return SYMBOL_TOKEN; } escape = (c == '\\'); m_string.push_back(c); next(); } } scanner::token scanner::read_symbol_core() { while (true) { char c = curr(); signed char n = m_normalized[static_cast(c)]; if (n == 'a' || n == '0' || n == '-') { m_string.push_back(c); next(); } else { m_string.push_back(0); m_id = m_string.begin(); TRACE("scanner", tout << "new symbol: " << m_id << "\n";); return SYMBOL_TOKEN; } } } scanner::token scanner::read_symbol() { SASSERT(m_normalized[static_cast(curr())] == 'a' || curr() == ':' || curr() == '-'); m_string.reset(); m_string.push_back(curr()); next(); return read_symbol_core(); } scanner::token scanner::read_number() { SASSERT('0' <= curr() && curr() <= '9'); rational q(1); m_number = rational(curr() - '0'); next(); bool is_float = false; while (true) { char c = curr(); if ('0' <= c && c <= '9') { m_number = rational(10)*m_number + rational(c - '0'); if (is_float) q *= rational(10); next(); } else if (c == '.') { if (is_float) break; is_float = true; next(); } else { break; } } if (is_float) m_number /= q; TRACE("scanner", tout << "new number: " << m_number << "\n";); return is_float ? FLOAT_TOKEN : INT_TOKEN; } scanner::token scanner::read_signed_number() { SASSERT(curr() == '-'); next(); if ('0' <= curr() && curr() <= '9') { scanner::token r = read_number(); m_number.neg(); return r; } else { // it is a symbol. m_string.reset(); m_string.push_back('-'); return read_symbol_core(); } } scanner::token scanner::read_string() { SASSERT(curr() == '\"'); next(); m_string.reset(); while (true) { char c = curr(); if (c == EOF) throw scanner_exception("unexpected end of string", m_line, m_spos); if (c == '\"') { next(); m_string.push_back(0); return STRING_TOKEN; } if (c == '\n') new_line(); else if (c == '\\') { next(); c = curr(); if (c == EOF) throw scanner_exception("unexpected end of string", m_line, m_spos); if (c != '\\' && c != '\"') throw scanner_exception("invalid escape sequence", m_line, m_spos); } m_string.push_back(c); next(); } } scanner::token scanner::read_bv_literal() { SASSERT(curr() == '#'); next(); char c = curr(); if (c == 'x') { next(); c = curr(); m_number = rational(0); m_bv_size = 0; while (true) { if ('0' <= c && c <= '9') { m_number *= rational(16); m_number += rational(c - '0'); } else if ('a' <= c && c <= 'f') { m_number *= rational(16); m_number += rational(10 + (c - 'a')); } else if ('A' <= c && c <= 'F') { m_number *= rational(16); m_number += rational(10 + (c - 'A')); } else { if (m_bv_size == 0) throw scanner_exception("invalid empty bit-vector literal", m_line, m_spos); return BV_TOKEN; } m_bv_size += 4; next(); c = curr(); } } else if (c == 'b') { next(); c = curr(); m_number = rational(0); m_bv_size = 0; while (c == '0' || c == '1') { m_number *= rational(2); m_number += rational(c - '0'); m_bv_size++; next(); c = curr(); } if (m_bv_size == 0) throw scanner_exception("invalid empty bit-vector literal", m_line, m_spos); return BV_TOKEN; } else { throw scanner_exception("invalid bit-vector literal, expecting 'x' or 'b'", m_line, m_spos); } } scanner::scanner(cmd_context & ctx, std::istream& stream, bool interactive): m_interactive(interactive), m_spos(0), m_curr(0), // avoid Valgrind warning m_line(1), m_pos(0), m_bv_size(UINT_MAX), m_bpos(0), m_bend(0), m_stream(stream), m_cache_input(false) { m_smtlib2_compliant = ctx.params().m_smtlib2_compliant; for (int i = 0; i < 256; ++i) { m_normalized[i] = (signed char) i; } m_normalized[static_cast('\t')] = ' '; m_normalized[static_cast('\r')] = ' '; // assert ('a' < 'z'); for (char ch = 'b'; ch <= 'z'; ++ch) { m_normalized[static_cast(ch)] = 'a'; } for (char ch = 'A'; ch <= 'Z'; ++ch) { m_normalized[static_cast(ch)] = 'a'; } // assert ('0' < '9', '9' - '0' == 9); for (char ch = '1'; ch <= '9'; ++ch) { m_normalized[static_cast(ch)] = '0'; } // SMT2 "Symbols": ~ ! @ $ % ^ & * _ - + = < > . ? / m_normalized[static_cast('~')] = 'a'; m_normalized[static_cast('!')] = 'a'; m_normalized[static_cast('@')] = 'a'; m_normalized[static_cast('$')] = 'a'; m_normalized[static_cast('%')] = 'a'; m_normalized[static_cast('^')] = 'a'; m_normalized[static_cast('&')] = 'a'; m_normalized[static_cast('*')] = 'a'; m_normalized[static_cast('_')] = 'a'; m_normalized[static_cast('-')] = '-'; m_normalized[static_cast('+')] = 'a'; m_normalized[static_cast('=')] = 'a'; m_normalized[static_cast('<')] = 'a'; m_normalized[static_cast('>')] = 'a'; m_normalized[static_cast('.')] = 'a'; m_normalized[static_cast('?')] = 'a'; m_normalized[static_cast('/')] = 'a'; next(); } scanner::token scanner::scan() { while (true) { signed char c = curr(); m_pos = m_spos; switch (m_normalized[(unsigned char) c]) { case ' ': next(); break; case '\n': next(); new_line(); break; case ';': read_comment(); break; case ':': read_symbol(); return KEYWORD_TOKEN; case '(': next(); return LEFT_PAREN; case ')': next(); return RIGHT_PAREN; case '|': return read_quoted_symbol(); case 'a': return read_symbol(); case '"': return read_string(); case '0': return read_number(); case '#': return read_bv_literal(); case '-': if (m_smtlib2_compliant) return read_symbol(); else return read_signed_number(); case -1: return EOF_TOKEN; default: { scanner_exception ex("unexpected character", m_line, m_spos); next(); throw ex; }} } } char const * scanner::cached_str(unsigned begin, unsigned end) { m_cache_result.reset(); while (isspace(m_cache[begin]) && begin < end) begin++; while (begin < end && isspace(m_cache[end-1])) end--; for (unsigned i = begin; i < end; i++) m_cache_result.push_back(m_cache[i]); m_cache_result.push_back(0); return m_cache_result.begin(); } }; z3-z3-4.4.1/src/parsers/smt2/smt2scanner.h000066400000000000000000000055031260446376700201730ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt2scanner.h Abstract: Author: Leonardo de Moura (leonardo) 2011-03-09. Revision History: --*/ #ifndef SMT2SCANNER_H_ #define SMT2SCANNER_H_ #include #include"symbol.h" #include"vector.h" #include"rational.h" #include"cmd_context.h" namespace smt2 { typedef cmd_exception scanner_exception; class scanner { private: bool m_interactive; int m_spos; // position in the current line of the stream char m_curr; // current char; int m_line; // line int m_pos; // start position of the token // data symbol m_id; rational m_number; unsigned m_bv_size; // end of data signed char m_normalized[256]; #define SCANNER_BUFFER_SIZE 1024 char m_buffer[SCANNER_BUFFER_SIZE]; unsigned m_bpos; unsigned m_bend; svector m_string; std::istream& m_stream; bool m_cache_input; svector m_cache; svector m_cache_result; bool m_smtlib2_compliant; char curr() const { return m_curr; } void new_line() { m_line++; m_spos = 0; } void next(); public: enum token { NULL_TOKEN = 0, LEFT_PAREN = 1, RIGHT_PAREN, KEYWORD_TOKEN, SYMBOL_TOKEN, STRING_TOKEN, INT_TOKEN, BV_TOKEN, FLOAT_TOKEN, EOF_TOKEN }; scanner(cmd_context & ctx, std::istream& stream, bool interactive = false); ~scanner() {} int get_line() const { return m_line; } int get_pos() const { return m_pos; } symbol const & get_id() const { return m_id; } rational get_number() const { return m_number; } unsigned get_bv_size() const { return m_bv_size; } char const * get_string() const { return m_string.begin(); } token scan(); token read_symbol_core(); token read_symbol(); token read_quoted_symbol(); void read_comment(); token read_number(); token read_signed_number(); token read_string(); token read_bv_literal(); void start_caching() { m_cache_input = true; m_cache.reset(); } void stop_caching() { m_cache_input = false; } unsigned cache_size() const { return m_cache.size(); } void reset_cache() { m_cache.reset(); } char const * cached_str(unsigned begin, unsigned end); }; }; #endif /* SCANNER_H_ */ z3-z3-4.4.1/src/parsers/util/000077500000000000000000000000001260446376700156505ustar00rootroot00000000000000z3-z3-4.4.1/src/parsers/util/cost_parser.cpp000066400000000000000000000032141260446376700207000ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cost_parser.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-14. Revision History: --*/ #include"cost_parser.h" cost_parser::cost_parser(ast_manager & m): simple_parser(m), m_util(m), m_vars(m) { family_id fid; fid = m.get_basic_family_id(); add_builtin_op("true", fid, OP_TRUE); add_builtin_op("false", fid, OP_FALSE); add_builtin_op("not", fid, OP_NOT); add_builtin_op("and", fid, OP_AND); add_builtin_op("implies", fid, OP_IMPLIES); add_builtin_op("or", fid, OP_OR); add_builtin_op("ite", fid, OP_ITE); add_builtin_op("=", fid, OP_EQ); add_builtin_op("iff", fid, OP_IFF); add_builtin_op("xor", fid, OP_XOR); fid = m_util.get_family_id(); add_builtin_op("+", fid, OP_ADD); add_builtin_op("*", fid, OP_MUL); add_builtin_op("-", fid, OP_SUB); add_builtin_op("/", fid, OP_DIV); add_builtin_op("<=", fid, OP_LE); add_builtin_op(">=", fid, OP_GE); add_builtin_op("<", fid, OP_LT); add_builtin_op(">", fid, OP_GT); } expr * cost_parser::parse_int(rational const & r) { return m_util.mk_numeral(r, false); } expr * cost_parser::parse_float(rational const & r) { return m_util.mk_numeral(r, false); } unsigned cost_parser::add_var(symbol name) { sort * real = m_util.mk_real(); unsigned r = m_vars.size(); var * v = m_manager.mk_var(r, real); simple_parser::add_var(name, v); m_vars.push_back(v); return r; } void cost_parser::reset_vars() { simple_parser::reset_vars(); m_vars.reset(); } z3-z3-4.4.1/src/parsers/util/cost_parser.h000066400000000000000000000013261260446376700203470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cost_parser.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-14. Revision History: --*/ #ifndef COST_PARSER_H_ #define COST_PARSER_H_ #include"simple_parser.h" #include"arith_decl_plugin.h" class cost_parser : public simple_parser { arith_util m_util; var_ref_vector m_vars; public: cost_parser(ast_manager & m); virtual ~cost_parser() {} virtual expr * parse_int(rational const & r); virtual expr * parse_float(rational const & r); unsigned add_var(symbol name); unsigned add_var(char const * name) { return add_var(symbol(name)); } void reset_vars(); }; #endif /* COST_PARSER_H_ */ z3-z3-4.4.1/src/parsers/util/parser_params.pyg000066400000000000000000000006251260446376700212330ustar00rootroot00000000000000def_module_params('parser', export=True, params=(('ignore_user_patterns', BOOL, False, 'ignore patterns provided by the user'), ('ignore_bad_patterns', BOOL, True, 'ignore malformed patterns'), ('error_for_visual_studio', BOOL, False, 'display error messages in Visual Studio format'), )) z3-z3-4.4.1/src/parsers/util/pattern_validation.cpp000066400000000000000000000056401260446376700222500ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pattern_validation.cpp Abstract: Code for checking whether a pattern is valid or not. Author: Leonardo de Moura (leonardo) 2006-12-08. Revision History: --*/ #include"pattern_validation.h" #include"for_each_expr.h" #include"warning.h" #include"ast_pp.h" struct pattern_validation_functor { uint_set & m_found_vars; unsigned m_num_bindings; unsigned m_num_new_bindings; bool m_result; bool m_found_a_var; family_id m_bfid; family_id m_lfid; pattern_validation_functor(uint_set & found_vars, unsigned num_bindings, unsigned num_new_bindings, family_id bfid, family_id lfid): m_found_vars(found_vars), m_num_bindings(num_bindings), m_num_new_bindings(num_new_bindings), m_result(true), m_found_a_var(false), m_bfid(bfid), m_lfid(lfid) { } bool is_forbidden(func_decl const * decl) { 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; if (fid == m_lfid) return true; return false; } void operator()(app * n) { func_decl * decl = to_app(n)->get_decl(); if (is_forbidden(decl)) { warning_msg("'%s' cannot be used in patterns.", decl->get_name().str().c_str()); m_result = false; } } void operator()(var * v) { unsigned idx = to_var(v)->get_idx(); if (idx >= m_num_bindings) { warning_msg("free variables cannot be used in patterns."); m_result = false; return; } if (idx < m_num_new_bindings) { m_found_a_var = true; m_found_vars.insert(idx); } } void operator()(quantifier * q) { m_result = false; } }; bool pattern_validator::process(uint_set & found_vars, unsigned num_bindings, unsigned num_new_bindings, expr * n) { // I'm traversing the DAG as a tree, this is not a problem since pattern are supposed to be small ASTs. if (n->get_kind() == AST_VAR) { warning_msg("invalid pattern: variable."); return false; } pattern_validation_functor f(found_vars, num_bindings, num_new_bindings, m_bfid, m_lfid); for_each_expr(f, n); if (!f.m_result) return false; if (!f.m_found_a_var) { warning_msg("pattern does contain any variable."); return false; } return true; } bool pattern_validator::operator()(unsigned num_bindings, unsigned num_new_bindings, expr * n) { uint_set found_vars; if (!process(found_vars, num_bindings, num_new_bindings, n)) return false; bool r = found_vars.num_elems() == num_new_bindings; if (!r) warning_msg("pattern does not contain all quantified variables."); return r; } z3-z3-4.4.1/src/parsers/util/pattern_validation.h000066400000000000000000000016301260446376700217100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pattern_validation.h Abstract: Code for checking whether a pattern is valid or not. Author: Leonardo de Moura (leonardo) 2006-12-08. Revision History: --*/ #ifndef PATTERN_VALIDATION_H_ #define PATTERN_VALIDATION_H_ #include"ast.h" #include"uint_set.h" #include"vector.h" class pattern_validator { family_id m_bfid; family_id m_lfid; bool process(uint_set & found_vars, unsigned num_bindings, unsigned num_new_bindings, expr * n); public: pattern_validator(ast_manager const & m): m_bfid(m.get_basic_family_id()), m_lfid(m.get_label_family_id()) { } bool operator()(unsigned num_bindings, unsigned num_new_bindings, expr * n); bool operator()(unsigned num_new_bindings, expr * n) { return operator()(UINT_MAX, num_new_bindings, n); } }; #endif /* PATTERN_VALIDATION_H_ */ z3-z3-4.4.1/src/parsers/util/scanner.cpp000066400000000000000000000330641260446376700200130ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: scanner.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-03-31. Revision History: --*/ #include"scanner.h" inline char scanner::read_char() { if (m_is_interactive) { ++m_pos; return m_stream.get(); } if (m_bpos >= m_bend) { m_buffer[0] = m_last_char; m_stream.read(m_buffer.c_ptr()+1, m_buffer.size()-1); m_bend = 1 + static_cast(m_stream.gcount()); m_bpos = 1; m_last_char = m_buffer[m_bend-1]; } ++m_pos; if (m_bpos < m_bend) { return m_buffer[m_bpos++]; } else { // increment m_bpos, so unread_char() will work properly ++m_bpos; return -1; } } inline void scanner::unread_char() { --m_pos; if (m_is_interactive) { m_stream.unget(); } else { // at most one character can be unread. SASSERT(m_bpos > 0); --m_bpos; } } inline bool scanner::state_ok() { return m_state != ERROR_TOKEN && m_state != EOF_TOKEN; } void scanner::comment(char delimiter) { while(state_ok()) { char ch = read_char(); if ('\n' == ch) { ++m_line; } if (delimiter == ch || -1 == ch) { return; } } } scanner::token scanner::read_symbol(char ch) { bool escape = false; if (m_smt2) m_string.pop_back(); // remove leading '|' while (ch != '|' || escape) { if (ch == EOF) { // TODO: use error reporting m_err << "ERROR: unexpected end of file.\n"; return EOF_TOKEN; } if (ch == '\n') { ++m_line; } escape = (ch == '\\'); m_string.push_back(ch); ch = read_char(); } if (!m_smt2) m_string.push_back(ch); // don't add trailing '|' m_string.push_back(0); m_id = m_string.begin(); return ID_TOKEN; } scanner::token scanner::read_id(char first_char) { char ch; m_string.reset(); m_params.reset(); m_string.push_back(first_char); bool is_arith = (m_normalized[(unsigned char) first_char] == '+'); bool is_alpha = (m_normalized[(unsigned char) first_char] == 'a'); ch = read_char(); // In SMT2 "-20" is an identifier. if (!m_smt2 && state_ok() && first_char == '-' && m_normalized[(unsigned char) ch] == '0') { return read_number(ch, false); } if (state_ok() && first_char == '|') { return read_symbol(ch); } while (state_ok()) { switch(m_normalized[(unsigned char) ch]) { case '+': if (is_arith) { m_string.push_back(ch); break; } // strings can have hyphens. if (!is_alpha || ch != '-') { goto bail_out; } case 'a': case ':': case '.': case '0': if (is_arith) { goto bail_out; } m_string.push_back(ch); break; case '[': m_string.push_back(0); m_id = m_string.begin(); if (read_params()) { return ID_TOKEN; } else { return m_state; } default: goto bail_out; } ch = read_char(); } return m_state; bail_out: m_string.push_back(0); m_id = m_string.begin(); unread_char(); return ID_TOKEN; } bool scanner::read_params() { unsigned param_num = 0; while (state_ok()) { char ch = read_char(); switch (m_normalized[(unsigned char) ch]) { case '0': param_num = 10*param_num + (ch - '0'); break; case ']': m_params.push_back(parameter(param_num)); return true; case ':': m_params.push_back(parameter(param_num)); param_num = 0; break; default: m_string.reset(); m_string.push_back(ch); while (true) { ch = read_char(); if (ch == ':' || ch == ']') { m_string.push_back(0); m_params.push_back(parameter(symbol(m_string.c_ptr()))); param_num = 0; if (ch == ':') { unread_char(); } else { return true; } break; } if (ch == EOF) { // TODO: use error reporting m_err << "ERROR: unexpected character: '" << ((int)ch) << " " << ch << "'.\n"; m_state = ERROR_TOKEN; break; } m_string.push_back(ch); } break; } } return false; } scanner::token scanner::read_number(char first_char, bool is_pos) { unsigned divide_by = 0; m_number = rational(first_char - '0'); m_state = INT_TOKEN; while (true) { char ch = read_char(); if (m_normalized[(unsigned char) ch] == '0') { m_number = rational(10)*m_number + rational(ch - '0'); if (m_state == FLOAT_TOKEN) { ++divide_by; } } else if (ch == '.') { m_state = FLOAT_TOKEN; } else { unread_char(); break; } } if (!is_pos) { m_number.neg(); } if (m_state == FLOAT_TOKEN) { m_number /= power(rational(10), divide_by); } return m_state; } scanner::token scanner::read_string(char delimiter, token result) { m_string.reset(); m_params.reset(); while (true) { char ch = read_char(); if (!state_ok()) { return m_state; } if (ch == '\n') { ++m_line; } if (ch == delimiter || ch == EOF) { m_string.push_back(0); m_id = m_string.begin(); return result; } if (ch == '\\') { m_string.push_back('\\'); ch = read_char(); } m_string.push_back(ch); } return m_state; } scanner::token scanner::read_bv_literal() { TRACE("scanner", tout << "read_bv_literal\n";); if (m_bv_token) { char ch = read_char(); if (ch == 'x') { ch = read_char(); m_number = rational(0); m_bv_size = 0; while (true) { if ('0' <= ch && ch <= '9') { m_number *= rational(16); m_number += rational(ch - '0'); } else if ('a' <= ch && ch <= 'f') { m_number *= rational(16); m_number += rational(10 + (ch - 'a')); } else if ('A' <= ch && ch <= 'F') { m_number *= rational(16); m_number += rational(10 + (ch - 'A')); } else { unread_char(); m_state = m_bv_size == 0 ? ERROR_TOKEN : BV_TOKEN; TRACE("scanner", tout << m_state << ", bv-size: " << m_bv_size << ", INT_TOKEN: " << INT_TOKEN << ", BV_TOKEN: " << BV_TOKEN << "\n";); return m_state; } m_bv_size += 4; ch = read_char(); } } else if (ch == 'b') { ch = read_char(); m_number = rational(0); m_bv_size = 0; while (ch == '0' || ch == '1') { m_number *= rational(2); m_number += rational(ch - '0'); m_bv_size++; ch = read_char(); } unread_char(); m_state = m_bv_size == 0 ? ERROR_TOKEN : BV_TOKEN; return m_state; } else { m_state = ERROR_TOKEN; return m_state; } } else { // hack for the old parser char ch = read_char(); bool is_hex = false; m_state = ID_TOKEN; m_string.reset(); m_params.reset(); // convert to SMT1 format m_string.push_back('b'); m_string.push_back('v'); if (ch == 'x') { m_string.push_back('h'); m_string.push_back('e'); m_string.push_back('x'); is_hex = true; } else if (ch == 'b') { m_string.push_back('b'); m_string.push_back('i'); m_string.push_back('n'); } else { // TODO: use error reporting m_err << "ERROR: unexpected character after '#': '" << ((int)ch) << " " << ch << "'.\n"; m_state = ERROR_TOKEN; return m_state; } while (true) { ch = read_char(); if (ch == '0' || ch == '1' || (is_hex && (('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')))) { m_string.push_back(ch); } else { unread_char(); break; } } m_string.push_back(0); m_id = m_string.begin(); return m_state; } } scanner::scanner(std::istream& stream, std::ostream& err, bool smt2, bool bv_token): m_line(1), m_pos(0), m_id(""), m_bv_size(UINT_MAX), m_state(ID_TOKEN), m_stream(stream), m_err(err), m_bpos(1 << 10), m_bend(1 << 10), m_last_char(0), m_smt2(smt2), m_bv_token(bv_token) { char ch; m_is_interactive = &stream == &std::cin; m_buffer.resize(m_bpos); for (int i = 0; i < 256; ++i) { m_normalized[i] = (char) i; } m_normalized[static_cast('\t')] = ' '; m_normalized[static_cast('\r')] = ' '; // assert ('a' < 'z'); for (ch = 'b'; ch <= 'z'; ++ch) { m_normalized[static_cast(ch)] = 'a'; } for (ch = 'A'; ch <= 'Z'; ++ch) { m_normalized[static_cast(ch)] = 'a'; } // assert ('0' < '9', '9' - '0' == 9); for (ch = '1'; ch <= '9'; ++ch) { m_normalized[static_cast(ch)] = '0'; } if (m_smt2) { // SMT2 3.1, "Symbols": ~ ! @ $ % ^ & * _ - + = < > . ? / m_normalized[static_cast('~')] = 'a'; m_normalized[static_cast('!')] = 'a'; m_normalized[static_cast('@')] = 'a'; m_normalized[static_cast('$')] = 'a'; m_normalized[static_cast('%')] = 'a'; m_normalized[static_cast('^')] = 'a'; m_normalized[static_cast('&')] = 'a'; m_normalized[static_cast('*')] = 'a'; m_normalized[static_cast('_')] = 'a'; m_normalized[static_cast('-')] = 'a'; m_normalized[static_cast('+')] = 'a'; m_normalized[static_cast('=')] = 'a'; m_normalized[static_cast('<')] = 'a'; m_normalized[static_cast('>')] = 'a'; m_normalized[static_cast('.')] = 'a'; m_normalized[static_cast('?')] = 'a'; m_normalized[static_cast('/')] = 'a'; // SMT2 3.1, "Hexadecimals", "Binaries" m_normalized[static_cast('#')] = '#'; m_normalized[static_cast('|')] = '+'; } else { m_normalized[static_cast('=')] = '+'; m_normalized[static_cast('<')] = '+'; m_normalized[static_cast('>')] = '+'; m_normalized[static_cast('+')] = '+'; m_normalized[static_cast('-')] = '+'; m_normalized[static_cast('*')] = '+'; m_normalized[static_cast('/')] = '+'; m_normalized[static_cast('%')] = '+'; m_normalized[static_cast('~')] = '+'; m_normalized[static_cast('&')] = '+'; m_normalized[static_cast('@')] = '+'; m_normalized[static_cast('#')] = '+'; m_normalized[static_cast('|')] = '+'; m_normalized[static_cast('\\')] = '+'; m_normalized[static_cast('.')] = '.'; m_normalized[static_cast('_')] = 'a'; m_normalized[static_cast('\'')] = 'a'; m_normalized[static_cast('!')] = 'a'; m_normalized[static_cast('?')] = 'a'; } } scanner::token scanner::scan() { while (state_ok()) { char ch = read_char(); switch (m_normalized[(unsigned char) ch]) { case ' ': break; case '\n': m_pos = 0; ++m_line; break; case ';': comment('\n'); break; case ':': return COLON; case '(': return LEFT_PAREN; case ')': return RIGHT_PAREN; case '?': case '$': case 'a': case '+': case '.': return read_id(ch); case '{': return read_string('}',COMMENT_TOKEN); case '"': return read_string('"',STRING_TOKEN); case '0': return read_number(ch, true); case '#': return read_bv_literal(); case -1: m_state = EOF_TOKEN; break; default: // TODO: use error reporting m_err << "ERROR: unexpected character: '" << ((int)ch) << " " << ch << "'.\n"; m_state = ERROR_TOKEN; break; } } return m_state; } z3-z3-4.4.1/src/parsers/util/scanner.h000066400000000000000000000036331260446376700174570ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: scanner.h Abstract: Author: Leonardo de Moura (leonardo) 2008-03-31. Revision History: --*/ #ifndef SCANNER_H_ #define SCANNER_H_ #include"ast.h" class scanner { public: enum token { LEFT_PAREN = 1, RIGHT_PAREN, COLON, ID_TOKEN, STRING_TOKEN, COMMENT_TOKEN, INT_TOKEN, BV_TOKEN, FLOAT_TOKEN, EOF_TOKEN, ERROR_TOKEN }; scanner(std::istream& stream, std::ostream& err, bool smt2, bool bv_token=false); ~scanner() {} int get_line() const { return m_line; } int get_pos() const { return m_pos; } symbol const & get_id() const { return m_id; } rational get_number() const { return m_number; } unsigned get_bv_size() const { return m_bv_size; } vector const & get_params() const { return m_params; } token scan(); private: int m_line; int m_pos; symbol m_id; rational m_number; unsigned m_bv_size; token m_state; char m_normalized[256]; vector m_string; std::istream& m_stream; std::ostream& m_err; vector m_params; buffer m_buffer; unsigned m_bpos; unsigned m_bend; char m_last_char; bool m_is_interactive; bool m_smt2; bool m_bv_token; char read_char(); token read_symbol(char ch); void unread_char(); void comment(char delimiter); token read_id(char first_char); bool read_params(); token read_number(char first_char, bool is_pos); token read_string(char delimiter, token result); token read_bv_literal(); bool state_ok(); }; #endif /* SCANNER_H_ */ z3-z3-4.4.1/src/parsers/util/simple_parser.cpp000066400000000000000000000066371260446376700212350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: simple_parser.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-03-31. Revision History: --*/ #include #include #include"simple_parser.h" #include"warning.h" #include"scanner.h" simple_parser::simple_parser(ast_manager & m): m_manager(m), m_exprs(m) { } simple_parser::~simple_parser() { } void simple_parser::add_builtin_op(symbol const & s, family_id fid, decl_kind kind) { SASSERT(!m_builtin.contains(s)); SASSERT(!m_vars.contains(s)); m_builtin.insert(s, builtin_op(fid, kind)); } void simple_parser::add_builtin_op(char const * str, family_id fid, decl_kind kind) { add_builtin_op(symbol(str), fid, kind); } void simple_parser::add_var(symbol const & s, var * v) { SASSERT(!m_builtin.contains(s)); SASSERT(!m_vars.contains(s)); m_vars.insert(s, v); } void simple_parser::add_var(char const * str, var * v) { add_var(symbol(str), v); } void simple_parser::reset() { m_builtin.reset(); m_vars.reset(); m_exprs.reset(); } void simple_parser::reset_vars() { m_vars.reset(); } expr * simple_parser::parse_expr(scanner & s) { builtin_op op; var * v; expr * r; scanner::token token; token = s.scan(); switch (token) { case scanner::LEFT_PAREN: token = s.scan(); if (token != scanner::ID_TOKEN) throw parser_error(); if (m_builtin.find(s.get_id(), op)) { ptr_vector args; while (true) { expr * arg = parse_expr(s); if (arg) { args.push_back(arg); } else { expr * r = m_manager.mk_app(op.m_family_id, op.m_kind, args.size(), args.c_ptr()); m_exprs.push_back(r); return r; } } } throw parser_error(); case scanner::RIGHT_PAREN: return 0; case scanner::ID_TOKEN: if (m_builtin.find(s.get_id(), op)) { expr * r = m_manager.mk_const(op.m_family_id, op.m_kind); m_exprs.push_back(r); return r; } else if (m_vars.find(s.get_id(), v)) { return v; } throw parser_error(); case scanner::INT_TOKEN: r = parse_int(s.get_number()); m_exprs.push_back(r); return r; case scanner::FLOAT_TOKEN: r = parse_float(s.get_number()); m_exprs.push_back(r); return r; default: throw parser_error(); } } bool simple_parser::parse(std::istream & in, expr_ref & result) { scanner s(in, std::cerr, false); try { result = parse_expr(s); if (!result) throw parser_error(); } catch (parser_error) { warning_msg("parser error"); return false; } m_exprs.reset(); return result.get() != 0; } bool simple_parser::parse_string(char const * str, expr_ref & result) { std::string s = str; std::istringstream is(s); return parse(is, result); } bool simple_parser::parse_file(char const * file, expr_ref & result) { if (file != 0) { std::ifstream stream(file); if (!stream) { warning_msg("ERROR: could not open file '%s'.", file); return false; } return parse(stream, result); } else { return parse(std::cin, result); } } z3-z3-4.4.1/src/parsers/util/simple_parser.h000066400000000000000000000033011260446376700206630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: simple_parser.h Abstract: Simple sexpr parser Author: Leonardo de Moura (leonardo) 2008-03-31. Revision History: --*/ #ifndef SIMPLE_PARSER_H_ #define SIMPLE_PARSER_H_ #include"ast.h" #include"map.h" class scanner; /** \brief Simple sexpr parser. This class is used to parse small expressions used for configuring Z3. */ class simple_parser { protected: struct parser_error {}; struct builtin_op { family_id m_family_id; decl_kind m_kind; builtin_op() : m_family_id(null_family_id), m_kind(0) {} builtin_op(family_id fid, decl_kind k) : m_family_id(fid), m_kind(k) {} }; typedef map op_map; typedef map var_map; ast_manager & m_manager; op_map m_builtin; var_map m_vars; expr_ref_vector m_exprs; expr * parse_expr(scanner & s); public: simple_parser(ast_manager & m); virtual ~simple_parser(); void add_builtin_op(symbol const & s, family_id fid, decl_kind kind); void add_builtin_op(char const * str, family_id fid, decl_kind kind); void add_var(symbol const & s, var * v); void add_var(char const * str, var * v); void reset(); void reset_vars(); bool parse(std::istream & in, expr_ref & result); bool parse_string(char const * str, expr_ref & result); bool parse_file(char const * file, expr_ref & result); virtual expr * parse_int(rational const & r) { throw parser_error(); } virtual expr * parse_float(rational const & r) { throw parser_error(); } }; #endif /* SIMPLE_PARSER_H_ */ z3-z3-4.4.1/src/qe/000077500000000000000000000000001260446376700136215ustar00rootroot00000000000000z3-z3-4.4.1/src/qe/nlarith_util.cpp000066400000000000000000002200471260446376700170300ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/qe/nlarith_util.h000066400000000000000000000113441260446376700164730ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/qe.cpp000066400000000000000000002636311260446376700147450ustar00rootroot00000000000000/*++ 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; 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) { 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-z3-4.4.1/src/qe/qe.h000066400000000000000000000307141260446376700144040ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/qe_arith.cpp000066400000000000000000000242441260446376700161270ustar00rootroot00000000000000/*++ 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 "ast_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); flatten_and(fml, lits); return ap(model, vars, lits); } } z3-z3-4.4.1/src/qe/qe_arith.h000066400000000000000000000007641260446376700155750ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/qe/qe_arith_plugin.cpp000066400000000000000000002727361260446376700175200ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/qe_array_plugin.cpp000066400000000000000000000242711260446376700175140ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/qe/qe_bool_plugin.cpp000066400000000000000000000134641260446376700173330ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/qe_bv_plugin.cpp000066400000000000000000000047131260446376700170040ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/qe_cmd.cpp000066400000000000000000000036071260446376700155630ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/qe/qe_cmd.h000066400000000000000000000005151260446376700152230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/qe_datatype_plugin.cpp000066400000000000000000000742171260446376700202160ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/qe/qe_dl_plugin.cpp000066400000000000000000000174261260446376700170010ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/qe/qe_lite.cpp000066400000000000000000002644571260446376700157710ustar00rootroot00000000000000/*++ 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 "ast_util.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(); 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; signed_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); 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) { 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-z3-4.4.1/src/qe/qe_lite.h000066400000000000000000000032051260446376700154140ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/qe_sat_tactic.cpp000066400000000000000000000650321260446376700171360ustar00rootroot00000000000000/*++ 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; vector m_fparamv; 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() { reset(); } 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_fparamv.push_back(m_fparams); } for (unsigned i = 0; i <= num_alternations(); ++i) { m_Ms.push_back(m.mk_true()); m_solvers.push_back(alloc(smt::kernel, m, m_fparamv[i], m_params)); } m_Ms[m_Ms.size()-1] = m_fml; 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() { for (unsigned i = 0; i < m_solvers.size(); ++i) { dealloc(m_solvers[i]); } m_fml = 0; m_Ms.reset(); m_fparamv.reset(); m_solvers.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 = false; 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); smt_params fparams(m_fparams); ctx.add_plugin(mk_arith_plugin(ctx, false, 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-z3-4.4.1/src/qe/qe_sat_tactic.h000066400000000000000000000010551260446376700165760ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/qe_tactic.cpp000066400000000000000000000100371260446376700162620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/qe_tactic.h000066400000000000000000000007501260446376700157300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/qe_util.cpp000066400000000000000000000010631260446376700157670ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe_util.h" #include "bool_rewriter.h" namespace qe { 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-z3-4.4.1/src/qe/qe_util.h000066400000000000000000000005721260446376700154400ustar00rootroot00000000000000/*++ 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 { expr_ref mk_and(expr_ref_vector const& fmls); expr_ref mk_or(expr_ref_vector const& fmls); } #endif z3-z3-4.4.1/src/qe/vsubst_tactic.cpp000066400000000000000000000121471260446376700172070ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/qe/vsubst_tactic.h000066400000000000000000000011201260446376700166410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/sat/000077500000000000000000000000001260446376700140035ustar00rootroot00000000000000z3-z3-4.4.1/src/sat/dimacs.cpp000066400000000000000000000047251260446376700157570ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dimacs.cpp Abstract: Dimacs CNF parser Author: Leonardo de Moura (leonardo) 2011-07-26. Revision History: --*/ #include"dimacs.h" #undef max #undef min #include"sat_solver.h" class stream_buffer { std::istream & m_stream; int m_val; public: stream_buffer(std::istream & s): m_stream(s) { m_val = m_stream.get(); } int operator *() const { return m_val; } void operator ++() { m_val = m_stream.get(); } }; template void skip_whitespace(Buffer & in) { while ((*in >= 9 && *in <= 13) || *in == 32) { ++in; } } template void skip_line(Buffer & in) { while(true) { if (*in == EOF) { return; } if (*in == '\n') { ++in; return; } ++in; } } template int parse_int(Buffer & in) { int val = 0; bool neg = false; skip_whitespace(in); if (*in == '-') { neg = true; ++in; } else if (*in == '+') { ++in; } if (*in < '0' || *in > '9') { std::cerr << "(error, \"unexpected char: " << *in << "\")\n"; exit(3); exit(ERR_PARSER); } while (*in >= '0' && *in <= '9') { val = val*10 + (*in - '0'); ++in; } return neg ? -val : val; } template void read_clause(Buffer & in, sat::solver & solver, sat::literal_vector & lits) { int parsed_lit; int var; lits.reset(); while (true) { parsed_lit = parse_int(in); if (parsed_lit == 0) break; var = abs(parsed_lit); SASSERT(var > 0); while (static_cast(var) >= solver.num_vars()) solver.mk_var(); lits.push_back(sat::literal(var, parsed_lit < 0)); } } template void parse_dimacs_core(Buffer & in, sat::solver & solver) { sat::literal_vector lits; while (true) { skip_whitespace(in); if (*in == EOF) { break; } else if (*in == 'c' || *in == 'p') { skip_line(in); } else { read_clause(in, solver, lits); solver.mk_clause(lits.size(), lits.c_ptr()); } } } void parse_dimacs(std::istream & in, sat::solver & solver) { stream_buffer _in(in); parse_dimacs_core(_in, solver); } z3-z3-4.4.1/src/sat/dimacs.h000066400000000000000000000005231260446376700154140ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dimacs.h Abstract: Dimacs CNF parser Author: Leonardo de Moura (leonardo) 2011-07-26. Revision History: --*/ #ifndef DIMACS_H_ #define DIMACS_H_ #include"sat_types.h" void parse_dimacs(std::istream & s, sat::solver & solver); #endif /* DIMACS_PARSER_H_ */ z3-z3-4.4.1/src/sat/sat_asymm_branch.cpp000066400000000000000000000164461260446376700200340ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_asymm_branch.cpp Abstract: SAT solver asymmetric branching Author: Leonardo de Moura (leonardo) 2011-05-30. Revision History: --*/ #include"sat_asymm_branch.h" #include"sat_asymm_branch_params.hpp" #include"sat_solver.h" #include"stopwatch.h" #include"trace.h" namespace sat { asymm_branch::asymm_branch(solver & _s, params_ref const & p): s(_s), m_counter(0) { updt_params(p); reset_statistics(); } struct clause_size_lt { bool operator()(clause * c1, clause * c2) const { return c1->size() > c2->size(); } }; struct asymm_branch::report { asymm_branch & m_asymm_branch; stopwatch m_watch; unsigned m_elim_literals; report(asymm_branch & a): m_asymm_branch(a), m_elim_literals(a.m_elim_literals) { m_watch.start(); } ~report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-asymm-branch :elim-literals " << (m_asymm_branch.m_elim_literals - m_elim_literals) << " :cost " << m_asymm_branch.m_counter << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; void asymm_branch::operator()(bool force) { if (!m_asymm_branch) return; s.propagate(false); // must propagate, since it uses s.push() if (s.m_inconsistent) return; if (!force && m_counter > 0) return; CASSERT("asymm_branch", s.check_invariant()); TRACE("asymm_branch_detail", s.display(tout);); report rpt(*this); svector saved_phase(s.m_phase); m_counter = 0; // counter is moving down to capture propagate cost. int limit = -static_cast(m_asymm_branch_limit); std::stable_sort(s.m_clauses.begin(), s.m_clauses.end(), clause_size_lt()); m_counter -= s.m_clauses.size(); SASSERT(s.m_qhead == s.m_trail.size()); clause_vector::iterator it = s.m_clauses.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = s.m_clauses.end(); try { for (; it != end; ++it) { if (s.inconsistent()) break; SASSERT(s.m_qhead == s.m_trail.size()); if (m_counter < limit || s.inconsistent()) { *it2 = *it; ++it2; continue; } s.checkpoint(); clause & c = *(*it); m_counter -= c.size(); if (!process(c)) continue; // clause was removed *it2 = *it; // throw exception to test bug fix: if (it2 != it) throw solver_exception("trigger bug"); ++it2; } s.m_clauses.set_end(it2); } catch (solver_exception & ex) { // put m_clauses in a consistent state... for (; it != end; ++it, ++it2) { *it2 = *it; } s.m_clauses.set_end(it2); m_counter = -m_counter; throw ex; } m_counter = -m_counter; s.m_phase = saved_phase; CASSERT("asymm_branch", s.check_invariant()); } bool asymm_branch::process(clause & c) { TRACE("asymm_branch_detail", tout << "processing: " << c << "\n";); SASSERT(s.scope_lvl() == 0); SASSERT(s.m_qhead == s.m_trail.size()); #ifdef Z3DEBUG unsigned trail_sz = s.m_trail.size(); #endif SASSERT(!s.inconsistent()); unsigned sz = c.size(); SASSERT(sz > 0); unsigned i; // check if the clause is already satisfied for (i = 0; i < sz; i++) { if (s.value(c[i]) == l_true) { s.dettach_clause(c); s.del_clause(c); return false; } } // try asymmetric branching // clause must not be used for propagation s.dettach_clause(c); s.push(); for (i = 0; i < sz - 1; i++) { literal l = c[i]; SASSERT(!s.inconsistent()); TRACE("asymm_branch_detail", tout << "assigning: " << ~l << "\n";); s.assign(~l, justification()); s.propagate_core(false); // must not use propagate(), since check_missed_propagation may fail for c if (s.inconsistent()) break; } s.pop(1); SASSERT(!s.inconsistent()); SASSERT(s.scope_lvl() == 0); SASSERT(trail_sz == s.m_trail.size()); SASSERT(s.m_qhead == s.m_trail.size()); if (i == sz - 1) { // clause size can't be reduced. s.attach_clause(c); return true; } // clause can be reduced unsigned new_sz = i+1; SASSERT(new_sz >= 1); SASSERT(new_sz < sz); TRACE("asymm_branch", tout << c << "\nnew_size: " << new_sz << "\n"; for (unsigned i = 0; i < c.size(); i++) tout << static_cast(s.value(c[i])) << " "; tout << "\n";); // cleanup reduced clause unsigned j = 0; for (i = 0; i < new_sz; i++) { literal l = c[i]; switch (s.value(l)) { case l_undef: c[j] = l; j++; break; case l_false: break; case l_true: UNREACHABLE(); break; } } new_sz = j; m_elim_literals += sz - new_sz; switch(new_sz) { case 0: s.set_conflict(justification()); return false; case 1: TRACE("asymm_branch", tout << "produced unit clause: " << c[0] << "\n";); s.assign(c[0], justification()); s.del_clause(c); s.propagate_core(false); SASSERT(s.inconsistent() || s.m_qhead == s.m_trail.size()); return false; // check_missed_propagation() may fail, since m_clauses is not in a consistent state. case 2: SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); s.mk_bin_clause(c[0], c[1], false); s.del_clause(c); SASSERT(s.m_qhead == s.m_trail.size()); return false; default: c.shrink(new_sz); s.attach_clause(c); SASSERT(s.m_qhead == s.m_trail.size()); return true; } } void asymm_branch::updt_params(params_ref const & _p) { sat_asymm_branch_params p(_p); m_asymm_branch = p.asymm_branch(); m_asymm_branch_rounds = p.asymm_branch_rounds(); m_asymm_branch_limit = p.asymm_branch_limit(); if (m_asymm_branch_limit > INT_MAX) m_asymm_branch_limit = INT_MAX; } void asymm_branch::collect_param_descrs(param_descrs & d) { sat_asymm_branch_params::collect_param_descrs(d); } void asymm_branch::collect_statistics(statistics & st) const { st.update("elim literals", m_elim_literals); } void asymm_branch::reset_statistics() { m_elim_literals = 0; } }; z3-z3-4.4.1/src/sat/sat_asymm_branch.h000066400000000000000000000021341260446376700174660ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_asymm_branch.h Abstract: SAT solver asymmetric branching Author: Leonardo de Moura (leonardo) 2011-05-30. Revision History: --*/ #ifndef SAT_ASYMM_BRANCH_H_ #define SAT_ASYMM_BRANCH_H_ #include"sat_types.h" #include"statistics.h" #include"params.h" namespace sat { class solver; class asymm_branch { struct report; solver & s; int m_counter; // config bool m_asymm_branch; unsigned m_asymm_branch_rounds; unsigned m_asymm_branch_limit; // stats unsigned m_elim_literals; bool process(clause & c); public: asymm_branch(solver & s, params_ref const & p); void operator()(bool force = false); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void collect_statistics(statistics & st) const; void reset_statistics(); void dec(unsigned c) { m_counter -= c; } }; }; #endif z3-z3-4.4.1/src/sat/sat_asymm_branch_params.pyg000066400000000000000000000007141260446376700214030ustar00rootroot00000000000000def_module_params(module_name='sat', class_name='sat_asymm_branch_params', export=True, params=(('asymm_branch', BOOL, True, 'asymmetric branching'), ('asymm_branch.rounds', UINT, 32, 'maximum number of rounds of asymmetric branching'), ('asymm_branch.limit', UINT, 100000000, 'approx. maximum number of literals visited during asymmetric branching'))) z3-z3-4.4.1/src/sat/sat_bceq.cpp000066400000000000000000000416741260446376700163040ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sat_bceq.cpp Abstract: Find equivalent literals based on blocked clause decomposition. Author: Nikolaj Bjorner (nbjorner) 2014-09-27. Revision History: --*/ #include"sat_bceq.h" #include"sat_solver.h" #include"trace.h" #include"bit_vector.h" #include"map.h" #include"sat_elim_eqs.h" namespace sat { void bceq::use_list::init(unsigned num_vars) { m_clauses.reset(); m_clauses.resize(2*num_vars); } void bceq::use_list::insert(clause& c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { m_clauses[c[i].index()].push_back(&c); } } void bceq::use_list::erase(clause& c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { m_clauses[c[i].index()].erase(&c); } } ptr_vector& bceq::use_list::get(literal lit) { return m_clauses[lit.index()]; } bceq::bceq(solver & s): m_solver(s) { } void bceq::register_clause(clause* cls) { m_clauses.setx(cls->id(), cls, 0); } void bceq::unregister_clause(clause* cls) { m_clauses.setx(cls->id(), 0, 0); } void bceq::init() { m_clauses.reset(); m_bin_clauses.reset(); m_L.reset(); m_R.reset(); m_L_blits.reset(); m_R_blits.reset(); m_bce_use_list.reset(); clause * const* it = m_solver.begin_clauses(); clause * const* end = m_solver.end_clauses(); for (; it != end; ++it) { clause* cls = *it; if (!cls->was_removed()) { m_use_list->insert(*cls); register_clause(cls); } } bin_clauses bc; m_solver.collect_bin_clauses(bc, false); // exclude roots. literal lits[2]; for (unsigned i = 0; i < bc.size(); ++i) { lits[0] = bc[i].first; lits[1] = bc[i].second; clause* cls = m_solver.m_cls_allocator.mk_clause(2, lits, false); m_use_list->insert(*cls); m_bin_clauses.push_back(cls); register_clause(cls); } TRACE("sat", for (unsigned i = 0; i < m_clauses.size(); ++i) { clause const* cls = m_clauses[i]; if (cls) tout << *cls << "\n"; }); } void bceq::pure_decompose() { // while F != empty // pick a clause and variable x in clause. // get use list U1 of x and U2 of ~x // assume |U1| >= |U2| // add U1 to clause set. for (unsigned i = 0; i < m_clauses.size(); ++i) { clause* cls = m_clauses[i]; if (cls) { SASSERT(i == cls->id()); pure_decompose((*cls)[0]); SASSERT(!m_clauses[i]); } } m_L.reverse(); m_L_blits.reverse(); } void bceq::pure_decompose(literal lit) { clause_use_list& pos = m_use_list->get(lit); clause_use_list& neg = m_use_list->get(~lit); unsigned sz1 = m_L.size(); unsigned sz2 = m_R.size(); pure_decompose(pos, m_L); pure_decompose(neg, m_R); unsigned delta1 = m_L.size() - sz1; unsigned delta2 = m_R.size() - sz2; if (delta1 < delta2) { m_L_blits.resize(sz1+delta2, ~lit); m_R_blits.resize(sz2+delta1, lit); for (unsigned i = 0; i < delta1; ++i) { std::swap(m_L[sz1 + i], m_R[sz2 + i]); } for (unsigned i = delta1; i < delta2; ++i) { m_L.push_back(m_R[sz2 + i]); } m_R.resize(sz2 + delta1); std::swap(delta1, delta2); } else { m_L_blits.resize(sz1+delta1, lit); m_R_blits.resize(sz2+delta2, ~lit); } TRACE("bceq", tout << lit << " " << "pos: " << delta1 << " " << "neg: " << delta2 << "\n";); } void bceq::pure_decompose(clause_use_list& uses, svector& clauses) { unsigned sz = uses.size(); for (unsigned i = 0; i < sz; ++i) { clause& cls = *uses[i]; if (!cls.was_removed() && m_clauses[cls.id()]) { clauses.push_back(&cls); m_clauses[cls.id()] = 0; } } } void bceq::post_decompose() { m_marked.reset(); m_marked.resize(2*m_solver.num_vars(), false); use_list ul; use_list* save = m_use_list; m_use_list = &ul; ul.init(m_solver.num_vars()); for (unsigned i = 0; i < m_L.size(); ++i) { ul.insert(*m_L[i]); } // cheap pass: add clauses from R in order // such that they are blocked with respect to // predecessors. m_removed.reset(); for (unsigned i = 0; i < m_R.size(); ++i) { literal lit = find_blocked(*m_R[i]); if (lit != null_literal) { m_L.push_back(m_R[i]); m_L_blits.push_back(lit); ul.insert(*m_R[i]); m_R[i] = m_R.back(); m_R_blits[i] = m_R_blits.back(); m_R.pop_back(); m_R_blits.pop_back(); --i; } } // expensive pass: add clauses from R as long // as BCE produces the empty set of clauses. m_bce_use_list.init(m_solver.num_vars()); for (unsigned i = 0; i < m_L.size(); ++i) { m_bce_use_list.insert(*m_L[i]); } for (unsigned i = 0; i < m_R.size(); ++i) { if (bce(*m_R[i])) { m_R[i] = m_R.back(); m_R_blits[i] = m_R_blits.back(); m_R.pop_back(); m_R_blits.pop_back(); --i; } } m_use_list = save; } // Note: replay blocked clause elimination: // Suppose C u { c1 } is blocked. // annotate each clause by blocking literal. // for new clause c2, check if C u { c2 } is blocked. // For each c in C record which literal it is blocked. // (Order the clauses in C by block ordering) // l | c is blocked, // -> c2 contains ~l => check if c c2 is blocked // bool bceq::bce(clause& cls0) { IF_VERBOSE(1, verbose_stream() << "bce " << m_L.size() << " " << m_R.size() << " " << cls0 << "\n";); unsigned_vector& live_clauses = m_live_clauses; live_clauses.reset(); m_use_list = &m_bce_use_list; m_bce_use_list.insert(cls0); svector& clauses = m_L; literal_vector& blits = m_L_blits; clauses.push_back(&cls0); blits.push_back(null_literal); bool removed = false; m_removed.reset(); for (unsigned i = 0; i < clauses.size(); ++i) { clause& cls1 = *clauses[i]; literal lit = find_blocked(cls1); if (lit == null_literal) { live_clauses.push_back(i); } else { m_removed.setx(cls1.id(), true, false); removed = true; } } while (removed) { removed = false; //std::cout << live_clauses.size() << " "; for (unsigned i = 0; i < live_clauses.size(); ++i) { clause& cls1 = *clauses[live_clauses[i]]; literal lit = find_blocked(cls1); if (lit != null_literal) { m_removed.setx(cls1.id(), true, false); removed = true; live_clauses[i] = live_clauses.back(); live_clauses.pop_back(); --i; } } } //std::cout << "\n"; m_bce_use_list.erase(cls0); clauses.pop_back(); blits.pop_back(); return live_clauses.empty(); } literal bceq::find_blocked(clause const& cls) { TRACE("bceq", tout << cls << "\n";); unsigned sz = cls.size(); for (unsigned i = 0; i < sz; ++i) { m_marked[(~cls[i]).index()] = true; } literal result = null_literal; for (unsigned i = 0; i < sz; ++i) { literal lit = cls[i]; if (is_blocked(lit)) { TRACE("bceq", tout << "is blocked " << lit << " : " << cls << "\n";); result = lit; break; } } for (unsigned i = 0; i < sz; ++i) { m_marked[(~cls[i]).index()] = false; } return result; } bool bceq::is_blocked(literal lit) const { clause_use_list& uses = m_use_list->get(~lit); unsigned sz = uses.size(); for (unsigned i = 0; i < sz; ++i) { clause const& cls = *uses[i]; unsigned sz = cls.size(); bool is_axiom = m_removed.get(cls.id(), false); for (unsigned i = 0; !is_axiom && i < sz; ++i) { is_axiom = m_marked[cls[i].index()] && cls[i] != ~lit; } TRACE("bceq", tout << "resolvent " << lit << " : " << cls << " " << (is_axiom?"axiom":"non-axiom") << "\n";); if (!is_axiom) { return false; } } return true; } void bceq::init_rbits() { m_rbits.reset(); for (unsigned i = 0; i < m_solver.num_vars(); ++i) { uint64 lo = m_rand() + (m_rand() << 16); uint64 hi = m_rand() + (m_rand() << 16); m_rbits.push_back(lo + (hi << 32ULL)); } } void bceq::init_reconstruction_stack() { m_rstack.reset(); m_bstack.reset(); // decomposition already creates a blocked stack in the proper order. m_rstack.append(m_L); m_bstack.append(m_L_blits); } uint64 bceq::eval_clause(clause const& cls) const { uint64 b = 0; unsigned sz = cls.size(); for (unsigned i = 0; i < sz; ++i) { literal lit = cls[i]; uint64 val = m_rbits[lit.var()]; if (lit.sign()) { val = ~val; } b |= val; } return b; } void bceq::sat_sweep() { init_rbits(); init_reconstruction_stack(); for (unsigned i = 0; i < m_rstack.size(); ++i) { clause const& cls = *m_rstack[i]; literal block_lit = m_bstack[i]; uint64 b = eval_clause(cls); // v = 0, b = 0 -> v := 1 // v = 0, b = 1 -> v := 0 // v = 1, b = 0 -> v := 0 // v = 1, b = 1 -> v := 1 m_rbits[block_lit.var()] ^= ~b; } DEBUG_CODE(verify_sweep();); } void bceq::verify_sweep() { for (unsigned i = 0; i < m_L.size(); ++i) { uint64 b = eval_clause(*m_L[i]); SASSERT((~b) == 0); } } struct u64_hash { unsigned operator()(uint64 u) const { return (unsigned)u; } }; struct u64_eq { bool operator()(uint64 u1, uint64 u2) const { return u1 == u2; } }; void bceq::extract_partition() { unsigned num_vars = m_solver.num_vars(); map table; union_find<> union_find(m_union_find_ctx); for (unsigned i = 0; i < num_vars; ++i) { m_s->mk_var(true, true); union_find.mk_var(); } for (unsigned i = 0; i < m_L.size(); ++i) { m_s->mk_clause(m_L[i]->size(), m_L[i]->begin()); } for (unsigned i = 0; i < num_vars; ++i) { uint64 val = m_rbits[i]; unsigned index; if (table.find(val, index)) { union_find.merge(i, index); } else if (table.find(~val, index)) { union_find.merge(i, index); } else { table.insert(val, i); } } TRACE("sat", union_find.display(tout);); // // Preliminary version: // A more appropriate is to walk each pair, // and refine partition based on SAT results. // for (unsigned i = 0; i < num_vars; ++i) { if (!union_find.is_root(i)) continue; unsigned v = union_find.next(i); unsigned last_v = UINT_MAX; if (!m_solver.was_eliminated(i)) { last_v = i; } while (v != i) { if (!m_solver.was_eliminated(v)) { if (last_v != UINT_MAX) { if (check_equality(v, last_v)) { // last_v was eliminated. } else { // TBD: refine partition. } } last_v = v; } v = union_find.next(v); } } } bool bceq::check_equality(unsigned v1, unsigned v2) { TRACE("sat", tout << "check: " << v1 << " = " << v2 << "\n";); uint64 val1 = m_rbits[v1]; uint64 val2 = m_rbits[v2]; literal l1 = literal(v1, false); literal l2 = literal(v2, false); if (val1 != val2) { SASSERT(val1 == ~val2); l2.neg(); } if (is_already_equiv(l1, l2)) { TRACE("sat", tout << "Already equivalent: " << l1 << " " << l2 << "\n";); return false; } literal lits[2]; lits[0] = l1; lits[1] = ~l2; lbool is_sat = m_s->check(2, lits); if (is_sat == l_false) { lits[0] = ~l1; lits[1] = l2; is_sat = m_s->check(2, lits); } if (is_sat == l_false) { TRACE("sat", tout << "Found equivalent: " << l1 << " " << l2 << "\n";); assert_equality(l1, l2); } else { TRACE("sat", tout << "Not equivalent: " << l1 << " " << l2 << "\n";); // TBD: if is_sat == l_true, then refine partition. } return is_sat == l_false; } bool bceq::is_already_equiv(literal l1, literal l2) { watch_list const& w1 = m_solver.get_wlist(l1); bool found = false; for (unsigned i = 0; !found && i < w1.size(); ++i) { watched const& w = w1[i]; found = w.is_binary_clause() && w.get_literal() == ~l2; } if (!found) return false; found = false; watch_list const& w2 = m_solver.get_wlist(~l1); for (unsigned i = 0; !found && i < w2.size(); ++i) { watched const& w = w2[i]; found = w.is_binary_clause() && w.get_literal() == l2; } return found; } void bceq::assert_equality(literal l1, literal l2) { if (l2.sign()) { l1.neg(); l2.neg(); } literal_vector roots; bool_var_vector vars; for (unsigned i = 0; i < m_solver.num_vars(); ++i) { roots.push_back(literal(i, false)); } roots[l2.var()] = l1; vars.push_back(l2.var()); elim_eqs elim(m_solver); IF_VERBOSE(1, for (unsigned i = 0; i < vars.size(); ++i) { verbose_stream() << "var: " << vars[i] << " root: " << roots[vars[i]] << "\n"; }); elim(roots, vars); } void bceq::cleanup() { m_solver.del_clauses(m_bin_clauses.begin(), m_bin_clauses.end()); m_bin_clauses.reset(); } void bceq::operator()() { if (!m_solver.m_config.m_bcd) return; flet _disable_bcd(m_solver.m_config.m_bcd, false); flet _disable_min(m_solver.m_config.m_minimize_core, false); flet _disable_opt(m_solver.m_config.m_optimize_model, false); flet _bound_maxc(m_solver.m_config.m_max_conflicts, 1500); use_list ul; solver s(m_solver.m_params, m_solver.rlimit(), 0); s.m_config.m_bcd = false; s.m_config.m_minimize_core = false; s.m_config.m_optimize_model = false; s.m_config.m_max_conflicts = 1500; m_use_list = &ul; m_s = &s; ul.init(m_solver.num_vars()); init(); pure_decompose(); post_decompose(); IF_VERBOSE(1, verbose_stream() << "Decomposed set " << m_L.size() << " rest: " << m_R.size() << "\n";); TRACE("sat", tout << "Decomposed set " << m_L.size() << "\n"; for (unsigned i = 0; i < m_L.size(); ++i) { clause const* cls = m_L[i]; if (cls) tout << *cls << "\n"; } tout << "remainder " << m_R.size() << "\n"; for (unsigned i = 0; i < m_R.size(); ++i) { clause const* cls = m_R[i]; if (cls) tout << *cls << "\n"; } ); sat_sweep(); extract_partition(); cleanup(); } }; z3-z3-4.4.1/src/sat/sat_bceq.h000066400000000000000000000050161260446376700157370ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sat_bceq.h Abstract: Find equivalent literals based on blocked clause decomposition. Author: Nikolaj Bjorner (nbjorner) 2014-09-27. Revision History: --*/ #ifndef SAT_BCEQ_H_ #define SAT_BCEQ_H_ #include"sat_types.h" #include"union_find.h" namespace sat { class solver; class bceq { typedef ptr_vector clause_use_list; class use_list { vector > m_clauses; public: use_list() {} void init(unsigned num_vars); void reset() { m_clauses.reset(); } void erase(clause& c); void insert(clause& c); ptr_vector& get(literal lit); }; typedef std::pair bin_clause; typedef svector bin_clauses; solver & m_solver; use_list* m_use_list; use_list m_bce_use_list; solver* m_s; random_gen m_rand; svector m_clauses; svector m_L; svector m_R; literal_vector m_L_blits; literal_vector m_R_blits; svector m_bin_clauses; svector m_rbits; svector m_rstack; // stack of blocked clauses literal_vector m_bstack; // stack of blocking literals svector m_marked; svector m_removed; // set of clauses removed (not considered in clause set during BCE) union_find_default_ctx m_union_find_ctx; unsigned_vector m_live_clauses; void init(); void register_clause(clause* cls); void unregister_clause(clause* cls); void pure_decompose(); void pure_decompose(literal lit); void pure_decompose(ptr_vector& uses, svector& clauses); void post_decompose(); literal find_blocked(clause const& cls); bool bce(clause& cls); bool is_blocked(literal lit) const; void init_rbits(); void init_reconstruction_stack(); void sat_sweep(); void cleanup(); uint64 eval_clause(clause const& cls) const; void verify_sweep(); void extract_partition(); bool check_equality(unsigned v1, unsigned v2); bool is_already_equiv(literal l1, literal l2); void assert_equality(literal l1, literal l2); public: bceq(solver & s); void operator()(); }; }; #endif z3-z3-4.4.1/src/sat/sat_clause.cpp000066400000000000000000000151701260446376700166360ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause.cpp Abstract: Clauses Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #include #include"sat_clause.h" #include"z3_exception.h" #include"trace.h" namespace sat { clause::clause(unsigned id, unsigned sz, literal const * lits, bool learned): m_id(id), m_size(sz), m_capacity(sz), m_removed(false), m_learned(learned), m_used(false), m_frozen(false), m_reinit_stack(false), m_inact_rounds(0) { memcpy(m_lits, lits, sizeof(literal) * sz); mark_strengthened(); SASSERT(check_approx()); } var_approx_set clause::approx(unsigned num, literal const * lits) { var_approx_set r; for (unsigned i = 0; i < num; i++) r.insert(lits[i].var()); return r; } void clause::update_approx() { m_approx = approx(m_size, m_lits); } bool clause::check_approx() const { var_approx_set curr = m_approx; const_cast(this)->update_approx(); SASSERT(may_eq(curr, m_approx)); return true; } 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; } void clause::elim(literal l) { unsigned i; for (i = 0; i < m_size; i++) if (m_lits[i] == l) break; SASSERT(i < m_size); i++; for (; i < m_size; i++) m_lits[i-1] = m_lits[i]; m_size--; mark_strengthened(); } bool clause::satisfied_by(model const & m) const { for (unsigned i = 0; i < m_size; i++) { literal l = m_lits[i]; if (l.sign()) { if (m[l.var()] == l_false) return true; } else { if (m[l.var()] == l_true) return true; } } return false; } void tmp_clause::set(unsigned num_lits, literal const * lits, bool learned) { if (m_clause && m_clause->m_capacity < num_lits) { dealloc_svect(m_clause); m_clause = 0; } if (!m_clause) { void * mem = alloc_svect(char, clause::get_obj_size(num_lits)); m_clause = new (mem) clause(UINT_MAX, num_lits, lits, learned); } else { SASSERT(m_clause->m_id == UINT_MAX); m_clause->m_size = num_lits; m_clause->m_learned = learned; memcpy(m_clause->m_lits, lits, sizeof(literal) * num_lits); } SASSERT(m_clause->m_size <= m_clause->m_capacity); for (unsigned i = 0; i < num_lits; i++) { SASSERT((*m_clause)[i] == lits[i]); } } clause_allocator::clause_allocator(): m_allocator("clause-allocator") { #ifdef _AMD64_ m_num_segments = 0; #endif } clause * clause_allocator::get_clause(clause_offset cls_off) const { #ifdef _AMD64_ return reinterpret_cast(m_segments[cls_off & c_aligment_mask] + (static_cast(cls_off) & ~c_aligment_mask)); #else return reinterpret_cast(cls_off); #endif } #ifdef _AMD64_ unsigned clause_allocator::get_segment(size_t ptr) { SASSERT((ptr & c_aligment_mask) == 0); ptr &= ~0xFFFFFFFFull; // Keep only high part unsigned i = 0; for (i = 0; i < m_num_segments; ++i) if (m_segments[i] == ptr) return i; i = m_num_segments; m_num_segments++; if (i > c_max_segments) throw default_exception("segment out of range"); m_segments[i] = ptr; return i; } #endif clause_offset clause_allocator::get_offset(clause const * ptr) const { #ifdef _AMD64_ return static_cast(reinterpret_cast(ptr)) + const_cast(this)->get_segment(reinterpret_cast(ptr)); #else return reinterpret_cast(ptr); #endif } clause * clause_allocator::mk_clause(unsigned num_lits, literal const * lits, bool learned) { size_t size = clause::get_obj_size(num_lits); #ifdef _AMD64_ size_t slot = size >> c_cls_alignment; if ((size & c_aligment_mask) != 0) slot++; size = slot << c_cls_alignment; #endif void * mem = m_allocator.allocate(size); clause * cls = new (mem) clause(m_id_gen.mk(), num_lits, lits, learned); SASSERT(!learned || cls->is_learned()); return cls; } void clause_allocator::del_clause(clause * cls) { TRACE("sat", tout << "delete: " << cls->id() << " " << cls << " " << *cls << "\n";); m_id_gen.recycle(cls->id()); size_t size = clause::get_obj_size(cls->m_capacity); #ifdef _AMD64_ size_t slot = size >> c_cls_alignment; if ((size & c_aligment_mask) != 0) slot++; size = slot << c_cls_alignment; #endif cls->~clause(); m_allocator.deallocate(size, cls); } std::ostream & operator<<(std::ostream & out, clause const & c) { out << "("; for (unsigned i = 0; i < c.size(); i++) { if (i > 0) out << " "; out << c[i]; } out << ")"; if (c.was_removed()) out << "x"; if (c.strengthened()) out << "+"; if (c.is_learned()) out << "*"; return out; } std::ostream & operator<<(std::ostream & out, clause_vector const & cs) { clause_vector::const_iterator it = cs.begin(); clause_vector::const_iterator end = cs.end(); for (; it != end; ++it) { out << *(*it) << "\n"; } return out; } bool clause_wrapper::contains(literal l) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) if (operator[](i) == l) return true; return false; } bool clause_wrapper::contains(bool_var v) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) if (operator[](i).var() == v) return true; return false; } std::ostream & operator<<(std::ostream & out, clause_wrapper const & c) { out << "("; for (unsigned i = 0; i < c.size(); i++) { if (i > 0) out << " "; out << c[i]; } out << ")"; return out; } }; z3-z3-4.4.1/src/sat/sat_clause.h000066400000000000000000000154371260446376700163110ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause.h Abstract: Clauses Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_CLAUSE_H_ #define SAT_CLAUSE_H_ #include"sat_types.h" #include"small_object_allocator.h" #include"id_gen.h" #ifdef _MSC_VER #pragma warning(disable : 4200) #pragma warning(disable : 4355) #endif namespace sat { class clause_allocator; class clause { friend class clause_allocator; friend class tmp_clause; unsigned m_id; unsigned m_size; unsigned m_capacity; var_approx_set m_approx; unsigned m_strengthened:1; unsigned m_removed:1; unsigned m_learned:1; unsigned m_used:1; unsigned m_frozen:1; unsigned m_reinit_stack:1; unsigned m_inact_rounds:8; unsigned m_glue:8; unsigned m_psm:8; // transient field used during gc 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); public: unsigned id() const { return m_id; } unsigned size() const { return m_size; } 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; } void unset_learned() { SASSERT(is_learned()); m_learned = false; } void shrink(unsigned num_lits) { SASSERT(num_lits <= m_size); if (num_lits < m_size) { m_size = num_lits; mark_strengthened(); } } bool strengthened() const { return m_strengthened; } void mark_strengthened() { m_strengthened = true; update_approx(); } void unmark_strengthened() { m_strengthened = false; } void elim(literal l); bool was_removed() const { return m_removed; } void set_removed(bool f) { m_removed = f; } var_approx_set approx() const { return m_approx; } void update_approx(); bool check_approx() const; // for debugging literal * begin() { return m_lits; } literal * end() { return m_lits + m_size; } bool contains(literal l) const; bool contains(bool_var v) const; bool satisfied_by(model const & m) const; void mark_used() { m_used = true; } void unmark_used() { m_used = false; } bool was_used() const { return m_used; } void inc_inact_rounds() { m_inact_rounds++; } void reset_inact_rounds() { m_inact_rounds = 0; } unsigned inact_rounds() const { return m_inact_rounds; } bool frozen() const { return m_frozen; } void freeze() { SASSERT(is_learned()); SASSERT(!frozen()); m_frozen = true; } void unfreeze() { SASSERT(is_learned()); SASSERT(frozen()); m_frozen = false; } static var_approx_set approx(unsigned num, literal const * lits); void set_glue(unsigned glue) { m_glue = glue > 255 ? 255 : glue; } unsigned glue() const { return m_glue; } void set_psm(unsigned psm) { m_psm = psm > 255 ? 255 : psm; } unsigned psm() const { return m_psm; } bool on_reinit_stack() const { return m_reinit_stack; } void set_reinit_stack(bool f) { m_reinit_stack = f; } }; std::ostream & operator<<(std::ostream & out, clause const & c); std::ostream & operator<<(std::ostream & out, clause_vector const & cs); class bin_clause { unsigned m_val1; unsigned m_val2; public: bin_clause(literal l1, literal l2, bool learned):m_val1(l1.to_uint()), m_val2((l2.to_uint() << 1) + static_cast(learned)) {} literal get_literal1() const { return to_literal(m_val1); } literal get_literal2() const { return to_literal(m_val2 >> 1); } bool is_learned() const { return (m_val2 & 1) == 1; } }; class tmp_clause { clause * m_clause; public: tmp_clause():m_clause(0) {} ~tmp_clause() { if (m_clause) dealloc_svect(m_clause); } clause * get() const { return m_clause; } void set(unsigned num_lits, literal const * lits, bool learned); void set(literal l1, literal l2, bool learned) { literal ls[2] = { l1, l2 }; set(2, ls, learned); } void set(bin_clause const & c) { set(c.get_literal1(), c.get_literal2(), c.is_learned()); } }; /** \brief Simple clause allocator that allows uint (32bit integers) to be used to reference clauses (even in 64bit machines). */ class clause_allocator { small_object_allocator m_allocator; id_gen m_id_gen; #ifdef _AMD64_ unsigned get_segment(size_t ptr); static const unsigned c_cls_alignment = 3; static const unsigned c_max_segments = 1 << c_cls_alignment; static const size_t c_aligment_mask = (1ull << c_cls_alignment) - 1ull; unsigned m_num_segments; size_t m_segments[c_max_segments]; #endif public: clause_allocator(); clause * get_clause(clause_offset cls_off) const; clause_offset get_offset(clause const * ptr) const; clause * mk_clause(unsigned num_lits, literal const * lits, bool learned); void del_clause(clause * cls); }; /** \brief Wrapper for clauses & binary clauses. I do not create clause objects for binary clauses. clause_ref wraps a clause object or a pair of literals (i.e., a binary clause). */ class clause_wrapper { union { clause * m_cls; unsigned m_l1_idx; }; unsigned m_l2_idx; public: clause_wrapper(literal l1, literal l2):m_l1_idx(l1.to_uint()), m_l2_idx(l2.to_uint()) {} clause_wrapper(clause & c):m_cls(&c), m_l2_idx(null_literal.to_uint()) {} bool is_binary() const { return m_l2_idx != null_literal.to_uint(); } unsigned size() const { return is_binary() ? 2 : m_cls->size(); } literal operator[](unsigned idx) const { SASSERT(idx < size()); if (is_binary()) return idx == 0 ? to_literal(m_l1_idx) : to_literal(m_l2_idx); else return m_cls->operator[](idx); } bool contains(literal l) const; bool contains(bool_var v) const; clause * get_clause() const { SASSERT(!is_binary()); return m_cls; } }; typedef svector clause_wrapper_vector; std::ostream & operator<<(std::ostream & out, clause_wrapper const & c); }; #endif z3-z3-4.4.1/src/sat/sat_clause_set.cpp000066400000000000000000000041731260446376700175120ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause_set.cpp Abstract: Set of clauses Author: Leonardo de Moura (leonardo) 2011-05-25. Revision History: --*/ #include"sat_clause_set.h" namespace sat { void clause_set::insert(clause & c) { unsigned id = c.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); CASSERT("clause_set", check_invariant()); } void clause_set::erase(clause & c) { unsigned id = c.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) { clause * last_c = m_set[last_pos]; m_set[pos] = last_c; m_id2pos[last_c->id()] = pos; } m_set.pop_back(); CASSERT("clause_set", check_invariant()); } clause & clause_set::erase() { SASSERT(!empty()); clause & c = *m_set.back(); m_id2pos[c.id()] = UINT_MAX; m_set.pop_back(); return c; } bool clause_set::check_invariant() const { { clause_vector::const_iterator it = m_set.begin(); clause_vector::const_iterator end = m_set.end(); for (unsigned pos = 0; it != end; ++it, ++pos) { clause & c = *(*it); SASSERT(c.id() < m_id2pos.size()); SASSERT(m_id2pos[c.id()] == pos); } } { unsigned_vector::const_iterator it = m_id2pos.begin(); unsigned_vector::const_iterator end = m_id2pos.end(); for (unsigned id = 0; it != end; ++it, ++id) { unsigned pos = *it; if (pos == UINT_MAX) continue; SASSERT(pos < m_set.size()); SASSERT(m_set[pos]->id() == id); } } return true; } }; z3-z3-4.4.1/src/sat/sat_clause_set.h000066400000000000000000000022301260446376700171470ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause_set.h Abstract: Set of clauses Author: Leonardo de Moura (leonardo) 2011-05-25. Revision History: --*/ #ifndef SAT_CLAUSE_SET_H_ #define SAT_CLAUSE_SET_H_ #include"sat_clause.h" namespace sat { class clause_set { unsigned_vector m_id2pos; clause_vector m_set; public: typedef clause_vector::const_iterator iterator; bool contains(clause const & cls) const { if (cls.id() >= m_id2pos.size()) return false; return m_id2pos[cls.id()] != UINT_MAX; } bool empty() const { return m_set.empty(); } unsigned size() const { return m_set.size(); } void insert(clause & c); void erase(clause & c); // erase some clause from the set clause & erase(); 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(); } bool check_invariant() const; }; }; #endif z3-z3-4.4.1/src/sat/sat_clause_use_list.cpp000066400000000000000000000020351260446376700205410ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause_use_list.cpp Abstract: Clause use list Author: Leonardo de Moura (leonardo) 2011-05-31. Revision History: --*/ #include"sat_clause.h" #include"sat_clause_use_list.h" namespace sat { bool clause_use_list::check_invariant() const { #ifdef LAZY_USE_LIST unsigned sz = 0; for (unsigned i = 0; i < m_clauses.size(); i++) if (!m_clauses[i]->was_removed()) sz++; SASSERT(sz == m_size); #endif return true; } #ifdef LAZY_USE_LIST void clause_use_list::iterator::consume() { while (true) { if (m_i == m_size) return; if (!m_clauses[m_i]->was_removed()) { m_clauses[m_j] = m_clauses[m_i]; return; } m_i++; } } #endif clause_use_list::iterator::~iterator() { #ifdef LAZY_USE_LIST while (m_i < m_size) next(); m_clauses.shrink(m_j); #endif } }; z3-z3-4.4.1/src/sat/sat_clause_use_list.h000066400000000000000000000060611260446376700202110ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause_use_list.h Abstract: Clause use list Author: Leonardo de Moura (leonardo) 2011-05-31. Revision History: --*/ #ifndef SAT_CLAUSE_USE_LIST_H_ #define SAT_CLAUSE_USE_LIST_H_ #include"sat_types.h" #include"trace.h" namespace sat { #define LAZY_USE_LIST /** \brief Clause use list with delayed deletion. */ class clause_use_list { clause_vector m_clauses; #ifdef LAZY_USE_LIST unsigned m_size; #endif public: clause_use_list() { STRACE("clause_use_list_bug", tout << "[cul_created] " << this << "\n";); #ifdef LAZY_USE_LIST m_size = 0; #endif } unsigned size() const { #ifdef LAZY_USE_LIST return m_size; #else return m_clauses.size(); #endif } bool empty() const { return size() == 0; } void insert(clause & c) { STRACE("clause_use_list_bug", tout << "[cul_insert] " << this << " " << &c << "\n";); SASSERT(!m_clauses.contains(&c)); SASSERT(!c.was_removed()); m_clauses.push_back(&c); #ifdef LAZY_USE_LIST m_size++; #endif } void erase_not_removed(clause & c) { STRACE("clause_use_list_bug", tout << "[cul_erase_not_removed] " << this << " " << &c << "\n";); #ifdef LAZY_USE_LIST SASSERT(m_clauses.contains(&c)); SASSERT(!c.was_removed()); m_clauses.erase(&c); m_size--; #else m_clauses.erase(&c); #endif } void erase(clause & c) { STRACE("clause_use_list_bug", tout << "[cul_erase] " << this << " " << &c << "\n";); #ifdef LAZY_USE_LIST SASSERT(m_clauses.contains(&c)); SASSERT(c.was_removed()); m_size--; #else m_clauses.erase(&c); #endif } void reset() { m_clauses.finalize(); #ifdef LAZY_USE_LIST m_size = 0; #endif } bool check_invariant() const; // iterate & compress class iterator { clause_vector & m_clauses; unsigned m_size; unsigned m_i; #ifdef LAZY_USE_LIST unsigned m_j; void consume(); #endif public: iterator(clause_vector & v):m_clauses(v), m_size(v.size()), m_i(0) { #ifdef LAZY_USE_LIST m_j = 0; consume(); #endif } ~iterator(); bool at_end() const { return m_i == m_size; } clause & curr() const { SASSERT(!at_end()); return *(m_clauses[m_i]); } void next() { SASSERT(!at_end()); SASSERT(!m_clauses[m_i]->was_removed()); m_i++; #ifdef LAZY_USE_LIST m_j++; consume(); #endif } }; iterator mk_iterator() const { return iterator(const_cast(this)->m_clauses); } }; }; #endif z3-z3-4.4.1/src/sat/sat_cleaner.cpp000066400000000000000000000167471260446376700170060ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_cleaner.h Abstract: Eliminate satisfied clauses, and literals assigned to false. Author: Leonardo de Moura (leonardo) 2011-05-24. Revision History: --*/ #include"sat_cleaner.h" #include"sat_solver.h" #include"trace.h" #include"stopwatch.h" namespace sat { cleaner::cleaner(solver & _s): s(_s), m_last_num_units(0), m_cleanup_counter(0) { reset_statistics(); } /** - Delete watch lists of assigned literals. - Delete satisfied binary watched binary clauses - Delete watched clauses (they will be reinserted after they are cleaned). */ void cleaner::cleanup_watches() { vector::iterator it = s.m_watches.begin(); vector::iterator end = s.m_watches.end(); unsigned l_idx = 0; for (; it != end; ++it, ++l_idx) { if (s.value(to_literal(l_idx)) != l_undef) { it->finalize(); SASSERT(it->empty()); continue; } TRACE("cleanup_bug", tout << "processing wlist of " << to_literal(l_idx) << "\n";); watch_list & wlist = *it; watch_list::iterator it2 = wlist.begin(); watch_list::iterator it_prev = it2; watch_list::iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { switch (it2->get_kind()) { case watched::BINARY: SASSERT(s.value(it2->get_literal()) == l_true || s.value(it2->get_literal()) == l_undef); if (s.value(it2->get_literal()) == l_undef) { *it_prev = *it2; ++it_prev; } TRACE("cleanup_bug", tout << "keeping: " << ~to_literal(l_idx) << " " << it2->get_literal() << "\n";); break; case watched::TERNARY: case watched::CLAUSE: // skip break; case watched::EXT_CONSTRAINT: *it_prev = *it2; ++it_prev; break; default: UNREACHABLE(); break; } } wlist.set_end(it_prev); } } void cleaner::cleanup_clauses(clause_vector & cs) { clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); TRACE("sat_cleaner_bug", tout << "cleaning: " << c << "\n"; for (unsigned i = 0; i < c.size(); i++) tout << c[i] << ": " << s.value(c[i]) << "\n";); CTRACE("sat_cleaner_frozen", c.frozen(), tout << c << "\n";); unsigned sz = c.size(); unsigned i = 0, j = 0; bool sat = false; m_cleanup_counter += sz; for (; i < sz; i++) { switch (s.value(c[i])) { case l_true: sat = true; goto end_loop; case l_false: m_elim_literals++; break; case l_undef: c[j] = c[i]; j++; break; } } end_loop: CTRACE("sat_cleaner_frozen", c.frozen(), tout << "sat: " << sat << ", new_size: " << j << "\n"; tout << mk_lits_pp(j, c.begin()) << "\n";); if (sat) { m_elim_clauses++; s.del_clause(c); } else { unsigned new_sz = j; CTRACE("sat_cleaner_bug", new_sz < 2, tout << "new_sz: " << new_sz << "\n"; if (c.size() > 0) tout << "unit: " << c[0] << "\n";); SASSERT(c.frozen() || new_sz >= 2); if (new_sz == 0) { // It can only happen with frozen clauses. // active clauses would have signed the conflict. SASSERT(c.frozen()); s.set_conflict(justification()); s.del_clause(c); } else if (new_sz == 1) { // It can only happen with frozen clauses. // active clauses would have propagated the literal SASSERT(c.frozen()); s.assign(c[0], justification()); s.del_clause(c); } else { SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); if (new_sz == 2) { TRACE("cleanup_bug", tout << "clause became binary: " << c[0] << " " << c[1] << "\n";); s.mk_bin_clause(c[0], c[1], c.is_learned()); s.del_clause(c); } else { c.shrink(new_sz); *it2 = *it; it2++; if (!c.frozen()) { if (new_sz == 3) s.attach_ter_clause(c); else s.attach_nary_clause(c); } } } } } cs.set_end(it2); } struct cleaner::report { cleaner & m_cleaner; stopwatch m_watch; unsigned m_elim_clauses; unsigned m_elim_literals; report(cleaner & c): m_cleaner(c), m_elim_clauses(c.m_elim_clauses), m_elim_literals(c.m_elim_literals) { m_watch.start(); } ~report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-cleaner :elim-literals " << (m_cleaner.m_elim_literals - m_elim_literals) << " :elim-clauses " << (m_cleaner.m_elim_clauses - m_elim_clauses) << " :cost " << m_cleaner.m_cleanup_counter << mk_stat(m_cleaner.s) << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; /** \brief Return true if cleaner executed. */ bool cleaner::operator()(bool force) { CASSERT("cleaner_bug", s.check_invariant()); unsigned trail_sz = s.m_trail.size(); s.propagate(false); // make sure that everything was propagated. if (s.m_inconsistent) return false; if (m_last_num_units == trail_sz) return false; // there are no new assigned literals since last time... nothing to be done if (!force && m_cleanup_counter > 0) return false; // prevent simplifier from being executed over and over again. report rpt(*this); m_last_num_units = trail_sz; m_cleanup_counter = 0; cleanup_watches(); cleanup_clauses(s.m_clauses); cleanup_clauses(s.m_learned); s.propagate(false); CASSERT("cleaner_bug", s.check_invariant()); return true; } void cleaner::reset_statistics() { m_elim_clauses = 0; m_elim_literals = 0; } void cleaner::collect_statistics(statistics & st) const { st.update("elim clauses", m_elim_clauses); st.update("elim literals", m_elim_literals); } }; z3-z3-4.4.1/src/sat/sat_cleaner.h000066400000000000000000000015721260446376700164410ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_cleaner.h Abstract: Eliminate satisfied clauses, and literals assigned to false. Author: Leonardo de Moura (leonardo) 2011-05-24. Revision History: --*/ #ifndef SAT_CLEANER_H_ #define SAT_CLEANER_H_ #include"sat_types.h" #include"statistics.h" namespace sat { class cleaner { struct report; solver & s; unsigned m_last_num_units; int m_cleanup_counter; // stats unsigned m_elim_clauses; unsigned m_elim_literals; void cleanup_watches(); void cleanup_clauses(clause_vector & cs); public: cleaner(solver & s); bool operator()(bool force = false); void collect_statistics(statistics & st) const; void reset_statistics(); void dec() { m_cleanup_counter--; } }; }; #endif z3-z3-4.4.1/src/sat/sat_config.cpp000066400000000000000000000067411260446376700166330ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_config.cpp Abstract: SAT configuration options Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #include"sat_config.h" #include"sat_types.h" #include"sat_params.hpp" namespace sat { config::config(params_ref const & p): m_always_true("always_true"), m_always_false("always_false"), m_caching("caching"), m_random("random"), m_geometric("geometric"), m_luby("luby"), m_dyn_psm("dyn_psm"), m_psm("psm"), m_glue("glue"), m_glue_psm("glue_psm"), m_psm_glue("psm_glue") { updt_params(p); } void config::updt_params(params_ref const & _p) { sat_params p(_p); m_max_memory = megabytes_to_bytes(p.max_memory()); symbol s = p.restart(); if (s == m_luby) m_restart = RS_LUBY; else if (s == m_geometric) m_restart = RS_GEOMETRIC; else throw sat_param_exception("invalid restart strategy"); s = p.phase(); if (s == m_always_false) m_phase = PS_ALWAYS_FALSE; else if (s == m_always_true) m_phase = PS_ALWAYS_TRUE; else if (s == m_caching) m_phase = PS_CACHING; else if (s == m_random) m_phase = PS_RANDOM; else throw sat_param_exception("invalid phase selection strategy"); m_phase_caching_on = p.phase_caching_on(); m_phase_caching_off = p.phase_caching_off(); m_restart_initial = p.restart_initial(); m_restart_factor = p.restart_factor(); m_random_freq = p.random_freq(); m_random_seed = p.random_seed(); if (m_random_seed == 0) m_random_seed = _p.get_uint("random_seed", 0); m_burst_search = p.burst_search(); m_max_conflicts = p.max_conflicts(); // These parameters are not exposed m_simplify_mult1 = _p.get_uint("simplify_mult1", 300); m_simplify_mult2 = _p.get_double("simplify_mult2", 1.5); m_simplify_max = _p.get_uint("simplify_max", 500000); // -------------------------------- s = p.gc(); if (s == m_dyn_psm) { m_gc_strategy = GC_DYN_PSM; m_gc_initial = p.gc_initial(); m_gc_increment = p.gc_increment(); m_gc_small_lbd = p.gc_small_lbd(); m_gc_k = p.gc_k(); if (m_gc_k > 255) m_gc_k = 255; } else { if (s == m_glue_psm) m_gc_strategy = GC_GLUE_PSM; else if (s == m_glue) m_gc_strategy = GC_GLUE; else if (s == m_psm) m_gc_strategy = GC_PSM; else if (s == m_psm_glue) m_gc_strategy = GC_PSM_GLUE; else throw sat_param_exception("invalid gc strategy"); m_gc_initial = p.gc_initial(); m_gc_increment = p.gc_increment(); } m_minimize_lemmas = p.minimize_lemmas(); m_minimize_core = p.minimize_core(); m_minimize_core_partial = p.minimize_core_partial(); m_optimize_model = p.optimize_model(); m_bcd = p.bcd(); m_dyn_sub_res = p.dyn_sub_res(); } void config::collect_param_descrs(param_descrs & r) { sat_params::collect_param_descrs(r); } }; z3-z3-4.4.1/src/sat/sat_config.h000066400000000000000000000045451260446376700163000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_config.h Abstract: SAT main configuration options. Sub-components have their own options. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_CONFIG_H_ #define SAT_CONFIG_H_ #include"params.h" namespace sat { enum phase_selection { PS_ALWAYS_TRUE, PS_ALWAYS_FALSE, PS_CACHING, PS_RANDOM }; enum restart_strategy { RS_GEOMETRIC, RS_LUBY }; enum gc_strategy { GC_DYN_PSM, GC_PSM, GC_GLUE, GC_GLUE_PSM, GC_PSM_GLUE }; struct config { unsigned long long m_max_memory; phase_selection m_phase; unsigned m_phase_caching_on; unsigned m_phase_caching_off; restart_strategy m_restart; unsigned m_restart_initial; double m_restart_factor; // for geometric case double m_random_freq; unsigned m_random_seed; unsigned m_burst_search; unsigned m_max_conflicts; unsigned m_simplify_mult1; double m_simplify_mult2; unsigned m_simplify_max; gc_strategy m_gc_strategy; unsigned m_gc_initial; unsigned m_gc_increment; unsigned m_gc_small_lbd; unsigned m_gc_k; bool m_minimize_lemmas; bool m_dyn_sub_res; bool m_minimize_core; bool m_minimize_core_partial; bool m_optimize_model; bool m_bcd; symbol m_always_true; symbol m_always_false; symbol m_caching; symbol m_random; symbol m_geometric; symbol m_luby; symbol m_dyn_psm; symbol m_psm; symbol m_glue; symbol m_glue_psm; symbol m_psm_glue; config(params_ref const & p); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); }; }; #endif z3-z3-4.4.1/src/sat/sat_elim_eqs.cpp000066400000000000000000000173661260446376700171710ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_elim_eqs.cpp Abstract: Helper class for eliminating eqs. Author: Leonardo de Moura (leonardo) 2011-05-27. Revision History: --*/ #include"sat_elim_eqs.h" #include"sat_solver.h" #include"trace.h" namespace sat { elim_eqs::elim_eqs(solver & s): m_solver(s) { } inline literal norm(literal_vector const & roots, literal l) { if (l.sign()) return ~roots[l.var()]; else return roots[l.var()]; } void elim_eqs::cleanup_bin_watches(literal_vector const & roots) { vector::iterator it = m_solver.m_watches.begin(); vector::iterator end = m_solver.m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { watch_list & wlist = *it; literal l1 = ~to_literal(l_idx); literal r1 = norm(roots, l1); watch_list::iterator it2 = wlist.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_clause()) { literal l2 = it2->get_literal(); literal r2 = norm(roots, l2); if (r1 == r2) { m_solver.assign(r1, justification()); if (m_solver.inconsistent()) return; // consume unit continue; } if (r1 == ~r2) { // consume tautology continue; } if (l1 != r1) { // add half r1 => r2, the other half ~r2 => ~r1 is added when traversing l2 m_solver.m_watches[(~r1).index()].push_back(watched(r2, it2->is_learned())); continue; } it2->set_literal(r2); // keep it } *itprev = *it2; itprev++; } wlist.set_end(itprev); } } void elim_eqs::cleanup_clauses(literal_vector const & roots, clause_vector & cs) { clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); TRACE("elim_eqs", tout << "processing: " << c << "\n";); unsigned sz = c.size(); unsigned i; for (i = 0; i < sz; i++) { literal l = c[i]; literal r = norm(roots, l); if (l != r) break; } if (i == sz) { // clause was not affected *it2 = *it; it2++; continue; } if (!c.frozen()) m_solver.dettach_clause(c); // apply substitution for (i = 0; i < sz; i++) { SASSERT(!m_solver.was_eliminated(c[i].var())); c[i] = norm(roots, c[i]); } std::sort(c.begin(), c.end()); TRACE("elim_eqs", tout << "after normalization/sorting: " << c << "\n";); // remove duplicates, and check if it is a tautology literal l_prev = null_literal; unsigned j = 0; for (i = 0; i < sz; i++) { literal l = c[i]; if (l == l_prev) continue; if (l == ~l_prev) break; l_prev = l; lbool val = m_solver.value(l); if (val == l_true) break; // clause was satisfied if (val == l_false) continue; // skip c[j] = l; j++; } if (i < sz) { // clause is a tautology or was simplified m_solver.del_clause(c); continue; } if (j == 0) { // empty clause m_solver.set_conflict(justification()); for (; it != end; ++it) { *it2 = *it; it2++; } cs.set_end(it2); return; } TRACE("elim_eqs", tout << "after removing duplicates: " << c << " j: " << j << "\n";); if (j < sz) c.shrink(j); else c.update_approx(); SASSERT(c.size() == j); DEBUG_CODE({ for (unsigned i = 0; i < c.size(); i++) { SASSERT(c[i] == norm(roots, c[i])); } }); SASSERT(j >= 1); switch (j) { case 1: m_solver.assign(c[0], justification()); m_solver.del_clause(c); break; case 2: m_solver.mk_bin_clause(c[0], c[1], c.is_learned()); m_solver.del_clause(c); break; default: SASSERT(*it == &c); *it2 = *it; it2++; if (!c.frozen()) m_solver.attach_clause(c); break; } } cs.set_end(it2); } void elim_eqs::save_elim(literal_vector const & roots, bool_var_vector const & to_elim) { model_converter & mc = m_solver.m_mc; bool_var_vector::const_iterator it = to_elim.begin(); bool_var_vector::const_iterator end = to_elim.end(); for (; it != end; ++it) { bool_var v = *it; literal l(v, false); literal r = roots[v]; SASSERT(v != r.var()); if (m_solver.is_external(v)) { // cannot really eliminate v, since we have to notify extension of future assignments m_solver.mk_bin_clause(~l, r, false); m_solver.mk_bin_clause(l, ~r, false); } else { model_converter::entry & e = mc.mk(model_converter::ELIM_VAR, v); TRACE("save_elim", tout << "marking as deleted: " << v << " l: " << l << " r: " << r << "\n";); m_solver.m_eliminated[v] = true; mc.insert(e, ~l, r); mc.insert(e, l, ~r); } } } bool elim_eqs::check_clauses(literal_vector const & roots) const { clause_vector * vs[2] = { &m_solver.m_clauses, &m_solver.m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector & cs = *(vs[i]); clause_vector::iterator it = cs.begin(); clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { CTRACE("elim_eqs_bug", m_solver.was_eliminated(c[i].var()), tout << "lit: " << c[i] << " " << norm(roots, c[i]) << "\n"; tout << c << "\n";); SASSERT(!m_solver.was_eliminated(c[i].var())); } } } return true; } void elim_eqs::operator()(literal_vector const & roots, bool_var_vector const & to_elim) { cleanup_bin_watches(roots); TRACE("elim_eqs", tout << "after bin cleanup\n"; m_solver.display(tout);); cleanup_clauses(roots, m_solver.m_clauses); if (m_solver.inconsistent()) return; cleanup_clauses(roots, m_solver.m_learned); if (m_solver.inconsistent()) return; save_elim(roots, to_elim); m_solver.propagate(false); SASSERT(check_clauses(roots)); } }; z3-z3-4.4.1/src/sat/sat_elim_eqs.h000066400000000000000000000014551260446376700166260ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_elim_eqs.h Abstract: Helper class for eliminating eqs. Author: Leonardo de Moura (leonardo) 2011-05-27. Revision History: --*/ #ifndef SAT_ELIM_EQS_H_ #define SAT_ELIM_EQS_H_ #include"sat_types.h" namespace sat { class solver; class elim_eqs { solver & m_solver; void save_elim(literal_vector const & roots, bool_var_vector const & to_elim); void cleanup_clauses(literal_vector const & roots, clause_vector & cs); void cleanup_bin_watches(literal_vector const & roots); bool check_clauses(literal_vector const & roots) const; public: elim_eqs(solver & s); void operator()(literal_vector const & roots, bool_var_vector const & to_elim); }; }; #endif z3-z3-4.4.1/src/sat/sat_extension.h000066400000000000000000000016371260446376700170460ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_extension.h Abstract: An abstract class for SAT extensions. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_EXTENSION_H_ #define SAT_EXTENSION_H_ #include"sat_types.h" #include"params.h" namespace sat { enum check_result { CR_DONE, CR_CONTINUE, CR_GIVEUP }; class extension { public: virtual void propagate(literal l, ext_constraint_idx idx, bool & keep) = 0; virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) = 0; virtual void asserted(literal l) = 0; virtual check_result check() = 0; virtual void push() = 0; virtual void pop(unsigned n) = 0; virtual void simplify() = 0; virtual void clauses_modifed() = 0; virtual lbool get_phase(bool_var v) = 0; }; }; #endif z3-z3-4.4.1/src/sat/sat_iff3_finder.cpp000066400000000000000000000167401260446376700175440ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_iff3_finder.cpp Abstract: Find constraints of the form x = l1 = l2 That is, search for clauses of the form ~x \/ l1 \/ ~l2 ~x \/ ~l1 \/ l2 x \/ l1 \/ l2 x \/ ~l1 \/ ~l2 The basic idea is to sort the watch lists. This information can be used to propagate equivalences during probing (and search). The initial experiments were disappointing. Not using it on the solver. Author: Leonardo de Moura (leonardo) 2011-06-04. Revision History: --*/ #include"sat_iff3_finder.h" #include"sat_solver.h" namespace sat { struct iff3_lt { bool operator()(watched const & w1, watched const & w2) const { // keep th binary clauses in the beginning if (w2.is_binary_clause()) return false; if (w1.is_binary_clause()) return true; // if (w2.is_ternary_clause()) { if (w1.is_ternary_clause()) { literal l1_1 = w1.get_literal1(); literal l1_2 = w1.get_literal2(); literal l2_1 = w2.get_literal1(); literal l2_2 = w2.get_literal2(); if (l1_1.index() < l2_1.index()) return true; if (l1_1.index() > l2_1.index()) return false; return l1_2.index() < l2_2.index(); } return false; } if (w1.is_ternary_clause()) return true; return false; } }; static void unmark(svector & marks, literal_vector & to_unmark) { literal_vector::const_iterator it = to_unmark.begin(); literal_vector::const_iterator end = to_unmark.end(); for (; it != end; ++it) { marks[it->index()] = false; } to_unmark.reset(); } #define SMALL_WLIST 16 /** \brief Return true if wlist contains (l1, l2) It assumes wlist have been sorted using iff3_lt */ static bool contains(watch_list const & wlist, literal l1, literal l2) { watched k(l1, l2); if (wlist.size() < SMALL_WLIST) return wlist.contains(k); iff3_lt lt; int low = 0; int high = wlist.size(); while (true) { int mid = low + ((high - low) / 2); watched const & m = wlist[mid]; if (m == k) return true; if (lt(m, k)) { low = mid + 1; } else { SASSERT(lt(k, m)); high = mid - 1; } if (low > high) return false; } } iff3_finder::iff3_finder(solver & _s): s(_s) { } void iff3_finder::sort_watches() { vector::iterator it = s.m_watches.begin(); vector::iterator end = s.m_watches.end(); for (; it != end; ++it) { watch_list & wlist = *it; std::stable_sort(wlist.begin(), wlist.end(), iff3_lt()); } } void iff3_finder::mk_eq(literal l1, literal l2) { s.mk_clause(l1, ~l2); s.mk_clause(~l1, l2); } void iff3_finder::operator()() { TRACE("iff3_finder", tout << "starting iff3_finder\n";); sort_watches(); unsigned counter = 0; svector found; found.resize(s.num_vars()*2, false); literal_vector to_unmark; typedef std::pair lit_pair; svector pairs; for (bool_var x = 0; x < s.num_vars(); x++) { literal pos_x(x, false); literal neg_x(x, true); watch_list & pos_wlist = s.get_wlist(neg_x); watch_list & neg_wlist = s.get_wlist(pos_x); // TRACE("iff3_finder", tout << "visiting: " << x << "\n"; tout << "pos:\n"; display(tout, s.m_cls_allocator, pos_wlist); tout << "\nneg:\n"; display(tout, s.m_cls_allocator, neg_wlist); tout << "\n--------------\n";); // traverse the ternary clauses x \/ l1 \/ l2 bool_var curr_v1 = null_bool_var; watch_list::iterator it = pos_wlist.begin(); watch_list::iterator end = pos_wlist.end(); for (; it != end; ++it) { if (it->is_binary_clause()) continue; if (it->is_ternary_clause()) { literal l1 = it->get_literal1(); if (l1.index() < pos_x.index()) break; // stop literal l2 = it->get_literal2(); bool_var v1 = l1.var(); if (v1 != curr_v1) { curr_v1 = v1; unmark(found, to_unmark); pairs.reset(); } if (!l1.sign()) { if (!found[l2.index()]) { found[l2.index()] = true; to_unmark.push_back(l2); } } else { l2.neg(); if (found[l2.index()]) { // Found clauses x \/ v1 \/ l2 and x \/ ~v1 \/ ~l2 // So, I have to find the clauses // ~x \/ v1 \/ ~l2 // ~x \/ ~v1 \/ l2 if (contains(neg_wlist, literal(v1, false), ~l2) && contains(neg_wlist, literal(v1, true), l2)) { // found new iff3 // x = v1 = l2 counter++; // verbose_stream() << counter << ": " << x << " = " << v1 << " = " << l2 << "\n"; TRACE("iff3_finder", tout << counter << ": " << x << " = " << v1 << " = " << l2 << "\n";); l1.neg(); svector::iterator it2 = pairs.begin(); svector::iterator end2 = pairs.end(); for (; it2 != end2; ++it2) { if (it2->first == l1) { // l2 == it2->second mk_eq(l2, it2->second); } else if (it2->second == l1) { // l2 == it2->first mk_eq(l2, it2->first); } else if (it2->first == l2) { // l1 == it2->second mk_eq(l1, it2->second); } else if (it2->second == l2) { // l1 == it2->first mk_eq(l1, it2->first); } } pairs.push_back(lit_pair(l1, l2)); } } } } else { break; // stop, no more ternary clauses from this point } } } } }; z3-z3-4.4.1/src/sat/sat_iff3_finder.h000066400000000000000000000014701260446376700172030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_iff3_finder.h Abstract: Find constraints of the form x = l1 = l2 That is, search for clauses of the form ~x \/ l1 \/ ~l2 ~x \/ ~l1 \/ l2 x \/ l1 \/ l2 x \/ ~l1 \/ ~l2 The basic idea is to sort the watch lists. This information can be used to propagate equivalences during probing (and search). Author: Leonardo de Moura (leonardo) 2011-06-04. Revision History: --*/ #ifndef SAT_IFF3_FINDER_H_ #define SAT_IFF3_FINDER_H_ #include"sat_types.h" namespace sat { class iff3_finder { solver & s; void sort_watches(); void mk_eq(literal l1, literal l2); public: iff3_finder(solver & s); void operator()(); }; }; #endif z3-z3-4.4.1/src/sat/sat_integrity_checker.cpp000066400000000000000000000222331260446376700210620ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_integrity_checker.cpp Abstract: Checker whether the SAT solver internal datastructures are consistent or not. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #include"sat_integrity_checker.h" #include"sat_solver.h" #include"trace.h" namespace sat { integrity_checker::integrity_checker(solver const & _s): s(_s) { } // for ternary clauses static bool contains_watched(watch_list const & wlist, literal l1, literal l2) { return wlist.contains(watched(l1, l2)); } // for nary clauses static bool contains_watched(watch_list const & wlist, clause const & c, clause_offset cls_off) { watch_list::const_iterator it = wlist.begin(); watch_list::const_iterator end = wlist.end(); for (; it != end; ++it) { if (it->is_clause()) { if (it->get_clause_offset() == cls_off) { // the blocked literal must be in the clause. literal l = it->get_blocked_literal(); SASSERT(c.contains(l)); return true; } } } UNREACHABLE(); return false; } bool integrity_checker::check_clause(clause const & c) const { SASSERT(!c.was_removed()); for (unsigned i = 0; i < c.size(); i++) { SASSERT(c[i].var() <= s.num_vars()); CTRACE("sat_bug", s.was_eliminated(c[i].var()), tout << "l: " << c[i].var() << "\n"; tout << "c: " << c << "\n"; s.display(tout);); SASSERT(!s.was_eliminated(c[i].var())); } SASSERT(c.check_approx()); if (c.frozen()) return true; if (c.size() == 3) { CTRACE("sat_ter_watch_bug", !contains_watched(s.get_wlist(~c[0]), c[1], c[2]), tout << c << "\n"; tout << "watch_list:\n"; sat::display(tout, s.m_cls_allocator, s.get_wlist(~c[0])); tout << "\n";); SASSERT(contains_watched(s.get_wlist(~c[0]), c[1], c[2])); SASSERT(contains_watched(s.get_wlist(~c[1]), c[0], c[2])); SASSERT(contains_watched(s.get_wlist(~c[2]), c[0], c[1])); } else { if (s.value(c[0]) == l_false || s.value(c[1]) == l_false) { bool on_prop_stack = false; for (unsigned i = s.m_qhead; i < s.m_trail.size(); i++) { if (s.m_trail[i].var() == c[0].var() || s.m_trail[i].var() == c[1].var()) { on_prop_stack = true; break; } } // the clause has been satisfied or all other literals are assigned to false. if (!on_prop_stack && s.status(c) != l_true) { for (unsigned i = 2; i < c.size(); i++) { CTRACE("sat_bug", s.value(c[i]) != l_false, tout << c << " status: " << s.status(c) << "\n"; for (unsigned i = 0; i < c.size(); i++) tout << "val(" << i << "): " << s.value(c[i]) << "\n";); SASSERT(s.value(c[i]) == l_false); } } } // the first two literals must be watched. SASSERT(contains_watched(s.get_wlist(~c[0]), c, s.get_offset(c))); SASSERT(contains_watched(s.get_wlist(~c[1]), c, s.get_offset(c))); } return true; } bool integrity_checker::check_clauses(clause * const * begin, clause * const * end) const { for (clause * const * it = begin; it != end; ++it) { SASSERT(check_clause(*(*it))); } return true; } bool integrity_checker::check_clauses() const { return check_clauses(s.begin_clauses(), s.end_clauses()); } bool integrity_checker::check_learned_clauses() const { unsigned num_frozen = 0; clause * const * end = s.end_clauses(); for (clause * const * it = s.begin_clauses(); it != end; ++it) { clause & c = *(*it); if (c.frozen()) num_frozen++; } SASSERT(num_frozen == s.m_num_frozen); return check_clauses(s.begin_learned(), s.end_learned()); } bool integrity_checker::check_assignment() const { return true; } bool integrity_checker::check_bool_vars() const { SASSERT(s.m_watches.size() == s.num_vars() * 2); SASSERT(s.m_assignment.size() == s.num_vars() * 2); SASSERT(s.m_lit_mark.size() == s.num_vars() * 2); SASSERT(s.m_justification.size() == s.num_vars()); SASSERT(s.m_decision.size() == s.num_vars()); SASSERT(s.m_eliminated.size() == s.num_vars()); SASSERT(s.m_external.size() == s.num_vars()); SASSERT(s.m_level.size() == s.num_vars()); SASSERT(s.m_mark.size() == s.num_vars()); SASSERT(s.m_activity.size() == s.num_vars()); SASSERT(s.m_phase.size() == s.num_vars()); SASSERT(s.m_prev_phase.size() == s.num_vars()); SASSERT(s.m_assigned_since_gc.size() == s.num_vars()); for (bool_var v = 0; v < s.num_vars(); v++) { if (s.was_eliminated(v)) { SASSERT(s.get_wlist(literal(v, false)).empty()); SASSERT(s.get_wlist(literal(v, true)).empty()); } } return true; } bool integrity_checker::check_watches() const { vector::const_iterator it = s.m_watches.begin(); vector::const_iterator end = s.m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l = ~to_literal(l_idx); watch_list const & wlist = *it; CTRACE("sat_bug", s.was_eliminated(l.var()) && !wlist.empty(), tout << "l: " << l << "\n"; s.display_watches(tout); s.display(tout);); SASSERT(!s.was_eliminated(l.var()) || wlist.empty()); watch_list::const_iterator it2 = wlist.begin(); watch_list::const_iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { switch (it2->get_kind()) { case watched::BINARY: SASSERT(!s.was_eliminated(it2->get_literal().var())); CTRACE("sat_watched_bug", !s.get_wlist(~(it2->get_literal())).contains(watched(l, it2->is_learned())), tout << "l: " << l << " l2: " << it2->get_literal() << "\n"; tout << "was_eliminated1: " << s.was_eliminated(l.var()); tout << " was_eliminated2: " << s.was_eliminated(it2->get_literal().var()); tout << " learned: " << it2->is_learned() << "\n"; sat::display(tout, s.m_cls_allocator, wlist); tout << "\n"; sat::display(tout, s.m_cls_allocator, s.get_wlist(~(it2->get_literal()))); tout << "\n";); SASSERT(s.get_wlist(~(it2->get_literal())).contains(watched(l, it2->is_learned()))); break; case watched::TERNARY: SASSERT(!s.was_eliminated(it2->get_literal1().var())); SASSERT(!s.was_eliminated(it2->get_literal2().var())); SASSERT(it2->get_literal1().index() < it2->get_literal2().index()); break; case watched::CLAUSE: SASSERT(!s.m_cls_allocator.get_clause(it2->get_clause_offset())->was_removed()); break; default: break; } } } return true; } bool integrity_checker::check_reinit_stack() const { clause_wrapper_vector::const_iterator it = s.m_clauses_to_reinit.begin(); clause_wrapper_vector::const_iterator end = s.m_clauses_to_reinit.end(); for (; it != end; ++it) { if (it->is_binary()) continue; SASSERT(it->get_clause()->on_reinit_stack()); } return true; } bool integrity_checker::check_disjoint_clauses() const { uint_set ids; clause_vector::const_iterator it = s.m_clauses.begin(); clause_vector::const_iterator end = s.m_clauses.end(); for (; it != end; ++it) { ids.insert((*it)->id()); } it = s.m_learned.begin(); end = s.m_learned.end(); for (; it != end; ++it) { if (ids.contains((*it)->id())) { TRACE("sat", tout << "Repeated clause: " << (*it)->id() << "\n";); return false; } } return true; } bool integrity_checker::operator()() const { if (s.inconsistent()) return true; SASSERT(check_clauses()); SASSERT(check_learned_clauses()); SASSERT(check_watches()); SASSERT(check_bool_vars()); SASSERT(check_reinit_stack()); SASSERT(check_disjoint_clauses()); return true; } }; z3-z3-4.4.1/src/sat/sat_integrity_checker.h000066400000000000000000000016741260446376700205350ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_integrity_checker.h Abstract: Checker whether the SAT solver internal datastructures are consistent or not. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_INTEGRITY_CHECKER_H_ #define SAT_INTEGRITY_CHECKER_H_ #include"sat_types.h" namespace sat { class integrity_checker { solver const & s; public: integrity_checker(solver const & s); bool check_clause(clause const & c) const; bool check_clauses(clause * const * begin, clause * const * end) const; bool check_clauses() const; bool check_learned_clauses() const; bool check_assignment() const; bool check_bool_vars() const; bool check_watches() const; bool check_reinit_stack() const; bool check_disjoint_clauses() const; bool operator()() const; }; }; #endif z3-z3-4.4.1/src/sat/sat_justification.h000066400000000000000000000050261260446376700177010ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_justification.h Abstract: Justifications for SAT assignments Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_JUSTIFICATIONS_H_ #define SAT_JUSTIFICATIONS_H_ namespace sat { class justification { public: enum kind { NONE, BINARY, TERNARY, CLAUSE, EXT_JUSTIFICATION }; private: unsigned m_val1; unsigned m_val2; justification(ext_justification_idx idx, kind k):m_val1(idx), m_val2(k) {} public: justification():m_val1(0), m_val2(NONE) {} explicit justification(literal l):m_val1(l.to_uint()), m_val2(BINARY) {} justification(literal l1, literal l2):m_val1(l1.to_uint()), m_val2(TERNARY + (l2.to_uint() << 3)) {} explicit justification(clause_offset cls_off):m_val1(cls_off), m_val2(CLAUSE) {} justification mk_ext_justification(ext_justification_idx idx) { return justification(idx, EXT_JUSTIFICATION); } kind get_kind() const { return static_cast(m_val2 & 7); } bool is_none() const { return m_val2 == NONE; } bool is_binary_clause() const { return m_val2 == BINARY; } literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(m_val1); } bool is_ternary_clause() const { return get_kind() == TERNARY; } literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(m_val1); } literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 3); } bool is_clause() const { return m_val2 == CLAUSE; } clause_offset get_clause_offset() const { return m_val1; } bool is_ext_justification() const { return m_val2 == EXT_JUSTIFICATION; } ext_justification_idx get_ext_justification_idx() const { return m_val1; } }; inline std::ostream & operator<<(std::ostream & out, justification const & j) { switch (j.get_kind()) { case justification::NONE: out << "none"; break; case justification::BINARY: out << "binary " << j.get_literal(); break; case justification::TERNARY: out << "ternary " << j.get_literal1() << " " << j.get_literal2(); break; case justification::CLAUSE: out << "clause"; break; case justification::EXT_JUSTIFICATION: out << "external"; break; } return out; } }; #endif z3-z3-4.4.1/src/sat/sat_model_converter.cpp000066400000000000000000000205041260446376700205460ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_model_converter.cpp Abstract: Low level model converter for SAT solver. Author: Leonardo de Moura (leonardo) 2011-05-26. Revision History: --*/ #include"sat_model_converter.h" #include"sat_clause.h" #include"trace.h" namespace sat { model_converter::model_converter() { } model_converter::~model_converter() { reset(); } void model_converter::reset() { m_entries.finalize(); } void model_converter::operator()(model & m) const { vector::const_iterator begin = m_entries.begin(); vector::const_iterator it = m_entries.end(); while (it != begin) { --it; SASSERT(it->get_kind() != ELIM_VAR || m[it->var()] == l_undef); // if it->get_kind() == BLOCK_LIT, then it might be the case that m[it->var()] != l_undef, // and the following procedure flips its value. bool sat = false; bool var_sign; literal_vector::const_iterator it2 = it->m_clauses.begin(); literal_vector::const_iterator end2 = it->m_clauses.end(); for (; it2 != end2; ++it2) { literal l = *it2; if (l == null_literal) { // end of clause if (!sat) { m[it->var()] = var_sign ? l_false : l_true; break; } sat = false; continue; } if (sat) continue; bool sign = l.sign(); bool_var v = l.var(); if (v == it->var()) var_sign = sign; if (value_at(l, m) == l_true) sat = true; else if (!sat && v != it->var() && m[v] == l_undef) { // clause can be satisfied by assigning v. m[v] = sign ? l_false : l_true; sat = true; } } DEBUG_CODE({ // all clauses must be satisfied bool sat = false; it2 = it->m_clauses.begin(); for (; it2 != end2; ++it2) { literal l = *it2; if (l == null_literal) { SASSERT(sat); sat = false; continue; } if (sat) continue; if (value_at(l, m) == l_true) sat = true; } }); } } /** \brief Test if after applying the model converter, all eliminated clauses are satisfied by m. */ bool model_converter::check_model(model const & m) const { bool ok = true; vector::const_iterator begin = m_entries.begin(); vector::const_iterator it = m_entries.end(); while (it != begin) { --it; bool sat = false; literal_vector::const_iterator it2 = it->m_clauses.begin(); literal_vector::const_iterator itbegin = it2; literal_vector::const_iterator end2 = it->m_clauses.end(); for (; it2 != end2; ++it2) { literal l = *it2; if (l == null_literal) { // end of clause if (!sat) { TRACE("sat_model_bug", tout << "failed eliminated: " << mk_lits_pp(static_cast(it2 - itbegin), itbegin) << "\n";); ok = false; } sat = false; itbegin = it2; itbegin++; continue; } if (sat) continue; if (value_at(l, m) == l_true) sat = true; } } return ok; } model_converter::entry & model_converter::mk(kind k, bool_var v) { m_entries.push_back(entry(k, v)); entry & e = m_entries.back(); SASSERT(e.var() == v); SASSERT(e.get_kind() == k); return e; } void model_converter::insert(entry & e, clause const & c) { SASSERT(c.contains(e.var())); SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { e.m_clauses.push_back(c[i]); } e.m_clauses.push_back(null_literal); TRACE("sat_mc_bug", tout << "adding: " << c << "\n";); } void model_converter::insert(entry & e, literal l1, literal l2) { SASSERT(l1.var() == e.var() || l2.var() == e.var()); SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); e.m_clauses.push_back(l1); e.m_clauses.push_back(l2); e.m_clauses.push_back(null_literal); TRACE("sat_mc_bug", tout << "adding (binary): " << l1 << " " << l2 << "\n";); } void model_converter::insert(entry & e, clause_wrapper const & c) { SASSERT(c.contains(e.var())); SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) e.m_clauses.push_back(c[i]); e.m_clauses.push_back(null_literal); TRACE("sat_mc_bug", tout << "adding (wrapper): "; for (unsigned i = 0; i < c.size(); i++) tout << c[i] << " "; tout << "\n";); } bool model_converter::check_invariant(unsigned num_vars) const { // After a variable v occurs in an entry n and the entry has kind ELIM_VAR, // then the variable must not occur in any other entry occurring after it. vector::const_iterator it = m_entries.begin(); vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { SASSERT(it->var() < num_vars); if (it->get_kind() == ELIM_VAR) { svector::const_iterator it2 = it; it2++; for (; it2 != end; ++it2) { SASSERT(it2->var() != it->var()); literal_vector::const_iterator it3 = it2->m_clauses.begin(); literal_vector::const_iterator end3 = it2->m_clauses.end(); for (; it3 != end3; ++it3) { CTRACE("sat_model_converter", it3->var() == it->var(), tout << "var: " << it->var() << "\n"; display(tout);); SASSERT(it3->var() != it->var()); SASSERT(*it3 == null_literal || it3->var() < num_vars); } } } } return true; } void model_converter::display(std::ostream & out) const { out << "(sat::model-converter"; vector::const_iterator it = m_entries.begin(); vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { out << "\n (" << (it->get_kind() == ELIM_VAR ? "elim" : "blocked") << " " << it->var(); bool start = true; literal_vector::const_iterator it2 = it->m_clauses.begin(); literal_vector::const_iterator end2 = it->m_clauses.end(); for (; it2 != end2; ++it2) { if (start) { out << "\n ("; start = false; } else { if (*it2 != null_literal) out << " "; } if (*it2 == null_literal) { out << ")"; start = true; continue; } out << *it2; } out << ")"; } out << ")\n"; } void model_converter::copy(model_converter const & src) { vector::const_iterator it = src.m_entries.begin(); vector::const_iterator end = src.m_entries.end(); for (; it != end; ++it) m_entries.push_back(*it); } void model_converter::collect_vars(bool_var_set & s) const { vector::const_iterator it = m_entries.begin(); vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { s.insert(it->m_var); } } }; z3-z3-4.4.1/src/sat/sat_model_converter.h000066400000000000000000000044161260446376700202170ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_model_converter.h Abstract: Low level model converter for SAT solver. Author: Leonardo de Moura (leonardo) 2011-05-26. Revision History: --*/ #ifndef SAT_MODEL_CONVERTER_H_ #define SAT_MODEL_CONVERTER_H_ #include"sat_types.h" namespace sat { /** \brief Stores eliminated variables and Blocked clauses. It uses these clauses to extend/patch the model produced for the simplified CNF formula. This information may also be used to support incremental solving. If new clauses are asserted into the SAT engine, then we can restore the state by re-asserting all clauses in the model converter. This is a low-level model_converter. Given a mapping from bool_var to expr, it can be converted into a general Z3 model_converter */ class model_converter { public: enum kind { ELIM_VAR = 0, BLOCK_LIT }; class entry { friend class model_converter; unsigned m_var:31; unsigned m_kind:1; literal_vector m_clauses; // the different clauses are separated by null_literal entry(kind k, bool_var v):m_var(v), m_kind(k) {} public: entry(entry const & src): m_var(src.m_var), m_kind(src.m_kind), m_clauses(src.m_clauses) { } bool_var var() const { return m_var; } kind get_kind() const { return static_cast(m_kind); } }; private: vector m_entries; public: model_converter(); ~model_converter(); void operator()(model & m) const; entry & mk(kind k, bool_var v); void insert(entry & e, clause const & c); void insert(entry & e, literal l1, literal l2); void insert(entry & e, clause_wrapper const & c); bool empty() const { return m_entries.empty(); } void reset(); bool check_invariant(unsigned num_vars) const; void display(std::ostream & out) const; bool check_model(model const & m) const; void copy(model_converter const & src); void collect_vars(bool_var_set & s) const; }; }; #endif z3-z3-4.4.1/src/sat/sat_mus.cpp000066400000000000000000000214311260446376700161630ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sat_mus.cpp Abstract: Faster MUS extraction Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #include "sat_solver.h" #include "sat_mus.h" #include "sat_sls.h" namespace sat { mus::mus(solver& s):s(s), m_is_active(false), m_best_value(0), m_restart(0), m_max_restarts(0) {} mus::~mus() {} void mus::reset() { m_core.reset(); m_mus.reset(); m_model.reset(); m_best_value = 0; m_max_restarts = (s.m_stats.m_restart - m_restart) + 10; m_restart = s.m_stats.m_restart; } void mus::set_core() { m_mus.append(m_core); s.m_core.reset(); s.m_core.append(m_mus); TRACE("sat", tout << "new core: " << s.m_core << "\n";); } void mus::update_model() { double new_value = s.m_wsls.evaluate_model(s.m_model); if (m_model.empty()) { m_model.append(s.m_model); m_best_value = new_value; } else if (m_best_value > new_value) { m_model.reset(); m_model.append(s.m_model); m_best_value = new_value; } } lbool mus::operator()() { flet _disable_min(s.m_config.m_minimize_core, false); flet _disable_opt(s.m_config.m_optimize_model, false); flet _is_active(m_is_active, true); IF_VERBOSE(3, verbose_stream() << "(sat.mus " << s.get_core() << ")\n";); reset(); lbool r = mus1(); m_restart = s.m_stats.m_restart; return r; } lbool mus::mus1() { bool minimize_partial = s.m_config.m_minimize_core_partial; TRACE("sat", tout << "old core: " << s.get_core() << "\n";); literal_vector& core = get_core(); literal_vector& mus = m_mus; if (core.size() > 64) { return mus2(); } unsigned delta_time = 0; unsigned core_miss = 0; while (!core.empty()) { IF_VERBOSE(3, verbose_stream() << "(opt.mus reducing core: " << core.size() << " mus: " << mus.size() << ")\n";); TRACE("sat", tout << "core: " << core << "\n"; tout << "mus: " << mus << "\n";); if (s.m_cancel) { set_core(); return l_undef; } if (minimize_partial && 3*delta_time > core.size() && core.size() < mus.size()) { break; } unsigned num_literals = core.size() + mus.size(); if (num_literals <= 2) { // IF_VERBOSE(0, verbose_stream() << "num literals: " << core << " " << mus << "\n";); break; } if (s.m_config.m_minimize_core_partial && s.m_stats.m_restart - m_restart > m_max_restarts) { IF_VERBOSE(1, verbose_stream() << "restart budget exceeded\n";); set_core(); return l_true; } literal lit = core.back(); core.pop_back(); lbool is_sat; { scoped_append _sa(mus, core); mus.push_back(~lit); is_sat = s.check(mus.size(), mus.c_ptr()); TRACE("sat", tout << "mus: " << mus << "\n";); } switch (is_sat) { case l_undef: core.push_back(lit); set_core(); return l_undef; case l_true: { SASSERT(value_at(lit, s.get_model()) == l_false); mus.push_back(lit); update_model(); if (!core.empty()) { // mr(); // TBD: measure } break; } case l_false: literal_vector const& new_core = s.get_core(); if (new_core.contains(~lit)) { IF_VERBOSE(3, verbose_stream() << "miss core " << lit << "\n";); ++core_miss; } else { core_miss = 0; TRACE("sat", tout << "core: " << new_core << " mus: " << mus << "\n";); core.reset(); for (unsigned i = 0; i < new_core.size(); ++i) { literal lit = new_core[i]; if (!mus.contains(lit)) { core.push_back(lit); } } } break; } unsigned new_num_literals = core.size() + mus.size(); if (new_num_literals == num_literals) { delta_time++; } else { delta_time = 0; } } set_core(); IF_VERBOSE(3, verbose_stream() << "(sat.mus.new " << s.m_core << ")\n";); return l_true; } // bisection search. lbool mus::mus2() { literal_set core(get_core()); literal_set support; lbool is_sat = qx(core, support, false); s.m_core.reset(); s.m_core.append(core.to_vector()); IF_VERBOSE(3, verbose_stream() << "(sat.mus.new " << s.m_core << ")\n";); return is_sat; } lbool mus::qx(literal_set& assignment, literal_set& support, bool has_support) { lbool is_sat = l_true; if (s.m_config.m_minimize_core_partial && s.m_stats.m_restart - m_restart > m_max_restarts) { IF_VERBOSE(1, verbose_stream() << "restart budget exceeded\n";); return l_true; } if (has_support) { scoped_append _sa(m_mus, support.to_vector()); is_sat = s.check(m_mus.size(), m_mus.c_ptr()); switch (is_sat) { case l_false: { literal_set core(s.get_core()); support &= core; assignment.reset(); return l_true; } case l_undef: return l_undef; case l_true: update_model(); break; default: break; } } if (assignment.size() == 1) { return l_true; } literal_set assign2; split(assignment, assign2); support |= assignment; is_sat = qx(assign2, support, !assignment.empty()); unsplit(support, assignment); if (is_sat != l_true) return is_sat; support |= assign2; is_sat = qx(assignment, support, !assign2.empty()); assignment |= assign2; unsplit(support, assign2); return is_sat; } void mus::unsplit(literal_set& A, literal_set& B) { literal_set A1, B1; literal_set::iterator it = A.begin(), end = A.end(); for (; it != end; ++it) { if (B.contains(*it)) { B1.insert(*it); } else { A1.insert(*it); } } A = A1; B = B1; } void mus::split(literal_set& lits1, literal_set& lits2) { unsigned half = lits1.size()/2; literal_set lits3; literal_set::iterator it = lits1.begin(), end = lits1.end(); for (unsigned i = 0; it != end; ++it, ++i) { if (i < half) { lits3.insert(*it); } else { lits2.insert(*it); } } lits1 = lits3; } literal_vector& mus::get_core() { m_core.reset(); m_mus.reset(); literal_vector& core = m_core; core.append(s.get_core()); for (unsigned i = 0; i < core.size(); ++i) { if (s.m_user_scope_literals.contains(core[i])) { m_mus.push_back(core[i]); core[i] = core.back(); core.pop_back(); --i; } } return core; } void mus::verify_core(literal_vector const& core) { lbool is_sat = s.check(core.size(), core.c_ptr()); IF_VERBOSE(3, verbose_stream() << "core verification: " << is_sat << " " << core << "\n";); } void mus::mr() { sls sls(s); literal_vector tabu; tabu.append(m_mus); tabu.append(m_core); bool reuse_model = false; for (unsigned i = m_mus.size(); i < tabu.size(); ++i) { tabu[i] = ~tabu[i]; lbool is_sat = sls(tabu.size(), tabu.c_ptr(), reuse_model); tabu[i] = ~tabu[i]; if (is_sat == l_true) { m_mus.push_back(tabu[i]); m_core.erase(tabu[i]); IF_VERBOSE(3, verbose_stream() << "in core " << tabu[i] << "\n";); reuse_model = true; } else { IF_VERBOSE(3, verbose_stream() << "NOT in core " << tabu[i] << "\n";); reuse_model = false; } } } } z3-z3-4.4.1/src/sat/sat_mus.h000066400000000000000000000033211260446376700156260ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: mus.h Abstract: Faster MUS extraction based on Belov et.al. HYB (Algorithm 3, 4) Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #ifndef SAT_MUS_H_ #define SAT_MUS_H_ namespace sat { class mus { solver& s; literal_vector m_core; literal_vector m_mus; bool m_is_active; model m_model; // model obtained during minimal unsat core double m_best_value; unsigned m_restart; unsigned m_max_restarts; public: mus(solver& s); ~mus(); lbool operator()(); bool is_active() const { return m_is_active; } model const& get_model() const { return m_model; } private: lbool mus1(); lbool mus2(); lbool qx(literal_set& assignment, literal_set& support, bool has_support); void mr(); void reset(); void set_core(); void update_model(); literal_vector & get_core(); void verify_core(literal_vector const& lits); void split(literal_set& src, literal_set& dst); void intersect(literal_set& dst, literal_set const& src); void unsplit(literal_set& A, literal_set& B); class scoped_append { unsigned m_size; literal_vector& m_lits; public: scoped_append(literal_vector& lits, literal_vector const& other): m_size(lits.size()), m_lits(lits) { m_lits.append(other); } ~scoped_append() { m_lits.resize(m_size); } }; }; }; #endif z3-z3-4.4.1/src/sat/sat_params.pyg000066400000000000000000000046641260446376700166700ustar00rootroot00000000000000def_module_params('sat', export=True, description='propositional SAT solver', params=(max_memory_param(), ('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, caching, random'), ('phase.caching.on', UINT, 400, 'phase caching on period (in number of conflicts)'), ('phase.caching.off', UINT, 100, 'phase caching off period (in number of conflicts)'), ('restart', SYMBOL, 'luby', 'restart strategy: luby or geometric'), ('restart.initial', UINT, 100, 'initial restart (number of conflicts)'), ('restart.factor', DOUBLE, 1.5, 'restart increment factor for geometric strategy'), ('random_freq', DOUBLE, 0.01, 'frequency of random case splits'), ('random_seed', UINT, 0, 'random seed'), ('burst_search', UINT, 100, 'number of conflicts before first global simplification'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts'), ('gc', SYMBOL, 'glue_psm', 'garbage collection strategy: psm, glue, glue_psm, dyn_psm'), ('gc.initial', UINT, 20000, 'learned clauses garbage collection frequence'), ('gc.increment', UINT, 500, 'increment to the garbage collection threshold'), ('gc.small_lbd', UINT, 3, 'learned clauses with small LBD are never deleted (only used in dyn_psm)'), ('gc.k', UINT, 7, 'learned clauses that are inactive for k gc rounds are permanently deleted (only used in dyn_psm)'), ('minimize_lemmas', BOOL, True, 'minimize learned clauses'), ('dyn_sub_res', BOOL, True, 'dynamic subsumption resolution for minimizing learned clauses'), ('minimize_core', BOOL, False, 'minimize computed core'), ('minimize_core_partial', BOOL, False, 'apply partial (cheap) core minimization'), ('optimize_model', BOOL, False, 'enable optimization of soft constraints'), ('bcd', BOOL, False, 'enable blocked clause decomposition for equality extraction'), ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'))) z3-z3-4.4.1/src/sat/sat_probing.cpp000066400000000000000000000212221260446376700170150ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_probing.cpp Abstract: Probing (aka failed literal detection). Author: Leonardo de Moura (leonardo) 2011-06-04. Revision History: --*/ #include"sat_probing.h" #include"sat_solver.h" namespace sat { probing::probing(solver & _s, params_ref const & p): s(_s) { updt_params(p); reset_statistics(); m_stopped_at = 0; m_counter = 0; } // reset the cache for the given literal void probing::reset_cache(literal l) { if (l.index() < m_cached_bins.size()) { m_cached_bins[l.index()].m_available = false; m_cached_bins[l.index()].m_lits.finalize(); } } // l implied the literals on the trail stack starting at position old_tr_sz // Thus, ~l \/ l2 is a binary clause for every l2 on this fragment of the trail stack. void probing::cache_bins(literal l, unsigned old_tr_sz) { if (!m_probing_cache) return; if (memory::get_allocation_size() > m_probing_cache_limit) return; // not enough memory to spare m_cached_bins.reserve(l.index() + 1); cache_entry & entry = m_cached_bins[l.index()]; entry.m_available = true; entry.m_lits.reset(); unsigned tr_sz = s.m_trail.size(); for (unsigned i = old_tr_sz; i < tr_sz; i++) { entry.m_lits.push_back(s.m_trail[i]); } } // Return true if should keep going. // It will assert literals implied by l that are already marked // as assigned. bool probing::try_lit(literal l, bool updt_cache) { SASSERT(s.m_qhead == s.m_trail.size()); SASSERT(s.value(l.var()) == l_undef); literal_vector * implied_lits = updt_cache ? 0 : cached_implied_lits(l); if (implied_lits) { literal_vector::iterator it = implied_lits->begin(); literal_vector::iterator end = implied_lits->end(); for (; it != end; ++it) { if (m_assigned.contains(*it)) { s.assign(*it, justification()); m_num_assigned++; } } } else { m_to_assert.reset(); s.push(); s.assign(l, justification()); m_counter--; unsigned old_tr_sz = s.m_trail.size(); s.propagate(false); if (s.inconsistent()) { // ~l must be true s.pop(1); s.assign(~l, justification()); s.propagate(false); return false; } // collect literals that were assigned after assigning l unsigned tr_sz = s.m_trail.size(); for (unsigned i = old_tr_sz; i < tr_sz; i++) { if (m_assigned.contains(s.m_trail[i])) { m_to_assert.push_back(s.m_trail[i]); } } if (updt_cache) cache_bins(l, old_tr_sz); s.pop(1); literal_vector::iterator it = m_to_assert.begin(); literal_vector::iterator end = m_to_assert.end(); for (; it != end; ++it) { s.assign(*it, justification()); m_num_assigned++; } } s.propagate(false); return !s.inconsistent(); } void probing::process_core(bool_var v) { TRACE("probing", tout << "processing: " << v << " counter: " << -m_counter << "\n";); SASSERT(s.m_qhead == s.m_trail.size()); SASSERT(s.value(v) == l_undef); m_counter--; s.push(); literal l(v, false); s.assign(l, justification()); unsigned old_tr_sz = s.m_trail.size(); s.propagate(false); if (s.inconsistent()) { // ~l must be true s.pop(1); s.assign(~l, justification()); s.propagate(false); m_num_assigned++; return; } // collect literals that were assigned after assigning l m_assigned.reset(); unsigned tr_sz = s.m_trail.size(); for (unsigned i = old_tr_sz; i < tr_sz; i++) { m_assigned.insert(s.m_trail[i]); } cache_bins(l, old_tr_sz); s.pop(1); if (!try_lit(~l, true)) return; if (m_probing_binary) { watch_list & wlist = s.get_wlist(~l); watch_list::iterator it = wlist.begin(); watch_list::iterator end = wlist.end(); for (; it != end ; ++it) { if (!it->is_binary_clause()) break; literal l2 = it->get_literal(); if (l.index() > l2.index()) continue; if (s.value(l2) != l_undef) continue; // verbose_stream() << "probing " << l << " " << l2 << " " << m_counter << "\n"; if (!try_lit(l2, false)) return; if (s.inconsistent()) return; } } } void probing::process(bool_var v) { int old_counter = m_counter; unsigned old_num_assigned = m_num_assigned; process_core(v); if (m_num_assigned > old_num_assigned) { // if new variables were assigned when probing x, // then assume the cost is 0. m_counter = old_counter; } } struct probing::report { probing & m_probing; stopwatch m_watch; unsigned m_num_assigned; report(probing & p): m_probing(p), m_num_assigned(p.m_num_assigned) { m_watch.start(); } ~report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-probing :probing-assigned " << (m_probing.m_num_assigned - m_num_assigned) << " :cost " << m_probing.m_counter; if (m_probing.m_stopped_at != 0) verbose_stream() << " :stopped-at " << m_probing.m_stopped_at; verbose_stream() << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; bool probing::operator()(bool force) { if (!m_probing) return true; s.propagate(false); // make sure propagation queue is empty if (s.inconsistent()) return true; SASSERT(s.m_qhead == s.m_trail.size()); CASSERT("probing", s.check_invariant()); if (!force && m_counter > 0) return true; if (m_probing_cache && memory::get_allocation_size() > m_probing_cache_limit) m_cached_bins.finalize(); report rpt(*this); bool r = true; m_counter = 0; int limit = -static_cast(m_probing_limit); unsigned i; unsigned num = s.num_vars(); for (i = 0; i < num; i++) { bool_var v = (m_stopped_at + i) % num; if (m_counter < limit) { m_stopped_at = v; r = false; break; } if (s.inconsistent()) { break; } if (s.value(v) != l_undef || s.was_eliminated(v)) { if (m_probing_cache) { // cache for v literals is not needed anymore. reset_cache(literal(v, false)); reset_cache(literal(v, true)); } continue; } s.checkpoint(); process(v); } if (r) m_stopped_at = 0; m_counter = -m_counter; if (rpt.m_num_assigned == m_num_assigned) { // penalize m_counter *= 2; } CASSERT("probing", s.check_invariant()); free_memory(); return r; } void probing::updt_params(params_ref const & p) { m_probing = p.get_bool("probing", true); m_probing_limit = p.get_uint("probing_limit", 5000000); m_probing_cache = p.get_bool("probing_cache", true); m_probing_binary = p.get_bool("probing_binary", true); m_probing_cache_limit = megabytes_to_bytes(p.get_uint("probing_chache_limit", 1024)); } void probing::collect_param_descrs(param_descrs & d) { // TODO } void probing::free_memory() { m_assigned.cleanup(); m_to_assert.finalize(); } void probing::collect_statistics(statistics & st) const { st.update("probing assigned", m_num_assigned); } void probing::reset_statistics() { m_num_assigned = 0; } }; z3-z3-4.4.1/src/sat/sat_probing.h000066400000000000000000000045501260446376700164670ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_probing.h Abstract: Probing (aka failed literal detection). Author: Leonardo de Moura (leonardo) 2011-06-04. Revision History: --*/ #ifndef SAT_PROBING_H_ #define SAT_PROBING_H_ #include"sat_types.h" #include"params.h" #include"statistics.h" namespace sat { class probing { solver & s; unsigned m_stopped_at; // where did it stop literal_set m_assigned; // literals assigned in the first branch literal_vector m_to_assert; // counters int m_counter; // track cost // config bool m_probing; // enabled/disabled unsigned m_probing_limit; // max cost per round bool m_probing_cache; // cache implicit binary clauses bool m_probing_binary; // try l1 and l2 for binary clauses l1 \/ l2 unsigned long long m_probing_cache_limit; // memory limit for enabling caching. // stats unsigned m_num_assigned; struct cache_entry { bool m_available; literal_vector m_lits; cache_entry():m_available(false) {} }; vector m_cached_bins; struct report; void reset_cache(literal l); void cache_bins(literal l, unsigned old_tr_sz); bool try_lit(literal l, bool updt_cache); void process(bool_var v); void process_core(bool_var v); public: probing(solver & s, params_ref const & p); bool operator()(bool force = false); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void free_memory(); void collect_statistics(statistics & st) const; void reset_statistics(); // return the literals implied by l. // return 0, if the cache is not available literal_vector * cached_implied_lits(literal l) { if (!m_probing_cache) return 0; if (l.index() >= m_cached_bins.size()) return 0; cache_entry & e = m_cached_bins[l.index()]; if (!e.m_available) return 0; return &(e.m_lits); } void dec(unsigned c) { m_counter -= c; } }; }; #endif z3-z3-4.4.1/src/sat/sat_scc.cpp000066400000000000000000000214401260446376700161270ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_scc.cpp Abstract: Use binary clauses to compute strongly connected components. Author: Leonardo de Moura (leonardo) 2011-05-26. Revision History: --*/ #include"sat_scc.h" #include"sat_solver.h" #include"sat_elim_eqs.h" #include"stopwatch.h" #include"trace.h" #include"sat_scc_params.hpp" namespace sat { scc::scc(solver & s, params_ref const & p): m_solver(s) { reset_statistics(); updt_params(p); } struct frame { unsigned m_lidx; bool m_first; watched * m_it; watched * m_end; frame(unsigned lidx, watched * it, watched * end):m_lidx(lidx), m_first(true), m_it(it), m_end(end) {} }; typedef svector frames; struct scc::report { scc & m_scc; stopwatch m_watch; unsigned m_num_elim; report(scc & c): m_scc(c), m_num_elim(c.m_num_elim) { m_watch.start(); } ~report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-scc :elim-vars " << (m_scc.m_num_elim - m_num_elim) << mk_stat(m_scc.m_solver) << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; unsigned scc::operator()() { if (m_solver.m_inconsistent) return 0; if (!m_scc) return 0; CASSERT("scc_bug", m_solver.check_invariant()); report rpt(*this); TRACE("scc", m_solver.display(tout);); TRACE("scc_details", m_solver.display_watches(tout);); unsigned_vector index; unsigned_vector lowlink; unsigned_vector s; svector in_s; unsigned num_lits = m_solver.num_vars() * 2; index.resize(num_lits, UINT_MAX); lowlink.resize(num_lits, UINT_MAX); in_s.resize(num_lits, false); literal_vector roots; roots.resize(m_solver.num_vars(), null_literal); unsigned next_index = 0; svector frames; bool_var_vector to_elim; for (unsigned l_idx = 0; l_idx < num_lits; l_idx++) { if (index[l_idx] != UINT_MAX) continue; if (m_solver.was_eliminated(to_literal(l_idx).var())) continue; m_solver.checkpoint(); #define NEW_NODE(LIDX) { \ index[LIDX] = next_index; \ lowlink[LIDX] = next_index; \ next_index++; \ s.push_back(LIDX); \ in_s[LIDX] = true; \ watch_list & wlist = m_solver.get_wlist(LIDX); \ frames.push_back(frame(LIDX, wlist.begin(), wlist.end())); \ } NEW_NODE(l_idx); while (!frames.empty()) { loop: frame & fr = frames.back(); unsigned l_idx = fr.m_lidx; if (!fr.m_first) { // after visiting child literal l2 = fr.m_it->get_literal(); unsigned l2_idx = l2.index(); SASSERT(index[l2_idx] != UINT_MAX); if (lowlink[l2_idx] < lowlink[l_idx]) lowlink[l_idx] = lowlink[l2_idx]; fr.m_it++; } fr.m_first = false; while (fr.m_it != fr.m_end) { if (!fr.m_it->is_binary_clause()) { fr.m_it++; continue; } literal l2 = fr.m_it->get_literal(); unsigned l2_idx = l2.index(); if (index[l2_idx] == UINT_MAX) { NEW_NODE(l2_idx); goto loop; } else if (in_s[l2_idx]) { if (index[l2_idx] < lowlink[l_idx]) lowlink[l_idx] = index[l2_idx]; } fr.m_it++; } // visited all successors if (lowlink[l_idx] == index[l_idx]) { // found new SCC CTRACE("scc_cycle", s.back() != l_idx, { tout << "cycle: "; unsigned j = s.size() - 1; unsigned l2_idx; do { l2_idx = s[j]; j--; tout << to_literal(l2_idx) << " "; } while (l2_idx != l_idx); tout << "\n"; }); SASSERT(!s.empty()); literal l = to_literal(l_idx); bool_var v = l.var(); if (roots[v] != null_literal) { // variable was already assigned... just consume stack TRACE("scc_detail", tout << "consuming stack...\n";); unsigned l2_idx; do { l2_idx = s.back(); s.pop_back(); in_s[l2_idx] = false; SASSERT(roots[to_literal(l2_idx).var()].var() == roots[v].var()); } while (l2_idx != l_idx); } else { // check if the SCC has an external variable, and check for conflicts TRACE("scc_detail", tout << "assigning roots...\n";); literal r = null_literal; unsigned j = s.size() - 1; unsigned l2_idx; do { l2_idx = s[j]; j--; if (to_literal(l2_idx) == ~l) { m_solver.set_conflict(justification()); return 0; } if (m_solver.is_external(to_literal(l2_idx).var())) { r = to_literal(l2_idx); break; } } while (l2_idx != l_idx); if (r == null_literal) { // SCC does not contain external variable r = to_literal(l_idx); } TRACE("scc_detail", tout << "r: " << r << "\n";); do { l2_idx = s.back(); s.pop_back(); in_s[l2_idx] = false; literal l2 = to_literal(l2_idx); bool_var v2 = l2.var(); if (roots[v2] == null_literal) { if (l2.sign()) { roots[v2] = ~r; } else { roots[v2] = r; } if (v2 != r.var()) to_elim.push_back(v2); } } while (l2_idx != l_idx); } } frames.pop_back(); } } TRACE("scc", for (unsigned i = 0; i < roots.size(); i++) { tout << i << " -> " << roots[i] << "\n"; } tout << "to_elim: "; for (unsigned i = 0; i < to_elim.size(); i++) tout << to_elim[i] << " "; tout << "\n";); m_num_elim += to_elim.size(); elim_eqs eliminator(m_solver); eliminator(roots, to_elim); TRACE("scc_detail", m_solver.display(tout);); CASSERT("scc_bug", m_solver.check_invariant()); return to_elim.size(); } void scc::collect_statistics(statistics & st) const { st.update("elim bool vars", m_num_elim); } void scc::reset_statistics() { m_num_elim = 0; } void scc::updt_params(params_ref const & _p) { sat_scc_params p(_p); m_scc = p.scc(); } void scc::collect_param_descrs(param_descrs & d) { sat_scc_params::collect_param_descrs(d); } }; z3-z3-4.4.1/src/sat/sat_scc.h000066400000000000000000000015061260446376700155750ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_scc.h Abstract: Use binary clauses to compute strongly connected components. Author: Leonardo de Moura (leonardo) 2011-05-26. Revision History: --*/ #ifndef SAT_SCC_H_ #define SAT_SCC_H_ #include"sat_types.h" #include"statistics.h" #include"params.h" namespace sat { class solver; class scc { struct report; solver & m_solver; // config bool m_scc; // stats unsigned m_num_elim; public: scc(solver & s, params_ref const & p); unsigned operator()(); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void collect_statistics(statistics & st) const; void reset_statistics(); }; }; #endif z3-z3-4.4.1/src/sat/sat_scc_params.pyg000066400000000000000000000003601260446376700175050ustar00rootroot00000000000000def_module_params(module_name='sat', class_name='sat_scc_params', export=True, params=(('scc', BOOL, True, 'eliminate Boolean variables by computing strongly connected components'),)) z3-z3-4.4.1/src/sat/sat_simplifier.cpp000066400000000000000000001510551260446376700175300ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_simplifier.cpp Abstract: SAT simplification procedures that use a "full" occurrence list: Subsumption, Blocked Clause Removal, Variable Elimination, ... Author: Leonardo de Moura (leonardo) 2011-05-24. Revision History: --*/ #include"sat_simplifier.h" #include"sat_simplifier_params.hpp" #include"sat_solver.h" #include"stopwatch.h" #include"trace.h" namespace sat { void use_list::init(unsigned num_vars) { m_use_list.reset(); unsigned num_lits = 2 * num_vars; m_use_list.resize(num_lits); } void use_list::insert(clause & c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { m_use_list[c[i].index()].insert(c); } } void use_list::erase(clause & c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { m_use_list[c[i].index()].erase(c); } } void use_list::erase(clause & c, literal l) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { literal l2 = c[i]; if (l2 != l) m_use_list[l2.index()].erase(c); } } simplifier::simplifier(solver & _s, params_ref const & p): s(_s), m_num_calls(0) { updt_params(p); reset_statistics(); } simplifier::~simplifier() { } inline watch_list & simplifier::get_wlist(literal l) { return s.get_wlist(l); } inline watch_list const & simplifier::get_wlist(literal l) const { return s.get_wlist(l); } inline bool simplifier::is_external(bool_var v) const { return s.is_external(v); } inline bool simplifier::was_eliminated(bool_var v) const { return s.was_eliminated(v); } lbool simplifier::value(bool_var v) const { return s.value(v); } lbool simplifier::value(literal l) const { return s.value(l); } inline void simplifier::checkpoint() { s.checkpoint(); } void simplifier::register_clauses(clause_vector & cs) { std::stable_sort(cs.begin(), cs.end(), size_lt()); clause_vector::iterator it = cs.begin(); clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); if (!c.frozen()) { m_use_list.insert(c); if (c.strengthened()) m_sub_todo.insert(c); } } } inline void simplifier::remove_clause_core(clause & c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) insert_todo(c[i].var()); m_sub_todo.erase(c); c.set_removed(true); TRACE("resolution_bug", tout << "del_clause: " << c << "\n";); m_need_cleanup = true; } inline void simplifier::remove_clause(clause & c) { remove_clause_core(c); m_use_list.erase(c); } inline void simplifier::remove_clause(clause & c, literal l) { remove_clause_core(c); m_use_list.erase(c, l); } inline void simplifier::remove_bin_clause_half(literal l1, literal l2, bool learned) { SASSERT(s.get_wlist(~l1).contains(watched(l2, learned))); s.get_wlist(~l1).erase(watched(l2, learned)); } void simplifier::init_visited() { m_visited.reset(); m_visited.resize(2*s.num_vars(), false); } void simplifier::free_memory() { m_use_list.finalize(); m_sub_todo.finalize(); m_sub_bin_todo.finalize(); m_visited.finalize(); m_bs_cs.finalize(); m_bs_ls.finalize(); } void simplifier::operator()(bool learned) { if (s.inconsistent()) return; if (!m_subsumption && !m_elim_blocked_clauses && !m_resolution) return; CASSERT("sat_solver", s.check_invariant()); TRACE("before_simplifier", s.display(tout);); s.m_cleaner(true); m_last_sub_trail_sz = s.m_trail.size(); TRACE("after_cleanup", s.display(tout);); CASSERT("sat_solver", s.check_invariant()); m_need_cleanup = false; m_use_list.init(s.num_vars()); init_visited(); bool learned_in_use_lists = false; if (learned) { register_clauses(s.m_learned); learned_in_use_lists = true; } register_clauses(s.m_clauses); if (!learned && (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls)) elim_blocked_clauses(); if (!learned) m_num_calls++; m_sub_counter = m_subsumption_limit; m_elim_counter = m_res_limit; unsigned old_num_elim_vars = m_num_elim_vars; do { if (m_subsumption) subsume(); if (s.inconsistent()) return; if (!learned && m_resolution) elim_vars(); if (s.inconsistent()) return; if (!m_subsumption || m_sub_counter < 0) break; } while (!m_sub_todo.empty()); bool vars_eliminated = m_num_elim_vars > old_num_elim_vars; if (m_need_cleanup) { TRACE("after_simplifier", tout << "cleanning watches...\n";); cleanup_watches(); cleanup_clauses(s.m_learned, true, vars_eliminated, learned_in_use_lists); cleanup_clauses(s.m_clauses, false, vars_eliminated, true); } else { TRACE("after_simplifier", tout << "skipping cleanup...\n";); if (vars_eliminated) { // must remove learned clauses with eliminated variables cleanup_clauses(s.m_learned, true, true, learned_in_use_lists); } } CASSERT("sat_solver", s.check_invariant()); TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); free_memory(); } /** \brief Eliminate all ternary and clause watches. */ void simplifier::cleanup_watches() { vector::iterator it = s.m_watches.begin(); vector::iterator end = s.m_watches.end(); for (; it != end; ++it) { watch_list & wlist = *it; watch_list::iterator it2 = wlist.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { switch (it2->get_kind()) { case watched::TERNARY: case watched::CLAUSE: // consume break; default: *itprev = *it2; itprev++; break; } } wlist.set_end(itprev); } } void simplifier::cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists) { TRACE("sat", tout << "cleanup_clauses\n";); clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); if (c.was_removed()) { s.del_clause(c); continue; } if (learned && vars_eliminated) { unsigned sz = c.size(); unsigned i; for (i = 0; i < sz; i++) { if (was_eliminated(c[i].var())) break; } if (i < sz) { s.del_clause(c); continue; } } if (cleanup_clause(c, in_use_lists)) { s.del_clause(c); continue; } unsigned sz = c.size(); if (sz == 0) { s.set_conflict(justification()); return; } if (sz == 1) { s.assign(c[0], justification()); s.del_clause(c); continue; } if (sz == 2) { s.mk_bin_clause(c[0], c[1], c.is_learned()); s.del_clause(c); continue; } // clause became a problem clause if (learned && !c.is_learned()) { SASSERT(!c.frozen()); s.m_clauses.push_back(&c); continue; } *it2 = *it; it2++; if (!c.frozen()) { if (sz == 3) s.attach_ter_clause(c); else s.attach_nary_clause(c); } } cs.set_end(it2); } void simplifier::mark_all_but(clause const & c, literal l) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { if (c[i] == l) continue; mark_visited(c[i]); } } void simplifier::unmark_all(clause const & c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) unmark_visited(c[i]); } /** \brief Return the variable in c with the minimal number positive+negative occurrences. */ bool_var simplifier::get_min_occ_var(clause const & c) const { literal l_best = c[0]; unsigned best = m_use_list.get(l_best).size() + m_use_list.get(~l_best).size(); unsigned sz = c.size(); for (unsigned i = 1; i < sz; i++) { literal l = c[i]; unsigned num = m_use_list.get(l).size() + m_use_list.get(~l).size(); if (num < best) { l_best = l; best = num; } } return l_best.var(); } /** If c1 subsumes c2, return true If c2 can self subsumed by c1, return true and store the literal that can be removed from c2 in l. Otherwise return false */ bool simplifier::subsumes1(clause const & c1, clause const & c2, literal & l) { unsigned sz2 = c2.size(); for (unsigned i = 0; i < sz2; i++) mark_visited(c2[i]); bool r = true; l = null_literal; unsigned sz1 = c1.size(); for (unsigned i = 0; i < sz1; i++) { if (!is_marked(c1[i])) { if (l == null_literal && is_marked(~c1[i])) { l = ~c1[i]; } else { l = null_literal; r = false; break; } } } for (unsigned i = 0; i < sz2; i++) unmark_visited(c2[i]); return r; } /** \brief Return the clauses subsumed by c1 and the clauses that can be subsumed resolved using c1. The collections is populated using the use list of target. */ void simplifier::collect_subsumed1_core(clause const & c1, clause_vector & out, literal_vector & out_lits, literal target) { clause_use_list const & cs = m_use_list.get(target); clause_use_list::iterator it = cs.mk_iterator(); while (!it.at_end()) { clause & c2 = it.curr(); CTRACE("resolution_bug", c2.was_removed(), tout << "clause has been removed:\n" << c2 << "\n";); SASSERT(!c2.was_removed()); if (&c2 != &c1 && c1.size() <= c2.size() && approx_subset(c1.approx(), c2.approx())) { m_sub_counter -= c1.size() + c2.size(); literal l; if (subsumes1(c1, c2, l)) { out.push_back(&c2); out_lits.push_back(l); } } it.next(); } } /** \brief Return the clauses subsumed by c1 and the clauses that can be subsumed resolved using c1. */ void simplifier::collect_subsumed1(clause const & c1, clause_vector & out, literal_vector & out_lits) { bool_var v = get_min_occ_var(c1); collect_subsumed1_core(c1, out, out_lits, literal(v, false)); collect_subsumed1_core(c1, out, out_lits, literal(v, true)); } /** \brief Perform backward subsumption and self-subsumption resolution using c. */ void simplifier::back_subsumption1(clause & c1) { m_bs_cs.reset(); m_bs_ls.reset(); collect_subsumed1(c1, m_bs_cs, m_bs_ls); SASSERT(m_bs_cs.size() == m_bs_ls.size()); clause_vector::iterator it = m_bs_cs.begin(); clause_vector::iterator end = m_bs_cs.end(); literal_vector::iterator l_it = m_bs_ls.begin(); for (; it != end; ++it, ++l_it) { clause & c2 = *(*it); if (*l_it == null_literal) { // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) c1.unset_learned(); TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2); m_num_subsumed++; } else if (!c2.was_removed()) { // subsumption resolution TRACE("subsumption_resolution", tout << c1 << " sub-ref(" << *l_it << ") " << c2 << "\n";); elim_lit(c2, *l_it); m_num_sub_res++; TRACE("subsumption_resolution", tout << "result: " << c2 << "\n";); } if (s.inconsistent()) break; } } void simplifier::back_subsumption1(literal l1, literal l2, bool learned) { m_dummy.set(l1, l2, learned); clause & c = *(m_dummy.get()); back_subsumption1(c); } /** \brief Return the literal in c with the minimal number of occurrences. */ literal simplifier::get_min_occ_var0(clause const & c) const { literal l_best = c[0]; unsigned best = m_use_list.get(l_best).size(); unsigned sz = c.size(); for (unsigned i = 1; i < sz; i++) { literal l = c[i]; unsigned num = m_use_list.get(l).size(); if (num < best) { l_best = l; best = num; } } return l_best; } /** If c1 subsumes c2, return true Otherwise return false */ bool simplifier::subsumes0(clause const & c1, clause const & c2) { unsigned sz2 = c2.size(); for (unsigned i = 0; i < sz2; i++) mark_visited(c2[i]); bool r = true; unsigned sz1 = c1.size(); for (unsigned i = 0; i < sz1; i++) { if (!is_marked(c1[i])) { r = false; break; } } for (unsigned i = 0; i < sz2; i++) unmark_visited(c2[i]); return r; } /** \brief Collect the clauses subsumed by c1 (using the occurrence list of target). */ void simplifier::collect_subsumed0_core(clause const & c1, clause_vector & out, literal target) { clause_use_list const & cs = m_use_list.get(target); clause_use_list::iterator it = cs.mk_iterator(); while (!it.at_end()) { clause & c2 = it.curr(); SASSERT(!c2.was_removed()); if (&c2 != &c1 && c1.size() <= c2.size() && approx_subset(c1.approx(), c2.approx())) { m_sub_counter -= c1.size() + c2.size(); if (subsumes0(c1, c2)) { out.push_back(&c2); } } it.next(); } } /** \brief Collect the clauses subsumed by c1 */ void simplifier::collect_subsumed0(clause const & c1, clause_vector & out) { literal l = get_min_occ_var0(c1); collect_subsumed0_core(c1, out, l); } /** \brief Perform backward subsumption using c1. */ void simplifier::back_subsumption0(clause & c1) { m_bs_cs.reset(); collect_subsumed0(c1, m_bs_cs); clause_vector::iterator it = m_bs_cs.begin(); clause_vector::iterator end = m_bs_cs.end(); for (; it != end; ++it) { clause & c2 = *(*it); // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) c1.unset_learned(); TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2); m_num_subsumed++; } } /** \brief Eliminate false literals from c, and update occurrence lists Return true if the clause is satisfied */ bool simplifier::cleanup_clause(clause & c, bool in_use_list) { bool r = false; unsigned sz = c.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = c[i]; switch (value(l)) { case l_undef: c[j] = l; j++; break; case l_false: m_need_cleanup = true; if (in_use_list && !c.frozen()) { // Remark: if in_use_list is false, then the given clause was not added to the use lists. // Remark: frozen clauses are not added to the use lists. m_use_list.get(l).erase_not_removed(c); } break; case l_true: r = true; c[j] = l; j++; break; } } c.shrink(j); return r; } /** \brief Eliminate false literals from c. Return true if the clause is satisfied */ bool simplifier::cleanup_clause(literal_vector & c) { unsigned sz = c.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = c[i]; switch (value(l)) { case l_undef: c[j] = l; j++; break; case l_false: break; case l_true: return true; } } c.shrink(j); return false; } inline void simplifier::propagate_unit(literal l) { unsigned old_trail_sz = s.m_trail.size(); s.assign(l, justification()); s.propagate_core(false); // must not use propagate(), since s.m_clauses is not in a consistent state. if (s.inconsistent()) return; unsigned new_trail_sz = s.m_trail.size(); for (unsigned i = old_trail_sz; i < new_trail_sz; i++) { literal l = s.m_trail[i]; { // put clauses with literals assigned to false back into todo-list clause_use_list & cs = m_use_list.get(~l); clause_use_list::iterator it = cs.mk_iterator(); while (!it.at_end()) { clause & c = it.curr(); it.next(); m_sub_todo.insert(c); } } { // erase satisfied clauses clause_use_list & cs = m_use_list.get(l); { clause_use_list::iterator it = cs.mk_iterator(); while (!it.at_end()) { clause & c = it.curr(); it.next(); remove_clause(c, l); } } cs.reset(); } } } void simplifier::elim_lit(clause & c, literal l) { TRACE("elim_lit", tout << "processing: " << c << "\n";); m_need_cleanup = true; m_num_elim_lits++; insert_todo(l.var()); c.elim(l); clause_use_list & occurs = m_use_list.get(l); occurs.erase_not_removed(c); m_sub_counter -= occurs.size()/2; if (cleanup_clause(c, true /* clause is in the use lists */)) { // clause was satisfied TRACE("elim_lit", tout << "clause was satisfied\n";); remove_clause(c); return; } switch (c.size()) { case 0: TRACE("elim_lit", tout << "clause is empty\n";); s.set_conflict(justification()); return; case 1: TRACE("elim_lit", tout << "clause became unit: " << c[0] << "\n";); propagate_unit(c[0]); // propagate_unit will delete c. // remove_clause(c); return; case 2: TRACE("elim_lit", tout << "clause became binary: " << c[0] << " " << c[1] << "\n";); s.mk_bin_clause(c[0], c[1], c.is_learned()); m_sub_bin_todo.push_back(bin_clause(c[0], c[1], c.is_learned())); remove_clause(c); return; default: TRACE("elim_lit", tout << "result: " << c << "\n";); m_sub_todo.insert(c); return; } } bool simplifier::subsume_with_binaries() { unsigned init = s.m_rand(); // start in a random place, since subsumption can be aborted unsigned num_lits = s.m_watches.size(); for (unsigned i = 0; i < num_lits; i++) { unsigned l_idx = (i + init) % num_lits; watch_list & wlist = get_wlist(to_literal(l_idx)); literal l = ~to_literal(l_idx); // should not traverse wlist using iterators, since back_subsumption1 may add new binary clauses there for (unsigned j = 0; j < wlist.size(); j++) { watched w = wlist[j]; if (w.is_binary_clause()) { literal l2 = w.get_literal(); if (l.index() < l2.index()) { m_dummy.set(l, l2, w.is_learned()); clause & c = *(m_dummy.get()); back_subsumption1(c); if (w.is_learned() && !c.is_learned()) { SASSERT(wlist[j] == w); TRACE("mark_not_learned_bug", tout << "marking as not learned: " << l2 << " " << wlist[j].is_learned() << "\n";); wlist[j].mark_not_learned(); mark_as_not_learned_core(get_wlist(~l2), l); } if (s.inconsistent()) return false; } } } if (m_sub_counter < 0) break; } return true; } void simplifier::mark_as_not_learned_core(watch_list & wlist, literal l2) { watch_list::iterator it = wlist.begin(); watch_list::iterator end = wlist.end(); for (; it != end; ++it) { if (it->is_binary_clause() && it->get_literal() == l2 && it->is_learned()) { it->mark_not_learned(); return; } } } void simplifier::mark_as_not_learned(literal l1, literal l2) { mark_as_not_learned_core(get_wlist(~l1), l2); mark_as_not_learned_core(get_wlist(~l2), l1); } struct bin_lt { bool operator()(watched const & w1, watched const & w2) const { if (!w1.is_binary_clause()) return false; if (!w2.is_binary_clause()) return true; unsigned l1_idx = w1.get_literal().index(); unsigned l2_idx = w2.get_literal().index(); if (l1_idx < l2_idx) return true; if (l1_idx == l2_idx && !w1.is_learned() && w2.is_learned()) return true; return false; } }; /** \brief Eliminate duplicated binary clauses. */ void simplifier::elim_dup_bins() { vector::iterator it = s.m_watches.begin(); vector::iterator end = s.m_watches.end(); #ifdef _TRACE unsigned l_idx = 0; #endif unsigned elim = 0; for (; it != end; ++it) { checkpoint(); watch_list & wlist = *it; std::stable_sort(wlist.begin(), wlist.end(), bin_lt()); literal last_lit = null_literal; watch_list::iterator it2 = wlist.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { if (!it2->is_binary_clause()) { *itprev = *it2; itprev++; continue; } if (it2->get_literal() == last_lit) { TRACE("subsumption", tout << "eliminating: " << ~to_literal(l_idx) << " " << it2->get_literal() << "\n";); elim++; } else { last_lit = it2->get_literal(); *itprev = *it2; itprev++; } } wlist.set_end(itprev); TRACE_CODE(l_idx++;); } m_num_subsumed += elim/2; // each binary clause is "eliminated" twice. } struct simplifier::subsumption_report { simplifier & m_simplifier; stopwatch m_watch; unsigned m_num_subsumed; unsigned m_num_sub_res; subsumption_report(simplifier & s): m_simplifier(s), m_num_subsumed(s.m_num_subsumed), m_num_sub_res(s.m_num_sub_res) { m_watch.start(); } ~subsumption_report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-subsumer :subsumed " << (m_simplifier.m_num_subsumed - m_num_subsumed) << " :subsumption-resolution " << (m_simplifier.m_num_sub_res - m_num_sub_res) << " :threshold " << m_simplifier.m_sub_counter << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; void simplifier::subsume() { subsumption_report rpt(*this); elim_dup_bins(); subsume_with_binaries(); TRACE("subsumption_bug", s.display(tout);); while (true) { TRACE("subsumption", tout << "sub_todo size: " << m_sub_todo.size() << "\n";); m_sub_counter -= m_sub_bin_todo.size(); while (!m_sub_bin_todo.empty()) { checkpoint(); m_dummy.set(m_sub_bin_todo.back()); m_sub_bin_todo.pop_back(); clause & c = *(m_dummy.get()); bool was_learned = c.is_learned(); back_subsumption1(c); if (was_learned && !c.is_learned()) { mark_as_not_learned(c[0], c[1]); } } checkpoint(); TRACE("subsumption_bug", s.display(tout);); if (m_sub_todo.empty()) { m_last_sub_trail_sz = s.m_trail.size(); break; } if (m_sub_counter < 0) break; clause & c = m_sub_todo.erase(); c.unmark_strengthened(); m_sub_counter--; TRACE("subsumption", tout << "next: " << c << "\n";); if (s.m_trail.size() > m_last_sub_trail_sz) { if (cleanup_clause(c, true /* clause is in the use_lists */)) { remove_clause(c); continue; } unsigned sz = c.size(); if (sz == 0) { s.set_conflict(justification()); return; } if (sz == 1) { propagate_unit(c[0]); // propagate_unit will delete c. // remove_clause(c); continue; } if (sz == 2) { TRACE("subsumption", tout << "clause became binary: " << c << "\n";); s.mk_bin_clause(c[0], c[1], c.is_learned()); m_sub_bin_todo.push_back(bin_clause(c[0], c[1], c.is_learned())); remove_clause(c); continue; } } TRACE("subsumption", tout << "using: " << c << "\n";); back_subsumption1(c); } } struct simplifier::blocked_clause_elim { class literal_lt { use_list const & m_use_list; vector const & m_watches; public: literal_lt(use_list const & l, vector const & ws):m_use_list(l), m_watches(ws) {} unsigned weight(unsigned l_idx) const { return 2*m_use_list.get(~to_literal(l_idx)).size() + m_watches[l_idx].size(); } bool operator()(unsigned l_idx1, unsigned l_idx2) const { return weight(l_idx1) < weight(l_idx2); } }; class queue { heap m_queue; public: queue(use_list const & l, vector const & ws):m_queue(128, literal_lt(l, ws)) {} void insert(literal l) { unsigned idx = l.index(); m_queue.reserve(idx + 1); SASSERT(!m_queue.contains(idx)); m_queue.insert(idx); } void decreased(literal l) { unsigned idx = l.index(); if (m_queue.contains(idx)) m_queue.decreased(idx); else m_queue.insert(idx); } literal next() { SASSERT(!empty()); return to_literal(m_queue.erase_min()); } bool empty() const { return m_queue.empty(); } }; simplifier & s; int m_counter; model_converter & mc; queue m_queue; clause_vector m_to_remove; blocked_clause_elim(simplifier & _s, unsigned limit, model_converter & _mc, use_list & l, vector & wlist): s(_s), m_counter(limit), mc(_mc), m_queue(l, wlist) { } void insert(literal l) { bool_var v = l.var(); if (s.is_external(v) || s.was_eliminated(v)) return; m_queue.insert(l); } void operator()(unsigned num_vars) { for (bool_var v = 0; v < num_vars; v++) { insert(literal(v, false)); insert(literal(v, true)); } while (!m_queue.empty()) { s.checkpoint(); if (m_counter < 0) return; literal l = m_queue.next(); process(l); } } void process(literal l) { TRACE("blocked_clause", tout << "processing: " << l << "\n";); model_converter::entry * new_entry = 0; if (s.is_external(l.var()) || s.was_eliminated(l.var())) return; { m_to_remove.reset(); { clause_use_list & occs = s.m_use_list.get(l); clause_use_list::iterator it = occs.mk_iterator(); while (!it.at_end()) { clause & c = it.curr(); m_counter -= c.size(); SASSERT(c.contains(l)); s.mark_all_but(c, l); if (all_tautology(l)) { TRACE("blocked_clause", tout << "new blocked clause: " << c << "\n";); if (new_entry == 0) new_entry = &(mc.mk(model_converter::BLOCK_LIT, l.var())); m_to_remove.push_back(&c); s.m_num_blocked_clauses++; mc.insert(*new_entry, c); unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { if (c[i] != l) m_queue.decreased(~c[i]); } } s.unmark_all(c); it.next(); } } clause_vector::iterator it = m_to_remove.begin(); clause_vector::iterator end = m_to_remove.end(); for (; it != end; ++it) { s.remove_clause(*(*it)); } } { watch_list & wlist = s.get_wlist(~l); m_counter -= wlist.size(); watch_list::iterator it = wlist.begin(); watch_list::iterator it2 = it; watch_list::iterator end = wlist.end(); for (; it != end; ++it) { if (!it->is_binary_clause()) { *it2 = *it; it2++; continue; } literal l2 = it->get_literal(); s.mark_visited(l2); if (all_tautology(l)) { if (new_entry == 0) new_entry = &(mc.mk(model_converter::BLOCK_LIT, l.var())); TRACE("blocked_clause", tout << "new blocked clause: " << l2 << " " << l << "\n";); s.remove_bin_clause_half(l2, l, it->is_learned()); s.m_num_blocked_clauses++; m_queue.decreased(~l2); mc.insert(*new_entry, l, l2); } else { *it2 = *it; it2++; } s.unmark_visited(l2); } wlist.set_end(it2); } } bool all_tautology(literal l) { { watch_list & wlist = s.get_wlist(l); m_counter -= wlist.size(); watch_list::iterator it = wlist.begin(); watch_list::iterator end = wlist.end(); for (; it != end; ++it) { if (!it->is_binary_non_learned_clause()) continue; if (!s.is_marked(~it->get_literal())) return false; } } { clause_use_list & neg_occs = s.m_use_list.get(~l); clause_use_list::iterator it = neg_occs.mk_iterator(); while (!it.at_end()) { clause & c = it.curr(); m_counter -= c.size(); unsigned sz = c.size(); unsigned i; for (i = 0; i < sz; i++) { if (s.is_marked(~c[i])) break; } if (i == sz) return false; it.next(); } } return true; } }; struct simplifier::blocked_cls_report { simplifier & m_simplifier; stopwatch m_watch; unsigned m_num_blocked_clauses; blocked_cls_report(simplifier & s): m_simplifier(s), m_num_blocked_clauses(s.m_num_blocked_clauses) { m_watch.start(); } ~blocked_cls_report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-blocked-clauses :elim-blocked-clauses " << (m_simplifier.m_num_blocked_clauses - m_num_blocked_clauses) << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; void simplifier::elim_blocked_clauses() { TRACE("blocked_clause_bug", tout << "trail: " << s.m_trail.size() << "\n"; s.display_watches(tout); s.display(tout);); blocked_cls_report rpt(*this); blocked_clause_elim elim(*this, m_blocked_clause_limit, s.m_mc, m_use_list, s.m_watches); elim(s.num_vars()); } unsigned simplifier::get_num_non_learned_bin(literal l) const { unsigned r = 0; watch_list const & wlist = get_wlist(~l); watch_list::const_iterator it = wlist.begin(); watch_list::const_iterator end = wlist.end(); for (; it != end; ++it) { if (it->is_binary_non_learned_clause()) r++; } return r; } unsigned simplifier::get_to_elim_cost(bool_var v) const { literal pos_l(v, false); literal neg_l(v, true); unsigned num_pos = m_use_list.get(pos_l).size(); unsigned num_neg = m_use_list.get(neg_l).size(); unsigned num_bin_pos = get_num_non_learned_bin(pos_l); unsigned num_bin_neg = get_num_non_learned_bin(neg_l); unsigned cost = 2 * num_pos * num_neg + num_pos * num_bin_neg + num_neg * num_bin_pos; CTRACE("elim_vars_detail", cost == 0, tout << v << " num_pos: " << num_pos << " num_neg: " << num_neg << " num_bin_pos: " << num_bin_pos << " num_bin_neg: " << num_bin_neg << " cost: " << cost << "\n";); return cost; } typedef std::pair bool_var_and_cost; struct bool_var_and_cost_lt { bool operator()(bool_var_and_cost const & p1, bool_var_and_cost const & p2) const { return p1.second < p2.second; } }; void simplifier::order_vars_for_elim(bool_var_vector & r) { svector tmp; bool_var_set::iterator it = m_elim_todo.begin(); bool_var_set::iterator end = m_elim_todo.end(); for (; it != end; ++it) { bool_var v = *it; if (is_external(v)) continue; if (was_eliminated(v)) continue; if (value(v) != l_undef) continue; unsigned c = get_to_elim_cost(v); tmp.push_back(bool_var_and_cost(v, c)); } m_elim_todo.reset(); std::stable_sort(tmp.begin(), tmp.end(), bool_var_and_cost_lt()); TRACE("elim_vars", svector::iterator it = tmp.begin(); svector::iterator end = tmp.end(); for (; it != end; ++it) { tout << "(" << it->first << ", " << it->second << ") "; } tout << "\n";); svector::iterator it2 = tmp.begin(); svector::iterator end2 = tmp.end(); for (; it2 != end2; ++it2) { r.push_back(it2->first); } } /** \brief Collect clauses and binary clauses containing l. */ void simplifier::collect_clauses(literal l, clause_wrapper_vector & r) { clause_use_list const & cs = m_use_list.get(l); clause_use_list::iterator it = cs.mk_iterator(); while (!it.at_end()) { r.push_back(clause_wrapper(it.curr())); SASSERT(r.back().size() == it.curr().size()); it.next(); } watch_list & wlist = get_wlist(~l); watch_list::iterator it2 = wlist.begin(); watch_list::iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_non_learned_clause()) { r.push_back(clause_wrapper(l, it2->get_literal())); SASSERT(r.back().size() == 2); } } } /** \brief Resolve clauses c1 and c2. c1 must contain l. c2 must contain ~l. Store result in r. Return false if the result is a tautology */ bool simplifier::resolve(clause_wrapper const & c1, clause_wrapper const & c2, literal l, literal_vector & r) { CTRACE("resolve_bug", !c1.contains(l), tout << c1 << "\n" << c2 << "\nl: " << l << "\n";); SASSERT(c1.contains(l)); SASSERT(c2.contains(~l)); bool res = true; unsigned sz = c1.size(); m_elim_counter -= sz; for (unsigned i = 0; i < sz; i++) { literal l2 = c1[i]; if (l == l2) continue; m_visited[l2.index()] = true; r.push_back(l2); } literal not_l = ~l; sz = c2.size(); m_elim_counter -= sz; for (unsigned i = 0; i < sz; i++) { literal l2 = c2[i]; if (not_l == l2) continue; if (m_visited[(~l2).index()]) { res = false; break; } if (!m_visited[l2.index()]) r.push_back(l2); } sz = c1.size(); for (unsigned i = 0; i < sz; i++) { literal l2 = c1[i]; if (l == l2) continue; m_visited[l2.index()] = false; } return res; } void simplifier::save_clauses(model_converter::entry & mc_entry, clause_wrapper_vector const & cs) { model_converter & mc = s.m_mc; clause_wrapper_vector::const_iterator it = cs.begin(); clause_wrapper_vector::const_iterator end = cs.end(); for (; it != end; ++it) mc.insert(mc_entry, *it); } void simplifier::add_non_learned_binary_clause(literal l1, literal l2) { watch_list & wlist1 = s.m_watches[(~l1).index()]; watch_list & wlist2 = s.m_watches[(~l2).index()]; watch_list::iterator it1 = wlist1.begin(); watch_list::iterator end1 = wlist1.end(); for (; it1 != end1; ++it1) { if (it1->is_binary_clause() && it1->get_literal() == l2) { *it1 = watched(l2, false); watch_list::iterator it2 = wlist2.begin(); watch_list::iterator end2 = wlist2.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_clause() && it2->get_literal() == l1) { *it2 = watched(l1, false); break; } } CTRACE("resolve_bug", it2 == end2, tout << ~l1 << " -> "; display(tout, s.m_cls_allocator, wlist1); tout << "\n" << ~l2 << " -> "; display(tout, s.m_cls_allocator, wlist2); tout << "\n";); SASSERT(it2 != end2); return; } } wlist1.push_back(watched(l2, false)); wlist2.push_back(watched(l1, false)); } /** \brief Eliminate the binary clauses watched by l, when l.var() is being eliminated */ void simplifier::remove_bin_clauses(literal l) { watch_list & wlist = get_wlist(~l); watch_list::iterator it = wlist.begin(); watch_list::iterator end = wlist.end(); for (; it != end; ++it) { if (it->is_binary_clause()) { literal l2 = it->get_literal(); watch_list & wlist2 = get_wlist(~l2); watch_list::iterator it2 = wlist2.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist2.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_clause() && it2->get_literal() == l) { TRACE("bin_clause_bug", tout << "removing: " << l << " " << it2->get_literal() << "\n";); continue; } *itprev = *it2; itprev++; } wlist2.set_end(itprev); } } TRACE("bin_clause_bug", tout << "collapsing watch_list of: " << l << "\n";); wlist.finalize(); } /** \brief Eliminate the clauses where the variable being eliminated occur. */ void simplifier::remove_clauses(clause_use_list const & cs, literal l) { clause_use_list::iterator it = cs.mk_iterator(); while (!it.at_end()) { clause & c = it.curr(); it.next(); SASSERT(c.contains(l)); c.set_removed(true); m_use_list.erase(c, l); m_sub_todo.erase(c); TRACE("resolution_bug", tout << "del_clause (elim_var): " << c << "\n";); m_need_cleanup = true; } } bool simplifier::try_eliminate(bool_var v) { TRACE("resolution_bug", tout << "processing: " << v << "\n";); if (value(v) != l_undef) return false; literal pos_l(v, false); literal neg_l(v, true); unsigned num_bin_pos = get_num_non_learned_bin(pos_l); unsigned num_bin_neg = get_num_non_learned_bin(neg_l); clause_use_list & pos_occs = m_use_list.get(pos_l); clause_use_list & neg_occs = m_use_list.get(neg_l); unsigned num_pos = pos_occs.size() + num_bin_pos; unsigned num_neg = neg_occs.size() + num_bin_neg; m_elim_counter -= num_pos + num_neg; TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << "\n";); if (num_pos >= m_res_occ_cutoff && num_neg >= m_res_occ_cutoff) return false; unsigned before_lits = num_bin_pos*2 + num_bin_neg*2; { clause_use_list::iterator it = pos_occs.mk_iterator(); while (!it.at_end()) { before_lits += it.curr().size(); it.next(); } } { clause_use_list::iterator it2 = neg_occs.mk_iterator(); while (!it2.at_end()) { before_lits += it2.curr().size(); it2.next(); } } TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << " before_lits: " << before_lits << "\n";); if (num_pos >= m_res_occ_cutoff3 && num_neg >= m_res_occ_cutoff3 && before_lits > m_res_lit_cutoff3 && s.m_clauses.size() > m_res_cls_cutoff2) return false; if (num_pos >= m_res_occ_cutoff2 && num_neg >= m_res_occ_cutoff2 && before_lits > m_res_lit_cutoff2 && s.m_clauses.size() > m_res_cls_cutoff1 && s.m_clauses.size() <= m_res_cls_cutoff2) return false; if (num_pos >= m_res_occ_cutoff1 && num_neg >= m_res_occ_cutoff1 && before_lits > m_res_lit_cutoff1 && s.m_clauses.size() <= m_res_cls_cutoff1) return false; m_pos_cls.reset(); m_neg_cls.reset(); collect_clauses(pos_l, m_pos_cls); collect_clauses(neg_l, m_neg_cls); m_elim_counter -= num_pos * num_neg + before_lits; TRACE("resolution_detail", tout << "collecting number of after_clauses\n";); unsigned before_clauses = num_pos + num_neg; unsigned after_clauses = 0; clause_wrapper_vector::iterator it1 = m_pos_cls.begin(); clause_wrapper_vector::iterator end1 = m_pos_cls.end(); for (; it1 != end1; ++it1) { clause_wrapper_vector::iterator it2 = m_neg_cls.begin(); clause_wrapper_vector::iterator end2 = m_neg_cls.end(); for (; it2 != end2; ++it2) { m_new_cls.reset(); if (resolve(*it1, *it2, pos_l, m_new_cls)) { TRACE("resolution_detail", tout << *it1 << "\n" << *it2 << "\n-->\n"; for (unsigned i = 0; i < m_new_cls.size(); i++) tout << m_new_cls[i] << " "; tout << "\n";); after_clauses++; if (after_clauses > before_clauses) { TRACE("resolution", tout << "too many after clauses: " << after_clauses << "\n";); return false; } } } } TRACE("resolution", tout << "found var to eliminate, before: " << before_clauses << " after: " << after_clauses << "\n";); // eliminate variable model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); save_clauses(mc_entry, m_pos_cls); save_clauses(mc_entry, m_neg_cls); s.m_eliminated[v] = true; remove_bin_clauses(pos_l); remove_bin_clauses(neg_l); remove_clauses(pos_occs, pos_l); remove_clauses(neg_occs, neg_l); pos_occs.reset(); neg_occs.reset(); m_elim_counter -= num_pos * num_neg + before_lits; it1 = m_pos_cls.begin(); end1 = m_pos_cls.end(); for (; it1 != end1; ++it1) { clause_wrapper_vector::iterator it2 = m_neg_cls.begin(); clause_wrapper_vector::iterator end2 = m_neg_cls.end(); for (; it2 != end2; ++it2) { m_new_cls.reset(); if (!resolve(*it1, *it2, pos_l, m_new_cls)) continue; TRACE("resolution_new_cls", tout << *it1 << "\n" << *it2 << "\n-->\n" << m_new_cls << "\n";); if (cleanup_clause(m_new_cls)) continue; // clause is already satisfied. switch (m_new_cls.size()) { case 0: s.set_conflict(justification()); break; case 1: propagate_unit(m_new_cls[0]); break; case 2: s.m_stats.m_mk_bin_clause++; add_non_learned_binary_clause(m_new_cls[0], m_new_cls[1]); back_subsumption1(m_new_cls[0], m_new_cls[1], false); break; default: if (m_new_cls.size() == 3) s.m_stats.m_mk_ter_clause++; else s.m_stats.m_mk_clause++; clause * new_c = s.m_cls_allocator.mk_clause(m_new_cls.size(), m_new_cls.c_ptr(), false); s.m_clauses.push_back(new_c); m_use_list.insert(*new_c); if (m_sub_counter > 0) back_subsumption1(*new_c); else back_subsumption0(*new_c); break; } if (s.inconsistent()) return true; } } return true; } struct simplifier::elim_var_report { simplifier & m_simplifier; stopwatch m_watch; unsigned m_num_elim_vars; elim_var_report(simplifier & s): m_simplifier(s), m_num_elim_vars(s.m_num_elim_vars) { m_watch.start(); } ~elim_var_report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-resolution :elim-bool-vars " << (m_simplifier.m_num_elim_vars - m_num_elim_vars) << " :threshold " << m_simplifier.m_elim_counter << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; void simplifier::elim_vars() { if (!m_elim_vars) return; elim_var_report rpt(*this); bool_var_vector vars; order_vars_for_elim(vars); bool_var_vector::iterator it = vars.begin(); bool_var_vector::iterator end = vars.end(); for (; it != end; ++it) { checkpoint(); if (m_elim_counter < 0) return; bool_var v = *it; if (try_eliminate(v)) { m_num_elim_vars++; } } m_pos_cls.finalize(); m_neg_cls.finalize(); m_new_cls.finalize(); } void simplifier::updt_params(params_ref const & _p) { sat_simplifier_params p(_p); m_elim_blocked_clauses = p.elim_blocked_clauses(); m_elim_blocked_clauses_at = p.elim_blocked_clauses_at(); m_blocked_clause_limit = p.blocked_clause_limit(); m_resolution = p.resolution(); m_res_limit = p.resolution_limit(); m_res_occ_cutoff = p.resolution_occ_cutoff(); m_res_occ_cutoff1 = p.resolution_occ_cutoff_range1(); m_res_occ_cutoff2 = p.resolution_occ_cutoff_range2(); m_res_occ_cutoff3 = p.resolution_occ_cutoff_range3(); m_res_lit_cutoff1 = p.resolution_lit_cutoff_range1(); m_res_lit_cutoff2 = p.resolution_lit_cutoff_range2(); m_res_lit_cutoff3 = p.resolution_lit_cutoff_range3(); m_res_cls_cutoff1 = p.resolution_cls_cutoff1(); m_res_cls_cutoff2 = p.resolution_cls_cutoff2(); m_subsumption = p.subsumption(); m_subsumption_limit = p.subsumption_limit(); m_elim_vars = p.elim_vars(); } void simplifier::collect_param_descrs(param_descrs & r) { sat_simplifier_params::collect_param_descrs(r); } void simplifier::collect_statistics(statistics & st) const { st.update("subsumed", m_num_subsumed); st.update("subsumption resolution", m_num_sub_res); st.update("elim literals", m_num_elim_lits); st.update("elim bool vars", m_num_elim_vars); st.update("elim blocked clauses", m_num_blocked_clauses); } void simplifier::reset_statistics() { m_num_blocked_clauses = 0; m_num_subsumed = 0; m_num_sub_res = 0; m_num_elim_lits = 0; m_num_elim_vars = 0; } }; z3-z3-4.4.1/src/sat/sat_simplifier.h000066400000000000000000000152661260446376700172000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_simplifier.h Abstract: SAT simplification procedures that use a "full" occurrence list: Subsumption, Blocked Clause Removal, Variable Elimination, ... Author: Leonardo de Moura (leonardo) 2011-05-24. Revision History: --*/ #ifndef SAT_SIMPLIFIER_H_ #define SAT_SIMPLIFIER_H_ #include"sat_types.h" #include"sat_clause.h" #include"sat_clause_set.h" #include"sat_clause_use_list.h" #include"sat_watched.h" #include"sat_model_converter.h" #include"heap.h" #include"statistics.h" #include"params.h" namespace sat { class solver; class use_list { vector m_use_list; public: void init(unsigned num_vars); void insert(clause & c); void erase(clause & c); void erase(clause & c, literal l); clause_use_list & get(literal l) { return m_use_list[l.index()]; } clause_use_list const & get(literal l) const { return m_use_list[l.index()]; } void finalize() { m_use_list.finalize(); } }; class simplifier { solver & s; unsigned m_num_calls; use_list m_use_list; clause_set m_sub_todo; svector m_sub_bin_todo; unsigned m_last_sub_trail_sz; // size of the trail since last cleanup bool_var_set m_elim_todo; bool m_need_cleanup; tmp_clause m_dummy; // simplifier extra variable fields. svector m_visited; // transient // counters int m_sub_counter; int m_elim_counter; // config bool m_elim_blocked_clauses; unsigned m_elim_blocked_clauses_at; unsigned m_blocked_clause_limit; bool m_resolution; unsigned m_res_limit; unsigned m_res_occ_cutoff; unsigned m_res_occ_cutoff1; unsigned m_res_occ_cutoff2; unsigned m_res_occ_cutoff3; unsigned m_res_lit_cutoff1; unsigned m_res_lit_cutoff2; unsigned m_res_lit_cutoff3; unsigned m_res_cls_cutoff1; unsigned m_res_cls_cutoff2; bool m_subsumption; unsigned m_subsumption_limit; bool m_elim_vars; // stats unsigned m_num_blocked_clauses; unsigned m_num_subsumed; unsigned m_num_elim_vars; unsigned m_num_sub_res; unsigned m_num_elim_lits; struct size_lt { bool operator()(clause const * c1, clause const * c2) const { return c1->size() > c2->size(); } }; void checkpoint(); void init_visited(); void mark_visited(literal l) { m_visited[l.index()] = true; } void unmark_visited(literal l) { m_visited[l.index()] = false; } bool is_marked(literal l) const { return m_visited[l.index()] != 0; } void mark_all_but(clause const & c, literal l); void unmark_all(clause const & c); void register_clauses(clause_vector & cs); void remove_clause_core(clause & c); void remove_clause(clause & c); void remove_clause(clause & c, literal l); void remove_bin_clause_half(literal l1, literal l2, bool learned); bool_var get_min_occ_var(clause const & c) const; bool subsumes1(clause const & c1, clause const & c2, literal & l); void collect_subsumed1_core(clause const & c, clause_vector & out, literal_vector & out_lits, literal target); void collect_subsumed1(clause const & c, clause_vector & out, literal_vector & out_lits); clause_vector m_bs_cs; literal_vector m_bs_ls; void back_subsumption1(clause & c); void back_subsumption1(literal l1, literal l2, bool learned); literal get_min_occ_var0(clause const & c) const; bool subsumes0(clause const & c1, clause const & c2); void collect_subsumed0_core(clause const & c1, clause_vector & out, literal target); void collect_subsumed0(clause const & c1, clause_vector & out); void back_subsumption0(clause & c1); bool cleanup_clause(clause & c, bool in_use_list); bool cleanup_clause(literal_vector & c); void propagate_unit(literal l); void elim_lit(clause & c, literal l); void elim_dup_bins(); bool subsume_with_binaries(); void mark_as_not_learned_core(watch_list & wlist, literal l2); void mark_as_not_learned(literal l1, literal l2); void subsume(); void cleanup_watches(); void cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists); bool is_external(bool_var v) const; bool was_eliminated(bool_var v) const; lbool value(bool_var v) const; lbool value(literal l) const; watch_list & get_wlist(literal l); watch_list const & get_wlist(literal l) const; struct blocked_clause_elim; void elim_blocked_clauses(); unsigned get_num_non_learned_bin(literal l) const; unsigned get_to_elim_cost(bool_var v) const; void order_vars_for_elim(bool_var_vector & r); void collect_clauses(literal l, clause_wrapper_vector & r); clause_wrapper_vector m_pos_cls; clause_wrapper_vector m_neg_cls; literal_vector m_new_cls; bool resolve(clause_wrapper const & c1, clause_wrapper const & c2, literal l, literal_vector & r); void save_clauses(model_converter::entry & mc_entry, clause_wrapper_vector const & cs); void add_non_learned_binary_clause(literal l1, literal l2); void remove_bin_clauses(literal l); void remove_clauses(clause_use_list const & cs, literal l); bool try_eliminate(bool_var v); void elim_vars(); struct blocked_cls_report; struct subsumption_report; struct elim_var_report; public: simplifier(solver & s, params_ref const & p); ~simplifier(); void insert_todo(bool_var v) { m_elim_todo.insert(v); } void reset_todo() { m_elim_todo.reset(); } void operator()(bool learned); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void free_memory(); void collect_statistics(statistics & st) const; void reset_statistics(); }; }; #endif z3-z3-4.4.1/src/sat/sat_simplifier_params.pyg000066400000000000000000000055161260446376700211100ustar00rootroot00000000000000def_module_params(module_name='sat', class_name='sat_simplifier_params', export=True, params=(('elim_blocked_clauses', BOOL, False, 'eliminate blocked clauses'), ('elim_blocked_clauses_at', UINT, 2, 'eliminate blocked clauses only once at the given simplification round'), ('blocked_clause_limit', UINT, 100000000, 'maximum number of literals visited during blocked clause elimination'), ('resolution', BOOL, True, 'eliminate boolean variables using resolution'), ('resolution.limit', UINT, 500000000, 'approx. maximum number of literals visited during variable elimination'), ('resolution.occ_cutoff', UINT, 10, 'first cutoff (on number of positive/negative occurrences) for Boolean variable elimination'), ('resolution.occ_cutoff_range1', UINT, 8, 'second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing less than res_cls_cutoff1 clauses'), ('resolution.occ_cutoff_range2', UINT, 5, 'second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing more than res_cls_cutoff1 and less than res_cls_cutoff2'), ('resolution.occ_cutoff_range3', UINT, 3, 'second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing more than res_cls_cutoff2'), ('resolution.lit_cutoff_range1', UINT, 700, 'second cutoff (total number of literals) for Boolean variable elimination, for problems containing less than res_cls_cutoff1 clauses'), ('resolution.lit_cutoff_range2', UINT, 400, 'second cutoff (total number of literals) for Boolean variable elimination, for problems containing more than res_cls_cutoff1 and less than res_cls_cutoff2'), ('resolution.lit_cutoff_range3', UINT, 300, 'second cutoff (total number of literals) for Boolean variable elimination, for problems containing more than res_cls_cutoff2'), ('resolution.cls_cutoff1', UINT, 100000000, 'limit1 - total number of problems clauses for the second cutoff of Boolean variable elimination'), ('resolution.cls_cutoff2', UINT, 700000000, 'limit2 - total number of problems clauses for the second cutoff of Boolean variable elimination'), ('elim_vars', BOOL, True, 'enable variable elimination during simplification'), ('subsumption', BOOL, True, 'eliminate subsumed clauses'), ('subsumption.limit', UINT, 100000000, 'approx. maximum number of literals visited during subsumption (and subsumption resolution)'))) z3-z3-4.4.1/src/sat/sat_sls.cpp000066400000000000000000000557441260446376700161760ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sat_sls.cpp Abstract: SLS for clauses in SAT solver Author: Nikolaj Bjorner (nbjorner) 2014-12-8 Notes: --*/ #include "sat_sls.h" #include "sat_solver.h" namespace sat { bool index_set::contains(unsigned idx) const { return (idx < m_index.size()) && (m_index[idx] < m_elems.size()) && (m_elems[m_index[idx]] == idx); } void index_set::insert(unsigned idx) { m_index.reserve(idx+1); if (!contains(idx)) { m_index[idx] = m_elems.size(); m_elems.push_back(idx); } } void index_set::remove(unsigned idx) { if (!contains(idx)) return; unsigned pos = m_index[idx]; m_elems[pos] = m_elems.back(); m_index[m_elems[pos]] = pos; m_elems.pop_back(); } unsigned index_set::choose(random_gen& rnd) const { SASSERT(!empty()); return m_elems[rnd(num_elems())]; } sls::sls(solver& s): s(s), m_cancel(false) { m_prob_choose_min_var = 43; m_clause_generation = 0; } sls::~sls() { for (unsigned i = 0; i < m_bin_clauses.size(); ++i) { m_alloc.del_clause(m_bin_clauses[i]); } } lbool sls::operator()(unsigned sz, literal const* tabu, bool reuse_model) { init(sz, tabu, reuse_model); unsigned i; for (i = 0; !m_false.empty() && !m_cancel && i < m_max_tries; ++i) { flip(); } IF_VERBOSE(2, verbose_stream() << "tries " << i << "\n";); if (m_false.empty()) { SASSERT(s.check_model(m_model)); return l_true; } return l_undef; } void sls::init(unsigned sz, literal const* tabu, bool reuse_model) { bool same_generation = (m_clause_generation == s.m_stats.m_non_learned_generation); if (!same_generation) { init_clauses(); init_use(); IF_VERBOSE(0, verbose_stream() << s.m_stats.m_non_learned_generation << " " << m_clause_generation << "\n";); } if (!reuse_model) { init_model(); } init_tabu(sz, tabu); m_clause_generation = s.m_stats.m_non_learned_generation; m_max_tries = 10*(s.num_vars() + m_clauses.size()); } void sls::init_clauses() { for (unsigned i = 0; i < m_bin_clauses.size(); ++i) { m_alloc.del_clause(m_bin_clauses[i]); } m_bin_clauses.reset(); m_clauses.reset(); clause * const * it = s.begin_clauses(); clause * const * end = s.end_clauses(); for (; it != end; ++it) { m_clauses.push_back(*it); } svector bincs; s.collect_bin_clauses(bincs, false); literal lits[2]; for (unsigned i = 0; i < bincs.size(); ++i) { lits[0] = bincs[i].first; lits[1] = bincs[i].second; clause* cl = m_alloc.mk_clause(2, lits, false); m_clauses.push_back(cl); m_bin_clauses.push_back(cl); } } void sls::init_model() { m_num_true.reset(); m_model.reset(); m_model.append(s.get_model()); unsigned sz = m_clauses.size(); for (unsigned i = 0; i < sz; ++i) { clause const& c = *m_clauses[i]; unsigned n = 0; unsigned csz = c.size(); for (unsigned j = 0; j < csz; ++j) { lbool val = value_at(c[j], m_model); switch (val) { case l_true: ++n; break; case l_undef: ++n; m_model[c[j].var()] = c[j].sign()?l_false:l_true; SASSERT(value_at(c[j], m_model) == l_true); break; default: break; } } m_num_true.push_back(n); if (n == 0) { m_false.insert(i); } } } void sls::init_tabu(unsigned sz, literal const* tabu) { // our main use is where m_model satisfies all the hard constraints. // SASSERT(s.check_model(m_model)); // SASSERT(m_false.empty()); // ASSERT: m_num_true is correct count. m_tabu.reset(); m_tabu.resize(s.num_vars(), false); for (unsigned i = 0; i < sz; ++i) { literal lit = tabu[i]; if (s.m_level[lit.var()] == 0) continue; if (value_at(lit, m_model) == l_false) { flip(lit); } m_tabu[lit.var()] = true; } for (unsigned i = 0; i < s.m_trail.size(); ++i) { literal lit = s.m_trail[i]; if (s.m_level[lit.var()] > 0) break; if (value_at(lit, m_model) != l_true) { flip(lit); } m_tabu[lit.var()] = true; } } void sls::init_use() { m_use_list.reset(); m_use_list.resize(s.num_vars()*2); unsigned sz = m_clauses.size(); for (unsigned i = 0; i < sz; ++i) { clause const& c = *m_clauses[i]; unsigned csz = c.size(); for (unsigned j = 0; j < csz; ++j) { m_use_list[c[j].index()].push_back(i); } } DEBUG_CODE(check_use_list();); } unsigned_vector const& sls::get_use(literal lit) { SASSERT(lit.index() < m_use_list.size()); return m_use_list[lit.index()]; } unsigned sls::get_break_count(literal lit, unsigned min_break) { SASSERT(value_at(lit, m_model) == l_false); unsigned result = 0; unsigned_vector const& uses = get_use(~lit); unsigned sz = uses.size(); for (unsigned i = 0; i < sz; ++i) { if (m_num_true[uses[i]] == 1) { ++result; if (result > min_break) return result; } } return result; } bool sls::pick_flip(literal& lit) { unsigned clause_idx = m_false.choose(m_rand); clause const& c = *m_clauses[clause_idx]; SASSERT(!c.satisfied_by(m_model)); unsigned min_break = UINT_MAX; unsigned sz = c.size(); m_min_vars.reset(); for (unsigned i = 0; i < sz; ++i) { lit = c[i]; if (m_tabu[lit.var()]) continue; unsigned break_count = get_break_count(lit, min_break); if (break_count < min_break) { min_break = break_count; m_min_vars.reset(); m_min_vars.push_back(lit); } else if (break_count == min_break) { m_min_vars.push_back(lit); } } if (min_break == 0 || (!m_min_vars.empty() && m_rand(100) >= m_prob_choose_min_var)) { lit = m_min_vars[m_rand(m_min_vars.size())]; return true; } else if (min_break == UINT_MAX) { return false; } else { lit = c[m_rand(c.size())]; return !m_tabu[lit.var()]; } } void sls::flip() { literal lit; if (pick_flip(lit)) { flip(lit); } } void sls::flip(literal lit) { //IF_VERBOSE(0, verbose_stream() << lit << " ";); SASSERT(value_at(lit, m_model) == l_false); SASSERT(!m_tabu[lit.var()]); m_model[lit.var()] = lit.sign()?l_false:l_true; SASSERT(value_at(lit, m_model) == l_true); unsigned_vector const& use1 = get_use(lit); unsigned sz = use1.size(); for (unsigned i = 0; i < sz; ++i) { unsigned cl = use1[i]; m_num_true[cl]++; SASSERT(m_num_true[cl] <= m_clauses[cl]->size()); if (m_num_true[cl] == 1) m_false.remove(cl); } unsigned_vector const& use2 = get_use(~lit); sz = use2.size(); for (unsigned i = 0; i < sz; ++i) { unsigned cl = use2[i]; SASSERT(m_num_true[cl] > 0); m_num_true[cl]--; if (m_num_true[cl] == 0) m_false.insert(cl); } } void sls::check_invariant() { for (unsigned i = 0; i < m_clauses.size(); ++i) { clause const& c = *m_clauses[i]; bool is_sat = c.satisfied_by(m_model); SASSERT(is_sat != m_false.contains(i)); SASSERT(is_sat == (m_num_true[i] > 0)); } } void sls::check_use_list() { for (unsigned i = 0; i < m_clauses.size(); ++i) { clause const& c = *m_clauses[i]; for (unsigned j = 0; j < c.size(); ++j) { unsigned idx = c[j].index(); SASSERT(m_use_list[idx].contains(i)); } } for (unsigned i = 0; i < m_use_list.size(); ++i) { literal lit = to_literal(i); for (unsigned j = 0; j < m_use_list[i].size(); ++j) { clause const& c = *m_clauses[m_use_list[i][j]]; bool found = false; for (unsigned k = 0; !found && k < c.size(); ++k) { found = c[k] == lit; } SASSERT(found); } } } void sls::display(std::ostream& out) const { out << "Model\n"; for (bool_var v = 0; v < m_model.size(); ++v) { out << v << ": " << m_model[v] << "\n"; } out << "Clauses\n"; unsigned sz = m_false.num_elems(); for (unsigned i = 0; i < sz; ++i) { out << *m_clauses[m_false[i]] << "\n"; } for (unsigned i = 0; i < m_clauses.size(); ++i) { if (m_false.contains(i)) continue; clause const& c = *m_clauses[i]; out << c << " " << m_num_true[i] << "\n"; } bool has_tabu = false; for (unsigned i = 0; !has_tabu && i < m_tabu.size(); ++i) { has_tabu = m_tabu[i]; } if (has_tabu) { out << "Tabu: "; for (unsigned i = 0; i < m_tabu.size(); ++i) { if (m_tabu[i]) { literal lit(i, false); if (value_at(lit, m_model) == l_false) lit.neg(); out << lit << " "; } } out << "\n"; } } wsls::wsls(solver& s): sls(s) { m_smoothing_probability = 1; // 1/1000 } wsls::~wsls() {} void wsls::set_soft(unsigned sz, literal const* lits, double const* weights) { m_soft.reset(); m_weights.reset(); m_soft.append(sz, lits); m_weights.append(sz, weights); } void wsls::opt(unsigned sz, literal const* tabu, bool reuse_model) { init(sz, tabu, reuse_model); // // Initialize m_clause_weights, m_hscore, m_sscore. // m_best_value = m_false.empty()?evaluate_model(m_model):-1.0; m_best_model.reset(); m_clause_weights.reset(); m_hscore.reset(); m_sscore.reset(); m_H.reset(); m_S.reset(); m_best_model.append(s.get_model()); m_clause_weights.resize(m_clauses.size(), 1); m_sscore.resize(s.num_vars(), 0.0); m_hscore.resize(s.num_vars(), 0); for (unsigned i = 0; i < m_soft.size(); ++i) { literal lit = m_soft[i]; m_sscore[lit.var()] = m_weights[i]; if (value_at(lit, m_model) == l_true) { m_sscore[lit.var()] = -m_sscore[lit.var()]; } } for (bool_var i = 0; i < s.num_vars(); ++i) { m_hscore[i] = compute_hscore(i); refresh_scores(i); } DEBUG_CODE(check_invariant();); unsigned i = 0; for (; !m_cancel && m_best_value > 0 && i < m_max_tries; ++i) { wflip(); if (m_false.empty()) { double val = evaluate_model(m_model); if (val < m_best_value || m_best_value < 0.0) { m_best_value = val; m_best_model.reset(); m_best_model.append(m_model); s.set_model(m_best_model); IF_VERBOSE(1, verbose_stream() << "new value: " << val << " @ " << i << "\n";); if (i*2 > m_max_tries) { m_max_tries *= 2; } } } } TRACE("sat", display(tout);); IF_VERBOSE(0, verbose_stream() << "tries " << i << "\n";); } void wsls::wflip() { literal lit; if (pick_wflip(lit)) { // IF_VERBOSE(0, verbose_stream() << lit << " ";); wflip(lit); } } bool wsls::pick_wflip(literal & lit) { unsigned idx; if (!m_H.empty()) { idx = m_H.choose(m_rand); lit = literal(idx, false); if (value_at(lit, m_model) == l_true) lit.neg(); SASSERT(value_at(lit, m_model) == l_false); TRACE("sat", tout << "flip H(" << m_H.num_elems() << ") " << lit << "\n";); } else if (!m_S.empty()) { double score = 0.0; m_min_vars.reset(); for (unsigned i = 0; i < m_S.num_elems(); ++i) { unsigned v = m_S[i]; SASSERT(m_sscore[v] > 0.0); if (m_sscore[v] > score) { m_min_vars.reset(); m_min_vars.push_back(literal(v, false)); score = m_sscore[v]; } else if (m_sscore[v] == score) { m_min_vars.push_back(literal(v, false)); } } lit = m_min_vars[m_rand(m_min_vars.size())]; // pick with largest sscore. SASSERT(value_at(lit, m_model) == l_false); TRACE("sat", tout << "flip S(" << m_min_vars.size() << "," << score << ") " << lit << "\n";); } else { update_hard_weights(); if (!m_false.empty()) { unsigned cls_idx = m_false.choose(m_rand); clause const& c = *m_clauses[cls_idx]; lit = c[m_rand(c.size())]; TRACE("sat", tout << "flip hard(" << m_false.num_elems() << "," << c.size() << ") " << lit << "\n";); } else { m_min_vars.reset(); for (unsigned i = 0; i < m_soft.size(); ++i) { lit = m_soft[i]; if (value_at(lit, m_model) == l_false) { m_min_vars.push_back(lit); } } if (m_min_vars.empty()) { SASSERT(m_best_value == 0.0); UNREACHABLE(); // we should have exited the main loop before. return false; } else { lit = m_min_vars[m_rand(m_min_vars.size())]; } TRACE("sat", tout << "flip soft(" << m_min_vars.size() << ", " << m_sscore[lit.var()] << ") " << lit << "\n";); } SASSERT(value_at(lit, m_model) == l_false); } return !m_tabu[lit.var()]; } void wsls::wflip(literal lit) { flip(lit); unsigned v = lit.var(); m_sscore[v] = -m_sscore[v]; m_hscore[v] = compute_hscore(v); refresh_scores(v); recompute_hscores(lit); } void wsls::update_hard_weights() { unsigned csz = m_clauses.size(); if (m_smoothing_probability >= m_rand(1000)) { for (unsigned i = 0; i < csz; ++i) { if (m_clause_weights[i] > 1 && !m_false.contains(i)) { --m_clause_weights[i]; if (m_num_true[i] == 1) { clause const& c = *m_clauses[i]; unsigned sz = c.size(); for (unsigned j = 0; j < sz; ++j) { if (value_at(c[j], m_model) == l_true) { ++m_hscore[c[j].var()]; refresh_scores(c[j].var()); break; } } } } } } else { for (unsigned i = 0; i < csz; ++i) { if (m_false.contains(i)) { ++m_clause_weights[i]; clause const& c = *m_clauses[i]; unsigned sz = c.size(); for (unsigned j = 0; j < sz; ++j) { ++m_hscore[c[j].var()]; refresh_scores(c[j].var()); } } } } DEBUG_CODE(check_invariant();); } double wsls::evaluate_model(model& mdl) { SASSERT(m_false.empty()); double result = 0.0; for (unsigned i = 0; i < m_soft.size(); ++i) { literal lit = m_soft[i]; if (value_at(lit, mdl) != l_true) { result += m_weights[i]; } } return result; } int wsls::compute_hscore(bool_var v) { literal lit(v, false); if (value_at(lit, m_model) == l_false) { lit.neg(); } SASSERT(value_at(lit, m_model) == l_true); int hs = 0; unsigned_vector const& use1 = get_use(~lit); unsigned sz = use1.size(); for (unsigned i = 0; i < sz; ++i) { unsigned cl = use1[i]; if (m_num_true[cl] == 0) { SASSERT(m_false.contains(cl)); hs += m_clause_weights[cl]; } else { SASSERT(!m_false.contains(cl)); } } unsigned_vector const& use2 = get_use(lit); sz = use2.size(); for (unsigned i = 0; i < sz; ++i) { unsigned cl = use2[i]; if (m_num_true[cl] == 1) { SASSERT(!m_false.contains(cl)); hs -= m_clause_weights[cl]; } } return hs; } void wsls::recompute_hscores(literal lit) { SASSERT(value_at(lit, m_model) == l_true); bool_var v = lit.var(); TRACE("sat", tout << v << " := " << m_hscore[v] << "\n";); unsigned_vector const& use1 = get_use(lit); unsigned sz = use1.size(); for (unsigned i = 0; i < sz; ++i) { unsigned cl = use1[i]; TRACE("sat", tout << *m_clauses[cl] << " " << m_num_true[cl] << "\n";); SASSERT(m_num_true[cl] > 0); if (m_num_true[cl] == 1) { // num_true 0 -> 1 // other literals don't have upside any more. // subtract one from all other literals adjust_all_values(lit, cl, -static_cast(m_clause_weights[cl])); } else if (m_num_true[cl] == 2) { // num_true 1 -> 2, previous critical literal is no longer critical adjust_pivot_value(lit, cl, +m_clause_weights[cl]); } } unsigned_vector const& use2 = get_use(~lit); sz = use2.size(); for (unsigned i = 0; i < sz; ++i) { unsigned cl = use2[i]; TRACE("sat", tout << *m_clauses[cl] << " " << m_num_true[cl] << "\n";); if (m_num_true[cl] == 0) { // num_true 1 -> 0 // all variables became critical. adjust_all_values(~lit, cl, +m_clause_weights[cl]); } else if (m_num_true[cl] == 1) { adjust_pivot_value(~lit, cl, -static_cast(m_clause_weights[cl])); } // else n+1 -> n >= 2 } } void wsls::adjust_all_values(literal lit, unsigned cl, int delta) { clause const& c = *m_clauses[cl]; unsigned sz = c.size(); TRACE("sat", tout << lit << " " << c << " delta: " << delta << " nt: " << m_num_true[cl] << "\n";); for (unsigned i = 0; i < sz; ++i) { literal lit2 = c[i]; if (lit2 != lit) { TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); m_hscore[lit2.var()] += delta; TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); refresh_scores(lit2.var()); } } } void wsls::adjust_pivot_value(literal lit, unsigned cl, int delta) { clause const& c = *m_clauses[cl]; unsigned csz = c.size(); for (unsigned j = 0; j < csz; ++j) { literal lit2 = c[j]; if (lit2 != lit && value_at(lit2, m_model) == l_true) { TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); m_hscore[lit2.var()] += delta; TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); refresh_scores(lit2.var()); break; } } } void wsls::refresh_scores(bool_var v) { if (m_hscore[v] > 0 && !m_tabu[v] && m_sscore[v] == 0) { m_H.insert(v); } else { m_H.remove(v); } if (m_sscore[v] > 0) { if (m_hscore[v] == 0 && !m_tabu[v]) { m_S.insert(v); } else { m_S.remove(v); } } else if (m_sscore[v] < 0) { m_S.remove(v); } } void wsls::check_invariant() { sls::check_invariant(); // The hscore is the reward for flipping the truth value of variable v. // hscore(v) = Sum weight(c) for num_true(c) = 0 and v in c // - Sum weight(c) for num_true(c) = 1 and (v in c, M(v) or !v in c and !M(v)) for (unsigned v = 0; v < s.num_vars(); ++v) { int hs = compute_hscore(v); CTRACE("sat", hs != m_hscore[v], display(tout << v << " - computed: " << hs << " - assigned: " << m_hscore[v] << "\n");); SASSERT(m_hscore[v] == hs); } // The score(v) is the reward on soft clauses for flipping v. for (unsigned j = 0; j < m_soft.size(); ++j) { unsigned v = m_soft[j].var(); double ss = (l_true == value_at(m_soft[j], m_model))?(-m_weights[j]):m_weights[j]; SASSERT(m_sscore[v] == ss); } // m_H are values such that m_hscore > 0 and sscore = 0. for (bool_var v = 0; v < m_hscore.size(); ++v) { SASSERT((m_hscore[v] > 0 && !m_tabu[v] && m_sscore[v] == 0) == m_H.contains(v)); } // m_S are values such that hscore = 0, sscore > 0 for (bool_var v = 0; v < m_sscore.size(); ++v) { SASSERT((m_hscore[v] == 0 && m_sscore[v] > 0 && !m_tabu[v]) == m_S.contains(v)); } } void wsls::display(std::ostream& out) const { sls::display(out); out << "Best model\n"; for (bool_var v = 0; v < m_best_model.size(); ++v) { out << v << ": " << m_best_model[v] << " h: " << m_hscore[v]; if (m_sscore[v] != 0.0) out << " s: " << m_sscore[v]; out << "\n"; } } }; z3-z3-4.4.1/src/sat/sat_sls.h000066400000000000000000000075361260446376700156370ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sat_sls.h Abstract: SLS for clauses in SAT solver Author: Nikolaj Bjorner (nbjorner) 2014-12-8 Notes: --*/ #ifndef SAT_SLS_H_ #define SAT_SLS_H_ #include "util.h" #include "sat_simplifier.h" namespace sat { class index_set { unsigned_vector m_elems; unsigned_vector m_index; public: unsigned num_elems() const { return m_elems.size(); } unsigned operator[](unsigned idx) const { return m_elems[idx]; } void reset() { m_elems.reset(); m_index.reset(); } bool empty() const { return m_elems.empty(); } bool contains(unsigned idx) const; void insert(unsigned idx); void remove(unsigned idx); unsigned choose(random_gen& rnd) const; }; class sls { protected: solver& s; random_gen m_rand; unsigned m_max_tries; unsigned m_prob_choose_min_var; // number between 0 and 99. unsigned m_clause_generation; ptr_vector m_clauses; // vector of all clauses. index_set m_false; // clauses currently false vector m_use_list; // use lists for literals unsigned_vector m_num_true; // per clause, count of # true literals svector m_min_vars; // literals with smallest break count model m_model; // current model clause_allocator m_alloc; // clause allocator clause_vector m_bin_clauses; // binary clauses svector m_tabu; // variables that cannot be swapped volatile bool m_cancel; public: sls(solver& s); virtual ~sls(); lbool operator()(unsigned sz, literal const* tabu, bool reuse_model); void set_cancel(bool f) { m_cancel = f; } void set_max_tries(unsigned mx) { m_max_tries = mx; } virtual void display(std::ostream& out) const; protected: void init(unsigned sz, literal const* tabu, bool reuse_model); void init_tabu(unsigned sz, literal const* tabu); void init_model(); void init_use(); void init_clauses(); unsigned_vector const& get_use(literal lit); void flip(literal lit); virtual void check_invariant(); void check_use_list(); private: bool pick_flip(literal& lit); void flip(); unsigned get_break_count(literal lit, unsigned min_break); }; /** \brief sls with weighted soft clauses. */ class wsls : public sls { unsigned_vector m_clause_weights; svector m_hscore; svector m_sscore; literal_vector m_soft; svector m_weights; double m_best_value; model m_best_model; index_set m_H, m_S; unsigned m_smoothing_probability; public: wsls(solver& s); virtual ~wsls(); void set_soft(unsigned sz, literal const* lits, double const* weights); bool has_soft() const { return !m_soft.empty(); } void opt(unsigned sz, literal const* tabu, bool reuse_model); virtual void display(std::ostream& out) const; double evaluate_model(model& mdl); private: void wflip(); void wflip(literal lit); void update_hard_weights(); bool pick_wflip(literal & lit); virtual void check_invariant(); void refresh_scores(bool_var v); int compute_hscore(bool_var v); void recompute_hscores(literal lit); void adjust_all_values(literal lit, unsigned cl, int delta); void adjust_pivot_value(literal lit, unsigned cl, int delta); }; }; #endif z3-z3-4.4.1/src/sat/sat_solver.cpp000066400000000000000000003320361260446376700166770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_solver.cpp Abstract: SAT solver main class. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #include"sat_solver.h" #include"sat_integrity_checker.h" #include"luby.h" #include"trace.h" #include"sat_bceq.h" // define to update glue during propagation #define UPDATE_GLUE // define to create a copy of the solver before starting the search // useful for checking models // #define CLONE_BEFORE_SOLVING namespace sat { solver::solver(params_ref const & p, reslimit& l, extension * ext): m_cancel(false), m_rlimit(l), m_config(p), m_ext(ext), m_cleaner(*this), m_simplifier(*this, p), m_scc(*this, p), m_asymm_branch(*this, p), m_probing(*this, p), m_mus(*this), m_wsls(*this), m_inconsistent(false), m_num_frozen(0), m_activity_inc(128), m_case_split_queue(m_activity), m_qhead(0), m_scope_lvl(0), m_params(p) { updt_params(p); m_conflicts_since_gc = 0; m_conflicts = 0; m_next_simplify = 0; m_num_checkpoints = 0; m_initializing_preferred = false; } solver::~solver() { SASSERT(check_invariant()); TRACE("sat", tout << "Delete clauses\n";); del_clauses(m_clauses.begin(), m_clauses.end()); TRACE("sat", tout << "Delete learned\n";); del_clauses(m_learned.begin(), m_learned.end()); } void solver::del_clauses(clause * const * begin, clause * const * end) { for (clause * const * it = begin; it != end; ++it) { m_cls_allocator.del_clause(*it); } ++m_stats.m_non_learned_generation; } void solver::copy(solver const & src) { SASSERT(m_mc.empty() && src.m_mc.empty()); // create new vars if (num_vars() < src.num_vars()) { for (bool_var v = num_vars(); v < src.num_vars(); v++) { SASSERT(!src.was_eliminated(v)); bool ext = src.m_external[v] != 0; bool dvar = src.m_decision[v] != 0; bool_var new_v = mk_var(ext, dvar); SASSERT(v == new_v); } } { // copy binary clauses vector::const_iterator it = src.m_watches.begin(); vector::const_iterator end = src.m_watches.begin(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { watch_list const & wlist = *it; literal l = ~to_literal(l_idx); watch_list::const_iterator it2 = wlist.begin(); watch_list::const_iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { if (!it2->is_binary_non_learned_clause()) continue; literal l2 = it2->get_literal(); mk_clause_core(l, l2); } } } { literal_vector buffer; // copy clause clause_vector::const_iterator it = src.m_clauses.begin(); clause_vector::const_iterator end = src.m_clauses.end(); for (; it != end; ++it) { clause const & c = *(*it); buffer.reset(); for (unsigned i = 0; i < c.size(); i++) buffer.push_back(c[i]); mk_clause_core(buffer); } } } // ----------------------- // // Variable & Clause creation // // ----------------------- bool_var solver::mk_var(bool ext, bool dvar) { m_stats.m_mk_var++; bool_var v = m_level.size(); m_watches.push_back(watch_list()); m_watches.push_back(watch_list()); m_assignment.push_back(l_undef); m_assignment.push_back(l_undef); m_justification.push_back(justification()); m_decision.push_back(dvar); m_eliminated.push_back(false); m_external.push_back(ext); m_activity.push_back(0); m_level.push_back(UINT_MAX); m_mark.push_back(false); m_lit_mark.push_back(false); m_lit_mark.push_back(false); m_phase.push_back(PHASE_NOT_AVAILABLE); m_prev_phase.push_back(PHASE_NOT_AVAILABLE); m_assigned_since_gc.push_back(false); m_case_split_queue.mk_var_eh(v); m_simplifier.insert_todo(v); SASSERT(!was_eliminated(v)); return v; } void solver::mk_clause(unsigned num_lits, literal * lits) { DEBUG_CODE({ for (unsigned i = 0; i < num_lits; i++) SASSERT(m_eliminated[lits[i].var()] == false); }); if (m_user_scope_literals.empty()) { mk_clause_core(num_lits, lits, false); } else { m_aux_literals.reset(); m_aux_literals.append(num_lits, lits); m_aux_literals.append(m_user_scope_literals); mk_clause_core(m_aux_literals.size(), m_aux_literals.c_ptr(), false); } } void solver::mk_clause(literal l1, literal l2) { literal ls[2] = { l1, l2 }; mk_clause(2, ls); } void solver::mk_clause(literal l1, literal l2, literal l3) { literal ls[3] = { l1, l2, l3 }; mk_clause(3, ls); } void solver::del_clause(clause& c) { if (!c.is_learned()) m_stats.m_non_learned_generation++; m_cls_allocator.del_clause(&c); m_stats.m_del_clause++; } clause * solver::mk_clause_core(unsigned num_lits, literal * lits, bool learned) { TRACE("sat", tout << "mk_clause: " << mk_lits_pp(num_lits, lits) << "\n";); if (!learned) { bool keep = simplify_clause(num_lits, lits); TRACE("sat_mk_clause", tout << "mk_clause (after simp), keep: " << keep << "\n" << mk_lits_pp(num_lits, lits) << "\n";); if (!keep) { return 0; // clause is equivalent to true. } ++m_stats.m_non_learned_generation; } switch (num_lits) { case 0: set_conflict(justification()); return 0; case 1: assign(lits[0], justification()); return 0; case 2: mk_bin_clause(lits[0], lits[1], learned); return 0; case 3: return mk_ter_clause(lits, learned); default: return mk_nary_clause(num_lits, lits, learned); } } void solver::mk_bin_clause(literal l1, literal l2, bool learned) { if (propagate_bin_clause(l1, l2)) { if (scope_lvl() == 0) return; if (!learned) m_clauses_to_reinit.push_back(clause_wrapper(l1, l2)); } m_stats.m_mk_bin_clause++; m_watches[(~l1).index()].push_back(watched(l2, learned)); m_watches[(~l2).index()].push_back(watched(l1, learned)); } bool solver::propagate_bin_clause(literal l1, literal l2) { if (value(l2) == l_false) { m_stats.m_bin_propagate++; assign(l1, justification(l2)); return true; } else if (value(l1) == l_false) { m_stats.m_bin_propagate++; assign(l2, justification(l1)); return true; } return false; } void solver::push_reinit_stack(clause & c) { m_clauses_to_reinit.push_back(clause_wrapper(c)); c.set_reinit_stack(true); } clause * solver::mk_ter_clause(literal * lits, bool learned) { m_stats.m_mk_ter_clause++; clause * r = m_cls_allocator.mk_clause(3, lits, learned); bool reinit; attach_ter_clause(*r, reinit); if (!learned && reinit) { TRACE("sat_reinit", tout << "adding to reinit stack: " << *r << "\n";); push_reinit_stack(*r); } if (learned) m_learned.push_back(r); else m_clauses.push_back(r); return r; } void solver::attach_ter_clause(clause & c, bool & reinit) { reinit = false; m_watches[(~c[0]).index()].push_back(watched(c[1], c[2])); m_watches[(~c[1]).index()].push_back(watched(c[0], c[2])); m_watches[(~c[2]).index()].push_back(watched(c[0], c[1])); if (scope_lvl() > 0) { if (value(c[1]) == l_false && value(c[2]) == l_false) { m_stats.m_ter_propagate++; assign(c[0], justification(c[1], c[2])); reinit = true; } else if (value(c[0]) == l_false && value(c[2]) == l_false) { m_stats.m_ter_propagate++; assign(c[1], justification(c[0], c[2])); reinit = true; } else if (value(c[0]) == l_false && value(c[1]) == l_false) { m_stats.m_ter_propagate++; assign(c[2], justification(c[0], c[1])); reinit = true; } } } clause * solver::mk_nary_clause(unsigned num_lits, literal * lits, bool learned) { m_stats.m_mk_clause++; clause * r = m_cls_allocator.mk_clause(num_lits, lits, learned); SASSERT(!learned || r->is_learned()); bool reinit; attach_nary_clause(*r, reinit); if (!learned && reinit) { TRACE("sat_reinit", tout << "adding to reinit stack: " << *r << "\n";); push_reinit_stack(*r); } if (learned) m_learned.push_back(r); else m_clauses.push_back(r); return r; } void solver::attach_nary_clause(clause & c, bool & reinit) { reinit = false; clause_offset cls_off = m_cls_allocator.get_offset(&c); if (scope_lvl() > 0) { if (c.is_learned()) { unsigned w2_idx = select_learned_watch_lit(c); std::swap(c[1], c[w2_idx]); } else { unsigned w1_idx = select_watch_lit(c, 0); std::swap(c[0], c[w1_idx]); unsigned w2_idx = select_watch_lit(c, 1); std::swap(c[1], c[w2_idx]); } if (value(c[0]) == l_false) { m_stats.m_propagate++; assign(c[1], justification(cls_off)); reinit = true; } else if (value(c[1]) == l_false) { m_stats.m_propagate++; assign(c[0], justification(cls_off)); reinit = true; } } unsigned some_idx = c.size() >> 1; literal block_lit = c[some_idx]; m_watches[(~c[0]).index()].push_back(watched(block_lit, cls_off)); m_watches[(~c[1]).index()].push_back(watched(block_lit, cls_off)); } void solver::attach_clause(clause & c, bool & reinit) { SASSERT(c.size() > 2); reinit = false; if (c.size() == 3) attach_ter_clause(c, reinit); else attach_nary_clause(c, reinit); } /** \brief Select a watch literal starting the search at the given position. This method is only used for clauses created during the search. I use the following rules to select a watch literal. 1- select a literal l in idx >= starting_at such that value(l) = l_true, and for all l' in idx' >= starting_at . value(l') = l_true implies lvl(l) <= lvl(l') The purpose of this rule is to make the clause inactive for as long as possible. A clause is inactive when it contains a literal assigned to true. 2- if there isn't a literal assigned to true, then select an unassigned literal l in idx >= starting_at 3- if there isn't a literal l in idx >= starting_at such that value(l) = l_true or value(l) = l_undef (that is, all literals at positions >= starting_at are assigned to false), then peek the literal l such that for all l' starting at starting_at lvl(l) >= lvl(l') Without rule 3, boolean propagation is incomplete, that is, it may miss possible propagations. \remark The method select_lemma_watch_lit is used to select the watch literal for regular learned clauses. */ unsigned solver::select_watch_lit(clause const & cls, unsigned starting_at) const { SASSERT(cls.size() >= 2); unsigned min_true_idx = UINT_MAX; unsigned max_false_idx = UINT_MAX; unsigned unknown_idx = UINT_MAX; unsigned n = cls.size(); for (unsigned i = starting_at; i < n; i++) { literal l = cls[i]; switch(value(l)) { case l_false: if (max_false_idx == UINT_MAX || lvl(l) > lvl(cls[max_false_idx])) max_false_idx = i; break; case l_undef: unknown_idx = i; break; case l_true: if (min_true_idx == UINT_MAX || lvl(l) < lvl(cls[min_true_idx])) min_true_idx = i; break; } } if (min_true_idx != UINT_MAX) return min_true_idx; if (unknown_idx != UINT_MAX) return unknown_idx; SASSERT(max_false_idx != UINT_MAX); return max_false_idx; } /** \brief The learned clauses (lemmas) produced by the SAT solver have the property that the first literal will be implied by it after backtracking. All other literals are assigned to (or implied to be) false when the learned clause is created. The first watch literal will always be the first literal. The second watch literal is computed by this method. It should be the literal with the highest decision level. // TODO: do we really need this? strength the conflict resolution */ unsigned solver::select_learned_watch_lit(clause const & cls) const { SASSERT(cls.size() >= 2); unsigned max_false_idx = UINT_MAX; unsigned num_lits = cls.size(); for (unsigned i = 1; i < num_lits; i++) { literal l = cls[i]; lbool val = value(l); CTRACE("sat", val != l_false, tout << l << ":=" << val;); SASSERT(val == l_false); if (max_false_idx == UINT_MAX || lvl(l) > lvl(cls[max_false_idx])) max_false_idx = i; } return max_false_idx; } template bool solver::simplify_clause_core(unsigned & num_lits, literal * lits) const { std::sort(lits, lits+num_lits); literal prev = null_literal; unsigned i = 0; unsigned j = 0; for (; i < num_lits; i++) { literal curr = lits[i]; lbool val = value(curr); if (!lvl0 && m_level[curr.var()] > 0) val = l_undef; switch (val) { case l_false: break; // ignore literal case l_undef: if (curr == ~prev) return false; // clause is equivalent to true if (curr != prev) { prev = curr; if (i != j) lits[j] = lits[i]; j++; } break; case l_true: return false; // clause is equivalent to true } } num_lits = j; return true; } bool solver::simplify_clause(unsigned & num_lits, literal * lits) const { if (scope_lvl() == 0) return simplify_clause_core(num_lits, lits); else return simplify_clause_core(num_lits, lits); } void solver::dettach_bin_clause(literal l1, literal l2, bool learned) { get_wlist(~l1).erase(watched(l2, learned)); get_wlist(~l2).erase(watched(l1, learned)); } void solver::dettach_clause(clause & c) { if (c.size() == 3) dettach_ter_clause(c); else dettach_nary_clause(c); } void solver::dettach_nary_clause(clause & c) { clause_offset cls_off = get_offset(c); erase_clause_watch(get_wlist(~c[0]), cls_off); erase_clause_watch(get_wlist(~c[1]), cls_off); } void solver::dettach_ter_clause(clause & c) { erase_ternary_watch(get_wlist(~c[0]), c[1], c[2]); erase_ternary_watch(get_wlist(~c[1]), c[0], c[2]); erase_ternary_watch(get_wlist(~c[2]), c[0], c[1]); } // ----------------------- // // Basic // // ----------------------- void solver::set_conflict(justification c, literal not_l) { if (m_inconsistent) return; m_inconsistent = true; m_conflict = c; m_not_l = not_l; } void solver::assign_core(literal l, justification j) { SASSERT(value(l) == l_undef); TRACE("sat_assign_core", tout << l << "\n";); if (scope_lvl() == 0) j = justification(); // erase justification for level 0 m_assignment[l.index()] = l_true; m_assignment[(~l).index()] = l_false; bool_var v = l.var(); m_level[v] = scope_lvl(); m_justification[v] = j; m_phase[v] = static_cast(l.sign()); m_assigned_since_gc[v] = true; m_trail.push_back(l); if (m_ext && m_external[v]) m_ext->asserted(l); SASSERT(!l.sign() || m_phase[v] == NEG_PHASE); SASSERT(l.sign() || m_phase[v] == POS_PHASE); SASSERT(!l.sign() || value(v) == l_false); SASSERT(l.sign() || value(v) == l_true); SASSERT(value(l) == l_true); SASSERT(value(~l) == l_false); } lbool solver::status(clause const & c) const { bool found_undef = false; unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { switch (value(c[i])) { case l_true: return l_true; case l_undef: found_undef = true; break; default: break; } } return found_undef ? l_undef : l_false; } // ----------------------- // // Propagation // // ----------------------- bool solver::propagate_core(bool update) { if (m_inconsistent) return false; literal l, not_l, l1, l2; lbool val1, val2; bool keep; while (m_qhead < m_trail.size()) { checkpoint(); m_cleaner.dec(); SASSERT(!m_inconsistent); l = m_trail[m_qhead]; TRACE("sat_propagate", tout << "propagating: " << l << " " << m_justification[l.var()] << "\n";); m_qhead++; not_l = ~l; SASSERT(value(l) == l_true); SASSERT(value(not_l) == l_false); watch_list & wlist = m_watches[l.index()]; m_asymm_branch.dec(wlist.size()); m_probing.dec(wlist.size()); watch_list::iterator it = wlist.begin(); watch_list::iterator it2 = it; watch_list::iterator end = wlist.end(); #define CONFLICT_CLEANUP() { \ for (; it != end; ++it, ++it2) \ *it2 = *it; \ wlist.set_end(it2); \ } for (; it != end; ++it) { switch (it->get_kind()) { case watched::BINARY: l1 = it->get_literal(); switch (value(l1)) { case l_false: CONFLICT_CLEANUP(); set_conflict(justification(not_l), ~l1); return false; case l_undef: m_stats.m_bin_propagate++; assign_core(l1, justification(not_l)); break; case l_true: break; // skip } *it2 = *it; it2++; break; case watched::TERNARY: l1 = it->get_literal1(); l2 = it->get_literal2(); val1 = value(l1); val2 = value(l2); if (val1 == l_false && val2 == l_undef) { m_stats.m_ter_propagate++; assign_core(l2, justification(l1, not_l)); } else if (val1 == l_undef && val2 == l_false) { m_stats.m_ter_propagate++; assign_core(l1, justification(l2, not_l)); } else if (val1 == l_false && val2 == l_false) { CONFLICT_CLEANUP(); set_conflict(justification(l1, not_l), ~l2); return false; } *it2 = *it; it2++; break; case watched::CLAUSE: { if (value(it->get_blocked_literal()) == l_true) { TRACE("propagate_clause_bug", tout << "blocked literal " << it->get_blocked_literal() << "\n"; clause_offset cls_off = it->get_clause_offset(); clause & c = *(m_cls_allocator.get_clause(cls_off)); tout << c << "\n";); *it2 = *it; it2++; break; } clause_offset cls_off = it->get_clause_offset(); clause & c = *(m_cls_allocator.get_clause(cls_off)); TRACE("propagate_clause_bug", tout << "processing... " << c << "\nwas_removed: " << c.was_removed() << "\n";); if (c[0] == not_l) std::swap(c[0], c[1]); CTRACE("propagate_bug", c[1] != not_l, tout << "l: " << l << " " << c << "\n";); if (c.was_removed() || c[1] != not_l) { // Remark: this method may be invoked when the watch lists are not in a consistent state, // and may contain dead/removed clauses, or clauses with removed literals. // See: method propagate_unit at sat_simplifier.cpp // So, we must check whether the clause was marked for deletion, or // c[1] != not_l *it2 = *it; it2++; break; } SASSERT(c[1] == not_l); if (value(c[0]) == l_true) { it2->set_clause(c[0], cls_off); it2++; break; } literal * l_it = c.begin() + 2; literal * l_end = c.end(); for (; l_it != l_end; ++l_it) { if (value(*l_it) != l_false) { c[1] = *l_it; *l_it = not_l; m_watches[(~c[1]).index()].push_back(watched(c[0], cls_off)); goto end_clause_case; } } SASSERT(value(c[0]) == l_false || value(c[0]) == l_undef); if (value(c[0]) == l_false) { c.mark_used(); CONFLICT_CLEANUP(); set_conflict(justification(cls_off)); return false; } else { *it2 = *it; it2++; m_stats.m_propagate++; c.mark_used(); assign_core(c[0], justification(cls_off)); #ifdef UPDATE_GLUE if (update && c.is_learned() && c.glue() > 2) { unsigned glue = num_diff_levels(c.size(), c.begin()); if (glue+1 < c.glue()) { c.set_glue(glue); } } #endif } end_clause_case: break; } case watched::EXT_CONSTRAINT: SASSERT(m_ext); m_ext->propagate(l, it->get_ext_constraint_idx(), keep); if (keep) { *it2 = *it; it2++; } if (m_inconsistent) { CONFLICT_CLEANUP(); return false; } break; default: UNREACHABLE(); break; } } wlist.set_end(it2); } SASSERT(m_qhead == m_trail.size()); SASSERT(!m_inconsistent); return true; } bool solver::propagate(bool update) { bool r = propagate_core(update); CASSERT("sat_propagate", check_invariant()); CASSERT("sat_missed_prop", check_missed_propagation()); return r; } // ----------------------- // // Search // // ----------------------- lbool solver::check(unsigned num_lits, literal const* lits, double const* weights, double max_weight) { pop_to_base_level(); IF_VERBOSE(2, verbose_stream() << "(sat.sat-solver)\n";); SASSERT(scope_lvl() == 0); #ifdef CLONE_BEFORE_SOLVING if (m_mc.empty()) { m_clone = alloc(solver, m_params, 0 /* do not clone extension */); SASSERT(m_clone); } #endif try { if (inconsistent()) return l_false; init_search(); propagate(false); if (inconsistent()) return l_false; init_assumptions(num_lits, lits, weights, max_weight); propagate(false); if (check_inconsistent()) return l_false; cleanup(); if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; lbool r = bounded_search(); if (r != l_undef) return r; pop_reinit(scope_lvl()); m_conflicts_since_restart = 0; m_restart_threshold = m_config.m_restart_initial; } // iff3_finder(*this)(); simplify_problem(); if (check_inconsistent()) return l_false; if (m_config.m_max_conflicts == 0) { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"abort: max-conflicts = 0\"\n";); return l_undef; } while (true) { SASSERT(!inconsistent()); lbool r = bounded_search(); if (r != l_undef) return r; if (m_conflicts > m_config.m_max_conflicts) { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"abort: max-conflicts = " << m_conflicts << "\"\n";); return l_undef; } restart(); simplify_problem(); if (check_inconsistent()) return l_false; gc(); } } catch (abort_solver) { return l_undef; } } bool_var solver::next_var() { bool_var next; if (m_rand() < static_cast(m_config.m_random_freq * random_gen::max_value())) { if (num_vars() == 0) return null_bool_var; next = m_rand() % num_vars(); TRACE("random_split", tout << "next: " << next << " value(next): " << value(next) << "\n";); if (value(next) == l_undef && !was_eliminated(next)) return next; } while (!m_case_split_queue.empty()) { next = m_case_split_queue.next_var(); if (value(next) == l_undef && !was_eliminated(next)) return next; } return null_bool_var; } bool solver::decide() { bool_var next = next_var(); if (next == null_bool_var) return false; push(); m_stats.m_decision++; lbool phase = m_ext ? m_ext->get_phase(next) : l_undef; if (phase == l_undef) { switch (m_config.m_phase) { case PS_ALWAYS_TRUE: phase = l_true; break; case PS_ALWAYS_FALSE: phase = l_false; break; case PS_CACHING: if (m_phase_cache_on && m_phase[next] != PHASE_NOT_AVAILABLE) phase = m_phase[next] == POS_PHASE ? l_true : l_false; else phase = l_false; break; case PS_RANDOM: phase = to_lbool((m_rand() % 2) == 0); break; default: UNREACHABLE(); phase = l_false; break; } } SASSERT(phase != l_undef); literal next_lit(next, phase == l_false); assign(next_lit, justification()); TRACE("sat_decide", tout << scope_lvl() << ": next-case-split: " << next_lit << "\n";); return true; } lbool solver::bounded_search() { while (true) { checkpoint(); while (true) { propagate(true); if (!inconsistent()) break; if (!resolve_conflict()) return l_false; if (m_conflicts > m_config.m_max_conflicts) return l_undef; if (m_conflicts_since_restart > m_restart_threshold) return l_undef; if (scope_lvl() == 0) { cleanup(); // cleaner may propagate frozen clauses if (inconsistent()) { TRACE("sat", tout << "conflict at level 0\n";); return l_false; } gc(); } } gc(); if (!decide()) { if (m_ext) { switch (m_ext->check()) { case CR_DONE: mk_model(); return l_true; case CR_CONTINUE: break; case CR_GIVEUP: throw abort_solver(); } } else { mk_model(); return l_true; } } } } bool solver::check_inconsistent() { if (inconsistent()) { if (tracking_assumptions()) resolve_conflict(); return true; } else { return false; } } void solver::init_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight) { if (num_lits == 0 && m_user_scope_literals.empty()) { return; } retry_init_assumptions: reset_assumptions(); push(); propagate(false); if (inconsistent()) { return; } TRACE("sat", for (unsigned i = 0; i < num_lits; ++i) tout << lits[i] << " "; tout << "\n"; if (!m_user_scope_literals.empty()) { tout << "user literals: " << m_user_scope_literals << "\n"; } m_mc.display(tout); ); for (unsigned i = 0; !inconsistent() && i < m_user_scope_literals.size(); ++i) { literal nlit = ~m_user_scope_literals[i]; assign(nlit, justification()); } if (weights && !inconsistent()) { if (m_config.m_optimize_model) { m_wsls.set_soft(num_lits, lits, weights); } if (!init_weighted_assumptions(num_lits, lits, weights, max_weight)) { goto retry_init_assumptions; } return; } for (unsigned i = 0; !inconsistent() && i < num_lits; ++i) { literal lit = lits[i]; SASSERT(is_external(lit.var())); add_assumption(lit); assign(lit, justification()); } } bool solver::init_weighted_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight) { flet _min1(m_config.m_minimize_core, false); m_weight = 0; m_blocker.reset(); svector values; unsigned num_cores = 0; for (unsigned i = 0; !inconsistent() && i < num_lits; ++i) { literal lit = lits[i]; SASSERT(is_external(lit.var())); TRACE("sat", tout << "propagate: " << lit << " " << value(lit) << "\n";); SASSERT(m_scope_lvl == 1); add_assumption(lit); switch(value(lit)) { case l_undef: values.push_back(l_true); assign(lit, justification()); if (num_cores*2 >= num_lits) { break; } propagate(false); if (inconsistent()) { flet _init(m_initializing_preferred, true); while (inconsistent()) { if (!resolve_conflict()) { return true; } propagate(true); } if (m_scope_lvl == 0) { return false; } // backjump to last consistent assumption: unsigned j; m_weight = 0; m_blocker.reset(); for (j = 0; j < i && value(lits[j]) == values[j]; ++j) { if (values[j] == l_false) { m_weight += weights[j]; m_blocker.push_back(lits[j]); } } SASSERT(value(lits[j]) != values[j]); SASSERT(j <= i); SASSERT(j == 0 || value(lits[j-1]) == values[j-1]); for (unsigned k = i; k >= j; --k) { if (is_assumption(lits[k])) { pop_assumption(); } } values.resize(j); TRACE("sat", tout << "backjump " << (i - j + 1) << " steps " << num_cores << "\n";); i = j - 1; } break; case l_false: ++num_cores; values.push_back(l_false); SASSERT(!inconsistent()); set_conflict(justification(), ~lit); m_conflict_lvl = scope_lvl(); resolve_conflict_for_unsat_core(); IF_VERBOSE(3, verbose_stream() << "core: " << m_core << "\n";); update_min_core(); SASSERT(m_min_core_valid); m_weight += weights[i]; if (m_weight <= max_weight) { m_blocker.push_back(lit); } TRACE("sat", tout << "core: " << m_core << "\nassumptions: " << m_assumptions << "\n";); SASSERT(m_core.size() <= m_assumptions.size()); SASSERT(m_assumptions.size() <= i+1); if (m_core.size() <= 3) { m_inconsistent = true; TRACE("opt", tout << "found small core: " << m_core << "\n";); IF_VERBOSE(11, verbose_stream() << "small core: " << m_core << "\n";); return true; } pop_assumption(); m_inconsistent = false; break; case l_true: values.push_back(l_true); SASSERT(m_justification[lit.var()].get_kind() != justification::NONE || lvl(lit) == 0); break; } } TRACE("sat", tout << "initialized\n";); IF_VERBOSE(11, verbose_stream() << "Blocker: " << m_blocker << "\nCore: " << m_min_core << "\n";); if (m_weight >= max_weight) { // block the current correction set candidate. ++m_stats.m_blocked_corr_sets; TRACE("opt", tout << "blocking soft correction set: " << m_blocker << "\n";); IF_VERBOSE(11, verbose_stream() << "blocking " << m_blocker << "\n";); pop_to_base_level(); mk_clause_core(m_blocker); return false; } return true; } void solver::update_min_core() { if (!m_min_core_valid || m_core.size() < m_min_core.size()) { m_min_core.reset(); m_min_core.append(m_core); m_min_core_valid = true; } } void solver::reset_assumptions() { m_assumptions.reset(); m_assumption_set.reset(); } void solver::add_assumption(literal lit) { m_assumption_set.insert(lit); m_assumptions.push_back(lit); } void solver::pop_assumption() { VERIFY(m_assumptions.back() == m_assumption_set.pop()); m_assumptions.pop_back(); } void solver::reassert_min_core() { SASSERT(m_min_core_valid); pop_to_base_level(); push(); reset_assumptions(); TRACE("sat", tout << "reassert: " << m_min_core << "\n";); for (unsigned i = 0; i < m_min_core.size(); ++i) { literal lit = m_min_core[i]; SASSERT(is_external(lit.var())); add_assumption(lit); assign(lit, justification()); } propagate(false); SASSERT(inconsistent()); } void solver::reinit_assumptions() { if (tracking_assumptions() && scope_lvl() == 0) { TRACE("sat", tout << m_assumptions << "\n";); push(); for (unsigned i = 0; !inconsistent() && i < m_user_scope_literals.size(); ++i) { assign(~m_user_scope_literals[i], justification()); } for (unsigned i = 0; !inconsistent() && i < m_assumptions.size(); ++i) { assign(m_assumptions[i], justification()); } } } bool solver::tracking_assumptions() const { return !m_assumptions.empty() || !m_user_scope_literals.empty(); } bool solver::is_assumption(literal l) const { return tracking_assumptions() && m_assumption_set.contains(l); } void solver::init_search() { m_model_is_current = false; m_phase_counter = 0; m_phase_cache_on = false; m_conflicts_since_restart = 0; m_restart_threshold = m_config.m_restart_initial; m_luby_idx = 1; m_gc_threshold = m_config.m_gc_initial; m_min_d_tk = 1.0; m_stopwatch.reset(); m_stopwatch.start(); m_core.reset(); m_min_core_valid = false; m_min_core.reset(); TRACE("sat", display(tout);); if (m_config.m_bcd) { bceq bc(*this); bc(); } } /** \brief Apply all simplifications. */ void solver::simplify_problem() { if (m_conflicts < m_next_simplify) { return; } IF_VERBOSE(2, verbose_stream() << "(sat.simplify)\n";); // Disable simplification during MUS computation. // if (m_mus.is_active()) return; TRACE("sat", tout << "simplify\n";); pop(scope_lvl()); SASSERT(scope_lvl() == 0); m_cleaner(); CASSERT("sat_simplify_bug", check_invariant()); m_scc(); CASSERT("sat_simplify_bug", check_invariant()); m_simplifier(false); CASSERT("sat_simplify_bug", check_invariant()); CASSERT("sat_missed_prop", check_missed_propagation()); if (!m_learned.empty()) { m_simplifier(true); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); } sort_watch_lits(); CASSERT("sat_simplify_bug", check_invariant()); m_probing(); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); m_asymm_branch(); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); if (m_ext) { m_ext->clauses_modifed(); m_ext->simplify(); } TRACE("sat", display(tout << "consistent: " << (!inconsistent()) << "\n");); reinit_assumptions(); if (m_next_simplify == 0) { m_next_simplify = m_config.m_restart_initial * m_config.m_simplify_mult1; } else { m_next_simplify = static_cast(m_conflicts * m_config.m_simplify_mult2); if (m_next_simplify > m_conflicts + m_config.m_simplify_max) m_next_simplify = m_conflicts + m_config.m_simplify_max; } } void solver::sort_watch_lits() { vector::iterator it = m_watches.begin(); vector::iterator end = m_watches.end(); for (; it != end; ++it) { watch_list & wlist = *it; std::stable_sort(wlist.begin(), wlist.end(), watched_lt()); } } void solver::set_model(model const& mdl) { m_model.reset(); m_model.append(mdl); m_model_is_current = !m_model.empty(); } void solver::mk_model() { m_model.reset(); m_model_is_current = true; unsigned num = num_vars(); m_model.resize(num, l_undef); for (bool_var v = 0; v < num; v++) { if (!was_eliminated(v)) m_model[v] = value(v); } TRACE("sat_mc_bug", m_mc.display(tout);); if (m_config.m_optimize_model) { m_wsls.opt(0, 0, false); } m_mc(m_model); TRACE("sat", for (bool_var v = 0; v < num; v++) tout << v << ": " << m_model[v] << "\n";); #ifndef _EXTERNAL_RELEASE IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"checking model\"\n";); if (!check_model(m_model)) throw solver_exception("check model failed"); if (m_clone) { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"checking model (on original set of clauses)\"\n";); if (!m_clone->check_model(m_model)) throw solver_exception("check model failed (for cloned solver)"); } #endif } bool solver::check_model(model const & m) const { bool ok = true; clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); clause_vector::const_iterator it = cs.begin(); clause_vector::const_iterator end = cs.end(); for (; it != end; ++it) { clause const & c = *(*it); if (!c.satisfied_by(m)) { TRACE("sat", tout << "failed: " << c << "\n"; tout << "assumptions: " << m_assumptions << "\n"; tout << "trail: " << m_trail << "\n"; tout << "model: " << m << "\n"; m_mc.display(tout); ); ok = false; } } } vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l = ~to_literal(l_idx); if (value_at(l, m) != l_true) { watch_list const & wlist = *it; watch_list::const_iterator it2 = wlist.begin(); watch_list::const_iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { if (!it2->is_binary_clause()) continue; literal l2 = it2->get_literal(); if (value_at(l2, m) != l_true) { TRACE("sat", tout << "failed binary: " << l << " " << l2 << " learned: " << it2->is_learned() << "\n"; m_mc.display(tout);); ok = false; } } } } for (unsigned i = 0; i < m_assumptions.size(); ++i) { if (value_at(m_assumptions[i], m) != l_true) { TRACE("sat", tout << m_assumptions[i] << " does not model check\n"; tout << "trail: " << m_trail << "\n"; tout << "model: " << m << "\n"; m_mc.display(tout); ); ok = false; } } if (ok && !m_mc.check_model(m)) { ok = false; TRACE("sat", tout << "model: " << m << "\n"; m_mc.display(tout);); } return ok; } void solver::restart() { m_stats.m_restart++; IF_VERBOSE(1, verbose_stream() << "(sat-restart :conflicts " << m_stats.m_conflict << " :decisions " << m_stats.m_decision << " :restarts " << m_stats.m_restart << mk_stat(*this) << " :time " << std::fixed << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n";); IF_VERBOSE(30, display_status(verbose_stream());); pop_reinit(scope_lvl()); m_conflicts_since_restart = 0; switch (m_config.m_restart) { case RS_GEOMETRIC: m_restart_threshold = static_cast(m_restart_threshold * m_config.m_restart_factor); break; case RS_LUBY: m_luby_idx++; m_restart_threshold = m_config.m_restart_initial * get_luby(m_luby_idx); break; default: UNREACHABLE(); break; } CASSERT("sat_restart", check_invariant()); } // ----------------------- // // GC // // ----------------------- void solver::gc() { if (m_conflicts_since_gc <= m_gc_threshold) return; CASSERT("sat_gc_bug", check_invariant()); switch (m_config.m_gc_strategy) { case GC_GLUE: gc_glue(); break; case GC_PSM: gc_psm(); break; case GC_GLUE_PSM: gc_glue_psm(); break; case GC_PSM_GLUE: gc_psm_glue(); break; case GC_DYN_PSM: if (m_scope_lvl != 0) return; gc_dyn_psm(); break; default: UNREACHABLE(); break; } m_conflicts_since_gc = 0; m_gc_threshold += m_config.m_gc_increment; CASSERT("sat_gc_bug", check_invariant()); } /** \brief Lex on (glue, size) */ struct glue_lt { bool operator()(clause const * c1, clause const * c2) const { if (c1->glue() < c2->glue()) return true; return c1->glue() == c2->glue() && c1->size() < c2->size(); } }; /** \brief Lex on (psm, size) */ struct psm_lt { bool operator()(clause const * c1, clause const * c2) const { if (c1->psm() < c2->psm()) return true; return c1->psm() == c2->psm() && c1->size() < c2->size(); } }; /** \brief Lex on (glue, psm, size) */ struct glue_psm_lt { bool operator()(clause const * c1, clause const * c2) const { if (c1->glue() < c2->glue()) return true; if (c1->glue() > c2->glue()) return false; if (c1->psm() < c2->psm()) return true; if (c1->psm() > c2->psm()) return false; return c1->size() < c2->size(); } }; /** \brief Lex on (psm, glue, size) */ struct psm_glue_lt { bool operator()(clause const * c1, clause const * c2) const { if (c1->psm() < c2->psm()) return true; if (c1->psm() > c2->psm()) return false; if (c1->glue() < c2->glue()) return true; if (c1->glue() > c2->glue()) return false; return c1->size() < c2->size(); } }; void solver::gc_glue() { std::stable_sort(m_learned.begin(), m_learned.end(), glue_lt()); gc_half("glue"); } void solver::gc_psm() { save_psm(); std::stable_sort(m_learned.begin(), m_learned.end(), psm_lt()); gc_half("psm"); } void solver::gc_glue_psm() { save_psm(); std::stable_sort(m_learned.begin(), m_learned.end(), glue_psm_lt()); gc_half("glue-psm"); } void solver::gc_psm_glue() { save_psm(); std::stable_sort(m_learned.begin(), m_learned.end(), psm_glue_lt()); gc_half("psm-glue"); } /** \brief Compute the psm of all learned clauses. */ void solver::save_psm() { clause_vector::iterator it = m_learned.begin(); clause_vector::iterator end = m_learned.end(); for (; it != end; ++it) { clause & c = *(*it); c.set_psm(psm(c)); } } /** \brief GC (the second) half of the clauses in the database. */ void solver::gc_half(char const * st_name) { TRACE("sat", tout << "gc\n";); unsigned sz = m_learned.size(); unsigned new_sz = sz/2; unsigned j = new_sz; for (unsigned i = new_sz; i < sz; i++) { clause & c = *(m_learned[i]); if (can_delete(c)) { dettach_clause(c); del_clause(c); } else { m_learned[j] = &c; j++; } } new_sz = j; m_stats.m_gc_clause += sz - new_sz; m_learned.shrink(new_sz); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << (sz - new_sz) << ")\n";); } /** \brief Use gc based on dynamic psm. Clauses are initially frozen. */ void solver::gc_dyn_psm() { TRACE("sat", tout << "gc\n";); // To do gc at scope_lvl() > 0, I will need to use the reinitialization stack, or live with the fact // that I may miss some propagations for reactivated clauses. SASSERT(scope_lvl() == 0); // compute // d_tk unsigned h = 0; unsigned V_tk = 0; for (bool_var v = 0; v < num_vars(); v++) { if (m_assigned_since_gc[v]) { V_tk++; m_assigned_since_gc[v] = false; } if (m_phase[v] != m_prev_phase[v]) { h++; m_prev_phase[v] = m_phase[v]; } } double d_tk = V_tk == 0 ? static_cast(num_vars() + 1) : static_cast(h)/static_cast(V_tk); if (d_tk < m_min_d_tk) m_min_d_tk = d_tk; TRACE("sat_frozen", tout << "m_min_d_tk: " << m_min_d_tk << "\n";); unsigned frozen = 0; unsigned deleted = 0; unsigned activated = 0; clause_vector::iterator it = m_learned.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = m_learned.end(); for (; it != end; ++it) { clause & c = *(*it); if (!c.frozen()) { // Active clause if (c.glue() > m_config.m_gc_small_lbd) { // I never delete clauses with small lbd if (c.was_used()) { c.reset_inact_rounds(); } else { c.inc_inact_rounds(); if (c.inact_rounds() > m_config.m_gc_k) { dettach_clause(c); del_clause(c); m_stats.m_gc_clause++; deleted++; continue; } } c.unmark_used(); if (psm(c) > static_cast(c.size() * m_min_d_tk)) { // move to frozen; TRACE("sat_frozen", tout << "freezing size: " << c.size() << " psm: " << psm(c) << " " << c << "\n";); dettach_clause(c); c.reset_inact_rounds(); c.freeze(); m_num_frozen++; frozen++; } } } else { // frozen clause clause & c = *(*it); if (psm(c) <= static_cast(c.size() * m_min_d_tk)) { c.unfreeze(); m_num_frozen--; activated++; if (!activate_frozen_clause(c)) { // clause was satisfied, reduced to a conflict, unit or binary clause. del_clause(c); continue; } } else { c.inc_inact_rounds(); if (c.inact_rounds() > m_config.m_gc_k) { m_num_frozen--; del_clause(c); m_stats.m_gc_clause++; deleted++; continue; } } } *it2 = *it; ++it2; } m_learned.set_end(it2); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :d_tk " << d_tk << " :min-d_tk " << m_min_d_tk << " :frozen " << frozen << " :activated " << activated << " :deleted " << deleted << ")\n";); } // return true if should keep the clause, and false if we should delete it. bool solver::activate_frozen_clause(clause & c) { TRACE("sat_gc", tout << "reactivating:\n" << c << "\n";); SASSERT(scope_lvl() == 0); // do some cleanup unsigned sz = c.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = c[i]; switch (value(l)) { case l_true: return false; case l_false: break; case l_undef: c[j] = c[i]; j++; break; } } TRACE("sat", tout << "after cleanup:\n" << mk_lits_pp(j, c.begin()) << "\n";); unsigned new_sz = j; switch (new_sz) { case 0: set_conflict(justification()); return false; case 1: assign(c[0], justification()); return false; case 2: mk_bin_clause(c[0], c[1], true); return false; default: c.shrink(new_sz); attach_clause(c); return true; } } /** \brief Compute phase saving measure for the given clause. */ unsigned solver::psm(clause const & c) const { unsigned r = 0; unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { literal l = c[i]; if (l.sign()) { if (m_phase[l.var()] == NEG_PHASE) r++; } else { if (m_phase[l.var()] == POS_PHASE) r++; } } return r; } // ----------------------- // // Conflict resolution // // ----------------------- bool solver::resolve_conflict() { while (true) { bool r = resolve_conflict_core(); CASSERT("sat_check_marks", check_marks()); // after pop, clauses are reinitialized, this may trigger another conflict. if (!r) return false; if (!inconsistent()) return true; } } bool solver::resolve_conflict_core() { m_stats.m_conflict++; m_conflicts++; m_conflicts_since_restart++; m_conflicts_since_gc++; m_conflict_lvl = get_max_lvl(m_not_l, m_conflict); TRACE("sat", tout << "conflict detected at level " << m_conflict_lvl << " for "; if (m_not_l == literal()) tout << "null literal\n"; else tout << m_not_l << "\n";); if (m_initializing_preferred) { SASSERT(m_conflict_lvl <= 1); return resolve_conflict_for_init(); } if (m_conflict_lvl <= 1 && tracking_assumptions()) { resolve_conflict_for_unsat_core(); return false; } if (m_conflict_lvl == 0) { return false; } m_lemma.reset(); forget_phase_of_vars(m_conflict_lvl); unsigned idx = skip_literals_above_conflict_level(); // save space for first uip m_lemma.push_back(null_literal); unsigned num_marks = 0; if (m_not_l != null_literal) { TRACE("sat_conflict", tout << "not_l: " << m_not_l << "\n";); process_antecedent(m_not_l, num_marks); } literal consequent = m_not_l; justification js = m_conflict; do { TRACE("sat_conflict_detail", tout << "processing consequent: " << consequent << "\n"; tout << "num_marks: " << num_marks << ", js: " << js << "\n";); switch (js.get_kind()) { case justification::NONE: break; case justification::BINARY: process_antecedent(~(js.get_literal()), num_marks); break; case justification::TERNARY: process_antecedent(~(js.get_literal1()), num_marks); process_antecedent(~(js.get_literal2()), num_marks); break; case justification::CLAUSE: { clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); if (c[0] == consequent) { i = 1; } else { process_antecedent(~c[0], num_marks); i = 2; } } unsigned sz = c.size(); for (; i < sz; i++) process_antecedent(~c[i], num_marks); break; } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js); literal_vector::iterator it = m_ext_antecedents.begin(); literal_vector::iterator end = m_ext_antecedents.end(); for (; it != end; ++it) process_antecedent(*it, num_marks); break; } default: UNREACHABLE(); break; } while (true) { literal l = m_trail[idx]; if (is_marked(l.var())) break; SASSERT(idx > 0); idx--; } consequent = m_trail[idx]; bool_var c_var = consequent.var(); SASSERT(lvl(consequent) == m_conflict_lvl); js = m_justification[c_var]; idx--; num_marks--; reset_mark(c_var); } while (num_marks > 0); m_lemma[0] = ~consequent; TRACE("sat_lemma", tout << "new lemma size: " << m_lemma.size() << "\n" << m_lemma << "\n";); if (m_config.m_minimize_lemmas) { minimize_lemma(); reset_lemma_var_marks(); if (m_config.m_dyn_sub_res) dyn_sub_res(); TRACE("sat_lemma", tout << "new lemma (after minimization) size: " << m_lemma.size() << "\n" << m_lemma << "\n";); } else reset_lemma_var_marks(); literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); unsigned new_scope_lvl = 0; ++it; for(; it != end; ++it) { bool_var var = (*it).var(); new_scope_lvl = std::max(new_scope_lvl, lvl(var)); } unsigned glue = num_diff_levels(m_lemma.size(), m_lemma.c_ptr()); pop_reinit(m_scope_lvl - new_scope_lvl); TRACE("sat_conflict_detail", display(tout); tout << "assignment:\n"; display_assignment(tout);); clause * lemma = mk_clause_core(m_lemma.size(), m_lemma.c_ptr(), true); if (lemma) { lemma->set_glue(glue); } decay_activity(); updt_phase_counters(); return true; } void solver::process_antecedent_for_unsat_core(literal antecedent) { bool_var var = antecedent.var(); SASSERT(var < num_vars()); TRACE("sat", tout << antecedent << " " << (is_marked(var)?"+":"-") << "\n";); if (!is_marked(var)) { mark(var); m_unmark.push_back(var); if (is_assumption(antecedent)) { m_core.push_back(antecedent); } } } void solver::process_consequent_for_unsat_core(literal consequent, justification const& js) { TRACE("sat", tout << "processing consequent: "; if (consequent == null_literal) tout << "null\n"; else tout << consequent << "\n"; display_justification(tout << "js kind: ", js); tout << "\n";); switch (js.get_kind()) { case justification::NONE: break; case justification::BINARY: SASSERT(consequent != null_literal); process_antecedent_for_unsat_core(~(js.get_literal())); break; case justification::TERNARY: SASSERT(consequent != null_literal); process_antecedent_for_unsat_core(~(js.get_literal1())); process_antecedent_for_unsat_core(~(js.get_literal2())); break; case justification::CLAUSE: { clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); if (c[0] == consequent) { i = 1; } else { process_antecedent_for_unsat_core(~c[0]); i = 2; } } unsigned sz = c.size(); for (; i < sz; i++) process_antecedent_for_unsat_core(~c[i]); break; } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js); literal_vector::iterator it = m_ext_antecedents.begin(); literal_vector::iterator end = m_ext_antecedents.end(); for (; it != end; ++it) process_antecedent_for_unsat_core(*it); break; } default: UNREACHABLE(); break; } } bool solver::resolve_conflict_for_init() { if (m_conflict_lvl == 0) { return false; } m_lemma.reset(); m_lemma.push_back(null_literal); // asserted literal if (m_not_l != null_literal) { TRACE("sat", tout << "not_l: " << m_not_l << "\n";); process_antecedent_for_init(m_not_l); } literal consequent = m_not_l; justification js = m_conflict; SASSERT(m_trail.size() > 0); unsigned idx = m_trail.size(); while (process_consequent_for_init(consequent, js)) { while (true) { --idx; literal l = m_trail[idx]; if (is_marked(l.var())) break; SASSERT(idx > 0); } consequent = m_trail[idx]; bool_var c_var = consequent.var(); if (lvl(consequent) == 0) { return false; } SASSERT(m_conflict_lvl == 1); js = m_justification[c_var]; reset_mark(c_var); } unsigned new_scope_lvl = 0; m_lemma[0] = ~consequent; for (unsigned i = 1; i < m_lemma.size(); ++i) { bool_var var = m_lemma[i].var(); if (is_marked(var)) { reset_mark(var); new_scope_lvl = std::max(new_scope_lvl, lvl(var)); } else { m_lemma[i] = m_lemma.back(); m_lemma.pop_back(); --i; } } TRACE("sat", tout << "lemma: " << m_lemma << "\n"; display(tout); tout << "assignment:\n"; display_assignment(tout);); if (new_scope_lvl == 0) { pop_reinit(m_scope_lvl); } else { unassign_vars(idx); } mk_clause_core(m_lemma.size(), m_lemma.c_ptr(), true); TRACE("sat", tout << "Trail: " << m_trail << "\n";); m_inconsistent = false; return true; } bool solver::process_consequent_for_init(literal consequent, justification const& js) { switch (js.get_kind()) { case justification::NONE: return false; case justification::BINARY: process_antecedent_for_init(~(js.get_literal())); break; case justification::TERNARY: process_antecedent_for_init(~(js.get_literal1())); process_antecedent_for_init(~(js.get_literal2())); break; case justification::CLAUSE: { clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); if (c[0] == consequent) { i = 1; } else { process_antecedent_for_init(~c[0]); i = 2; } } unsigned sz = c.size(); for (; i < sz; i++) process_antecedent_for_init(~c[i]); break; } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js); literal_vector::iterator it = m_ext_antecedents.begin(); literal_vector::iterator end = m_ext_antecedents.end(); for (; it != end; ++it) process_antecedent_for_init(*it); break; } default: UNREACHABLE(); break; } return true; } void solver::process_antecedent_for_init(literal antecedent) { bool_var var = antecedent.var(); SASSERT(var < num_vars()); if (!is_marked(var) && lvl(var) > 0) { mark(var); m_lemma.push_back(~antecedent); } } void solver::resolve_conflict_for_unsat_core() { TRACE("sat", display(tout); unsigned level = 0; for (unsigned i = 0; i < m_trail.size(); ++i) { literal l = m_trail[i]; if (level != m_level[l.var()]) { level = m_level[l.var()]; tout << level << ": "; } tout << l; if (m_mark[l.var()]) { tout << "*"; } tout << " "; } tout << "\n"; ); m_core.reset(); if (m_conflict_lvl == 0) { return; } SASSERT(m_unmark.empty()); DEBUG_CODE({ for (unsigned i = 0; i < m_trail.size(); ++i) { SASSERT(!is_marked(m_trail[i].var())); }}); unsigned old_size = m_unmark.size(); int idx = skip_literals_above_conflict_level(); if (m_not_l != null_literal) { justification js = m_justification[m_not_l.var()]; TRACE("sat", tout << "not_l: " << m_not_l << "\n"; display_justification(tout, js); tout << "\n";); process_antecedent_for_unsat_core(m_not_l); if (is_assumption(~m_not_l)) { m_core.push_back(~m_not_l); } else { process_consequent_for_unsat_core(m_not_l, js); } } literal consequent = m_not_l; justification js = m_conflict; while (true) { process_consequent_for_unsat_core(consequent, js); while (idx >= 0) { literal l = m_trail[idx]; if (is_marked(l.var())) break; idx--; } if (idx < 0) { break; } consequent = m_trail[idx]; if (lvl(consequent) < m_conflict_lvl) { TRACE("sat", tout << consequent << " at level " << lvl(consequent) << "\n";); break; } bool_var c_var = consequent.var(); SASSERT(lvl(consequent) == m_conflict_lvl); js = m_justification[c_var]; idx--; } reset_unmark(old_size); if (m_config.m_minimize_core) { if (m_min_core_valid && m_min_core.size() < m_core.size()) { IF_VERBOSE(1, verbose_stream() << "(sat.updating core " << m_min_core.size() << " " << m_core.size() << ")\n";); m_core.reset(); m_core.append(m_min_core); } // TBD: // apply optional clause minimization by detecting subsumed literals. // initial experiment suggests it has no effect. m_mus(); // ignore return value on cancelation. set_model(m_mus.get_model()); IF_VERBOSE(2, verbose_stream() << "(sat.core: " << m_core << ")\n";); } } unsigned solver::get_max_lvl(literal consequent, justification js) { if (!m_ext) return scope_lvl(); if (scope_lvl() == 0) return 0; unsigned r = 0; if (consequent != null_literal) r = lvl(consequent); switch (js.get_kind()) { case justification::NONE: break; case justification::BINARY: r = std::max(r, lvl(js.get_literal())); break; case justification::TERNARY: r = std::max(r, lvl(js.get_literal1())); r = std::max(r, lvl(js.get_literal2())); break; case justification::CLAUSE: { clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); if (c[0] == consequent) { i = 1; } else { r = std::max(r, lvl(c[0])); i = 2; } } unsigned sz = c.size(); for (; i < sz; i++) r = std::max(r, lvl(c[i])); break; } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js); literal_vector::iterator it = m_ext_antecedents.begin(); literal_vector::iterator end = m_ext_antecedents.end(); for (; it != end; ++it) r = std::max(r, lvl(*it)); break; } default: UNREACHABLE(); break; } return r; } /** \brief Skip literals from levels above m_conflict_lvl. It returns an index idx such that lvl(m_trail[idx]) <= m_conflict_lvl, and for all idx' > idx, lvl(m_trail[idx']) > m_conflict_lvl */ unsigned solver::skip_literals_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 solver::process_antecedent(literal antecedent, unsigned & num_marks) { bool_var var = antecedent.var(); unsigned var_lvl = lvl(var); SASSERT(var < num_vars()); if (!is_marked(var) && var_lvl > 0) { mark(var); inc_activity(var); if (var_lvl == m_conflict_lvl) num_marks++; else m_lemma.push_back(~antecedent); } } /** \brief js is an external justification. Collect its antecedents and store at m_ext_antecedents. */ void solver::fill_ext_antecedents(literal consequent, justification js) { SASSERT(js.is_ext_justification()); SASSERT(m_ext); m_ext_antecedents.reset(); m_ext->get_antecedents(consequent, js.get_ext_justification_idx(), m_ext_antecedents); } void solver::forget_phase_of_vars(unsigned from_lvl) { unsigned head = from_lvl == 0 ? 0 : m_scopes[from_lvl - 1].m_trail_lim; unsigned sz = m_trail.size(); for (unsigned i = head; i < sz; i++) { literal l = m_trail[i]; bool_var v = l.var(); TRACE("forget_phase", tout << "forgeting phase of l: " << l << "\n";); m_phase[v] = PHASE_NOT_AVAILABLE; } } void solver::updt_phase_counters() { m_phase_counter++; if (m_phase_cache_on) { if (m_phase_counter >= m_config.m_phase_caching_on) { m_phase_counter = 0; m_phase_cache_on = false; } } else { if (m_phase_counter >= m_config.m_phase_caching_off) { m_phase_counter = 0; m_phase_cache_on = true; } } } /** \brief Return the number of different levels in lits. All literals in lits must be assigned. */ unsigned solver::num_diff_levels(unsigned num, literal const * lits) { m_diff_levels.reserve(scope_lvl() + 1, false); unsigned r = 0; for (unsigned i = 0; i < num; i++) { SASSERT(value(lits[i]) != l_undef); unsigned lit_lvl = lvl(lits[i]); if (m_diff_levels[lit_lvl] == false) { m_diff_levels[lit_lvl] = true; r++; } } // reset m_diff_levels. for (unsigned i = 0; i < num; i++) m_diff_levels[lvl(lits[i])] = false; return r; } /** \brief Process an antecedent for lemma minimization. */ bool solver::process_antecedent_for_minimization(literal antecedent) { bool_var var = antecedent.var(); unsigned var_lvl = lvl(var); if (!is_marked(var) && var_lvl > 0) { if (m_lvl_set.may_contain(var_lvl)) { mark(var); m_unmark.push_back(var); m_lemma_min_stack.push_back(var); } else { return false; } } return true; } /** \brief Return true if lit is implied by other marked literals and/or literals assigned at the base level. The set lvl_set is used as an optimization. The idea is to stop the recursive search with a failure as soon as we find a literal assigned in a level that is not in lvl_set. */ bool solver::implied_by_marked(literal lit) { m_lemma_min_stack.reset(); // avoid recursive function m_lemma_min_stack.push_back(lit.var()); unsigned old_size = m_unmark.size(); while (!m_lemma_min_stack.empty()) { bool_var var = m_lemma_min_stack.back(); m_lemma_min_stack.pop_back(); justification js = m_justification[var]; switch(js.get_kind()) { case justification::NONE: // it is a decision variable from a previous scope level if (lvl(var) > 0) { reset_unmark(old_size); return false; } break; case justification::BINARY: if (!process_antecedent_for_minimization(~(js.get_literal()))) { reset_unmark(old_size); return false; } break; case justification::TERNARY: if (!process_antecedent_for_minimization(~(js.get_literal1())) || !process_antecedent_for_minimization(~(js.get_literal2()))) { reset_unmark(old_size); return false; } break; case justification::CLAUSE: { clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); unsigned i = 0; if (c[0].var() == var) { i = 1; } else { SASSERT(c[1].var() == var); if (!process_antecedent_for_minimization(~c[0])) { reset_unmark(old_size); return false; } i = 2; } unsigned sz = c.size(); for (; i < sz; i++) { if (!process_antecedent_for_minimization(~c[i])) { reset_unmark(old_size); return false; } } break; } case justification::EXT_JUSTIFICATION: { literal consequent(var, value(var) == l_false); fill_ext_antecedents(consequent, js); literal_vector::iterator it = m_ext_antecedents.begin(); literal_vector::iterator end = m_ext_antecedents.end(); for (; it != end; ++it) { if (!process_antecedent_for_minimization(*it)) { reset_unmark(old_size); return false; } } break; } default: UNREACHABLE(); break; } } return true; } /** \brief Restore the size of m_unmark to old_size, and unmark variables at positions [old_size, m_unmark.size()). */ void solver::reset_unmark(unsigned old_size) { unsigned curr_size = m_unmark.size(); for(unsigned i = old_size; i < curr_size; i++) reset_mark(m_unmark[i]); m_unmark.shrink(old_size); } /** \brief Store the levels of the literals at m_lemma in the approximated set m_lvl_set. */ void solver::updt_lemma_lvl_set() { m_lvl_set.reset(); literal_vector::const_iterator it = m_lemma.begin(); literal_vector::const_iterator end = m_lemma.end(); for(; it != end; ++it) m_lvl_set.insert(lvl(*it)); } /** \brief Minimize the number of literals in m_lemma. The main idea is to remove literals that are implied by other literals in m_lemma and/or literals assigned at level 0. */ void solver::minimize_lemma() { SASSERT(m_unmark.empty()); //m_unmark.reset(); updt_lemma_lvl_set(); unsigned sz = m_lemma.size(); unsigned i = 1; // the first literal is the FUIP unsigned j = 1; for (; i < sz; i++) { literal l = m_lemma[i]; if (implied_by_marked(l)) { TRACE("sat", tout << "drop: " << l << "\n";); m_unmark.push_back(l.var()); } else { if (j != i) { m_lemma[j] = m_lemma[i]; } j++; } } reset_unmark(0); m_lemma.shrink(j); m_stats.m_minimized_lits += sz - j; } /** \brief Reset the mark of the variables in the current lemma. */ void solver::reset_lemma_var_marks() { literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); SASSERT(!is_marked((*it).var())); ++it; for(; it != end; ++it) { bool_var var = (*it).var(); reset_mark(var); } } /** \brief Apply dynamic subsumption resolution to new lemma. Only binary and ternary clauses are used. */ void solver::dyn_sub_res() { unsigned sz = m_lemma.size(); for (unsigned i = 0; i < sz; i++) { mark_lit(m_lemma[i]); } literal l0 = m_lemma[0]; // l0 is the FUIP, and we never remove the FUIP. // // In the following loop, we use unmark_lit(l) to remove a // literal from m_lemma. for (unsigned i = 0; i < sz; i++) { literal l = m_lemma[i]; if (!is_marked_lit(l)) continue; // literal was eliminated // first use watch lists watch_list const & wlist = get_wlist(~l); watch_list::const_iterator it = wlist.begin(); watch_list::const_iterator end = wlist.end(); for (; it != end; ++it) { // In this for-loop, the conditions l0 != ~l2 and l0 != ~l3 // are not really needed if the solver does not miss unit propagations. // However, we add them anyway because we don't want to rely on this // property of the propagator. // For example, if this property is relaxed in the future, then the code // without the conditions l0 != ~l2 and l0 != ~l3 may remove the FUIP if (it->is_binary_clause()) { literal l2 = it->get_literal(); if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } } else if (it->is_ternary_clause()) { literal l2 = it->get_literal1(); literal l3 = it->get_literal2(); if (is_marked_lit(l2) && is_marked_lit(~l3) && l0 != ~l3) { // eliminate ~l3 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l3); } else if (is_marked_lit(~l2) && is_marked_lit(l3) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l2); } } else { // May miss some binary/ternary clauses, but that is ok. // I sort the watch lists at every simplification round. break; } } // try to use cached implication if available literal_vector * implied_lits = m_probing.cached_implied_lits(~l); if (implied_lits) { literal_vector::iterator it = implied_lits->begin(); literal_vector::iterator end = implied_lits->end(); for (; it != end; ++it) { literal l2 = *it; // Here, we must check l0 != ~l2. // l \/ l2 is an implied binary clause. // However, it may have been deduced using a lemma that has been deleted. // For example, consider the following sequence of events: // // 1. Initial clause database: // // l \/ ~p1 // p1 \/ ~p2 // p2 \/ ~p3 // p3 \/ ~p4 // q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 // q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 // ~q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 // ~q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 // ... // // 2. Now suppose we learned the lemma // // p1 \/ p2 \/ p3 \/ p4 \/ l2 (*) // // 3. Probing is executed and we notice hat (~l => l2) when we assign l to false. // That is, l \/ l2 is an implied clause. Note that probing does not add // this clause to the clause database (there are too many). // // 4. Lemma (*) is deleted (garbage collected). // // 5. l is decided to be false, p1, p2, p3 and p4 are propagated using BCP, // but l2 is not since the lemma (*) was deleted. // // Probing module still "knows" that l \/ l2 is valid binary clause // // 6. A new lemma is created where ~l2 is the FUIP and the lemma also contains l. // If we remove l0 != ~l2 may try to delete the FUIP. if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } } } } // can't eliminat FUIP SASSERT(is_marked_lit(m_lemma[0])); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = m_lemma[i]; if (is_marked_lit(l)) { unmark_lit(l); m_lemma[j] = l; j++; } } m_stats.m_dyn_sub_res += sz - j; SASSERT(j >= 1); m_lemma.shrink(j); } // ----------------------- // // Backtracking // // ----------------------- void solver::push() { SASSERT(!inconsistent()); TRACE("sat_verbose", tout << "q:" << m_qhead << " trail: " << m_trail.size() << "\n";); SASSERT(m_qhead == m_trail.size()); m_scopes.push_back(scope()); scope & s = m_scopes.back(); m_scope_lvl++; s.m_trail_lim = m_trail.size(); s.m_clauses_to_reinit_lim = m_clauses_to_reinit.size(); s.m_inconsistent = m_inconsistent; if (m_ext) m_ext->push(); } void solver::pop_reinit(unsigned num_scopes) { pop(num_scopes); reinit_assumptions(); } void solver::pop(unsigned num_scopes) { if (num_scopes == 0) return; if (m_ext) m_ext->pop(num_scopes); SASSERT(num_scopes <= scope_lvl()); unsigned new_lvl = scope_lvl() - num_scopes; scope & s = m_scopes[new_lvl]; m_inconsistent = false; unassign_vars(s.m_trail_lim); m_scope_lvl -= num_scopes; m_scopes.shrink(new_lvl); reinit_clauses(s.m_clauses_to_reinit_lim); } void solver::unassign_vars(unsigned old_sz) { SASSERT(old_sz <= m_trail.size()); unsigned i = m_trail.size(); while (i != old_sz) { --i; literal l = m_trail[i]; m_assignment[l.index()] = l_undef; m_assignment[(~l).index()] = l_undef; bool_var v = l.var(); SASSERT(value(v) == l_undef); m_case_split_queue.unassign_var_eh(v); } m_trail.shrink(old_sz); m_qhead = old_sz; SASSERT(m_qhead == m_trail.size()); } void solver::reinit_clauses(unsigned old_sz) { unsigned sz = m_clauses_to_reinit.size(); SASSERT(old_sz <= sz); unsigned j = old_sz; for (unsigned i = old_sz; i < sz; i++) { clause_wrapper cw = m_clauses_to_reinit[i]; bool reinit = false; if (cw.is_binary()) { if (propagate_bin_clause(cw[0], cw[1])) { if (scope_lvl() > 0) { m_clauses_to_reinit[j] = cw; j++; } } } else { clause & c = *(cw.get_clause()); dettach_clause(c); attach_clause(c, reinit); if (scope_lvl() > 0 && reinit) { // clause propagated literal, must keep it in the reinit stack. m_clauses_to_reinit[j] = cw; j++; } else { c.set_reinit_stack(false); } } } m_clauses_to_reinit.shrink(j); } // // All new clauses that are added to the solver // are relative to the user-scope literals. // void solver::user_push() { literal lit; bool_var new_v = mk_var(true, false); lit = literal(new_v, false); m_user_scope_literals.push_back(lit); TRACE("sat", tout << "user_push: " << lit << "\n";); } void solver::gc_lit(clause_vector &clauses, literal lit) { unsigned j = 0; for (unsigned i = 0; i < clauses.size(); ++i) { clause & c = *(clauses[i]); if (c.contains(lit)) { dettach_clause(c); del_clause(c); } else { clauses[j] = &c; ++j; } } clauses.shrink(j); } void solver::gc_bin(bool learned, literal nlit) { m_user_bin_clauses.reset(); collect_bin_clauses(m_user_bin_clauses, learned); for (unsigned i = 0; i < m_user_bin_clauses.size(); ++i) { literal l1 = m_user_bin_clauses[i].first; literal l2 = m_user_bin_clauses[i].second; if (nlit == l1 || nlit == l2) { dettach_bin_clause(l1, l2, learned); } } } bool_var solver::max_var(bool learned, bool_var v) { m_user_bin_clauses.reset(); collect_bin_clauses(m_user_bin_clauses, learned); for (unsigned i = 0; i < m_user_bin_clauses.size(); ++i) { literal l1 = m_user_bin_clauses[i].first; literal l2 = m_user_bin_clauses[i].second; if (l1.var() > v) v = l1.var(); if (l2.var() > v) v = l2.var(); } return v; } bool_var solver::max_var(clause_vector& clauses, bool_var v) { for (unsigned i = 0; i < clauses.size(); ++i) { clause & c = *(clauses[i]); literal* it = c.begin(); literal * end = c.end(); for (; it != end; ++it) { if (it->var() > v) { v = it->var(); } } } return v; } void solver::gc_var(bool_var v) { if (v > 0) { bool_var w = max_var(m_learned, v-1); w = max_var(m_clauses, w); w = max_var(true, w); w = max_var(false, w); for (unsigned i = 0; i < m_trail.size(); ++i) { if (m_trail[i].var() > w) w = m_trail[i].var(); } v = std::max(v, w + 1); } // v is an index of a variable that does not occur in solver state. if (v < m_level.size()) { for (bool_var i = v; i < m_level.size(); ++i) { m_case_split_queue.del_var_eh(i); } m_watches.shrink(2*v); m_assignment.shrink(2*v); m_justification.shrink(v); m_decision.shrink(v); m_eliminated.shrink(v); m_external.shrink(v); m_activity.shrink(v); m_level.shrink(v); m_mark.shrink(v); m_lit_mark.shrink(2*v); m_phase.shrink(v); m_prev_phase.shrink(v); m_assigned_since_gc.shrink(v); m_simplifier.reset_todo(); } } void solver::user_pop(unsigned num_scopes) { pop_to_base_level(); while (num_scopes > 0) { literal lit = m_user_scope_literals.back(); m_user_scope_literals.pop_back(); gc_lit(m_learned, lit); gc_lit(m_clauses, lit); gc_bin(true, lit); gc_bin(false, lit); TRACE("sat", tout << "gc: " << lit << "\n"; display(tout);); --num_scopes; for (unsigned i = 0; i < m_trail.size(); ++i) { if (m_trail[i] == lit) { TRACE("sat", tout << m_trail << "\n";); unassign_vars(i); break; } } gc_var(lit.var()); } } void solver::pop_to_base_level() { reset_assumptions(); pop(scope_lvl()); } // ----------------------- // // Misc // // ----------------------- void solver::updt_params(params_ref const & p) { m_params = p; m_config.updt_params(p); m_simplifier.updt_params(p); m_asymm_branch.updt_params(p); m_probing.updt_params(p); m_scc.updt_params(p); m_rand.set_seed(m_config.m_random_seed); } void solver::collect_param_descrs(param_descrs & d) { config::collect_param_descrs(d); simplifier::collect_param_descrs(d); asymm_branch::collect_param_descrs(d); probing::collect_param_descrs(d); scc::collect_param_descrs(d); } void solver::set_cancel(bool f) { m_cancel = f; m_wsls.set_cancel(f); } void solver::collect_statistics(statistics & st) const { m_stats.collect_statistics(st); m_cleaner.collect_statistics(st); m_simplifier.collect_statistics(st); m_scc.collect_statistics(st); m_asymm_branch.collect_statistics(st); m_probing.collect_statistics(st); } void solver::reset_statistics() { m_stats.reset(); m_cleaner.reset_statistics(); m_simplifier.reset_statistics(); m_asymm_branch.reset_statistics(); m_probing.reset_statistics(); } // ----------------------- // // Activity related stuff // // ----------------------- void solver::rescale_activity() { svector::iterator it = m_activity.begin(); svector::iterator end = m_activity.end(); for (; it != end; ++it) { *it >>= 14; } m_activity_inc >>= 14; } // ----------------------- // // Iterators // // ----------------------- void solver::collect_bin_clauses(svector & r, bool learned) const { unsigned sz = m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; l_idx++) { literal l = to_literal(l_idx); l.neg(); watch_list const & wlist = m_watches[l_idx]; watch_list::const_iterator it = wlist.begin(); watch_list::const_iterator end = wlist.end(); for (; it != end; ++it) { if (!it->is_binary_clause()) continue; if (!learned && it->is_learned()) continue; literal l2 = it->get_literal(); if (l.index() > l2.index()) continue; TRACE("cleanup_bug", tout << "collected: " << l << " " << l2 << "\n";); r.push_back(bin_clause(l, l2)); } } } // ----------------------- // // Debugging // // ----------------------- bool solver::check_invariant() const { if (m_cancel) return true; integrity_checker checker(*this); SASSERT(checker()); return true; } bool solver::check_marks() const { for (bool_var v = 0; v < num_vars(); v++) { SASSERT(!is_marked(v)); } return true; } void solver::display_binary(std::ostream & out) const { unsigned sz = m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; l_idx++) { literal l = to_literal(l_idx); l.neg(); watch_list const & wlist = m_watches[l_idx]; watch_list::const_iterator it = wlist.begin(); watch_list::const_iterator end = wlist.end(); for (; it != end; ++it) { if (!it->is_binary_clause()) continue; literal l2 = it->get_literal(); if (l.index() > l2.index()) continue; out << "(" << l << " " << l2 << ")\n"; } } } void solver::display_units(std::ostream & out) const { unsigned end = scope_lvl() == 0 ? m_trail.size() : m_scopes[0].m_trail_lim; for (unsigned i = 0; i < end; i++) { out << m_trail[i] << " "; } if (end != 0) out << "\n"; } void solver::display(std::ostream & out) const { out << "(sat\n"; display_units(out); display_binary(out); out << m_clauses << m_learned; out << ")\n"; } void solver::display_justification(std::ostream & out, justification const& js) const { out << js; if (js.is_clause()) { out << *(m_cls_allocator.get_clause(js.get_clause_offset())); } } unsigned solver::num_clauses() const { unsigned num_cls = 0; num_cls += m_trail.size(); // units; vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l = ~to_literal(l_idx); watch_list const & wlist = *it; watch_list::const_iterator it2 = wlist.begin(); watch_list::const_iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_clause() && l.index() < it2->get_literal().index()) num_cls++; } } clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); num_cls += cs.size(); } return num_cls; } void solver::display_dimacs(std::ostream & out) const { out << "p cnf " << num_vars() << " " << num_clauses() << "\n"; for (unsigned i = 0; i < m_trail.size(); i++) { out << dimacs_lit(m_trail[i]) << " 0\n"; } vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l = ~to_literal(l_idx); watch_list const & wlist = *it; watch_list::const_iterator it2 = wlist.begin(); watch_list::const_iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_clause() && l.index() < it2->get_literal().index()) out << dimacs_lit(l) << " " << dimacs_lit(it2->get_literal()) << " 0\n"; } } clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); clause_vector::const_iterator it = cs.begin(); clause_vector::const_iterator end = cs.end(); for (; it != end; ++it) { clause const & c = *(*it); unsigned sz = c.size(); for (unsigned j = 0; j < sz; j++) out << dimacs_lit(c[j]) << " "; out << "0\n"; } } } void solver::display_wcnf(std::ostream & out, unsigned sz, literal const* lits, unsigned const* weights) const { unsigned max_weight = 0; for (unsigned i = 0; i < sz; ++i) { max_weight = std::max(max_weight, weights[i]); } ++max_weight; out << "p wcnf " << num_vars() << " " << num_clauses() + sz << " " << max_weight << "\n"; for (unsigned i = 0; i < m_trail.size(); i++) { out << max_weight << " " << dimacs_lit(m_trail[i]) << " 0\n"; } vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l = ~to_literal(l_idx); watch_list const & wlist = *it; watch_list::const_iterator it2 = wlist.begin(); watch_list::const_iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_clause() && l.index() < it2->get_literal().index()) out << max_weight << " " << dimacs_lit(l) << " " << dimacs_lit(it2->get_literal()) << " 0\n"; } } clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); clause_vector::const_iterator it = cs.begin(); clause_vector::const_iterator end = cs.end(); for (; it != end; ++it) { clause const & c = *(*it); unsigned sz = c.size(); out << max_weight << " "; for (unsigned j = 0; j < sz; j++) out << dimacs_lit(c[j]) << " "; out << "0\n"; } } for (unsigned i = 0; i < sz; ++i) { out << weights[i] << " " << lits[i] << " 0\n"; } } void solver::display_watches(std::ostream & out) const { vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { watch_list const & wlist = *it; literal l = to_literal(l_idx); out << l << ": "; sat::display(out, m_cls_allocator, wlist); out << "\n"; } } void solver::display_assignment(std::ostream & out) const { out << m_trail << "\n"; } /** \brief Return true, if c is a clause containing one unassigned literal. */ bool solver::is_unit(clause const & c) const { bool found_undef = false; unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { switch (value(c[i])) { case l_undef: if (found_undef) return false; found_undef = true; break; case l_true: return false; case l_false: break; } } return found_undef; } /** \brief Return true, if all literals in c are assigned to false. */ bool solver::is_empty(clause const & c) const { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { if (value(c[i]) != l_false) return false; } return true; } bool solver::check_missed_propagation(clause_vector const & cs) const { clause_vector::const_iterator it = cs.begin(); clause_vector::const_iterator end = cs.end(); for (; it != end; ++it) { clause const & c = *(*it); if (c.frozen()) continue; if (is_empty(c) || is_unit(c)) { TRACE("sat_missed_prop", tout << "missed_propagation: " << c << "\n"; for (unsigned i = 0; i < c.size(); i++) tout << c[i] << ": " << value(c[i]) << "\n";); UNREACHABLE(); } SASSERT(!is_empty(c)); SASSERT(!is_unit(c)); } return true; } bool solver::check_missed_propagation() const { if (inconsistent()) return true; return check_missed_propagation(m_clauses) && check_missed_propagation(m_learned); } // ----------------------- // // Simplification // // ----------------------- void solver::cleanup() { if (scope_lvl() > 0 || inconsistent()) return; if (m_cleaner() && m_ext) m_ext->clauses_modifed(); } void solver::simplify(bool learned) { if (scope_lvl() > 0 || inconsistent()) return; m_simplifier(learned); m_simplifier.free_memory(); if (m_ext) m_ext->clauses_modifed(); } unsigned solver::scc_bin() { if (scope_lvl() > 0 || inconsistent()) return 0; unsigned r = m_scc(); if (r > 0 && m_ext) m_ext->clauses_modifed(); return r; } void solver::asymmetric_branching() { if (scope_lvl() > 0 || inconsistent()) return; m_asymm_branch(); if (m_ext) m_ext->clauses_modifed(); } // ----------------------- // // Statistics // // ----------------------- void solver::display_status(std::ostream & out) const { unsigned num_bin = 0; unsigned num_ext = 0; unsigned num_lits = 0; vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l = ~to_literal(l_idx); watch_list const & wlist = *it; watch_list::const_iterator it2 = wlist.begin(); watch_list::const_iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { switch (it2->get_kind()) { case watched::BINARY: if (l.index() < it2->get_literal().index()) { num_lits += 2; num_bin++; } break; case watched::EXT_CONSTRAINT: num_ext++; break; default: break; } } } unsigned num_elim = 0; for (bool_var v = 0; v < num_vars(); v++) { if (m_eliminated[v]) num_elim++; } unsigned num_ter = 0; unsigned num_cls = 0; clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); clause_vector::const_iterator it = cs.begin(); clause_vector::const_iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); if (c.size() == 3) num_ter++; else num_cls++; num_lits += c.size(); } } unsigned total_cls = num_cls + num_ter + num_bin; double mem = static_cast(memory::get_allocation_size())/static_cast(1024*1024); out << "(sat-status\n"; out << " :inconsistent " << (m_inconsistent ? "true" : "false") << "\n"; out << " :vars " << num_vars() << "\n"; out << " :elim-vars " << num_elim << "\n"; out << " :lits " << num_lits << "\n"; out << " :assigned " << m_trail.size() << "\n"; out << " :binary-clauses " << num_bin << "\n"; out << " :ternary-clauses " << num_ter << "\n"; out << " :clauses " << num_cls << "\n"; out << " :del-clause " << m_stats.m_del_clause << "\n"; out << " :avg-clause-size " << (total_cls == 0 ? 0.0 : static_cast(num_lits) / static_cast(total_cls)) << "\n"; out << " :memory " << std::fixed << std::setprecision(2) << mem << ")" << std::endl; } void stats::collect_statistics(statistics & st) const { st.update("mk bool var", m_mk_var); st.update("mk binary clause", m_mk_bin_clause); st.update("mk ternary clause", m_mk_ter_clause); st.update("mk clause", m_mk_clause); st.update("gc clause", m_gc_clause); st.update("del clause", m_del_clause); st.update("conflicts", m_conflict); st.update("propagations", m_propagate); st.update("decisions", m_decision); st.update("binary propagations", m_bin_propagate); st.update("ternary propagations", m_ter_propagate); st.update("restarts", m_restart); st.update("minimized lits", m_minimized_lits); st.update("dyn subsumption resolution", m_dyn_sub_res); st.update("blocked correction sets", m_blocked_corr_sets); } void stats::reset() { m_mk_var = 0; m_mk_bin_clause = 0; m_mk_ter_clause = 0; m_mk_clause = 0; m_conflict = 0; m_propagate = 0; m_bin_propagate = 0; m_ter_propagate = 0; m_decision = 0; m_restart = 0; m_gc_clause = 0; m_del_clause = 0; m_minimized_lits = 0; m_dyn_sub_res = 0; m_non_learned_generation = 0; m_blocked_corr_sets = 0; } void mk_stat::display(std::ostream & out) const { if (!m_solver.m_clauses.empty()) out << " :clauses " << m_solver.m_clauses.size(); if (!m_solver.m_learned.empty()) { out << " :learned " << (m_solver.m_learned.size() - m_solver.m_num_frozen); if (m_solver.m_num_frozen > 0) out << " :frozen " << m_solver.m_num_frozen; } out << " :gc-clause " << m_solver.m_stats.m_gc_clause; out << mem_stat(); } std::ostream & operator<<(std::ostream & out, mk_stat const & stat) { stat.display(out); return out; } }; z3-z3-4.4.1/src/sat/sat_solver.h000066400000000000000000000450371260446376700163460ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_solver.h Abstract: SAT solver main class. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_SOLVER_H_ #define SAT_SOLVER_H_ #include"sat_types.h" #include"sat_clause.h" #include"sat_watched.h" #include"sat_justification.h" #include"sat_var_queue.h" #include"sat_extension.h" #include"sat_config.h" #include"sat_cleaner.h" #include"sat_simplifier.h" #include"sat_scc.h" #include"sat_asymm_branch.h" #include"sat_iff3_finder.h" #include"sat_probing.h" #include"sat_mus.h" #include"sat_sls.h" #include"params.h" #include"statistics.h" #include"stopwatch.h" #include"trace.h" #include"rlimit.h" namespace sat { /** \brief Main statistic counters. */ struct stats { unsigned m_mk_var; unsigned m_mk_bin_clause; unsigned m_mk_ter_clause; unsigned m_mk_clause; unsigned m_conflict; unsigned m_propagate; unsigned m_bin_propagate; unsigned m_ter_propagate; unsigned m_decision; unsigned m_restart; unsigned m_gc_clause; unsigned m_del_clause; unsigned m_minimized_lits; unsigned m_dyn_sub_res; unsigned m_non_learned_generation; unsigned m_blocked_corr_sets; stats() { reset(); } void reset(); void collect_statistics(statistics & st) const; }; class solver { public: struct abort_solver {}; protected: volatile bool m_cancel; reslimit& m_rlimit; config m_config; stats m_stats; extension * m_ext; random_gen m_rand; clause_allocator m_cls_allocator; cleaner m_cleaner; model m_model; model_converter m_mc; bool m_model_is_current; simplifier m_simplifier; scc m_scc; asymm_branch m_asymm_branch; probing m_probing; mus m_mus; // MUS for minimal core extraction wsls m_wsls; // SLS facility for MaxSAT use bool m_inconsistent; // A conflict is usually a single justification. That is, a justification // for false. If m_not_l is not null_literal, then m_conflict is a // justification for l, and the conflict is union of m_no_l and m_conflict; justification m_conflict; literal m_not_l; clause_vector m_clauses; clause_vector m_learned; unsigned m_num_frozen; vector m_watches; char_vector m_assignment; svector m_justification; svector m_decision; svector m_mark; svector m_lit_mark; svector m_eliminated; svector m_external; svector m_level; svector m_activity; unsigned m_activity_inc; svector m_phase; svector m_prev_phase; svector m_assigned_since_gc; bool m_phase_cache_on; unsigned m_phase_counter; var_queue m_case_split_queue; unsigned m_qhead; unsigned m_scope_lvl; literal_vector m_trail; clause_wrapper_vector m_clauses_to_reinit; struct scope { unsigned m_trail_lim; unsigned m_clauses_to_reinit_lim; bool m_inconsistent; }; svector m_scopes; stopwatch m_stopwatch; params_ref m_params; scoped_ptr m_clone; // for debugging purposes literal_vector m_assumptions; // additional assumptions during check literal_set m_assumption_set; // set of enabled assumptions literal_vector m_core; // unsat core void del_clauses(clause * const * begin, clause * const * end); friend class integrity_checker; friend class cleaner; friend class simplifier; friend class scc; friend class elim_eqs; friend class asymm_branch; friend class probing; friend class iff3_finder; friend class mus; friend class sls; friend class wsls; friend class bceq; friend struct mk_stat; public: solver(params_ref const & p, reslimit& l, extension * ext); ~solver(); // ----------------------- // // 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) const; void reset_statistics(); void display_status(std::ostream & out) const; /** \brief Copy (non learned) clauses from src to this solver. Create missing variables if needed. \pre the model converter of src and this must be empty */ void copy(solver const & src); // ----------------------- // // Variable & Clause creation // // ----------------------- bool_var mk_var(bool ext = false, bool dvar = true); void mk_clause(literal_vector const& lits) { mk_clause(lits.size(), lits.c_ptr()); } void mk_clause(unsigned num_lits, literal * lits); void mk_clause(literal l1, literal l2); void mk_clause(literal l1, literal l2, literal l3); protected: void del_clause(clause & c); clause * mk_clause_core(unsigned num_lits, literal * lits, bool learned); void mk_clause_core(literal_vector const& lits) { mk_clause_core(lits.size(), lits.c_ptr()); } void mk_clause_core(unsigned num_lits, literal * lits) { mk_clause_core(num_lits, lits, false); } void mk_clause_core(literal l1, literal l2) { literal lits[2] = { l1, l2 }; mk_clause_core(2, lits); } void mk_bin_clause(literal l1, literal l2, bool learned); bool propagate_bin_clause(literal l1, literal l2); clause * mk_ter_clause(literal * lits, bool learned); void attach_ter_clause(clause & c, bool & reinit); void attach_ter_clause(clause & c) { bool reinit; attach_ter_clause(c, reinit); } clause * mk_nary_clause(unsigned num_lits, literal * lits, bool learned); void attach_nary_clause(clause & c, bool & reinit); void attach_nary_clause(clause & c) { bool reinit; attach_nary_clause(c, reinit); } void attach_clause(clause & c, bool & reinit); void attach_clause(clause & c) { bool reinit; attach_clause(c, reinit); } unsigned select_watch_lit(clause const & cls, unsigned starting_at) const; unsigned select_learned_watch_lit(clause const & cls) const; bool simplify_clause(unsigned & num_lits, literal * lits) const; template bool simplify_clause_core(unsigned & num_lits, literal * lits) const; void dettach_bin_clause(literal l1, literal l2, bool learned); void dettach_clause(clause & c); void dettach_nary_clause(clause & c); void dettach_ter_clause(clause & c); void push_reinit_stack(clause & c); // ----------------------- // // Basic // // ----------------------- public: bool inconsistent() const { return m_inconsistent; } unsigned num_vars() const { return m_level.size(); } unsigned num_clauses() const; bool is_external(bool_var v) const { return m_external[v] != 0; } bool was_eliminated(bool_var v) const { return m_eliminated[v] != 0; } unsigned scope_lvl() const { return m_scope_lvl; } lbool value(literal l) const { return static_cast(m_assignment[l.index()]); } lbool value(bool_var v) const { return static_cast(m_assignment[literal(v, false).index()]); } unsigned lvl(bool_var v) const { return m_level[v]; } unsigned lvl(literal l) const { return m_level[l.var()]; } void assign(literal l, justification j) { TRACE("sat_assign", tout << l << " previous value: " << value(l) << "\n";); switch (value(l)) { case l_false: set_conflict(j, ~l); break; case l_undef: assign_core(l, j); break; case l_true: return; } } void assign_core(literal l, justification jst); void set_conflict(justification c, literal not_l); void set_conflict(justification c) { set_conflict(c, null_literal); } lbool status(clause const & c) const; clause_offset get_offset(clause const & c) const { return m_cls_allocator.get_offset(&c); } void checkpoint() { if (m_cancel) throw solver_exception(Z3_CANCELED_MSG); if (!m_rlimit.inc()) { m_cancel = true; throw solver_exception(Z3_CANCELED_MSG); } ++m_num_checkpoints; if (m_num_checkpoints < 10) return; m_num_checkpoints = 0; if (memory::get_allocation_size() > m_config.m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); } typedef std::pair bin_clause; protected: watch_list & get_wlist(literal l) { return m_watches[l.index()]; } watch_list const & get_wlist(literal l) const { return m_watches[l.index()]; } watch_list & get_wlist(unsigned l_idx) { return m_watches[l_idx]; } bool is_marked(bool_var v) const { return m_mark[v] != 0; } void mark(bool_var v) { SASSERT(!is_marked(v)); m_mark[v] = true; } void reset_mark(bool_var v) { SASSERT(is_marked(v)); m_mark[v] = false; } bool is_marked_lit(literal l) const { return m_lit_mark[l.index()] != 0; } void mark_lit(literal l) { SASSERT(!is_marked_lit(l)); m_lit_mark[l.index()] = true; } void unmark_lit(literal l) { SASSERT(is_marked_lit(l)); m_lit_mark[l.index()] = false; } bool check_inconsistent(); // ----------------------- // // Propagation // // ----------------------- public: // if update == true, then glue of learned clauses is updated. bool propagate(bool update); protected: bool propagate_core(bool update); // ----------------------- // // Search // // ----------------------- public: lbool check(unsigned num_lits = 0, literal const* lits = 0) { return check(num_lits, lits, 0, 0); } lbool check(unsigned num_lits, literal const* lits, double const* weights, double max_weight); model const & get_model() const { return m_model; } bool model_is_current() const { return m_model_is_current; } literal_vector const& get_core() const { return m_core; } model_converter const & get_model_converter() const { return m_mc; } void set_model(model const& mdl); protected: unsigned m_conflicts; unsigned m_conflicts_since_restart; unsigned m_restart_threshold; unsigned m_luby_idx; unsigned m_conflicts_since_gc; unsigned m_gc_threshold; unsigned m_num_checkpoints; double m_min_d_tk; unsigned m_next_simplify; bool decide(); bool_var next_var(); lbool bounded_search(); void init_search(); literal_vector m_min_core; bool m_min_core_valid; literal_vector m_blocker; double m_weight; bool m_initializing_preferred; void init_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight); bool init_weighted_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight); void reassert_min_core(); void update_min_core(); void resolve_weighted(); void reset_assumptions(); void add_assumption(literal lit); void pop_assumption(); void reinit_assumptions(); bool tracking_assumptions() const; bool is_assumption(literal l) const; void simplify_problem(); void mk_model(); bool check_model(model const & m) const; void restart(); void sort_watch_lits(); // ----------------------- // // GC // // ----------------------- protected: void gc(); void gc_glue(); void gc_psm(); void gc_glue_psm(); void gc_psm_glue(); void save_psm(); void gc_half(char const * st_name); void gc_dyn_psm(); bool activate_frozen_clause(clause & c); unsigned psm(clause const & c) const; bool can_delete(clause const & c) const { if (c.on_reinit_stack()) return false; if (c.size() == 3) return true; // not needed to justify anything. literal l0 = c[0]; if (value(l0) != l_true) return true; justification const & jst = m_justification[l0.var()]; return !jst.is_clause() || m_cls_allocator.get_clause(jst.get_clause_offset()) != &c; } // ----------------------- // // Conflict resolution // // ----------------------- protected: unsigned m_conflict_lvl; literal_vector m_lemma; literal_vector m_ext_antecedents; bool resolve_conflict(); bool resolve_conflict_core(); unsigned get_max_lvl(literal consequent, justification js); void process_antecedent(literal antecedent, unsigned & num_marks); void resolve_conflict_for_unsat_core(); void process_antecedent_for_unsat_core(literal antecedent); void process_consequent_for_unsat_core(literal consequent, justification const& js); bool resolve_conflict_for_init(); void process_antecedent_for_init(literal antecedent); bool process_consequent_for_init(literal consequent, justification const& js); void fill_ext_antecedents(literal consequent, justification js); unsigned skip_literals_above_conflict_level(); void forget_phase_of_vars(unsigned from_lvl); void updt_phase_counters(); svector m_diff_levels; unsigned num_diff_levels(unsigned num, literal const * lits); // lemma minimization typedef approx_set_tpl level_approx_set; bool_var_vector m_unmark; level_approx_set m_lvl_set; bool_var_vector m_lemma_min_stack; bool process_antecedent_for_minimization(literal antecedent); bool implied_by_marked(literal lit); void reset_unmark(unsigned old_size); void updt_lemma_lvl_set(); void minimize_lemma(); void reset_lemma_var_marks(); void dyn_sub_res(); // ----------------------- // // Backtracking // // ----------------------- void push(); void pop(unsigned num_scopes); void pop_reinit(unsigned num_scopes); void unassign_vars(unsigned old_sz); void reinit_clauses(unsigned old_sz); literal_vector m_user_scope_literals; literal_vector m_aux_literals; svector m_user_bin_clauses; void gc_lit(clause_vector& clauses, literal lit); void gc_bin(bool learned, literal nlit); void gc_var(bool_var v); bool_var max_var(clause_vector& clauses, bool_var v); bool_var max_var(bool learned, bool_var v); public: void user_push(); void user_pop(unsigned num_scopes); void pop_to_base_level(); reslimit& rlimit() { return m_rlimit; } // ----------------------- // // Simplification // // ----------------------- public: void cleanup(); void simplify(bool learned = true); void asymmetric_branching(); unsigned scc_bin(); // ----------------------- // // Activity related stuff // // ----------------------- public: void inc_activity(bool_var v) { unsigned & act = m_activity[v]; act += m_activity_inc; m_case_split_queue.activity_increased_eh(v); if (act > (1 << 24)) rescale_activity(); } void decay_activity() { m_activity_inc *= 11; m_activity_inc /= 10; } private: void rescale_activity(); // ----------------------- // // Iterators // // ----------------------- public: clause * const * begin_clauses() const { return m_clauses.begin(); } clause * const * end_clauses() const { return m_clauses.end(); } clause * const * begin_learned() const { return m_learned.begin(); } clause * const * end_learned() const { return m_learned.end(); } void collect_bin_clauses(svector & r, bool learned) const; // ----------------------- // // Debugging // // ----------------------- public: bool check_invariant() const; void display(std::ostream & out) const; void display_watches(std::ostream & out) const; void display_dimacs(std::ostream & out) const; void display_wcnf(std::ostream & out, unsigned sz, literal const* lits, unsigned const* weights) const; void display_assignment(std::ostream & out) const; void display_justification(std::ostream & out, justification const& j) const; protected: void display_binary(std::ostream & out) const; void display_units(std::ostream & out) const; bool is_unit(clause const & c) const; bool is_empty(clause const & c) const; bool check_missed_propagation(clause_vector const & cs) const; bool check_missed_propagation() const; bool check_marks() const; }; struct mk_stat { solver const & m_solver; mk_stat(solver const & s):m_solver(s) {} void display(std::ostream & out) const; }; std::ostream & operator<<(std::ostream & out, mk_stat const & stat); }; #endif z3-z3-4.4.1/src/sat/sat_solver/000077500000000000000000000000001260446376700161645ustar00rootroot00000000000000z3-z3-4.4.1/src/sat/sat_solver/inc_sat_solver.cpp000066400000000000000000000350371260446376700217120ustar00rootroot00000000000000/*++ 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 "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" #include "filter_model_converter.h" #include "bit_blaster_model_converter.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; bit_blaster_rewriter m_bb_rewriter; 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; svector m_weights; typedef obj_map dep2asm_t; public: inc_sat_solver(ast_manager& m, params_ref const& p): m(m), m_solver(p, m.limit(), 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_bb_rewriter(m, p), m_num_scopes(0), m_dep_core(m) { 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), using_params(mk_simplify_tactic(m), simp2_p), mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m, &m_bb_rewriter), 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) { return check_sat(num_assumptions, assumptions, 0, 0); } void display_weighted(std::ostream& out, unsigned sz, expr * const * assumptions, unsigned const* weights) { m_weights.reset(); if (weights != 0) { for (unsigned i = 0; i < sz; ++i) m_weights.push_back(weights[i]); } m_solver.pop_to_base_level(); dep2asm_t dep2asm; VERIFY(l_true == internalize_formulas()); VERIFY(l_true == internalize_assumptions(sz, assumptions, dep2asm)); svector nweights; for (unsigned i = 0; i < m_asms.size(); ++i) { nweights.push_back((unsigned) m_weights[i]); } m_solver.display_wcnf(out, m_asms.size(), m_asms.c_ptr(), nweights.c_ptr()); } lbool check_sat(unsigned sz, expr * const * assumptions, double const* weights, double max_weight) { m_weights.reset(); if (weights != 0) { m_weights.append(sz, weights); } SASSERT(m_weights.empty() == (m_weights.c_ptr() == 0)); 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(sz, assumptions, dep2asm); if (r != l_true) return r; r = m_solver.check(m_asms.size(), m_asms.c_ptr(), m_weights.c_ptr(), max_weight); switch (r) { case l_true: if (sz > 0 && !weights) { check_assumptions(dep2asm); } break; case l_false: // TBD: expr_dependency core is not accounted for. if (sz > 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); m_bb_rewriter.push(); m_map.push(); } virtual void pop(unsigned n) { if (n < m_num_scopes) { // allow inc_sat_solver to n = m_num_scopes; // take over for another solver. } m_bb_rewriter.pop(n); m_map.pop(n); 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("sat", 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_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]; } private: lbool internalize_goal(goal_ref& g, dep2asm_t& dep2asm) { m_mc2.reset(); m_pc.reset(); m_dep_core.reset(); m_subgoals.reset(); m_preprocess->reset(); SASSERT(g->models_enabled()); SASSERT(!g->proofs_enabled()); TRACE("sat", 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; } 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; } CTRACE("sat", m_mc.get(), m_mc->display(tout); ); g = m_subgoals[0]; TRACE("sat", 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])); } lbool res = internalize_goal(g, dep2asm); if (res == l_true) { extract_assumptions(sz, asms, dep2asm); } return res; } 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(unsigned sz, expr* const* asms, dep2asm_t& dep2asm) { m_asms.reset(); unsigned j = 0; sat::literal lit; for (unsigned i = 0; i < sz; ++i) { if (dep2asm.find(asms[i], lit)) { m_asms.push_back(lit); if (i != j && !m_weights.empty()) { m_weights[j] = m_weights[i]; } ++j; } } SASSERT(dep2asm.size() == m_asms.size()); } void extract_core(dep2asm_t& dep2asm) { u_map asm2dep; dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); for (; it != end; ++it) { expr* e = it->m_key; asm2dep.insert(it->m_value.index(), e); } sat::literal_vector const& core = m_solver.get_core(); TRACE("sat", 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 (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_bb_rewriter.const2bits().empty()) { model_converter_ref mc = m_mc; if (!m_bb_rewriter.const2bits().empty()) { mc = concat(mc.get(), mk_bit_blaster_model_converter(m, m_bb_rewriter.const2bits())); } (*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("sat", !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 const& p) { return alloc(inc_sat_solver, m, p); } lbool inc_sat_check_sat(solver& _s, unsigned sz, expr*const* soft, rational const* _weights, rational const& max_weight) { inc_sat_solver& s = dynamic_cast(_s); vector weights; for (unsigned i = 0; _weights && i < sz; ++i) { weights.push_back(_weights[i].get_double()); } return s.check_sat(sz, soft, weights.c_ptr(), max_weight.get_double()); } void inc_sat_display(std::ostream& out, solver& _s, unsigned sz, expr*const* soft, rational const* _weights) { inc_sat_solver& s = dynamic_cast(_s); vector weights; for (unsigned i = 0; _weights && i < sz; ++i) { if (!_weights[i].is_unsigned()) { throw default_exception("Cannot display weights that are not integers"); } weights.push_back(_weights[i].get_unsigned()); } return s.display_weighted(out, sz, soft, weights.c_ptr()); } z3-z3-4.4.1/src/sat/sat_solver/inc_sat_solver.h000066400000000000000000000011121260446376700213420ustar00rootroot00000000000000/*++ 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 const& p); lbool inc_sat_check_sat(solver& s, unsigned sz, expr*const* soft, rational const* weights, rational const& max_weight); void inc_sat_display(std::ostream& out, solver& s, unsigned sz, expr*const* soft, rational const* _weights); #endif z3-z3-4.4.1/src/sat/sat_types.h000066400000000000000000000217031260446376700161720ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_types.h Abstract: Basic types used in the SAT solver Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_TYPES_H_ #define SAT_TYPES_H_ #include"debug.h" #include"approx_set.h" #include"lbool.h" #include"z3_exception.h" #include"common_msgs.h" #include"vector.h" #include namespace sat { #define SAT_VB_LVL 10 // TODO: there is some duplication in the sat and smt namespaces. // The sat namespace should be the base. // I should cleanup the smt namespace later. /** \brief A boolean variable is just an integer. */ typedef unsigned bool_var; typedef svector bool_var_vector; const bool_var null_bool_var = UINT_MAX >> 1; /** \brief The literal b is represented by the value 2*b, and the literal (not b) by the value 2*b + 1 */ class literal { unsigned m_val; explicit literal(unsigned v):m_val(v) {} public: literal():m_val(null_bool_var << 1) { SASSERT(var() == null_bool_var && !sign()); } literal(bool_var v, bool _sign): m_val((v << 1) + static_cast(_sign)) { SASSERT(var() == v); SASSERT(sign() == _sign); } bool_var var() const { return m_val >> 1; } bool sign() const { return m_val & 1; } literal unsign() const { return literal(m_val & ~1); } unsigned index() const { return m_val; } void neg() { m_val = m_val ^ 1; } friend literal operator~(literal l) { return literal(l.m_val ^ 1); } unsigned to_uint() const { return m_val; } unsigned hash() const { return to_uint(); } friend literal to_literal(unsigned x); friend bool operator<(literal const & l1, literal const & l2); friend bool operator==(literal const & l1, literal const & l2); friend bool operator!=(literal const & l1, literal const & l2); }; const literal null_literal; inline literal to_literal(unsigned x) { return literal(x); } inline bool operator<(literal const & l1, literal const & l2) { return l1.m_val < l2.m_val; } inline bool operator==(literal const & l1, literal const & l2) { return l1.m_val == l2.m_val; } inline bool operator!=(literal const & l1, literal const & l2) { return l1.m_val != l2.m_val; } inline std::ostream & operator<<(std::ostream & out, literal l) { out << (l.sign() ? "-" : "") << l.var(); return out; } typedef svector literal_vector; typedef unsigned clause_offset; typedef unsigned ext_constraint_idx; typedef unsigned ext_justification_idx; struct literal2unsigned { unsigned operator()(literal l) const { return l.to_uint(); } }; typedef approx_set_tpl literal_approx_set; typedef approx_set_tpl var_approx_set; enum phase { POS_PHASE, NEG_PHASE, PHASE_NOT_AVAILABLE }; class solver; class clause; class clause_wrapper; class integrity_checker; typedef ptr_vector clause_vector; class solver_exception : public default_exception { public: solver_exception(char const * msg):default_exception(msg) {} }; typedef default_exception sat_param_exception; typedef svector model; inline lbool value_at(bool_var v, model const & m) { return m[v]; } inline lbool value_at(literal l, model const & m) { lbool r = value_at(l.var(), m); return l.sign() ? ~r : r; } inline std::ostream & operator<<(std::ostream & out, model const & m) { bool first = true; for (bool_var v = 0; v < m.size(); v++) { if (m[v] == l_undef) continue; if (first) first = false; else out << " "; if (m[v] == l_true) out << v; else out << "-" << v; } return out; } class uint_set { svector m_in_set; svector m_set; public: typedef svector::const_iterator iterator; void insert(unsigned v) { m_in_set.reserve(v+1, false); if (m_in_set[v]) return; m_in_set[v] = true; m_set.push_back(v); } uint_set& operator=(uint_set const& other) { m_in_set = other.m_in_set; m_set = other.m_set; return *this; } bool contains(unsigned v) const { return v < m_in_set.size() && m_in_set[v] != 0; } bool empty() const { return m_set.empty(); } // erase some variable from the set unsigned erase() { SASSERT(!empty()); unsigned v = m_set.back(); m_set.pop_back(); m_in_set[v] = false; return v; } unsigned size() const { return m_set.size(); } iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } void reset() { m_set.reset(); m_in_set.reset(); } void cleanup() { m_set.finalize(); m_in_set.finalize(); } uint_set& operator&=(uint_set const& other) { unsigned j = 0; for (unsigned i = 0; i < m_set.size(); ++i) { if (other.contains(m_set[i])) { m_set[j] = m_set[i]; ++j; } else { m_in_set[m_set[i]] = false; } } m_set.resize(j); return *this; } uint_set& operator|=(uint_set const& other) { for (unsigned i = 0; i < other.m_set.size(); ++i) { insert(other.m_set[i]); } return *this; } }; typedef uint_set bool_var_set; class literal_set { uint_set m_set; public: literal_set(literal_vector const& v) { for (unsigned i = 0; i < v.size(); ++i) insert(v[i]); } literal_set() {} literal_vector to_vector() const { literal_vector result; iterator it = begin(), e = end(); for (; it != e; ++it) { result.push_back(*it); } return result; } void insert(literal l) { m_set.insert(l.index()); } literal pop() { return to_literal(m_set.erase()); } bool contains(literal l) const { return m_set.contains(l.index()); } bool empty() const { return m_set.empty(); } unsigned size() const { return m_set.size(); } void reset() { m_set.reset(); } void cleanup() { m_set.cleanup(); } class iterator { uint_set::iterator m_it; public: iterator(uint_set::iterator it):m_it(it) {} literal operator*() const { return to_literal(*m_it); } iterator& operator++() { ++m_it; return *this; } iterator operator++(int) { iterator tmp = *this; ++m_it; return tmp; } bool operator==(iterator const& it) const { return m_it == it.m_it; } bool operator!=(iterator const& it) const { return m_it != it.m_it; } }; iterator begin() const { return iterator(m_set.begin()); } iterator end() const { return iterator(m_set.end()); } literal_set& operator&=(literal_set const& other) { m_set &= other.m_set; return *this; } literal_set& operator|=(literal_set const& other) { m_set |= other.m_set; return *this; } }; struct mem_stat { }; inline std::ostream & operator<<(std::ostream & out, mem_stat const & m) { double mem = static_cast(memory::get_allocation_size())/static_cast(1024*1024); out << " :memory " << std::fixed << std::setprecision(2) << mem; return out; } struct dimacs_lit { literal m_lit; dimacs_lit(literal l):m_lit(l) {} }; inline std::ostream & operator<<(std::ostream & out, dimacs_lit const & dl) { literal l = dl.m_lit; if (l.sign()) out << "-" << (l.var() + 1); else out << (l.var() + 1); return out; } struct mk_lits_pp { unsigned m_num; literal const * m_lits; mk_lits_pp(unsigned num, literal const * ls):m_num(num), m_lits(ls) {} }; inline std::ostream & operator<<(std::ostream & out, mk_lits_pp const & ls) { for (unsigned i = 0; i < ls.m_num; i++) { if (i > 0) out << " "; out << ls.m_lits[i]; } return out; } inline std::ostream & operator<<(std::ostream & out, literal_vector const & ls) { return out << mk_lits_pp(ls.size(), ls.c_ptr()); } }; #endif z3-z3-4.4.1/src/sat/sat_var_queue.h000066400000000000000000000025751260446376700170300ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_var_queue.h Abstract: SAT variable priority queue. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_VAR_QUEUE_H_ #define SAT_VAR_QUEUE_H_ #include"heap.h" #include"sat_types.h" namespace sat { class var_queue { struct lt { svector & m_activity; lt(svector & act):m_activity(act) {} bool operator()(bool_var v1, bool_var v2) const { return m_activity[v1] > m_activity[v2]; } }; heap m_queue; public: var_queue(svector & act):m_queue(128, lt(act)) {} void activity_increased_eh(bool_var v) { if (m_queue.contains(v)) m_queue.decreased(v); } void mk_var_eh(bool_var v) { m_queue.reserve(v+1); m_queue.insert(v); } void del_var_eh(bool_var v) { if (m_queue.contains(v)) m_queue.erase(v); } void unassign_var_eh(bool_var v) { if (!m_queue.contains(v)) m_queue.insert(v); } void reset() { m_queue.reset(); } bool empty() const { return m_queue.empty(); } bool_var next_var() { SASSERT(!empty()); return m_queue.erase_min(); } }; }; #endif z3-z3-4.4.1/src/sat/sat_watched.cpp000066400000000000000000000044541260446376700170040ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_watched.cpp Abstract: Element of the SAT solver watchlist. Author: Leonardo de Moura (leonardo) 2011-05-24. Revision History: --*/ #include"sat_watched.h" #include"sat_clause.h" namespace sat { bool erase_clause_watch(watch_list & wlist, clause_offset c) { watch_list::iterator it = wlist.begin(); watch_list::iterator end = wlist.end(); for (; it != end; ++it) { if (it->is_clause() && it->get_clause_offset() == c) { watch_list::iterator it2 = it; ++it; for (; it != end; ++it) { *it2 = *it; ++it2; } wlist.set_end(it2); return true; } } return false; } void display(std::ostream & out, clause_allocator const & ca, watch_list const & wlist) { watch_list::const_iterator it = wlist.begin(); watch_list::const_iterator end = wlist.end(); for (bool first = true; it != end; ++it) { if (first) first = false; else out << " "; switch (it->get_kind()) { case watched::BINARY: out << it->get_literal(); if (it->is_learned()) out << "*"; break; case watched::TERNARY: out << "(" << it->get_literal1() << " " << it->get_literal2() << ")"; break; case watched::CLAUSE: out << "(" << it->get_blocked_literal() << " " << *(ca.get_clause(it->get_clause_offset())) << ")"; break; case watched::EXT_CONSTRAINT: out << it->get_ext_constraint_idx(); break; default: UNREACHABLE(); } } } }; z3-z3-4.4.1/src/sat/sat_watched.h000066400000000000000000000123701260446376700164450ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_watched.h Abstract: Element of the SAT solver watchlist. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_WATCHED_H_ #define SAT_WATCHED_H_ #include"sat_types.h" #include"vector.h" namespace sat { /** A watched element can be: 1) A literal: for watched binary clauses 2) A pair of literals: for watched ternary clauses 3) A pair (literal, clause-offset): for watched clauses, where the first element of the pair is a literal of the clause. 4) A external constraint-idx: for external constraints. For binary clauses: we use a bit to store whether the binary clause was learned or not. Remark: there is not Clause object for binary clauses. */ class watched { public: enum kind { BINARY = 0, TERNARY, CLAUSE, EXT_CONSTRAINT }; private: unsigned m_val1; unsigned m_val2; public: watched(literal l, bool learned): m_val1(l.to_uint()), m_val2(static_cast(BINARY) + (static_cast(learned) << 2)) { SASSERT(is_binary_clause()); SASSERT(get_literal() == l); SASSERT(is_learned() == learned); SASSERT(learned || is_binary_non_learned_clause()); } watched(literal l1, literal l2) { SASSERT(l1 != l2); if (l1.index() > l2.index()) std::swap(l1, l2); m_val1 = l1.to_uint(); m_val2 = static_cast(TERNARY) + (l2.to_uint() << 2); SASSERT(is_ternary_clause()); SASSERT(get_literal1() == l1); SASSERT(get_literal2() == l2); } watched(literal blocked_lit, clause_offset cls_off): m_val1(cls_off), m_val2(static_cast(CLAUSE) + (blocked_lit.to_uint() << 2)) { SASSERT(is_clause()); SASSERT(get_blocked_literal() == blocked_lit); SASSERT(get_clause_offset() == cls_off); } watched(ext_constraint_idx cnstr_idx): m_val1(cnstr_idx), m_val2(static_cast(EXT_CONSTRAINT)) { SASSERT(is_ext_constraint()); SASSERT(get_ext_constraint_idx() == cnstr_idx); } kind get_kind() const { return static_cast(m_val2 & 3); } bool is_binary_clause() const { return get_kind() == BINARY; } literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(m_val1); } void set_literal(literal l) { SASSERT(is_binary_clause()); m_val1 = l.to_uint(); } bool is_learned() const { SASSERT(is_binary_clause()); return (m_val2 >> 2) == 1; } bool is_binary_non_learned_clause() const { return m_val2 == 0; } void mark_not_learned() { SASSERT(is_learned()); m_val2 = static_cast(BINARY); SASSERT(!is_learned()); } bool is_ternary_clause() const { return get_kind() == TERNARY; } literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(m_val1); } literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 2); } bool is_clause() const { return get_kind() == CLAUSE; } clause_offset get_clause_offset() const { SASSERT(is_clause()); return m_val1; } literal get_blocked_literal() const { SASSERT(is_clause()); return to_literal(m_val2 >> 2); } void set_clause_offset(clause_offset c) { SASSERT(is_clause()); m_val1 = c; } void set_blocked_literal(literal l) { SASSERT(is_clause()); m_val2 = static_cast(CLAUSE) + (l.to_uint() << 2); } void set_clause(literal blocked_lit, clause_offset cls_off) { m_val1 = cls_off; m_val2 = static_cast(CLAUSE) + (blocked_lit.to_uint() << 2); } bool is_ext_constraint() const { return get_kind() == EXT_CONSTRAINT; } ext_constraint_idx get_ext_constraint_idx() const { SASSERT(is_ext_constraint()); return m_val2; } bool operator==(watched const & w) const { return m_val1 == w.m_val1 && m_val2 == w.m_val2; } bool operator!=(watched const & w) const { return !operator==(w); } }; COMPILE_TIME_ASSERT(0 <= watched::BINARY && watched::BINARY <= 3); COMPILE_TIME_ASSERT(0 <= watched::TERNARY && watched::TERNARY <= 3); COMPILE_TIME_ASSERT(0 <= watched::CLAUSE && watched::CLAUSE <= 3); COMPILE_TIME_ASSERT(0 <= watched::EXT_CONSTRAINT && watched::EXT_CONSTRAINT <= 3); struct watched_lt { bool operator()(watched const & w1, watched const & w2) const { if (w2.is_binary_clause()) return false; if (w1.is_binary_clause()) return true; if (w2.is_ternary_clause()) return false; if (w1.is_ternary_clause()) return true; return false; } }; typedef vector watch_list; bool erase_clause_watch(watch_list & wlist, clause_offset c); inline void erase_ternary_watch(watch_list & wlist, literal l1, literal l2) { wlist.erase(watched(l1, l2)); } class clause_allocator; void display(std::ostream & out, clause_allocator const & ca, watch_list const & wlist); }; #endif z3-z3-4.4.1/src/sat/tactic/000077500000000000000000000000001260446376700152525ustar00rootroot00000000000000z3-z3-4.4.1/src/sat/tactic/atom2bool_var.cpp000066400000000000000000000072151260446376700205310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: atom2bool_var.cpp Abstract: The mapping between SAT boolean variables and atoms Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #include"atom2bool_var.h" #include"ast_smt2_pp.h" #include"ref_util.h" #include"goal.h" void atom2bool_var::mk_inv(expr_ref_vector & lit2expr) const { obj_map::iterator it = m_mapping.begin(); obj_map::iterator end = m_mapping.end(); for (; it != end; ++it) { sat::literal l(static_cast(it->m_value), false); lit2expr.set(l.index(), it->m_key); l.neg(); lit2expr.set(l.index(), m().mk_not(it->m_key)); } } sat::bool_var atom2bool_var::to_bool_var(expr * n) const { sat::bool_var v = sat::null_bool_var; m_mapping.find(n, v); return v; } struct collect_boolean_interface_proc { struct visitor { obj_hashtable & m_r; visitor(obj_hashtable & r):m_r(r) {} void operator()(var * n) {} void operator()(app * n) { if (is_uninterp_const(n)) m_r.insert(n); } void operator()(quantifier * n) {} }; ast_manager & m; expr_fast_mark2 fvisited; expr_fast_mark1 tvisited; ptr_vector todo; visitor proc; collect_boolean_interface_proc(ast_manager & _m, obj_hashtable & r): m(_m), proc(r) { } void process(expr * f) { if (fvisited.is_marked(f)) return; fvisited.mark(f); todo.push_back(f); while (!todo.empty()) { expr * t = todo.back(); todo.pop_back(); if (is_uninterp_const(t)) continue; if (is_app(t) && to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0) { decl_kind k = to_app(t)->get_decl_kind(); if (k == OP_OR || k == OP_NOT || k == OP_IFF || ((k == OP_EQ || k == OP_ITE) && m.is_bool(to_app(t)->get_arg(1)))) { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); if (fvisited.is_marked(arg)) continue; fvisited.mark(arg); todo.push_back(arg); } } } else { quick_for_each_expr(proc, tvisited, t); } } } template void operator()(T const & g) { unsigned sz = g.size(); ptr_vector deps, all_deps; for (unsigned i = 0; i < sz; i++) { if (g.dep(i)) { deps.reset(); m.linearize(g.dep(i), deps); all_deps.append(deps); } } for (unsigned i = 0; i < all_deps.size(); i++) { quick_for_each_expr(proc, tvisited, all_deps[i]); } for (unsigned i = 0; i < sz; i++) { process(g.form(i)); } } void operator()(unsigned sz, expr * const * fs) { for (unsigned i = 0; i < sz; i++) process(fs[i]); } }; template void collect_boolean_interface_core(T const & s, obj_hashtable & r) { collect_boolean_interface_proc proc(s.m(), r); proc(s); } void collect_boolean_interface(goal const & g, obj_hashtable & r) { collect_boolean_interface_core(g, r); } void collect_boolean_interface(ast_manager & m, unsigned num, expr * const * fs, obj_hashtable & r) { collect_boolean_interface_proc proc(m, r); proc(num, fs); } z3-z3-4.4.1/src/sat/tactic/atom2bool_var.h000066400000000000000000000017231260446376700201740ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: atom2bool_var.h Abstract: The mapping between SAT boolean variables and atoms Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #ifndef ATOM2BOOL_VAR_H_ #define ATOM2BOOL_VAR_H_ #include"expr2var.h" #include"sat_types.h" /** \brief Mapping from atoms into SAT boolean variables. */ class atom2bool_var : public expr2var { public: atom2bool_var(ast_manager & m):expr2var(m) {} void insert(expr * n, sat::bool_var v) { expr2var::insert(n, v); } sat::bool_var to_bool_var(expr * n) const; void mk_inv(expr_ref_vector & lit2expr) const; // return true if the mapping contains uninterpreted atoms. bool interpreted_atoms() const { return expr2var::interpreted_vars(); } }; class goal; void collect_boolean_interface(goal const & g, obj_hashtable & r); void collect_boolean_interface(ast_manager & m, unsigned num, expr * const * fs, obj_hashtable & r); #endif z3-z3-4.4.1/src/sat/tactic/goal2sat.cpp000066400000000000000000000633011260446376700174750ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal2sat.cpp Abstract: "Compile" a goal into the SAT engine. Atoms are "abstracted" into boolean variables. The mapping between boolean variables and atoms can be used to convert back the state of the SAT engine into a goal. The idea is to support scenarios such as: 1) simplify, blast, convert into SAT, and solve 2) convert into SAT, apply SAT for a while, learn new units, and translate back into a goal. 3) convert into SAT, apply SAT preprocessor (failed literal propagation, resolution, etc) and translate back into a goal. 4) Convert boolean structure into SAT, convert atoms into another engine, combine engines using lazy combination, solve. Author: Leonardo (leonardo) 2011-10-26 Notes: --*/ #include"goal2sat.h" #include"ast_smt2_pp.h" #include"ref_util.h" #include"cooperate.h" #include"filter_model_converter.h" #include"model_evaluator.h" #include"for_each_expr.h" #include"model_v2_pp.h" #include"tactic.h" #include"ast_pp.h" #include struct goal2sat::imp { struct frame { app * m_t; unsigned m_root:1; unsigned m_sign:1; unsigned m_idx; frame(app * t, bool r, bool s, unsigned idx): m_t(t), m_root(r), m_sign(s), m_idx(idx) {} }; ast_manager & m; svector m_frame_stack; svector m_result_stack; obj_map m_cache; obj_hashtable m_interface_vars; sat::solver & m_solver; atom2bool_var & m_map; dep2asm_map & m_dep2asm; sat::bool_var m_true; bool m_ite_extra; unsigned long long m_max_memory; volatile bool m_cancel; expr_ref_vector m_trail; bool m_default_external; imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external): m(_m), m_solver(s), m_map(map), m_dep2asm(dep2asm), m_trail(m), m_default_external(default_external) { updt_params(p); m_cancel = false; m_true = sat::null_bool_var; } void updt_params(params_ref const & p) { m_ite_extra = p.get_bool("ite_extra", true); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); } void throw_op_not_handled(std::string const& s) { std::string s0 = "operator " + s + " not supported, apply simplifier before invoking translator"; throw tactic_exception(s0.c_str()); } void mk_clause(sat::literal l) { TRACE("goal2sat", tout << "mk_clause: " << l << "\n";); m_solver.mk_clause(1, &l); } void mk_clause(sat::literal l1, sat::literal l2) { TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << "\n";); m_solver.mk_clause(l1, l2); } void mk_clause(sat::literal l1, sat::literal l2, sat::literal l3) { TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << " " << l3 << "\n";); m_solver.mk_clause(l1, l2, l3); } void mk_clause(unsigned num, sat::literal * lits) { TRACE("goal2sat", tout << "mk_clause: "; for (unsigned i = 0; i < num; i++) tout << lits[i] << " "; tout << "\n";); m_solver.mk_clause(num, lits); } sat::bool_var mk_true() { if (m_true == sat::null_bool_var) { // create fake variable to represent true; m_true = m_solver.mk_var(); mk_clause(sat::literal(m_true, false)); // v is true } return m_true; } void convert_atom(expr * t, bool root, bool sign) { SASSERT(m.is_bool(t)); sat::literal l; sat::bool_var v = m_map.to_bool_var(t); if (v == sat::null_bool_var) { if (m.is_true(t)) { l = sat::literal(mk_true(), sign); } else if (m.is_false(t)) { l = sat::literal(mk_true(), !sign); } else { bool ext = m_default_external || !is_uninterp_const(t) || m_interface_vars.contains(t); sat::bool_var v = m_solver.mk_var(ext); m_map.insert(t, v); l = sat::literal(v, sign); TRACE("goal2sat", tout << "new_var: " << v << "\n" << mk_ismt2_pp(t, m) << "\n";); } } else { SASSERT(v != sat::null_bool_var); l = sat::literal(v, sign); } SASSERT(l != sat::null_literal); if (root) mk_clause(l); else m_result_stack.push_back(l); } bool process_cached(app * t, bool root, bool sign) { sat::literal l; if (m_cache.find(t, l)) { if (sign) l.neg(); if (root) mk_clause(l); else m_result_stack.push_back(l); return true; } return false; } bool visit(expr * t, bool root, bool sign) { if (!is_app(t)) { convert_atom(t, root, sign); return true; } if (process_cached(to_app(t), root, sign)) return true; if (to_app(t)->get_family_id() != m.get_basic_family_id()) { convert_atom(t, root, sign); return true; } switch (to_app(t)->get_decl_kind()) { case OP_NOT: case OP_OR: case OP_IFF: m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); return false; case OP_ITE: case OP_EQ: if (m.is_bool(to_app(t)->get_arg(1))) { m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); return false; } convert_atom(t, root, sign); return true; case OP_AND: case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: { TRACE("goal2sat_not_handled", tout << mk_ismt2_pp(t, m) << "\n";); std::ostringstream strm; strm << mk_ismt2_pp(t, m); throw_op_not_handled(strm.str()); } default: convert_atom(t, root, sign); return true; } } void convert_or(app * t, bool root, bool sign) { TRACE("goal2sat", tout << "convert_or:\n" << mk_ismt2_pp(t, m) << "\n";); unsigned num = t->get_num_args(); if (root) { SASSERT(num == m_result_stack.size()); if (sign) { // this case should not really happen. for (unsigned i = 0; i < num; i++) { sat::literal l = m_result_stack[i]; l.neg(); mk_clause(l); } } else { mk_clause(m_result_stack.size(), m_result_stack.c_ptr()); m_result_stack.reset(); } } else { SASSERT(num <= m_result_stack.size()); sat::bool_var k = m_solver.mk_var(); sat::literal l(k, false); m_cache.insert(t, l); sat::literal * lits = m_result_stack.end() - num; for (unsigned i = 0; i < num; i++) { mk_clause(~lits[i], l); } m_result_stack.push_back(~l); lits = m_result_stack.end() - num - 1; // remark: mk_clause may perform destructive updated to lits. // I have to execute it after the binary mk_clause above. mk_clause(num+1, lits); unsigned old_sz = m_result_stack.size() - num - 1; m_result_stack.shrink(old_sz); if (sign) l.neg(); m_result_stack.push_back(l); } } void convert_ite(app * n, bool root, bool sign) { unsigned sz = m_result_stack.size(); SASSERT(sz >= 3); sat::literal c = m_result_stack[sz-3]; sat::literal t = m_result_stack[sz-2]; sat::literal e = m_result_stack[sz-1]; if (root) { SASSERT(sz == 3); if (sign) { mk_clause(~c, ~t); mk_clause(c, ~e); } else { mk_clause(~c, t); mk_clause(c, e); } m_result_stack.reset(); } else { sat::bool_var k = m_solver.mk_var(); sat::literal l(k, false); m_cache.insert(n, l); mk_clause(~l, ~c, t); mk_clause(~l, c, e); mk_clause(l, ~c, ~t); mk_clause(l, c, ~e); if (m_ite_extra) { mk_clause(~t, ~e, l); mk_clause(t, e, ~l); } m_result_stack.shrink(sz-3); if (sign) l.neg(); m_result_stack.push_back(l); } } void convert_iff(app * t, bool root, bool sign) { TRACE("goal2sat", tout << "convert_iff " << root << " " << sign << "\n" << mk_ismt2_pp(t, m) << "\n";); unsigned sz = m_result_stack.size(); SASSERT(sz >= 2); sat::literal l1 = m_result_stack[sz-1]; sat::literal l2 = m_result_stack[sz-2]; if (root) { SASSERT(sz == 2); if (sign) { mk_clause(l1, l2); mk_clause(~l1, ~l2); } else { mk_clause(l1, ~l2); mk_clause(~l1, l2); } m_result_stack.reset(); } else { sat::bool_var k = m_solver.mk_var(); sat::literal l(k, false); m_cache.insert(t, l); mk_clause(~l, l1, ~l2); mk_clause(~l, ~l1, l2); mk_clause(l, l1, l2); mk_clause(l, ~l1, ~l2); m_result_stack.shrink(sz-2); if (sign) l.neg(); m_result_stack.push_back(l); } } void convert(app * t, bool root, bool sign) { SASSERT(t->get_family_id() == m.get_basic_family_id()); switch (to_app(t)->get_decl_kind()) { case OP_OR: convert_or(t, root, sign); break; case OP_ITE: convert_ite(t, root, sign); break; case OP_IFF: case OP_EQ: convert_iff(t, root, sign); break; default: UNREACHABLE(); } } void process(expr * n) { TRACE("goal2sat", tout << "converting: " << mk_ismt2_pp(n, m) << "\n";); if (visit(n, true, false)) { SASSERT(m_result_stack.empty()); return; } while (!m_frame_stack.empty()) { loop: cooperate("goal2sat"); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); frame & fr = m_frame_stack.back(); app * t = fr.m_t; bool root = fr.m_root; bool sign = fr.m_sign; TRACE("goal2sat_bug", tout << "result stack\n"; tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n"; for (unsigned i = 0; i < m_result_stack.size(); i++) tout << m_result_stack[i] << " "; tout << "\n";); if (fr.m_idx == 0 && process_cached(t, root, sign)) { m_frame_stack.pop_back(); continue; } if (m.is_not(t)) { m_frame_stack.pop_back(); visit(t->get_arg(0), root, !sign); continue; } unsigned num = t->get_num_args(); while (fr.m_idx < num) { expr * arg = t->get_arg(fr.m_idx); fr.m_idx++; if (!visit(arg, false, false)) goto loop; } TRACE("goal2sat_bug", tout << "converting\n"; tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n"; for (unsigned i = 0; i < m_result_stack.size(); i++) tout << m_result_stack[i] << " "; tout << "\n";); convert(t, root, sign); m_frame_stack.pop_back(); } SASSERT(m_result_stack.empty()); } void insert_dep(expr* dep0, expr* dep, bool sign) { SASSERT(sign || dep0 == dep); // !sign || (not dep0) == dep. SASSERT(!sign || m.is_not(dep0)); expr_ref new_dep(m), fml(m); if (is_uninterp_const(dep)) { new_dep = dep; } else { new_dep = m.mk_fresh_const("dep", m.mk_bool_sort()); m_trail.push_back(new_dep); m_interface_vars.insert(new_dep); fml = m.mk_iff(new_dep, dep); process(fml); } convert_atom(new_dep, false, false); sat::literal lit = m_result_stack.back(); m_dep2asm.insert(dep0, sign?~lit:lit); m_result_stack.pop_back(); } void operator()(goal const & g) { m_interface_vars.reset(); collect_boolean_interface(g, m_interface_vars); unsigned size = g.size(); expr_ref f(m), d_new(m); ptr_vector deps; expr_ref_vector fmls(m); for (unsigned idx = 0; idx < size; idx++) { f = g.form(idx); // Add assumptions. if (g.dep(idx)) { deps.reset(); fmls.reset(); m.linearize(g.dep(idx), deps); fmls.push_back(f); for (unsigned i = 0; i < deps.size(); ++i) { expr * d = deps[i]; expr * d1 = d; SASSERT(m.is_bool(d)); bool sign = m.is_not(d, d1); insert_dep(d, d1, sign); if (d == f) { goto skip_dep; } if (sign) { d_new = d1; } else { d_new = m.mk_not(d); } fmls.push_back(d_new); } f = m.mk_or(fmls.size(), fmls.c_ptr()); } TRACE("sat", tout << mk_pp(f, m) << "\n";); process(f); skip_dep: ; } } void operator()(unsigned sz, expr * const * fs) { m_interface_vars.reset(); collect_boolean_interface(m, sz, fs, m_interface_vars); for (unsigned i = 0; i < sz; i++) process(fs[i]); } void set_cancel(bool f) { m_cancel = f; } }; struct unsupported_bool_proc { struct found {}; ast_manager & m; unsupported_bool_proc(ast_manager & _m):m(_m) {} void operator()(var *) {} void operator()(quantifier *) {} void operator()(app * n) { if (n->get_family_id() == m.get_basic_family_id()) { switch (n->get_decl_kind()) { case OP_AND: case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: throw found(); default: break; } } } }; /** \brief Return true if s contains an unsupported Boolean operator. goal_rewriter (with the following configuration) can be used to eliminate unsupported operators. :elim-and true :blast-distinct true */ bool goal2sat::has_unsupported_bool(goal const & g) { return test(g); } goal2sat::goal2sat():m_imp(0) { } void goal2sat::collect_param_descrs(param_descrs & r) { insert_max_memory(r); r.insert("ite_extra", CPK_BOOL, "(default: true) add redundant clauses (that improve unit propagation) when encoding if-then-else formulas"); } struct goal2sat::scoped_set_imp { goal2sat * m_owner; scoped_set_imp(goal2sat * o, goal2sat::imp * i):m_owner(o) { #pragma omp critical (goal2sat) { m_owner->m_imp = i; } } ~scoped_set_imp() { #pragma omp critical (goal2sat) { m_owner->m_imp = 0; } } }; void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external) { imp proc(g.m(), p, t, m, dep2asm, default_external); scoped_set_imp set(this, &proc); proc(g); } void goal2sat::set_cancel(bool f) { #pragma omp critical (goal2sat) { if (m_imp) m_imp->set_cancel(f); } } struct sat2goal::imp { // Wrapper for sat::model_converter: converts it into an "AST level" model_converter. class sat_model_converter : public model_converter { sat::model_converter m_mc; // TODO: the following mapping is storing a lot of useless information, and may be a performance bottleneck. // We need to save only the expressions associated with variables that occur in m_mc. // This information may be stored as a vector of pairs. // The mapping is only created during the model conversion. expr_ref_vector m_var2expr; ref m_fmc; // filter for eliminating fresh variables introduced in the assertion-set --> sat conversion sat_model_converter(ast_manager & m): m_var2expr(m) { } public: sat_model_converter(ast_manager & m, sat::solver const & s):m_var2expr(m) { m_mc.copy(s.get_model_converter()); m_fmc = alloc(filter_model_converter, m); } ast_manager & m() { return m_var2expr.get_manager(); } void insert(expr * atom, bool aux) { m_var2expr.push_back(atom); if (aux) { SASSERT(is_uninterp_const(atom)); SASSERT(m().is_bool(atom)); m_fmc->insert(to_app(atom)->get_decl()); } } virtual void operator()(model_ref & md, unsigned goal_idx) { SASSERT(goal_idx == 0); TRACE("sat_mc", tout << "before sat_mc\n"; model_v2_pp(tout, *md); display(tout);); // REMARK: potential problem // model_evaluator can't evaluate quantifiers. Then, // an eliminated variable that depends on a quantified expression can't be recovered. // A similar problem also affects any model_converter that uses elim_var_model_converter. // // Possible solution: // model_converters reject any variable elimination that depends on a quantified expression. model_evaluator ev(*md); ev.set_model_completion(false); // create a SAT model using md sat::model sat_md; unsigned sz = m_var2expr.size(); expr_ref val(m()); for (sat::bool_var v = 0; v < sz; v++) { expr * atom = m_var2expr.get(v); ev(atom, val); if (m().is_true(val)) sat_md.push_back(l_true); else if (m().is_false(val)) sat_md.push_back(l_false); else sat_md.push_back(l_undef); } // apply SAT model converter m_mc(sat_md); // register value of non-auxiliary boolean variables back into md sz = m_var2expr.size(); for (sat::bool_var v = 0; v < sz; v++) { expr * atom = m_var2expr.get(v); if (is_uninterp_const(atom)) { func_decl * d = to_app(atom)->get_decl(); lbool new_val = sat_md[v]; if (new_val == l_true) md->register_decl(d, m().mk_true()); else if (new_val == l_false) md->register_decl(d, m().mk_false()); } } // apply filter model converter (*m_fmc)(md); TRACE("sat_mc", tout << "after sat_mc\n"; model_v2_pp(tout, *md);); } virtual model_converter * translate(ast_translation & translator) { sat_model_converter * res = alloc(sat_model_converter, translator.to()); res->m_fmc = static_cast(m_fmc->translate(translator)); unsigned sz = m_var2expr.size(); for (unsigned i = 0; i < sz; i++) res->m_var2expr.push_back(translator(m_var2expr.get(i))); return res; } void display(std::ostream & out) { out << "(sat-model-converter\n"; m_mc.display(out); sat::bool_var_set vars; m_mc.collect_vars(vars); out << "(atoms"; unsigned sz = m_var2expr.size(); for (unsigned i = 0; i < sz; i++) { if (vars.contains(i)) { out << "\n (" << i << "\n " << mk_ismt2_pp(m_var2expr.get(i), m(), 2) << ")"; } } out << ")\n"; m_fmc->display(out); out << ")\n"; } }; ast_manager & m; expr_ref_vector m_lit2expr; unsigned long long m_max_memory; bool m_learned; volatile bool m_cancel; imp(ast_manager & _m, params_ref const & p):m(_m), m_lit2expr(m), m_cancel(false) { updt_params(p); } void updt_params(params_ref const & p) { m_learned = p.get_bool("learned", false); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); } void checkpoint() { 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 init_lit2expr(sat::solver const & s, atom2bool_var const & map, model_converter_ref & mc, bool produce_models) { ref _mc; if (produce_models) _mc = alloc(sat_model_converter, m, s); unsigned num_vars = s.num_vars(); m_lit2expr.resize(num_vars * 2); map.mk_inv(m_lit2expr); sort * b = m.mk_bool_sort(); for (sat::bool_var v = 0; v < num_vars; v++) { checkpoint(); sat::literal l(v, false); if (m_lit2expr.get(l.index()) == 0) { SASSERT(m_lit2expr.get((~l).index()) == 0); app * aux = m.mk_fresh_const(0, b); if (_mc) _mc->insert(aux, true); m_lit2expr.set(l.index(), aux); m_lit2expr.set((~l).index(), m.mk_not(aux)); } else { if (_mc) _mc->insert(m_lit2expr.get(l.index()), false); SASSERT(m_lit2expr.get((~l).index()) != 0); } } mc = _mc.get(); } expr * lit2expr(sat::literal l) { return m_lit2expr.get(l.index()); } void assert_clauses(sat::clause * const * begin, sat::clause * const * end, goal & r) { ptr_buffer lits; for (sat::clause * const * it = begin; it != end; it++) { checkpoint(); lits.reset(); sat::clause const & c = *(*it); unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { lits.push_back(lit2expr(c[i])); } r.assert_expr(m.mk_or(lits.size(), lits.c_ptr())); } } void operator()(sat::solver const & s, atom2bool_var const & map, goal & r, model_converter_ref & mc) { if (s.inconsistent()) { r.assert_expr(m.mk_false()); return; } init_lit2expr(s, map, mc, r.models_enabled()); // collect units unsigned num_vars = s.num_vars(); for (sat::bool_var v = 0; v < num_vars; v++) { checkpoint(); switch (s.value(v)) { case l_true: r.assert_expr(lit2expr(sat::literal(v, false))); break; case l_false: r.assert_expr(lit2expr(sat::literal(v, true))); break; case l_undef: break; } } // collect binary clauses svector bin_clauses; s.collect_bin_clauses(bin_clauses, m_learned); svector::iterator it = bin_clauses.begin(); svector::iterator end = bin_clauses.end(); for (; it != end; ++it) { checkpoint(); r.assert_expr(m.mk_or(lit2expr(it->first), lit2expr(it->second))); } // collect clauses assert_clauses(s.begin_clauses(), s.end_clauses(), r); if (m_learned) assert_clauses(s.begin_learned(), s.end_learned(), r); } void set_cancel(bool f) { m_cancel = f; } }; sat2goal::sat2goal():m_imp(0) { } void sat2goal::collect_param_descrs(param_descrs & r) { insert_max_memory(r); r.insert("learned", CPK_BOOL, "(default: false) collect also learned clauses."); } struct sat2goal::scoped_set_imp { sat2goal * m_owner; scoped_set_imp(sat2goal * o, sat2goal::imp * i):m_owner(o) { #pragma omp critical (sat2goal) { m_owner->m_imp = i; } } ~scoped_set_imp() { #pragma omp critical (sat2goal) { m_owner->m_imp = 0; } } }; void sat2goal::operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p, goal & g, model_converter_ref & mc) { imp proc(g.m(), p); scoped_set_imp set(this, &proc); proc(t, m, g, mc); } void sat2goal::set_cancel(bool f) { #pragma omp critical (sat2goal) { if (m_imp) m_imp->set_cancel(f); } } z3-z3-4.4.1/src/sat/tactic/goal2sat.h000066400000000000000000000050411260446376700171370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal2sat.h Abstract: "Compile" a goal into the SAT engine. Atoms are "abstracted" into boolean variables. The mapping between boolean variables and atoms can be used to convert back the state of the SAT engine into a goal. The idea is to support scenarios such as: 1) simplify, blast, convert into SAT, and solve 2) convert into SAT, apply SAT for a while, learn new units, and translate back into a goal. 3) convert into SAT, apply SAT preprocessor (failed literal propagation, resolution, etc) and translate back into a goal. 4) Convert boolean structure into SAT, convert atoms into another engine, combine engines using lazy combination, solve. Author: Leonardo (leonardo) 2011-10-26 Notes: --*/ #ifndef GOAL2SAT_H_ #define GOAL2SAT_H_ #include"goal.h" #include"sat_solver.h" #include"model_converter.h" #include"atom2bool_var.h" class goal2sat { struct imp; imp * m_imp; struct scoped_set_imp; public: goal2sat(); typedef obj_map dep2asm_map; static void collect_param_descrs(param_descrs & r); static bool has_unsupported_bool(goal const & s); /** \brief "Compile" the goal into the given sat solver. Store a mapping from atoms to boolean variables into m. \remark m doesn't need to be empty. the definitions there are reused. \warning conversion throws a tactic_exception, if it is interrupted (by set_cancel), an unsupported operator is found, or memory consumption limit is reached (set with param :max-memory). */ void operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false); void set_cancel(bool f); }; class sat2goal { struct imp; imp * m_imp; struct scoped_set_imp; public: sat2goal(); static void collect_param_descrs(param_descrs & r); /** \brief Translate the state of the SAT engine back into a goal. The SAT solver may use variables that are not in \c m. The translator creates fresh boolean AST variables for them. They are stored in fvars. \warning conversion throws a tactic_exception, if it is interrupted (by set_cancel), or memory consumption limit is reached (set with param :max-memory). */ void operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p, goal & s, model_converter_ref & mc); void set_cancel(bool f); }; #endif z3-z3-4.4.1/src/sat/tactic/sat_tactic.cpp000066400000000000000000000200101260446376700200650ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_tactic.cpp Abstract: Tactic for using the SAT solver and its preprocessing capabilities. Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #include"tactical.h" #include"goal2sat.h" #include"sat_solver.h" #include"filter_model_converter.h" #include"ast_smt2_pp.h" #include"model_v2_pp.h" class sat_tactic : public tactic { struct imp { ast_manager & m; goal2sat m_goal2sat; sat2goal m_sat2goal; sat::solver m_solver; params_ref m_params; imp(ast_manager & _m, params_ref const & p): m(_m), m_solver(p, m.limit(), 0), m_params(p) { SASSERT(!m.proofs_enabled()); } 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; fail_if_proof_generation("sat", g); bool produce_models = g->models_enabled(); bool produce_core = g->unsat_core_enabled(); TRACE("before_sat_solver", g->display(tout);); g->elim_redundancies(); atom2bool_var map(m); obj_map dep2asm; sat::literal_vector assumptions; m_goal2sat(*g, m_params, m_solver, map, dep2asm); 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) << "\n"; }); g->reset(); g->m().compact_memory(); CASSERT("sat_solver", m_solver.check_invariant()); IF_VERBOSE(TACTIC_VERBOSITY_LVL, m_solver.display_status(verbose_stream());); TRACE("sat_dimacs", m_solver.display_dimacs(tout);); dep2assumptions(dep2asm, assumptions); lbool r = m_solver.check(assumptions.size(), assumptions.c_ptr()); if (r == l_false) { expr_dependency * lcore = 0; if (produce_core) { sat::literal_vector const& core = m_solver.get_core(); u_map asm2dep; mk_asm2dep(dep2asm, asm2dep); for (unsigned i = 0; i < core.size(); ++i) { sat::literal lit = core[i]; expr* dep = asm2dep.find(lit.index()); lcore = m.mk_join(lcore, m.mk_leaf(dep)); } } g->assert_expr(m.mk_false(), 0, lcore); } else if (r == l_true && !map.interpreted_atoms()) { // register model if (produce_models) { model_ref md = alloc(model, m); sat::model const & ll_m = 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; TRACE("sat_tactic", tout << "extracting value of " << mk_ismt2_pp(n, m) << "\nvar: " << v << "\n";); 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; } } TRACE("sat_tactic", model_v2_pp(tout, *md);); mc = model2model_converter(md.get()); } } else { // get simplified problem. #if 0 IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "\"formula constains interpreted atoms, recovering formula from sat solver...\"\n";); #endif m_solver.pop_to_base_level(); m_sat2goal(m_solver, map, m_params, *(g.get()), mc); } g->inc_depth(); result.push_back(g.get()); } void set_cancel(bool f) { m_goal2sat.set_cancel(f); m_sat2goal.set_cancel(f); m_solver.set_cancel(f); } void dep2assumptions(obj_map& dep2asm, sat::literal_vector& assumptions) { obj_map::iterator it = dep2asm.begin(), end = dep2asm.end(); for (; it != end; ++it) { assumptions.push_back(it->m_value); } } void mk_asm2dep(obj_map& dep2asm, u_map& lit2asm) { obj_map::iterator it = dep2asm.begin(), end = dep2asm.end(); for (; it != end; ++it) { lit2asm.insert(it->m_value.index(), it->m_key); } } }; struct scoped_set_imp { sat_tactic * m_owner; scoped_set_imp(sat_tactic * o, imp * i):m_owner(o) { #pragma omp critical (sat_tactic) { m_owner->m_imp = i; } } ~scoped_set_imp() { #pragma omp critical (sat_tactic) { m_owner->m_imp = 0; } } }; imp * m_imp; params_ref m_params; statistics m_stats; public: sat_tactic(ast_manager & m, params_ref const & p): m_imp(0), m_params(p) { } virtual tactic * translate(ast_manager & m) { return alloc(sat_tactic, m, m_params); } virtual ~sat_tactic() { SASSERT(m_imp == 0); } virtual void updt_params(params_ref const & p) { m_params = p; } virtual void collect_param_descrs(param_descrs & r) { goal2sat::collect_param_descrs(r); sat2goal::collect_param_descrs(r); sat::solver::collect_param_descrs(r); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { imp proc(g->m(), m_params); scoped_set_imp set(this, &proc); try { proc(g, result, mc, pc, core); proc.m_solver.collect_statistics(m_stats); } catch (sat::solver_exception & ex) { proc.m_solver.collect_statistics(m_stats); throw tactic_exception(ex.msg()); } TRACE("sat_stats", m_stats.display_smt2(tout);); } virtual void cleanup() { SASSERT(m_imp == 0); } virtual void collect_statistics(statistics & st) const { st.copy(m_stats); } virtual void reset_statistics() { m_stats.reset(); } protected: virtual void set_cancel(bool f) { #pragma omp critical (sat_tactic) { if (m_imp) m_imp->set_cancel(f); } } }; tactic * mk_sat_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(sat_tactic, m, p)); } tactic * mk_sat_preprocessor_tactic(ast_manager & m, params_ref const & p) { params_ref p_aux; p_aux.set_uint("max_conflicts", 0); tactic * t = clean(using_params(mk_sat_tactic(m, p), p_aux)); t->updt_params(p); return t; } z3-z3-4.4.1/src/sat/tactic/sat_tactic.h000066400000000000000000000014441260446376700175440ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_tactic.cpp Abstract: Tactic for using the SAT solver and its preprocessing capabilities. Author: Leonardo (leonardo) 2011-10-26 Notes: --*/ #ifndef SAT_TACTIC_H_ #define SAT_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_sat_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_sat_preprocessor_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC('sat', '(try to) solve goal using a SAT solver.', 'mk_sat_tactic(m, p)') ADD_TACTIC('sat-preprocess', 'Apply SAT solver preprocessing procedures (bounded resolution, Boolean constant propagation, 2-SAT, subsumption, subsumption resolution).', 'mk_sat_preprocessor_tactic(m, p)') */ #endif z3-z3-4.4.1/src/shell/000077500000000000000000000000001260446376700143235ustar00rootroot00000000000000z3-z3-4.4.1/src/shell/datalog_frontend.cpp000066400000000000000000000161311260446376700203430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: datalog_frontend.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #include #include #include #include"stopwatch.h" #ifdef _CYGWIN #undef min #undef max #endif #include"smt_params.h" #include"arith_decl_plugin.h" #include"dl_compiler.h" #include"dl_mk_filter_rules.h" #include"dl_finite_product_relation.h" #include"dl_context.h" #include"rel_context.h" #include"dl_register_engine.h" #include"datalog_parser.h" #include"datalog_frontend.h" #include"timeout.h" static stopwatch g_overall_time; static stopwatch g_piece_timer; static unsigned t_parsing = 0; static datalog::context * g_ctx = 0; static datalog::rule_set * g_orig_rules; static datalog::instruction_block * g_code; static datalog::execution_context * g_ectx; static void display_statistics( std::ostream& out, datalog::context& ctx, datalog::rule_set& orig_rules, datalog::instruction_block& code, datalog::execution_context& ex_ctx, bool verbose ) { g_piece_timer.stop(); unsigned t_other = static_cast(g_piece_timer.get_seconds()*1000); g_overall_time.stop(); code.process_all_costs(); { params_ref p; p.set_bool("output_profile", true); p.set_uint("profile_milliseconds_threshold", 100); ctx.updt_params(p); out << "--------------\n"; out << "original rules\n"; orig_rules.display(out); out << "---------------\n"; out << "generated rules\n"; ctx.display_rules(out); out << "--------------\n"; out << "instructions \n"; code.display(ex_ctx, out); out << "--------------\n"; out << "big relations \n"; ex_ctx.report_big_relations(1000, out); } out << "--------------\n"; out << "relation sizes\n"; ctx.get_rel_context()->get_rmanager().display_relation_sizes(out); if (verbose) { out << "--------------\n"; out << "rules\n"; ctx.display_rules(out); } out << "Time: " << static_cast(g_overall_time.get_seconds()*1000) << "ms\n"; out << "Parsing: " << t_parsing << "ms, other: " << t_other << "ms\n"; } static void display_statistics() { if (g_ctx) { display_statistics(std::cout, *g_ctx, *g_orig_rules, *g_code, *g_ectx, true); } } static void on_timeout() { display_statistics(); exit(0); } static void on_ctrl_c(int) { signal (SIGINT, SIG_DFL); display_statistics(); raise(SIGINT); } unsigned read_datalog(char const * file) { IF_VERBOSE(1, verbose_stream() << "Z3 Datalog Engine\n";); smt_params s_params; ast_manager m; datalog::register_engine re; g_overall_time.start(); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); params_ref params; params.set_sym("engine", symbol("datalog")); datalog::context ctx(m, re, s_params, params); datalog::relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); datalog::relation_plugin & inner_plg = *rmgr.get_relation_plugin(symbol("tr_hashtable")); SASSERT(&inner_plg); rmgr.register_plugin(alloc(datalog::finite_product_relation_plugin, inner_plg, rmgr)); g_piece_timer.reset(); g_piece_timer.start(); bool wpa_benchmark = datalog::is_directory(std::string(file)); if (wpa_benchmark) { scoped_ptr parser = datalog::wpa_parser::create(ctx, m); if (!parser->parse_directory(file)) { std::cerr << "ERROR: failed to parse file\n"; return 1; } } else { scoped_ptr parser = datalog::parser::create(ctx, m); if (!parser->parse_file(file)) { std::cerr << "ERROR: failed to parse file\n"; return 1; } } g_piece_timer.stop(); t_parsing = static_cast(g_piece_timer.get_seconds()*1000); IF_VERBOSE(1, verbose_stream() << "parsing finished\n";); IF_VERBOSE(1, verbose_stream() << "running saturation...\n";); g_piece_timer.reset(); g_piece_timer.start(); //all rules were added ctx.close(); TRACE("dl_compiler", ctx.display(tout);); datalog::rule_set original_rules(ctx.get_rules()); datalog::instruction_block rules_code; datalog::instruction_block termination_code; datalog::execution_context ex_ctx(ctx); IF_VERBOSE(10, original_rules.display_deps(verbose_stream());); g_ctx = &ctx; g_orig_rules = &original_rules; g_code = &rules_code; g_ectx = &ex_ctx; try { g_piece_timer.reset(); g_piece_timer.start(); bool early_termination; unsigned timeout = ctx.initial_restart_timeout(); if (timeout == 0) { timeout = UINT_MAX; } do { ctx.get_rel_context()->transform_rules(); datalog::compiler::compile(ctx, ctx.get_rules(), rules_code, termination_code); TRACE("dl_compiler", rules_code.display(ex_ctx, tout);); rules_code.make_annotations(ex_ctx); ex_ctx.set_timelimit(timeout); early_termination = !rules_code.perform(ex_ctx); if(early_termination) { IF_VERBOSE(10, ex_ctx.report_big_relations(1000, verbose_stream());); if (memory::above_high_watermark()) { throw out_of_memory_error(); } } ex_ctx.reset_timelimit(); TRUSTME( termination_code.perform(ex_ctx) ); ctx.saturation_was_run(); if (early_termination) { IF_VERBOSE(1, verbose_stream() << "restarting saturation\n";); uint64 new_timeout = static_cast(timeout)*ctx.initial_restart_timeout(); if(new_timeout>UINT_MAX) { timeout=UINT_MAX; } else { timeout=static_cast(new_timeout); } rules_code.process_all_costs(); rules_code.reset(); termination_code.reset(); ex_ctx.reset(); ctx.reopen(); ctx.replace_rules(original_rules); ctx.close(); } } while (early_termination); TRACE("dl_compiler", ctx.display(tout); rules_code.display(ex_ctx, tout);); if (ctx.output_tuples()) { ctx.get_rel_context()->display_output_facts(ctx.get_rules(), std::cout); } display_statistics( std::cout, ctx, original_rules, rules_code, ex_ctx, false); } catch (out_of_memory_error) { std::cout << "\n\nOUT OF MEMORY!\n\n"; display_statistics( std::cout, ctx, original_rules, rules_code, ex_ctx, true); return ERR_MEMOUT; } register_on_timeout_proc(0); return 0; } z3-z3-4.4.1/src/shell/datalog_frontend.h000066400000000000000000000005071260446376700200100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: datalog_frontend.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #ifndef DATALOG_FRONTEND_H_ #define DATALOG_FRONTEND_H_ unsigned read_datalog(char const * file); #endif /* DATALOG_FRONTEND_H_ */ z3-z3-4.4.1/src/shell/dimacs_frontend.cpp000066400000000000000000000123741260446376700201750ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dimacs_frontend.cpp Abstract: Frontend for reading dimacs input files Author: Leonardo de Moura (leonardo) 2011-07-26. Revision History: --*/ #include #include #include #include"timeout.h" #include"rlimit.h" #include"dimacs.h" #include"sat_solver.h" #include"gparams.h" extern bool g_display_statistics; static sat::solver * g_solver = 0; static clock_t g_start_time; static void display_statistics() { clock_t end_time = clock(); if (g_solver && g_display_statistics) { std::cout.flush(); std::cerr.flush(); statistics st; g_solver->collect_statistics(st); st.update("total time", ((static_cast(end_time) - static_cast(g_start_time)) / CLOCKS_PER_SEC)); st.display_smt2(std::cout); } } static void on_timeout() { display_statistics(); exit(0); } static void on_ctrl_c(int) { signal (SIGINT, SIG_DFL); display_statistics(); raise(SIGINT); } static void display_model(sat::solver const & s) { sat::model const & m = s.get_model(); for (unsigned i = 1; i < m.size(); i++) { switch (m[i]) { case l_false: std::cout << "-" << i << " "; break; case l_undef: break; case l_true: std::cout << i << " "; break; } } std::cout << "\n"; } static void display_core(sat::solver const& s, vector const& tracking_clauses) { std::cout << "core\n"; sat::literal_vector const& c = s.get_core(); for (unsigned i = 0; i < c.size(); ++i) { sat::literal_vector const& cls = tracking_clauses[c[i].var()]; for (unsigned j = 0; j < cls.size(); ++j) { std::cout << cls[j] << " "; } std::cout << "\n"; } } static void track_clause(sat::solver& dst, sat::literal_vector& lits, sat::literal_vector& assumptions, vector& tracking_clauses) { sat::literal lit = sat::literal(dst.mk_var(true, false), false); tracking_clauses.set(lit.var(), lits); lits.push_back(~lit); dst.mk_clause(lits.size(), lits.c_ptr()); assumptions.push_back(lit); } static void track_clauses(sat::solver const& src, sat::solver& dst, sat::literal_vector& assumptions, vector& tracking_clauses) { for (sat::bool_var v = 0; v < src.num_vars(); ++v) { dst.mk_var(false, true); } sat::literal_vector lits; sat::literal lit; sat::clause * const * it = src.begin_clauses(); sat::clause * const * end = src.end_clauses(); svector bin_clauses; src.collect_bin_clauses(bin_clauses, false); tracking_clauses.reserve(2*src.num_vars() + static_cast(end - it) + bin_clauses.size()); for (sat::bool_var v = 1; v < src.num_vars(); ++v) { if (src.value(v) != l_undef) { bool sign = src.value(v) == l_false; lits.reset(); lits.push_back(sat::literal(v, sign)); track_clause(dst, lits, assumptions, tracking_clauses); } } for (; it != end; ++it) { lits.reset(); sat::clause& cls = *(*it); lits.append(static_cast(cls.end()-cls.begin()), cls.begin()); track_clause(dst, lits, assumptions, tracking_clauses); } for (unsigned i = 0; i < bin_clauses.size(); ++i) { lits.reset(); lits.push_back(bin_clauses[i].first); lits.push_back(bin_clauses[i].second); track_clause(dst, lits, assumptions, tracking_clauses); } } unsigned read_dimacs(char const * file_name) { g_start_time = clock(); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); params_ref p = gparams::get_module("sat"); p.set_bool("produce_models", true); reslimit limit; sat::solver solver(p, limit, 0); g_solver = &solver; if (file_name) { std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } parse_dimacs(in, solver); } else { parse_dimacs(std::cin, solver); } IF_VERBOSE(20, solver.display_status(verbose_stream());); lbool r; vector tracking_clauses; sat::solver solver2(p, limit, 0); if (p.get_bool("dimacs.core", false)) { g_solver = &solver2; sat::literal_vector assumptions; track_clauses(solver, solver2, assumptions, tracking_clauses); r = g_solver->check(assumptions.size(), assumptions.c_ptr()); } else { r = g_solver->check(); } switch (r) { case l_true: std::cout << "sat\n"; display_model(*g_solver); break; case l_undef: std::cout << "unknown\n"; break; case l_false: std::cout << "unsat\n"; if (p.get_bool("dimacs.core", false)) { display_core(*g_solver, tracking_clauses); } break; } if (g_display_statistics) display_statistics(); return 0; } z3-z3-4.4.1/src/shell/dimacs_frontend.h000066400000000000000000000005121260446376700176310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dimacs_frontend.h Abstract: Author: Leonardo de Moura (leonardo) 2011-07-26. Revision History: --*/ #ifndef DIMACS_FRONTEND_H_ #define DIMACS_FRONTEND_H_ unsigned read_dimacs(char const * benchmark_file); #endif /* DIMACS_FRONTEND_H_ */ z3-z3-4.4.1/src/shell/main.cpp000066400000000000000000000317361260446376700157650ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: main.cpp Abstract: Z3 command line tool. Author: Leonardo de Moura (leonardo) 2006-10-10. Nikolaj Bjorner (nbjorner) Revision History: --*/ #include #include"memory_manager.h" #include"trace.h" #include"debug.h" #include"util.h" #include"pp.h" #include"smtlib_frontend.h" #include"z3_log_frontend.h" #include"warning.h" #include"version.h" #include"dimacs_frontend.h" #include"datalog_frontend.h" #include"opt_frontend.h" #include"timeout.h" #include"z3_exception.h" #include"error_codes.h" #include"gparams.h" #include"env_params.h" typedef enum { IN_UNSPECIFIED, IN_SMTLIB, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_Z3_LOG } input_kind; std::string g_aux_input_file; char const * g_input_file = 0; bool g_standard_input = false; input_kind g_input_kind = IN_UNSPECIFIED; bool g_display_statistics = false; bool g_display_istatistics = false; void error(const char * msg) { std::cerr << "Error: " << msg << "\n"; std::cerr << "For usage information: z3 -h\n"; exit(ERR_CMD_LINE); } #define STRINGIZE(x) #x #define STRINGIZE_VALUE_OF(x) STRINGIZE(x) void display_usage() { std::cout << "Z3 [version " << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER; std::cout << " - "; #ifdef _AMD64_ std::cout << "64"; #else std::cout << "32"; #endif std::cout << " bit"; #ifdef Z3GITHASH std::cout << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH); #endif std::cout << "]. (C) Copyright 2006-2014 Microsoft Corp.\n"; std::cout << "Usage: z3 [options] [-file:]file\n"; std::cout << "\nInput format:\n"; std::cout << " -smt use parser for SMT input format.\n"; std::cout << " -smt2 use parser for SMT 2 input format.\n"; std::cout << " -dl use parser for Datalog input format.\n"; std::cout << " -dimacs use parser for DIMACS input format.\n"; std::cout << " -log use parser for Z3 log input format.\n"; std::cout << " -in read formula from standard input.\n"; std::cout << "\nMiscellaneous:\n"; std::cout << " -h, -? prints this message.\n"; std::cout << " -version prints version number of Z3.\n"; std::cout << " -v:level be verbose, where is the verbosity level.\n"; std::cout << " -nw disable warning messages.\n"; std::cout << " -p display Z3 global (and module) parameters.\n"; std::cout << " -pd display Z3 global (and module) parameter descriptions.\n"; std::cout << " -pm:name display Z3 module ('name') parameters.\n"; std::cout << " -pp:name display Z3 parameter description, if 'name' is not provided, then all module names are listed.\n"; std::cout << " --" << " all remaining arguments are assumed to be part of the input file name. This option allows Z3 to read files with strange names such as: -foo.smt2.\n"; std::cout << "\nResources:\n"; // timeout and memout are now available on Linux and OSX too. std::cout << " -T:timeout set the timeout (in seconds).\n"; std::cout << " -t:timeout set the soft timeout (in milli seconds). It only kills the current query.\n"; std::cout << " -memory:Megabytes set a limit for virtual memory consumption.\n"; // std::cout << "\nOutput:\n"; std::cout << " -st display statistics.\n"; #if defined(Z3DEBUG) || defined(_TRACE) std::cout << "\nDebugging support:\n"; #endif #ifdef _TRACE std::cout << " -tr:tag enable trace messages tagged with .\n"; #endif #ifdef Z3DEBUG std::cout << " -dbg:tag enable assertions tagged with .\n"; #endif std::cout << "\nParameter setting:\n"; std::cout << "Global and module parameters can be set in the command line.\n"; std::cout << " param_name=value for setting global parameters.\n"; std::cout << " module_name.param_name=value for setting module parameters.\n"; std::cout << "Use 'z3 -p' for the complete list of global and module parameters.\n"; } void parse_cmd_line_args(int argc, char ** argv) { int i = 1; char * eq_pos = 0; while (i < argc) { char * arg = argv[i]; if (arg[0] == '-' && arg[1] == '-' && arg[2] == 0) { // Little hack used to read files with strange names such as -foo.smt2 // z3 -- -foo.smt2 i++; g_aux_input_file = ""; for (; i < argc; i++) { g_aux_input_file += argv[i]; if (i < argc - 1) g_aux_input_file += " "; } if (g_input_file) { warning_msg("input file was already specified."); } else { g_input_file = g_aux_input_file.c_str(); } break; } if (arg[0] == '-' #ifdef _WINDOWS || arg[0] == '/' #endif ) { char * opt_name = arg + 1; // allow names such as --help if (*opt_name == '-') opt_name++; char * opt_arg = 0; char * colon = strchr(arg, ':'); if (colon) { opt_arg = colon + 1; *colon = 0; } if (strcmp(opt_name, "h") == 0 || strcmp(opt_name, "?") == 0 || strcmp(opt_name, "help") == 0) { display_usage(); exit(0); } if (strcmp(opt_name, "version") == 0) { std::cout << "Z3 version " << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER << "\n"; exit(0); } else if (strcmp(opt_name, "smt") == 0) { g_input_kind = IN_SMTLIB; } else if (strcmp(opt_name, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } else if (strcmp(opt_name, "dl") == 0) { g_input_kind = IN_DATALOG; } else if (strcmp(opt_name, "in") == 0) { g_standard_input = true; } else if (strcmp(opt_name, "dimacs") == 0) { g_input_kind = IN_DIMACS; } else if (strcmp(opt_name, "wcnf") == 0) { g_input_kind = IN_WCNF; } else if (strcmp(opt_name, "bpo") == 0) { g_input_kind = IN_OPB; } else if (strcmp(opt_name, "log") == 0) { g_input_kind = IN_Z3_LOG; } else if (strcmp(opt_name, "st") == 0) { g_display_statistics = true; } else if (strcmp(opt_name, "ist") == 0) { g_display_istatistics = true; } else if (strcmp(opt_name, "v") == 0) { if (!opt_arg) error("option argument (-v:level) is missing."); long lvl = strtol(opt_arg, 0, 10); set_verbosity_level(lvl); } else if (strcmp(opt_name, "file") == 0) { g_input_file = opt_arg; } else if (strcmp(opt_name, "T") == 0) { if (!opt_arg) error("option argument (-T:timeout) is missing."); long tm = strtol(opt_arg, 0, 10); set_timeout(tm * 1000); } else if (strcmp(opt_name, "t") == 0) { if (!opt_arg) error("option argument (-t:timeout) is missing."); gparams::set("timeout", opt_arg); } else if (strcmp(opt_name, "nw") == 0) { enable_warning_messages(false); } else if (strcmp(opt_name, "p") == 0) { gparams::display(std::cout, 0, false, false); exit(0); } else if (strcmp(opt_name, "pd") == 0) { gparams::display(std::cout, 0, false, true); exit(0); } else if (strcmp(opt_name, "pm") == 0) { if (opt_arg) { gparams::display_module(std::cout, opt_arg); } else { gparams::display_modules(std::cout); std::cout << "\nUse -pm:name to display all parameters available at module 'name'\n"; } exit(0); } else if (strcmp(opt_name, "pp") == 0) { if (!opt_arg) error("option argument (-pp:name) is missing."); gparams::display_parameter(std::cout, opt_arg); exit(0); } #ifdef _TRACE else if (strcmp(opt_name, "tr") == 0) { if (!opt_arg) error("option argument (-tr:tag) is missing."); enable_trace(opt_arg); } #endif #ifdef Z3DEBUG else if (strcmp(opt_name, "dbg") == 0) { if (!opt_arg) error("option argument (-dbg:tag) is missing."); enable_debug(opt_arg); } #endif else if (strcmp(opt_name, "memory") == 0) { if (!opt_arg) error("option argument (-memory:val) is missing."); gparams::set("memory_max_size", opt_arg); } else { std::cerr << "Error: invalid command line option: " << arg << "\n"; std::cerr << "For usage information: z3 -h\n"; exit(ERR_CMD_LINE); } } else if (argv[i][0] != '"' && (eq_pos = strchr(argv[i], '='))) { char * key = argv[i]; *eq_pos = 0; char * value = eq_pos+1; gparams::set(key, value); } else { if (g_input_file) { warning_msg("input file was already specified."); } else { g_input_file = arg; } } i++; } } char const * get_extension(char const * file_name) { if (file_name == 0) return 0; char const * last_dot = 0; for (;;) { char const * tmp = strchr(file_name, '.'); if (tmp == 0) { return last_dot; } last_dot = tmp + 1; file_name = last_dot; } } int main(int argc, char ** argv) { try{ unsigned return_value = 0; memory::initialize(0); memory::exit_when_out_of_memory(true, "ERROR: out of memory"); parse_cmd_line_args(argc, argv); env_params::updt_params(); if (g_input_file && g_standard_input) { error("using standard input to read formula."); } if (!g_input_file && !g_standard_input) { error("input file was not specified."); } if (g_input_kind == IN_UNSPECIFIED) { g_input_kind = IN_SMTLIB_2; char const * ext = get_extension(g_input_file); if (ext) { if (strcmp(ext, "datalog") == 0 || strcmp(ext, "dl") == 0) { g_input_kind = IN_DATALOG; } else if (strcmp(ext, "dimacs") == 0 || strcmp(ext, "cnf") == 0) { g_input_kind = IN_DIMACS; } else if (strcmp(ext, "wcnf") == 0) { g_input_kind = IN_WCNF; } else if (strcmp(ext, "opb") == 0) { g_input_kind = IN_OPB; } else if (strcmp(ext, "log") == 0) { g_input_kind = IN_Z3_LOG; } else if (strcmp(ext, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } else if (strcmp(ext, "smt") == 0) { g_input_kind = IN_SMTLIB; } } } switch (g_input_kind) { case IN_SMTLIB: return_value = read_smtlib_file(g_input_file); break; case IN_SMTLIB_2: memory::exit_when_out_of_memory(true, "(error \"out of memory\")"); return_value = read_smtlib2_commands(g_input_file); break; case IN_DIMACS: return_value = read_dimacs(g_input_file); break; case IN_WCNF: return_value = parse_opt(g_input_file, true); break; case IN_OPB: return_value = parse_opt(g_input_file, false); break; case IN_DATALOG: read_datalog(g_input_file); break; case IN_Z3_LOG: replay_z3_log(g_input_file); break; default: UNREACHABLE(); } memory::finalize(); #ifdef _WINDOWS _CrtDumpMemoryLeaks(); #endif return return_value; } catch (z3_exception & ex) { // unhandled exception std::cerr << "ERROR: " << ex.msg() << "\n"; if (ex.has_error_code()) return ex.error_code(); else return ERR_INTERNAL_FATAL; } } z3-z3-4.4.1/src/shell/opt_frontend.cpp000066400000000000000000000234711260446376700175370ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include #include #include"opt_context.h" #include"ast_util.h" #include"arith_decl_plugin.h" #include"gparams.h" #include"timeout.h" #include"reg_decl_plugins.h" extern bool g_display_statistics; static bool g_first_interrupt = true; static opt::context* g_opt = 0; static double g_start_time = 0; static unsigned_vector g_handles; class stream_buffer { std::istream & m_stream; int m_val; unsigned m_line; public: stream_buffer(std::istream & s): m_stream(s), m_line(0) { m_val = m_stream.get(); } int operator *() const { return m_val;} void operator ++() { m_val = m_stream.get(); } int ch() const { return m_val; } void next() { m_val = m_stream.get(); } bool eof() const { return ch() == EOF; } unsigned line() const { return m_line; } void skip_whitespace() { while ((ch() >= 9 && ch() <= 13) || ch() == 32) { if (ch() == 10) ++m_line; next(); } } void skip_space() { while (ch() != 10 && ((ch() >= 9 && ch() <= 13) || ch() == 32)) { next(); } } void skip_line() { while(true) { if (eof()) { return; } if (ch() == '\n') { ++m_line; next(); return; } next(); } } bool parse_token(char const* token) { skip_whitespace(); char const* t = token; while (ch() == *t) { next(); ++t; } return 0 == *t; } int parse_int() { int val = 0; bool neg = false; skip_whitespace(); if (ch() == '-') { neg = true; next(); } else if (ch() == '+') { next(); } if (ch() < '0' || ch() > '9') { std::cerr << "(error line " << line() << " \"unexpected char: " << ((char)ch()) << "\" )\n"; exit(3); } while (ch() >= '0' && ch() <= '9') { val = val*10 + (ch() - '0'); next(); } return neg ? -val : val; } unsigned parse_unsigned() { skip_space(); if (ch() == '\n') { return UINT_MAX; } unsigned val = 0; while (ch() >= '0' && ch() <= '9') { val = val*10 + (ch() - '0'); next(); } return val; } }; class wcnf { opt::context& opt; ast_manager& m; stream_buffer& in; app_ref read_clause(unsigned& weight) { int parsed_lit; int var; weight = in.parse_unsigned(); app_ref result(m), p(m); expr_ref_vector ors(m); while (true) { parsed_lit = in.parse_int(); if (parsed_lit == 0) break; var = abs(parsed_lit); p = m.mk_const(symbol(var), m.mk_bool_sort()); if (parsed_lit < 0) p = m.mk_not(p); ors.push_back(p); } result = to_app(mk_or(m, ors.size(), ors.c_ptr())); return result; } void parse_spec(unsigned& num_vars, unsigned& num_clauses, unsigned& max_weight) { in.parse_token("wcnf"); num_vars = in.parse_unsigned(); num_clauses = in.parse_unsigned(); max_weight = in.parse_unsigned(); } public: wcnf(opt::context& opt, stream_buffer& in): opt(opt), m(opt.get_manager()), in(in) { opt.set_clausal(true); } void parse() { unsigned num_vars = 0, num_clauses = 0, max_weight = 0; while (true) { in.skip_whitespace(); if (in.eof()) { break; } else if (*in == 'c') { in.skip_line(); } else if (*in == 'p') { ++in; parse_spec(num_vars, num_clauses, max_weight); } else { unsigned weight = 0; app_ref cls = read_clause(weight); if (weight >= max_weight) { opt.add_hard_constraint(cls); } else { unsigned id = opt.add_soft_constraint(cls, rational(weight), symbol::null); if (g_handles.empty()) { g_handles.push_back(id); } } } } } }; class opb { opt::context& opt; ast_manager& m; stream_buffer& in; arith_util arith; app_ref parse_id() { bool negated = in.parse_token("~"); if (!in.parse_token("x")) { std::cerr << "(error line " << in.line() << " \"unexpected char: " << ((char)in.ch()) << "\")\n"; exit(3); } app_ref p(m); int id = in.parse_int(); p = m.mk_const(symbol(id), m.mk_bool_sort()); if (negated) p = m.mk_not(p); in.skip_whitespace(); return p; } app_ref parse_ids() { app_ref result = parse_id(); while (*in == '~' || *in == 'x') { result = m.mk_and(result, parse_id()); } return result; } rational parse_coeff_r() { in.skip_whitespace(); svector num; bool pos = true; if (*in == '-') pos = false, ++in; if (*in == '+') ++in; if (!pos) num.push_back('-'); in.skip_whitespace(); while ('0' <= *in && *in <='9') num.push_back(*in), ++in; num.push_back(0); return rational(num.c_ptr()); } app_ref parse_coeff() { return app_ref(arith.mk_numeral(parse_coeff_r(), true), m); } app_ref parse_term() { app_ref c = parse_coeff(); app_ref e = parse_ids(); return app_ref(m.mk_ite(e, c, arith.mk_numeral(rational(0), true)), m); } void parse_objective() { app_ref t = parse_term(); while (!in.parse_token(";") && !in.eof()) { t = arith.mk_add(t, parse_term()); } g_handles.push_back(opt.add_objective(t, false)); } void parse_constraint() { app_ref t = parse_term(); while (!in.eof()) { if (in.parse_token(">=")) { t = arith.mk_ge(t, parse_coeff()); in.parse_token(";"); break; } if (in.parse_token("=")) { t = m.mk_eq(t, parse_coeff()); in.parse_token(";"); break; } t = arith.mk_add(t, parse_term()); } opt.add_hard_constraint(t); } public: opb(opt::context& opt, stream_buffer& in): opt(opt), m(opt.get_manager()), in(in), arith(m) {} void parse() { while (true) { in.skip_whitespace(); if (in.eof()) { break; } else if (*in == '*') { in.skip_line(); } else if (in.parse_token("min:")) { parse_objective(); } else { parse_constraint(); } } } }; static void display_results() { if (g_opt) { for (unsigned i = 0; i < g_handles.size(); ++i) { expr_ref lo = g_opt->get_lower(g_handles[i]); expr_ref hi = g_opt->get_upper(g_handles[i]); if (lo == hi) { std::cout << " " << lo << "\n"; } else { std::cout << " [" << lo << ":" << hi << "]\n"; } } } } static void display_statistics() { if (g_display_statistics && g_opt) { ::statistics stats; g_opt->collect_statistics(stats); stats.display(std::cout); double end_time = static_cast(clock()); std::cout << "time: " << (end_time - g_start_time)/CLOCKS_PER_SEC << " secs\n"; } display_results(); } static void on_ctrl_c(int) { if (g_opt && g_first_interrupt) { g_opt->set_cancel(true); g_first_interrupt = false; } else { signal (SIGINT, SIG_DFL); #pragma omp critical (g_display_stats) { display_statistics(); } raise(SIGINT); } } static void on_timeout() { #pragma omp critical (g_display_stats) { display_statistics(); exit(0); } } static unsigned parse_opt(std::istream& in, bool is_wcnf) { ast_manager m; reg_decl_plugins(m); opt::context opt(m); g_opt = &opt; params_ref p = gparams::get_module("opt"); opt.updt_params(p); stream_buffer _in(in); if (is_wcnf) { wcnf wcnf(opt, _in); wcnf.parse(); } else { opb opb(opt, _in); opb.parse(); } try { lbool r = opt.optimize(); switch (r) { case l_true: std::cout << "sat\n"; break; case l_false: std::cout << "unsat\n"; break; case l_undef: std::cout << "unknown\n"; break; } } catch (z3_exception & ex) { std::cerr << ex.msg() << "\n"; } #pragma omp critical (g_display_stats) { display_statistics(); register_on_timeout_proc(0); g_opt = 0; } return 0; } unsigned parse_opt(char const* file_name, bool is_wcnf) { g_first_interrupt = true; g_start_time = static_cast(clock()); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); if (file_name) { std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } return parse_opt(in, is_wcnf); } else { return parse_opt(std::cin, is_wcnf); } } z3-z3-4.4.1/src/shell/opt_frontend.h000066400000000000000000000004251260446376700171760ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: opt_frontend.h Author: Nikolaj Bjorner (nbjorner) 2014-10-10. --*/ #ifndef OPT_FRONTEND_H_ #define OPT_FRONTEND_H_ unsigned parse_opt(char const* file_name, bool is_wcnf); #endif /* OPT_FRONTEND_H_ */ z3-z3-4.4.1/src/shell/options.h000066400000000000000000000044321260446376700161720ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ /** \page cmdline Command line options \section informat Input format Z3 understands a set of default file extensions, and will invoke a parser based on the extension. - \ext{smt2} - SMT-LIB 2 format, this is the preferred input format. - \ext{dimacs}, \ext{cnf} - DIMACS format used by regular SAT solvers. - \ext{dl} - Datalog input format. - \ext{smt} - (deprecated) SMT-LIB 1 format. You can tell Z3 explicitly which grammar the input belongs to by using the following options: \cmdopt{smt2} use parser for SMT-LIB 2.0 input format. \cmdopt{dimacs} use dimacs parser to read the input file. \section cmdlinemis Miscellaneous \cmdopt{h\, ?} prints the help message. \cmdopt{version} prints version number of Z3. \cmdopt{v:level} be verbose, where is the verbosity level. \cmdopt{nw} disable warning messages. \cmdopt{ini:file} configuration file. Several parameters are available besides the ones listed by \ty{/h}. These parameters can be loaded from an initialization file by using this option. \cmdopt{ini?} display all available INI file parameters. The available \ref config can also be supplied on the command line as a pair parameter-name=parameter-value. \section cmdlineres Resources \cmdopt{T:timeout} set the timeout (in seconds). Setting this option causes the entire process to exit. It is a reliable way to kill Z3. \cmdopt{t:timeout} set the soft timeout (in seconds). It only kills the current query. \cmdopt{memory:Megabytes} set a limit for virtual memory consumption. This limit for virtual memory consumption is approximate, but in general a good guideline for controlling the memory consumption of Z3. If the memory consumption exceeds the specified number of Megabytes, Z3 exits with a warning message. \section cmdlineout Output \cmdopt{st} display statistics. This option can be used to dump various statistics about the search, such as number of splits, conflict clauses, and quantifier instantiations. \section cmdlinesearch Search heuristics \cmdopt{rs:num} random seed. */ z3-z3-4.4.1/src/shell/smtlib_frontend.cpp000066400000000000000000000067731260446376700202350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smtlib_frontend.cpp Abstract: Frontend for reading Smtlib input files Author: Nikolaj Bjorner (nbjorner) 2006-11-3. Revision History: Leonardo de Moura: new SMT 2.0 front-end, removed support for .smtc files and smtcmd_solver object. --*/ #include #include #include #include"smtlib_solver.h" #include"timeout.h" #include"smt2parser.h" #include"dl_cmds.h" #include"dbg_cmds.h" #include"opt_cmds.h" #include"polynomial_cmds.h" #include"subpaving_cmds.h" #include"smt_strategic_solver.h" #include"smt_solver.h" extern bool g_display_statistics; extern void display_config(); static clock_t g_start_time; static smtlib::solver* g_solver = 0; static cmd_context * g_cmd_context = 0; static void display_statistics() { clock_t end_time = clock(); if ((g_solver || g_cmd_context) && g_display_statistics) { std::cout.flush(); std::cerr.flush(); if (g_solver) { g_solver->display_statistics(); memory::display_max_usage(std::cout); std::cout << "time: " << ((static_cast(end_time) - static_cast(g_start_time)) / CLOCKS_PER_SEC) << " secs\n"; } else if (g_cmd_context) { g_cmd_context->set_regular_stream("stdout"); g_cmd_context->display_statistics(true, ((static_cast(end_time) - static_cast(g_start_time)) / CLOCKS_PER_SEC)); } } } static void on_timeout() { #pragma omp critical (g_display_stats) { display_statistics(); exit(0); } } static void on_ctrl_c(int) { signal (SIGINT, SIG_DFL); #pragma omp critical (g_display_stats) { display_statistics(); } raise(SIGINT); } unsigned read_smtlib_file(char const * benchmark_file) { g_start_time = clock(); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); smtlib::solver solver; g_solver = &solver; bool ok = true; ok = solver.solve_smt(benchmark_file); if (!ok) { if (benchmark_file) { std::cerr << "ERROR: solving '" << benchmark_file << "'.\n"; } else { std::cerr << "ERROR: solving input stream.\n"; } } #pragma omp critical (g_display_stats) { display_statistics(); register_on_timeout_proc(0); g_solver = 0; } return solver.get_error_code(); } unsigned read_smtlib2_commands(char const * file_name) { g_start_time = clock(); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); cmd_context ctx; ctx.set_solver_factory(mk_smt_strategic_solver_factory()); ctx.set_interpolating_solver_factory(mk_smt_solver_factory()); install_dl_cmds(ctx); install_dbg_cmds(ctx); install_polynomial_cmds(ctx); install_subpaving_cmds(ctx); install_opt_cmds(ctx); g_cmd_context = &ctx; signal(SIGINT, on_ctrl_c); bool result = true; if (file_name) { std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } result = parse_smt2_commands(ctx, in); } else { result = parse_smt2_commands(ctx, std::cin, true); } #pragma omp critical (g_display_stats) { display_statistics(); g_cmd_context = 0; } return result ? 0 : 1; } z3-z3-4.4.1/src/shell/smtlib_frontend.h000066400000000000000000000006201260446376700176630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smtlib_frontend.h Abstract: Smtlib frontend. Author: Leonardo de Moura (leonardo) 2006-11-2. Revision History: --*/ #ifndef SMTLIB_FRONTEND_H_ #define SMTLIB_FRONTEND_H_ unsigned read_smtlib_file(char const * benchmark_file); unsigned read_smtlib2_commands(char const * command_file); #endif /* SMTLIB_FRONTEND_H_ */ z3-z3-4.4.1/src/shell/z3_log_frontend.cpp000066400000000000000000000023041260446376700201220ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_log_frontend.cpp Abstract: Z3 log frontend. Replay a log generated by Z3 Author: Leonardo de Moura (leonardo) 2011-09-26. Revision History: --*/ #include #include #include #include"util.h" #include"error_codes.h" #include"z3_replayer.h" static void solve(char const * stream_name, std::istream & in) { clock_t start_time = clock(); z3_replayer r(in); try { r.parse(); } catch (z3_exception & ex) { std::cerr << "Error at line " << r.get_line() << ": " << ex.msg() << std::endl; } clock_t end_time = clock(); memory::display_max_usage(std::cout); std::cout << "time: " << ((static_cast(end_time) - static_cast(start_time)) / CLOCKS_PER_SEC) << "\n"; } void replay_z3_log(char const * file_name) { if (!file_name) { solve(file_name, std::cin); } else { std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "Error: failed to open file \"" << file_name << "\".\n"; exit(ERR_OPEN_FILE); } solve(file_name, in); } exit(0); } z3-z3-4.4.1/src/shell/z3_log_frontend.h000066400000000000000000000005541260446376700175740ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_log_frontend.h Abstract: Z3 log frontend. Replay a log generated by Z3 Author: Leonardo de Moura (leonardo) 2011-09-26. Revision History: --*/ #ifndef Z3_LOG_FRONTEND_H_ #define Z3_LOG_FRONTEND_H_ void replay_z3_log(char const * benchmark_file); #endif /* Z3_FRONTEND_H_ */ z3-z3-4.4.1/src/smt/000077500000000000000000000000001260446376700140175ustar00rootroot00000000000000z3-z3-4.4.1/src/smt/arith_eq_adapter.cpp000066400000000000000000000333641260446376700200300ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: arith_eq_adapter.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-25. Revision History: --*/ #include"smt_context.h" #include"arith_eq_adapter.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"stats.h" #include"simplifier.h" #include"ast_smt2_pp.h" namespace smt { class already_processed_trail : public trail { // Remark: it is safer to use a trail object, because it guarantees that the enodes // are still alive when the undo operation is performed. // // If a local backtracking stack is used in the class arith_eq_adapter is used, // then we cannot guarantee that. arith_eq_adapter::already_processed & m_already_processed; enode * m_n1; enode * m_n2; public: already_processed_trail(arith_eq_adapter::already_processed & m, enode * n1, enode * n2): m_already_processed(m), m_n1(n1), m_n2(n2) { } virtual void undo(context & ctx) { m_already_processed.erase(m_n1, m_n2); TRACE("arith_eq_adapter_profile", tout << "del #" << m_n1->get_owner_id() << " #" << m_n2->get_owner_id() << "\n";); } }; /** \brief The atoms m_eq, m_le, and m_ge should be marked as relevant only after m_n1 and m_n2 are marked as relevant. */ class arith_eq_relevancy_eh : public relevancy_eh { expr * m_n1; expr * m_n2; expr * m_eq; expr * m_le; expr * m_ge; public: arith_eq_relevancy_eh(expr * n1, expr * n2, expr * eq, expr * le, expr * ge): m_n1(n1), m_n2(n2), m_eq(eq), m_le(le), m_ge(ge) { } virtual ~arith_eq_relevancy_eh() {} virtual void operator()(relevancy_propagator & rp) { if (!rp.is_relevant(m_n1)) return; if (!rp.is_relevant(m_n2)) return; rp.mark_as_relevant(m_eq); rp.mark_as_relevant(m_le); rp.mark_as_relevant(m_ge); } }; arith_simplifier_plugin * arith_eq_adapter::get_simplifier() { if (!m_as) { simplifier & s = get_context().get_simplifier(); m_as = static_cast(s.get_plugin(m_owner.get_family_id())); } return m_as; } void arith_eq_adapter::mk_axioms(enode * n1, enode * n2) { SASSERT(n1 != n2); ast_manager & m = get_manager(); TRACE("arith_eq_adapter_mk_axioms", tout << "#" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n"; tout << mk_ismt2_pp(n1->get_owner(), m) << "\n" << mk_ismt2_pp(n2->get_owner(), m) << "\n";); if (n1->get_owner_id() > n2->get_owner_id()) std::swap(n1, n2); app * t1 = n1->get_owner(); app * t2 = n2->get_owner(); if (m.is_value(t1) && m.is_value(t2)) { // Nothing to be done // We don't need to create axioms for 2 = 3 return; } context & ctx = get_context(); CTRACE("arith_eq_adapter_relevancy", !(ctx.is_relevant(n1) && ctx.is_relevant(n2)), tout << "is_relevant(n1): #" << n1->get_owner_id() << " " << ctx.is_relevant(n1) << "\n"; tout << "is_relevant(n2): #" << n2->get_owner_id() << " " << ctx.is_relevant(n2) << "\n"; tout << mk_pp(n1->get_owner(), get_manager()) << "\n"; tout << mk_pp(n2->get_owner(), get_manager()) << "\n"; ctx.display(tout);); // // The atoms d.m_t1_eq_t2, d.m_le, and d.m_ge should only be marked as relevant // after n1 and n2 are marked as relevant. // data d; if (m_already_processed.find(n1, n2, d)) return; TRACE("arith_eq_adapter_profile", tout << "mk #" << n1->get_owner_id() << " #" << n2->get_owner_id() << " " << m_already_processed.size() << " " << ctx.get_scope_level() << "\n";); m_stats.m_num_eq_axioms++; TRACE("arith_eq_adapter_profile_detail", tout << "mk_detail " << mk_bounded_pp(n1->get_owner(), m, 5) << " " << mk_bounded_pp(n2->get_owner(), m, 5) << "\n";); app_ref t1_eq_t2(m); t1_eq_t2 = ctx.mk_eq_atom(t1, t2); SASSERT(!m.is_false(t1_eq_t2)); TRACE("arith_eq_adapter_bug", tout << mk_bounded_pp(t1_eq_t2, m) << "\n" << mk_bounded_pp(t1, m) << "\n" << mk_bounded_pp(t2, m) << "\n";); // UNRESOLVED ISSUE: // // arith_eq_adapter is still creating problems. // The following disabled code fixes the issues, but create performance problems. // The alternative does not works 100%. It generates problems when the literal // created by the adapter during the search is included in a learned clause. // Here is a sequence of events that triggers a crash: // 1) The terms t1 >= t2 and t1 <= t2 are not in simplified form. // For example, let us assume t1 := (* -1 x) and t2 := x. // Since, t1 and t2 were internalized at this point, the following code works. // That is the arith internalizer accepts the formula (+ (* -1 x) (* -1 x)) // that is not in simplified form. Let s be the term (+ (* -1 x) (* -1 x)) // 2) Assume now that a conflict is detected a lemma containing s is created. // 3) The enodes associated with t1, t2 and s are destroyed during backtracking. // 4) The term s is reinternalized at smt::context::reinit_clauses. Term t2 is // also reinitialized, but t1 is not. We only create a "name" for a term (* -1 x) // if it is embedded in a function application. // 5) theory_arith fails to internalize (+ (* -1 x) (* -1 x)), and Z3 crashes. // #if 0 // This block of code uses the simplifier for creating the literals t1 >= t2 and t1 <= t2. // It has serious performance problems in VCC benchmarks. // The problem seems to be the following: // t1 and t2 are slacks (i.e., names for linear polynomials). // The simplifier will create inequalities that will indirectly imply that t1 >= t2 and t1 <= t2. // Example if: t1 := 1 + a // t2 := 2 + b // the simplifier will create // a - b >= -1 // a - b <= -1 // These inequalities imply that 1+a >= 2+b and 1+a <= 2+b, // but the tableau is complete different. // BTW, note that we don't really need to handle the is_numeral case when using // the simplifier. However, doing that, it seems we minimize the performance problem. expr_ref le(m); expr_ref ge(m); if (m_util.is_numeral(t1)) std::swap(t1, t2); if (m_util.is_numeral(t2)) { le = m_util.mk_le(t1, t2); ge = m_util.mk_ge(t1, t2); } else { arith_simplifier_plugin & s = *(get_simplifier()); s.mk_le(t1, t2, le); s.mk_ge(t1, t2, ge); } TRACE("arith_eq_adapter_perf", tout << mk_ismt2_pp(t1_eq_t2, m) << "\n" << mk_ismt2_pp(le, m) << "\n" << mk_ismt2_pp(ge, m) << "\n";); #else // Old version that used to be buggy. // I fixed the theory arithmetic internalizer to accept non simplified terms of the form t1 - t2 // if t1 and t2 already have slacks (theory variables) associated with them. app * le = 0; app * ge = 0; if (m_util.is_numeral(t1)) std::swap(t1, t2); if (m_util.is_numeral(t2)) { le = m_util.mk_le(t1, t2); ge = m_util.mk_ge(t1, t2); } else { sort * st = m.get_sort(t1); app * minus_one = m_util.mk_numeral(rational::minus_one(), st); app * zero = m_util.mk_numeral(rational::zero(), st); app_ref s(m_util.mk_add(t1, m_util.mk_mul(minus_one, t2)), m); le = m_util.mk_le(s, zero); ge = m_util.mk_ge(s, zero); } TRACE("arith_eq_adapter_perf", tout << mk_ismt2_pp(t1_eq_t2, m) << "\n" << mk_ismt2_pp(le, m) << "\n" << mk_ismt2_pp(ge, m) << "\n";); #endif ctx.push_trail(already_processed_trail(m_already_processed, n1, n2)); m_already_processed.insert(n1, n2, data(t1_eq_t2, le, ge)); TRACE("arith_eq_adapter_profile", tout << "insert #" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";); ctx.internalize(t1_eq_t2, true); literal t1_eq_t2_lit(ctx.get_bool_var(t1_eq_t2)); TRACE("interface_eq", tout << "core should try true phase first for the equality: " << t1_eq_t2_lit << "\n"; tout << "#" << n1->get_owner_id() << " == #" << n2->get_owner_id() << "\n"; tout << "try_true_first: " << ctx.try_true_first(t1_eq_t2_lit.var()) << "\n";); TRACE("arith_eq_adapter_bug", tout << "le: " << mk_ismt2_pp(le, m) << "\nge: " << mk_ismt2_pp(ge, m) << "\n";); ctx.internalize(le, true); ctx.internalize(ge, true); SASSERT(ctx.lit_internalized(le)); SASSERT(ctx.lit_internalized(ge)); literal le_lit = ctx.get_literal(le); literal ge_lit = ctx.get_literal(ge); if (ctx.try_true_first(t1_eq_t2_lit.var())) { // Remark: I need to propagate the try_true_first flag to the auxiliary atom le_lit and ge_lit. // Otherwise model based theory combination will be ineffective, because if the core // case splits in le_lit and ge_lit before t1_eq_t2_lit it will essentially assign an arbitrary phase to t1_eq_t2_lit. ctx.set_true_first_flag(le_lit.var()); ctx.set_true_first_flag(ge_lit.var()); } theory_id tid = m_owner.get_id(); if (m.proofs_enabled() && m_proof_hint.empty()) { m_proof_hint.push_back(parameter(symbol("triangle-eq"))); } ctx.mk_th_axiom(tid, ~t1_eq_t2_lit, le_lit, m_proof_hint.size(), m_proof_hint.c_ptr()); ctx.mk_th_axiom(tid, ~t1_eq_t2_lit, ge_lit, m_proof_hint.size(), m_proof_hint.c_ptr()); ctx.mk_th_axiom(tid, t1_eq_t2_lit, ~le_lit, ~ge_lit, m_proof_hint.size(), m_proof_hint.c_ptr()); TRACE("arith_eq_adapter", tout << "internalizing: " << " " << mk_pp(le, m) << ": " << le_lit << " " << mk_pp(ge, m) << ": " << ge_lit << " " << mk_pp(t1_eq_t2, m) << ": " << t1_eq_t2_lit << "\n";); if (m_params.m_arith_add_binary_bounds) { TRACE("arith_eq_adapter", tout << "adding binary bounds...\n";); ctx.mk_th_axiom(tid, le_lit, ge_lit, 3, m_proof_hint.c_ptr()); } if (ctx.relevancy()) { relevancy_eh * eh = ctx.mk_relevancy_eh(arith_eq_relevancy_eh(n1->get_owner(), n2->get_owner(), t1_eq_t2, le, ge)); ctx.add_relevancy_eh(n1->get_owner(), eh); ctx.add_relevancy_eh(n2->get_owner(), eh); } if (!m_params.m_arith_lazy_adapter && !ctx.at_base_level() && n1->get_iscope_lvl() <= ctx.get_base_level() && n2->get_iscope_lvl() <= ctx.get_base_level()) { m_restart_pairs.push_back(enode_pair(n1, n2)); } TRACE("arith_eq_adapter_detail", ctx.display(tout);); } void arith_eq_adapter::new_eq_eh(theory_var v1, theory_var v2) { TRACE("arith_eq_adapter", tout << "v" << v1 << " = v" << v2 << " #" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";); TRACE("arith_eq_adapter_bug", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); mk_axioms(get_enode(v1), get_enode(v2)); } void arith_eq_adapter::new_diseq_eh(theory_var v1, theory_var v2) { TRACE("arith_eq_adapter", tout << "v" << v1 << " != v" << v2 << " #" << get_enode(v1)->get_owner_id() << " != #" << get_enode(v2)->get_owner_id() << "\n";); mk_axioms(get_enode(v1), get_enode(v2)); } void arith_eq_adapter::init_search_eh() { m_restart_pairs.reset(); } void arith_eq_adapter::reset_eh() { TRACE("arith_eq_adapter", tout << "reset\n";); m_already_processed .reset(); m_restart_pairs .reset(); m_stats .reset(); } void arith_eq_adapter::restart_eh() { context & ctx = get_context(); TRACE("arith_eq_adapter", tout << "restart\n";); svector tmp(m_restart_pairs); svector::iterator it = tmp.begin(); svector::iterator end = tmp.end(); m_restart_pairs.reset(); for (; it != end && !ctx.inconsistent(); ++it) { TRACE("arith_eq_adapter", tout << "creating arith_eq_adapter axioms at the base level #" << it->first->get_owner_id() << " #" << it->second->get_owner_id() << "\n";); mk_axioms(it->first, it->second); } } void arith_eq_adapter::collect_statistics(::statistics & st) const { st.update("eq adapter", m_stats.m_num_eq_axioms); } void arith_eq_adapter::display_already_processed(std::ostream & out) const { obj_pair_map::iterator it = m_already_processed.begin(); obj_pair_map::iterator end = m_already_processed.end(); for (; it != end; ++it) { enode * n1 = it->get_key1(); enode * n2 = it->get_key2(); out << "eq_adapter: #" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n"; } } }; z3-z3-4.4.1/src/smt/arith_eq_adapter.h000066400000000000000000000052651260446376700174740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: arith_eq_adapter.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-25. Revision History: --*/ #ifndef ARITH_EQ_ADAPTER_H_ #define ARITH_EQ_ADAPTER_H_ #include"smt_theory.h" #include"obj_pair_hashtable.h" #include"arith_decl_plugin.h" #include"statistics.h" #include"arith_simplifier_plugin.h" namespace smt { struct arith_eq_adapter_stats { unsigned m_num_eq_axioms; void reset() { m_num_eq_axioms = 0; } arith_eq_adapter_stats() { reset(); } }; /** \brief Auxiliary class used to convert (dis) equalities propagated from the core into arith equalities/inequalities atoms. This class is used by the arithmetic theories to handle the (dis) equalities propagated from the logical context. - config 1: recreate axioms at restart - config 2: lazy diseq split */ class arith_eq_adapter { public: arith_eq_adapter_stats m_stats; private: struct data { expr * m_t1_eq_t2; expr * m_le; expr * m_ge; data():m_t1_eq_t2(0), m_le(0), m_ge(0) {} data(expr * t1_eq_t2, expr * le, expr * ge):m_t1_eq_t2(t1_eq_t2), m_le(le), m_ge(ge) {} }; public: typedef obj_pair_map already_processed; private: theory & m_owner; theory_arith_params & m_params; arith_util & m_util; arith_simplifier_plugin * m_as; already_processed m_already_processed; svector m_restart_pairs; svector m_proof_hint; context & get_context() const { return m_owner.get_context(); } ast_manager & get_manager() const { return m_owner.get_manager(); } enode * get_enode(theory_var v) const { return m_owner.get_enode(v); } arith_simplifier_plugin * get_simplifier(); public: arith_eq_adapter(theory & owner, theory_arith_params & params, arith_util & u):m_owner(owner), m_params(params), m_util(u), m_as(0) {} void new_eq_eh(theory_var v1, theory_var v2); void new_diseq_eh(theory_var v1, theory_var v2); void reset_eh(); void init_search_eh(); void restart_eh(); /** \brief Add the eq axioms for n1 and n2. */ void mk_axioms(enode * n1, enode * n2); void collect_statistics(::statistics & st) const; void display_already_processed(std::ostream & out) const; }; }; #endif /* ARITH_EQ_ADAPTER_H_ */ z3-z3-4.4.1/src/smt/arith_eq_solver.cpp000066400000000000000000000426631260446376700177240ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: arith_eq_solver.cpp Abstract: Solver for linear arithmetic equalities. Author: Nikolaj Bjorner (nbjorner) 2012-02-25 --*/ #include"arith_eq_solver.h" arith_eq_solver::~arith_eq_solver() { } arith_eq_solver::arith_eq_solver(ast_manager & m, params_ref const& p): m(m), m_params(p), m_util(m), m_arith_rewriter(m) { m_params.set_bool("gcd_rounding", true); // m_params.set_bool("sum", true); m_arith_rewriter.updt_params(m_params); } /** \brief Return true if the first monomial of t is negative. */ bool arith_eq_solver::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_eq_solver::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 (m_util.is_add(e) || m_util.is_mul(e)) { expr_ref_vector args(m); expr_ref tmp(m); 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); } m_arith_rewriter.mk_app(a->get_decl(), args.size(), args.c_ptr(), result); } else if (m_util.is_numeral(e, n, is_int) && is_int) { result = m_util.mk_numeral(mod(n, k), true); } else { result = e; } } void arith_eq_solver::gcd_normalize(vector& values) { numeral g(0); for (unsigned i = 0; !g.is_one() && i < values.size(); ++i) { SASSERT(values[i].is_int()); if (!values[i].is_zero()) { if (g.is_zero()) { g = abs(values[i]); } else { g = gcd(abs(values[i]), g); } } } if (g.is_zero() || g.is_one()) { return; } for (unsigned i = 0; i < values.size(); ++i) { values[i] = values[i] / g; SASSERT(values[i].is_int()); } } unsigned arith_eq_solver::find_abs_min(vector& values) { SASSERT(values.size() >= 2); unsigned index = 0; numeral v(0); for (unsigned i = 1; i < values.size(); ++i) { numeral w = abs(values[i]); if (v.is_zero() || (!w.is_zero() && w < v)) { index = i; v = w; } } return index; } static void print_row(std::ostream& out, vector const& row) { for(unsigned i = 0; i < row.size(); ++i) { out << row[i] << " "; } out << "\n"; } static void print_rows(std::ostream& out, vector > const& rows) { for (unsigned i = 0; i < rows.size(); ++i) { print_row(out, rows[i]); } } // // The gcd of the coefficients to variables have to divide the // coefficient to the constant. // The constant is the last value in the array. // bool arith_eq_solver::gcd_test(vector& values) { SASSERT(values.size() > 0); numeral g(0); numeral first_value = values[0]; for (unsigned i = 1; !g.is_one() && i < values.size(); ++i) { if (!values[i].is_zero()) { if (g.is_zero()) { g = abs(values[i]); } else { g = gcd(abs(values[i]), g); } } } if (g.is_one()) { return true; } if (g.is_zero()) { return first_value.is_zero(); } numeral r = first_value/g; return r.is_int(); } bool arith_eq_solver::solve_integer_equation( vector& values, unsigned& index, bool& is_fresh ) { TRACE("arith_eq_solver", tout << "solving: "; print_row(tout, values); ); // // perform one step of the omega test equality elimination. // // Given: // a1*x1 + a2*x2 + .. + a_n*x_n + a_{n+1} = 0 // // Assume gcd(a1,..,a_n,a_{n+1}) = 1 // Assume gcd(a1,...,a_n) divides a_{n+1} (eg. gcd(a1,..,an) = 1) // // post-condition: values[index] = -1. // // Let a_index be index of least absolute value. // // If |a_index| = 1, then return row and index. // Otherwise: // Let m = |a_index| + 1 // Set // // m*x_index' // = // ((a1 mod_hat m)*x1 + (a2 mod_hat m)*x2 + .. + (a_n mod_hat m)*x_n + (k mod_hat m)) // = // (a1'*x1 + a2'*x2 + .. (-)1*x_index + ...) // // <=> Normalize signs so that sign to x_index is -1. // (-)a1'*x1 + (-)a2'*x2 + .. -1*x_index + ... + m*x_index' = 0 // // Return row, where the coefficient to x_index is implicit. // Instead used the coefficient 'm' at position 'index'. // gcd_normalize(values); if (!gcd_test(values)) { TRACE("arith_eq_solver", tout << "not sat\n"; print_row(tout, values);); return false; } index = find_abs_min(values); SASSERT(1 <= index && index < values.size()); numeral a = values[index]; numeral abs_a = abs(a); if (abs_a.is_zero()) { // The equation is trivial. return true; } if (a.is_one()) { for (unsigned i = 0; i < values.size(); ++i) { values[i].neg(); } } is_fresh = !abs_a.is_one(); if (is_fresh) { numeral m = abs_a + numeral(1); for (unsigned i = 0; i < values.size(); ++i) { values[i] = mod_hat(values[i], m); } if (values[index].is_one()) { for (unsigned i = 0; i < values.size(); ++i) { values[i].neg(); } } SASSERT(values[index].is_minus_one()); values[index] = m; } TRACE("arith_eq_solver", tout << "solved at index " << index << ": "; print_row(tout, values); ); return true; } void arith_eq_solver::substitute( row& r, row const& s, unsigned index ) { SASSERT(1 <= index && index < s.size()); TRACE("arith_eq_solver", tout << "substitute " << index << ":\n"; print_row(tout, r); print_row(tout, s); ); if (index >= r.size()) { return; } numeral c = r[index]; if (c.is_zero()) { // no-op } else if (abs(s[index]).is_one()) { // // s encodes an equation that contains a variable // with a unit coefficient. // // Let // c = r[index] // s = s[index]*x + s'*y = 0 // r = c*x + r'*y = 0 // // => // // 0 // = // -sign(s[index])*c*s + r // = // -s[index]*sign(s[index])*c*x - sign(s[index])*c*s'*y + c*x + r'*y // = // -c*x - sign(s[index])*c*s'*y + c*x + r'*y // = // -sign(s[index])*c*s'*y + r'*y // numeral sign_s = s[index].is_pos()?numeral(1):numeral(-1); for (unsigned i = 0; i < r.size(); ++i) { r[i] -= c*sign_s*s[i]; } for (unsigned i = r.size(); i < s.size(); ++i) { r.push_back(-c*sign_s*s[i]); } } else { // // s encodes a substitution using an auxiliary variable. // the auxiliary variable is at position 'index'. // // Let // c = r[index] // s = s[index]*x + s'*y = 0 // r = c*x + r'*y = 0 // // s encodes : x |-> s[index]*x' + s'*y // // Set: // // r := c*s + r'*y // r[index] = numeral(0); for (unsigned i = 0; i < r.size(); ++i) { r[i] += c*s[i]; } for (unsigned i = r.size(); i < s.size(); ++i) { r.push_back(c*s[i]); } } TRACE("arith_eq_solver", tout << "result: "; print_row(tout, r); ); } bool arith_eq_solver::solve_integer_equations( vector& rows, row& unsat_row ) { // return solve_integer_equations_units(rows, unsat_row); return solve_integer_equations_gcd(rows, unsat_row); } // // Naive integer equation solver where only units are eliminated. // bool arith_eq_solver::solve_integer_equations_units( vector& rows, row& unsat_row ) { TRACE("arith_eq_solver", print_rows(tout << "solving:\n", rows);); unsigned_vector todo, done; for (unsigned i = 0; i < rows.size(); ++i) { todo.push_back(i); row& r = rows[i]; gcd_normalize(r); if (!gcd_test(r)) { unsat_row = r; TRACE("arith_eq_solver", print_row(tout << "input is unsat: ", unsat_row); ); return false; } } for (unsigned i = 0; i < todo.size(); ++i) { row& r = rows[todo[i]]; gcd_normalize(r); if (!gcd_test(r)) { unsat_row = r; TRACE("arith_eq_solver", print_row(tout << "unsat: ", unsat_row); ); return false; } unsigned index = find_abs_min(r); SASSERT(1 <= index && index < r.size()); numeral a = r[index]; numeral abs_a = abs(a); if (abs_a.is_zero()) { continue; } else if (abs_a.is_one()) { for (unsigned j = i+1; j < todo.size(); ++j) { substitute(rows[todo[j]], r, index); } for (unsigned j = 0; j < done.size(); ++j) { row& r2 = rows[done[j]]; if (!r2[index].is_zero()) { substitute(r2, r, index); todo.push_back(done[j]); done.erase(done.begin()+j); --j; } } } else { done.push_back(todo[i]); } } TRACE("arith_eq_solver", tout << ((done.size()<=1)?"solved ":"incomplete check ") << done.size() << "\n"; for (unsigned i = 0; i < done.size(); ++i) { print_row(tout, rows[done[i]]); } ); return true; } // // Partial solver based on the omega test equalities. // unsatisfiability is not preserved when eliminating // auxiliary variables. // bool arith_eq_solver::solve_integer_equations_omega( vector & rows, row& unsat_row ) { unsigned index; bool is_fresh; vector rows_solved; unsigned_vector indices; unsigned_vector aux_indices; for (unsigned i = 0; i < rows.size(); ++i) { rows_solved.push_back(rows[i]); row& r = rows_solved.back(); for (unsigned j = 0; j + 1 < rows_solved.size(); ++j) { substitute(r, rows_solved[j], indices[j]); } if (!solve_integer_equation(r, index, is_fresh)) { unsat_row = r; gcd_normalize(unsat_row); // invert the substitution for every index that is fresh. TRACE("arith_eq_solver", tout << "unsat:\n"; print_row(tout, unsat_row); for (unsigned l = 0; l + 1< rows_solved.size(); ++l) { print_row(tout, rows_solved[l]); }); for (unsigned j = rows_solved.size()-1; j > 0; ) { --j; row& solved_row = rows_solved[j]; unsigned index_j = indices[j]; unsigned aux_index_j = aux_indices[j]; SASSERT(index_j <= aux_index_j); if (unsat_row.size() <= aux_index_j) { unsat_row.resize(aux_index_j+1); } numeral m = solved_row[aux_index_j]; numeral k = unsat_row[aux_index_j]; if (aux_index_j != index_j && !k.is_zero()) { // // solved_row: -x_index + m*sigma + r1 = 0 // unsat_row: k*sigma + r2 = 0 // // <=> // // solved_row: -k*x_index + k*m*sigma + k*r1 = 0 // unsat_row: m*k*sigma + m*r2 = 0 // // => // // m*k*sigma + m*r2 + k*x_index - k*m*sigma - k*r1 = 0 // for (unsigned l = 0; l < unsat_row.size(); ++l) { unsat_row[l] *= m; unsat_row[l] -= k*solved_row[l]; } for (unsigned l = unsat_row.size(); l < solved_row.size(); ++l) { unsat_row.push_back(solved_row[l]); } gcd_normalize(unsat_row); TRACE("arith_eq_solver", tout << "gcd: "; print_row(tout, solved_row); print_row(tout, unsat_row); ); } if (gcd_test(unsat_row)) { TRACE("arith_eq_solver", tout << "missed pure explanation\n";); return true; } SASSERT(!gcd_test(unsat_row)); } return false; } else if (r[index].is_zero()) { // Row is trival rows_solved.pop_back(); continue; } else if (!abs(r[index]).is_one()) { // // The solution introduces a fresh auxiliary variable. // Make space for this variable as a fresh numeral. // indices.push_back(index); aux_indices.push_back(r.size()); r.push_back(r[index]); r[index] = numeral(-1); // re-solve the same row. --i; } else { indices.push_back(index); aux_indices.push_back(index); } } return true; } // // Eliminate variables by searching for combination of rows where // the coefficients have gcd = 1. // bool arith_eq_solver::solve_integer_equations_gcd( vector & rows, row& unsat_row ) { unsigned_vector live, useful, gcd_pos; vector gcds; rational u, v; if (rows.empty()) { return true; } for (unsigned i = 0; i < rows.size(); ++i) { live.push_back(i); row& r = rows[i]; gcd_normalize(r); if (!gcd_test(r)) { unsat_row = r; TRACE("arith_eq_solver", print_row(tout << "input is unsat: ", unsat_row); ); return false; } } unsigned max_column = rows[0].size(); bool change = true; while (change && !live.empty()) { change = false; for (unsigned i = 1; i < max_column; ++i) { rational g(0); gcds.reset(); gcd_pos.reset(); unsigned j = 0; for (; j < live.size(); ++j) { rational const& k = rows[live[j]][i]; if (k.is_zero()) { continue; } if (g.is_zero()) { g = abs(k); } else { g = gcd(g, abs(k)); } if (abs(g).is_one()) { break; } gcds.push_back(g); gcd_pos.push_back(live[j]); } if (j == live.size()) { continue; } change = true; // found gcd, now identify reduced set of rows with GCD = 1. g = abs(rows[live[j]][i]); useful.push_back(live[j]); unsigned live_pos = j; for (j = gcds.size(); !g.is_one() && j > 0; ) { SASSERT(g.is_pos()); --j; if (j == 0 || !gcd(g, gcds[j-1]).is_one()) { useful.push_back(gcd_pos[j]); g = gcd(g, gcds[j]); SASSERT(j == 0 || gcd(g,gcds[j-1]).is_one()); } } // // we now have a set "useful" of rows whose combined GCD = 1. // pivot the remaining with the first row. // row& r0 = rows[useful[0]]; for (j = 1; j < useful.size(); ++j) { row& r1 = rows[useful[j]]; g = gcd(r0[i], r1[i], u, v); for (unsigned k = 0; k < max_column; ++k) { r0[k] = u*r0[k] + v*r1[k]; } SASSERT(g == r0[i]); } if (!abs(r0[i]).is_one()) { return false; } live.erase(live.begin()+live_pos); for (j = 0; j < live.size(); ++j) { row& r = rows[live[j]]; if (!r[i].is_zero()) { substitute(r, r0, i); gcd_normalize(r); if (!gcd_test(r)) { unsat_row = r; TRACE("arith_eq_solver", print_row(tout << "unsat: ", unsat_row); ); return false; } } } } } TRACE("arith_eq_solver", tout << ((live.size()<=1)?"solved ":"incomplete check ") << live.size() << "\n"; for (unsigned i = 0; i < live.size(); ++i) { print_row(tout, rows[live[i]]); } ); return true; } z3-z3-4.4.1/src/smt/arith_eq_solver.h000066400000000000000000000051111260446376700173540ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: arith_eq_solver.h Abstract: Solver for linear arithmetic equalities. Author: Nikolaj Bjorner (nbjorner) 2012-02-25 --*/ #ifndef ARITH_EQ_SOLVER_H_ #define ARITH_EQ_SOLVER_H_ #include"arith_decl_plugin.h" #include"arith_rewriter.h" /** \brief Simplifier for the arith family. */ class arith_eq_solver { typedef rational numeral; ast_manager& m; params_ref m_params; arith_util m_util; arith_rewriter m_arith_rewriter; bool is_neg_poly(expr * t) const; void prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result); bool gcd_test(vector& value); unsigned find_abs_min(vector& values); void gcd_normalize(vector& values); void substitute(vector& r, vector const& s, unsigned index); bool solve_integer_equations_units( vector > & rows, vector& unsat_row ); bool solve_integer_equations_omega( vector > & rows, vector& unsat_row ); void compute_hnf(vector >& A); bool solve_integer_equations_hermite( vector > & rows, vector& unsat_row ); bool solve_integer_equations_gcd( vector > & rows, vector& unsat_row ); public: arith_eq_solver(ast_manager & m, params_ref const& p = params_ref()); ~arith_eq_solver(); // Integer linear solver for a single equation. // The array values contains integer coefficients // // Determine integer solutions to: // // a+k = 0 // // where a = sum_i a_i*k_i // typedef vector row; typedef vector matrix; bool solve_integer_equation( row& values, unsigned& index, bool& is_fresh ); // Integer linear solver. // Determine integer solutions to: // // a+k = 0 // // where a = sum_i a_i*k_i // // Solution, if there is any, is returned as a substitution. // The return value is "true". // If there is no solution, then return "false". // together with equality "eq_unsat", such that // // eq_unsat = 0 // // is implied and is unsatisfiable over the integers. // bool solve_integer_equations(vector& rows, row& unsat_row); }; #endif /* ARITH_EQ_SOLVER_H_ */ z3-z3-4.4.1/src/smt/asserted_formulas.cpp000066400000000000000000001162331260446376700202530ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: asserted_formulas.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-11. Revision History: --*/ #include"asserted_formulas.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"arith_simplifier_plugin.h" #include"array_simplifier_plugin.h" #include"datatype_simplifier_plugin.h" #include"fpa_simplifier_plugin.h" #include"bv_simplifier_plugin.h" #include"for_each_expr.h" #include"well_sorted.h" #include"pull_quant.h" #include"pull_ite_tree.h" #include"push_app_ite.h" #include"elim_term_ite.h" #include"pattern_inference.h" #include"nnf.h" #include"bv_elim.h" #include"inj_axiom.h" #include"der.h" #include"elim_bounds.h" #include"warning.h" #include"bit2int.h" #include"distribute_forall.h" #include"quasi_macros.h" asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p): m_manager(m), m_params(p), m_pre_simplifier(m), m_simplifier(m), m_defined_names(m), m_static_features(m), m_asserted_formulas(m), m_asserted_formula_prs(m), m_asserted_qhead(0), m_macro_manager(m, m_simplifier), m_bit2int(m), m_bv_sharing(m), m_inconsistent(false), m_cancel_flag(false) { m_bsimp = 0; m_bvsimp = 0; arith_simplifier_plugin * arith_simp = 0; setup_simplifier_plugins(m_simplifier, m_bsimp, arith_simp, m_bvsimp); SASSERT(m_bsimp != 0); SASSERT(arith_simp != 0); m_macro_finder = alloc(macro_finder, m_manager, m_macro_manager); basic_simplifier_plugin * basic_simp = 0; bv_simplifier_plugin * bv_simp = 0; setup_simplifier_plugins(m_pre_simplifier, basic_simp, arith_simp, bv_simp); m_bit2int.set_bv_simplifier(bv_simp); m_pre_simplifier.enable_presimp(); } void asserted_formulas::setup() { switch (m_params.m_lift_ite) { case LI_FULL: m_params.m_ng_lift_ite = LI_NONE; break; case LI_CONSERVATIVE: if (m_params.m_ng_lift_ite == LI_CONSERVATIVE) m_params.m_ng_lift_ite = LI_NONE; break; default: break; } if (m_params.m_relevancy_lvl == 0) m_params.m_relevancy_lemma = false; } void asserted_formulas::setup_simplifier_plugins(simplifier & s, basic_simplifier_plugin * & bsimp, arith_simplifier_plugin * & asimp, bv_simplifier_plugin * & bvsimp) { bsimp = alloc(basic_simplifier_plugin, m_manager); s.register_plugin(bsimp); asimp = alloc(arith_simplifier_plugin, m_manager, *bsimp, m_params); s.register_plugin(asimp); s.register_plugin(alloc(array_simplifier_plugin, m_manager, *bsimp, s, m_params)); bvsimp = alloc(bv_simplifier_plugin, m_manager, *bsimp, m_params); s.register_plugin(bvsimp); s.register_plugin(alloc(datatype_simplifier_plugin, m_manager, *bsimp)); s.register_plugin(alloc(fpa_simplifier_plugin, m_manager, *bsimp)); } void asserted_formulas::init(unsigned num_formulas, expr * const * formulas, proof * const * prs) { SASSERT(m_asserted_formulas.empty()); SASSERT(m_asserted_formula_prs.empty()); SASSERT(!m_inconsistent); SASSERT(m_scopes.empty()); m_asserted_formulas.append(num_formulas, formulas); if (m_manager.proofs_enabled()) m_asserted_formula_prs.append(num_formulas, prs); } bool asserted_formulas::has_bv() const { // approaximated answer... assume the formula has bit-vectors if the bv_simplifier_plugin was invoked at least once. return m_bvsimp->reduce_invoked(); } asserted_formulas::~asserted_formulas() { } void asserted_formulas::push_assertion(expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) { if (inconsistent()) { SASSERT(!result.empty()); return; } if (m_manager.is_false(e)) m_inconsistent = true; ::push_assertion(m_manager, e, pr, result, result_prs); } void asserted_formulas::set_eliminate_and(bool flag) { if (m_bsimp->eliminate_and() == flag) return; TRACE("eliminate_and", tout << "flushing cache...\n";); flush_cache(); m_bsimp->set_eliminate_and(flag); } void asserted_formulas::assert_expr(expr * e, proof * _in_pr) { if (inconsistent()) return; if (!m_params.m_preprocess) { push_assertion(e, _in_pr, m_asserted_formulas, m_asserted_formula_prs); return; } proof_ref in_pr(_in_pr, m_manager); expr_ref r1(m_manager); proof_ref pr1(m_manager); expr_ref r2(m_manager); proof_ref pr2(m_manager); TRACE("assert_expr_before_simp", tout << mk_ll_pp(e, m_manager) << "\n";); TRACE("assert_expr_bug", tout << mk_pp(e, m_manager) << "\n";); if (m_params.m_pre_simplifier) { m_pre_simplifier(e, r1, pr1); } else { r1 = e; pr1 = 0; } set_eliminate_and(false); // do not eliminate and before nnf. m_simplifier(r1, r2, pr2); TRACE("assert_expr_bug", tout << "after...\n" << mk_pp(r1, m_manager) << "\n";); if (m_manager.proofs_enabled()) { if (e == r2) pr2 = in_pr; else pr2 = m_manager.mk_modus_ponens(in_pr, m_manager.mk_transitivity(pr1, pr2)); } TRACE("assert_expr_after_simp", tout << mk_ll_pp(r1, m_manager) << "\n";); push_assertion(r2, pr2, m_asserted_formulas, m_asserted_formula_prs); TRACE("asserted_formulas_bug", tout << "after assert_expr\n"; display(tout);); } void asserted_formulas::assert_expr(expr * e) { if (inconsistent()) return; assert_expr(e, m_manager.mk_asserted(e)); } void asserted_formulas::get_assertions(ptr_vector & result) { result.append(m_asserted_formulas.size(), m_asserted_formulas.c_ptr()); } void asserted_formulas::push_scope() { SASSERT(inconsistent() || m_asserted_qhead == m_asserted_formulas.size()); TRACE("asserted_formulas_scopes", tout << "push:\n"; display(tout);); m_scopes.push_back(scope()); m_macro_manager.push_scope(); scope & s = m_scopes.back(); s.m_asserted_formulas_lim = m_asserted_formulas.size(); SASSERT(inconsistent() || s.m_asserted_formulas_lim == m_asserted_qhead); s.m_inconsistent_old = m_inconsistent; m_defined_names.push(); m_bv_sharing.push_scope(); commit(); } void asserted_formulas::pop_scope(unsigned num_scopes) { TRACE("asserted_formulas_scopes", tout << "before pop " << num_scopes << "\n"; display(tout);); m_bv_sharing.pop_scope(num_scopes); m_macro_manager.pop_scope(num_scopes); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; m_inconsistent = s.m_inconsistent_old; m_defined_names.pop(num_scopes); m_asserted_formulas.shrink(s.m_asserted_formulas_lim); if (m_manager.proofs_enabled()) m_asserted_formula_prs.shrink(s.m_asserted_formulas_lim); m_asserted_qhead = s.m_asserted_formulas_lim; m_scopes.shrink(new_lvl); flush_cache(); TRACE("asserted_formulas_scopes", tout << "after pop " << num_scopes << "\n"; display(tout);); } void asserted_formulas::reset() { m_defined_names.reset(); m_asserted_qhead = 0; m_asserted_formulas.reset(); m_asserted_formula_prs.reset(); m_macro_manager.reset(); m_bv_sharing.reset(); m_inconsistent = false; } void asserted_formulas::set_cancel_flag(bool f) { m_cancel_flag = f; } #ifdef Z3DEBUG bool asserted_formulas::check_well_sorted() const { for (unsigned i = 0; i < m_asserted_formulas.size(); i++) { if (!is_well_sorted(m_manager, m_asserted_formulas.get(i))) return false; } return true; } #endif void asserted_formulas::reduce() { if (inconsistent()) return; if (canceled()) { return; } if (m_asserted_qhead == m_asserted_formulas.size()) return; if (!m_params.m_preprocess) return; if (m_macro_manager.has_macros()) expand_macros(); TRACE("before_reduce", display(tout);); CASSERT("well_sorted", check_well_sorted()); #define INVOKE(COND, FUNC) if (COND) { FUNC; IF_VERBOSE(10000, verbose_stream() << "total size: " << get_total_size() << "\n";); } TRACE("reduce_step_ll", ast_mark visited; display_ll(tout, visited);); TRACE("reduce_step", display(tout << #FUNC << " ");); CASSERT("well_sorted",check_well_sorted()); if (inconsistent() || canceled()) { TRACE("after_reduce", display(tout);); TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); return; } set_eliminate_and(false); // do not eliminate and before nnf. INVOKE(m_params.m_propagate_booleans, propagate_booleans()); INVOKE(m_params.m_propagate_values, propagate_values()); INVOKE(m_params.m_macro_finder && has_quantifiers(), find_macros()); INVOKE(m_params.m_nnf_cnf, nnf_cnf()); INVOKE(m_params.m_eliminate_and, eliminate_and()); INVOKE(m_params.m_pull_cheap_ite_trees, pull_cheap_ite_trees()); INVOKE(m_params.m_pull_nested_quantifiers && has_quantifiers(), pull_nested_quantifiers()); INVOKE(m_params.m_ng_lift_ite != LI_NONE, ng_lift_ite()); INVOKE(m_params.m_lift_ite != LI_NONE, lift_ite()); INVOKE(m_params.m_eliminate_term_ite && m_params.m_lift_ite != LI_FULL, eliminate_term_ite()); INVOKE(m_params.m_refine_inj_axiom && has_quantifiers(), refine_inj_axiom()); INVOKE(m_params.m_distribute_forall && has_quantifiers(), apply_distribute_forall()); TRACE("qbv_bug", tout << "after distribute_forall:\n"; display(tout);); INVOKE(m_params.m_macro_finder && has_quantifiers(), find_macros()); INVOKE(m_params.m_quasi_macros && has_quantifiers(), apply_quasi_macros()); INVOKE(m_params.m_simplify_bit2int, apply_bit2int()); INVOKE(m_params.m_eliminate_bounds && has_quantifiers(), cheap_quant_fourier_motzkin()); INVOKE(m_params.m_ematching && has_quantifiers(), infer_patterns()); INVOKE(m_params.m_max_bv_sharing && has_bv(), max_bv_sharing()); INVOKE(m_params.m_bb_quantifiers, elim_bvs_from_quantifiers()); // temporary HACK: make sure that arith & bv are list-assoc // this may destroy some simplification steps such as max_bv_sharing reduce_asserted_formulas(); CASSERT("well_sorted",check_well_sorted()); IF_VERBOSE(10, verbose_stream() << "(smt.simplifier-done)\n";); TRACE("after_reduce", display(tout);); TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); TRACE("macros", m_macro_manager.display(tout);); flush_cache(); } void asserted_formulas::eliminate_and() { IF_IVERBOSE(10, verbose_stream() << "(smt.eliminating-and)\n";); set_eliminate_and(true); reduce_asserted_formulas(); TRACE("after_elim_and", display(tout);); } unsigned asserted_formulas::get_formulas_last_level() const { if (m_scopes.empty()) { return 0; } else { return m_scopes.back().m_asserted_formulas_lim; } } void asserted_formulas::collect_static_features() { if (m_params.m_display_features) { unsigned sz = m_asserted_formulas.size(); unsigned head = m_asserted_qhead; while (head < sz) { expr * f = m_asserted_formulas.get(head); head++; m_static_features.collect(f); } m_static_features.display_primitive(std::cout); m_static_features.display(std::cout); } } void asserted_formulas::display(std::ostream & out) const { out << "asserted formulas:\n"; for (unsigned i = 0; i < m_asserted_formulas.size(); i++) { if (i == m_asserted_qhead) out << "[HEAD] ==>\n"; out << mk_pp(m_asserted_formulas.get(i), m_manager) << "\n"; } out << "inconsistent: " << inconsistent() << "\n"; } void asserted_formulas::display_ll(std::ostream & out, ast_mark & pp_visited) const { if (!m_asserted_formulas.empty()) { unsigned sz = m_asserted_formulas.size(); for (unsigned i = 0; i < sz; i++) ast_def_ll_pp(out, m_manager, m_asserted_formulas.get(i), pp_visited, true, false); out << "asserted formulas:\n"; for (unsigned i = 0; i < sz; i++) out << "#" << m_asserted_formulas[i]->get_id() << " "; out << "\n"; } } void asserted_formulas::collect_statistics(statistics & st) const { } void asserted_formulas::reduce_asserted_formulas() { if (inconsistent()) { return; } expr_ref_vector new_exprs(m_manager); proof_ref_vector new_prs(m_manager); unsigned i = m_asserted_qhead; unsigned sz = m_asserted_formulas.size(); for (; i < sz && !inconsistent(); i++) { expr * n = m_asserted_formulas.get(i); SASSERT(n != 0); proof * pr = m_asserted_formula_prs.get(i, 0); expr_ref new_n(m_manager); proof_ref new_pr(m_manager); m_simplifier(n, new_n, new_pr); TRACE("reduce_asserted_formulas", tout << mk_pp(n, m_manager) << " -> " << mk_pp(new_n, m_manager) << "\n";); if (n == new_n.get()) { push_assertion(n, pr, new_exprs, new_prs); } else { new_pr = m_manager.mk_modus_ponens(pr, new_pr); push_assertion(new_n, new_pr, new_exprs, new_prs); } if (canceled()) { return; } } swap_asserted_formulas(new_exprs, new_prs); } void asserted_formulas::swap_asserted_formulas(expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { SASSERT(!inconsistent() || !new_exprs.empty()); m_asserted_formulas.shrink(m_asserted_qhead); m_asserted_formulas.append(new_exprs); if (m_manager.proofs_enabled()) { m_asserted_formula_prs.shrink(m_asserted_qhead); m_asserted_formula_prs.append(new_prs); } } void asserted_formulas::find_macros_core() { expr_ref_vector new_exprs(m_manager); proof_ref_vector new_prs(m_manager); unsigned sz = m_asserted_formulas.size(); m_macro_finder->operator()(sz - m_asserted_qhead, m_asserted_formulas.c_ptr() + m_asserted_qhead, m_asserted_formula_prs.c_ptr() + m_asserted_qhead, new_exprs, new_prs); swap_asserted_formulas(new_exprs, new_prs); reduce_and_solve(); } void asserted_formulas::find_macros() { IF_IVERBOSE(10, verbose_stream() << "(smt.find-macros)\n";); TRACE("before_find_macros", display(tout);); find_macros_core(); TRACE("after_find_macros", display(tout);); } void asserted_formulas::expand_macros() { IF_IVERBOSE(10, verbose_stream() << "(smt.expand-macros)\n";); find_macros_core(); } void asserted_formulas::apply_quasi_macros() { IF_IVERBOSE(10, verbose_stream() << "(smt.find-quasi-macros)\n";); TRACE("before_quasi_macros", display(tout);); expr_ref_vector new_exprs(m_manager); proof_ref_vector new_prs(m_manager); quasi_macros proc(m_manager, m_macro_manager, m_simplifier); while (proc(m_asserted_formulas.size() - m_asserted_qhead, m_asserted_formulas.c_ptr() + m_asserted_qhead, m_asserted_formula_prs.c_ptr() + m_asserted_qhead, new_exprs, new_prs)) { swap_asserted_formulas(new_exprs, new_prs); new_exprs.reset(); new_prs.reset(); } TRACE("after_quasi_macros", display(tout);); reduce_and_solve(); } void asserted_formulas::nnf_cnf() { IF_IVERBOSE(10, verbose_stream() << "(smt.nnf)\n";); nnf apply_nnf(m_manager, m_defined_names); expr_ref_vector new_exprs(m_manager); proof_ref_vector new_prs(m_manager); expr_ref_vector push_todo(m_manager); proof_ref_vector push_todo_prs(m_manager); unsigned i = m_asserted_qhead; unsigned sz = m_asserted_formulas.size(); TRACE("nnf_bug", tout << "i: " << i << " sz: " << sz << "\n";); for (; i < sz; i++) { expr * n = m_asserted_formulas.get(i); TRACE("nnf_bug", tout << "processing:\n" << mk_pp(n, m_manager) << "\n";); proof * pr = m_asserted_formula_prs.get(i, 0); expr_ref r1(m_manager); proof_ref pr1(m_manager); CASSERT("well_sorted",is_well_sorted(m_manager, n)); push_todo.reset(); push_todo_prs.reset(); apply_nnf(n, push_todo, push_todo_prs, r1, pr1); CASSERT("well_sorted",is_well_sorted(m_manager, r1)); pr = m_manager.mk_modus_ponens(pr, pr1); push_todo.push_back(r1); push_todo_prs.push_back(pr); if (canceled()) { return; } unsigned sz2 = push_todo.size(); for (unsigned k = 0; k < sz2; k++) { expr * n = push_todo.get(k); proof * pr = 0; m_simplifier(n, r1, pr1); CASSERT("well_sorted",is_well_sorted(m_manager, r1)); if (canceled()) { return; } if (m_manager.proofs_enabled()) pr = m_manager.mk_modus_ponens(push_todo_prs.get(k), pr1); else pr = 0; push_assertion(r1, pr, new_exprs, new_prs); } } swap_asserted_formulas(new_exprs, new_prs); } #define MK_SIMPLE_SIMPLIFIER(NAME, FUNCTOR_DEF, LABEL, MSG) \ void asserted_formulas::NAME() { \ IF_IVERBOSE(10, verbose_stream() << "(smt." << MSG << ")\n";); \ TRACE(LABEL, tout << "before:\n"; display(tout);); \ FUNCTOR_DEF; \ expr_ref_vector new_exprs(m_manager); \ proof_ref_vector new_prs(m_manager); \ unsigned i = m_asserted_qhead; \ unsigned sz = m_asserted_formulas.size(); \ for (; i < sz; i++) { \ expr * n = m_asserted_formulas.get(i); \ proof * pr = m_asserted_formula_prs.get(i, 0); \ expr_ref new_n(m_manager); \ functor(n, new_n); \ TRACE("simplifier_simple_step", tout << mk_pp(n, m_manager) << "\n" << mk_pp(new_n, m_manager) << "\n";); \ if (n == new_n.get()) { \ push_assertion(n, pr, new_exprs, new_prs); \ } \ else if (m_manager.proofs_enabled()) { \ proof_ref new_pr(m_manager); \ new_pr = m_manager.mk_rewrite_star(n, new_n, 0, 0); \ new_pr = m_manager.mk_modus_ponens(pr, new_pr); \ push_assertion(new_n, new_pr, new_exprs, new_prs); \ } \ else { \ push_assertion(new_n, 0, new_exprs, new_prs); \ } \ } \ swap_asserted_formulas(new_exprs, new_prs); \ TRACE(LABEL, display(tout);); \ reduce_and_solve(); \ TRACE(LABEL, display(tout);); \ } MK_SIMPLE_SIMPLIFIER(apply_distribute_forall, distribute_forall functor(m_manager, *m_bsimp), "distribute_forall", "distribute-forall"); void asserted_formulas::reduce_and_solve() { IF_IVERBOSE(10, verbose_stream() << "(smt.reducing)\n";); flush_cache(); // collect garbage reduce_asserted_formulas(); } void asserted_formulas::infer_patterns() { IF_IVERBOSE(10, verbose_stream() << "(smt.pattern-inference)\n";); TRACE("before_pattern_inference", display(tout);); pattern_inference infer(m_manager, m_params); expr_ref_vector new_exprs(m_manager); proof_ref_vector new_prs(m_manager); unsigned i = m_asserted_qhead; unsigned sz = m_asserted_formulas.size(); for (; i < sz; i++) { expr * n = m_asserted_formulas.get(i); proof * pr = m_asserted_formula_prs.get(i, 0); expr_ref new_n(m_manager); proof_ref new_pr(m_manager); infer(n, new_n, new_pr); if (n == new_n.get()) { push_assertion(n, pr, new_exprs, new_prs); } else if (m_manager.proofs_enabled()) { new_pr = m_manager.mk_modus_ponens(pr, new_pr); push_assertion(new_n, new_pr, new_exprs, new_prs); } else { push_assertion(new_n, 0, new_exprs, new_prs); } } swap_asserted_formulas(new_exprs, new_prs); TRACE("after_pattern_inference", display(tout);); } void asserted_formulas::commit() { m_macro_manager.mark_forbidden(m_asserted_formulas.size() - m_asserted_qhead, m_asserted_formulas.c_ptr() + m_asserted_qhead); m_asserted_qhead = m_asserted_formulas.size(); } void asserted_formulas::eliminate_term_ite() { IF_IVERBOSE(10, verbose_stream() << "(smt.eliminating-ite-term)\n";); TRACE("before_elim_term_ite", display(tout);); elim_term_ite elim(m_manager, m_defined_names); expr_ref_vector new_exprs(m_manager); proof_ref_vector new_prs(m_manager); unsigned i = m_asserted_qhead; unsigned sz = m_asserted_formulas.size(); for (; i < sz; i++) { expr * n = m_asserted_formulas.get(i); proof * pr = m_asserted_formula_prs.get(i, 0); expr_ref new_n(m_manager); proof_ref new_pr(m_manager); elim(n, new_exprs, new_prs, new_n, new_pr); SASSERT(new_n.get() != 0); DEBUG_CODE({ for (unsigned i = 0; i < new_exprs.size(); i++) { SASSERT(new_exprs.get(i) != 0); } }); if (n == new_n.get()) { push_assertion(n, pr, new_exprs, new_prs); } else if (m_manager.proofs_enabled()) { new_pr = m_manager.mk_modus_ponens(pr, new_pr); push_assertion(new_n, new_pr, new_exprs, new_prs); } else { push_assertion(new_n, 0, new_exprs, new_prs); } } swap_asserted_formulas(new_exprs, new_prs); TRACE("after_elim_term_ite", display(tout);); reduce_and_solve(); TRACE("after_elim_term_ite", display(tout);); } void asserted_formulas::propagate_values() { IF_IVERBOSE(10, verbose_stream() << "(smt.constant-propagation)\n";); TRACE("propagate_values", tout << "before:\n"; display(tout);); flush_cache(); bool found = false; // Separate the formulas in two sets: C and R // C is a set which contains formulas of the form // { x = n }, where x is a variable and n a numberal. // R contains the rest. // // - new_exprs1 is the set C // - new_exprs2 is the set R // // The loop also updates the m_cache. It adds the entries x -> n to it. expr_ref_vector new_exprs1(m_manager); proof_ref_vector new_prs1(m_manager); expr_ref_vector new_exprs2(m_manager); proof_ref_vector new_prs2(m_manager); unsigned sz = m_asserted_formulas.size(); for (unsigned i = 0; i < sz; i++) { expr * n = m_asserted_formulas.get(i); proof * pr = m_asserted_formula_prs.get(i, 0); TRACE("simplifier", tout << mk_pp(n, m_manager) << "\n";); if (m_manager.is_eq(n)) { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (m_manager.is_value(lhs) || m_manager.is_value(rhs)) { if (m_manager.is_value(lhs)) std::swap(lhs, rhs); if (!m_manager.is_value(lhs) && !m_simplifier.is_cached(lhs)) { if (i >= m_asserted_qhead) { new_exprs1.push_back(n); if (m_manager.proofs_enabled()) new_prs1.push_back(pr); } TRACE("propagate_values", tout << "found:\n" << mk_pp(lhs, m_manager) << "\n->\n" << mk_pp(rhs, m_manager) << "\n";); m_simplifier.cache_result(lhs, rhs, pr); found = true; continue; } } } if (i >= m_asserted_qhead) { new_exprs2.push_back(n); if (m_manager.proofs_enabled()) new_prs2.push_back(pr); } } TRACE("propagate_values", tout << "found: " << found << "\n";); // If C is not empty, then reduce R using the updated simplifier cache with entries // x -> n for each constraint 'x = n' in C. if (found) { unsigned sz = new_exprs2.size(); for (unsigned i = 0; i < sz; i++) { expr * n = new_exprs2.get(i); proof * pr = new_prs2.get(i, 0); expr_ref new_n(m_manager); proof_ref new_pr(m_manager); m_simplifier(n, new_n, new_pr); if (n == new_n.get()) { push_assertion(n, pr, new_exprs1, new_prs1); } else { new_pr = m_manager.mk_modus_ponens(pr, new_pr); push_assertion(new_n, new_pr, new_exprs1, new_prs1); } } swap_asserted_formulas(new_exprs1, new_prs1); // IMPORTANT: the cache MUST be flushed. This guarantees that all entries // x->n will be removed from m_cache. If we don't do that, the next transformation // may simplify constraints in C using these entries, and the variables x in C // will be (silently) eliminated, and models produced by Z3 will not contain them. flush_cache(); } TRACE("propagate_values", tout << "after:\n"; display(tout);); } void asserted_formulas::propagate_booleans() { bool cont = true; bool modified = false; flush_cache(); while (cont) { TRACE("propagate_booleans", tout << "before:\n"; display(tout);); IF_IVERBOSE(10, verbose_stream() << "(smt.propagate-booleans)\n";); cont = false; unsigned i = m_asserted_qhead; unsigned sz = m_asserted_formulas.size(); #define PROCESS() { \ expr * n = m_asserted_formulas.get(i); \ proof * pr = m_asserted_formula_prs.get(i, 0); \ expr_ref new_n(m_manager); \ proof_ref new_pr(m_manager); \ m_simplifier(n, new_n, new_pr); \ m_asserted_formulas.set(i, new_n); \ if (m_manager.proofs_enabled()) { \ new_pr = m_manager.mk_modus_ponens(pr, new_pr); \ m_asserted_formula_prs.set(i, new_pr); \ } \ if (n != new_n) { \ cont = true; \ modified = true; \ } \ if (m_manager.is_not(new_n)) \ m_simplifier.cache_result(to_app(new_n)->get_arg(0), m_manager.mk_false(), m_manager.mk_iff_false(new_pr)); \ else \ m_simplifier.cache_result(new_n, m_manager.mk_true(), m_manager.mk_iff_true(new_pr)); \ } for (; i < sz; i++) { PROCESS(); } flush_cache(); TRACE("propagate_booleans", tout << "middle:\n"; display(tout);); i = sz; while (i > m_asserted_qhead) { --i; PROCESS(); } flush_cache(); TRACE("propagate_booleans", tout << "after:\n"; display(tout);); } if (modified) reduce_asserted_formulas(); } #define MK_SIMPLIFIER(NAME, FUNCTOR, TAG, MSG, REDUCE) \ bool asserted_formulas::NAME() { \ IF_IVERBOSE(10, verbose_stream() << "(smt." << MSG << ")\n";); \ TRACE(TAG, ast_mark visited; display_ll(tout, visited);); \ FUNCTOR; \ bool changed = false; \ expr_ref_vector new_exprs(m_manager); \ proof_ref_vector new_prs(m_manager); \ unsigned i = m_asserted_qhead; \ unsigned sz = m_asserted_formulas.size(); \ for (; i < sz; i++) { \ expr * n = m_asserted_formulas.get(i); \ proof * pr = m_asserted_formula_prs.get(i, 0); \ expr_ref new_n(m_manager); \ proof_ref new_pr(m_manager); \ functor(n, new_n, new_pr); \ if (n == new_n.get()) { \ push_assertion(n, pr, new_exprs, new_prs); \ } \ else if (m_manager.proofs_enabled()) { \ changed = true; \ if (!new_pr) new_pr = m_manager.mk_rewrite(n, new_n); \ new_pr = m_manager.mk_modus_ponens(pr, new_pr); \ push_assertion(new_n, new_pr, new_exprs, new_prs); \ } \ else { \ changed = true; \ push_assertion(new_n, 0, new_exprs, new_prs); \ } \ } \ swap_asserted_formulas(new_exprs, new_prs); \ TRACE(TAG, ast_mark visited; display_ll(tout, visited);); \ if (changed && REDUCE) { \ reduce_and_solve(); \ TRACE(TAG, ast_mark visited; display_ll(tout, visited);); \ } \ return changed; \ } MK_SIMPLIFIER(pull_cheap_ite_trees, pull_cheap_ite_tree_star functor(m_manager, m_simplifier), "pull_cheap_ite_trees", "pull-cheap-ite-trees", false); MK_SIMPLIFIER(pull_nested_quantifiers, pull_nested_quant functor(m_manager), "pull_nested_quantifiers", "pull-nested-quantifiers", false); proof * asserted_formulas::get_inconsistency_proof() const { if (!inconsistent()) return 0; if (!m_manager.proofs_enabled()) return 0; unsigned sz = m_asserted_formulas.size(); for (unsigned i = 0; i < sz; i++) { expr * f = m_asserted_formulas.get(i); if (m_manager.is_false(f)) return m_asserted_formula_prs.get(i); } UNREACHABLE(); return 0; } void asserted_formulas::refine_inj_axiom() { IF_IVERBOSE(10, verbose_stream() << "(smt.refine-injectivity)\n";); TRACE("inj_axiom", display(tout);); unsigned i = m_asserted_qhead; unsigned sz = m_asserted_formulas.size(); for (; i < sz; i++) { expr * n = m_asserted_formulas.get(i); proof * pr = m_asserted_formula_prs.get(i, 0); expr_ref new_n(m_manager); if (is_quantifier(n) && simplify_inj_axiom(m_manager, to_quantifier(n), new_n)) { TRACE("inj_axiom", tout << "simplifying...\n" << mk_pp(n, m_manager) << "\n" << mk_pp(new_n, m_manager) << "\n";); m_asserted_formulas.set(i, new_n); if (m_manager.proofs_enabled()) { proof_ref new_pr(m_manager); new_pr = m_manager.mk_rewrite(n, new_n); new_pr = m_manager.mk_modus_ponens(pr, new_pr); m_asserted_formula_prs.set(i, new_pr); } } } TRACE("inj_axiom", display(tout);); } MK_SIMPLIFIER(apply_bit2int, bit2int& functor = m_bit2int, "bit2int", "propagate-bit-vector-over-integers", true); MK_SIMPLIFIER(cheap_quant_fourier_motzkin, elim_bounds_star functor(m_manager), "elim_bounds", "cheap-fourier-motzkin", true); // MK_SIMPLIFIER(quant_elim, qe::expr_quant_elim_star1 &functor = m_quant_elim, // "quantifiers", "quantifier elimination procedures", true); bool asserted_formulas::quant_elim() { throw default_exception("QUANT_ELIM option is deprecated, please consider using the 'qe' tactic."); return false; } MK_SIMPLIFIER(elim_bvs_from_quantifiers, bv_elim_star functor(m_manager), "bv_elim", "eliminate-bit-vectors-from-quantifiers", true); #define LIFT_ITE(NAME, FUNCTOR, MSG) \ void asserted_formulas::NAME() { \ IF_IVERBOSE(10, verbose_stream() << "(smt." << MSG << ")\n";); \ TRACE("lift_ite", display(tout);); \ FUNCTOR; \ unsigned i = m_asserted_qhead; \ unsigned sz = m_asserted_formulas.size(); \ for (; i < sz; i++) { \ expr * n = m_asserted_formulas.get(i); \ proof * pr = m_asserted_formula_prs.get(i, 0); \ expr_ref new_n(m_manager); \ proof_ref new_pr(m_manager); \ functor(n, new_n, new_pr); \ TRACE("lift_ite_step", tout << mk_pp(n, m_manager) << "\n";); \ IF_IVERBOSE(10000, verbose_stream() << "lift before: " << get_num_exprs(n) << ", after: " << get_num_exprs(new_n) << "\n";); \ m_asserted_formulas.set(i, new_n); \ if (m_manager.proofs_enabled()) { \ new_pr = m_manager.mk_modus_ponens(pr, new_pr); \ m_asserted_formula_prs.set(i, new_pr); \ } \ } \ TRACE("lift_ite", display(tout);); \ reduce_and_solve(); \ } LIFT_ITE(lift_ite, push_app_ite functor(m_simplifier, m_params.m_lift_ite == LI_CONSERVATIVE), "lifting ite"); LIFT_ITE(ng_lift_ite, ng_push_app_ite functor(m_simplifier, m_params.m_ng_lift_ite == LI_CONSERVATIVE), "lifting ng ite"); unsigned asserted_formulas::get_total_size() const { expr_mark visited; unsigned r = 0; unsigned sz = m_asserted_formulas.size(); for (unsigned i = 0; i < sz; i++) r += get_num_exprs(m_asserted_formulas.get(i), visited); return r; } void asserted_formulas::max_bv_sharing() { IF_IVERBOSE(10, verbose_stream() << "(smt.maximizing-bv-sharing)\n";); TRACE("bv_sharing", display(tout);); unsigned i = m_asserted_qhead; unsigned sz = m_asserted_formulas.size(); for (; i < sz; i++) { expr * n = m_asserted_formulas.get(i); proof * pr = m_asserted_formula_prs.get(i, 0); expr_ref new_n(m_manager); proof_ref new_pr(m_manager); m_bv_sharing(n, new_n, new_pr); m_asserted_formulas.set(i, new_n); if (m_manager.proofs_enabled()) { new_pr = m_manager.mk_modus_ponens(pr, new_pr); m_asserted_formula_prs.set(i, new_pr); } } reduce_asserted_formulas(); TRACE("bv_sharing", display(tout);); } #ifdef Z3DEBUG void pp(asserted_formulas & f) { f.display(std::cout); } #endif z3-z3-4.4.1/src/smt/asserted_formulas.h000066400000000000000000000130311260446376700177100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: asserted_formulas.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-11. Revision History: --*/ #ifndef ASSERTED_FORMULAS_H_ #define ASSERTED_FORMULAS_H_ #include"smt_params.h" #include"simplifier.h" #include"basic_simplifier_plugin.h" #include"static_features.h" #include"macro_manager.h" #include"macro_finder.h" #include"defined_names.h" #include"maximise_ac_sharing.h" #include"bit2int.h" #include"statistics.h" #include"pattern_inference.h" class arith_simplifier_plugin; class bv_simplifier_plugin; class asserted_formulas { ast_manager & m_manager; smt_params & m_params; simplifier m_pre_simplifier; simplifier m_simplifier; basic_simplifier_plugin * m_bsimp; bv_simplifier_plugin * m_bvsimp; defined_names m_defined_names; static_features m_static_features; expr_ref_vector m_asserted_formulas; // formulas asserted by user proof_ref_vector m_asserted_formula_prs; // proofs for the asserted formulas. unsigned m_asserted_qhead; macro_manager m_macro_manager; scoped_ptr m_macro_finder; bit2int m_bit2int; maximise_bv_sharing m_bv_sharing; bool m_inconsistent; // qe::expr_quant_elim_star1 m_quant_elim; struct scope { unsigned m_asserted_formulas_lim; bool m_inconsistent_old; }; svector m_scopes; volatile bool m_cancel_flag; void setup_simplifier_plugins(simplifier & s, basic_simplifier_plugin * & bsimp, arith_simplifier_plugin * & asimp, bv_simplifier_plugin * & bvsimp); void reduce_asserted_formulas(); void swap_asserted_formulas(expr_ref_vector & new_exprs, proof_ref_vector & new_prs); void find_macros_core(); void find_macros(); void expand_macros(); void apply_quasi_macros(); void nnf_cnf(); void infer_patterns(); void eliminate_term_ite(); void reduce_and_solve(); void flush_cache() { m_pre_simplifier.reset(); m_simplifier.reset(); } void set_eliminate_and(bool flag); void propagate_values(); void propagate_booleans(); bool pull_cheap_ite_trees(); bool pull_nested_quantifiers(); void push_assertion(expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs); void eliminate_and(); void refine_inj_axiom(); bool cheap_quant_fourier_motzkin(); bool quant_elim(); void apply_distribute_forall(); bool apply_bit2int(); void lift_ite(); bool elim_bvs_from_quantifiers(); void ng_lift_ite(); #ifdef Z3DEBUG bool check_well_sorted() const; #endif unsigned get_total_size() const; bool has_bv() const; void max_bv_sharing(); bool canceled() { return m_cancel_flag; } public: asserted_formulas(ast_manager & m, smt_params & p); ~asserted_formulas(); void setup(); void assert_expr(expr * e, proof * in_pr); void assert_expr(expr * e); void reset(); void set_cancel_flag(bool f); void push_scope(); void pop_scope(unsigned num_scopes); bool inconsistent() const { return m_inconsistent; } proof * get_inconsistency_proof() const; void reduce(); unsigned get_num_formulas() const { return m_asserted_formulas.size(); } unsigned get_formulas_last_level() const; unsigned get_qhead() const { return m_asserted_qhead; } void commit(); expr * get_formula(unsigned idx) const { return m_asserted_formulas.get(idx); } proof * get_formula_proof(unsigned idx) const { return m_manager.proofs_enabled() ? m_asserted_formula_prs.get(idx) : 0; } expr * const * get_formulas() const { return m_asserted_formulas.c_ptr(); } proof * const * get_formula_proofs() const { return m_asserted_formula_prs.c_ptr(); } void init(unsigned num_formulas, expr * const * formulas, proof * const * prs); void register_simplifier_plugin(simplifier_plugin * p) { m_simplifier.register_plugin(p); } simplifier & get_simplifier() { return m_simplifier; } void get_assertions(ptr_vector & result); bool empty() const { return m_asserted_formulas.empty(); } void collect_static_features(); void display(std::ostream & out) const; void display_ll(std::ostream & out, ast_mark & pp_visited) const; void collect_statistics(statistics & st) const; // TODO: improve precision of the following method. bool has_quantifiers() const { return m_simplifier.visited_quantifier(); /* approximation */ } // ----------------------------------- // // Macros // // ----------------------------------- unsigned get_num_macros() const { return m_macro_manager.get_num_macros(); } unsigned get_first_macro_last_level() const { return m_macro_manager.get_first_macro_last_level(); } func_decl * get_macro_func_decl(unsigned i) const { return m_macro_manager.get_macro_func_decl(i); } func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const { return m_macro_manager.get_macro_interpretation(i, interp); } quantifier * get_macro_quantifier(func_decl * f) const { return m_macro_manager.get_macro_quantifier(f); } // auxiliary function used to create a logic context based on a model. void insert_macro(func_decl * f, quantifier * m, proof * pr) { m_macro_manager.insert(f, m, pr); } }; #endif /* ASSERTED_FORMULAS_H_ */ z3-z3-4.4.1/src/smt/cached_var_subst.cpp000066400000000000000000000040731260446376700200260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cached_var_subst.cpp Abstract: Author: Leonardo de Moura (leonardo) 2009-01-23. Revision History: --*/ #include"cached_var_subst.h" bool cached_var_subst::key_eq_proc::operator()(cached_var_subst::key * k1, cached_var_subst::key * k2) const { if (k1->m_qa != k2->m_qa) return false; if (k1->m_num_bindings != k2->m_num_bindings) return false; for (unsigned i = 0; i < k1->m_num_bindings; i++) if (k1->m_bindings[i] != k2->m_bindings[i]) return false; return true; } cached_var_subst::cached_var_subst(ast_manager & m): m_proc(m), m_refs(m) { } void cached_var_subst::reset() { m_refs.reset(); m_instances.reset(); m_region.reset(); m_new_keys.reset(); } void cached_var_subst::operator()(quantifier * qa, unsigned num_bindings, smt::enode * const * bindings, expr_ref & result) { m_new_keys.reserve(num_bindings+1, 0); key * new_key = m_new_keys[num_bindings]; if (new_key == 0) new_key = static_cast(m_region.allocate(sizeof(key) + sizeof(expr*)*num_bindings)); new_key->m_qa = qa; new_key->m_num_bindings = num_bindings; for (unsigned i = 0; i < num_bindings; i++) new_key->m_bindings[i] = bindings[i]->get_owner(); instances::entry * entry = m_instances.insert_if_not_there2(new_key, 0); if (entry->get_data().m_key != new_key) { SASSERT(entry->get_data().m_value != 0); // entry was already there m_new_keys[num_bindings] = new_key; // recycle key result = entry->get_data().m_value; return; } m_proc(qa->get_expr(), new_key->m_num_bindings, new_key->m_bindings, result); // cache result entry->get_data().m_value = result; // remove key from cache m_new_keys[num_bindings] = 0; // increment reference counters m_refs.push_back(qa); for (unsigned i = 0; i < new_key->m_num_bindings; i++) m_refs.push_back(new_key->m_bindings[i]); m_refs.push_back(result); } z3-z3-4.4.1/src/smt/cached_var_subst.h000066400000000000000000000023351260446376700174720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cached_var_subst.h Abstract: Author: Leonardo de Moura (leonardo) 2009-01-23. Revision History: --*/ #ifndef CACHED_VAR_SUBST_H_ #define CACHED_VAR_SUBST_H_ #include"var_subst.h" #include"map.h" #include"smt_enode.h" class cached_var_subst { struct key { quantifier * m_qa; unsigned m_num_bindings; expr * m_bindings[0]; }; struct key_hash_proc { unsigned operator()(key * k) const { return string_hash(reinterpret_cast(k->m_bindings), sizeof(expr *) * k->m_num_bindings, k->m_qa->get_id()); } }; struct key_eq_proc { bool operator()(key * k1, key * k2) const; }; typedef map instances; var_subst m_proc; expr_ref_vector m_refs; instances m_instances; region m_region; ptr_vector m_new_keys; // mapping from num_bindings -> next key public: cached_var_subst(ast_manager & m); void operator()(quantifier * qa, unsigned num_bindings, smt::enode * const * bindings, expr_ref & result); void reset(); }; #endif /* CACHED_VAR_SUBST_H_ */ z3-z3-4.4.1/src/smt/cost_evaluator.cpp000066400000000000000000000062021260446376700175550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cost_evaluator.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-14. Revision History: --*/ #include"cost_evaluator.h" #include"warning.h" cost_evaluator::cost_evaluator(ast_manager & m): m_manager(m), m_util(m) { } float cost_evaluator::eval(expr * f) const { #define E(IDX) eval(to_app(f)->get_arg(IDX)) if (is_app(f)) { unsigned num_args; family_id fid = to_app(f)->get_family_id(); if (fid == m_manager.get_basic_family_id()) { switch (to_app(f)->get_decl_kind()) { case OP_TRUE: return 1.0f; case OP_FALSE: return 0.0f; case OP_NOT: return E(0) == 0.0f ? 1.0f : 0.0f; case OP_AND: num_args = to_app(f)->get_num_args(); for (unsigned i = 0; i < num_args; i++) if (E(i) == 0.0f) return 0.0f; return 1.0f; case OP_OR: num_args = to_app(f)->get_num_args(); for (unsigned i = 0; i < num_args; i++) if (E(i) != 0.0f) return 1.0f; return 0.0f; case OP_ITE: return E(0) != 0.0f ? E(1) : E(2); case OP_EQ: case OP_IFF: return E(0) == E(1) ? 1.0f : 0.0f; case OP_XOR: return E(0) != E(1) ? 1.0f : 0.0f; case OP_IMPLIES: if (E(0) == 0.0f) return 1.0f; return E(1) != 0.0f ? 1.0f : 0.0f; default: ; } } else if (fid == m_util.get_family_id()) { switch (to_app(f)->get_decl_kind()) { case OP_NUM: { rational r = to_app(f)->get_decl()->get_parameter(0).get_rational(); return static_cast(numerator(r).get_int64())/static_cast(denominator(r).get_int64()); } case OP_LE: return E(0) <= E(1) ? 1.0f : 0.0f; case OP_GE: return E(0) >= E(1) ? 1.0f : 0.0f; case OP_LT: return E(0) < E(1) ? 1.0f : 0.0f; case OP_GT: return E(0) > E(1) ? 1.0f : 0.0f; case OP_ADD: return E(0) + E(1); case OP_SUB: return E(0) - E(1); case OP_UMINUS: return - E(0); case OP_MUL: return E(0) * E(1); case OP_DIV: { float q = E(1); if (q == 0.0f) { warning_msg("cost function division by zero"); return 1.0f; } return E(0) / q; } default: ; } } } else if (is_var(f)) { unsigned idx = to_var(f)->get_idx(); if (idx < m_num_args) return m_args[m_num_args - idx - 1]; } warning_msg("cost function evaluation error"); return 1.0f; } float cost_evaluator::operator()(expr * f, unsigned num_args, float const * args) { m_num_args = num_args; m_args = args; return eval(f); } z3-z3-4.4.1/src/smt/cost_evaluator.h000066400000000000000000000015441260446376700172260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cost_evaluator.h Abstract: Simple evaluator for cost function Author: Leonardo de Moura (leonardo) 2008-06-14. Revision History: --*/ #ifndef COST_EVALUATOR_H_ #define COST_EVALUATOR_H_ #include"ast.h" #include"arith_decl_plugin.h" class cost_evaluator { ast_manager & m_manager; arith_util m_util; unsigned m_num_args; float const * m_args; float eval(expr * f) const; public: cost_evaluator(ast_manager & m); /** 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. */ float operator()(expr * f, unsigned num_args, float const * args); }; #endif /* COST_EVALUATOR_H_ */ z3-z3-4.4.1/src/smt/database.h000066400000000000000000000227571260446376700157510ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ static char const g_pattern_database[] = "(benchmark patterns \n" " :status unknown \n" " :logic ALL \n" " :extrafuns ((?f1 Int Int Int Int) (?f2 Int Int Int) (?f3 Int Int Int) (?f4 Int Int Int)\n" " (?f5 Int Int Int) (?f6 Int Int) (?f7 Int Int) (?f8 Int Int Int) (?f9 Int Int Int)\n" " (?f10 Int) (?f11 Int) (?f12 Int Int) (?f13 Int Int) (?f14 Int Int Int) \n" " (?f15 Int Int) (?f16 Int Int) (?f17 Int Int) (?f18 Int Int) (?f19 Int Int)\n" " (?f20 Int Int) (?f21 Int) (?f22 Int) (?f23 Int) (?f24 Int Int) (?f25 Int Int)\n" " )\n" "\n" " :formula (forall (a Int) (i Int) (e Int) \n" " (= (?f2 (?f1 a i e) i) e)\n" " :pats { (?f1 a i e) }\n" " :weight { 0 })\n" "\n" " :formula (forall (a Int) (i Int) (j Int) (e Int) \n" " (or (= i j) (= (?f2 (?f1 a i e) j) (?f2 a j)))\n" " :pats { (?f2 (?f1 a i e) j) }\n" " :weight { 0 })\n" "\n" " :formula (forall (t0 Int) (t1 Int) (t2 Int) \n" " (or (not (= (?f3 t0 t1) 1))\n" " (not (= (?f3 t1 t2) 1))\n" " (= (?f3 t0 t2) 1))\n" " :pats { (?f3 t0 t1) (?f3 t1 t2) })\n" "\n" " :formula (forall (t0 Int) (t1 Int) \n" " (or (not (= (?f3 t0 t1) 1))\n" " (not (= (?f3 t1 t0) 1))\n" " (= t0 t1))\n" " :pats { (?f3 t0 t1) (?f3 t1 t0) })\n" "\n" " :formula (forall (t0 Int) (t1 Int) (t2 Int) \n" " (or (not (= (?f3 t0 (?f4 t1 t2)) 1))\n" " (= (?f5 t2 t0) (?f4 t1 t2)))\n" " :pats { (?f3 t0 (?f4 t1 t2)) })\n" "\n" " :formula (forall (t Int) \n" " (= (?f25 (?f24 t)) t)\n" " :pats { (?f24 t) })\n" "\n" " :formula (forall (t0 Int) (t1 Int) \n" " (iff (= (?f3 t0 (?f6 t1)) 1)\n" " (not (or (not (= t0 (?f6 (?f7 t0))))\n" " (not (= (?f3 (?f7 t0) t1) 1)))))\n" " :pats { (?f3 t0 (?f6 t1)) })\n" "\n" " :formula (forall (x Int) (t Int) \n" " (or (not (= (?f8 x t) 1))\n" " (= (?f9 x t) x))\n" " :pats { (?f9 x t) })\n" "\n" " :formula (forall (x Int) (t Int) \n" " (or (not (= (?f3 t ?f10) 1))\n" " (iff (= (?f8 x t) 1)\n" " (or (= x ?f11)\n" " (= (?f3 (?f12 x) t) 1))))\n" " :pats { (?f3 t ?f10) (?f8 x t) })\n" "\n" " :formula (forall (e Int) (a Int) (i Int) \n" " (= (?f8 (?f2 (?f2 (?f13 e) a) i)\n" " (?f7 (?f12 a))) 1)\n" " :pats { (?f2 (?f2 (?f13 e) a) i) })\n" "\n" " :formula (forall (x Int) (f Int) (a0 Int) \n" " (or (<= (+ a0 (* -1 (?f15 f))) 0)\n" " (not (= (?f14 x a0) 1))\n" " (= (?f14 (?f2 f x) a0) 1))\n" " :pats { (?f14 (?f2 f x) a0) })\n" "\n" " :formula (forall (a Int) (e Int) (i Int) (a0 Int) \n" " (or (<= (+ a0 (* -1 (?f16 e))) 0)\n" " (not (= (?f14 a a0) 1))\n" " (= (?f14 (?f2 (?f2 e a) i) a0) 1))\n" " :pats { (?f14 (?f2 (?f2 e a) i) a0) })\n" "\n" " :formula (forall (S Int) \n" " (= (?f2 (?f18 S) (?f17 (?f18 S))) 1)\n" " :pats { (?f2 (?f18 S) (?f17 (?f18 S))) })\n" "\n" " :formula (forall (s Int) \n" " (or (not (= 1 (?f19 s)))\n" " (= (?f3 (?f12 s) ?f23) 1))\n" " :pats { (?f19 s) })\n" "\n" " :formula (forall (t Int) \n" " (not (or (= (?f20 t) ?f11)\n" " (not (= (?f8 (?f20 t) ?f21) 1))\n" " (not (= (?f14 (?f20 t) ?f22) 1))))\n" " :pats { (?f20 t) })\n" "\n" " :extrafuns ((?f26 Int Int Int Int) \n" " (?f27 Int Int Int Int Int))\n" " \n" " :formula (forall (A Int) (o Int) (f Int) (v Int)\n" " (= (?f26 (?f27 A o f v) o f) v)\n" " :pats { (?f27 A o f v) }\n" " :weight { 0 }) \n" "\n" " :formula (forall (A Int) (o Int) (f Int) (p Int) (g Int) (v Int)\n" " (or (= o p) (= (?f26 (?f27 A o f v) p g) (?f26 A p g)))\n" " :pats { (?f26 (?f27 A o f v) p g) }\n" " :weight { 0 })\n" "\n" " :formula (forall (A Int) (o Int) (f Int) (p Int) (g Int) (v Int)\n" " (or (= f g) (= (?f26 (?f27 A o f v) p g) (?f26 A p g)))\n" " :pats { (?f26 (?f27 A o f v) p g) }\n" " :weight { 0 })\n" "\n" " :extrapreds ((?f28 Int Int))\n" "\n" " :formula (forall (t Int) (u Int) (v Int)\n" " (or (not (?f28 t u))\n" " (not (?f28 u v))\n" " (?f28 t v))\n" " :pat {(?f28 t u) (?f28 u v)})\n" "\n" " :formula (forall (t Int) (u Int)\n" " (or (not (?f28 t u))\n" " (not (?f28 u t))\n" " (= t u))\n" " :pat {(?f28 t u) (?f28 u t)})\n" "\n" " :extrafuns ((?f29 Int Int) (?f30 Int Int) (?f31 Int Int Int) (?f32 Int) (?f33 Int) (?f34 Int Int Int)\n" " (?f35 Int Int) (?f36 Int) (?f37 Int) (?f38 Int) (?f39 Int Int) (?f40 Int)\n" " (?f41 Int) (?f42 Int Int) (?f43 Int Int) (?f44 Int) (?f45 Int Int))\n" "\n" " :formula (forall (x Int) (p Int)\n" " (or (not (?f28 (?f30 (?f31 x p)) ?f32))\n" " (not (= (?f31 x p) p))\n" " (= x p))\n" " :pat { (?f28 (?f30 (?f31 x p)) ?f32)} )\n" " \n" " :formula (forall (h Int) (o Int) (f Int) (T Int)\n" " (or \n" " (not (= (?f39 h) ?f33))\n" " (= (?f26 h o (?f34 f T)) ?f36)\n" " (not (or (not (= (?f26 h (?f26 h o (?f34 f T)) ?f37) o))\n" " (not (= (?f26 h (?f26 h o (?f34 f T)) ?f38) T)))))\n" " :pat {(?f26 h o (?f34 f T))})\n" "\n" " :formula (forall (h Int) (o Int) (f Int)\n" " (or\n" " (not (= (?f39 h) ?f33))\n" " (= (?f26 h o (?f35 f)) ?f36)\n" " (not (or (not (= (?f26 h (?f26 h o (?f35 f)) ?f37) (?f26 h o ?f37)))\n" " (not (= (?f26 h (?f26 h o (?f35 f)) ?f38) (?f26 h o ?f38))))))\n" " :pat {(?f26 h o (?f35 f))})\n" " \n" " :formula (forall (h Int) (o Int)\n" " (or \n" " (not (= (?f39 h) ?f33))\n" " (= (?f26 h o ?f38) ?f44)\n" " (not (?f28 (?f26 h (?f26 h o ?f37) ?f41) (?f26 h o ?f38)))\n" " (= (?f26 h (?f26 h o ?f37) ?f40) (?f42 (?f26 h o ?f38)))\n" " (not (or (not (= (?f26 h o ?f41) (?f43 o)))\n" " (not (= (?f26 h o ?f40) (?f43 o))))))\n" " :pat {(?f28 (?f26 h (?f26 h o ?f37) ?f41) (?f26 h o ?f38))})\n" "\n" " :formula (forall (T Int) (h Int)\n" " (or (not (= (?f39 h) ?f33))\n" " (= (?f26 h (?f45 T) ?f38) ?f44))\n" " :pat {(?f26 h (?f45 T) ?f38)})\n" "\n" " :extrafuns ((?f46 Int Int Int)\n" " (?f47 Int Int Int)\n" " (?f48 Int Int Int)\n" " (?f49 Int)\n" " (?f50 Int Int Int)\n" " (?f51 Int Int Int)\n" " (?f52 Int Int)\n" " )\n" "\n" " :formula (forall (a Int) (T Int) (i Int) (r Int) (heap Int)\n" " (or (not (= (?f39 heap) ?f33))\n" " (not (?f28 (?f43 a) (?f46 T r)))\n" " (= (?f47 (?f48 (?f26 heap a ?f49) i) T) ?f33))\n" " :pat {(?f28 (?f43 a) (?f46 T r)) (?f48 (?f26 heap a ?f49) i)})\n" "\n" " :formula (forall (a Int) (T Int) (r Int)\n" " (or (= a ?f36) \n" " (not (?f28 (?f43 a) (?f46 T r)))\n" " (= (?f52 a) r))\n" " :pat {(?f28 (?f43 a) (?f46 T r))})\n" "\n" " :extrafuns ((?f53 Int Int Int)\n" " (?f54 Int Int)\n" " (?f55 Int)\n" " (?f56 Int Int)\n" " (?f57 Int)\n" " (?f58 Int)\n" " (?f59 Int Int Int)\n" " (?f60 Int Int Int)\n" " (?f61 Int Int Int)\n" " )\n" "\n" " :extrapreds ((?f62 Int Int))\n" " \n" " :formula (forall (T Int) (ET Int) (r Int)\n" " (or (not (?f28 T (?f53 ET r)))\n" " (= (?f54 T) ?f55))\n" " :pat {(?f28 T (?f53 ET r))})\n" "\n" " :formula (forall (A Int) (r Int) (T Int)\n" " (or\n" " (not (?f28 T (?f46 A r)))\n" " (not (or (not (= T (?f46 (?f56 T) r)))\n" " (not (?f28 (?f56 T) A)))))\n" " :pat {(?f28 T (?f46 A r))})\n" "\n" " :formula (forall (A Int) (r Int) (T Int)\n" " (or (not (?f28 T (?f53 A r)))\n" " (= T (?f53 A r)))\n" " :pat {(?f28 T (?f53 A r))})\n" "\n" " :extrafuns ((?f63 Int Int Int)\n" " (?f64 Int Int Int)\n" " )\n" "\n" " :formula (forall (A Int) (B Int) (C Int)\n" " (or (not (?f28 C (?f63 B A)))\n" " (= (?f64 C A) B))\n" " :pat {(?f28 C (?f63 B A))})\n" " \n" " :formula (forall (o Int) (T Int)\n" " (iff (= (?f47 o T) ?f33)\n" " (or (= o ?f36)\n" " (?f28 (?f43 o) T)))\n" " :pat {(?f47 o T)})\n" "\n" " :formula (forall (o Int) (T Int)\n" " (iff (= (?f51 o T) ?f33)\n" " (or (= o ?f36)\n" " (not (= (?f47 o T) ?f33))))\n" " :pat {(?f51 o T)})\n" "\n" " :formula (forall (h Int) (o Int)\n" " (or (not (= (?f39 h) ?f33))\n" " (= o ?f36)\n" " (not (?f28 (?f43 o) ?f57))\n" " (not (or (not (= (?f26 h o ?f41) (?f43 o)))\n" " (not (= (?f26 h o ?f40) (?f43 o))))))\n" " :pat {(?f28 (?f43 o) ?f57) (?f26 h o ?f41)})\n" "\n" " :formula (forall (h Int) (o Int) (f Int) (T Int)\n" " (or (not (= (?f39 h) ?f33))\n" " (?f62 (?f26 h o (?f60 f T)) T))\n" " :pat {(?f26 h o (?f60 f T))})\n" "\n" " :formula (forall (h Int) (o Int) (f Int)\n" " (or\n" " (not (= (?f39 h) ?f33))\n" " (not (= (?f26 h o ?f58) ?f33))\n" " (= (?f61 h (?f26 h o f)) ?f33))\n" " :pat {(?f61 h (?f26 h o f))})\n" "\n" " :formula (forall (h Int) (s Int) (f Int)\n" " (or (not (= (?f61 h s) ?f33))\n" " (= (?f61 h (?f59 s f)) ?f33))\n" " :pat {(?f61 h (?f59 s f))})\n" "\n" " :extrapreds ((?f65 Int Int))\n" "\n" " :formula (forall (x Int) (f Int) (a0 Int)\n" " (or (<= (+ a0 (* -1 (?f15 f))) 0)\n" " (not (?f65 x a0))\n" " (?f65 (?f2 f x) a0))\n" " :pat {(?f65 (?f2 f x) a0)})\n" "\n" " :formula (forall (a Int) (e Int) (i Int) (a0 Int) \n" " (or (<= (+ a0 (* -1 (?f16 e))) 0)\n" " (not (?f65 a a0))\n" " (?f65 (?f2 (?f2 e a) i) a0))\n" " :pats { (?f65 (?f2 (?f2 e a) i) a0) })\n" "\n" " :formula (forall (e Int) (a Int) (i Int) \n" " (= (?f8 (?f2 (?f2 (?f13 e) a) i)\n" " (?f7 (?f12 a))) ?f33)\n" " :pats { (?f2 (?f2 (?f13 e) a) i) })\n" "\n" " :formula (forall (t0 Int) (t1 Int)\n" " (iff (?f28 t0 (?f6 t1))\n" " (not (or (not (= t0 (?f6 (?f7 t0))))\n" " (not (?f28 (?f7 t0) t1)))))\n" " :pat {(?f28 t0 (?f6 t1))})\n" "\n" " :formula (forall (t0 Int) (t1 Int) (t2 Int) \n" " (or (not (?f28 t0 (?f4 t1 t2)))\n" " (= (?f5 t2 t0) (?f4 t1 t2)))\n" " :pats { (?f28 t0 (?f4 t1 t2)) })\n" "\n" " :formula (forall (t0 Int) (t1 Int) \n" " (iff (?f28 t0 (?f6 t1))\n" " (not (or (not (= t0 (?f6 (?f7 t0))))\n" " (not (?f28 (?f7 t0) t1)))))\n" " :pats { (?f28 t0 (?f6 t1)) })\n" "\n" " :formula (forall (x Int) (t Int) \n" " (or (not (= (?f8 x t) ?f33))\n" " (= (?f9 x t) x))\n" " :pats { (?f9 x t) })\n" "\n" " :formula (forall (x Int) (t Int) \n" " (or (not (?f28 t ?f10))\n" " (iff (= (?f8 x t) ?f33)\n" " (or (= x ?f11)\n" " (?f28 (?f12 x) t))))\n" " :pats { (?f28 t ?f10) (?f8 x t) })\n" "\n" " :formula (forall (e Int) (a Int) (i Int) \n" " (= (?f8 (?f2 (?f2 (?f13 e) a) i)\n" " (?f7 (?f12 a))) 1)\n" " :pats { (?f2 (?f2 (?f13 e) a) i) })\n" " )\n" ; z3-z3-4.4.1/src/smt/database.smt000066400000000000000000000345741260446376700163250ustar00rootroot00000000000000(benchmark patterns :status unknown :logic ALL :extrafuns ((?store Int Int Int Int) (?select Int Int Int) (?PO Int Int Int) (?asChild Int Int Int) (?classDown Int Int Int) (?array Int Int) (?elemtype Int Int) (?is Int Int Int) (?cast Int Int Int) (?Object Int) (?null Int) (?typeof Int Int) (?asElems Int Int) (?isAllocated Int Int Int) (?fClosedTime Int Int) (?eClosedTime Int Int) (?max Int Int) (?asLockSet Int Int) (?isNewArray Int Int) (?classLiteral Int Int) (?Class Int) (?alloc Int) (?arrayType Int) (?f Int Int) (?finv Int Int) ) :formula (forall (a Int) (i Int) (e Int) (= (?select (?store a i e) i) e) :pats { (?store a i e) } :weight { 0 }) :formula (forall (a Int) (i Int) (j Int) (e Int) (or (= i j) (= (?select (?store a i e) j) (?select a j))) :pats { (?select (?store a i e) j) } :weight { 0 }) :formula (forall (t0 Int) (t1 Int) (t2 Int) (or (not (= (?PO t0 t1) 1)) (not (= (?PO t1 t2) 1)) (= (?PO t0 t2) 1)) :pats { (?PO t0 t1) (?PO t1 t2) }) :formula (forall (t0 Int) (t1 Int) (or (not (= (?PO t0 t1) 1)) (not (= (?PO t1 t0) 1)) (= t0 t1)) :pats { (?PO t0 t1) (?PO t1 t0) }) :formula (forall (t0 Int) (t1 Int) (t2 Int) (or (not (= (?PO t0 (?asChild t1 t2)) 1)) (= (?classDown t2 t0) (?asChild t1 t2))) :pats { (?PO t0 (?asChild t1 t2)) }) :formula (forall (t Int) (= (?finv (?f t)) t) :pats { (?f t) }) :formula (forall (t0 Int) (t1 Int) (iff (= (?PO t0 (?array t1)) 1) (not (or (not (= t0 (?array (?elemtype t0)))) (not (= (?PO (?elemtype t0) t1) 1))))) :pats { (?PO t0 (?array t1)) }) :formula (forall (x Int) (t Int) (or (not (= (?is x t) 1)) (= (?cast x t) x)) :pats { (?cast x t) }) :formula (forall (x Int) (t Int) (or (not (= (?PO t ?Object) 1)) (iff (= (?is x t) 1) (or (= x ?null) (= (?PO (?typeof x) t) 1)))) :pats { (?PO t ?Object) (?is x t) }) :formula (forall (e Int) (a Int) (i Int) (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pats { (?select (?select (?asElems e) a) i) }) :formula (forall (x Int) (f Int) (a0 Int) (or (<= (+ a0 (* -1 (?fClosedTime f))) 0) (not (= (?isAllocated x a0) 1)) (= (?isAllocated (?select f x) a0) 1)) :pats { (?isAllocated (?select f x) a0) }) :formula (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)) :pats { (?isAllocated (?select (?select e a) i) a0) }) :formula (forall (S Int) (= (?select (?asLockSet S) (?max (?asLockSet S))) 1) :pats { (?select (?asLockSet S) (?max (?asLockSet S))) }) :formula (forall (s Int) (or (not (= 1 (?isNewArray s))) (= (?PO (?typeof s) ?arrayType) 1)) :pats { (?isNewArray s) }) :formula (forall (t Int) (not (or (= (?classLiteral t) ?null) (not (= (?is (?classLiteral t) ?Class) 1)) (not (= (?isAllocated (?classLiteral t) ?alloc) 1)))) :pats { (?classLiteral t) }) :extrafuns ((?select2 Int Int Int Int) (?store2 Int Int Int Int Int)) :formula (forall (A Int) (o Int) (f Int) (v Int) (= (?select2 (?store2 A o f v) o f) v) :pats { (?store2 A o f v) } :weight { 0 }) :formula (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))) :pats { (?select2 (?store2 A o f v) p g) } :weight { 0 }) :formula (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))) :pats { (?select2 (?store2 A o f v) p g) } :weight { 0 }) :extrapreds ((?subtypes Int Int)) :formula (forall (t Int) (u Int) (v Int) (or (not (?subtypes t u)) (not (?subtypes u v)) (?subtypes t v)) :pat {(?subtypes t u) (?subtypes u v)}) :formula (forall (t Int) (u Int) (or (not (?subtypes t u)) (not (?subtypes u t)) (= t u)) :pat {(?subtypes t u) (?subtypes u t)}) :extrafuns ((?Unbox Int Int) (?UnboxedType Int Int) (?Box Int Int Int) (?System.Object Int) (?Smt.true Int) (?AsRepField Int Int Int) (?AsPeerField Int Int) (?nullObject Int) (?ownerRef_ Int) (?ownerFrame_ Int) (IntsHeap Int Int) (?localinv_ Int) (?inv_ Int) (?BaseClass_ Int Int) (?typeof_ Int Int) (?PeerGroupPlaceholder_ Int) (?ClassRepr Int Int)) :formula (forall (x Int) (p Int) (or (not (?subtypes (?UnboxedType (?Box x p)) ?System.Object)) (not (= (?Box x p) p)) (= x p)) :pat { (?subtypes (?UnboxedType (?Box x p)) ?System.Object)} ) :formula (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))))) :pat {(?select2 h o (?AsRepField f T))}) :formula (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_)))))) :pat {(?select2 h o (?AsPeerField f))}) :formula (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)))))) :pat {(?subtypes (?select2 h (?select2 h o ?ownerRef_) ?inv_) (?select2 h o ?ownerFrame_))}) :formula (forall (T Int) (h Int) (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h (?ClassRepr T) ?ownerFrame_) ?PeerGroupPlaceholder_)) :pat {(?select2 h (?ClassRepr T) ?ownerFrame_)}) :extrafuns ((?RefArray Int Int Int) (Ints_ Int Int Int) (?RefArrayGet Int Int Int) (?elements_ Int) (?NonNullRefArray Int Int Int) (IntsNotNull_ Int Int Int) (?Rank_ Int Int) ) :formula (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)) :pat {(?subtypes (?typeof_ a) (?RefArray T r)) (?RefArrayGet (?select2 heap a ?elements_) i)}) :formula (forall (a Int) (T Int) (r Int) (or (= a ?nullObject) (not (?subtypes (?typeof_ a) (?RefArray T r))) (= (?Rank_ a) r)) :pat {(?subtypes (?typeof_ a) (?RefArray T r))}) :extrafuns ((?ValueArray Int Int Int) (?ArrayCategory_ Int Int) (?ArrayCategoryValue_ Int) (?ElementType_ Int Int) (?System.Array Int) (?allocated_ Int) (?StructGet_ Int Int Int) (?AsRangeField Int Int Int) (IntsAllocated Int Int Int) ) :extrapreds ((IntnRange Int Int)) :formula (forall (T Int) (ET Int) (r Int) (or (not (?subtypes T (?ValueArray ET r))) (= (?ArrayCategory_ T) ?ArrayCategoryValue_)) :pat {(?subtypes T (?ValueArray ET r))}) :formula (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))))) :pat {(?subtypes T (?RefArray A r))}) :formula (forall (A Int) (r Int) (T Int) (or (not (?subtypes T (?ValueArray A r))) (= T (?ValueArray A r))) :pat {(?subtypes T (?ValueArray A r))}) :extrafuns ((?AsDirectSubClass Int Int Int) (?OneClassDown Int Int Int) ) :formula (forall (A Int) (B Int) (C Int) (or (not (?subtypes C (?AsDirectSubClass B A))) (= (?OneClassDown C A) B)) :pat {(?subtypes C (?AsDirectSubClass B A))}) :formula (forall (o Int) (T Int) (iff (= (Ints_ o T) ?Smt.true) (or (= o ?nullObject) (?subtypes (?typeof_ o) T))) :pat {(Ints_ o T)}) :formula (forall (o Int) (T Int) (iff (= (IntsNotNull_ o T) ?Smt.true) (or (= o ?nullObject) (not (= (Ints_ o T) ?Smt.true)))) :pat {(IntsNotNull_ o T)}) :formula (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)))))) :pat {(?subtypes (?typeof_ o) ?System.Array) (?select2 h o ?inv_)}) :formula (forall (h Int) (o Int) (f Int) (T Int) (or (not (= (IntsHeap h) ?Smt.true)) (IntnRange (?select2 h o (?AsRangeField f T)) T)) :pat {(?select2 h o (?AsRangeField f T))}) :formula (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)) :pat {(IntsAllocated h (?select2 h o f))}) :formula (forall (h Int) (s Int) (f Int) (or (not (= (IntsAllocated h s) ?Smt.true)) (= (IntsAllocated h (?StructGet_ s f)) ?Smt.true)) :pat {(IntsAllocated h (?StructGet_ s f))}) :extrapreds ((?isAllocated_ Int Int)) :formula (forall (x Int) (f Int) (a0 Int) (or (<= (+ a0 (* -1 (?fClosedTime f))) 0) (not (?isAllocated_ x a0)) (?isAllocated_ (?select f x) a0)) :pat {(?isAllocated_ (?select f x) a0)}) :formula (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)) :pats { (?isAllocated_ (?select (?select e a) i) a0) }) :formula (forall (e Int) (a Int) (i Int) (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) ?Smt.true) :pats { (?select (?select (?asElems e) a) i) }) :formula (forall (t0 Int) (t1 Int) (iff (?subtypes t0 (?array t1)) (not (or (not (= t0 (?array (?elemtype t0)))) (not (?subtypes (?elemtype t0) t1))))) :pat {(?subtypes t0 (?array t1))}) :formula (forall (t0 Int) (t1 Int) (t2 Int) (or (not (?subtypes t0 (?asChild t1 t2))) (= (?classDown t2 t0) (?asChild t1 t2))) :pats { (?subtypes t0 (?asChild t1 t2)) }) :formula (forall (t0 Int) (t1 Int) (iff (?subtypes t0 (?array t1)) (not (or (not (= t0 (?array (?elemtype t0)))) (not (?subtypes (?elemtype t0) t1))))) :pats { (?subtypes t0 (?array t1)) }) :formula (forall (x Int) (t Int) (or (not (= (?is x t) ?Smt.true)) (= (?cast x t) x)) :pats { (?cast x t) }) :formula (forall (x Int) (t Int) (or (not (?subtypes t ?Object)) (iff (= (?is x t) ?Smt.true) (or (= x ?null) (?subtypes (?typeof x) t)))) :pats { (?subtypes t ?Object) (?is x t) }) :formula (forall (e Int) (a Int) (i Int) (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pats { (?select (?select (?asElems e) a) i) }) ) z3-z3-4.4.1/src/smt/diff_logic.h000066400000000000000000002021221260446376700162540ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: diff_logic.h Abstract: Basic support for difference logic Author: Leonardo de Moura (leonardo) 2006-11-21. Revision History: --*/ #ifndef DIFF_LOGIC_H_ #define DIFF_LOGIC_H_ #include"vector.h" #include"heap.h" #include"statistics.h" #include"trace.h" #include"warning.h" #include"uint_set.h" #include typedef int dl_var; typedef enum { DL_UNMARKED = 0, // Node/Variable was not yet found in the forward/backward search. DL_FOUND, // Node/Variable was found but not yet processed. DL_PROCESSED // Node/Variable was found and processed in the forward/backward search/ } dl_search_mark; typedef enum { DL_PROP_UNMARKED = 0, DL_PROP_IRRELEVANT = 1, DL_PROP_RELEVANT = 2, DL_PROP_PROCESSED_RELEVANT = 3, DL_PROP_PROCESSED_IRRELEVANT = 4 } dl_prop_search_mark ; template class dl_edge { typedef typename Ext::numeral numeral; typedef typename Ext::explanation explanation; dl_var m_source; dl_var m_target; numeral m_weight; unsigned m_timestamp; explanation m_explanation; bool m_enabled; public: dl_edge(dl_var s, dl_var t, const numeral & w, unsigned ts, const explanation & ex): m_source(s), m_target(t), m_weight(w), m_timestamp(ts), m_explanation(ex), m_enabled(false) { } dl_var get_source() const { return m_source; } dl_var get_target() const { return m_target; } const numeral & get_weight() const { return m_weight; } const explanation & get_explanation() const { return m_explanation; } unsigned get_timestamp() const { return m_timestamp; } bool is_enabled() const { return m_enabled; } void enable(unsigned timestamp) { SASSERT(!m_enabled); m_timestamp = timestamp; m_enabled = true; } void disable() { m_enabled = false; } }; // Functor for comparing difference logic variables. // This functor is used to implement Dijkstra algorithm (i.e., Heap). template class dl_var_lt { typedef typename Ext::numeral numeral; vector & m_values; public: dl_var_lt(vector & values): m_values(values) { } bool operator()(dl_var v1, dl_var v2) const { return m_values[v1] < m_values[v2]; } }; typedef int edge_id; const edge_id null_edge_id = -1; template class dl_graph { struct stats { unsigned m_propagation_cost; unsigned m_implied_literal_cost; unsigned m_num_implied_literals; unsigned m_num_helpful_implied_literals; unsigned m_num_relax; void reset() { m_propagation_cost = 0; m_implied_literal_cost = 0; m_num_implied_literals = 0; m_num_helpful_implied_literals = 0; m_num_relax = 0; } stats() { reset(); } void collect_statistics(::statistics& st) const { st.update("dl prop steps", m_propagation_cost); st.update("dl impl steps", m_implied_literal_cost); st.update("dl impl lits", m_num_implied_literals); st.update("dl impl conf lits", m_num_helpful_implied_literals); st.update("dl bound relax", m_num_relax); } }; stats m_stats; typedef typename Ext::numeral numeral; typedef typename Ext::explanation explanation; typedef vector assignment; typedef dl_edge edge; typedef vector edges; class assignment_trail { dl_var m_var; numeral m_old_value; public: assignment_trail(dl_var v, const numeral & val): m_var(v), m_old_value(val) { } dl_var get_var() const { return m_var; } const numeral & get_old_value() const { return m_old_value; } }; typedef vector assignment_stack; assignment m_assignment; // per var assignment_stack m_assignment_stack; // temporary stack for restoring the assignment edges m_edges; typedef int_vector edge_id_vector; typedef int_vector dl_var_vector; vector m_out_edges; // per var vector m_in_edges; // per var struct scope { unsigned m_edges_lim; unsigned m_enabled_edges_lim; unsigned m_old_timestamp; scope(unsigned e, unsigned enabled, unsigned t): m_edges_lim(e), m_enabled_edges_lim(enabled), m_old_timestamp(t) { } }; svector m_trail_stack; // forward reachability vector m_gamma; // per var svector m_mark; // per var edge_id_vector m_parent; // per var dl_var_vector m_visited; typedef heap > var_heap; var_heap m_heap; unsigned m_timestamp; unsigned m_last_enabled_edge; edge_id_vector m_enabled_edges; // SCC for cheap equality propagation -- svector m_unfinished_set; // per var int_vector m_dfs_time; // per var dl_var_vector m_roots; dl_var_vector m_unfinished; int m_next_dfs_time; int m_next_scc_id; // ------------------------------------- // activity vector for edges. svector m_activity; bool check_invariant() const { #ifdef Z3DEBUG SASSERT(m_assignment.size() == m_gamma.size()); SASSERT(m_assignment.size() == m_mark.size()); SASSERT(m_assignment.size() == m_parent.size()); SASSERT(m_assignment.size() <= m_heap.get_bounds()); SASSERT(m_in_edges.size() == m_out_edges.size()); int n = m_out_edges.size(); for (dl_var id = 0; id < n; id++) { const edge_id_vector & e_ids = m_out_edges[id]; edge_id_vector::const_iterator it = e_ids.begin(); edge_id_vector::const_iterator end = e_ids.end(); for (; it != end; ++it) { edge_id e_id = *it; SASSERT(static_cast(e_id) <= m_edges.size()); const edge & e = m_edges[e_id]; SASSERT(e.get_source() == id); } } for (dl_var id = 0; id < n; id++) { const edge_id_vector & e_ids = m_in_edges[id]; edge_id_vector::const_iterator it = e_ids.begin(); edge_id_vector::const_iterator end = e_ids.end(); for (; it != end; ++it) { edge_id e_id = *it; SASSERT(static_cast(e_id) <= m_edges.size()); const edge & e = m_edges[e_id]; SASSERT(e.get_target() == id); } } n = m_edges.size(); for (int i = 0; i < n; i++) { const edge & e = m_edges[i]; SASSERT(std::find(m_out_edges[e.get_source()].begin(), m_out_edges[e.get_source()].end(), i) != m_out_edges[e.get_source()].end()); SASSERT(std::find(m_in_edges[e.get_target()].begin(), m_in_edges[e.get_target()].end(), i) != m_in_edges[e.get_target()].end()); } #endif return true; } bool is_feasible(const edge & e) const { return !e.is_enabled() || m_assignment[e.get_target()] - m_assignment[e.get_source()] <= e.get_weight(); } public: // An assignment is feasible if all edges are feasible. bool is_feasible() const { #ifdef Z3DEBUG for (unsigned i = 0; i < m_edges.size(); ++i) { if (!is_feasible(m_edges[i])) { return false; } } #endif return true; } unsigned get_num_edges() const { return m_edges.size(); } unsigned get_num_nodes() const { return m_out_edges.size(); } dl_var get_source(edge_id id) const { return m_edges[id].get_source(); } dl_var get_target(edge_id id) const { return m_edges[id].get_target(); } explanation const & get_explanation(edge_id id) const { return m_edges[id].get_explanation(); } bool is_enabled(edge_id id) const { return m_edges[id].is_enabled(); } bool is_feasible(edge_id id) const { return is_feasible(m_edges[id]); } numeral const& get_weight(edge_id id) const { return m_edges[id].get_weight(); } private: // An assignment is almost feasible if all but edge with idt edge are feasible. bool is_almost_feasible(edge_id id) const { #ifdef Z3DEBUG for (unsigned i = 0; i < m_edges.size(); ++i) { if (id != static_cast(i) && !is_feasible(m_edges[i])) { return false; } } #endif return true; } // Restore the assignment using the information in m_assignment_stack. // This method is called when make_feasible fails. void undo_assignments() { typename assignment_stack::iterator it = m_assignment_stack.end(); typename assignment_stack::iterator begin = m_assignment_stack.begin(); while (it != begin) { --it; TRACE("diff_logic_bug", tout << "undo assignment: " << it->get_var() << " " << it->get_old_value() << "\n";); m_assignment[it->get_var()] = it->get_old_value(); } m_assignment_stack.reset(); } // Store in gamma the normalized weight. The normalized weight is given // by the formula // m_assignment[e.get_source()] - m_assignment[e.get_target()] + e.get_weight() void set_gamma(const edge & e, numeral & gamma) { gamma = m_assignment[e.get_source()]; gamma -= m_assignment[e.get_target()]; gamma += e.get_weight(); } void reset_marks() { dl_var_vector::iterator it = m_visited.begin(); dl_var_vector::iterator end = m_visited.end(); for (; it != end; ++it) { m_mark[*it] = DL_UNMARKED; } m_visited.reset(); } bool marks_are_clear() const { for (unsigned i = 0; i < m_mark.size(); ++i) { if (m_mark[i] != DL_UNMARKED) { return false; } } return true; } // Make the assignment feasible. An assignment is feasible if // Forall edge e. m_assignment[e.get_target()] - m_assignment[e.get_source()] <= e.get_weight() // // This method assumes that if the assignment is not feasible, then the only infeasible edge // is the last added edge. bool make_feasible(edge_id id) { SASSERT(is_almost_feasible(id)); SASSERT(!m_edges.empty()); SASSERT(!is_feasible(m_edges[id])); SASSERT(m_assignment_stack.empty()); SASSERT(m_heap.empty()); const edge & last_e = m_edges[id]; dl_var root = last_e.get_source(); m_gamma[root].reset(); dl_var target = last_e.get_target(); numeral gamma; set_gamma(last_e, gamma); m_gamma[target] = gamma; m_mark[target] = DL_PROCESSED; m_parent[target] = id; m_visited.push_back(target); SASSERT(m_gamma[target].is_neg()); acc_assignment(target, gamma); TRACE("arith", tout << id << "\n";); dl_var source = target; for(;;) { ++m_stats.m_propagation_cost; if (m_mark[root] != DL_UNMARKED) { // negative cycle was found SASSERT(m_gamma[root].is_neg()); m_heap.reset(); reset_marks(); undo_assignments(); return false; } typename edge_id_vector::iterator it = m_out_edges[source].begin(); typename edge_id_vector::iterator end = m_out_edges[source].end(); for (; it != end; ++it) { edge_id e_id = *it; edge & e = m_edges[e_id]; SASSERT(e.get_source() == source); if (!e.is_enabled()) { continue; } set_gamma(e, gamma); if (gamma.is_neg()) { target = e.get_target(); switch (m_mark[target]) { case DL_UNMARKED: m_gamma[target] = gamma; m_mark[target] = DL_FOUND; m_parent[target] = e_id; m_visited.push_back(target); m_heap.insert(target); break; case DL_FOUND: if (gamma < m_gamma[target]) { m_gamma[target] = gamma; m_parent[target] = e_id; m_heap.decreased(target); } break; case DL_PROCESSED: default: UNREACHABLE(); } } } if (m_heap.empty()) { SASSERT(is_feasible()); reset_marks(); m_assignment_stack.reset(); return true; } source = m_heap.erase_min(); m_mark[source] = DL_PROCESSED; acc_assignment(source, m_gamma[source]); } } edge const* find_relaxed_edge(edge const* e, numeral & gamma) { SASSERT(gamma.is_neg()); dl_var src = e->get_source(); dl_var dst = e->get_target(); numeral w = e->get_weight(); typename edge_id_vector::iterator it = m_out_edges[src].begin(); typename edge_id_vector::iterator end = m_out_edges[src].end(); for (; it != end; ++it) { edge_id e_id = *it; edge const& e2 = m_edges[e_id]; if (e2.get_target() == dst && e2.is_enabled() && // or at least not be inconsistent with current choices e2.get_weight() > w && (e2.get_weight() - w + gamma).is_neg()) { e = &e2; gamma += (e2.get_weight() - w); w = e2.get_weight(); ++m_stats.m_num_relax; } } return e; } public: dl_graph(): m_heap(1024, dl_var_lt(m_gamma)), m_timestamp(0), m_fw(m_mark), m_bw(m_mark) { } void collect_statistics(::statistics& st) const { m_stats.collect_statistics(st); } // Create/Initialize a variable with the given id. // The graph does not have control over the ids assigned by the theory. // That is init_var receives the id as an argument. void init_var(dl_var v) { TRACE("diff_logic_bug", tout << "init_var " << v << "\n";); SASSERT(static_cast(v) >= m_out_edges.size() || m_out_edges[v].empty()); SASSERT(check_invariant()); while (static_cast(v) >= m_out_edges.size()) { m_assignment .push_back(numeral()); m_out_edges .push_back(edge_id_vector()); m_in_edges .push_back(edge_id_vector()); m_gamma .push_back(numeral()); m_mark .push_back(DL_UNMARKED); m_parent .push_back(null_edge_id); } if (static_cast(v) >= m_heap.get_bounds()) { m_heap.set_bounds(v+1); } m_assignment[v].reset(); SASSERT(static_cast(v) < m_heap.get_bounds()); TRACE("diff_logic_bug", tout << "init_var " << v << ", m_assignment[v]: " << m_assignment[v] << "\n";); SASSERT(m_assignment[v].is_zero()); SASSERT(m_out_edges[v].empty()); SASSERT(m_in_edges[v].empty()); SASSERT(m_mark[v] == DL_UNMARKED); SASSERT(check_invariant()); } // Add an new weighted edge "source --weight--> target" with explanation ex. edge_id add_edge(dl_var source, dl_var target, const numeral & weight, const explanation & ex) { // SASSERT(is_feasible()); edge_id new_id = m_edges.size(); m_edges.push_back(edge(source, target, weight, m_timestamp, ex)); m_activity.push_back(0); TRACE("dl_bug", tout << "creating edge:\n"; display_edge(tout, m_edges.back());); m_out_edges[source].push_back(new_id); m_in_edges[target].push_back(new_id); return new_id; } // Return false if the resultant graph has a negative cycle. The negative // cycle can be extracted using traverse_neg_cycle. // The method assumes the graph is feasible before the invocation. bool enable_edge(edge_id id) { edge& e = m_edges[id]; bool r = true; if (!e.is_enabled()) { e.enable(m_timestamp); m_last_enabled_edge = id; m_timestamp++; if (!is_feasible(e)) { r = make_feasible(id); } SASSERT(check_invariant()); SASSERT(!r || is_feasible()); m_enabled_edges.push_back(id); } return r; } // This method should only be invoked when add_edge returns false. // That is, there is a negative cycle in the graph. // It will apply the functor f on every explanation attached to the edges // in the negative cycle. template void traverse_neg_cycle(bool try_relax, Functor & f) { SASSERT(!is_feasible(m_edges[m_last_enabled_edge])); edge_id last_id = m_last_enabled_edge; edge const& last_e = m_edges[last_id]; numeral gamma = m_gamma[last_e.get_source()]; SASSERT(gamma.is_neg()); edge_id e_id = last_id; do { const edge * e = &m_edges[e_id]; if (try_relax) { e = find_relaxed_edge(e, gamma); } inc_activity(e_id); f(e->get_explanation()); e_id = m_parent[e->get_source()]; } while (e_id != last_id); } // // Here is a version that tries to // Find shortcuts on the cycle. // A shortcut is an edge that that is subsumed // by the current edges, but provides for a shorter // path to the conflict. // Example (<= (- a b) k1) (<= (- b c) k2) (<= (- c d) k3) // An edge (<= (- a d) k4) where k1 + k2 + k3 <= k4, but gamma + k4 - (k1+k2+k3) < 0 // is still a conflict. // template void traverse_neg_cycle2(bool try_relax, Functor & f) { static unsigned num_conflicts = 0; ++num_conflicts; SASSERT(!is_feasible(m_edges[m_last_enabled_edge])); vector potentials; svector edges; svector nodes; edge_id last_id = m_last_enabled_edge; edge const& last_e = m_edges[last_id]; numeral potential(0); edge_id e_id = last_id; numeral gamma = m_gamma[last_e.get_source()]; SASSERT(check_gamma(last_id)); do { SASSERT(gamma.is_neg()); edges.push_back(e_id); const edge & e = m_edges[e_id]; dl_var src = e.get_source(); potential += e.get_weight(); // // search for edges that can reduce size of negative cycle. // typename edge_id_vector::iterator it = m_out_edges[src].begin(); typename edge_id_vector::iterator end = m_out_edges[src].end(); for (; it != end; ++it) { edge_id e_id2 = *it; edge const& e2 = m_edges[e_id2]; dl_var src2 = e2.get_target(); if (e_id2 == e_id || !e2.is_enabled()) { continue; } for (unsigned j = 0; j < nodes.size(); ++j) { if (nodes[j] != src2) { continue; } numeral const& weight = e2.get_weight(); numeral delta = weight - potential + potentials[j]; if (delta.is_nonneg() && (gamma + delta).is_neg()) { TRACE("diff_logic_traverse", tout << "Reducing path by "; display_edge(tout, e2); tout << "gamma: " << gamma << " weight: " << weight << "\n"; tout << "enabled: " << e2.is_enabled() << "\n"; tout << "delta: " << delta << "\n"; tout << "literals saved: " << (nodes.size() - j - 1) << "\n"; ); gamma += delta; nodes.shrink(j + 1); potentials.shrink(j + 1); edges.shrink(j + 1); edges.push_back(e_id2); potential = potentials[j] + weight; break; } else { TRACE("diff_logic_traverse", display_edge(tout << "skipping: ", e2);); } } } potentials.push_back(potential); nodes.push_back(src); e_id = m_parent[src]; SASSERT(check_path(potentials, nodes, edges)); } while (e_id != last_id); TRACE("diff_logic_traverse", { tout << "Num conflicts: " << num_conflicts << "\n"; tout << "Resulting path:\n"; for (unsigned i = 0; i < edges.size(); ++i) { display_edge(tout << "potential: " << potentials[i] << " ", m_edges[edges[i]]); } } ); if (!check_explanation(edges.size(), edges.c_ptr())) { throw default_exception("edges are not inconsistent"); } // allow theory to introduce shortcut lemmas. prune_edges(edges, f); for (unsigned i = 0; i < edges.size(); ++i) { edge const& e = m_edges[edges[i]]; f(e.get_explanation()); } } // // Create fresh literals obtained by resolving a pair (or more) // literals associated with the edges. // template void prune_edges(svector& edges, Functor & f) { unsigned max_activity = 0; edge_id e_id; for (unsigned i = 0; i < edges.size(); ++i) { e_id = edges[i]; inc_activity(e_id); if (m_activity[e_id] > max_activity) { max_activity = m_activity[e_id]; } } if (edges.size() > 5 && max_activity > 20) { prune_edges_min2(edges, f); } } template void prune_edges_min1(svector& edges, Functor & f) { unsigned min_activity = ~0; unsigned idx = 0; for (unsigned i = 0; i + 1 < edges.size(); ++i) { edge_id e_id = edges[i]; if (m_activity[e_id] < min_activity) { min_activity = m_activity[e_id]; idx = i; } } dl_var dst = get_source(edges[idx+1]); dl_var src = get_target(edges[idx]); f.new_edge(src, dst, 2, edges.begin()+idx); } template void prune_edges_min2(svector& edges, Functor & f) { unsigned min1 = ~0, min2 = ~0, max = 0; unsigned idx1 = 0, idx2 = 0, max_idx = 0; dl_var src, dst; for (unsigned i = 0; i < edges.size(); ++i) { edge_id e_id = edges[i]; if (m_activity[e_id] <= min1) { min2 = min1; min1 = m_activity[e_id]; idx2 = idx1; idx1 = i; } else if (m_activity[e_id] < min2) { min2 = m_activity[e_id]; idx2 = i; } // TBD: use also the edge with the maximal // traversals to create cut-edge. // if (m_activity[e_id] > max) { max = m_activity[e_id]; max_idx = i; } } // // e1 e2 i1 e4 e5 e6 .. e8 i2 e9 e10 // => // e1 e2 e_new d9 e10 // // alternative: // e_new e4 ... e8 is the new edge. // // or both. // if (idx2 < idx1) { std::swap(idx1,idx2); } SASSERT(idx1 < idx2 && idx2 < edges.size()); SASSERT(max_idx < edges.size()); dst = get_source(edges[idx2]); src = get_target(edges[idx1]); f.new_edge(src, dst, idx2-idx1+1, edges.begin()+idx1); } // Create a new scope. // That is, save the number of edges in the graph. void push() { // SASSERT(is_feasible()); <<< I relaxed this condition m_trail_stack.push_back(scope(m_edges.size(), m_enabled_edges.size(), m_timestamp)); } // Backtrack num_scopes scopes. // Restore the previous number of edges. void pop(unsigned num_scopes) { unsigned lvl = m_trail_stack.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_trail_stack[new_lvl]; for (unsigned i = m_enabled_edges.size(); i > s.m_enabled_edges_lim; ) { --i; m_edges[m_enabled_edges[i]].disable(); } m_enabled_edges.shrink(s.m_enabled_edges_lim); unsigned old_num_edges = s.m_edges_lim; m_timestamp = s.m_old_timestamp; unsigned num_edges = m_edges.size(); SASSERT(old_num_edges <= num_edges); unsigned to_delete = num_edges - old_num_edges; for (unsigned i = 0; i < to_delete; i++) { const edge & e = m_edges.back(); TRACE("dl_bug", tout << "deleting edge:\n"; display_edge(tout, e);); dl_var source = e.get_source(); dl_var target = e.get_target(); SASSERT(static_cast(m_edges.size()) - 1 == m_out_edges[source].back()); SASSERT(static_cast(m_edges.size()) - 1 == m_in_edges[target].back()); m_out_edges[source].pop_back(); m_in_edges[target].pop_back(); m_edges.pop_back(); } m_trail_stack.shrink(new_lvl); SASSERT(check_invariant()); // SASSERT(is_feasible()); <<< I relaxed the condition in push(), so this assertion is not valid anymore. } // Make m_assignment[v] == zero // The whole assignment is adjusted in a way feasibility is preserved. // This method should only be invoked if the current assignment if feasible. void set_to_zero(dl_var v) { SASSERT(is_feasible()); if (!m_assignment[v].is_zero()) { numeral k = m_assignment[v]; typename assignment::iterator it = m_assignment.begin(); typename assignment::iterator end = m_assignment.end(); for (; it != end; ++it) { *it -= k; } SASSERT(is_feasible()); } } // // set assignments of v and w both to 0. // assumption: there are no prior dependencies between v and w. // so the graph is disconnected. // assumption: the current assignment is feasible. // void set_to_zero(dl_var v, dl_var w) { if (!m_assignment[v].is_zero()) { set_to_zero(v); } else { set_to_zero(w); } if (!m_assignment[v].is_zero() || !m_assignment[w].is_zero()) { enable_edge(add_edge(v, w, numeral(0), explanation())); enable_edge(add_edge(w, v, numeral(0), explanation())); SASSERT(is_feasible()); } } private: // Update the assignment of variable v, that is, // m_assignment[v] += inc // This method also stores the old value of v in the assignment stack. void acc_assignment(dl_var v, const numeral & inc) { TRACE("diff_logic_bug", tout << "update v: " << v << " += " << inc << " m_assignment[v] " << m_assignment[v] << "\n";); m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); m_assignment[v] += inc; } public: void inc_assignment(dl_var v, numeral const& inc) { m_assignment[v] += inc; } struct every_var_proc { bool operator()(dl_var v) const { return true; } }; void display(std::ostream & out) const { display_core(out, every_var_proc()); } void display_agl(std::ostream & out) const { uint_set vars; typename edges::const_iterator it = m_edges.begin(); typename edges::const_iterator end = m_edges.end(); for (; it != end; ++it) { edge const& e = *it; if (e.is_enabled()) { vars.insert(e.get_source()); vars.insert(e.get_target()); } } out << "digraph "" {\n"; unsigned n = m_assignment.size(); for (unsigned v = 0; v < n; v++) { if (vars.contains(v)) { out << "\"" << v << "\" [label=\"" << v << ":" << m_assignment[v] << "\"]\n"; } } it = m_edges.begin(); for (; it != end; ++it) { edge const& e = *it; if (e.is_enabled()) { out << "\"" << e.get_source() << "\"->\"" << e.get_target() << "\"[label=\"" << e.get_weight() << "\"]\n"; } } out << "}\n"; } template void display_core(std::ostream & out, FilterAssignmentProc p) const { display_edges(out); display_assignment(out, p); } void display_edges(std::ostream & out) const { typename edges::const_iterator it = m_edges.begin(); typename edges::const_iterator end = m_edges.end(); for (; it != end; ++it) { edge const& e = *it; if (e.is_enabled()) { display_edge(out, e); } } } void display_edge(std::ostream & out, edge_id id) const { display_edge(out, m_edges[id]); } void display_edge(std::ostream & out, const edge & e) const { out << e.get_explanation() << " (<= (- $" << e.get_target() << " $" << e.get_source() << ") " << e.get_weight() << ") " << e.get_timestamp() << "\n"; } template void display_assignment(std::ostream & out, FilterAssignmentProc p) const { unsigned n = m_assignment.size(); for (unsigned v = 0; v < n; v++) { if (p(v)) { out << "$" << v << " := " << m_assignment[v] << "\n"; } } } // Return true if there is an edge source --> target. // If there is such edge, then the weight is stored in w and the explanation in ex. bool get_edge_weight(dl_var source, dl_var target, numeral & w, explanation & ex) { edge_id_vector & edges = m_out_edges[source]; typename edge_id_vector::iterator it = edges.begin(); typename edge_id_vector::iterator end = edges.end(); bool found = false; for (; it != end; ++it) { edge_id e_id = *it; edge & e = m_edges[e_id]; if (e.is_enabled() && e.get_target() == target && (!found || e.get_weight() < w)) { w = e.get_weight(); ex = e.get_explanation(); found = true; } } return found; } // Return true if there is an edge source --> target (also counting disabled edges). // If there is such edge, return its edge_id in parameter id. bool get_edge_id(dl_var source, dl_var target, edge_id & id) const { edge_id_vector const & edges = m_out_edges[source]; typename edge_id_vector::const_iterator it = edges.begin(); typename edge_id_vector::const_iterator end = edges.end(); for (; it != end; ++it) { id = *it; edge const & e = m_edges[id]; if (e.get_target() == target) { return true; } } return false; } edges const & get_all_edges() const { return m_edges; } void get_neighbours_undirected(dl_var current, svector & neighbours) { neighbours.reset(); edge_id_vector & out_edges = m_out_edges[current]; typename edge_id_vector::iterator it = out_edges.begin(), end = out_edges.end(); for (; it != end; ++it) { edge_id e_id = *it; edge & e = m_edges[e_id]; SASSERT(e.get_source() == current); dl_var neighbour = e.get_target(); neighbours.push_back(neighbour); } edge_id_vector & in_edges = m_in_edges[current]; typename edge_id_vector::iterator it2 = in_edges.begin(), end2 = in_edges.end(); for (; it2 != end2; ++it2) { edge_id e_id = *it2; edge & e = m_edges[e_id]; SASSERT(e.get_target() == current); dl_var neighbour = e.get_source(); neighbours.push_back(neighbour); } } void dfs_undirected(dl_var start, svector & threads) { threads.reset(); threads.resize(get_num_nodes()); uint_set discovered, explored; svector nodes; discovered.insert(start); nodes.push_back(start); dl_var prev = start; while(!nodes.empty()) { dl_var current = nodes.back(); SASSERT(discovered.contains(current) && !explored.contains(current)); svector neighbours; get_neighbours_undirected(current, neighbours); SASSERT(!neighbours.empty()); bool found = false; for (unsigned i = 0; i < neighbours.size(); ++i) { dl_var next = neighbours[i]; DEBUG_CODE( edge_id id; SASSERT(get_edge_id(current, next, id) || get_edge_id(next, current, id));); if (!discovered.contains(next) && !explored.contains(next)) { TRACE("diff_logic", tout << "thread[" << prev << "] --> " << next << std::endl;); threads[prev] = next; prev = next; discovered.insert(next); nodes.push_back(next); found = true; break; } } SASSERT(!nodes.empty()); if (!found) { explored.insert(current); nodes.pop_back(); } } threads[prev] = start; } void bfs_undirected(dl_var start, svector & parents, svector & depths) { parents.reset(); parents.resize(get_num_nodes()); parents[start] = -1; depths.reset(); depths.resize(get_num_nodes()); uint_set visited; std::deque nodes; visited.insert(start); nodes.push_front(start); while(!nodes.empty()) { dl_var current = nodes.back(); nodes.pop_back(); SASSERT(visited.contains(current)); svector neighbours; get_neighbours_undirected(current, neighbours); SASSERT(!neighbours.empty()); for (unsigned i = 0; i < neighbours.size(); ++i) { dl_var next = neighbours[i]; DEBUG_CODE( edge_id id; SASSERT(get_edge_id(current, next, id) || get_edge_id(next, current, id));); if (!visited.contains(next)) { TRACE("diff_logic", tout << "parents[" << next << "] --> " << current << std::endl;); parents[next] = current; depths[next] = depths[current] + 1; visited.insert(next); nodes.push_front(next); } } } } template void enumerate_edges(dl_var source, dl_var target, Functor& f) { edge_id_vector & edges = m_out_edges[source]; typename edge_id_vector::iterator it = edges.begin(); typename edge_id_vector::iterator end = edges.end(); for (; it != end; ++it) { edge_id e_id = *it; edge const& e = m_edges[e_id]; if (e.get_target() == target) { f(e.get_weight(), e.get_explanation()); } } } void reset() { m_assignment .reset(); m_assignment_stack .reset(); m_edges .reset(); m_in_edges .reset(); m_out_edges .reset(); m_trail_stack .reset(); m_gamma .reset(); m_mark .reset(); m_parent .reset(); m_visited .reset(); m_heap .reset(); m_enabled_edges .reset(); m_activity .reset(); } // Compute strongly connected components connected by (normalized) zero edges. void compute_zero_edge_scc(int_vector & scc_id) { m_unfinished_set.reset(); m_dfs_time.reset(); scc_id.reset(); m_roots.reset(); m_unfinished.reset(); int n = m_assignment.size(); m_unfinished_set.resize(n, false); m_dfs_time.resize(n, -1); scc_id.resize(n, -1); m_next_dfs_time = 0; m_next_scc_id = 0; for (dl_var v = 0; v < n; v++) { if (m_dfs_time[v] == -1) { dfs(v, scc_id); } } TRACE("eq_scc", for (dl_var v = 0; v < n; v++) { tout << "$" << v << " -> " << scc_id[v] << "\n"; }); } void dfs(dl_var v, int_vector & scc_id) { m_dfs_time[v] = m_next_dfs_time; m_next_dfs_time++; m_unfinished_set[v] = true; m_unfinished.push_back(v); m_roots.push_back(v); numeral gamma; edge_id_vector & edges = m_out_edges[v]; typename edge_id_vector::iterator it = edges.begin(); typename edge_id_vector::iterator end = edges.end(); for (; it != end; ++it) { edge_id e_id = *it; edge & e = m_edges[e_id]; if (!e.is_enabled()) { continue; } SASSERT(e.get_source() == v); set_gamma(e, gamma); if (gamma.is_zero()) { dl_var target = e.get_target(); if (m_dfs_time[target] == -1) { dfs(target, scc_id); } else if (m_unfinished_set[target]) { SASSERT(!m_roots.empty()); while (m_dfs_time[m_roots.back()] > m_dfs_time[target]) { m_roots.pop_back(); SASSERT(!m_roots.empty()); } } } } if (v == m_roots.back()) { dl_var scc_elem; unsigned size = 0; do { scc_elem = m_unfinished.back(); m_unfinished.pop_back(); SASSERT(m_unfinished_set[scc_elem]); m_unfinished_set[scc_elem] = false; scc_id[scc_elem] = m_next_scc_id; size++; } while (scc_elem != v); // Ignore SCC with size 1 if (size == 1) { scc_id[scc_elem] = -1; } else { m_next_scc_id++; } m_roots.pop_back(); } } void compute_zero_succ(dl_var v, int_vector& succ) { unsigned n = m_assignment.size(); m_dfs_time.reset(); m_dfs_time.resize(n, -1); m_dfs_time[v] = 0; succ.push_back(v); numeral gamma; for (unsigned i = 0; i < succ.size(); ++i) { v = succ[i]; edge_id_vector & edges = m_out_edges[v]; typename edge_id_vector::iterator it = edges.begin(); typename edge_id_vector::iterator end = edges.end(); for (; it != end; ++it) { edge_id e_id = *it; edge & e = m_edges[e_id]; if (!e.is_enabled()) { continue; } SASSERT(e.get_source() == v); set_gamma(e, gamma); if (gamma.is_zero()) { dl_var target = e.get_target(); if (m_dfs_time[target] == -1) { succ.push_back(target); m_dfs_time[target] = 0; } } } } } numeral get_assignment(dl_var v) const { return m_assignment[v]; } void set_assignment(dl_var v, numeral const & n) { m_assignment[v] = n; } unsigned get_timestamp() const { return m_timestamp; } private: void inc_activity(edge_id e_id) { ++m_activity[e_id]; } bool check_explanation(unsigned num_edges, edge_id const* edges) { numeral w; for (unsigned i = 0; i < num_edges; ++i) { edge const& e = m_edges[edges[i]]; unsigned pred = (i>0)?(i-1):(num_edges-1); edge const& e1 = m_edges[edges[pred]]; if (e.get_target() != e1.get_source()) { TRACE("check_explanation", display_edge(tout, e); display_edge(tout, e1); ); return false; } w += e.get_weight(); } if (w.is_nonneg()) { TRACE("check_explanation", tout << "weight: " << w << "\n";); return false; } return true; } bool check_path(vector& potentials, svector& nodes, svector& edges) { // Debug: numeral potential0; for (unsigned i = 0; i < edges.size(); ++i) { potential0 += m_edges[edges[i]].get_weight(); if (potential0 != potentials[i] || nodes[i] != m_edges[edges[i]].get_source()) { TRACE("diff_logic_traverse", tout << "checking index " << i << " "; tout << "potential: " << potentials[i] << " "; display_edge(tout, m_edges[edges[i]]); ); return false; } } return true; } bool check_gamma(edge_id last_id) { edge_id e_id = last_id; numeral gamma2; do { gamma2 += m_edges[e_id].get_weight(); e_id = m_parent[m_edges[e_id].get_source()]; } while (e_id != last_id); return gamma2 == m_gamma[m_edges[last_id].get_source()]; } // Auxliary structure used for breadth-first search. struct bfs_elem { dl_var m_var; int m_parent_idx; edge_id m_edge_id; bfs_elem(dl_var v, int parent_idx, edge_id e): m_var(v), m_parent_idx(parent_idx), m_edge_id(e) { } }; public: // Find the shortest path from source to target using (normalized) zero edges with timestamp less than the given timestamp. // The functor f is applied on every explanation attached to the edges in the shortest path. // Return true if the path exists, false otherwise. template bool find_shortest_zero_edge_path(dl_var source, dl_var target, unsigned timestamp, Functor & f) { svector bfs_todo; svector bfs_mark; bfs_mark.resize(m_assignment.size(), false); bfs_todo.push_back(bfs_elem(source, -1, null_edge_id)); bfs_mark[source] = true; unsigned m_head = 0; numeral gamma; while (m_head < bfs_todo.size()) { bfs_elem & curr = bfs_todo[m_head]; int parent_idx = m_head; m_head++; dl_var v = curr.m_var; TRACE("dl_bfs", tout << "processing: " << v << "\n";); edge_id_vector & edges = m_out_edges[v]; typename edge_id_vector::iterator it = edges.begin(); typename edge_id_vector::iterator end = edges.end(); for (; it != end; ++it) { edge_id e_id = *it; edge & e = m_edges[e_id]; SASSERT(e.get_source() == v); if (!e.is_enabled()) { continue; } set_gamma(e, gamma); TRACE("dl_bfs", tout << "processing edge: "; display_edge(tout, e); tout << "gamma: " << gamma << "\n";); if (gamma.is_zero() && e.get_timestamp() < timestamp) { dl_var curr_target = e.get_target(); TRACE("dl_bfs", tout << "curr_target: " << curr_target << ", mark: " << static_cast(bfs_mark[curr_target]) << "\n";); if (curr_target == target) { TRACE("dl_bfs", tout << "found path\n";); TRACE("dl_eq_bug", tout << "path: " << source << " --> " << target << "\n"; display_edge(tout, e); int tmp_parent_idx = parent_idx; for (;;) { bfs_elem & curr = bfs_todo[tmp_parent_idx]; if (curr.m_edge_id == null_edge_id) { break; } else { edge & e = m_edges[curr.m_edge_id]; display_edge(tout, e); tmp_parent_idx = curr.m_parent_idx; } tout.flush(); }); TRACE("dl_eq_bug", display_edge(tout, e);); f(e.get_explanation()); for (;;) { SASSERT(parent_idx >= 0); bfs_elem & curr = bfs_todo[parent_idx]; if (curr.m_edge_id == null_edge_id) { return true; } else { edge & e = m_edges[curr.m_edge_id]; TRACE("dl_eq_bug", display_edge(tout, e);); f(e.get_explanation()); parent_idx = curr.m_parent_idx; } } } else { if (!bfs_mark[curr_target]) { bfs_todo.push_back(bfs_elem(curr_target, parent_idx, e_id)); bfs_mark[curr_target] = true; } } } } } return false; } // // Theory propagation: // Given a (newly) added edge id, find the ids of un-asserted edges that // that are subsumed by the id. // Separately, reproduce explanations for those ids. // // The algorithm works in the following way: // 1. Let e = source -- weight --> target be the edge at id. // 2. Compute successors (over the assigned edges) of source, // those traversing source-target and those leaving source over different edges. // compute forward potential of visited nodes. // queue up nodes that are visited, and require the source->target edge. // 3. Compute pre-decessors (over the assigned edges) of target, // those traversing source-target, and those entering target // without visiting source. Maintain only nodes that enter target // compute backward potential of visited nodes. // Queue up nodes that are visited, and require the source->target edge. // 4. traverse the smaller of the two lists. // check if there is an edge between the two sets such that // the weight of the edge is >= than the sum of the two potentials - weight // (since 'weight' is added twice in the traversal. // private: struct dfs_state { class hp_lt { assignment& m_delta; char_vector& m_mark; public: hp_lt(assignment& asgn, char_vector& m) : m_delta(asgn),m_mark(m) {} bool operator()(dl_var v1, dl_var v2) const { numeral const& delta1 = m_delta[v1]; numeral const& delta2 = m_delta[v2]; return delta1 < delta2 || (delta1 == delta2 && m_mark[v1] == DL_PROP_IRRELEVANT && m_mark[v2] == DL_PROP_RELEVANT); } }; assignment m_delta; int_vector m_visited; int_vector m_parent; heap m_heap; unsigned m_num_edges; dfs_state(char_vector& mark): m_heap(1024, hp_lt(m_delta, mark)), m_num_edges(0) {} void re_init(unsigned sz) { m_delta.resize(sz, numeral(0)); m_parent.resize(sz, 0); m_visited.reset(); m_num_edges = 0; m_heap.set_bounds(sz); SASSERT(m_heap.empty()); } void add_size(unsigned n) { m_num_edges += n; } unsigned get_size() const { return m_num_edges; } bool contains(dl_var v) const { // TBD can be done better using custom marking. for (unsigned i = 0; i < m_visited.size(); ++i) { if (v == m_visited[i]) { return true; } } return false; } }; dfs_state m_fw; dfs_state m_bw; void fix_sizes() { m_fw.re_init(m_assignment.size()); m_bw.re_init(m_assignment.size()); } numeral get_reduced_weight(dfs_state& state, dl_var n, edge const& e) { numeral gamma; set_gamma(e, gamma); return state.m_delta[n] + gamma; } template void find_relevant(dfs_state& state, edge_id id) { SASSERT(state.m_visited.empty()); SASSERT(state.m_heap.empty()); numeral delta; edge const& e_init = m_edges[id]; vector const& edges = is_fw?m_out_edges:m_in_edges; dl_var target = is_fw?e_init.get_target():e_init.get_source(); dl_var source = is_fw?e_init.get_source():e_init.get_target(); SASSERT(marks_are_clear()); dl_prop_search_mark source_mark = DL_PROP_IRRELEVANT; dl_prop_search_mark target_mark = DL_PROP_RELEVANT; m_mark[source] = source_mark; m_mark[target] = target_mark; state.m_delta[source] = numeral(0); state.m_delta[target] = get_reduced_weight(state, source, e_init); SASSERT(state.m_delta[source] <= state.m_delta[target]); state.m_heap.insert(source); state.m_heap.insert(target); unsigned num_relevant = 1; TRACE("diff_logic", display(tout); ); while (!state.m_heap.empty() && num_relevant > 0) { ++m_stats.m_implied_literal_cost; source = state.m_heap.erase_min(); source_mark = static_cast(m_mark[source]); SASSERT(source_mark == DL_PROP_RELEVANT || source_mark == DL_PROP_IRRELEVANT); state.m_visited.push_back(source); if (source_mark == DL_PROP_RELEVANT) { --num_relevant; state.add_size(edges[source].size()); m_mark[source] = DL_PROP_PROCESSED_RELEVANT; } else { m_mark[source] = DL_PROP_PROCESSED_IRRELEVANT; } TRACE("diff_logic", tout << "source: " << source << "\n";); typename edge_id_vector::const_iterator it = edges[source].begin(); typename edge_id_vector::const_iterator end = edges[source].end(); for (; it != end; ++it) { edge_id e_id = *it; edge const& e = m_edges[e_id]; if (&e == &e_init) { continue; } SASSERT(!is_fw || e.get_source() == source); SASSERT(is_fw || e.get_target() == source); if (!e.is_enabled()) { continue; } TRACE("diff_logic", display_edge(tout, e);); target = is_fw?e.get_target():e.get_source(); delta = get_reduced_weight(state, source, e); SASSERT(delta >= state.m_delta[source]); target_mark = static_cast(m_mark[target]); switch(target_mark) { case DL_PROP_UNMARKED: { state.m_delta[target] = delta; m_mark[target] = source_mark; state.m_heap.insert(target); if (source_mark == DL_PROP_RELEVANT) { ++num_relevant; } state.m_parent[target] = e_id; break; } case DL_PROP_RELEVANT: case DL_PROP_IRRELEVANT: { numeral const& old_delta = state.m_delta[target]; if (delta < old_delta || (delta == old_delta && source_mark == DL_PROP_IRRELEVANT && target_mark == DL_PROP_RELEVANT)) { state.m_delta[target] = delta; m_mark[target] = source_mark; state.m_heap.decreased(target); if (target_mark == DL_PROP_IRRELEVANT && source_mark == DL_PROP_RELEVANT) { ++num_relevant; } if (target_mark == DL_PROP_RELEVANT && source_mark == DL_PROP_IRRELEVANT) { --num_relevant; } state.m_parent[target] = e_id; } break; } case DL_PROP_PROCESSED_RELEVANT: TRACE("diff_logic", tout << delta << " ?> " << state.m_delta[target] << "\n";); SASSERT(delta >= state.m_delta[target]); SASSERT(!(delta == state.m_delta[target] && source_mark == DL_PROP_IRRELEVANT)); break; case DL_PROP_PROCESSED_IRRELEVANT: TRACE("diff_logic", tout << delta << " ?> " << state.m_delta[target] << "\n";); SASSERT(delta >= state.m_delta[target]); break; default: UNREACHABLE(); } } } // // Clear marks using m_visited and m_heap. // unsigned sz = state.m_visited.size(); for (unsigned i = 0; i < sz; ) { dl_var v = state.m_visited[i]; source_mark = static_cast(m_mark[v]); m_mark[v] = DL_PROP_UNMARKED; SASSERT(source_mark == DL_PROP_PROCESSED_RELEVANT || source_mark == DL_PROP_PROCESSED_IRRELEVANT); if (source_mark == DL_PROP_PROCESSED_RELEVANT) { ++i; } else { state.m_visited[i] = state.m_visited[--sz]; state.m_visited.resize(sz); } } TRACE("diff_logic", { tout << (is_fw?"is_fw":"is_bw") << ": "; for (unsigned i = 0; i < state.m_visited.size(); ++i) { tout << state.m_visited[i] << " "; } tout << "\n"; }); typename heap::const_iterator it = state.m_heap.begin(); typename heap::const_iterator end = state.m_heap.end(); for (; it != end; ++it) { SASSERT(m_mark[*it] != DL_PROP_UNMARKED); m_mark[*it] = DL_PROP_UNMARKED;; } state.m_heap.reset(); SASSERT(marks_are_clear()); } void find_subsumed(edge_id bridge_edge, dfs_state& src, dfs_state& tgt, svector& subsumed) { edge const& e0 = m_edges[bridge_edge]; dl_var a = e0.get_source(); dl_var b = e0.get_target(); numeral n0 = m_assignment[b] - m_assignment[a] - e0.get_weight(); vector const& edges = m_out_edges; TRACE("diff_logic", tout << "$" << a << " a:" << m_assignment[a] << " $" << b << " b: " << m_assignment[b] << " e0: " << e0.get_weight() << " n0: " << n0 << "\n"; display_edge(tout, e0); ); for (unsigned i = 0; i < src.m_visited.size(); ++i) { dl_var c = src.m_visited[i]; typename edge_id_vector::const_iterator it = edges[c].begin(); typename edge_id_vector::const_iterator end = edges[c].end(); numeral n1 = n0 + src.m_delta[c] - m_assignment[c]; for (; it != end; ++it) { edge_id e_id = *it; edge const& e1 = m_edges[e_id]; SASSERT(c == e1.get_source()); if (e1.is_enabled()) { continue; } dl_var d = e1.get_target(); numeral n2 = n1 + tgt.m_delta[d] + m_assignment[d]; if (tgt.contains(d) && n2 <= e1.get_weight()) { TRACE("diff_logic", tout << "$" << c << " delta_c: " << src.m_delta[c] << " c: " << m_assignment[c] << "\n"; tout << "$" << d << " delta_d: " << src.m_delta[d] << " d: " << m_assignment[d] << " n2: " << n2 << " e1: " << e1.get_weight() << "\n"; display_edge(tout << "found: ", e1);); ++m_stats.m_num_implied_literals; subsumed.push_back(e_id); } } } } public: void find_subsumed(edge_id id, svector& subsumed) { fix_sizes(); find_relevant(m_fw, id); find_relevant(m_bw, id); find_subsumed(id, m_bw, m_fw, subsumed); m_fw.m_visited.reset(); m_bw.m_visited.reset(); if (!subsumed.empty()) { TRACE("diff_logic", display(tout); tout << "subsumed\n"; for (unsigned i = 0; i < subsumed.size(); ++i) { display_edge(tout, m_edges[subsumed[i]]); }); } } // Find edges that are directly subsumed by id. void find_subsumed1(edge_id id, svector& subsumed) { edge const& e1 = m_edges[id]; dl_var src = e1.get_source(); dl_var dst = e1.get_target(); edge_id_vector& out_edges = m_out_edges[src]; edge_id_vector& in_edges = m_in_edges[dst]; numeral w = e1.get_weight(); typename edge_id_vector::const_iterator it, end; if (out_edges.size() < in_edges.size()) { end = out_edges.end(); for (it = out_edges.begin(); it != end; ++it) { ++m_stats.m_implied_literal_cost; edge_id e_id = *it; edge const& e2 = m_edges[e_id]; if (e_id != id && !e2.is_enabled() && e2.get_target() == dst && e2.get_weight() >= w) { subsumed.push_back(e_id); ++m_stats.m_num_implied_literals; } } } else { end = in_edges.end(); for (it = in_edges.begin(); it != end; ++it) { ++m_stats.m_implied_literal_cost; edge_id e_id = *it; edge const& e2 = m_edges[e_id]; if (e_id != id && !e2.is_enabled() && e2.get_source() == src && e2.get_weight() >= w) { subsumed.push_back(e_id); ++m_stats.m_num_implied_literals; } } } } // // Find edges that are subsumed by id, or is an edge between // a predecessor of id's source and id's destination, or // is an edge between a successor of id's dst, and id's source. // // src - id -> dst // - - // src' dst' // // so searching for: // . src - id' -> dst // . src' - id' -> dst // . src - id' -> dst' // void find_subsumed2(edge_id id, svector& subsumed) { edge const& e1 = m_edges[id]; dl_var src = e1.get_source(); dl_var dst = e1.get_target(); numeral w = e1.get_weight(); numeral w2; find_subsumed1(id, subsumed); typename edge_id_vector::const_iterator it, end, it3, end3; it = m_in_edges[src].begin(); end = m_in_edges[src].end(); for (; it != end; ++it) { edge_id e_id = *it; edge const& e2 = m_edges[e_id]; if (!e2.is_enabled() || e2.get_source() == dst) { continue; } w2 = e2.get_weight() + w; it3 = m_out_edges[e2.get_source()].begin(); end3 = m_out_edges[e2.get_source()].end(); for (; it3 != end3; ++it3) { ++m_stats.m_implied_literal_cost; edge_id e_id3 = *it3; edge const& e3 = m_edges[e_id3]; if (e3.is_enabled() || e3.get_target() != dst) { continue; } if (e3.get_weight() >= w2) { subsumed.push_back(e_id3); ++m_stats.m_num_implied_literals; } } } it = m_out_edges[dst].begin(); end = m_out_edges[dst].end(); for (; it != end; ++it) { edge_id e_id = *it; edge const& e2 = m_edges[e_id]; if (!e2.is_enabled() || e2.get_target() == src) { continue; } w2 = e2.get_weight() + w; it3 = m_in_edges[e2.get_target()].begin(); end3 = m_in_edges[e2.get_target()].end(); for (; it3 != end3; ++it3) { ++m_stats.m_implied_literal_cost; edge_id e_id3 = *it3; edge const& e3 = m_edges[e_id3]; if (e3.is_enabled() || e3.get_source() != src) { continue; } if (e3.get_weight() >= w2) { subsumed.push_back(e_id3); ++m_stats.m_num_implied_literals; } } } } template void explain_subsumed_lazy(edge_id bridge_id, edge_id subsumed_id, Functor& f) { edge const& e1 = m_edges[bridge_id]; edge const& e2 = m_edges[subsumed_id]; dl_var src2 = e2.get_source(); dl_var dst2 = e2.get_target(); unsigned timestamp = e1.get_timestamp(); // // Find path from src2 to dst2 with edges having timestamps no greater than // timestamp, and of length no longer than weight of e2. // // use basic O(m*n) algorithm that traverses each edge once per node. // ++m_stats.m_num_helpful_implied_literals; SASSERT(m_heap.empty()); SASSERT(e1.is_enabled()); m_gamma[src2].reset(); m_gamma[dst2] = e2.get_weight(); m_heap.insert(src2); m_visited.push_back(src2); TRACE("diff_logic", display_edge(tout << "bridge: ", e1); display_edge(tout << "subsumed: ", e2); display(tout); ); while (true) { SASSERT(!m_heap.empty()); dl_var v = m_heap.erase_min(); m_mark[v] = DL_PROCESSED; TRACE("diff_logic", tout << v << "\n";); typename edge_id_vector::iterator it = m_out_edges[v].begin(); typename edge_id_vector::iterator end = m_out_edges[v].end(); for (; it != end; ++it) { edge_id e_id = *it; edge const& e = m_edges[e_id]; if (!e.is_enabled() || e.get_timestamp() > timestamp) { continue; } dl_var w = e.get_target(); numeral gamma = m_gamma[v] + e.get_weight(); if ((m_mark[w] != DL_UNMARKED) && m_gamma[w] <= gamma) { continue; } m_gamma[w] = gamma; m_parent[w] = e_id; TRACE("diff_logic", tout << w << " : " << gamma << " " << e2.get_weight() << "\n";); if (w == dst2 && gamma <= e2.get_weight()) { // found path. reset_marks(); m_heap.reset(); unsigned length = 0; do { inc_activity(m_parent[w]); edge const& ee = m_edges[m_parent[w]]; f(ee.get_explanation()); w = ee.get_source(); ++length; } while (w != src2); return; } switch(m_mark[w]) { case DL_UNMARKED: m_visited.push_back(w); // fall through case DL_PROCESSED: m_mark[w] = DL_FOUND; m_heap.insert(w); break; case DL_FOUND: m_heap.decreased(w); break; } } } UNREACHABLE(); } }; #endif /* DIFF_LOGIC_H_ */ z3-z3-4.4.1/src/smt/dyn_ack.cpp000066400000000000000000000501671260446376700161440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dyn_ack.cpp Abstract: Dynamic Ackermann's reduction Author: Leonardo de Moura (leonardo) 2007-04-24. Revision History: --*/ #include"smt_context.h" #include"dyn_ack.h" #include"ast_pp.h" namespace smt { /** \brief Justification for dynamic ackermann clause */ class dyn_ack_justification : public justification { app * m_app1; app * m_app2; public: dyn_ack_justification(app * n1, app * n2): justification(false), // dyn_ack_justifications are not stored in regions. m_app1(n1), m_app2(n2) { SASSERT(m_app1->get_num_args() == m_app2->get_num_args()); SASSERT(m_app1->get_decl() == m_app2->get_decl()); SASSERT(m_app1->get_num_args() > 0); SASSERT(m_app1->get_id() < m_app2->get_id()); } virtual char const * get_name() const { return "dyn-ack"; } virtual void get_antecedents(conflict_resolution & cr) { } virtual void display_debug_info(conflict_resolution & cr, std::ostream & out) { ast_manager & m = cr.get_manager(); out << "m_app1:\n" << mk_pp(m_app1, m) << "\n"; out << "m_app2:\n" << mk_pp(m_app2, m) << "\n"; } /** \brief Make a hypothesis (= lhs rhs) for the given equality. The arguments of the given equality eq may have been swapped. That is, \c eq is of the form (= rhs lhs). In this case, we also apply a symmetry rule. \remark if negate == true, then the hypothesis is actually (not (= lhs rhs)) */ proof * mk_hypothesis(ast_manager & m, app * eq, bool negate, expr * lhs, expr * rhs) { SASSERT(m.is_eq(eq) || m.is_iff(eq)); SASSERT((eq->get_arg(0) == lhs && eq->get_arg(1) == rhs) || (eq->get_arg(0) == rhs && eq->get_arg(1) == lhs)); app * h = negate ? m.mk_not(eq) : eq; if (eq->get_arg(0) == lhs && eq->get_arg(1) == rhs) { return m.mk_hypothesis(h); } else { return m.mk_symmetry(m.mk_hypothesis(h)); } } virtual proof * mk_proof(conflict_resolution & cr) { ast_manager & m = cr.get_manager(); context & ctx = cr.get_context(); unsigned num_args = m_app1->get_num_args(); ptr_buffer prs; ptr_buffer lits; for (unsigned i = 0; i < num_args; i++) { expr * arg1 = m_app1->get_arg(i); expr * arg2 = m_app2->get_arg(i); if (arg1 != arg2) { app * eq = ctx.mk_eq_atom(arg1, arg2); app * neq = m.mk_not(eq); if (std::find(lits.begin(), lits.end(), neq) == lits.end()) { lits.push_back(neq); prs.push_back(mk_hypothesis(m, eq, false, arg1, arg2)); } } } proof * antecedents[2]; antecedents[0] = m.mk_congruence(m_app1, m_app2, prs.size(), prs.c_ptr()); app * eq = ctx.mk_eq_atom(m_app1, m_app2); antecedents[1] = mk_hypothesis(m, eq, true, m_app1, m_app2); proof * false_pr = m.mk_unit_resolution(2, antecedents); lits.push_back(eq); SASSERT(lits.size() >= 2); app * lemma = m.mk_or(lits.size(), lits.c_ptr()); TRACE("dyn_ack", tout << mk_pp(lemma, m) << "\n";); TRACE("dyn_ack", tout << mk_pp(false_pr, m) << "\n";); return m.mk_lemma(false_pr, lemma); } }; dyn_ack_manager::dyn_ack_manager(context & ctx, dyn_ack_params & p): m_context(ctx), m_manager(ctx.get_manager()), m_params(p) { } dyn_ack_manager::~dyn_ack_manager() { reset_app_pairs(); reset_app_triples(); } void dyn_ack_manager::reset_app_pairs() { svector::iterator it = m_app_pairs.begin(); svector::iterator end = m_app_pairs.end(); for (; it != end; ++it) { app_pair & p = *it; m_manager.dec_ref(p.first); m_manager.dec_ref(p.second); } m_app_pairs.reset(); } void dyn_ack_manager::init_search_eh() { m_app_pair2num_occs.reset(); reset_app_pairs(); m_to_instantiate.reset(); m_qhead = 0; m_num_instances = 0; m_num_propagations_since_last_gc = 0; m_triple.m_app2num_occs.reset(); reset_app_triples(); m_triple.m_to_instantiate.reset(); m_triple.m_qhead = 0; } void dyn_ack_manager::cg_eh(app * n1, app * n2) { SASSERT(n1->get_decl() == n2->get_decl()); SASSERT(n1->get_num_args() == n2->get_num_args()); SASSERT(n1 != n2); if (m_manager.is_eq(n1)) return; if (n1->get_id() > n2->get_id()) std::swap(n1,n2); app_pair p(n1, n2); if (m_instantiated.contains(p)) return; unsigned num_occs = 0; if (m_app_pair2num_occs.find(n1, n2, num_occs)) { TRACE("dyn_ack", tout << "used_cg_eh:\n" << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\nnum_occs: " << num_occs << "\n";); num_occs++; } else { num_occs = 1; m_manager.inc_ref(n1); m_manager.inc_ref(n2); m_app_pairs.push_back(p); } SASSERT(num_occs > 0); m_app_pair2num_occs.insert(n1, n2, num_occs); #ifdef Z3DEBUG unsigned num_occs2 = 0; SASSERT(m_app_pair2num_occs.find(n1, n2, num_occs2) && num_occs == num_occs2); #endif if (num_occs == m_params.m_dack_threshold) { TRACE("dyn_ack", tout << "found candidate:\n" << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\nnum_occs: " << num_occs << "\n";); m_to_instantiate.push_back(p); } } void dyn_ack_manager::eq_eh(app * n1, app * n2, app* r) { if (n1 == n2 || r == n1 || r == n2 || m_manager.is_bool(n1)) { return; } if (n1->get_id() > n2->get_id()) std::swap(n1,n2); TRACE("dyn_ack", tout << mk_pp(n1, m_manager) << " = " << mk_pp(n2, m_manager) << " = " << mk_pp(r, m_manager) << "\n";); app_triple tr(n1, n2, r); if (m_triple.m_instantiated.contains(tr)) return; unsigned num_occs = 0; if (m_triple.m_app2num_occs.find(n1, n2, r, num_occs)) { TRACE("dyn_ack", tout << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << mk_pp(r, m_manager) << "\n" << "\nnum_occs: " << num_occs << "\n";); num_occs++; } else { num_occs = 1; m_manager.inc_ref(n1); m_manager.inc_ref(n2); m_manager.inc_ref(r); m_triple.m_apps.push_back(tr); } SASSERT(num_occs > 0); m_triple.m_app2num_occs.insert(n1, n2, r, num_occs); #ifdef Z3DEBUG unsigned num_occs2 = 0; SASSERT(m_triple.m_app2num_occs.find(n1, n2, r, num_occs2) && num_occs == num_occs2); #endif if (num_occs == m_params.m_dack_threshold) { TRACE("dyn_ack", tout << "found candidate:\n" << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\n" << mk_pp(r, m_manager) << "\nnum_occs: " << num_occs << "\n";); m_triple.m_to_instantiate.push_back(tr); } } struct app_pair_lt { typedef std::pair app_pair; typedef obj_pair_map app_pair2num_occs; app_pair2num_occs & m_app_pair2num_occs; app_pair_lt(app_pair2num_occs & m): m_app_pair2num_occs(m) { } bool operator()(app_pair const & p1, app_pair const & p2) const { unsigned n1 = 0; unsigned n2 = 0; m_app_pair2num_occs.find(p1.first, p1.second, n1); m_app_pair2num_occs.find(p2.first, p2.second, n2); SASSERT(n1 > 0); SASSERT(n2 > 0); return n1 > n2; } }; void dyn_ack_manager::gc() { TRACE("dyn_ack", tout << "dyn_ack GC\n";); unsigned num_deleted = 0; m_to_instantiate.reset(); m_qhead = 0; svector::iterator it = m_app_pairs.begin(); svector::iterator end = m_app_pairs.end(); svector::iterator it2 = it; for (; it != end; ++it) { app_pair & p = *it; if (m_instantiated.contains(p)) { TRACE("dyn_ack", tout << "1) erasing:\n" << mk_pp(p.first, m_manager) << "\n" << mk_pp(p.second, m_manager) << "\n";); m_manager.dec_ref(p.first); m_manager.dec_ref(p.second); SASSERT(!m_app_pair2num_occs.contains(p.first, p.second)); continue; } unsigned num_occs = 0; m_app_pair2num_occs.find(p.first, p.second, num_occs); // The following invariant is not true. p.first and // p.second may have been instantiated, and removed from // m_app_pair2num_occs, but not from m_app_pairs. // // SASSERT(num_occs > 0); num_occs = static_cast(num_occs * m_params.m_dack_gc_inv_decay); if (num_occs <= 1) { num_deleted++; TRACE("dyn_ack", tout << "2) erasing:\n" << mk_pp(p.first, m_manager) << "\n" << mk_pp(p.second, m_manager) << "\n";); m_app_pair2num_occs.erase(p.first, p.second); m_manager.dec_ref(p.first); m_manager.dec_ref(p.second); continue; } *it2 = p; ++it2; SASSERT(num_occs > 0); m_app_pair2num_occs.insert(p.first, p.second, num_occs); if (num_occs >= m_params.m_dack_threshold) m_to_instantiate.push_back(p); } m_app_pairs.set_end(it2); app_pair_lt f(m_app_pair2num_occs); // app_pair_lt is not a total order on pairs of expressions. // So, we should use stable_sort to avoid different behavior in different platforms. std::stable_sort(m_to_instantiate.begin(), m_to_instantiate.end(), f); // IF_VERBOSE(10, if (num_deleted > 0) verbose_stream() << "dynamic ackermann GC: " << num_deleted << "\n";); } class dyn_ack_clause_del_eh : public clause_del_eh { dyn_ack_manager & m_manager; public: dyn_ack_clause_del_eh(dyn_ack_manager & m): m_manager(m) { } virtual ~dyn_ack_clause_del_eh() {} virtual void operator()(ast_manager & m, clause * cls) { m_manager.del_clause_eh(cls); dealloc(this); } }; void dyn_ack_manager::del_clause_eh(clause * cls) { TRACE("dyn_ack", tout << "del_clause_eh: "; m_context.display_clause(tout, cls); tout << "\n";); m_context.m_stats.m_num_del_dyn_ack++; app_pair p((app*)0,(app*)0); if (m_clause2app_pair.find(cls, p)) { SASSERT(p.first && p.second); m_instantiated.erase(p); SASSERT(!m_app_pair2num_occs.contains(p.first, p.second)); return; } app_triple tr(0,0,0); if (m_triple.m_clause2apps.find(cls, tr)) { SASSERT(tr.first && tr.second && tr.third); m_triple.m_instantiated.erase(tr); SASSERT(!m_triple.m_app2num_occs.contains(tr.first, tr.second, tr.third)); return; } } void dyn_ack_manager::propagate_eh() { if (m_params.m_dack == DACK_DISABLED) return; m_num_propagations_since_last_gc++; if (m_num_propagations_since_last_gc > m_params.m_dack_gc) { gc(); m_num_propagations_since_last_gc = 0; } unsigned max_instances = static_cast(m_context.get_num_conflicts() * m_params.m_dack_factor); while (m_num_instances < max_instances && m_qhead < m_to_instantiate.size()) { app_pair & p = m_to_instantiate[m_qhead]; m_qhead++; m_num_instances++; instantiate(p.first, p.second); } while (m_num_instances < max_instances && m_triple.m_qhead < m_triple.m_to_instantiate.size()) { app_triple & p = m_triple.m_to_instantiate[m_triple.m_qhead]; m_triple.m_qhead++; m_num_instances++; instantiate(p.first, p.second, p.third); } } literal dyn_ack_manager::mk_eq(expr * n1, expr * n2) { app * eq = m_context.mk_eq_atom(n1, n2); m_context.internalize(eq, true); literal l = m_context.get_literal(eq); TRACE("dyn_ack", tout << "eq:\n" << mk_pp(eq, m_manager) << "\nliteral: "; m_context.display_literal(tout, l); tout << "\n";); return l; } void dyn_ack_manager::instantiate(app * n1, app * n2) { SASSERT(m_params.m_dack != DACK_DISABLED); SASSERT(n1->get_decl() == n2->get_decl()); SASSERT(n1->get_num_args() == n2->get_num_args()); SASSERT(n1 != n2); m_context.m_stats.m_num_dyn_ack++; TRACE("dyn_ack_inst", tout << "dyn_ack: " << n1->get_id() << " " << n2->get_id() << "\n";); TRACE("dyn_ack", tout << "expanding Ackermann's rule for:\n" << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\n";); unsigned num_args = n1->get_num_args(); literal_buffer lits; for (unsigned i = 0; i < num_args; i++) { expr * arg1 = n1->get_arg(i); expr * arg2 = n2->get_arg(i); if (arg1 != arg2) lits.push_back(~mk_eq(arg1, arg2)); } app_pair p(n1, n2); SASSERT(m_app_pair2num_occs.contains(n1, n2)); m_app_pair2num_occs.erase(n1, n2); // pair n1,n2 is still in m_app_pairs m_instantiated.insert(p); lits.push_back(mk_eq(n1, n2)); clause_del_eh * del_eh = alloc(dyn_ack_clause_del_eh, *this); justification * js = 0; if (m_manager.proofs_enabled()) js = alloc(dyn_ack_justification, n1, n2); clause * cls = m_context.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, del_eh); if (!cls) { dealloc(del_eh); return; } TRACE("dyn_ack_clause", tout << "new clause:\n"; m_context.display_clause_detail(tout, cls); tout << "\n";); m_clause2app_pair.insert(cls, p); } void dyn_ack_manager::reset() { init_search_eh(); m_instantiated.reset(); m_clause2app_pair.reset(); m_triple.m_instantiated.reset(); m_triple.m_clause2apps.reset(); } void dyn_ack_manager::reset_app_triples() { svector::iterator it = m_triple.m_apps.begin(); svector::iterator end = m_triple.m_apps.end(); for (; it != end; ++it) { app_triple & p = *it; m_manager.dec_ref(p.first); m_manager.dec_ref(p.second); m_manager.dec_ref(p.third); } m_triple.m_apps.reset(); } void dyn_ack_manager::instantiate(app * n1, app * n2, app* r) { SASSERT(m_params.m_dack != DACK_DISABLED); SASSERT(n1 != n2 && n1 != r && n2 != r); m_context.m_stats.m_num_dyn_ack++; TRACE("dyn_ack_inst", tout << "dyn_ack: " << n1->get_id() << " " << n2->get_id() << " " << r->get_id() << "\n";); TRACE("dyn_ack", tout << "expanding Ackermann's rule for:\n" << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\n" << mk_pp(r, m_manager) << "\n"; ); app_triple tr(n1, n2, r); SASSERT(m_triple.m_app2num_occs.contains(n1, n2, r)); m_triple.m_app2num_occs.erase(n1, n2, r); // pair n1,n2 is still in m_triple.m_apps m_triple.m_instantiated.insert(tr); literal_buffer lits; lits.push_back(~mk_eq(n1, r)); lits.push_back(~mk_eq(n2, r)); lits.push_back(mk_eq(n1, n2)); clause_del_eh * del_eh = alloc(dyn_ack_clause_del_eh, *this); justification * js = 0; if (m_manager.proofs_enabled()) js = alloc(dyn_ack_justification, n1, n2); clause * cls = m_context.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, del_eh); if (!cls) { dealloc(del_eh); return; } TRACE("dyn_ack_clause", tout << "new clause:\n"; m_context.display_clause_detail(tout, cls); tout << "\n";); m_triple.m_clause2apps.insert(cls, tr); } struct app_triple_lt { typedef triple app_triple; typedef obj_triple_map app_triple2num_occs; app_triple2num_occs & m_app_triple2num_occs; app_triple_lt(app_triple2num_occs & m): m_app_triple2num_occs(m) { } bool operator()(app_triple const & p1, app_triple const & p2) const { unsigned n1 = 0; unsigned n2 = 0; m_app_triple2num_occs.find(p1.first, p1.second, p1.third, n1); m_app_triple2num_occs.find(p2.first, p2.second, p2.third, n2); SASSERT(n1 > 0); SASSERT(n2 > 0); return n1 > n2; } }; void dyn_ack_manager::gc_triples() { TRACE("dyn_ack", tout << "dyn_ack GC\n";); unsigned num_deleted = 0; m_triple.m_to_instantiate.reset(); m_triple.m_qhead = 0; svector::iterator it = m_triple.m_apps.begin(); svector::iterator end = m_triple.m_apps.end(); svector::iterator it2 = it; for (; it != end; ++it) { app_triple & p = *it; if (m_triple.m_instantiated.contains(p)) { TRACE("dyn_ack", tout << "1) erasing:\n" << mk_pp(p.first, m_manager) << "\n" << mk_pp(p.second, m_manager) << "\n";); m_manager.dec_ref(p.first); m_manager.dec_ref(p.second); m_manager.dec_ref(p.third); SASSERT(!m_triple.m_app2num_occs.contains(p.first, p.second, p.third)); continue; } unsigned num_occs = 0; m_triple.m_app2num_occs.find(p.first, p.second, p.third, num_occs); // The following invariant is not true. p.first and // p.second may have been instantiated, and removed from // m_app_triple2num_occs, but not from m_app_triples. // // SASSERT(num_occs > 0); num_occs = static_cast(num_occs * m_params.m_dack_gc_inv_decay); if (num_occs <= 1) { num_deleted++; TRACE("dyn_ack", tout << "2) erasing:\n" << mk_pp(p.first, m_manager) << "\n" << mk_pp(p.second, m_manager) << "\n";); m_triple.m_app2num_occs.erase(p.first, p.second, p.third); m_manager.dec_ref(p.first); m_manager.dec_ref(p.second); m_manager.dec_ref(p.third); continue; } *it2 = p; ++it2; SASSERT(num_occs > 0); m_triple.m_app2num_occs.insert(p.first, p.second, p.third, num_occs); if (num_occs >= m_params.m_dack_threshold) m_triple.m_to_instantiate.push_back(p); } m_triple.m_apps.set_end(it2); app_triple_lt f(m_triple.m_app2num_occs); // app_triple_lt is not a total order std::stable_sort(m_triple.m_to_instantiate.begin(), m_triple.m_to_instantiate.end(), f); // IF_VERBOSE(10, if (num_deleted > 0) verbose_stream() << "dynamic ackermann GC: " << num_deleted << "\n";); } #ifdef Z3DEBUG bool dyn_ack_manager::check_invariant() const { clause2app_pair::iterator it = m_clause2app_pair.begin(); clause2app_pair::iterator end = m_clause2app_pair.end(); for (; it != end; ++it) { app_pair const & p = it->get_value(); SASSERT(m_instantiated.contains(p)); SASSERT(!m_app_pair2num_occs.contains(p.first, p.second)); } return true; } #endif }; z3-z3-4.4.1/src/smt/dyn_ack.h000066400000000000000000000103371260446376700156040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dyn_ack.h Abstract: Support code for implementing Dynamic Ackermann's reduction Author: Leonardo de Moura (leonardo) 2007-04-12. Revision History: --*/ #ifndef DYN_ACK_H_ #define DYN_ACK_H_ #include"ast.h" #include"dyn_ack_params.h" #include"obj_hashtable.h" #include"obj_pair_hashtable.h" #include"obj_triple_hashtable.h" #include"smt_clause.h" namespace smt { class context; class dyn_ack_manager { typedef std::pair app_pair; typedef obj_pair_map app_pair2num_occs; typedef svector app_pair_vector; typedef obj_pair_hashtable app_pair_set; typedef obj_map clause2app_pair; typedef triple app_triple; typedef obj_triple_map app_triple2num_occs; typedef svector app_triple_vector; typedef obj_triple_hashtable app_triple_set; typedef obj_map clause2app_triple; context & m_context; ast_manager & m_manager; dyn_ack_params & m_params; app_pair2num_occs m_app_pair2num_occs; app_pair_vector m_app_pairs; app_pair_vector m_to_instantiate; unsigned m_qhead; unsigned m_num_instances; unsigned m_num_propagations_since_last_gc; app_pair_set m_instantiated; clause2app_pair m_clause2app_pair; struct _triple { app_triple2num_occs m_app2num_occs; app_triple_vector m_apps; app_triple_vector m_to_instantiate; unsigned m_qhead; unsigned m_num_instances; unsigned m_num_propagations_since_last_gc; app_triple_set m_instantiated; clause2app_triple m_clause2apps; }; _triple m_triple; void gc(); void reset_app_pairs(); friend class dyn_ack_clause_del_eh; void del_clause_eh(clause * cls); void instantiate(app * n1, app * n2); literal mk_eq(expr * n1, expr * n2); void cg_eh(app * n1, app * n2); void eq_eh(app * n1, app * n2, app* r); void instantiate(app * n1, app * n2, app* r); void reset_app_triples(); void gc_triples(); public: dyn_ack_manager(context & ctx, dyn_ack_params & p); ~dyn_ack_manager(); void setup() { } /** \brief This method is invoked before the beginning of the search. */ void init_search_eh(); /** \brief This method is invoked when the congruence rule was used during conflict resolution. */ void used_cg_eh(app * n1, app * n2) { if (m_params.m_dack == DACK_CR) cg_eh(n1, n2); } /** \brief This method is invoked when the congruence rule is the root of a conflict. */ void cg_conflict_eh(app * n1, app * n2) { if (m_params.m_dack == DACK_ROOT) cg_eh(n1, n2); } /** \brief This method is invoked when equalities are used during conflict resolution. */ void used_eq_eh(app * n1, app * n2, app* r) { if (m_params.m_dack_eq) eq_eh(n1, n2, r); } /** \brief This method is invoked when it is safe to expand the new ackermann rule entries. */ void propagate_eh(); void reset(); #ifdef Z3DEBUG bool check_invariant() const; #endif }; }; #endif /* DYN_ACK_H_ */ z3-z3-4.4.1/src/smt/elim_term_ite.cpp000066400000000000000000000100651260446376700173430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: elim_term_ite.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-12. Revision History: --*/ #include"elim_term_ite.h" #include"ast_smt2_pp.h" void elim_term_ite::operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & pr) { m_coarse_proofs.reset(); m_new_defs = &new_defs; m_new_def_proofs = &new_def_proofs; reduce_core(n); expr * r2; proof * pr2; get_cached(n, r2, pr2); r = r2; switch (m.proof_mode()) { case PGM_DISABLED: pr = m.mk_undef_proof(); break; case PGM_COARSE: remove_duplicates(m_coarse_proofs); pr = n == r2 ? m.mk_oeq_reflexivity(n) : m.mk_apply_defs(n, r, m_coarse_proofs.size(), m_coarse_proofs.c_ptr()); break; case PGM_FINE: pr = pr2 == 0 ? m.mk_oeq_reflexivity(n) : pr2; break; } m_coarse_proofs.reset(); } void elim_term_ite::reduce_core(expr * n) { m_todo.reset(); if (!is_cached(n)) { m_todo.push_back(n); 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(); reduce1(n); } } } } bool elim_term_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 elim_term_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 elim_term_ite::reduce1_app(app * n) { m_args.reset(); func_decl * decl = n->get_decl(); proof_ref p1(m); get_args(n, m_args, p1); if (!m.fine_grain_proofs()) p1 = 0; expr_ref r(m); r = m.mk_app(decl, m_args.size(), m_args.c_ptr()); if (m.is_term_ite(r)) { expr_ref new_def(m); proof_ref new_def_pr(m); app_ref new_r(m); proof_ref new_pr(m); if (m_defined_names.mk_name(r, new_def, new_def_pr, new_r, new_pr)) { CTRACE("elim_term_ite_bug", new_def.get() == 0, tout << mk_ismt2_pp(r, m) << "\n";); SASSERT(new_def.get() != 0); m_new_defs->push_back(new_def); if (m.fine_grain_proofs()) { m_new_def_proofs->push_back(new_def_pr); new_pr = m.mk_transitivity(p1, new_pr); } else { // [Leo] This looks fishy... why do we add 0 into m_coarse_proofs when fine_grain_proofs are disabled? new_pr = 0; if (m.proofs_enabled()) m_coarse_proofs.push_back(new_pr); } } else { SASSERT(new_def.get() == 0); if (!m.fine_grain_proofs()) new_pr = 0; } cache_result(n, new_r, new_pr); } else { cache_result(n, r, p1); } } void elim_term_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_oeq_quant_intro(q, new_q, new_body_pr); cache_result(q, new_q, p); } z3-z3-4.4.1/src/smt/elim_term_ite.h000066400000000000000000000024731260446376700170140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: elim_term_ite.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-12. Revision History: --*/ #ifndef ELIM_TERM_ITE_H_ #define ELIM_TERM_ITE_H_ #include"simplifier.h" #include"defined_names.h" class elim_term_ite : public simplifier { defined_names & m_defined_names; proof_ref_vector m_coarse_proofs; expr_ref_vector * m_new_defs; proof_ref_vector * m_new_def_proofs; 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: elim_term_ite(ast_manager & m, defined_names & d):simplifier(m), m_defined_names(d), m_coarse_proofs(m) { m_use_oeq = true; enable_ac_support(false); } virtual ~elim_term_ite() {} void operator()(expr * n, // [IN] 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 & pr // [OUT] proof for (~ n r) ); }; #endif /* ELIM_TERM_ITE_H_ */ z3-z3-4.4.1/src/smt/expr_context_simplifier.cpp000066400000000000000000000544111260446376700214750ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: expr_context_simplifier.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2008-06-03 Revision History: --*/ #include "expr_context_simplifier.h" #include "ast_pp.h" #include "obj_hashtable.h" #include "smt_kernel.h" #include "for_each_expr.h" // table lookup before/after simplification. expr_context_simplifier::expr_context_simplifier(ast_manager& m): m_manager(m), m_arith(m), m_trail(m), m_simp(m), m_forward(true) {} void expr_context_simplifier::reduce(expr * m, expr_ref & result) { expr_ref tmp(m_manager); m_mark.reset(); unsigned trail_size = m_trail.size(); m_forward = true; reduce_rec(m, tmp); m_mark.reset(); m_forward = false; reduce_rec(tmp.get(), result); clean_trail(trail_size); } void expr_context_simplifier::reduce_fix(expr * m, expr_ref & result) { expr_ref tmp(m_manager); result = m; do { tmp = result.get(); reduce(tmp.get(), result); } while (tmp.get() != result.get()); } void expr_context_simplifier::reduce_rec(expr * m, expr_ref & result) { // // reduce expr in context evaluation. // bool polarity; if (m_context.find(m, polarity)) { result = polarity ? m_manager.mk_true() : m_manager.mk_false(); } else if (m_mark.is_marked(m) && !m_manager.is_not(m)) { result = m; } else if (is_quantifier(m)) { reduce_rec(to_quantifier(m), result); m_mark.mark(m, true); } else if (is_app(m)) { reduce_rec(to_app(m), result); m_mark.mark(m, true); } else if (is_var(m)) { result = m; m_mark.mark(m, true); } else { UNREACHABLE(); result = m; } } void expr_context_simplifier::reduce_rec(quantifier* q, expr_ref & result) { result = q; #if 0 // // The context assumes that asserted expressions are in NNF with // respect to the quantifier occurrences. // This can be disabled if the strong context simplifier // is called from the API over the Z3_simplify method. // expr_context_simplifier nested(m_manager); expr_ref body_r(m_manager); nested.reduce(q->get_expr(), body_r); if (body_r.get() != q->get_expr()) { result = m_manager.update_quantifier(q, body_r.get()); } else { result = q; } #endif } void expr_context_simplifier::reduce_rec(app * a, expr_ref & result) { if (m_manager.get_basic_family_id() == a->get_family_id()) { switch(a->get_decl_kind()) { case OP_AND: reduce_and(a->get_num_args(), a->get_args(), result); return; case OP_OR: reduce_or(a->get_num_args(), a->get_args(), result); return; case OP_IFF: { expr_ref tmp1(m_manager), tmp2(m_manager); reduce_rec(a->get_arg(0), tmp1); reduce_rec(a->get_arg(1), tmp2); m_simp.mk_iff(tmp1.get(), tmp2.get(), result); return; } case OP_XOR: { expr_ref tmp1(m_manager), tmp2(m_manager); reduce_rec(a->get_arg(0), tmp1); reduce_rec(a->get_arg(1), tmp2); m_simp.mk_xor(tmp1.get(), tmp2.get(), result); return; } case OP_NOT: { expr_ref tmp(m_manager); reduce_rec(a->get_arg(0), tmp); m_simp.mk_not(tmp.get(), result); return; } case OP_IMPLIES: { app_ref tmp(m_manager); tmp = m_manager.mk_not(a->get_arg(0)); expr* args[2] = { tmp.get(), a->get_arg(1) }; reduce_or(2, args, result); return; } case OP_ITE: { expr_ref tmp(m_manager), tmp1(m_manager), tmp2(m_manager); reduce_rec(a->get_arg(0), tmp); if (is_true(tmp.get())) { reduce_rec(a->get_arg(1), result); } else if (is_false(tmp.get())) { reduce_rec(a->get_arg(2), result); } else { unsigned trail_size = m_trail.size(); insert_context(tmp.get(), true); reduce_rec(a->get_arg(1), tmp1); clean_trail(trail_size); insert_context(tmp.get(), false); reduce_rec(a->get_arg(2), tmp2); clean_trail(trail_size); m_simp.mk_ite(tmp.get(), tmp1.get(), tmp2.get(), result); } return; } default: break; } } expr_ref_vector args(m_manager); for (unsigned i = 0; i < a->get_num_args(); ++i) { expr_ref tmp(m_manager); reduce_rec(a->get_arg(i), tmp); args.push_back(tmp.get()); } result = m_manager.mk_app(a->get_decl(), args.size(), args.c_ptr()); } void expr_context_simplifier::clean_trail(unsigned old_lim) { for (unsigned i = m_trail.size(); i > old_lim; ) { --i; m_context.erase(m_trail[i].get()); } m_trail.resize(old_lim); } void expr_context_simplifier::insert_context(expr* e, bool polarity) { TRACE("expr_context_simplifier", tout << mk_pp(e, m_manager) << "\n";); if (m_manager.is_not(e)) { e = to_app(e)->get_arg(0); polarity = !polarity; } if (!m_context.contains(e)) { m_context.insert(e, polarity); m_trail.push_back(e); } } bool expr_context_simplifier::insert_arg(bool is_and, expr* arg, expr_ref_vector& args) { expr_ref tmp(m_manager); reduce_rec(arg, tmp); TRACE("expr_context_simplifier", tout << mk_pp(arg, m_manager) << " -> " << mk_pp(tmp.get(), m_manager) << "\n";); if (is_true(tmp.get()) && is_and) { // skip. } else if (is_false(tmp.get()) && !is_and) { // skip. } else if (is_false(tmp.get()) && is_and) { return true; } else if (is_true(tmp.get()) && !is_and) { return true; } else { insert_context(tmp.get(), is_and); if (arg != tmp.get()) { insert_context(arg, is_and); // allow to also use un-simplified version } args.push_back(tmp.get()); } return false; } void expr_context_simplifier::reduce_and_or(bool is_and, unsigned num_args, expr * const* args, expr_ref & result) { expr_ref tmp(m_manager); expr_ref_vector args1(m_manager); unsigned trail_size = m_trail.size(); if (m_forward) { for (unsigned i = 0; i < num_args; ++i) { if (insert_arg(is_and, args[i], args1)) { clean_trail(trail_size); result = is_and?m_manager.mk_false():m_manager.mk_true(); return; } } } else { for (unsigned i = num_args; i > 0; ) { --i; if (insert_arg(is_and, args[i], args1)) { clean_trail(trail_size); result = is_and?m_manager.mk_false():m_manager.mk_true(); return; } } } clean_trail(trail_size); if (is_and) { m_simp.mk_and(args1.size(), args1.c_ptr(), result); } else { m_simp.mk_or(args1.size(), args1.c_ptr(), result); } } void expr_context_simplifier::reduce_and(unsigned num_args, expr * const* args, expr_ref & result) { reduce_and_or(true, num_args, args, result); } void expr_context_simplifier::reduce_or(unsigned num_args, expr * const* args, expr_ref & result) { reduce_and_or(false, num_args, args, result); } bool expr_context_simplifier::is_true(expr* e) const { return m_manager.is_true(e) || (m_manager.is_not(e) && m_manager.is_false(to_app(e)->get_arg(0))); } bool expr_context_simplifier::is_false(expr* e) const { return m_manager.is_false(e) || (m_manager.is_not(e) && m_manager.is_true(to_app(e)->get_arg(0))); } // // This routine performs strong context simplification. // It replaces sub-formulas by a fresh name // and checks if the original formula is equivalent // to the resulting formula if the fresh name is set to // true or false. // otherwise it recursively expands the definition of the // fresh name to match the original formula. // // assert ! (fml <=> n) // //for (fml', n') // check visited // check n' // check !n' // if each a is visited, // fml' by fml'[a/a'] // pop_scope // ow, // let a be a non-visited argument. // push_scope // push (a, n'') // assert (n' <=> f(visited_args, n'', visited_or_non_visited_args)) // // The implementation avoid the stack. It uses the following vectors: // todo - DFS stack // names - collection of fresh names. // is_checked - Boolean to control if contextual equivalence with T or F was checked. // parent_ids - stack of IDs used to identify path down to expression on first visit. // self_ids - stack of IDs used to identify path down to children on first visit. // The parent_ids, self_ids stacks are used to ensure that caching results can be done // in a context dependent way. A cached result is only valid for simplification if // it occurs in the context (on the path) where it was inserted. // expr_strong_context_simplifier::expr_strong_context_simplifier(smt_params& p, ast_manager& m): m_manager(m), m_arith(m), m_fn(0,m), m_solver(m, p) { sort* i_sort = m_arith.mk_int(); m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } void expr_strong_context_simplifier::simplify_basic(expr* fml, expr_ref& result) { ast_manager& m = m_manager; // // The context assumes that asserted expressions are in NNF with // respect to the quantifier occurrences. // This can be disabled if the strong context simplifier // is called from the API over the Z3_simplify method. // if (!m.is_bool(fml) || has_quantifiers(fml)) { result = fml; return; } ptr_vector todo; ptr_vector names; svector is_checked; svector parent_ids, self_ids; expr_ref_vector fresh_vars(m); expr_ref_vector trail(m); obj_map > cache; m_solver.push(); unsigned id = 1; expr* n = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); expr* r, *n2; lbool is_sat; unsigned path_id = 0, self_pos = 0; app * a; unsigned sz; trail.push_back(n); m_solver.assert_expr(m.mk_not(m.mk_iff(fml, n))); todo.push_back(fml); names.push_back(n); is_checked.push_back(false); parent_ids.push_back(0); self_ids.push_back(0); std::pair path_r; m_solver.push(); while (!todo.empty()) { r = 0; ptr_buffer args; expr* e = todo.back(); unsigned pos = parent_ids.back(); n = names.back(); bool checked = is_checked.back(); if (cache.contains(e)) { goto done; } if (!m.is_bool(e)) { r = e; goto done; } if (m.is_bool(e) && !checked) { m_solver.push(); m_solver.assert_expr(n); is_sat = m_solver.check(); m_solver.pop(1); if (is_sat == l_false) { r = m.mk_true(); goto done; } } if (m.is_bool(e) && !checked) { m_solver.push(); m_solver.assert_expr(m.mk_not(n)); is_sat = m_solver.check(); m_solver.pop(1); if (is_sat == l_false) { r = m.mk_false(); goto done; } } if (!is_app(e)) { r = e; goto done; } a = to_app(e); if (!is_checked.back()) { self_ids.back() = ++path_id; is_checked.back() = true; } self_pos = self_ids.back(); sz = a->get_num_args(); n2 = 0; for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); if (cache.find(arg, path_r)) { if (path_r.first == self_pos) { args.push_back(path_r.second); } else { args.push_back(arg); } } else if (!m.is_bool(arg)) { args.push_back(arg); } else if (!n2) { n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); todo.push_back(arg); parent_ids.push_back(self_pos); self_ids.push_back(0); names.push_back(n2); trail.push_back(n2); args.push_back(n2); is_checked.push_back(false); } else { args.push_back(arg); } } r = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); trail.push_back(r); if (n2) { m_solver.push(); m_solver.assert_expr(m.mk_eq(r, n)); continue; } done: if (r) { cache.insert(e, std::make_pair(pos, r)); } TRACE("expr_context_simplifier", tout << mk_pp(e, m_manager) << " checked: " << checked << " cached: " << mk_pp(r?r:e, m_manager) << "\n";); todo.pop_back(); parent_ids.pop_back(); self_ids.pop_back(); names.pop_back(); is_checked.pop_back(); m_solver.pop(1); } VERIFY(cache.find(fml, path_r)); m_solver.pop(1); result = path_r.second; } void expr_strong_context_simplifier::simplify_model_based(expr* fml, expr_ref& result) { ast_manager& m = m_manager; // // The context assumes that asserted expressions are in NNF with // respect to the quantifier occurrences. // This can be disabled if the strong context simplifier // is called from the API over the Z3_simplify method. // if (!m.is_bool(fml) || has_quantifiers(fml)) { result = fml; return; } ptr_vector todo; ptr_vector names; svector is_checked; svector parent_ids, self_ids; expr_ref_vector fresh_vars(m); expr_ref_vector trail(m); obj_map > cache; lbool is_sat; expr_ref_vector assignments(m); m_solver.push(); m_solver.assert_expr(fml); is_sat = m_solver.check(); if (is_sat != l_false) { m_solver.get_assignments(assignments); } m_solver.pop(1); if (is_sat == l_false) { result = m.mk_false(); return; } // Collect assignments to sub-formulas from satisfying assignment. obj_map assignment_map; { expr* n1, *n2; for (unsigned i = 0; i < assignments.size(); ++i) { if (m.is_not(assignments[i].get(), n1)) { assignment_map.insert(n1, l_false); } else { assignment_map.insert(assignments[i].get(), l_true); } } todo.push_back(fml); while (!todo.empty()) { expr* n = todo.back(); if (!is_app(n)) { assignment_map.insert(n, l_undef); todo.pop_back(); continue; } app* a = to_app(n); unsigned sz = a->get_num_args(); bool all_visit = true; for (unsigned i = 0; i < sz; ++i) { if (!assignment_map.contains(a->get_arg(i))) { todo.push_back(a->get_arg(i)); all_visit = false; } } if (!all_visit) { continue; } todo.pop_back(); lbool value = l_undef; if (m.is_and(a)) { value = l_true; for (unsigned i = 0; value != l_false && i < sz; ++i) { switch(assignment_map.find(a->get_arg(i))) { case l_false: value = l_false; break; case l_undef: value = l_undef; break; default: break; } } assignment_map.insert(a, value); } else if (m.is_or(a)) { value = l_false; for (unsigned i = 0; value != l_true && i < sz; ++i) { switch(assignment_map.find(a->get_arg(i))) { case l_true: value = l_true; break; case l_undef: value = l_undef; break; default: break; } } assignment_map.insert(a, value); } else if (m.is_not(a)) { switch(assignment_map.find(a->get_arg(0))) { case l_true: value = l_false; break; case l_false: value = l_true; break; default: value = l_undef; break; } assignment_map.insert(a, value); } else if (m.is_implies(a, n1, n2)) { lbool v1 = assignment_map.find(n1); lbool v2 = assignment_map.find(n2); if (v1 == l_false || v2 == l_true) { value = l_true; } else if (v1 == l_true && v2 == l_false) { value = l_false; } else { value = l_undef; } assignment_map.insert(a, value); } else if (m.is_iff(a, n1, n2) || m.is_eq(a, n1, n2)) { lbool v1 = assignment_map.find(n1); lbool v2 = assignment_map.find(n2); if (v1 == l_undef || v2 == l_undef) { value = l_undef; } else if (v1 == v2) { value = l_true; } else { value = l_false; } assignment_map.insert(a, value); } else { assignment_map.insert(a, l_undef); } } } m_solver.push(); unsigned id = 1; expr* n = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); expr* r, *n2; unsigned path_id = 0, self_pos = 0; app * a; unsigned sz; trail.push_back(n); m_solver.assert_expr(m.mk_not(m.mk_iff(fml, n))); todo.push_back(fml); names.push_back(n); is_checked.push_back(false); parent_ids.push_back(0); self_ids.push_back(0); std::pair path_r; m_solver.push(); while (!todo.empty()) { r = 0; ptr_buffer args; expr* e = todo.back(); unsigned pos = parent_ids.back(); n = names.back(); bool checked = is_checked.back(); if (cache.contains(e)) { goto done; } if (!m.is_bool(e)) { r = e; goto done; } if (m.is_bool(e) && !checked) { lbool value = l_undef; assignment_map.find(e, value); switch(value) { case l_true: if (is_forced(n, m.mk_true())) { r = m.mk_true(); goto done; } break; case l_false: if (is_forced(n, m.mk_false())) { r = m.mk_false(); goto done; } break; default: // NB. assignments contain just internalized literals, // the literals in the input may not be internalized. // we therefore fall back to the default behavior, which // is to check both cases. if (is_forced(n, m.mk_true())) { r = m.mk_true(); goto done; } if (is_forced(n, m.mk_false())) { r = m.mk_false(); goto done; } break; } } if (!is_app(e)) { r = e; goto done; } a = to_app(e); if (!is_checked.back()) { self_ids.back() = ++path_id; is_checked.back() = true; } self_pos = self_ids.back(); sz = a->get_num_args(); n2 = 0; for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); if (cache.find(arg, path_r)) { if (path_r.first == self_pos) { args.push_back(path_r.second); } else { args.push_back(arg); } } else if (!m.is_bool(arg)) { args.push_back(arg); } else if (!n2) { n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); todo.push_back(arg); parent_ids.push_back(self_pos); self_ids.push_back(0); names.push_back(n2); trail.push_back(n2); args.push_back(n2); is_checked.push_back(false); } else { args.push_back(arg); } } r = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); trail.push_back(r); if (n2) { m_solver.push(); m_solver.assert_expr(m.mk_eq(r, n)); continue; } done: if (r) { cache.insert(e, std::make_pair(pos, r)); } TRACE("expr_context_simplifier", tout << mk_pp(e, m_manager) << " checked: " << checked << " cached: " << mk_pp(r?r:e, m_manager) << "\n";); todo.pop_back(); parent_ids.pop_back(); self_ids.pop_back(); names.pop_back(); is_checked.pop_back(); m_solver.pop(1); } VERIFY(cache.find(fml, path_r)); m_solver.pop(1); result = path_r.second; } bool expr_strong_context_simplifier::is_forced(expr* e, expr* v) { m_solver.push(); m_solver.assert_expr(m_manager.mk_eq(e, v)); lbool is_sat = m_solver.check(); m_solver.pop(1); return (is_sat == l_false); } z3-z3-4.4.1/src/smt/expr_context_simplifier.h000066400000000000000000000047721260446376700211470ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: expr_context_simplifier.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2008-06-03 Revision History: --*/ #ifndef EXPR_CONTEXT_SIMPLIFIER_H_ #define EXPR_CONTEXT_SIMPLIFIER_H_ #include "ast.h" #include "obj_hashtable.h" #include "basic_simplifier_plugin.h" #include "smt_params.h" #include "smt_kernel.h" #include "arith_decl_plugin.h" class expr_context_simplifier { typedef obj_map context_map; ast_manager& m_manager; arith_util m_arith; context_map m_context; expr_ref_vector m_trail; basic_simplifier_plugin m_simp; expr_mark m_mark; bool m_forward; public: expr_context_simplifier(ast_manager & m); void reduce_fix(expr * m, expr_ref & result); void operator()(expr * m, expr_ref & result) { reduce(m, result); } void insert_context(expr* e, bool polarity); private: void reduce(expr * m, expr_ref & result); void reduce_rec(expr * m, expr_ref & result); void reduce_rec(quantifier* q, expr_ref & result); void reduce_rec(app * a, expr_ref & result); void clean_trail(unsigned old_lim); bool insert_arg(bool is_and, expr* arg, expr_ref_vector& args); void reduce_and_or(bool is_and, unsigned num_args, expr * const* args, expr_ref & result); void reduce_and(unsigned num_args, expr * const* args, expr_ref & result); void reduce_or(unsigned num_args, expr * const* args, expr_ref & result); bool is_true(expr* e) const; bool is_false(expr* e) const; }; class expr_strong_context_simplifier { ast_manager& m_manager; arith_util m_arith; func_decl_ref m_fn; smt::kernel m_solver; void simplify(expr* e, expr_ref& result) { simplify_model_based(e, result); } void simplify_basic(expr* fml, expr_ref& result); void simplify_model_based(expr* fml, expr_ref& result); bool is_forced(expr* e, expr* v); public: expr_strong_context_simplifier(smt_params& p, ast_manager& m); void operator()(expr* e, expr_ref& result) { simplify(e, result); } void operator()(expr_ref& result) { simplify(result.get(), result); } void push() { m_solver.push(); } void pop() { m_solver.pop(1); } void assert_expr(expr* e) { m_solver.assert_expr(e); } void collect_statistics(statistics & st) const { m_solver.collect_statistics(st); } void reset_statistics() { m_solver.reset_statistics(); } void set_cancel(bool f) { m_solver.set_cancel(f); } }; #endif /* EXPR_CONTEXT_SIMPLIFIER_H_ */ z3-z3-4.4.1/src/smt/fingerprints.cpp000066400000000000000000000132421260446376700172370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: fingerprints.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-02-24. Revision History: --*/ #include"fingerprints.h" namespace smt { fingerprint::fingerprint(region & r, void * d, unsigned d_h, unsigned n, enode * const * args): m_data(d), m_data_hash(d_h), m_num_args(n), m_args(0) { m_args = new (r) enode*[n]; memcpy(m_args, args, sizeof(enode*) * n); } bool fingerprint_set::fingerprint_eq_proc::operator()(fingerprint const * f1, fingerprint const * f2) const { if (f1->get_data() != f2->get_data()) return false; if (f1->get_num_args() != f2->get_num_args()) return false; unsigned n = f1->get_num_args(); for(unsigned i = 0; i < n; i++) if (f1->get_arg(i) != f2->get_arg(i)) return false; return true; } fingerprint * fingerprint_set::mk_dummy(void * data, unsigned data_hash, unsigned num_args, enode * const * args) { m_tmp.reset(); m_tmp.append(num_args, args); m_dummy.m_data = data; m_dummy.m_data_hash = data_hash; m_dummy.m_num_args = num_args; m_dummy.m_args = m_tmp.c_ptr(); return &m_dummy; } fingerprint * fingerprint_set::insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args) { fingerprint * d = mk_dummy(data, data_hash, num_args, args); if (m_set.contains(d)) return 0; TRACE("fingerprint_bug", tout << "1) inserting: " << data_hash << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_owner_id(); tout << "\n";); for (unsigned i = 0; i < num_args; i++) d->m_args[i] = d->m_args[i]->get_root(); if (m_set.contains(d)) { TRACE("fingerprint_bug", tout << "failed: " << data_hash << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << d->m_args[i]->get_owner_id(); tout << "\n";); return 0; } TRACE("fingerprint_bug", tout << "2) inserting: " << data_hash << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_owner_id(); tout << "\n";); fingerprint * f = new (m_region) fingerprint(m_region, data, data_hash, num_args, d->m_args); m_fingerprints.push_back(f); m_set.insert(f); return f; } bool fingerprint_set::contains(void * data, unsigned data_hash, unsigned num_args, enode * const * args) { fingerprint * d = mk_dummy(data, data_hash, num_args, args); if (m_set.contains(d)) return true; for (unsigned i = 0; i < num_args; i++) d->m_args[i] = d->m_args[i]->get_root(); if (m_set.contains(d)) return true; return false; } void fingerprint_set::reset() { m_set.reset(); m_fingerprints.reset(); } void fingerprint_set::push_scope() { m_scopes.push_back(m_fingerprints.size()); } void fingerprint_set::pop_scope(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; unsigned old_size = m_scopes[new_lvl]; unsigned size = m_fingerprints.size(); for (unsigned i = old_size; i < size; i++) m_set.erase(m_fingerprints[i]); m_fingerprints.shrink(old_size); m_scopes.shrink(new_lvl); } void fingerprint_set::display(std::ostream & out) const { out << "fingerprints:\n"; SASSERT(m_set.size() == m_fingerprints.size()); ptr_vector::const_iterator it = m_fingerprints.begin(); ptr_vector::const_iterator end = m_fingerprints.end(); for (; it != end; ++it) { fingerprint const * f = *it; out << f->get_data() << " #" << f->get_data_hash(); for (unsigned i = 0; i < f->get_num_args(); i++) out << " #" << f->get_arg(i)->get_owner_id(); out << "\n"; } } #ifdef Z3DEBUG /** \brief Slow function for checking if there is a fingerprint congruent to (data args[0] ... args[num_args-1]) */ bool fingerprint_set::slow_contains(void const * data, unsigned data_hash, unsigned num_args, enode * const * args) const { ptr_vector::const_iterator it = m_fingerprints.begin(); ptr_vector::const_iterator end = m_fingerprints.end(); for (; it != end; ++it) { fingerprint const * f = *it; if (f->get_data() != data) continue; if (f->get_num_args() != num_args) continue; unsigned i = 0; for (i = 0; i < num_args; i++) if (f->get_arg(i)->get_root() != args[i]->get_root()) break; if (i == num_args) { TRACE("missing_instance_detail", tout << "found instance data: " << data << "=" << f->get_data() << " hash: " << f->get_data_hash(); for (unsigned i = 0; i < num_args; i++) { tout << " " << f->get_arg(i)->get_owner_id() << ":" << f->get_arg(i)->get_root()->get_owner_id() << "=" << args[i]->get_owner_id() << ":" << args[i]->get_root()->get_owner_id(); } tout << "\n";); return true; } } return false; } #endif }; z3-z3-4.4.1/src/smt/fingerprints.h000066400000000000000000000053401260446376700167040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: fingerprints.h Abstract: Author: Leonardo de Moura (leonardo) 2007-02-24. Revision History: --*/ #ifndef FINGERPRINTS_H_ #define FINGERPRINTS_H_ #include"smt_enode.h" namespace smt { class fingerprint { protected: void * m_data; unsigned m_data_hash; unsigned m_num_args; enode * * m_args; friend class fingerprint_set; fingerprint() {} public: fingerprint(region & r, void * d, unsigned d_hash, unsigned n, enode * const * args); void * get_data() const { return m_data; } unsigned get_data_hash() const { return m_data_hash; } unsigned get_num_args() const { return m_num_args; } enode * const * get_args() const { return m_args; } enode * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; } }; class fingerprint_set { struct fingerprint_khasher { unsigned operator()(fingerprint const * f) const { return f->get_data_hash(); } }; struct fingerprint_chasher { unsigned operator()(fingerprint const * f, unsigned idx) const { return f->get_arg(idx)->hash(); } }; struct fingerprint_hash_proc { unsigned operator()(fingerprint const * f) const { return get_composite_hash(const_cast(f), f->get_num_args()); } }; struct fingerprint_eq_proc { bool operator()(fingerprint const * f1, fingerprint const * f2) const; }; typedef ptr_hashtable set; region & m_region; set m_set; ptr_vector m_fingerprints; unsigned_vector m_scopes; ptr_vector m_tmp; fingerprint m_dummy; fingerprint * mk_dummy(void * data, unsigned data_hash, unsigned num_args, enode * const * args); public: fingerprint_set(region & r):m_region(r) {} fingerprint * insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args); unsigned size() const { return m_fingerprints.size(); } bool contains(void * data, unsigned data_hash, unsigned num_args, enode * const * args); void reset(); void push_scope(); void pop_scope(unsigned num_scopes); void display(std::ostream & out) const; #ifdef Z3DEBUG bool slow_contains(void const * data, unsigned data_hash, unsigned num_args, enode * const * args) const; #endif }; }; #endif /* FINGERPRINTS_H_ */ z3-z3-4.4.1/src/smt/mam.cpp000066400000000000000000005120411260446376700153000ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mam.cpp Abstract: Matching Abstract Machine Author: Leonardo de Moura (leonardo) 2007-02-13. Revision History: --*/ #include"mam.h" #include"smt_context.h" #include"pool.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"trail.h" #include"stopwatch.h" #include"ast_smt2_pp.h" #include // #define _PROFILE_MAM // ----------------------------------------- // Flags for _PROFILE_MAM // // send profiling information to stdout #define _PROFILE_MAM_TO_STDOUT // threshold in secs for being considered expensive #define _PROFILE_MAM_THRESHOLD 30.0 // dump expensive (> _PROFILE_MAM_THRESHOLD) code trees whenever execute_core is executed. #define _PROFILE_MAM_EXPENSIVE // #define _PROFILE_MAM_EXPENSIVE_FREQ 100000 // // ----------------------------------------- // #define _PROFILE_PATH_TREE // ----------------------------------------- // Flags for _PROFILE_PATH_TREE // #define _PROFILE_PATH_TREE_THRESHOLD 20000 // // ----------------------------------------- #define IS_CGR_SUPPORT true namespace smt { // ------------------------------------ // // Trail // // ------------------------------------ class mam_impl; typedef trail_stack mam_trail_stack; typedef trail mam_trail; template class mam_value_trail : public value_trail { public: mam_value_trail(T & value):value_trail(value) {} }; // ------------------------------------ // // Auxiliary // // ------------------------------------ class label_hasher { svector m_lbl2hash; // cache: lbl_id -> hash void mk_lbl_hash(unsigned lbl_id) { unsigned a = 17; unsigned b = 3; unsigned c = lbl_id; mix(a, b, c); m_lbl2hash[lbl_id] = c & (APPROX_SET_CAPACITY - 1); } public: unsigned char operator()(func_decl * lbl) { unsigned lbl_id = lbl->get_decl_id(); if (lbl_id >= m_lbl2hash.size()) m_lbl2hash.resize(lbl_id + 1, -1); if (m_lbl2hash[lbl_id] == -1) { mk_lbl_hash(lbl_id); } SASSERT(m_lbl2hash[lbl_id] >= 0); return m_lbl2hash[lbl_id]; } void display(std::ostream & out) const { out << "lbl-hasher:\n"; bool first = true; for (unsigned i = 0; i < m_lbl2hash.size(); i++) { if (m_lbl2hash[i] != -1) { if (first) first = false; else out << ", "; out << i << " -> " << static_cast(m_lbl2hash[i]); } } out << "\n"; } }; // ------------------------------------ // // Instructions // // ------------------------------------ typedef enum { INIT1=0, INIT2, INIT3, INIT4, INIT5, INIT6, INITN, BIND1, BIND2, BIND3, BIND4, BIND5, BIND6, BINDN, YIELD1, YIELD2, YIELD3, YIELD4, YIELD5, YIELD6, YIELDN, COMPARE, CHECK, FILTER, CFILTER, PFILTER, CHOOSE, NOOP, CONTINUE, GET_ENODE, GET_CGR1, GET_CGR2, GET_CGR3, GET_CGR4, GET_CGR5, GET_CGR6, GET_CGRN, IS_CGR } opcode; struct instruction { opcode m_opcode; instruction * m_next; #ifdef _PROFILE_MAM unsigned m_counter; // how often it was executed #endif bool is_init() const { return m_opcode >= INIT1 && m_opcode <= INITN; } }; struct initn : public instruction { // We need that because starting at Z3 3.0, some associative // operators (e.g., + and *) are represented using n-ary // applications. // We do not need the extra field for INIT1, ..., INIT6. unsigned m_num_args; }; struct compare : public instruction { unsigned m_reg1; unsigned m_reg2; }; struct check : public instruction { unsigned m_reg; enode * m_enode; }; struct filter : public instruction { unsigned m_reg; approx_set m_lbl_set; }; struct pcheck : public instruction { enode * m_enode; approx_set m_lbl_set; }; /** \brief Copy m_enode to register m_oreg */ struct get_enode_instr : public instruction { unsigned m_oreg; enode * m_enode; }; struct choose: public instruction { choose * m_alt; }; /** \brief A depth-2 joint. It is used in CONTINUE instruction. There are 3 forms of joints 1) Variables: (f ... X ...) 2) Ground terms: (f ... t ...) 3) depth 2 joint: (f ... (g ... X ...) ...) Joint2 stores the declartion g, and the position of variable X, and its idx. \remark Z3 has no support for depth 3 joints (f ... (g ... (h ... X ...) ...) ....) */ struct joint2 { func_decl * m_decl; unsigned m_arg_pos; unsigned m_reg; // register that contains the variable joint2(func_decl * f, unsigned pos, unsigned r):m_decl(f), m_arg_pos(pos), m_reg(r) {} }; #define NULL_TAG 0 #define GROUND_TERM_TAG 1 #define VAR_TAG 2 #define NESTED_VAR_TAG 3 struct cont: public instruction { func_decl * m_label; unsigned short m_num_args; unsigned m_oreg; approx_set m_lbl_set; // singleton set containing m_label /* The following field is an array of tagged pointers. Each positon contains: 1- null (no joint), NULL_TAG 2- a boxed integer (i.e., register that contains the variable bind) VAR_TAG 3- an enode pointer (ground term) GROUND_TERM_TAG 4- or, a joint2 pointer. NESTED_VAR_TAG The size of the array is m_num_args. */ enode * m_joints[0]; }; struct bind : public instruction { func_decl * m_label; unsigned short m_num_args; unsigned m_ireg; unsigned m_oreg; }; struct get_cgr : public instruction { func_decl * m_label; approx_set m_lbl_set; unsigned short m_num_args; unsigned m_oreg; unsigned m_iregs[0]; }; struct yield : public instruction { quantifier * m_qa; app * m_pat; unsigned short m_num_bindings; unsigned m_bindings[0]; }; struct is_cgr : public instruction { unsigned m_ireg; func_decl * m_label; unsigned short m_num_args; unsigned m_iregs[0]; }; void display_num_args(std::ostream & out, unsigned num_args) { if (num_args <= 6) { out << num_args; } else { out << "N"; } } void display_bind(std::ostream & out, const bind & b) { out << "(BIND"; display_num_args(out, b.m_num_args); out << " " << b.m_label->get_name() << " " << b.m_ireg << " " << b.m_oreg << ")"; } void display_get_cgr(std::ostream & out, const get_cgr & c) { out << "(GET_CGR"; display_num_args(out, c.m_num_args); out << " " << c.m_label->get_name() << " " << c.m_oreg; for (unsigned i = 0; i < c.m_num_args; i++) out << " " << c.m_iregs[i]; out << ")"; } void display_is_cgr(std::ostream & out, const is_cgr & c) { out << "(IS_CGR " << c.m_label->get_name() << " " << c.m_ireg; for (unsigned i = 0; i < c.m_num_args; i++) out << " " << c.m_iregs[i]; out << ")"; } void display_yield(std::ostream & out, const yield & y) { out << "(YIELD"; display_num_args(out, y.m_num_bindings); out << " #" << y.m_qa->get_id(); for (unsigned i = 0; i < y.m_num_bindings; i++) { out << " " << y.m_bindings[i]; } out << ")"; } void display_joints(std::ostream & out, unsigned num_joints, enode * const * joints) { for (unsigned i = 0; i < num_joints; i++) { if (i > 0) out << " "; enode * bare = joints[i]; switch (GET_TAG(bare)) { case NULL_TAG: out << "nil"; break; case GROUND_TERM_TAG: out << "#" << UNTAG(enode*, bare)->get_owner_id(); break; case VAR_TAG: out << UNBOXINT(bare); break; case NESTED_VAR_TAG: out << "(" << UNTAG(joint2*, bare)->m_decl->get_name() << " " << UNTAG(joint2*, bare)->m_arg_pos << " " << UNTAG(joint2*, bare)->m_reg << ")"; break; } } } void display_continue(std::ostream & out, const cont & c) { out << "(CONTINUE " << c.m_label->get_name() << " " << c.m_num_args << " " << c.m_oreg << " " << c.m_lbl_set << " ("; display_joints(out, c.m_num_args, c.m_joints); out << "))"; } void display_filter(std::ostream & out, char const * op, filter const & instr) { out << "(" << op << " " << instr.m_reg << " " << instr.m_lbl_set << ")"; } std::ostream & operator<<(std::ostream & out, const instruction & instr) { switch (instr.m_opcode) { case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN: out << "(INIT"; if (instr.m_opcode <= INIT6) out << (instr.m_opcode - INIT1 + 1); else out << "N"; out << ")"; break; case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: display_bind(out, static_cast(instr)); break; case GET_CGR1: case GET_CGR2: case GET_CGR3: case GET_CGR4: case GET_CGR5: case GET_CGR6: case GET_CGRN: display_get_cgr(out, static_cast(instr)); break; case IS_CGR: display_is_cgr(out, static_cast(instr)); break; case YIELD1: case YIELD2: case YIELD3: case YIELD4: case YIELD5: case YIELD6: case YIELDN: display_yield(out, static_cast(instr)); break; case CONTINUE: display_continue(out, static_cast(instr)); break; case COMPARE: out << "(COMPARE " << static_cast(instr).m_reg1 << " " << static_cast(instr).m_reg2 << ")"; break; case CHECK: out << "(CHECK " << static_cast(instr).m_reg << " #" << static_cast(instr).m_enode->get_owner_id() << ")"; break; case FILTER: display_filter(out, "FILTER", static_cast(instr)); break; case CFILTER: display_filter(out, "CFILTER", static_cast(instr)); break; case PFILTER: display_filter(out, "PFILTER", static_cast(instr)); break; case GET_ENODE: out << "(GET_ENODE " << static_cast(instr).m_oreg << " #" << static_cast(instr).m_enode->get_owner_id() << ")"; break; case CHOOSE: out << "(CHOOSE)"; break; case NOOP: out << "(NOOP)"; break; } #ifdef _PROFILE_MAM out << "[" << instr.m_counter << "]"; #endif return out; } // ------------------------------------ // // Code Tree // // ------------------------------------ inline enode * get_enode(context & ctx, app * n) { SASSERT(ctx.e_internalized(n)); enode * e = ctx.get_enode(n); SASSERT(e); return e; } inline enode * mk_enode(context & ctx, quantifier * qa, app * n) { ctx.internalize(n, false, ctx.get_generation(qa)); enode * e = ctx.get_enode(n); SASSERT(e); return e; } class code_tree { label_hasher & m_lbl_hasher; func_decl * m_root_lbl; unsigned m_num_args; //!< we need this information to avoid the nary *,+ crash bug unsigned char m_filter_candidates; unsigned m_num_regs; unsigned m_num_choices; instruction * m_root; enode_vector m_candidates; #ifdef Z3DEBUG context * m_context; ptr_vector m_patterns; #endif #ifdef _PROFILE_MAM stopwatch m_watch; unsigned m_counter; #endif friend class compiler; friend class code_tree_manager; void display_seq(std::ostream & out, instruction * head, unsigned indent) const { for (unsigned i = 0; i < indent; i++) { out << " "; } instruction * curr = head; out << *curr; curr = curr->m_next; while (curr != 0 && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { out << " "; out << *curr; curr = curr->m_next; } out << "\n"; if (curr != 0) { display_children(out, static_cast(curr), indent + 1); } } void display_children(std::ostream & out, choose * first_child, unsigned indent) const { choose * curr = first_child; while (curr != 0) { display_seq(out, curr, indent); curr = curr->m_alt; } } #ifdef Z3DEBUG void display_label_hashes_core(std::ostream & out, app * p) const { if (p->is_ground()) { enode * e = get_enode(*m_context, p); SASSERT(e->has_lbl_hash()); out << "#" << e->get_owner_id() << ":" << e->get_lbl_hash() << " "; } else { out << p->get_decl()->get_name() << ":" << m_lbl_hasher(p->get_decl()) << " "; for (unsigned i = 0; i < p->get_num_args(); i++) { expr * arg = p->get_arg(i); if (is_app(arg)) display_label_hashes(out, to_app(arg)); } } } void display_label_hashes(std::ostream & out, app * p) const { ast_manager & m = m_context->get_manager(); if (m.is_pattern(p)) { for (unsigned i = 0; i < p->get_num_args(); i++) { expr * arg = p->get_arg(i); if (is_app(arg)) { display_label_hashes_core(out, to_app(arg)); out << "\n"; } } } else { display_label_hashes_core(out, p); out << "\n"; } } #endif public: code_tree(label_hasher & h, func_decl * lbl, unsigned short num_args, bool filter_candidates): m_lbl_hasher(h), m_root_lbl(lbl), m_num_args(num_args), m_filter_candidates(filter_candidates), m_num_regs(num_args + 1), m_num_choices(0), m_root(0) { DEBUG_CODE(m_context = 0;); #ifdef _PROFILE_MAM m_counter = 0; #endif } #ifdef _PROFILE_MAM ~code_tree() { #ifdef _PROFILE_MAM_TO_STDOUT std::cout << "killing code tree for: " << m_root_lbl->get_name() << " " << static_cast(m_watch.get_seconds() * 1000) << "\n"; display(std::cout); #endif } stopwatch & get_watch() { return m_watch; } void inc_counter() { m_counter++; } unsigned get_counter() const { return m_counter; } #endif unsigned expected_num_args() const { return m_num_args; } unsigned get_num_regs() const { return m_num_regs; } unsigned get_num_choices() const { return m_num_choices; } func_decl * get_root_lbl() const { return m_root_lbl; } bool filter_candidates() const { return m_filter_candidates != 0; } const instruction * get_root() const { return m_root; } void add_candidate(enode * n) { m_candidates.push_back(n); } bool has_candidates() const { return !m_candidates.empty(); } void reset_candidates() { m_candidates.reset(); } enode_vector const & get_candidates() const { return m_candidates; } #ifdef Z3DEBUG void set_context(context * ctx) { SASSERT(m_context == 0); m_context = ctx; } ptr_vector & get_patterns() { return m_patterns; } #endif void display(std::ostream & out) const { #ifdef Z3DEBUG if (m_context) { ast_manager & m = m_context->get_manager(); out << "patterns:\n"; ptr_vector::const_iterator it = m_patterns.begin(); ptr_vector::const_iterator end = m_patterns.end(); for (; it != end; ++it) out << mk_pp(*it, m) << "\n"; } #endif out << "function: " << m_root_lbl->get_name(); #ifdef _PROFILE_MAM out << " " << m_watch.get_seconds() << " secs, [" << m_counter << "]"; #endif out << "\n"; out << "num. regs: " << m_num_regs << "\n" << "num. choices: " << m_num_choices << "\n"; display_seq(out, m_root, 0); } }; inline std::ostream & operator<<(std::ostream & out, code_tree const & tree) { tree.display(out); return out; } // ------------------------------------ // // Code Tree Manager // // ------------------------------------ class code_tree_manager { label_hasher & m_lbl_hasher; mam_trail_stack & m_trail_stack; region & m_region; template OP * mk_instr(opcode op, unsigned size) { void * mem = m_region.allocate(size); OP * r = new (mem) OP; r->m_opcode = op; r->m_next = 0; #ifdef _PROFILE_MAM r->m_counter = 0; #endif return r; } instruction * mk_init(unsigned n) { SASSERT(n >= 1); opcode op = n <= 6 ? static_cast(INIT1 + n - 1) : INITN; if (op == INITN) { // We store the actual number of arguments for INITN. // Starting at Z3 3.0, some associative operators // (e.g., + and *) are represented using n-ary // applications. initn * r = mk_instr(op, sizeof(initn)); r->m_num_args = n; return r; } else { return mk_instr(op, sizeof(instruction)); } } public: code_tree_manager(label_hasher & h, mam_trail_stack & s): m_lbl_hasher(h), m_trail_stack(s), m_region(s.get_region()) { } code_tree * mk_code_tree(func_decl * lbl, unsigned short num_args, bool filter_candidates) { code_tree * r = alloc(code_tree,m_lbl_hasher, lbl, num_args, filter_candidates); r->m_root = mk_init(num_args); return r; } joint2 * mk_joint2(func_decl * f, unsigned pos, unsigned reg) { return new (m_region) joint2(f, pos, reg); } compare * mk_compare(unsigned reg1, unsigned reg2) { compare * r = mk_instr(COMPARE, sizeof(compare)); r->m_reg1 = reg1; r->m_reg2 = reg2; return r; } check * mk_check(unsigned reg, enode * n) { check * r = mk_instr(CHECK, sizeof(check)); r->m_reg = reg; r->m_enode = n; return r; } filter * mk_filter_core(opcode op, unsigned reg, approx_set s) { filter * r = mk_instr(op, sizeof(filter)); r->m_reg = reg; r->m_lbl_set = s; return r; } filter * mk_filter(unsigned reg, approx_set s) { return mk_filter_core(FILTER, reg, s); } filter * mk_pfilter(unsigned reg, approx_set s) { return mk_filter_core(PFILTER, reg, s); } filter * mk_cfilter(unsigned reg, approx_set s) { return mk_filter_core(CFILTER, reg, s); } get_enode_instr * mk_get_enode(unsigned reg, enode * n) { get_enode_instr * s = mk_instr(GET_ENODE, sizeof(get_enode_instr)); s->m_oreg = reg; s->m_enode = n; return s; } choose * mk_choose(choose * alt) { choose * r = mk_instr(CHOOSE, sizeof(choose)); r->m_alt = alt; return r; } choose * mk_noop() { choose * r = mk_instr(NOOP, sizeof(choose)); r->m_alt = 0; return r; } bind * mk_bind(func_decl * lbl, unsigned short num_args, unsigned ireg, unsigned oreg) { SASSERT(num_args >= 1); opcode op = num_args <= 6 ? static_cast(BIND1 + num_args - 1) : BINDN; bind * r = mk_instr(op, sizeof(bind)); r->m_label = lbl; r->m_num_args = num_args; r->m_ireg = ireg; r->m_oreg = oreg; return r; } get_cgr * mk_get_cgr(func_decl * lbl, unsigned oreg, unsigned num_args, unsigned const * iregs) { SASSERT(num_args >= 1); opcode op = num_args <= 6 ? static_cast(GET_CGR1 + num_args - 1) : GET_CGRN; get_cgr * r = mk_instr(op, sizeof(get_cgr) + num_args * sizeof(unsigned)); r->m_label = lbl; r->m_lbl_set.insert(m_lbl_hasher(lbl)); r->m_oreg = oreg; r->m_num_args = num_args; memcpy(r->m_iregs, iregs, sizeof(unsigned) * num_args); return r; } is_cgr * mk_is_cgr(func_decl * lbl, unsigned ireg, unsigned num_args, unsigned const * iregs) { SASSERT(num_args >= 1); is_cgr * r = mk_instr(IS_CGR, sizeof(is_cgr) + num_args * sizeof(unsigned)); r->m_label = lbl; r->m_ireg = ireg; r->m_num_args = num_args; memcpy(r->m_iregs, iregs, sizeof(unsigned) * num_args); return r; } yield * mk_yield(quantifier * qa, app * pat, unsigned num_bindings, unsigned * bindings) { SASSERT(num_bindings >= 1); opcode op = num_bindings <= 6 ? static_cast(YIELD1 + num_bindings - 1) : YIELDN; yield * y = mk_instr(op, sizeof(yield) + num_bindings * sizeof(unsigned)); y->m_qa = qa; y->m_pat = pat; y->m_num_bindings = num_bindings; memcpy(y->m_bindings, bindings, sizeof(unsigned) * num_bindings); return y; } cont * mk_cont(func_decl * lbl, unsigned short num_args, unsigned oreg, approx_set const & s, enode * const * joints) { SASSERT(num_args >= 1); cont * r = mk_instr(CONTINUE, sizeof(cont) + num_args * sizeof(enode*)); r->m_label = lbl; r->m_num_args = num_args; r->m_oreg = oreg; r->m_lbl_set = s; memcpy(r->m_joints, joints, num_args * sizeof(enode *)); return r; } void set_next(instruction * instr, instruction * new_next) { m_trail_stack.push(mam_value_trail(instr->m_next)); instr->m_next = new_next; } void save_num_regs(code_tree * tree) { m_trail_stack.push(mam_value_trail(tree->m_num_regs)); } void save_num_choices(code_tree * tree) { m_trail_stack.push(mam_value_trail(tree->m_num_choices)); } void insert_new_lbl_hash(filter * instr, unsigned h) { m_trail_stack.push(mam_value_trail(instr->m_lbl_set)); instr->m_lbl_set.insert(h); } }; // ------------------------------------ // // Compiler: Pattern ---> Code Tree // // ------------------------------------ class compiler { context & m_context; ast_manager & m_ast_manager; code_tree_manager & m_ct_manager; label_hasher & m_lbl_hasher; bool m_use_filters; ptr_vector m_registers; unsigned_vector m_todo; // list of registers that have patterns to be processed. unsigned_vector m_aux; int_vector m_vars; // -1 the variable is unbound, >= 0 is the register that contains the variable quantifier * m_qa; app * m_mp; code_tree * m_tree; unsigned m_num_choices; bool m_is_tmp_tree; svector m_mp_already_processed; struct pcheck_checked { func_decl * m_label; enode * m_enode; }; typedef enum { NOT_CHECKED, CHECK_SET, CHECK_SINGLETON } check_mark; svector m_mark; unsigned_vector m_to_reset; ptr_vector m_compatible; ptr_vector m_incompatible; ptr_vector m_seq; void set_register(unsigned reg, expr * p) { m_registers.setx(reg, p, 0); } check_mark get_check_mark(unsigned reg) const { return m_mark.get(reg, NOT_CHECKED); } void set_check_mark(unsigned reg, check_mark m) { m_mark.setx(reg, m, NOT_CHECKED); } void init(code_tree * t, quantifier * qa, app * mp, unsigned first_idx) { SASSERT(m_ast_manager.is_pattern(mp)); #ifdef Z3DEBUG svector::iterator it = m_mark.begin(); svector::iterator end = m_mark.end(); for (; it != end; ++it) { SASSERT(*it == NOT_CHECKED); } #endif m_tree = t; m_qa = qa; m_mp = mp; m_num_choices = 0; m_todo.reset(); m_registers.fill(0); app * p = to_app(mp->get_arg(first_idx)); SASSERT(t->get_root_lbl() == p->get_decl()); unsigned num_args = p->get_num_args(); for (unsigned i = 0; i < num_args; i++) { set_register(i+1, p->get_arg(i)); m_todo.push_back(i+1); } unsigned num_decls = m_qa->get_num_decls(); if (num_decls > m_vars.size()) { m_vars.resize(num_decls, -1); } for (unsigned j = 0; j < num_decls; j++) { m_vars[j] = -1; } } /** \brief Return true if all arguments of n are bound variables. That is, during execution time, the variables will be already bound */ bool all_args_are_bound_vars(app * n) { unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (!is_var(arg)) return false; if (m_vars[to_var(arg)->get_idx()] == -1) return false; } return true; } /** \see get_stats */ void get_stats_core(app * n, unsigned & sz, unsigned & num_unbound_vars) { sz++; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (is_var(arg)) { sz++; unsigned var_id = to_var(arg)->get_idx(); if (m_vars[var_id] == -1) num_unbound_vars++; } else if (is_app(arg)) { get_stats_core(to_app(arg), sz, num_unbound_vars); } } } /** \brief Return statistics for the given pattern \remark Patterns are small. So, it doesn't hurt to use a recursive function. */ void get_stats(app * n, unsigned & sz, unsigned & num_unbound_vars) { sz = 0; num_unbound_vars = 0; return get_stats_core(n, sz, num_unbound_vars); } /** \brief Process registers in m_todo. The registers in m_todo that produce non-BIND operations are processed first. Then, a single BIND operation b is produced. After executing this method m_todo will contain the registers in m_todo that produce BIND operations and were not processed, and the registers generated when the operation b was produced. \remark The new operations are appended to m_seq. */ void linearise_core() { m_aux.reset(); app * first_app = 0; unsigned first_app_reg; unsigned first_app_sz; unsigned first_app_num_unbound_vars; // generate first the non-BIND operations unsigned_vector::iterator it = m_todo.begin(); unsigned_vector::iterator end = m_todo.end(); for (; it != end; ++it) { unsigned reg = *it; expr * p = m_registers[reg]; SASSERT(!is_quantifier(p)); if (is_var(p)) { unsigned var_id = to_var(p)->get_idx(); if (m_vars[var_id] != -1) m_seq.push_back(m_ct_manager.mk_compare(m_vars[var_id], reg)); else m_vars[var_id] = reg; continue; } SASSERT(is_app(p)); if (to_app(p)->is_ground()) { // ground applications are viewed as constants, and eagerly // converted into enodes. enode * e = mk_enode(m_context, m_qa, to_app(p)); m_seq.push_back(m_ct_manager.mk_check(reg, e)); set_check_mark(reg, NOT_CHECKED); // reset mark, register was fully processed. continue; } if (m_use_filters && get_check_mark(reg) != CHECK_SINGLETON) { func_decl * lbl = to_app(p)->get_decl(); approx_set s(m_lbl_hasher(lbl)); m_seq.push_back(m_ct_manager.mk_filter(reg, s)); set_check_mark(reg, CHECK_SINGLETON); } if (first_app) { #if 0 m_aux.push_back(reg); #else // Try to select the best first_app if (first_app_num_unbound_vars == 0) { // first_app doesn't have free vars... so it is the best choice... m_aux.push_back(reg); } else { unsigned sz; unsigned num_unbound_vars; get_stats(to_app(p), sz, num_unbound_vars); if (num_unbound_vars == 0 || sz > first_app_sz || (sz == first_app_sz && num_unbound_vars < first_app_num_unbound_vars)) { // change the first_app m_aux.push_back(first_app_reg); first_app = to_app(p); first_app_reg = reg; first_app_sz = sz; first_app_num_unbound_vars = num_unbound_vars; } else { m_aux.push_back(reg); } } #endif } else { first_app = to_app(p); first_app_reg = reg; get_stats(first_app, first_app_sz, first_app_num_unbound_vars); } } if (first_app) { // m_todo contains at least one (non-ground) application. func_decl * lbl = first_app->get_decl(); unsigned short num_args = first_app->get_num_args(); if (IS_CGR_SUPPORT && all_args_are_bound_vars(first_app)) { // use IS_CGR instead of BIND sbuffer iregs; for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(first_app)->get_arg(i); SASSERT(is_var(arg)); SASSERT(m_vars[to_var(arg)->get_idx()] != -1); iregs.push_back(m_vars[to_var(arg)->get_idx()]); } m_seq.push_back(m_ct_manager.mk_is_cgr(lbl, first_app_reg, num_args, iregs.c_ptr())); } else { // Generate a BIND operation for this application. unsigned oreg = m_tree->m_num_regs; m_tree->m_num_regs += num_args; for (unsigned j = 0; j < num_args; j++) { set_register(oreg + j, first_app->get_arg(j)); m_aux.push_back(oreg + j); } m_seq.push_back(m_ct_manager.mk_bind(lbl, num_args, first_app_reg, oreg)); m_num_choices++; } set_check_mark(first_app_reg, NOT_CHECKED); // reset mark, register was fully processed. } // make m_aux the new todo list. m_todo.swap(m_aux); } /** \brief Return the number of already bound vars in n. \remark Patterns are small. So, it doesn't hurt to use a recursive function. */ unsigned get_num_bound_vars_core(app * n, bool & has_unbound_vars) { unsigned r = 0; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (is_var(arg)) { unsigned var_id = to_var(arg)->get_idx(); if (m_vars[var_id] != -1) r++; else has_unbound_vars = true; } else if (is_app(arg)) { r += get_num_bound_vars_core(to_app(arg), has_unbound_vars); } } return r; } unsigned get_num_bound_vars(app * n, bool & has_unbound_vars) { has_unbound_vars = false; return get_num_bound_vars_core(n, has_unbound_vars); } /** \brief Compile a pattern where all free variables are already bound. Return the register where the enode congruent to f will be stored. */ unsigned gen_mp_filter(app * n) { if (is_ground(n)) { unsigned oreg = m_tree->m_num_regs; m_tree->m_num_regs += 1; enode * e = mk_enode(m_context, m_qa, n); m_seq.push_back(m_ct_manager.mk_get_enode(oreg, e)); return oreg; } sbuffer iregs; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (is_var(arg)) { SASSERT(m_vars[to_var(arg)->get_idx()] != -1); if (m_vars[to_var(arg)->get_idx()] == -1) verbose_stream() << "BUG.....\n"; iregs.push_back(m_vars[to_var(arg)->get_idx()]); } else { iregs.push_back(gen_mp_filter(to_app(arg))); } } unsigned oreg = m_tree->m_num_regs; m_tree->m_num_regs += 1; m_seq.push_back(m_ct_manager.mk_get_cgr(n->get_decl(), oreg, num_args, iregs.c_ptr())); return oreg; } /** \brief Process the rest of a multi-pattern. That is the patterns different from first_idx */ void linearise_multi_pattern(unsigned first_idx) { unsigned num_args = m_mp->get_num_args(); // multi_pattern support for (unsigned i = 1; i < num_args; i++) { // select the pattern with the biggest number of bound variables app * best = 0; unsigned best_num_bvars = 0; unsigned best_j = 0; bool found_bounded_mp = false; for (unsigned j = 0; j < m_mp->get_num_args(); j++) { if (std::find(m_mp_already_processed.begin(), m_mp_already_processed.end(), j) != m_mp_already_processed.end()) continue; app * p = to_app(m_mp->get_arg(j)); bool has_unbound_vars = false; unsigned num_bvars = get_num_bound_vars(p, has_unbound_vars); if (!has_unbound_vars) { best = p; best_j = j; found_bounded_mp = true; break; } if (best == 0 || (num_bvars > best_num_bvars)) { best = p; best_num_bvars = num_bvars; best_j = j; } } m_mp_already_processed.push_back(best_j); SASSERT(best != 0); app * p = best; func_decl * lbl = p->get_decl(); unsigned short num_args = p->get_num_args(); approx_set s; if (m_use_filters) s.insert(m_lbl_hasher(lbl)); if (found_bounded_mp) { gen_mp_filter(p); } else { // USE CONTINUE unsigned oreg = m_tree->m_num_regs; m_tree->m_num_regs += num_args; ptr_buffer joints; bool has_depth1_joint = false; // VAR_TAG or GROUND_TERM_TAG for (unsigned j = 0; j < num_args; j++) { expr * curr = p->get_arg(j); SASSERT(!is_quantifier(curr)); set_register(oreg + j, curr); m_todo.push_back(oreg + j); if ((is_var(curr) && m_vars[to_var(curr)->get_idx()] >= 0) || (is_app(curr) && (to_app(curr)->is_ground()))) has_depth1_joint = true; } if (has_depth1_joint) { for (unsigned j = 0; j < num_args; j++) { expr * curr = p->get_arg(j); if (is_var(curr)) { unsigned var_id = to_var(curr)->get_idx(); if (m_vars[var_id] >= 0) joints.push_back(BOXTAGINT(enode *, m_vars[var_id], VAR_TAG)); else joints.push_back(NULL_TAG); continue; } SASSERT(is_app(curr)); if (to_app(curr)->is_ground()) { enode * e = mk_enode(m_context, m_qa, to_app(curr)); joints.push_back(TAG(enode *, e, GROUND_TERM_TAG)); continue; } joints.push_back(0); } } else { // Only try to use depth2 joints if there is no depth1 joint. for (unsigned j = 0; j < num_args; j++) { expr * curr = p->get_arg(j); if (!is_app(curr)) { joints.push_back(0); continue; } unsigned num_args2 = to_app(curr)->get_num_args(); unsigned k = 0; for (; k < num_args2; k++) { expr * arg = to_app(curr)->get_arg(k); if (!is_var(arg)) continue; unsigned var_id = to_var(arg)->get_idx(); if (m_vars[var_id] < 0) continue; joint2 * new_joint = m_ct_manager.mk_joint2(to_app(curr)->get_decl(), k, m_vars[var_id]); joints.push_back(TAG(enode *, new_joint, NESTED_VAR_TAG)); break; // found a new joint } if (k == num_args2) joints.push_back(0); // didn't find joint } } SASSERT(joints.size() == num_args); m_seq.push_back(m_ct_manager.mk_cont(lbl, num_args, oreg, s, joints.c_ptr())); m_num_choices++; while (!m_todo.empty()) linearise_core(); } } } /** \brief Produce the operations for the registers in m_todo. */ void linearise(instruction * head, unsigned first_idx) { m_seq.reset(); m_mp_already_processed.reset(); m_mp_already_processed.push_back(first_idx); while (!m_todo.empty()) linearise_core(); if (m_mp->get_num_args() > 1) linearise_multi_pattern(first_idx); #ifdef Z3DEBUG for (unsigned i = 0; i < m_qa->get_num_decls(); i++) { CTRACE("mam_new_bug", m_vars[i] < 0, tout << mk_ismt2_pp(m_qa, m_ast_manager) << "\ni: " << i << " m_vars[i]: " << m_vars[i] << "\n"; tout << "m_mp:\n" << mk_ismt2_pp(m_mp, m_ast_manager) << "\n"; tout << "tree:\n" << *m_tree << "\n"; ); SASSERT(m_vars[i] >= 0); } #endif SASSERT(head->m_next == 0); m_seq.push_back(m_ct_manager.mk_yield(m_qa, m_mp, m_qa->get_num_decls(), reinterpret_cast(m_vars.begin()))); ptr_vector::iterator it = m_seq.begin(); ptr_vector::iterator end = m_seq.end(); for (; it != end; ++it) { instruction * curr = *it; head->m_next = curr; head = curr; } } void set_next(instruction * instr, instruction * new_next) { if (m_is_tmp_tree) instr->m_next = new_next; else m_ct_manager.set_next(instr, new_next); } /* The nodes in the bottom of the code-tree can have a lot of children in big examples. Example: parent-node: (CHOOSE) (CHECK #1 10) (YIELD ...) (CHOOSE) (CHECK #2 10) (YIELD ...) (CHOOSE) (CHECK #3 10) (YIELD ...) (CHOOSE) (CHECK #4 10) (YIELD ...) (CHOOSE) (CHECK #5 10) (YIELD ...) (CHOOSE) (CHECK #6 10) (YIELD ...) (CHOOSE) (CHECK #7 10) (YIELD ...) (CHOOSE) (CHECK #8 10) (YIELD ...) ... The method find_best_child will traverse this big list, and usually will not find a compatible child. So, I limit the number of simple code sequences that can be traversed. */ #define FIND_BEST_CHILD_THRESHOLD 64 choose * find_best_child(choose * first_child) { unsigned num_too_simple = 0; choose * best_child = 0; unsigned max_compatibility = 0; choose * curr_child = first_child; while (curr_child != 0) { bool simple = false; unsigned curr_compatibility = get_compatibility_measure(curr_child, simple); if (simple) { num_too_simple++; if (num_too_simple > FIND_BEST_CHILD_THRESHOLD) return 0; // it is unlikely we will find a compatible node } if (curr_compatibility > max_compatibility) { best_child = curr_child; max_compatibility = curr_compatibility; } curr_child = curr_child->m_alt; } return best_child; } bool is_compatible(bind * instr) const { unsigned ireg = instr->m_ireg; expr * n = m_registers[ireg]; return n != 0 && is_app(n) && // It is wasteful to use a bind of a ground term. // Actually, in the rest of the code I assume that. !is_ground(n) && to_app(n)->get_decl() == instr->m_label && to_app(n)->get_num_args() == instr->m_num_args; } bool is_compatible(compare * instr) const { unsigned reg1 = instr->m_reg1; unsigned reg2 = instr->m_reg2; return m_registers[reg1] != 0 && m_registers[reg2] != 0 && is_var(m_registers[reg1]) && is_var(m_registers[reg2]) && m_registers[reg1] == m_registers[reg2]; } bool is_compatible(check * instr) const { unsigned reg = instr->m_reg; enode * n = instr->m_enode; if (m_registers[reg] == 0) return false; if (!is_app(m_registers[reg])) return false; if (!to_app(m_registers[reg])->is_ground()) return false; enode * n_prime = mk_enode(m_context, m_qa, to_app(m_registers[reg])); // it is safe to compare the roots because the modifications // on the code tree are chronological. return n->get_root() == n_prime->get_root(); } /** \brief Get the label hash of the pattern stored at register reg. If the pattern is a ground application, then it is viewed as a constant. In this case, we use the field get_lbl_hash() in the enode associated with it. */ unsigned get_pat_lbl_hash(unsigned reg) const { SASSERT(m_registers[reg] != 0); SASSERT(is_app(m_registers[reg])); app * p = to_app(m_registers[reg]); if (p->is_ground()) { enode * e = mk_enode(m_context, m_qa, p); if (!e->has_lbl_hash()) e->set_lbl_hash(m_context); return e->get_lbl_hash(); } else { func_decl * lbl = p->get_decl(); return m_lbl_hasher(lbl); } } /** \brief We say a check operation is semi compatible if it access a register that was not yet processed, and given reg = instr->m_reg 1- is_ground(m_registers[reg]) 2- get_pat_lbl_hash(reg) == m_enode->get_lbl_hash() If that is the case, then a CFILTER is created */ bool is_semi_compatible(check * instr) const { unsigned reg = instr->m_reg; return m_registers[reg] != 0 && // if the register was already checked by another filter, then it doesn't make sense // to check it again. get_check_mark(reg) == NOT_CHECKED && is_ground(m_registers[reg]) && get_pat_lbl_hash(reg) == instr->m_enode->get_lbl_hash(); } /** \brief FILTER is not compatible with ground terms anymore. See CFILTER is the filter used for ground terms. */ bool is_compatible(filter * instr) const { unsigned reg = instr->m_reg; if (m_registers[reg] != 0 && is_app(m_registers[reg]) && !is_ground(m_registers[reg])) { // FILTER is fully compatible if it already contains // the label hash of the pattern stored at reg. unsigned elem = get_pat_lbl_hash(reg); return instr->m_lbl_set.may_contain(elem); } return false; } bool is_cfilter_compatible(filter * instr) const { unsigned reg = instr->m_reg; // only ground terms are considered in CFILTERS if (m_registers[reg] != 0 && is_ground(m_registers[reg])) { // FILTER is fully compatible if it already contains // the label hash of the pattern stored at reg. unsigned elem = get_pat_lbl_hash(reg); return instr->m_lbl_set.may_contain(elem); } return false; } /** \brief See comments at is_semi_compatible(check * instr) and is_compatible(filter * instr). Remark: FILTER is not compatible with ground terms anymore */ bool is_semi_compatible(filter * instr) const { unsigned reg = instr->m_reg; return m_registers[reg] != 0 && get_check_mark(reg) == NOT_CHECKED && is_app(m_registers[reg]) && !is_ground(m_registers[reg]); } bool is_compatible(cont * instr) const { unsigned oreg = instr->m_oreg; for (unsigned i = 0; i < instr->m_num_args; i++) if (m_registers[oreg + i] != 0) return false; return true; } // Threshold for a code sequence (in number of instructions) to be considered simple. #define SIMPLE_SEQ_THRESHOLD 4 /** \brief Return a "compatibility measure" that quantifies how many operations in the branch starting at child are compatible with the patterns in the registers stored in m_todo. Set simple to true, if the tree starting at child is too simple (no branching and less than SIMPLE_SEQ_THRESHOLD instructions) */ unsigned get_compatibility_measure(choose * child, bool & simple) { simple = true; m_to_reset.reset(); unsigned weight = 0; unsigned num_instr = 0; instruction * curr = child->m_next; while (curr != 0 && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { num_instr++; switch (curr->m_opcode) { case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: if (is_compatible(static_cast(curr))) { weight += 4; // the weight of BIND is bigger than COMPARE and CHECK unsigned ireg = static_cast(curr)->m_ireg; app * n = to_app(m_registers[ireg]); unsigned oreg = static_cast(curr)->m_oreg; unsigned num_args = static_cast(curr)->m_num_args; SASSERT(n->get_num_args() == num_args); for (unsigned i = 0; i < num_args; i++) { set_register(oreg + i, n->get_arg(i)); m_to_reset.push_back(oreg + i); } } break; case COMPARE: if (is_compatible(static_cast(curr))) weight += 2; break; case CHECK: if (is_compatible(static_cast(curr))) weight += 2; else if (m_use_filters && is_semi_compatible(static_cast(curr))) weight += 1; break; case CFILTER: if (is_cfilter_compatible(static_cast(curr))) weight += 2; break; case FILTER: if (is_compatible(static_cast(curr))) weight += 2; else if (is_semi_compatible(static_cast(curr))) weight += 1; break; // TODO: Try to reuse IS_CGR instruction default: break; } curr = curr->m_next; } if (num_instr > SIMPLE_SEQ_THRESHOLD || (curr != 0 && curr->m_opcode == CHOOSE)) simple = false; unsigned_vector::iterator it = m_to_reset.begin(); unsigned_vector::iterator end = m_to_reset.end(); for (; it != end; ++it) m_registers[*it] = 0; return weight; } void insert(instruction * head, unsigned first_mp_idx) { for (;;) { m_compatible.reset(); m_incompatible.reset(); TRACE("mam_compiler_detail", tout << "processing head: " << *head << "\n";); instruction * curr = head->m_next; instruction * last = head; while (curr != 0 && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { TRACE("mam_compiler_detail", tout << "processing instr: " << *curr << "\n";); switch (curr->m_opcode) { case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: if (is_compatible(static_cast(curr))) { TRACE("mam_compiler_detail", tout << "compatible\n";); unsigned ireg = static_cast(curr)->m_ireg; SASSERT(m_todo.contains(ireg)); m_todo.erase(ireg); set_check_mark(ireg, NOT_CHECKED); m_compatible.push_back(curr); app * app = to_app(m_registers[ireg]); unsigned oreg = static_cast(curr)->m_oreg; unsigned num_args = static_cast(curr)->m_num_args; for (unsigned i = 0; i < num_args; i++) { set_register(oreg + i, app->get_arg(i)); m_todo.push_back(oreg + i); } } else { TRACE("mam_compiler_detail", tout << "incompatible\n";); m_incompatible.push_back(curr); } break; case CHECK: if (is_compatible(static_cast(curr))) { TRACE("mam_compiler_detail", tout << "compatible\n";); unsigned reg = static_cast(curr)->m_reg; SASSERT(m_todo.contains(reg)); m_todo.erase(reg); set_check_mark(reg, NOT_CHECKED); m_compatible.push_back(curr); } else if (m_use_filters && is_semi_compatible(static_cast(curr))) { TRACE("mam_compiler_detail", tout << "semi compatible\n";); unsigned reg = static_cast(curr)->m_reg; enode * n1 = static_cast(curr)->m_enode; // n1->has_lbl_hash may be false, even // when update_filters is invoked before // executing this method. // // Reason: n1 is a ground subterm of a ground term T. // I incorrectly assumed n1->has_lbl_hash() was true because // update_filters executes set_lbl_hash for all maximal ground terms. // And, I also incorrectly assumed that all arguments of check were // maximal ground terms. This is not true. For example, assume // the code_tree already has the pattern // (f (g x) z) // So, when the pattern (f (g b) x) is compiled a check instruction // is created for a ground subterm b of the maximal ground term (g b). if (!n1->has_lbl_hash()) n1->set_lbl_hash(m_context); unsigned h1 = n1->get_lbl_hash(); unsigned h2 = get_pat_lbl_hash(reg); approx_set s(h1); s.insert(h2); filter * new_instr = m_ct_manager.mk_cfilter(reg, s); set_check_mark(reg, CHECK_SET); m_compatible.push_back(new_instr); m_incompatible.push_back(curr); } else { TRACE("mam_compiler_detail", tout << "incompatible\n";); m_incompatible.push_back(curr); } break; case COMPARE: if (is_compatible(static_cast(curr))) { TRACE("mam_compiler_detail", tout << "compatible\n";); unsigned reg1 = static_cast(curr)->m_reg1; unsigned reg2 = static_cast(curr)->m_reg2; SASSERT(m_todo.contains(reg2)); m_todo.erase(reg1); m_todo.erase(reg2); SASSERT(is_var(m_registers[reg1])); unsigned var_id = to_var(m_registers[reg1])->get_idx(); if (m_vars[var_id] == -1) m_vars[var_id] = reg1; m_compatible.push_back(curr); } else { TRACE("mam_compiler_detail", tout << "incompatible\n";); m_incompatible.push_back(curr); } break; case CFILTER: SASSERT(m_use_filters); if (is_cfilter_compatible(static_cast(curr))) { unsigned reg = static_cast(curr)->m_reg; SASSERT(static_cast(curr)->m_lbl_set.size() == 1); set_check_mark(reg, CHECK_SINGLETON); m_compatible.push_back(curr); } else { m_incompatible.push_back(curr); } break; case FILTER: SASSERT(m_use_filters); if (is_compatible(static_cast(curr))) { TRACE("mam_compiler_detail", tout << "compatible\n";); unsigned reg = static_cast(curr)->m_reg; CTRACE("mam_compiler_bug", !m_todo.contains(reg), { for (unsigned i = 0; i < m_todo.size(); i++) { tout << m_todo[i] << " "; } tout << "\nregisters:\n"; for (unsigned i = 0; i < m_registers.size(); i++) { if (m_registers[i]) { tout << i << ":\n" << mk_pp(m_registers[i], m_ast_manager) << "\n"; } } tout << "quantifier:\n" << mk_pp(m_qa, m_ast_manager) << "\n"; tout << "pattern:\n" << mk_pp(m_mp, m_ast_manager) << "\n"; }); SASSERT(m_todo.contains(reg)); if (static_cast(curr)->m_lbl_set.size() == 1) set_check_mark(reg, CHECK_SINGLETON); else set_check_mark(reg, CHECK_SET); m_compatible.push_back(curr); } else if (is_semi_compatible(static_cast(curr))) { TRACE("mam_compiler_detail", tout << "semi compatible\n";); unsigned reg = static_cast(curr)->m_reg; CTRACE("mam_compiler_bug", !m_todo.contains(reg), { for (unsigned i = 0; i < m_todo.size(); i++) { tout << m_todo[i] << " "; } tout << "\nregisters:\n"; for (unsigned i = 0; i < m_registers.size(); i++) { if (m_registers[i]) { tout << i << ":\n" << mk_pp(m_registers[i], m_ast_manager) << "\n"; } } tout << "quantifier:\n" << mk_pp(m_qa, m_ast_manager) << "\n"; tout << "pattern:\n" << mk_pp(m_mp, m_ast_manager) << "\n"; }); SASSERT(m_todo.contains(reg)); unsigned h = get_pat_lbl_hash(reg); TRACE("mam_lbl_bug", tout << "curr_set: " << static_cast(curr)->m_lbl_set << "\n"; tout << "new hash: " << h << "\n";); set_check_mark(reg, CHECK_SET); approx_set const & s = static_cast(curr)->m_lbl_set; if (s.size() > 1) { m_ct_manager.insert_new_lbl_hash(static_cast(curr), h); m_compatible.push_back(curr); } else { SASSERT(s.size() == 1); approx_set new_s(s); new_s.insert(h); filter * new_instr = m_ct_manager.mk_filter(reg, new_s); m_compatible.push_back(new_instr); m_incompatible.push_back(curr); } } else { TRACE("mam_compiler_detail", tout << "incompatible\n";); m_incompatible.push_back(curr); } break; default: TRACE("mam_compiler_detail", tout << "incompatible\n";); m_incompatible.push_back(curr); break; } last = curr; curr = curr->m_next; } TRACE("mam_compiler", tout << *head << " " << head << "\n"; tout << "m_compatible.size(): " << m_compatible.size() << "\n"; tout << "m_incompatible.size(): " << m_incompatible.size() << "\n";); if (m_incompatible.empty()) { // sequence starting at head is fully compatible SASSERT(curr != 0); SASSERT(curr->m_opcode == CHOOSE); choose * first_child = static_cast(curr); choose * best_child = find_best_child(first_child); if (best_child == 0) { // There is no compatible child // Suppose the sequence is: // head -> c1 -> ... -> (cn == last) -> first_child; // Then we should add // head -> c1 -> ... -> (cn == last) -> new_child // new_child: CHOOSE(first_child) -> linearise choose * new_child = m_ct_manager.mk_choose(first_child); m_num_choices++; set_next(last, new_child); linearise(new_child, first_mp_idx); // DONE return; } else { head = best_child; // CONTINUE from best_child } } else { SASSERT(head->is_init() || !m_compatible.empty()); SASSERT(!m_incompatible.empty()); // Suppose the sequence is: // head -> c1 -> i1 -> c2 -> c3 -> i2 -> first_child_head // where c_j are the compatible instructions, and i_j are the incompatible ones // Then the sequence starting at head should become // head -> c1 -> c2 -> c3 -> new_child_head1 // new_child_head1:CHOOSE(new_child_head2) -> i1 -> i2 -> first_child_head // new_child_head2:NOOP -> linearise() instruction * first_child_head = curr; choose * new_child_head2 = m_ct_manager.mk_noop(); SASSERT(new_child_head2->m_alt == 0); choose * new_child_head1 = m_ct_manager.mk_choose(new_child_head2); m_num_choices++; // set: head -> c1 -> c2 -> c3 -> new_child_head1 curr = head; ptr_vector::iterator it1 = m_compatible.begin(); ptr_vector::iterator end1 = m_compatible.end(); for (; it1 != end1; ++it1) { set_next(curr, *it1); curr = *it1; } set_next(curr, new_child_head1); // set: new_child_head1:CHOOSE(new_child_head2) -> i1 -> i2 -> first_child_head curr = new_child_head1; ptr_vector::iterator it2 = m_incompatible.begin(); ptr_vector::iterator end2 = m_incompatible.end(); for (; it2 != end2; ++it2) { if (curr == new_child_head1) curr->m_next = *it2; // new_child_head1 is a new node, I don't need to save trail else set_next(curr, *it2); curr = *it2; } set_next(curr, first_child_head); // build new_child_head2:NOOP -> linearise() linearise(new_child_head2, first_mp_idx); // DONE return; } } } public: compiler(context & ctx, code_tree_manager & ct_mg, label_hasher & h, bool use_filters = true): m_context(ctx), m_ast_manager(ctx.get_manager()), m_ct_manager(ct_mg), m_lbl_hasher(h), m_use_filters(use_filters) { } context & get_context() { return m_context; } /** \brief Create a new code tree for the given quantifier. - mp: is a pattern of qa that will be used to create the code tree - first_idx: index of mp that will be the "head" (first to be processed) of the multi-pattern. */ code_tree * mk_tree(quantifier * qa, app * mp, unsigned first_idx, bool filter_candidates) { SASSERT(m_ast_manager.is_pattern(mp)); app * p = to_app(mp->get_arg(first_idx)); unsigned num_args = p->get_num_args(); code_tree * r = m_ct_manager.mk_code_tree(p->get_decl(), num_args, filter_candidates); init(r, qa, mp, first_idx); linearise(r->m_root, first_idx); r->m_num_choices = m_num_choices; TRACE("mam_compiler", tout << "new tree for:\n" << mk_pp(mp, m_ast_manager) << "\n" << *r;); return r; } /** \brief Insert a pattern into the code tree. - is_tmp_tree: trail for update operations is created if is_tmp_tree = false. */ void insert(code_tree * tree, quantifier * qa, app * mp, unsigned first_idx, bool is_tmp_tree) { if (tree->expected_num_args() != to_app(mp->get_arg(first_idx))->get_num_args()) { // We have to check the number of arguments because of nary + and * operators. // The E-matching engine that was built when all + and * applications were binary. // We ignore the pattern if it does not have the expected number of arguments. // This is not the ideal solution, but it avoids possible crashes. return; } m_is_tmp_tree = is_tmp_tree; TRACE("mam_compiler", tout << "updating tree with:\n" << mk_pp(mp, m_ast_manager) << "\n";); TRACE("mam_bug", tout << "before insertion\n" << *tree << "\n";); if (!is_tmp_tree) m_ct_manager.save_num_regs(tree); init(tree, qa, mp, first_idx); m_num_choices = tree->m_num_choices; insert(tree->m_root, first_idx); TRACE("mam_bug", tout << "m_num_choices: " << m_num_choices << "\n";); if (m_num_choices > tree->m_num_choices) { if (!is_tmp_tree) m_ct_manager.save_num_choices(tree); tree->m_num_choices = m_num_choices; } TRACE("mam_bug", tout << "m_num_choices: " << m_num_choices << "\n"; tout << "new tree:\n" << *tree;); } }; #ifdef Z3DEBUG bool check_lbls(enode * n) { approx_set lbls; approx_set plbls; enode * first = n; do { lbls |= n->get_lbls(); plbls |= n->get_plbls(); n = n->get_next(); } while (first != n); SASSERT(n->get_root()->get_lbls() == lbls); SASSERT(n->get_root()->get_plbls() == plbls); return true; } #endif // ------------------------------------ // // Code Tree Interpreter // // ------------------------------------ struct backtrack_point { const instruction * m_instr; unsigned m_old_max_generation; unsigned m_old_used_enodes_size; union { enode * m_curr; struct { enode_vector * m_to_recycle; enode * const * m_it; enode * const * m_end; }; }; }; typedef svector backtrack_stack; class interpreter { context & m_context; ast_manager & m_ast_manager; mam & m_mam; bool m_use_filters; enode_vector m_registers; enode_vector m_bindings; enode_vector m_args; backtrack_stack m_backtrack_stack; unsigned m_top; const instruction * m_pc; // auxiliary temporary variables unsigned m_max_generation; // the maximum generation of an app enode processed. unsigned m_curr_max_generation; // temporary var used to store a copy of m_max_generation unsigned m_num_args; unsigned m_oreg; enode * m_n1; enode * m_n2; enode * m_app; const bind * m_b; ptr_vector m_used_enodes; unsigned m_curr_used_enodes_size; ptr_vector m_pattern_instances; // collect the pattern instances... used for computing min_top_generation and max_top_generation pool m_pool; enode_vector * mk_enode_vector() { enode_vector * r = m_pool.mk(); r->reset(); return r; } void recycle_enode_vector(enode_vector * v) { m_pool.recycle(v); } void update_max_generation(enode * n) { m_max_generation = std::max(m_max_generation, n->get_generation()); if (m_ast_manager.has_trace_stream()) m_used_enodes.push_back(n); } // We have to provide the number of expected arguments because we have flat-assoc applications such as +. // Flat-assoc applications may have arbitrary number of arguments. enode * get_first_f_app(func_decl * lbl, unsigned num_expected_args, enode * curr) { enode * first = curr; do { if (curr->get_decl() == lbl && curr->is_cgr() && curr->get_num_args() == num_expected_args) { update_max_generation(curr); return curr; } curr = curr->get_next(); } while (curr != first); return 0; } enode * get_next_f_app(func_decl * lbl, unsigned num_expected_args, enode * first, enode * curr) { curr = curr->get_next(); while (curr != first) { if (curr->get_decl() == lbl && curr->is_cgr() && curr->get_num_args() == num_expected_args) { update_max_generation(curr); return curr; } curr = curr->get_next(); } return 0; } /** \brief Execute the is_cgr instruction. Return true if succeeded, and false if backtracking is needed. */ bool exec_is_cgr(is_cgr const * pc) { unsigned num_args = pc->m_num_args; enode * n = m_registers[pc->m_ireg]; func_decl * f = pc->m_label; enode * first = n; switch (num_args) { case 1: m_args[0] = m_registers[pc->m_iregs[0]]->get_root(); SASSERT(n != 0); do { if (n->get_decl() == f && n->get_arg(0)->get_root() == m_args[0]) { update_max_generation(n); return true; } n = n->get_next(); } while (n != first); return false; case 2: m_args[0] = m_registers[pc->m_iregs[0]]->get_root(); m_args[1] = m_registers[pc->m_iregs[1]]->get_root(); SASSERT(n != 0); do { if (n->get_decl() == f && n->get_arg(0)->get_root() == m_args[0] && n->get_arg(1)->get_root() == m_args[1]) { update_max_generation(n); return true; } n = n->get_next(); } while (n != first); return false; default: { m_args.reserve(num_args+1, 0); for (unsigned i = 0; i < num_args; i++) m_args[i] = m_registers[pc->m_iregs[i]]->get_root(); SASSERT(n != 0); do { if (n->get_decl() == f) { unsigned i = 0; for (; i < num_args; i++) { if (n->get_arg(i)->get_root() != m_args[i]) break; } if (i == num_args) { update_max_generation(n); return true; } } n = n->get_next(); } while (n != first); return false; } } } enode_vector * mk_depth1_vector(enode * n, func_decl * f, unsigned i); enode_vector * mk_depth2_vector(joint2 * j2, func_decl * f, unsigned i); enode * init_continue(cont const * c, unsigned expected_num_args); void display_reg(std::ostream & out, unsigned reg); void display_instr_input_reg(std::ostream & out, instruction const * instr); void display_pc_info(std::ostream & out); #define INIT_ARGS_SIZE 16 public: interpreter(context & ctx, mam & m, bool use_filters): m_context(ctx), m_ast_manager(ctx.get_manager()), m_mam(m), m_use_filters(use_filters) { m_args.resize(INIT_ARGS_SIZE, 0); } ~interpreter() { } void init(code_tree * t) { TRACE("mam_bug", tout << "preparing to match tree:\n" << *t << "\n";); m_registers.reserve(t->get_num_regs(), 0); m_bindings.reserve(t->get_num_regs(), 0); if (m_backtrack_stack.size() < t->get_num_choices()) m_backtrack_stack.resize(t->get_num_choices()); } void execute(code_tree * t) { TRACE("trigger_bug", tout << "execute for code tree:\n"; t->display(tout);); init(t); enode_vector::const_iterator it = t->get_candidates().begin(); enode_vector::const_iterator end = t->get_candidates().end(); if (t->filter_candidates()) { for (; it != end; ++it) { enode * app = *it; if (!app->is_marked() && app->is_cgr()) { execute_core(t, app); app->set_mark(); } } it = t->get_candidates().begin(); for (; it != end; ++it) { enode * app = *it; if (app->is_marked()) app->unset_mark(); } } else { for (; it != end; ++it) { enode * app = *it; TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_owner(), m_ast_manager) << "\n";); if (app->is_cgr()) { TRACE("trigger_bug", tout << "is_cgr\n";); execute_core(t, app); } } } } // init(t) must be invoked before execute_core void execute_core(code_tree * t, enode * n); // Return the min generation of the enodes in m_pattern_instances. unsigned get_min_top_generation() const { SASSERT(!m_pattern_instances.empty()); unsigned min = m_pattern_instances[0]->get_generation(); for (unsigned i = 1; i < m_pattern_instances.size(); i++) { unsigned curr = m_pattern_instances[i]->get_generation(); if (min > curr) min = curr; } return min; } // Return the max generation of the enodes in m_pattern_instances. unsigned get_max_top_generation() const { SASSERT(!m_pattern_instances.empty()); unsigned max = m_pattern_instances[0]->get_generation(); for (unsigned i = 1; i < m_pattern_instances.size(); i++) { unsigned curr = m_pattern_instances[i]->get_generation(); if (max < curr) max = curr; } return max; } }; /** \brief Return a vector with the relevant f-parents of n such that n is the i-th argument. */ enode_vector * interpreter::mk_depth1_vector(enode * n, func_decl * f, unsigned i) { enode_vector * v = mk_enode_vector(); n = n->get_root(); enode_vector::const_iterator it = n->begin_parents(); enode_vector::const_iterator end = n->end_parents(); for (; it != end; ++it) { enode * p = *it; if (p->get_decl() == f && m_context.is_relevant(p) && p->is_cgr() && p->get_arg(i)->get_root() == n) { v->push_back(p); } } return v; } /** \brief Return a vector with the relevant f-parents of each p in joint2 where n is the i-th argument. We say a p is in joint2 if p is the g-parent of m_registers[j2->m_reg] where g is j2->m_decl, and m_registers[j2->m_reg] is the argument j2->m_arg_pos. */ enode_vector * interpreter::mk_depth2_vector(joint2 * j2, func_decl * f, unsigned i) { enode * n = m_registers[j2->m_reg]->get_root(); if (n->get_num_parents() == 0) return 0; enode_vector * v = mk_enode_vector(); enode_vector::const_iterator it1 = n->begin_parents(); enode_vector::const_iterator end1 = n->end_parents(); for (; it1 != end1; ++it1) { enode * p = *it1; if (p->get_decl() == j2->m_decl && m_context.is_relevant(p) && p->is_cgr() && p->get_arg(j2->m_arg_pos)->get_root() == n) { // p is in joint2 p = p->get_root(); enode_vector::const_iterator it2 = p->begin_parents(); enode_vector::const_iterator end2 = p->end_parents(); for (; it2 != end2; ++it2) { enode * p2 = *it2; if (p2->get_decl() == f && m_context.is_relevant(p2) && p2->is_cgr() && p2->get_arg(i)->get_root() == p) { v->push_back(p2); } } } } return v; } enode * interpreter::init_continue(cont const * c, unsigned expected_num_args) { func_decl * lbl = c->m_label; unsigned min_sz = m_context.get_num_enodes_of(lbl); unsigned short num_args = c->m_num_args; enode * r; // quick filter... check if any of the joint points have zero parents... for (unsigned i = 0; i < num_args; i++) { void * bare = c->m_joints[i]; enode * n = 0; switch (GET_TAG(bare)) { case NULL_TAG: goto non_depth1; case GROUND_TERM_TAG: n = UNTAG(enode *, bare); break; case VAR_TAG: n = m_registers[UNBOXINT(bare)]; break; case NESTED_VAR_TAG: goto non_depth1; } r = n->get_root(); if (m_use_filters && r->get_plbls().empty_intersection(c->m_lbl_set)) return 0; if (r->get_num_parents() == 0) return 0; non_depth1: ; } // traverse each joint and select the best one. enode_vector * best_v = 0; for (unsigned i = 0; i < num_args; i++) { enode * bare = c->m_joints[i]; enode_vector * curr_v; switch (GET_TAG(bare)) { case NULL_TAG: curr_v = 0; break; case GROUND_TERM_TAG: curr_v = mk_depth1_vector(UNTAG(enode *, bare), lbl, i); break; case VAR_TAG: curr_v = mk_depth1_vector(m_registers[UNBOXINT(bare)], lbl, i); break; case NESTED_VAR_TAG: curr_v = mk_depth2_vector(UNTAG(joint2 *, bare), lbl, i); break; } if (curr_v != 0) { if (curr_v->size() < min_sz && (best_v == 0 || curr_v->size() < best_v->size())) { if (best_v) recycle_enode_vector(best_v); best_v = curr_v; if (best_v->empty()) { recycle_enode_vector(best_v); return 0; } } else { recycle_enode_vector(curr_v); } } } backtrack_point & bp = m_backtrack_stack[m_top]; bp.m_instr = c; bp.m_old_max_generation = m_max_generation; bp.m_old_used_enodes_size = m_used_enodes.size(); if (best_v == 0) { TRACE("mam_bug", tout << "m_top: " << m_top << ", m_backtrack_stack.size(): " << m_backtrack_stack.size() << "\n"; tout << *c << "\n";); bp.m_to_recycle = 0; bp.m_it = m_context.begin_enodes_of(lbl); bp.m_end = m_context.end_enodes_of(lbl); } else { SASSERT(!best_v->empty()); bp.m_to_recycle = best_v; bp.m_it = best_v->begin(); bp.m_end = best_v->end(); } // find application with the right number of arguments for (; bp.m_it != bp.m_end; ++bp.m_it) { enode * curr = *bp.m_it; if (curr->get_num_args() == expected_num_args && m_context.is_relevant(curr)) break; } if (bp.m_it == bp.m_end) return 0; m_top++; update_max_generation(*(bp.m_it)); return *(bp.m_it); } void interpreter::display_reg(std::ostream & out, unsigned reg) { out << "reg[" << reg << "]: "; enode * n = m_registers[reg]; if (!n) { out << "nil\n"; } else { out << "#" << n->get_owner_id() << ", root: " << n->get_root()->get_owner_id(); if (m_use_filters) out << ", lbls: " << n->get_root()->get_lbls() << " "; out << "\n"; out << mk_pp(n->get_owner(), m_ast_manager) << "\n"; } } void interpreter::display_instr_input_reg(std::ostream & out, const instruction * instr) { switch (instr->m_opcode) { case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN: display_reg(out, 0); break; case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: display_reg(out, static_cast(instr)->m_ireg); break; case COMPARE: display_reg(out, static_cast(instr)->m_reg1); display_reg(out, static_cast(instr)->m_reg2); break; case CHECK: display_reg(out, static_cast(instr)->m_reg); break; case FILTER: display_reg(out, static_cast(instr)->m_reg); break; case YIELD1: case YIELD2: case YIELD3: case YIELD4: case YIELD5: case YIELD6: case YIELDN: for (unsigned i = 0; i < static_cast(instr)->m_num_bindings; i++) { display_reg(out, static_cast(instr)->m_bindings[i]); } break; default: break; } } void interpreter::display_pc_info(std::ostream & out) { out << "executing: " << *m_pc << "\n"; out << "m_pc: " << m_pc << ", next: " << m_pc->m_next; if (m_pc->m_opcode == CHOOSE) out << ", alt: " << static_cast(m_pc)->m_alt; out << "\n"; display_instr_input_reg(out, m_pc); } void interpreter::execute_core(code_tree * t, enode * n) { TRACE("trigger_bug", tout << "interpreter::execute_core\n"; t->display(tout); tout << "\nenode\n" << mk_ismt2_pp(n->get_owner(), m_ast_manager) << "\n";); unsigned since_last_check = 0; #ifdef _PROFILE_MAM #ifdef _PROFILE_MAM_EXPENSIVE if (t->get_watch().get_seconds() > _PROFILE_MAM_THRESHOLD && t->get_counter() % _PROFILE_MAM_EXPENSIVE_FREQ == 0) { std::cout << "EXPENSIVE\n"; t->display(std::cout); } #endif t->get_watch().start(); t->inc_counter(); #endif // It doesn't make sense to process an irrelevant enode. TRACE("mam_execute_core", tout << "EXEC " << t->get_root_lbl()->get_name() << "\n";); SASSERT(m_context.is_relevant(n)); m_pattern_instances.reset(); m_pattern_instances.push_back(n); m_max_generation = n->get_generation(); if (m_ast_manager.has_trace_stream()) { m_used_enodes.reset(); m_used_enodes.push_back(n); } m_pc = t->get_root(); m_registers[0] = n; m_top = 0; main_loop: TRACE("mam_int", display_pc_info(tout);); #ifdef _PROFILE_MAM const_cast(m_pc)->m_counter++; #endif switch (m_pc->m_opcode) { case INIT1: m_app = m_registers[0]; if (m_app->get_num_args() != 1) goto backtrack; m_registers[1] = m_app->get_arg(0); m_pc = m_pc->m_next; goto main_loop; case INIT2: m_app = m_registers[0]; if (m_app->get_num_args() != 2) goto backtrack; m_registers[1] = m_app->get_arg(0); m_registers[2] = m_app->get_arg(1); m_pc = m_pc->m_next; goto main_loop; case INIT3: m_app = m_registers[0]; if (m_app->get_num_args() != 3) goto backtrack; m_registers[1] = m_app->get_arg(0); m_registers[2] = m_app->get_arg(1); m_registers[3] = m_app->get_arg(2); m_pc = m_pc->m_next; goto main_loop; case INIT4: m_app = m_registers[0]; if (m_app->get_num_args() != 4) goto backtrack; m_registers[1] = m_app->get_arg(0); m_registers[2] = m_app->get_arg(1); m_registers[3] = m_app->get_arg(2); m_registers[4] = m_app->get_arg(3); m_pc = m_pc->m_next; goto main_loop; case INIT5: m_app = m_registers[0]; if (m_app->get_num_args() != 5) goto backtrack; m_registers[1] = m_app->get_arg(0); m_registers[2] = m_app->get_arg(1); m_registers[3] = m_app->get_arg(2); m_registers[4] = m_app->get_arg(3); m_registers[5] = m_app->get_arg(4); m_pc = m_pc->m_next; goto main_loop; case INIT6: m_app = m_registers[0]; if (m_app->get_num_args() != 6) goto backtrack; m_registers[1] = m_app->get_arg(0); m_registers[2] = m_app->get_arg(1); m_registers[3] = m_app->get_arg(2); m_registers[4] = m_app->get_arg(3); m_registers[5] = m_app->get_arg(4); m_registers[6] = m_app->get_arg(5); m_pc = m_pc->m_next; goto main_loop; case INITN: m_app = m_registers[0]; m_num_args = m_app->get_num_args(); if (m_num_args != static_cast(m_pc)->m_num_args) goto backtrack; for (unsigned i = 0; i < m_num_args; i++) m_registers[i+1] = m_app->get_arg(i); m_pc = m_pc->m_next; goto main_loop; case COMPARE: m_n1 = m_registers[static_cast(m_pc)->m_reg1]; m_n2 = m_registers[static_cast(m_pc)->m_reg2]; SASSERT(m_n1 != 0); SASSERT(m_n2 != 0); if (m_n1->get_root() != m_n2->get_root()) goto backtrack; m_pc = m_pc->m_next; goto main_loop; case CHECK: m_n1 = m_registers[static_cast(m_pc)->m_reg]; m_n2 = static_cast(m_pc)->m_enode; SASSERT(m_n1 != 0); SASSERT(m_n2 != 0); if (m_n1->get_root() != m_n2->get_root()) goto backtrack; m_pc = m_pc->m_next; goto main_loop; /* CFILTER AND FILTER are handled differently by the compiler The compiler will never merge two CFILTERs with different m_lbl_set fields. Essentially, CFILTER is used to combine CHECK statements, and FILTER for BIND */ case CFILTER: case FILTER: m_n1 = m_registers[static_cast(m_pc)->m_reg]->get_root(); if (static_cast(m_pc)->m_lbl_set.empty_intersection(m_n1->get_lbls())) goto backtrack; m_pc = m_pc->m_next; goto main_loop; case PFILTER: m_n1 = m_registers[static_cast(m_pc)->m_reg]->get_root(); if (static_cast(m_pc)->m_lbl_set.empty_intersection(m_n1->get_plbls())) goto backtrack; m_pc = m_pc->m_next; goto main_loop; case CHOOSE: m_backtrack_stack[m_top].m_instr = m_pc; m_backtrack_stack[m_top].m_old_max_generation = m_max_generation; m_backtrack_stack[m_top].m_old_used_enodes_size = m_used_enodes.size(); m_top++; m_pc = m_pc->m_next; goto main_loop; case NOOP: SASSERT(static_cast(m_pc)->m_alt == 0); m_pc = m_pc->m_next; goto main_loop; case BIND1: #define BIND_COMMON() \ m_n1 = m_registers[static_cast(m_pc)->m_ireg]; \ SASSERT(m_n1 != 0); \ m_oreg = static_cast(m_pc)->m_oreg; \ m_curr_max_generation = m_max_generation; \ m_curr_used_enodes_size = m_used_enodes.size(); \ m_app = get_first_f_app(static_cast(m_pc)->m_label, static_cast(m_pc)->m_num_args, m_n1); \ if (!m_app) \ goto backtrack; \ TRACE("mam_int", tout << "bind candidate: " << mk_pp(m_app->get_owner(), m_ast_manager) << "\n";); \ m_backtrack_stack[m_top].m_instr = m_pc; \ m_backtrack_stack[m_top].m_old_max_generation = m_curr_max_generation; \ m_backtrack_stack[m_top].m_old_used_enodes_size = m_curr_used_enodes_size; \ m_backtrack_stack[m_top].m_curr = m_app; \ m_top++; BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_pc = m_pc->m_next; goto main_loop; case BIND2: BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_pc = m_pc->m_next; goto main_loop; case BIND3: BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_pc = m_pc->m_next; goto main_loop; case BIND4: BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_pc = m_pc->m_next; goto main_loop; case BIND5: BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_registers[m_oreg+4] = m_app->get_arg(4); m_pc = m_pc->m_next; goto main_loop; case BIND6: BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_registers[m_oreg+4] = m_app->get_arg(4); m_registers[m_oreg+5] = m_app->get_arg(5); m_pc = m_pc->m_next; goto main_loop; case BINDN: BIND_COMMON(); m_num_args = static_cast(m_pc)->m_num_args; for (unsigned i = 0; i < m_num_args; i++) m_registers[m_oreg+i] = m_app->get_arg(i); m_pc = m_pc->m_next; goto main_loop; case YIELD1: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[0]]; #define ON_MATCH(NUM) \ m_max_generation = std::max(m_max_generation, get_max_generation(NUM, m_bindings.begin())); \ m_mam.on_match(static_cast(m_pc)->m_qa, \ static_cast(m_pc)->m_pat, \ NUM, \ m_bindings.begin(), \ m_max_generation, m_used_enodes) ON_MATCH(1); goto backtrack; case YIELD2: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[1]]; m_bindings[1] = m_registers[static_cast(m_pc)->m_bindings[0]]; ON_MATCH(2); goto backtrack; case YIELD3: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[2]]; m_bindings[1] = m_registers[static_cast(m_pc)->m_bindings[1]]; m_bindings[2] = m_registers[static_cast(m_pc)->m_bindings[0]]; ON_MATCH(3); goto backtrack; case YIELD4: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[3]]; m_bindings[1] = m_registers[static_cast(m_pc)->m_bindings[2]]; m_bindings[2] = m_registers[static_cast(m_pc)->m_bindings[1]]; m_bindings[3] = m_registers[static_cast(m_pc)->m_bindings[0]]; ON_MATCH(4); goto backtrack; case YIELD5: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[4]]; m_bindings[1] = m_registers[static_cast(m_pc)->m_bindings[3]]; m_bindings[2] = m_registers[static_cast(m_pc)->m_bindings[2]]; m_bindings[3] = m_registers[static_cast(m_pc)->m_bindings[1]]; m_bindings[4] = m_registers[static_cast(m_pc)->m_bindings[0]]; ON_MATCH(5); goto backtrack; case YIELD6: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[5]]; m_bindings[1] = m_registers[static_cast(m_pc)->m_bindings[4]]; m_bindings[2] = m_registers[static_cast(m_pc)->m_bindings[3]]; m_bindings[3] = m_registers[static_cast(m_pc)->m_bindings[2]]; m_bindings[4] = m_registers[static_cast(m_pc)->m_bindings[1]]; m_bindings[5] = m_registers[static_cast(m_pc)->m_bindings[0]]; ON_MATCH(6); goto backtrack; case YIELDN: m_num_args = static_cast(m_pc)->m_num_bindings; for (unsigned i = 0; i < m_num_args; i++) m_bindings[i] = m_registers[static_cast(m_pc)->m_bindings[m_num_args - i - 1]]; ON_MATCH(m_num_args); goto backtrack; case GET_ENODE: m_registers[static_cast(m_pc)->m_oreg] = static_cast(m_pc)->m_enode; m_pc = m_pc->m_next; goto main_loop; case GET_CGR1: #define GET_CGR_COMMON() \ m_n1 = m_context.get_enode_eq_to(static_cast(m_pc)->m_label, static_cast(m_pc)->m_num_args, m_args.c_ptr()); \ if (m_n1 == 0 || !m_context.is_relevant(m_n1)) \ goto backtrack; \ m_registers[static_cast(m_pc)->m_oreg] = m_n1; \ m_pc = m_pc->m_next; \ goto main_loop; #define SET_VAR(IDX) \ m_args[IDX] = m_registers[static_cast(m_pc)->m_iregs[IDX]]; \ if (m_use_filters && static_cast(m_pc)->m_lbl_set.empty_intersection(m_args[IDX]->get_root()->get_plbls())) { \ TRACE("trigger_bug", tout << "m_args[IDX]->get_root():\n" << mk_ismt2_pp(m_args[IDX]->get_root()->get_owner(), m_ast_manager) << "\n"; \ tout << "cgr set: "; static_cast(m_pc)->m_lbl_set.display(tout); tout << "\n"; \ tout << "node set: "; m_args[IDX]->get_root()->get_plbls().display(tout); tout << "\n";); \ goto backtrack; \ } SET_VAR(0); GET_CGR_COMMON(); case GET_CGR2: SET_VAR(0); SET_VAR(1); GET_CGR_COMMON(); case GET_CGR3: SET_VAR(0); SET_VAR(1); SET_VAR(2); GET_CGR_COMMON(); case GET_CGR4: SET_VAR(0); SET_VAR(1); SET_VAR(2); SET_VAR(3); GET_CGR_COMMON(); case GET_CGR5: SET_VAR(0); SET_VAR(1); SET_VAR(2); SET_VAR(3); SET_VAR(4); GET_CGR_COMMON(); case GET_CGR6: SET_VAR(0); SET_VAR(1); SET_VAR(2); SET_VAR(3); SET_VAR(4); SET_VAR(5); GET_CGR_COMMON(); case GET_CGRN: m_num_args = static_cast(m_pc)->m_num_args; m_args.reserve(m_num_args, 0); for (unsigned i = 0; i < m_num_args; i++) m_args[i] = m_registers[static_cast(m_pc)->m_iregs[i]]; GET_CGR_COMMON(); case IS_CGR: if (!exec_is_cgr(static_cast(m_pc))) goto backtrack; m_pc = m_pc->m_next; goto main_loop; case CONTINUE: m_num_args = static_cast(m_pc)->m_num_args; m_oreg = static_cast(m_pc)->m_oreg; m_app = init_continue(static_cast(m_pc), m_num_args); if (m_app == 0) goto backtrack; m_pattern_instances.push_back(m_app); TRACE("mam_int", tout << "continue candidate:\n" << mk_ll_pp(m_app->get_owner(), m_ast_manager);); for (unsigned i = 0; i < m_num_args; i++) m_registers[m_oreg+i] = m_app->get_arg(i); m_pc = m_pc->m_next; goto main_loop; } backtrack: TRACE("mam_int", tout << "backtracking.\n";); if (m_top == 0) { TRACE("mam_int", tout << "no more alternatives.\n";); #ifdef _PROFILE_MAM t->get_watch().stop(); #endif return; // no more alternatives } backtrack_point & bp = m_backtrack_stack[m_top - 1]; m_max_generation = bp.m_old_max_generation; if (m_ast_manager.has_trace_stream()) m_used_enodes.shrink(bp.m_old_used_enodes_size); TRACE("mam_int", tout << "backtrack top: " << bp.m_instr << " " << *(bp.m_instr) << "\n";); #ifdef _PROFILE_MAM if (bp.m_instr->m_opcode != CHOOSE) // CHOOSE has a different status. It is a control flow backtracking. const_cast(bp.m_instr)->m_counter++; #endif if (since_last_check++ > 100) { since_last_check = 0; if (m_context.resource_limits_exceeded()) { // Soft timeout... // Cleanup before exiting while (m_top != 0) { backtrack_point & bp = m_backtrack_stack[m_top - 1]; if (bp.m_instr->m_opcode == CONTINUE && bp.m_to_recycle) recycle_enode_vector(bp.m_to_recycle); m_top--; } #ifdef _PROFILE_MAM t->get_watch().stop(); #endif return; } } switch (bp.m_instr->m_opcode) { case CHOOSE: m_pc = static_cast(bp.m_instr)->m_alt; TRACE("mam_int", tout << "alt: " << m_pc << "\n";); SASSERT(m_pc != 0); m_top--; goto main_loop; case BIND1: #define BBIND_COMMON() m_b = static_cast(bp.m_instr); \ m_n1 = m_registers[m_b->m_ireg]; \ m_app = get_next_f_app(m_b->m_label, m_b->m_num_args, m_n1, bp.m_curr); \ if (m_app == 0) { \ m_top--; \ goto backtrack; \ } \ bp.m_curr = m_app; \ TRACE("mam_int", tout << "bind next candidate:\n" << mk_ll_pp(m_app->get_owner(), m_ast_manager);); \ m_oreg = m_b->m_oreg BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_pc = m_b->m_next; goto main_loop; case BIND2: BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_pc = m_b->m_next; goto main_loop; case BIND3: BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_pc = m_b->m_next; goto main_loop; case BIND4: BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_pc = m_b->m_next; goto main_loop; case BIND5: BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_registers[m_oreg+4] = m_app->get_arg(4); m_pc = m_b->m_next; goto main_loop; case BIND6: BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_registers[m_oreg+4] = m_app->get_arg(4); m_registers[m_oreg+5] = m_app->get_arg(5); m_pc = m_b->m_next; goto main_loop; case BINDN: BBIND_COMMON(); m_num_args = m_b->m_num_args; for (unsigned i = 0; i < m_num_args; i++) m_registers[m_oreg+i] = m_app->get_arg(i); m_pc = m_b->m_next; goto main_loop; case CONTINUE: ++bp.m_it; for (; bp.m_it != bp.m_end; ++bp.m_it) { m_app = *bp.m_it; const cont * c = static_cast(bp.m_instr); // bp.m_it may reference an enode in [begin_enodes_of(lbl), end_enodes_of(lbl)) // This enodes are not necessarily relevant. // So, we must check whether m_context.is_relevant(m_app) is true or not. if (m_app->get_num_args() == c->m_num_args && m_context.is_relevant(m_app)) { // update the pattern instance SASSERT(!m_pattern_instances.empty()); m_pattern_instances.pop_back(); m_pattern_instances.push_back(m_app); // continue succeeded update_max_generation(m_app); TRACE("mam_int", tout << "continue next candidate:\n" << mk_ll_pp(m_app->get_owner(), m_ast_manager);); m_num_args = c->m_num_args; m_oreg = c->m_oreg; for (unsigned i = 0; i < m_num_args; i++) m_registers[m_oreg+i] = m_app->get_arg(i); m_pc = c->m_next; goto main_loop; } } // continue failed if (bp.m_to_recycle) recycle_enode_vector(bp.m_to_recycle); m_top--; goto backtrack; default: UNREACHABLE(); } } // end of execute_core void display_trees(std::ostream & out, const ptr_vector & trees) { ptr_vector::const_iterator it = trees.begin(); ptr_vector::const_iterator end = trees.end(); unsigned lbl = 0; for (; it != end; ++it, ++lbl) { code_tree * tree = *it; if (tree) { out << "tree for f" << lbl << "\n"; out << *tree; } } } // ------------------------------------ // // A mapping from func_label -> code tree. // // ------------------------------------ class code_tree_map { ast_manager & m_ast_manager; compiler & m_compiler; ptr_vector m_trees; // mapping: func_label -> tree mam_trail_stack & m_trail_stack; #ifdef Z3DEBUG context * m_context; #endif class mk_tree_trail : public mam_trail { ptr_vector & m_trees; unsigned m_lbl_id; public: mk_tree_trail(ptr_vector & t, unsigned id):m_trees(t), m_lbl_id(id) {} virtual void undo(mam_impl & m) { dealloc(m_trees[m_lbl_id]); m_trees[m_lbl_id] = 0; } }; public: code_tree_map(ast_manager & m, compiler & c, mam_trail_stack & s): m_ast_manager(m), m_compiler(c), m_trail_stack(s) { } #ifdef Z3DEBUG void set_context(context * c) { m_context = c; } #endif ~code_tree_map() { std::for_each(m_trees.begin(), m_trees.end(), delete_proc()); } /** \brief Add a pattern to the code tree map. - mp: is used a pattern for qa. - first_idx: index to be used as head of the multi-pattern mp */ void add_pattern(quantifier * qa, app * mp, unsigned first_idx) { SASSERT(m_ast_manager.is_pattern(mp)); SASSERT(first_idx < mp->get_num_args()); app * p = to_app(mp->get_arg(first_idx)); func_decl * lbl = p->get_decl(); unsigned lbl_id = lbl->get_decl_id(); m_trees.reserve(lbl_id+1, 0); if (m_trees[lbl_id] == 0) { m_trees[lbl_id] = m_compiler.mk_tree(qa, mp, first_idx, false); SASSERT(m_trees[lbl_id]->expected_num_args() == p->get_num_args()); DEBUG_CODE(m_trees[lbl_id]->set_context(m_context);); m_trail_stack.push(mk_tree_trail(m_trees, lbl_id)); } else { code_tree * tree = m_trees[lbl_id]; // We have to check the number of arguments because of nary + and * operators. // The E-matching engine that was built when all + and * applications were binary. // We ignore the pattern if it does not have the expected number of arguments. // This is not the ideal solution, but it avoids possible crashes. if (tree->expected_num_args() == p->get_num_args()) { m_compiler.insert(tree, qa, mp, first_idx, false); } } DEBUG_CODE(m_trees[lbl_id]->get_patterns().push_back(mp); m_trail_stack.push(push_back_trail(m_trees[lbl_id]->get_patterns()));); TRACE("trigger_bug", tout << "after add_pattern, first_idx: " << first_idx << "\n"; m_trees[lbl_id]->display(tout);); } void reset() { std::for_each(m_trees.begin(), m_trees.end(), delete_proc()); m_trees.reset(); } code_tree * get_code_tree_for(func_decl * lbl) const { unsigned lbl_id = lbl->get_decl_id(); if (lbl_id < m_trees.size()) return m_trees[lbl_id]; else return 0; } ptr_vector::iterator begin_code_trees() { return m_trees.begin(); } ptr_vector::iterator end_code_trees() { return m_trees.end(); } }; // ------------------------------------ // // Path trees AKA inverted path index // // ------------------------------------ /** \brief Temporary object used to encode a path of the form: f.1 -> g.2 -> h.0 These objects are used to update the inverse path index data structure. For example, in the path above, given an enode n, I want to follow the parents p_0 of n that are f-applications, and n is the second argument, then for each such p_0, I want to follow the parents p_1 of p_0 that are g-applications, and p_0 is the third argument. Finally, I want to follow the p_2 parents of p_1 that are h-applications and p_1 is the first argument of p_2. To improve the filtering power of the inverse path index, I'm also storing a ground argument (when possible) in the inverted path index. the idea is to have paths of the form f.1:t.2 -> g.2 -> h.0:s.1 The extra pairs t.2 and s.1 are an extra filter on the parents. The idea is that I want only the f-parents s.t. the third argument is equal to t. */ struct path { func_decl * m_label; unsigned short m_arg_idx; unsigned short m_ground_arg_idx; enode * m_ground_arg; unsigned m_pattern_idx; path * m_child; path (func_decl * lbl, unsigned short arg_idx, unsigned short ground_arg_idx, enode * ground_arg, unsigned pat_idx, path * child): m_label(lbl), m_arg_idx(arg_idx), m_ground_arg_idx(ground_arg_idx), m_ground_arg(ground_arg), m_pattern_idx(pat_idx), m_child(child) { SASSERT(ground_arg != 0 || ground_arg_idx == 0); } }; bool is_equal(path const * p1, path const * p2) { for (;;) { if (p1->m_label != p2->m_label || p1->m_arg_idx != p2->m_arg_idx || p1->m_pattern_idx != p2->m_pattern_idx || (p1->m_child == 0) != (p2->m_child == 0)) { return false; } if (p1->m_child == 0 && p2->m_child == 0) return true; p1 = p1->m_child; p2 = p2->m_child; } } typedef ptr_vector paths; /** \brief Inverted path index data structure. See comments at struct path. */ struct path_tree { func_decl * m_label; unsigned short m_arg_idx; unsigned short m_ground_arg_idx; enode * m_ground_arg; code_tree * m_code; approx_set m_filter; path_tree * m_sibling; path_tree * m_first_child; enode_vector * m_todo; // temporary field used to collect candidates #ifdef _PROFILE_PATH_TREE stopwatch m_watch; unsigned m_counter; unsigned m_num_eq_visited; unsigned m_num_neq_visited; bool m_already_displayed; //!< true if the path_tree was already displayed after reaching _PROFILE_PATH_TREE_THRESHOLD #endif path_tree(path * p, label_hasher & h): m_label(p->m_label), m_arg_idx(p->m_arg_idx), m_ground_arg_idx(p->m_ground_arg_idx), m_ground_arg(p->m_ground_arg), m_code(0), m_filter(h(p->m_label)), m_sibling(0), m_first_child(0), m_todo(0) { #ifdef _PROFILE_PATH_TREE m_counter = 0; m_num_eq_visited = 0; m_num_neq_visited = 0; m_already_displayed = false; #endif } void display(std::ostream & out, unsigned indent) { path_tree * curr = this; while (curr != 0) { for (unsigned i = 0; i < indent; i++) out << " "; out << curr->m_label->get_name() << ":" << curr->m_arg_idx; if (curr->m_ground_arg) out << ":#" << curr->m_ground_arg->get_owner_id() << ":" << curr->m_ground_arg_idx; out << " " << m_filter << " " << m_code; #ifdef _PROFILE_PATH_TREE out << ", counter: " << m_counter << ", num_eq_visited: " << m_num_eq_visited << ", num_neq_visited: " << m_num_neq_visited << ", avg. : " << static_cast(m_num_neq_visited)/static_cast(m_num_neq_visited+m_num_eq_visited); #endif out << "\n"; curr->m_first_child->display(out, indent+1); curr = curr->m_sibling; } } }; typedef std::pair path_tree_pair; // ------------------------------------ // // Matching Abstract Machine Implementation // // ------------------------------------ class mam_impl : public mam { protected: ast_manager & m_ast_manager; bool m_use_filters; mam_trail_stack m_trail_stack; label_hasher m_lbl_hasher; code_tree_manager m_ct_manager; compiler m_compiler; interpreter m_interpreter; code_tree_map m_trees; ptr_vector m_tmp_trees; ptr_vector m_tmp_trees_to_delete; ptr_vector m_to_match; typedef std::pair qp_pair; svector m_new_patterns; // recently added patterns // m_is_plbl[f] is true, then when f(c_1, ..., c_n) becomes relevant, // for each c_i. c_i->get_root()->lbls().insert(lbl_hash(f)) svector m_is_plbl; // m_is_clbl[f] is true, then when n=f(c_1, ..., c_n) becomes relevant, // n->get_root()->lbls().insert(lbl_hash(f)) svector m_is_clbl; // children labels // auxiliary field used to update data-structures... typedef ptr_vector func_decls; vector m_var_parent_labels; region & m_region; region m_tmp_region; path_tree_pair m_pp[APPROX_SET_CAPACITY][APPROX_SET_CAPACITY]; path_tree * m_pc[APPROX_SET_CAPACITY][APPROX_SET_CAPACITY]; pool m_pool; // temporary field used to update path trees. vector m_var_paths; // temporary field used to collect candidates ptr_vector m_todo; obj_hashtable m_shared_enodes; // ground terms that appear in patterns. enode * m_r1; // temp field enode * m_r2; // temp field class add_shared_enode_trail; friend class add_shared_enode_trail; class add_shared_enode_trail : public mam_trail { enode * m_enode; public: add_shared_enode_trail(enode * n):m_enode(n) {} virtual void undo(mam_impl & m) { m.m_shared_enodes.erase(m_enode); } }; #ifdef Z3DEBUG bool m_check_missing_instances; #endif enode_vector * mk_tmp_vector() { enode_vector * r = m_pool.mk(); r->reset(); return r; } void recycle(enode_vector * v) { m_pool.recycle(v); } void add_candidate(code_tree * t, enode * app) { if (t != 0) { TRACE("mam_candidate", tout << "adding candidate:\n" << mk_ll_pp(app->get_owner(), m_ast_manager);); if (!t->has_candidates()) m_to_match.push_back(t); t->add_candidate(app); } } void add_candidate(enode * app) { func_decl * lbl = app->get_decl(); add_candidate(m_trees.get_code_tree_for(lbl), app); } bool is_plbl(func_decl * lbl) const { unsigned lbl_id = lbl->get_decl_id(); return lbl_id < m_is_plbl.size() && m_is_plbl[lbl_id]; } bool is_clbl(func_decl * lbl) const { unsigned lbl_id = lbl->get_decl_id(); return lbl_id < m_is_clbl.size() && m_is_clbl[lbl_id]; } void update_lbls(enode * n, unsigned elem) { approx_set & r_lbls = n->get_root()->get_lbls(); if (!r_lbls.may_contain(elem)) { m_trail_stack.push(mam_value_trail(r_lbls)); r_lbls.insert(elem); } } void update_clbls(func_decl * lbl) { unsigned lbl_id = lbl->get_decl_id(); m_is_clbl.reserve(lbl_id+1, false); TRACE("trigger_bug", tout << "update_clbls: " << lbl->get_name() << " is already clbl: " << m_is_clbl[lbl_id] << "\n";); TRACE("mam_bug", tout << "update_clbls: " << lbl->get_name() << " is already clbl: " << m_is_clbl[lbl_id] << "\n";); if (m_is_clbl[lbl_id]) return; m_trail_stack.push(set_bitvector_trail(m_is_clbl, lbl_id)); SASSERT(m_is_clbl[lbl_id]); unsigned h = m_lbl_hasher(lbl); enode_vector::const_iterator it = m_context.begin_enodes_of(lbl); enode_vector::const_iterator end = m_context.end_enodes_of(lbl); for (; it != end; ++it) { enode * app = *it; if (m_context.is_relevant(app)) { update_lbls(app, h); TRACE("mam_bug", tout << "updating labels of: #" << app->get_owner_id() << "\n"; tout << "new_elem: " << h << "\n"; tout << "lbls: " << app->get_lbls() << "\n"; tout << "r.lbls: " << app->get_root()->get_lbls() << "\n";); } } } void update_children_plbls(enode * app, unsigned char elem) { unsigned num_args = app->get_num_args(); for (unsigned i = 0; i < num_args; i++) { enode * c = app->get_arg(i); approx_set & r_plbls = c->get_root()->get_plbls(); if (!r_plbls.may_contain(elem)) { m_trail_stack.push(mam_value_trail(r_plbls)); r_plbls.insert(elem); TRACE("trigger_bug", tout << "updating plabels of:\n" << mk_ismt2_pp(c->get_root()->get_owner(), m_ast_manager) << "\n"; tout << "new_elem: " << static_cast(elem) << "\n"; tout << "plbls: " << c->get_root()->get_plbls() << "\n";); TRACE("mam_bug", tout << "updating plabels of: #" << c->get_root()->get_owner_id() << "\n"; tout << "new_elem: " << static_cast(elem) << "\n"; tout << "plbls: " << c->get_root()->get_plbls() << "\n";); } } } void update_plbls(func_decl * lbl) { unsigned lbl_id = lbl->get_decl_id(); m_is_plbl.reserve(lbl_id+1, false); TRACE("trigger_bug", tout << "update_plbls: " << lbl->get_name() << " is already plbl: " << m_is_plbl[lbl_id] << ", lbl_id: " << lbl_id << "\n"; tout << "mam: " << this << "\n";); TRACE("mam_bug", tout << "update_plbls: " << lbl->get_name() << " is already plbl: " << m_is_plbl[lbl_id] << "\n";); if (m_is_plbl[lbl_id]) return; m_trail_stack.push(set_bitvector_trail(m_is_plbl, lbl_id)); SASSERT(m_is_plbl[lbl_id]); SASSERT(is_plbl(lbl)); unsigned h = m_lbl_hasher(lbl); enode_vector::const_iterator it = m_context.begin_enodes_of(lbl); enode_vector::const_iterator end = m_context.end_enodes_of(lbl); for (; it != end; ++it) { enode * app = *it; if (m_context.is_relevant(app)) update_children_plbls(app, h); } } void reset_pp_pc() { for (unsigned i = 0; i < APPROX_SET_CAPACITY; i++) { for (unsigned j = 0; j < APPROX_SET_CAPACITY; j++) { m_pp[i][j].first = 0; m_pp[i][j].second = 0; m_pc[i][j] = 0; } } } code_tree * mk_code(quantifier * qa, app * mp, unsigned pat_idx) { SASSERT(m_ast_manager.is_pattern(mp)); return m_compiler.mk_tree(qa, mp, pat_idx, true); } void insert_code(path_tree * t, quantifier * qa, app * mp, unsigned pat_idx) { SASSERT(m_ast_manager.is_pattern(mp)); m_compiler.insert(t->m_code, qa, mp, pat_idx, false); } path_tree * mk_path_tree(path * p, quantifier * qa, app * mp) { SASSERT(m_ast_manager.is_pattern(mp)); SASSERT(p != 0); unsigned pat_idx = p->m_pattern_idx; path_tree * head = 0; path_tree * curr = 0; path_tree * prev = 0; while (p != 0) { curr = new (m_region) path_tree(p, m_lbl_hasher); if (prev) prev->m_first_child = curr; if (!head) head = curr; prev = curr; p = p->m_child; } curr->m_code = mk_code(qa, mp, pat_idx); m_trail_stack.push(new_obj_trail(curr->m_code)); return head; } void insert(path_tree * t, path * p, quantifier * qa, app * mp) { SASSERT(m_ast_manager.is_pattern(mp)); path_tree * head = t; path_tree * prev_sibling = 0; bool found_label = false; while (t != 0) { if (t->m_label == p->m_label) { found_label = true; if (t->m_arg_idx == p->m_arg_idx && t->m_ground_arg == p->m_ground_arg && t->m_ground_arg_idx == p->m_ground_arg_idx ) { // found compatible node if (t->m_first_child == 0) { if (p->m_child == 0) { SASSERT(t->m_code != 0); insert_code(t, qa, mp, p->m_pattern_idx); } else { m_trail_stack.push(set_ptr_trail(t->m_first_child)); t->m_first_child = mk_path_tree(p->m_child, qa, mp); } } else { if (p->m_child == 0) { if (t->m_code) { insert_code(t, qa, mp, p->m_pattern_idx); } else { m_trail_stack.push(set_ptr_trail(t->m_code)); t->m_code = mk_code(qa, mp, p->m_pattern_idx); m_trail_stack.push(new_obj_trail(t->m_code)); } } else { insert(t->m_first_child, p->m_child, qa, mp); } } return; } } prev_sibling = t; t = t->m_sibling; } m_trail_stack.push(set_ptr_trail(prev_sibling->m_sibling)); prev_sibling->m_sibling = mk_path_tree(p, qa, mp); if (!found_label) { m_trail_stack.push(value_trail(head->m_filter)); head->m_filter.insert(m_lbl_hasher(p->m_label)); } } void update_pc(unsigned char h1, unsigned char h2, path * p, quantifier * qa, app * mp) { if (m_pc[h1][h2]) { insert(m_pc[h1][h2], p, qa, mp); } else { m_trail_stack.push(set_ptr_trail(m_pc[h1][h2])); m_pc[h1][h2] = mk_path_tree(p, qa, mp); } TRACE("mam_path_tree_updt", tout << "updated path tree:\n"; m_pc[h1][h2]->display(tout, 2);); } void update_pp(unsigned char h1, unsigned char h2, path * p1, path * p2, quantifier * qa, app * mp) { if (h1 == h2) { SASSERT(m_pp[h1][h2].second == 0); if (m_pp[h1][h2].first) { insert(m_pp[h1][h2].first, p1, qa, mp); if (!is_equal(p1, p2)) insert(m_pp[h1][h2].first, p2, qa, mp); } else { m_trail_stack.push(set_ptr_trail(m_pp[h1][h2].first)); m_pp[h1][h2].first = mk_path_tree(p1, qa, mp); insert(m_pp[h1][h2].first, p2, qa, mp); } } else { if (h1 > h2) { std::swap(h1, h2); std::swap(p1, p2); } if (m_pp[h1][h2].first) { SASSERT(m_pp[h1][h2].second); insert(m_pp[h1][h2].first, p1, qa, mp); insert(m_pp[h1][h2].second, p2, qa, mp); } else { SASSERT(m_pp[h1][h2].second == 0); m_trail_stack.push(set_ptr_trail(m_pp[h1][h2].first)); m_trail_stack.push(set_ptr_trail(m_pp[h1][h2].second)); m_pp[h1][h2].first = mk_path_tree(p1, qa, mp); m_pp[h1][h2].second = mk_path_tree(p2, qa, mp); } } TRACE("mam_path_tree_updt", tout << "updated path tree:\n"; SASSERT(h1 <= h2); m_pp[h1][h2].first->display(tout, 2); if (h1 != h2) { m_pp[h1][h2].second->display(tout, 2); }); } void update_vars(unsigned short var_id, path * p, quantifier * qa, app * mp) { paths & var_paths = m_var_paths[var_id]; bool found = false; paths::iterator it = var_paths.begin(); paths::iterator end = var_paths.end(); for (; it != end; ++it) { path * curr_path = *it; if (is_equal(p, curr_path)) found = true; func_decl * lbl1 = curr_path->m_label; func_decl * lbl2 = p->m_label; update_plbls(lbl1); update_plbls(lbl2); update_pp(m_lbl_hasher(lbl1), m_lbl_hasher(lbl2), curr_path, p, qa, mp); } if (!found) var_paths.push_back(p); } enode * get_ground_arg(app * pat, quantifier * qa, unsigned & pos) { pos = 0; unsigned num_args = pat->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = pat->get_arg(i); if (is_ground(arg)) { pos = i; return mk_enode(m_context, qa, to_app(arg)); } } return 0; } /** \brief Update inverted path index with respect to pattern pat in the context of path p. pat is a sub-expression of mp->get_arg(pat_idx). mp is a multi-pattern of qa. If p == 0, then mp->get_arg(pat_idx) == pat. */ void update_filters(app * pat, path * p, quantifier * qa, app * mp, unsigned pat_idx) { unsigned short num_args = pat->get_num_args(); unsigned ground_arg_pos = 0; enode * ground_arg = get_ground_arg(pat, qa, ground_arg_pos); func_decl * plbl = pat->get_decl(); for (unsigned short i = 0; i < num_args; i++) { expr * child = pat->get_arg(i); path * new_path = new (m_tmp_region) path(plbl, i, ground_arg_pos, ground_arg, pat_idx, p); if (is_var(child)) { update_vars(to_var(child)->get_idx(), new_path, qa, mp); continue; } SASSERT(is_app(child)); if (to_app(child)->is_ground()) { enode * n = mk_enode(m_context, qa, to_app(child)); update_plbls(plbl); if (!n->has_lbl_hash()) n->set_lbl_hash(m_context); TRACE("mam_bug", tout << "updating pc labels " << plbl->get_name() << " " << static_cast(n->get_lbl_hash()) << "\n"; tout << "#" << n->get_owner_id() << " " << n->get_root()->get_lbls() << "\n"; tout << "relevant: " << m_context.is_relevant(n) << "\n";); update_pc(m_lbl_hasher(plbl), n->get_lbl_hash(), new_path, qa, mp); continue; } func_decl * clbl = to_app(child)->get_decl(); TRACE("mam_bug", tout << "updating pc labels " << plbl->get_name() << " " << clbl->get_name() << "\n";); update_plbls(plbl); update_clbls(clbl); update_pc(m_lbl_hasher(plbl), m_lbl_hasher(clbl), new_path, qa, mp); update_filters(to_app(child), new_path, qa, mp, pat_idx); } } /** \brief Update inverted path index. */ void update_filters(quantifier * qa, app * mp) { TRACE("mam_bug", tout << "updating filters using:\n" << mk_pp(mp, m_ast_manager) << "\n";); unsigned num_vars = qa->get_num_decls(); if (num_vars >= m_var_paths.size()) m_var_paths.resize(num_vars+1); for (unsigned i = 0; i < num_vars; i++) m_var_paths[i].reset(); m_tmp_region.reset(); // Given a multi-pattern (p_1, ..., p_n) // We need to update the filters using patterns: // (p_1, p_2, ..., p_n) // (p_2, p_1, ..., p_n) // ... // (p_n, p_2, ..., p_1) unsigned num_patterns = mp->get_num_args(); for (unsigned i = 0; i < num_patterns; i++) { app * pat = to_app(mp->get_arg(i)); update_filters(pat, 0, qa, mp, i); } } void display_filter_info(std::ostream & out) { for (unsigned i = 0; i < APPROX_SET_CAPACITY; i++) { for (unsigned j = 0; j < APPROX_SET_CAPACITY; j++) { if (m_pp[i][j].first) { out << "pp[" << i << "][" << j << "]:\n"; m_pp[i][j].first->display(out, 1); if (i != j) { m_pp[i][j].second->display(out, 1); } } if (m_pc[i][j]) { out << "pc[" << i << "][" << j << "]:\n"; m_pc[i][j]->display(out, 1); } } } } /** \brief Check equality modulo the equality m_r1 = m_r2 */ bool is_eq(enode * n1, enode * n2) { return n1->get_root() == n2->get_root() || (n1->get_root() == m_r1 && n2->get_root() == m_r2) || (n2->get_root() == m_r1 && n1->get_root() == m_r2); } /** \brief Collect new E-matching candidates using the inverted path index t. */ void collect_parents(enode * r, path_tree * t) { if (t == 0) return; #ifdef _PROFILE_PATH_TREE t->m_watch.start(); #endif m_todo.reset(); enode_vector * to_unmark = mk_tmp_vector(); enode_vector * to_unmark2 = mk_tmp_vector(); SASSERT(to_unmark->empty()); SASSERT(to_unmark2->empty()); t->m_todo = mk_tmp_vector(); t->m_todo->push_back(r); m_todo.push_back(t); unsigned head = 0; while (head < m_todo.size()) { path_tree * t = m_todo[head]; #ifdef _PROFILE_PATH_TREE t->m_counter++; #endif TRACE("mam_path_tree", tout << "processing:\n"; t->display(tout, 2);); enode_vector * v = t->m_todo; approx_set & filter = t->m_filter; head++; #ifdef _PROFILE_PATH_TREE static unsigned counter = 0; static unsigned total_sz = 0; static unsigned max_sz = 0; counter++; total_sz += v->size(); if (v->size() > max_sz) max_sz = v->size(); if (counter % 100000 == 0) std::cout << "Avg. " << static_cast(total_sz)/static_cast(counter) << ", Max. " << max_sz << "\n"; #endif enode_vector::iterator it1 = v->begin(); enode_vector::iterator end1 = v->end(); for (; it1 != end1; ++it1) { // Two different kinds of mark are used: // - enode mark field: it is used to mark the already processed parents. // - enode mark2 field: it is used to mark the roots already added to be processed in the next level. // // In a previous version of Z3, the "enode mark field" was used to mark both cases. This is incorrect, // and Z3 may fail to find potential new matches. // // The file regression\acu.sx exposed this problem. enode * curr_child = (*it1)->get_root(); if (m_use_filters && curr_child->get_plbls().empty_intersection(filter)) continue; #ifdef _PROFILE_PATH_TREE static unsigned counter2 = 0; static unsigned total_sz2 = 0; static unsigned max_sz2 = 0; counter2++; total_sz2 += curr_child->get_num_parents(); if (curr_child->get_num_parents() > max_sz2) max_sz2 = curr_child->get_num_parents(); if (counter2 % 100000 == 0) std::cout << "Avg2. " << static_cast(total_sz2)/static_cast(counter2) << ", Max2. " << max_sz2 << "\n"; #endif TRACE("mam_path_tree", tout << "processing: #" << curr_child->get_owner_id() << "\n";); enode_vector::const_iterator it2 = curr_child->begin_parents(); enode_vector::const_iterator end2 = curr_child->end_parents(); for (; it2 != end2; ++it2) { enode * curr_parent = *it2; #ifdef _PROFILE_PATH_TREE if (curr_parent->is_eq()) t->m_num_eq_visited++; else t->m_num_neq_visited++; #endif // Remark: equality is never in the inverted path index. if (curr_parent->is_eq()) continue; func_decl * lbl = curr_parent->get_decl(); bool is_flat_assoc = lbl->is_flat_associative(); enode * curr_parent_root = curr_parent->get_root(); enode * curr_parent_cg = curr_parent->get_cg(); TRACE("mam_path_tree", tout << "processing parent:\n" << mk_pp(curr_parent->get_owner(), m_ast_manager) << "\n";); TRACE("mam_path_tree", tout << "parent is marked: " << curr_parent->is_marked() << "\n";); if (filter.may_contain(m_lbl_hasher(lbl)) && !curr_parent->is_marked() && (curr_parent_cg == curr_parent || !is_eq(curr_parent_cg, curr_parent_root)) && m_context.is_relevant(curr_parent) ) { path_tree * curr_tree = t; while (curr_tree) { if (curr_tree->m_label == lbl && // Starting at Z3 3.0, some associative operators (e.g., + and *) are represented using n-ary applications. // In this cases, we say the declarations is is_flat_assoc(). // The MAM was implemented in Z3 2.0 when the following invariant was true: // For every application f(x_1, ..., x_n) of a function symbol f, n = f->get_arity(). // Starting at Z3 3.0, this is only true if !f->is_flat_associative(). // Thus, we need the extra checks. (!is_flat_assoc || (curr_tree->m_arg_idx < curr_parent->get_num_args() && curr_tree->m_ground_arg_idx < curr_parent->get_num_args()))) { enode * curr_parent_child = curr_parent->get_arg(curr_tree->m_arg_idx)->get_root(); if (// Filter 1. the curr_child is equal to child of the current parent. curr_child == curr_parent_child && // Filter 2. ( // curr_tree has no support for the filter based on a ground argument. curr_tree->m_ground_arg == 0 || // checks whether the child of the parent is equal to the expected ground argument. is_eq(curr_tree->m_ground_arg, curr_parent->get_arg(curr_tree->m_ground_arg_idx)) )) { if (curr_tree->m_code) { TRACE("mam_path_tree", tout << "found candidate\n";); add_candidate(curr_tree->m_code, curr_parent); } if (curr_tree->m_first_child) { path_tree * child = curr_tree->m_first_child; if (child->m_todo == 0) { child->m_todo = mk_tmp_vector(); m_todo.push_back(child); } if (!curr_parent_root->is_marked2()) { child->m_todo->push_back(curr_parent_root); } } } } curr_tree = curr_tree->m_sibling; } curr_parent->set_mark(); to_unmark->push_back(curr_parent); if (!curr_parent_root->is_marked2()) { curr_parent_root->set_mark2(); to_unmark2->push_back(curr_parent_root); } } } } recycle(t->m_todo); t->m_todo = 0; // remove both marks. unmark_enodes(to_unmark->size(), to_unmark->c_ptr()); unmark_enodes2(to_unmark2->size(), to_unmark2->c_ptr()); to_unmark->reset(); to_unmark2->reset(); } recycle(to_unmark); recycle(to_unmark2); #ifdef _PROFILE_PATH_TREE t->m_watch.stop(); if (t->m_counter % _PROFILE_PATH_TREE_THRESHOLD == 0) { std::cout << "EXPENSIVE " << t->m_watch.get_seconds() << " secs, counter: " << t->m_counter << "\n"; t->display(std::cout, 0); t->m_already_displayed = true; } #endif } void process_pp(enode * r1, enode * r2) { approx_set & plbls1 = r1->get_plbls(); approx_set & plbls2 = r2->get_plbls(); TRACE("incremental_matcher", tout << "pp: plbls1: " << plbls1 << ", plbls2: " << plbls2 << "\n";); TRACE("mam_info", tout << "pp: " << plbls1.size() * plbls2.size() << "\n";); if (!plbls1.empty() && !plbls2.empty()) { approx_set::iterator it1 = plbls1.begin(); approx_set::iterator end1 = plbls1.end(); for (; it1 != end1; ++it1) { unsigned plbl1 = *it1; SASSERT(plbls1.may_contain(plbl1)); approx_set::iterator it2 = plbls2.begin(); approx_set::iterator end2 = plbls2.end(); for (; it2 != end2; ++it2) { unsigned plbl2 = *it2; SASSERT(plbls2.may_contain(plbl2)); unsigned n_plbl1 = plbl1; unsigned n_plbl2 = plbl2; enode * n_r1 = r1; enode * n_r2 = r2; if (n_plbl1 > n_plbl2) { std::swap(n_plbl1, n_plbl2); std::swap(n_r1, n_r2); } if (n_plbl1 == n_plbl2) { SASSERT(m_pp[n_plbl1][n_plbl2].second == 0); if (n_r1->get_num_parents() <= n_r2->get_num_parents()) collect_parents(n_r1, m_pp[n_plbl1][n_plbl2].first); else collect_parents(n_r2, m_pp[n_plbl1][n_plbl2].first); } else { SASSERT(n_plbl1 < n_plbl2); if (n_r1->get_num_parents() <= n_r2->get_num_parents()) collect_parents(n_r1, m_pp[n_plbl1][n_plbl2].first); else collect_parents(n_r2, m_pp[n_plbl1][n_plbl2].second); } } } } } void process_pc(enode * r1, enode * r2) { approx_set & plbls = r1->get_plbls(); approx_set & clbls = r2->get_lbls(); if (!plbls.empty() && !clbls.empty()) { approx_set::iterator it1 = plbls.begin(); approx_set::iterator end1 = plbls.end(); for (; it1 != end1; ++it1) { unsigned plbl1 = *it1; SASSERT(plbls.may_contain(plbl1)); approx_set::iterator it2 = clbls.begin(); approx_set::iterator end2 = clbls.end(); for (; it2 != end2; ++it2) { unsigned lbl2 = *it2; SASSERT(clbls.may_contain(lbl2)); collect_parents(r1, m_pc[plbl1][lbl2]); } } } } void match_new_patterns() { TRACE("mam_new_pat", tout << "matching new patterns:\n";); m_tmp_trees_to_delete.reset(); svector::iterator it1 = m_new_patterns.begin(); svector::iterator end1 = m_new_patterns.end(); for (; it1 != end1; ++it1) { quantifier * qa = it1->first; app * mp = it1->second; SASSERT(m_ast_manager.is_pattern(mp)); app * p = to_app(mp->get_arg(0)); func_decl * lbl = p->get_decl(); if (m_context.get_num_enodes_of(lbl) > 0) { unsigned lbl_id = lbl->get_decl_id(); m_tmp_trees.reserve(lbl_id+1, 0); if (m_tmp_trees[lbl_id] == 0) { m_tmp_trees[lbl_id] = m_compiler.mk_tree(qa, mp, 0, false); m_tmp_trees_to_delete.push_back(lbl); } else { m_compiler.insert(m_tmp_trees[lbl_id], qa, mp, 0, true); } } } ptr_vector::iterator it2 = m_tmp_trees_to_delete.begin(); ptr_vector::iterator end2 = m_tmp_trees_to_delete.end(); for (; it2 != end2; ++it2) { func_decl * lbl = *it2; unsigned lbl_id = lbl->get_decl_id(); code_tree * tmp_tree = m_tmp_trees[lbl_id]; SASSERT(tmp_tree != 0); SASSERT(m_context.get_num_enodes_of(lbl) > 0); m_interpreter.init(tmp_tree); enode_vector::const_iterator it3 = m_context.begin_enodes_of(lbl); enode_vector::const_iterator end3 = m_context.end_enodes_of(lbl); for (; it3 != end3; ++it3) { enode * app = *it3; if (m_context.is_relevant(app)) m_interpreter.execute_core(tmp_tree, app); } m_tmp_trees[lbl_id] = 0; dealloc(tmp_tree); } m_new_patterns.reset(); } void collect_ground_exprs(quantifier * qa, app * mp) { ptr_buffer todo; unsigned num_patterns = mp->get_num_args(); for (unsigned i = 0; i < num_patterns; i++) { app * pat = to_app(mp->get_arg(i)); TRACE("mam_pat", tout << mk_ismt2_pp(qa, m_ast_manager) << "\npat:\n" << mk_ismt2_pp(pat, m_ast_manager) << "\n";); SASSERT(!pat->is_ground()); todo.push_back(pat); } while (!todo.empty()) { app * n = todo.back(); todo.pop_back(); if (n->is_ground()) { enode * e = mk_enode(m_context, qa, n); m_trail_stack.push(add_shared_enode_trail(e)); m_shared_enodes.insert(e); } else { unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (is_app(arg)) todo.push_back(to_app(arg)); } } } } public: mam_impl(context & ctx, bool use_filters): mam(ctx), m_ast_manager(ctx.get_manager()), m_use_filters(use_filters), m_trail_stack(*this), m_ct_manager(m_lbl_hasher, m_trail_stack), m_compiler(ctx, m_ct_manager, m_lbl_hasher, use_filters), m_interpreter(ctx, *this, use_filters), m_trees(m_ast_manager, m_compiler, m_trail_stack), m_region(m_trail_stack.get_region()), m_r1(0), m_r2(0) { DEBUG_CODE(m_trees.set_context(&ctx);); DEBUG_CODE(m_check_missing_instances = false;); reset_pp_pc(); } virtual ~mam_impl() { m_trail_stack.reset(); } virtual void add_pattern(quantifier * qa, app * mp) { SASSERT(m_ast_manager.is_pattern(mp)); TRACE("trigger_bug", tout << "adding pattern\n" << mk_ismt2_pp(qa, m_ast_manager) << "\n" << mk_ismt2_pp(mp, m_ast_manager) << "\n";); TRACE("mam_bug", tout << "adding pattern\n" << mk_pp(qa, m_ast_manager) << "\n" << mk_pp(mp, m_ast_manager) << "\n";); // Z3 checks if a pattern is ground or not before solving. // Ground patterns are discarded. // However, the simplifier may turn a non-ground pattern into a ground one. // So, we should check it again here. unsigned num_patterns = mp->get_num_args(); for (unsigned i = 0; i < num_patterns; i++) if (is_ground(mp->get_arg(i))) return; // ignore multi-pattern containing ground pattern. update_filters(qa, mp); collect_ground_exprs(qa, mp); m_new_patterns.push_back(qp_pair(qa, mp)); // The matching abstract machine implements incremental // e-matching. So, for a multi-pattern [ p_1, ..., p_n ], // we have to make n insertions. In the i-th insertion, // the pattern p_i is assumed to be the first one. for (unsigned i = 0; i < num_patterns; i++) m_trees.add_pattern(qa, mp, i); } virtual void push_scope() { m_trail_stack.push_scope(); } virtual void pop_scope(unsigned num_scopes) { if (!m_to_match.empty()) { ptr_vector::iterator it = m_to_match.begin(); ptr_vector::iterator end = m_to_match.end(); for (; it != end; ++it) { code_tree * t = *it; t->reset_candidates(); } m_to_match.reset(); } m_new_patterns.reset(); m_trail_stack.pop_scope(num_scopes); } virtual void reset() { m_trail_stack.reset(); m_trees.reset(); m_to_match.reset(); m_new_patterns.reset(); m_is_plbl.reset(); m_is_clbl.reset(); reset_pp_pc(); m_tmp_region.reset(); } virtual void display(std::ostream& out) { out << "mam:\n"; m_lbl_hasher.display(out); ptr_vector::iterator it = m_trees.begin_code_trees(); ptr_vector::iterator end = m_trees.end_code_trees(); for (; it != end; ++it) { if (*it) (*it)->display(out); } } virtual void match() { TRACE("trigger_bug", tout << "match\n"; display(tout);); ptr_vector::iterator it = m_to_match.begin(); ptr_vector::iterator end = m_to_match.end(); for (; it != end; ++it) { code_tree * t = *it; SASSERT(t->has_candidates()); m_interpreter.execute(t); t->reset_candidates(); } m_to_match.reset(); if (!m_new_patterns.empty()) { match_new_patterns(); m_new_patterns.reset(); } } virtual void rematch(bool use_irrelevant) { ptr_vector::iterator it = m_trees.begin_code_trees(); ptr_vector::iterator end = m_trees.end_code_trees(); unsigned lbl = 0; for (; it != end; ++it, ++lbl) { code_tree * t = *it; if (t) { m_interpreter.init(t); func_decl * lbl = t->get_root_lbl(); enode_vector::const_iterator it2 = m_context.begin_enodes_of(lbl); enode_vector::const_iterator end2 = m_context.end_enodes_of(lbl); for (; it2 != end2; ++it2) { enode * curr = *it2; if (use_irrelevant || m_context.is_relevant(curr)) m_interpreter.execute_core(t, curr); } } } } #ifdef Z3DEBUG virtual bool check_missing_instances() { TRACE("missing_instance", tout << "checking for missing instances...\n";); flet l(m_check_missing_instances, true); rematch(false); return true; } #endif virtual void on_match(quantifier * qa, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, ptr_vector & used_enodes) { TRACE("trigger_bug", tout << "found match\n";); #ifdef Z3DEBUG if (m_check_missing_instances) { if (!m_context.slow_contains_instance(qa, num_bindings, bindings)) { TRACE("missing_instance", tout << "qa:\n" << mk_ll_pp(qa, m_ast_manager) << "\npat:\n" << mk_ll_pp(pat, m_ast_manager); for (unsigned i = 0; i < num_bindings; i++) tout << "#" << bindings[i]->get_owner_id() << "\n" << mk_ll_pp(bindings[i]->get_owner(), m_ast_manager) << "\n"; ); UNREACHABLE(); } return; } for (unsigned i = 0; i < num_bindings; i++) { SASSERT(bindings[i]->get_generation() <= max_generation); } #endif m_context.add_instance(qa, pat, num_bindings, bindings, max_generation, m_interpreter.get_min_top_generation(), m_interpreter.get_max_top_generation(), used_enodes); } virtual bool is_shared(enode * n) const { return m_shared_enodes.contains(n); } // This method is invoked when n becomes relevant. // If lazy == true, then n is not added to the list of candidate enodes for matching. That is, the method just updates the lbls. virtual void relevant_eh(enode * n, bool lazy) { TRACE("trigger_bug", tout << "relevant_eh:\n" << mk_ismt2_pp(n->get_owner(), m_ast_manager) << "\n"; tout << "mam: " << this << "\n";); TRACE("mam", tout << "relevant_eh: #" << n->get_owner_id() << "\n";); if (n->has_lbl_hash()) update_lbls(n, n->get_lbl_hash()); if (n->get_num_args() > 0) { func_decl * lbl = n->get_decl(); unsigned h = m_lbl_hasher(lbl); TRACE("trigger_bug", tout << "lbl: " << lbl->get_name() << " is_clbl(lbl): " << is_clbl(lbl) << ", is_plbl(lbl): " << is_plbl(lbl) << ", h: " << h << "\n"; tout << "lbl_id: " << lbl->get_decl_id() << "\n";); if (is_clbl(lbl)) update_lbls(n, h); if (is_plbl(lbl)) update_children_plbls(n, h); TRACE("mam_bug", tout << "adding relevant candidate:\n" << mk_ll_pp(n->get_owner(), m_ast_manager) << "\n";); if (!lazy) add_candidate(n); } } virtual bool has_work() const { return !m_to_match.empty() || !m_new_patterns.empty(); } virtual void add_eq_eh(enode * r1, enode * r2) { flet l1(m_r1, r1); flet l2(m_r2, r2); TRACE("mam", tout << "add_eq_eh: #" << r1->get_owner_id() << " #" << r2->get_owner_id() << "\n";); TRACE("mam_inc_bug_detail", m_context.display(tout);); TRACE("mam_inc_bug", tout << "before:\n#" << r1->get_owner_id() << " #" << r2->get_owner_id() << "\n"; tout << "r1.lbls: " << r1->get_lbls() << "\n"; tout << "r2.lbls: " << r2->get_lbls() << "\n"; tout << "r1.plbls: " << r1->get_plbls() << "\n"; tout << "r2.plbls: " << r2->get_plbls() << "\n";); process_pc(r1, r2); process_pc(r2, r1); process_pp(r1, r2); approx_set r1_plbls = r1->get_plbls(); approx_set & r2_plbls = r2->get_plbls(); approx_set r1_lbls = r1->get_lbls(); approx_set & r2_lbls = r2->get_lbls(); m_trail_stack.push(mam_value_trail(r2_lbls)); m_trail_stack.push(mam_value_trail(r2_plbls)); r2_lbls |= r1_lbls; r2_plbls |= r1_plbls; TRACE("mam_inc_bug", tout << "after:\n"; tout << "r1.lbls: " << r1->get_lbls() << "\n"; tout << "r2.lbls: " << r2->get_lbls() << "\n"; tout << "r1.plbls: " << r1->get_plbls() << "\n"; tout << "r2.plbls: " << r2->get_plbls() << "\n";); SASSERT(approx_subset(r1->get_plbls(), r2->get_plbls())); SASSERT(approx_subset(r1->get_lbls(), r2->get_lbls())); } }; mam * mk_mam(context & ctx) { return alloc(mam_impl, ctx, true); } }; #ifdef Z3DEBUG void pp(smt::code_tree * c) { c->display(std::cout); } #endif z3-z3-4.4.1/src/smt/mam.h000066400000000000000000000026521260446376700147470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mam.h Abstract: Matching Abstract Machine Author: Leonardo de Moura (leonardo) 2007-02-13. Revision History: --*/ #ifndef MAM_H_ #define MAM_H_ #include"ast.h" #include"smt_types.h" namespace smt { /** \brief Matching Abstract Machine (MAM) */ class mam { protected: context & m_context; public: mam(context & ctx): m_context(ctx) { } virtual ~mam() { } virtual void add_pattern(quantifier * q, app * mp) = 0; virtual void push_scope() = 0; virtual void pop_scope(unsigned num_scopes) = 0; virtual void match() = 0; virtual void rematch(bool use_irrelevant = false) = 0; virtual bool has_work() const = 0; virtual void relevant_eh(enode * n, bool lazy) = 0; virtual void add_eq_eh(enode * r1, enode * r2) = 0; virtual void reset() = 0; virtual void display(std::ostream& out) = 0; virtual void on_match(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, ptr_vector & used_enodes) = 0; virtual bool is_shared(enode * n) const = 0; #ifdef Z3DEBUG virtual bool check_missing_instances() = 0; #endif }; mam * mk_mam(context & ctx); }; #endif /* MAM_H_ */ z3-z3-4.4.1/src/smt/old_interval.cpp000066400000000000000000000552411260446376700172140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: old_interval.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-12-09. Revision History: --*/ #include"old_interval.h" void ext_numeral::neg() { switch (m_kind) { case MINUS_INFINITY: m_kind = PLUS_INFINITY; break; case FINITE: m_value.neg(); break; case PLUS_INFINITY: m_kind = MINUS_INFINITY; break; } } ext_numeral & ext_numeral::operator+=(ext_numeral const & other) { SASSERT(!is_infinite() || !other.is_infinite() || m_kind == other.m_kind); if (is_infinite()) return *this; SASSERT(m_kind == FINITE); switch (other.m_kind) { case MINUS_INFINITY: m_kind = MINUS_INFINITY; m_value.reset(); return *this; case FINITE: m_value += other.m_value; return *this; case PLUS_INFINITY: m_kind = PLUS_INFINITY; m_value.reset(); return *this; } UNREACHABLE(); return *this; } ext_numeral & ext_numeral::operator-=(ext_numeral const & other) { SASSERT(!is_infinite() || !other.is_infinite() || (m_kind != other.m_kind)); if (is_infinite()) return *this; SASSERT(m_kind == FINITE); switch (other.m_kind) { case MINUS_INFINITY: m_kind = PLUS_INFINITY; m_value.reset(); return *this; case FINITE: m_value -= other.m_value; return *this; case PLUS_INFINITY: m_kind = MINUS_INFINITY; m_value.reset(); return *this; } UNREACHABLE(); return *this; } ext_numeral & ext_numeral::operator*=(ext_numeral const & other) { if (is_zero()) { m_kind = FINITE; return *this; } if (other.is_zero()) { m_kind = FINITE; m_value.reset(); return *this; } if (is_infinite() || other.is_infinite()) { if (sign() == other.sign()) m_kind = PLUS_INFINITY; else m_kind = MINUS_INFINITY; m_value.reset(); return *this; } SASSERT(m_kind == FINITE); m_value *= other.m_value; return *this; } void ext_numeral::expt(unsigned n) { switch (m_kind) { case MINUS_INFINITY: if (n % 2 == 0) m_kind = PLUS_INFINITY; return; case FINITE: m_value = m_value.expt(n); break; case PLUS_INFINITY: // do nothing break; } } void ext_numeral::inv() { SASSERT(!is_zero()); if (is_infinite()) { m_kind = FINITE; m_value.reset(); } else { m_value = rational(1) / m_value; } } void ext_numeral::display(std::ostream & out) const { switch (m_kind) { case MINUS_INFINITY: out << "-oo"; break; case FINITE: out << m_value; break; case PLUS_INFINITY: out << "oo"; break; } } bool operator==(ext_numeral const & n1, ext_numeral const & n2) { return n1.m_kind == n2.m_kind && (n1.is_infinite() || n1.m_value == n2.m_value); } bool operator<(ext_numeral const & n1, ext_numeral const & n2) { if (n1.is_infinite()) return n1.m_kind == ext_numeral::MINUS_INFINITY && n2.m_kind != ext_numeral::MINUS_INFINITY; if (n2.is_infinite()) return n2.m_kind != ext_numeral::MINUS_INFINITY; return n1.m_value < n2.m_value; } /** \brief Create interval (-oo, oo) */ interval::interval(v_dependency_manager & m): m_manager(m), m_lower(false), m_upper(true), m_lower_open(true), m_upper_open(true), m_lower_dep(0), m_upper_dep(0) { } /** \brief Create intervals [l,u], (l,u], [l, u), (l,u), where l and u are numerals. */ interval::interval(v_dependency_manager & m, rational const & lower, bool l_open, v_dependency * l_dep, rational const & upper, bool u_open, v_dependency * u_dep): m_manager(m), m_lower(lower), m_upper(upper), m_lower_open(l_open), m_upper_open(u_open), m_lower_dep(l_dep), m_upper_dep(u_dep) { SASSERT(lower <= upper); SASSERT(lower != upper || !l_open || !u_open); } /** \brief Create intervals [l,u], (l,u], [l, u), (l,u), where l and u are ext_numerals. */ interval::interval(v_dependency_manager & m, ext_numeral const & lower, bool l_open, v_dependency * l_dep, ext_numeral const & upper, bool u_open, v_dependency * u_dep): m_manager(m), m_lower(lower), m_upper(upper), m_lower_open(l_open), m_upper_open(u_open), m_lower_dep(l_dep), m_upper_dep(u_dep) { SASSERT(lower <= upper); SASSERT(lower != upper || !l_open || !u_open); } /** \brief Create interval [val,val] */ interval::interval(v_dependency_manager & m, rational const & val, v_dependency * l_dep, v_dependency * u_dep): m_manager(m), m_lower(val), m_upper(val), m_lower_open(false), m_upper_open(false), m_lower_dep(l_dep), m_upper_dep(u_dep) { } /** \brief Create intervals (-oo, val], (-oo, val), [val, oo), (val, oo) */ interval::interval(v_dependency_manager & m, rational const & val, bool open, bool lower, v_dependency * d): m_manager(m) { if (lower) { m_lower = ext_numeral(val); m_lower_open = open; m_lower_dep = d; m_upper = ext_numeral(true); m_upper_open = true; m_upper_dep = 0; } else { m_lower = ext_numeral(false); m_lower_open = true; m_lower_dep = 0; m_upper = ext_numeral(val); m_upper_open = open; m_upper_dep = d; } } interval::interval(interval const & other): m_manager(other.m_manager), m_lower(other.m_lower), m_upper(other.m_upper), m_lower_open(other.m_lower_open), m_upper_open(other.m_upper_open), m_lower_dep(other.m_lower_dep), m_upper_dep(other.m_upper_dep) { } interval & interval::operator=(interval const & other) { m_lower = other.m_lower; m_upper = other.m_upper; m_lower_open = other.m_lower_open; m_upper_open = other.m_upper_open; m_lower_dep = other.m_lower_dep; m_upper_dep = other.m_upper_dep; return *this; } interval & interval::operator+=(interval const & other) { m_lower += other.m_lower; m_upper += other.m_upper; m_lower_open |= other.m_lower_open; m_upper_open |= other.m_upper_open; m_lower_dep = m_lower.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, other.m_lower_dep); m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_upper_dep, other.m_upper_dep); return *this; } void interval::neg() { std::swap(m_lower, m_upper); std::swap(m_lower_open, m_upper_open); std::swap(m_lower_dep, m_upper_dep); m_lower.neg(); m_upper.neg(); } interval & interval::operator-=(interval const & other) { interval tmp(other); tmp.neg(); return operator+=(tmp); } v_dependency * interval::join(v_dependency * d1, v_dependency * d2, v_dependency * d3, v_dependency * d4) { return m_manager.mk_join(m_manager.mk_join(d1, d2), m_manager.mk_join(d3,d4)); } /** \brief Create a new v_dependency using d1, d2, and (opt1 or opt2). */ v_dependency * interval::join_opt(v_dependency * d1, v_dependency * d2, v_dependency * opt1, v_dependency * opt2) { if (opt1 == d1 || opt1 == d2) return join(d1, d2); if (opt2 == d1 || opt2 == d2) return join(d1, d2); if (opt1 == 0 || opt2 == 0) return join(d1, d2); // TODO: more opts... return join(d1, d2, opt1); } interval & interval::operator*=(interval const & other) { #if Z3DEBUG || _TRACE bool contains_zero1 = contains_zero(); bool contains_zero2 = other.contains_zero(); #endif if (is_zero()) { return *this; } if (other.is_zero()) { *this = other; m_lower_dep = m_manager.mk_join(m_lower_dep, m_upper_dep); m_upper_dep = m_lower_dep; return *this; } ext_numeral const & a = m_lower; ext_numeral const & b = m_upper; ext_numeral const & c = other.m_lower; ext_numeral const & d = other.m_upper; bool a_o = m_lower_open; bool b_o = m_upper_open; bool c_o = other.m_lower_open; bool d_o = other.m_upper_open; v_dependency * a_d = m_lower_dep; v_dependency * b_d = m_upper_dep; v_dependency * c_d = other.m_lower_dep; v_dependency * d_d = other.m_upper_dep; TRACE("interval_bug", tout << "operator*= " << *this << " " << other << "\n";); if (is_N()) { if (other.is_N()) { // x <= b <= 0, y <= d <= 0 --> b*d <= x*y // a <= x <= b <= 0, c <= y <= d <= 0 --> x*y <= a*c (we can use the fact that x or y is always negative (i.e., b is neg or d is neg)) TRACE("interval_bug", tout << "(N, N)\n";); ext_numeral new_lower = b * d; ext_numeral new_upper = a * c; // if b = 0 (and the interval is closed), then the lower bound is closed m_lower_open = (is_N0() || other.is_N0()) ? false : (b_o || d_o); m_upper_open = a_o || c_o; SASSERT(a.is_neg() && c.is_neg()); m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? 0 : join(b_d, d_d); m_upper_dep = m_upper.is_infinite() ? 0 : join_opt(a_d, c_d, b_d, d_d); } else if (other.is_M()) { // a <= x <= b <= 0, y <= d, d > 0 --> a*d <= x*y (uses the fact that b is not positive) // a <= x <= b <= 0, c <= y, c < 0 --> x*y <= a*c (uses the fact that b is not positive) TRACE("interval_bug", tout << "(N, M)\n";); ext_numeral new_lower = a * d; SASSERT(new_lower.is_neg()); ext_numeral new_upper = a * c; SASSERT(new_upper.is_pos()); m_lower_open = a_o || d_o; m_upper_open = a_o || c_o; m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, d_d, b_d); m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, c_d, b_d); } else { // a <= x <= b <= 0, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that x is neg (b is not positive) or y is pos (c is not negative)) // x <= b <= 0, 0 <= c <= y --> x*y <= b*c TRACE("interval_bug", tout << "(N, P)\n";); SASSERT(other.is_P()); ext_numeral new_lower = a * d; ext_numeral new_upper = b * c; bool is_N0_old = is_N0(); // see comment in (P, N) case m_lower_open = a_o || d_o; SASSERT(a.is_neg() && d.is_pos()); m_upper_open = (is_N0_old || other.is_P0()) ? false : (b_o || c_o); m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? 0 : join_opt(a_d, d_d, b_d, c_d); m_upper_dep = m_upper.is_infinite() ? 0 : join(b_d, c_d); } } else if (is_M()) { if (other.is_N()) { // b > 0, x <= b, c <= y <= d <= 0 --> b*c <= x*y (uses the fact that d is not positive) // a < 0, a <= x, c <= y <= d <= 0 --> x*y <= a*c (uses the fact that d is not positive) TRACE("interval_bug", tout << "(M, N)\n";); ext_numeral new_lower = b * c; SASSERT(new_lower.is_neg()); ext_numeral new_upper = a * c; SASSERT(new_upper.is_pos()); m_lower_open = b_o || c_o; SASSERT(b.is_pos() && c.is_neg()); m_upper_open = a_o || c_o; SASSERT(a.is_neg() && c.is_neg()); m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? 0 : join(b_d, c_d, d_d); m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, c_d, d_d); } else if (other.is_M()) { TRACE("interval_bug", tout << "(M, M)\n";); SASSERT(!a.is_zero() && !b.is_zero() && !c.is_zero() && !d.is_zero()); ext_numeral ad = a*d; SASSERT(!ad.is_zero()); ext_numeral bc = b*c; SASSERT(!bc.is_zero()); ext_numeral ac = a*c; SASSERT(!ac.is_zero()); ext_numeral bd = b*d; SASSERT(!bd.is_zero()); bool ad_o = a_o || d_o; bool bc_o = b_o || c_o; bool ac_o = a_o || c_o; bool bd_o = b_o || d_o; if (ad < bc || (ad == bc && !ad_o && bc_o)) { m_lower = ad; m_lower_open = ad_o; } else { m_lower = bc; m_lower_open = bc_o; } if (ac > bd || (ac == bd && !ac_o && bd_o)) { m_upper = ac; m_upper_open = ac_o; } else { m_upper = bd; m_upper_open = bd_o; } m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, b_d, c_d, d_d); m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, b_d, c_d, d_d); } else { // a < 0, a <= x, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that c is not negative) // b > 0, x <= b, 0 <= c <= y <= d --> x*y <= b*d (uses the fact that c is not negative) TRACE("interval_bug", tout << "(M, P)\n";); SASSERT(other.is_P()); ext_numeral new_lower = a * d; SASSERT(new_lower.is_neg()); ext_numeral new_upper = b * d; SASSERT(new_upper.is_pos()); m_lower_open = a_o || d_o; SASSERT(a.is_neg() && d.is_pos()); m_upper_open = b_o || d_o; SASSERT(b.is_pos() && d.is_pos()); m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, d_d, c_d); m_upper_dep = m_upper.is_infinite() ? 0 : join(b_d, d_d, c_d); } } else { SASSERT(is_P()); if (other.is_N()) { // 0 <= a <= x <= b, c <= y <= d <= 0 --> x*y <= b*c (uses the fact that x is pos (a is not neg) or y is neg (d is not pos)) // 0 <= a <= x, y <= d <= 0 --> a*d <= x*y TRACE("interval_bug", tout << "(P, N)\n";); ext_numeral new_lower = b * c; ext_numeral new_upper = a * d; bool is_P0_old = is_P0(); // cache the value of is_P0(), since it may be affected by the next update. m_lower_open = b_o || c_o; SASSERT(b.is_pos() && c.is_neg()); m_upper_open = (is_P0_old || other.is_N0()) ? false : a_o || d_o; m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? 0 : join_opt(b_d, c_d, a_d, d_d); m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, d_d); } else if (other.is_M()) { // 0 <= a <= x <= b, c <= y --> b*c <= x*y (uses the fact that a is not negative) // 0 <= a <= x <= b, y <= d --> x*y <= b*d (uses the fact that a is not negative) TRACE("interval_bug", tout << "(P, M)\n";); ext_numeral new_lower = b * c; SASSERT(new_lower.is_neg()); ext_numeral new_upper = b * d; SASSERT(new_upper.is_pos()); m_lower_open = b_o || c_o; m_upper_open = b_o || d_o; m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? 0 : join(b_d, c_d, a_d); m_upper_dep = m_upper.is_infinite() ? 0 : join(b_d, d_d, a_d); } else { // 0 <= a <= x, 0 <= c <= y --> a*c <= x*y // x <= b, y <= d --> x*y <= b*d (uses the fact that x is pos (a is not negative) or y is pos (c is not negative)) TRACE("interval_bug", tout << "(P, P)\n";); SASSERT(other.is_P()); ext_numeral new_lower = a * c; ext_numeral new_upper = b * d; m_lower_open = (is_P0() || other.is_P0()) ? false : a_o || c_o; m_upper_open = b_o || d_o; SASSERT(b.is_pos() && d.is_pos()); m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, c_d); m_upper_dep = m_upper.is_infinite() ? 0 : join_opt(b_d, d_d, a_d, c_d); } } TRACE("interval_bug", tout << "operator*= result: " << *this << "\n";); CTRACE("interval", !(!(contains_zero1 || contains_zero2) || contains_zero()), tout << "contains_zero1: " << contains_zero1 << ", contains_zero2: " << contains_zero2 << ", contains_zero(): " << contains_zero() << "\n";); SASSERT(!(contains_zero1 || contains_zero2) || contains_zero()); return *this; } bool interval::contains_zero() const { TRACE("interval_zero_bug", tout << "contains_zero info: " << *this << "\n"; tout << "m_lower.is_neg(): " << m_lower.is_neg() << "\n"; tout << "m_lower.is_zero: " << m_lower.is_zero() << "\n"; tout << "m_lower_open: " << m_lower_open << "\n"; tout << "m_upper.is_pos(): " << m_upper.is_pos() << "\n"; tout << "m_upper.is_zero: " << m_upper.is_zero() << "\n"; tout << "m_upper_open: " << m_upper_open << "\n"; tout << "result: " << ((m_lower.is_neg() || (m_lower.is_zero() && !m_lower_open)) && (m_upper.is_pos() || (m_upper.is_zero() && !m_upper_open))) << "\n";); return (m_lower.is_neg() || (m_lower.is_zero() && !m_lower_open)) && (m_upper.is_pos() || (m_upper.is_zero() && !m_upper_open)); } bool interval::contains(rational const& v) const { if (!inf().is_infinite()) { if (v < inf().to_rational()) return false; if (v == inf().to_rational() && m_lower_open) return false; } if (!sup().is_infinite()) { if (v > sup().to_rational()) return false; if (v == sup().to_rational() && m_upper_open) return false; } return true; } interval & interval::inv() { // If the interval [l,u] does not contain 0, then 1/[l,u] = [1/u, 1/l] SASSERT(!contains_zero()); if (is_P1()) { // 0 < a <= x --> 1/x <= 1/a // 0 < a <= x <= b --> 1/b <= 1/x (use lower and upper bounds) ext_numeral new_lower = m_upper; SASSERT(!m_upper.is_zero()); new_lower.inv(); ext_numeral new_upper; if (m_lower.is_zero()) { SASSERT(m_lower_open); ext_numeral plus_infinity(true); new_upper = plus_infinity; } else { new_upper = m_lower; new_upper.inv(); } m_lower = new_lower; m_upper = new_upper; std::swap(m_lower_open, m_upper_open); v_dependency * new_upper_dep = m_lower_dep; SASSERT(!m_lower.is_infinite()); m_lower_dep = m_manager.mk_join(m_lower_dep, m_upper_dep); m_upper_dep = new_upper_dep; } else if (is_N1()) { // x <= a < 0 --> 1/a <= 1/x // b <= x <= a < 0 --> 1/b <= 1/x (use lower and upper bounds) ext_numeral new_upper = m_lower; SASSERT(!m_lower.is_zero()); new_upper.inv(); ext_numeral new_lower; if (m_upper.is_zero()) { SASSERT(m_upper_open); ext_numeral minus_infinity(false); new_lower = minus_infinity; } else { new_lower = m_upper; new_lower.inv(); } m_lower = new_lower; m_upper = new_upper; std::swap(m_lower_open, m_upper_open); v_dependency * new_lower_dep = m_upper_dep; SASSERT(!m_upper.is_infinite()); m_upper_dep = m_manager.mk_join(m_lower_dep, m_upper_dep); m_lower_dep = new_lower_dep; } else { UNREACHABLE(); } return *this; } interval & interval::operator/=(interval const & other) { SASSERT(!other.contains_zero()); if (is_zero()) { // 0/other = 0 if other != 0 TRACE("interval", other.display_with_dependencies(tout);); if (other.m_lower.is_pos() || (other.m_lower.is_zero() && other.m_lower_open)) { // other.lower > 0 m_lower_dep = join(m_lower_dep, other.m_lower_dep); m_upper_dep = join(m_upper_dep, other.m_lower_dep); } else { // assertion must hold since !other.contains_zero() SASSERT(other.m_upper.is_neg() || (other.m_upper.is_zero() && other.m_upper_open)); // other.upper < 0 m_lower_dep = join(m_lower_dep, other.m_upper_dep); m_upper_dep = join(m_upper_dep, other.m_upper_dep); } return *this; } else { interval tmp(other); tmp.inv(); return operator*=(tmp); } } void interval::expt(unsigned n) { if (n == 1) return; if (n % 2 == 0) { if (m_lower.is_pos()) { // [l, u]^n = [l^n, u^n] if l > 0 // 0 < a <= x --> a^n <= x^n (lower bound guarantees that is positive) // 0 < a <= x <= b --> x^n <= b^n (use lower and upper bound -- need the fact that x is positive) m_lower.expt(n); m_upper.expt(n); m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep); } else if (m_upper.is_neg()) { // [l, u]^n = [u^n, l^n] if u < 0 // a <= x <= b < 0 --> x^n <= a^n (use lower and upper bound -- need the fact that x is negative) // x <= b < 0 --> b^n <= x^n std::swap(m_lower, m_upper); std::swap(m_lower_open, m_upper_open); std::swap(m_lower_dep, m_upper_dep); m_lower.expt(n); m_upper.expt(n); m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep); } else { // [l, u]^n = [0, max{l^n, u^n}] otherwise // we need both bounds to justify upper bound TRACE("interval", tout << "before: " << m_lower << " " << m_upper << " " << n << "\n";); m_lower.expt(n); m_upper.expt(n); TRACE("interval", tout << "after: " << m_lower << " " << m_upper << "\n";); if (m_lower > m_upper || (m_lower == m_upper && !m_lower_open && m_upper_open)) { m_upper = m_lower; m_upper_open = m_lower_open; } m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep); m_lower = ext_numeral(0); m_lower_open = false; m_lower_dep = 0; } } else { // Remark: when n is odd x^n is monotonic. m_lower.expt(n); m_upper.expt(n); } } void interval::display(std::ostream & out) const { out << (m_lower_open ? "(" : "[") << m_lower << ", " << m_upper << (m_upper_open ? ")" : "]"); } void interval::display_with_dependencies(std::ostream & out) const { ptr_vector vs; m_manager.linearize(m_lower_dep, vs); m_manager.linearize(m_upper_dep, vs); out << "["; display(out); out << ", "; bool first = true; ::display(out, vs.begin(), vs.end(), ", ", first); out << "]"; } z3-z3-4.4.1/src/smt/old_interval.h000066400000000000000000000165111260446376700166560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: old_interval.h Abstract: Old interval class. It is still used in some modules. Author: Leonardo de Moura (leonardo) 2008-12-09. Revision History: --*/ #ifndef OLD_INTERVAL_H_ #define OLD_INTERVAL_H_ #include"rational.h" #include"dependency.h" class ext_numeral { public: enum kind { MINUS_INFINITY, FINITE, PLUS_INFINITY }; private: kind m_kind; rational m_value; explicit ext_numeral(kind k):m_kind(k) {} public: ext_numeral():m_kind(FINITE) {} /* zero */ explicit ext_numeral(bool plus_infinity):m_kind(plus_infinity ? PLUS_INFINITY : MINUS_INFINITY) {} explicit ext_numeral(rational const & val):m_kind(FINITE), m_value(val) {} explicit ext_numeral(int i):m_kind(FINITE), m_value(i) {} ext_numeral(ext_numeral const & other):m_kind(other.m_kind), m_value(other.m_value) {} bool is_infinite() const { return m_kind != FINITE; } bool sign() const { return m_kind == MINUS_INFINITY || (m_kind == FINITE && m_value.is_neg()); } void neg(); bool is_zero() const { return m_kind == FINITE && m_value.is_zero(); } bool is_neg() const { return sign(); } bool is_pos() const { return !is_neg() && !is_zero(); } rational const & to_rational() const { SASSERT(!is_infinite()); return m_value; } ext_numeral & operator+=(ext_numeral const & other); ext_numeral & operator-=(ext_numeral const & other); ext_numeral & operator*=(ext_numeral const & other); void inv(); void expt(unsigned n); void display(std::ostream & out) const; friend bool operator==(ext_numeral const & n1, ext_numeral const & n2); friend bool operator<(ext_numeral const & n1, ext_numeral const & n2); }; bool operator==(ext_numeral const & n1, ext_numeral const & n2); bool operator<(ext_numeral const & n1, ext_numeral const & n2); inline bool operator!=(ext_numeral const & n1, ext_numeral const & n2) { return !operator==(n1,n2); } inline bool operator>(ext_numeral const & n1, ext_numeral const & n2) { return operator<(n2, n1); } inline bool operator<=(ext_numeral const & n1, ext_numeral const & n2) { return !operator>(n1, n2); } inline bool operator>=(ext_numeral const & n1, ext_numeral const & n2) { return !operator<(n1, n2); } inline ext_numeral operator+(ext_numeral const & n1, ext_numeral const & n2) { return ext_numeral(n1) += n2; } inline ext_numeral operator-(ext_numeral const & n1, ext_numeral const & n2) { return ext_numeral(n1) -= n2; } inline ext_numeral operator*(ext_numeral const & n1, ext_numeral const & n2) { return ext_numeral(n1) *= n2; } inline std::ostream & operator<<(std::ostream & out, ext_numeral const & n) { n.display(out); return out; } class old_interval { v_dependency_manager & m_manager; ext_numeral m_lower; ext_numeral m_upper; bool m_lower_open; bool m_upper_open; v_dependency * m_lower_dep; // justification for the lower bound v_dependency * m_upper_dep; // justification for the upper bound v_dependency * join(v_dependency * d1, v_dependency * d2) { return m_manager.mk_join(d1, d2); } v_dependency * join(v_dependency * d1, v_dependency * d2, v_dependency * d3) { return m_manager.mk_join(m_manager.mk_join(d1, d2), d3); } v_dependency * join(v_dependency * d1, v_dependency * d2, v_dependency * d3, v_dependency * d4); v_dependency * join_opt(v_dependency * d1, v_dependency * d2, v_dependency * opt1, v_dependency * opt2); public: explicit old_interval(v_dependency_manager & m); explicit old_interval(v_dependency_manager & m, rational const & lower, bool l_open, v_dependency * l_dep, rational const & upper, bool u_open, v_dependency * u_dep); explicit old_interval(v_dependency_manager & m, rational const & val, v_dependency * l_dep = 0, v_dependency * u_dep = 0); explicit old_interval(v_dependency_manager & m, rational const & val, bool open, bool lower, v_dependency * d); explicit old_interval(v_dependency_manager & m, ext_numeral const& lower, bool l_open, v_dependency * l_dep, ext_numeral const & upper, bool u_open, v_dependency * u_dep); old_interval(old_interval const & other); bool minus_infinity() const { return m_lower.is_infinite(); } bool plus_infinity() const { return m_upper.is_infinite(); } bool is_lower_open() const { return m_lower_open; } bool is_upper_open() const { return m_upper_open; } v_dependency * get_lower_dependencies() const { return m_lower_dep; } v_dependency * get_upper_dependencies() const { return m_upper_dep; } rational const & get_lower_value() const { SASSERT(!minus_infinity()); return m_lower.to_rational(); } rational const & get_upper_value() const { SASSERT(!plus_infinity()); return m_upper.to_rational(); } old_interval & operator=(old_interval const & other); old_interval & operator+=(old_interval const & other); old_interval & operator-=(old_interval const & other); old_interval & operator*=(old_interval const & other); old_interval & operator*=(rational const & value); old_interval & operator/=(old_interval const & other); bool operator==(old_interval const & other) const { return m_lower == other.m_lower && m_upper == other.m_upper && m_lower_open == other.m_lower_open && m_upper_open == other.m_upper_open; } bool contains_zero() const; bool contains(rational const& v) const; old_interval & inv(); void expt(unsigned n); void neg(); void display(std::ostream & out) const; void display_with_dependencies(std::ostream & out) const; bool is_P() const { return m_lower.is_pos() || m_lower.is_zero(); } bool is_P0() const { return m_lower.is_zero() && !m_lower_open; } bool is_P1() const { return m_lower.is_pos() || (m_lower.is_zero() && m_lower_open); } bool is_N() const { return m_upper.is_neg() || m_upper.is_zero(); } bool is_N0() const { return m_upper.is_zero() && !m_upper_open; } bool is_N1() const { return m_upper.is_neg() || (m_upper.is_zero() && m_upper_open); } bool is_M() const { return m_lower.is_neg() && m_upper.is_pos(); } bool is_zero() const { return m_lower.is_zero() && m_upper.is_zero(); } ext_numeral const& inf() const { return m_lower; } ext_numeral const& sup() const { return m_upper; } }; inline old_interval operator+(old_interval const & i1, old_interval const & i2) { return old_interval(i1) += i2; } inline old_interval operator-(old_interval const & i1, old_interval const & i2) { return old_interval(i1) -= i2; } inline old_interval operator*(old_interval const & i1, old_interval const & i2) { return old_interval(i1) *= i2; } inline old_interval operator/(old_interval const & i1, old_interval const & i2) { return old_interval(i1) /= i2; } inline old_interval expt(old_interval const & i, unsigned n) { old_interval tmp(i); tmp.expt(n); return tmp; } inline std::ostream & operator<<(std::ostream & out, old_interval const & i) { i.display(out); return out; } struct interval_detail{}; inline std::pair wd(old_interval const & i) { interval_detail d; return std::make_pair(i, d); } inline std::ostream & operator<<(std::ostream & out, std::pair const & p) { p.first.display_with_dependencies(out); return out; } // allow "customers" of this file to keep using interval #define interval old_interval #endif /* OLD_INTERVAL_H_ */ z3-z3-4.4.1/src/smt/params/000077500000000000000000000000001260446376700153025ustar00rootroot00000000000000z3-z3-4.4.1/src/smt/params/dyn_ack_params.cpp000066400000000000000000000011041260446376700207550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dyn_ack_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-05-18. Revision History: --*/ #include"dyn_ack_params.h" #include"smt_params_helper.hpp" void dyn_ack_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_dack = static_cast(p.dack()); m_dack_eq = p.dack_eq(); m_dack_factor = p.dack_factor(); m_dack_threshold = p.dack_threshold(); m_dack_gc = p.dack_gc(); m_dack_gc_inv_decay = p.dack_gc_inv_decay(); }z3-z3-4.4.1/src/smt/params/dyn_ack_params.h000066400000000000000000000017651260446376700204370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dyn_ack_params.h Abstract: Author: Leonardo de Moura (leonardo) 2007-05-18. Revision History: --*/ #ifndef DYN_ACK_PARAMS_H_ #define DYN_ACK_PARAMS_H_ #include"params.h" enum dyn_ack_strategy { DACK_DISABLED, DACK_ROOT, // congruence is the root of the conflict DACK_CR // congruence used during conflict resolution }; struct dyn_ack_params { dyn_ack_strategy m_dack; bool m_dack_eq; double m_dack_factor; unsigned m_dack_threshold; unsigned m_dack_gc; double m_dack_gc_inv_decay; public: dyn_ack_params(params_ref const & p = params_ref()) : m_dack(DACK_ROOT), m_dack_eq(false), m_dack_factor(0.1), m_dack_threshold(10), m_dack_gc(2000), m_dack_gc_inv_decay(0.8) { updt_params(p); } void updt_params(params_ref const & _p); }; #endif /* DYN_ACK_PARAMS_H_ */ z3-z3-4.4.1/src/smt/params/preprocessor_params.cpp000066400000000000000000000014331260446376700221000ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: preprocessor_params.h Abstract: Preprocess AST before adding them to the logical context Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include"preprocessor_params.h" #include"smt_params_helper.hpp" void preprocessor_params::updt_local_params(params_ref const & _p) { smt_params_helper p(_p); m_macro_finder = p.macro_finder(); m_pull_nested_quantifiers = p.pull_nested_quantifiers(); m_refine_inj_axiom = p.refine_inj_axioms(); } void preprocessor_params::updt_params(params_ref const & p) { pattern_inference_params::updt_params(p); bv_simplifier_params::updt_params(p); arith_simplifier_params::updt_params(p); updt_local_params(p); } z3-z3-4.4.1/src/smt/params/preprocessor_params.h000066400000000000000000000050051260446376700215440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: preprocessor_params.h Abstract: Preprocess AST before adding them to the logical context Author: Leonardo de Moura (leonardo) 2008-01-17. Revision History: --*/ #ifndef PREPROCESSOR_PARAMS_H_ #define PREPROCESSOR_PARAMS_H_ #include"pattern_inference_params.h" #include"bit_blaster_params.h" #include"bv_simplifier_params.h" #include"arith_simplifier_params.h" enum lift_ite_kind { LI_NONE, LI_CONSERVATIVE, LI_FULL }; struct preprocessor_params : public pattern_inference_params, public bit_blaster_params, public bv_simplifier_params, public arith_simplifier_params { lift_ite_kind m_lift_ite; lift_ite_kind m_ng_lift_ite; // lift ite for non ground terms bool m_pull_cheap_ite_trees; bool m_pull_nested_quantifiers; bool m_eliminate_term_ite; bool m_eliminate_and; // represent (and a b) as (not (or (not a) (not b))) bool m_macro_finder; bool m_propagate_values; bool m_propagate_booleans; bool m_refine_inj_axiom; bool m_eliminate_bounds; bool m_simplify_bit2int; bool m_nnf_cnf; bool m_distribute_forall; bool m_reduce_args; bool m_quasi_macros; bool m_restricted_quasi_macros; bool m_max_bv_sharing; bool m_pre_simplifier; bool m_nlquant_elim; public: preprocessor_params(params_ref const & p = params_ref()): m_lift_ite(LI_NONE), m_ng_lift_ite(LI_NONE), m_pull_cheap_ite_trees(false), m_pull_nested_quantifiers(false), m_eliminate_term_ite(false), m_eliminate_and(true), m_macro_finder(false), m_propagate_values(true), m_propagate_booleans(false), // TODO << check peformance m_refine_inj_axiom(true), m_eliminate_bounds(false), m_simplify_bit2int(false), m_nnf_cnf(true), m_distribute_forall(false), m_reduce_args(false), m_quasi_macros(false), m_restricted_quasi_macros(false), m_max_bv_sharing(true), m_pre_simplifier(true), m_nlquant_elim(false) { updt_local_params(p); } void updt_local_params(params_ref const & p); void updt_params(params_ref const & p); }; #endif /* PREPROCESSOR_PARAMS_H_ */ z3-z3-4.4.1/src/smt/params/qi_params.cpp000066400000000000000000000016401260446376700177630ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qi_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include"qi_params.h" #include"smt_params_helper.hpp" void qi_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_mbqi = p.mbqi(); m_mbqi_max_cexs = p.mbqi_max_cexs(); m_mbqi_max_cexs_incr = p.mbqi_max_cexs_incr(); m_mbqi_max_iterations = p.mbqi_max_iterations(); m_mbqi_trace = p.mbqi_trace(); m_mbqi_force_template = p.mbqi_force_template(); m_mbqi_id = p.mbqi_id(); m_qi_profile = p.qi_profile(); m_qi_profile_freq = p.qi_profile_freq(); m_qi_max_instances = p.qi_max_instances(); m_qi_eager_threshold = p.qi_eager_threshold(); m_qi_lazy_threshold = p.qi_lazy_threshold(); m_qi_cost = p.qi_cost(); m_qi_max_eager_multipatterns = p.qi_max_multi_patterns(); } z3-z3-4.4.1/src/smt/params/qi_params.h000066400000000000000000000101021260446376700174210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: qi_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-15. Revision History: --*/ #ifndef QI_PARAMS_H_ #define QI_PARAMS_H_ #include"util.h" #include"params.h" enum quick_checker_mode { MC_NO, // do not use (cheap) model checking based instantiation MC_UNSAT, // instantiate unsatisfied instances MC_NO_SAT // instantiate unsatisfied and not-satisfied instances }; struct qi_params { bool m_qi_ematching; std::string m_qi_cost; std::string m_qi_new_gen; double m_qi_eager_threshold; double m_qi_lazy_threshold; unsigned m_qi_max_eager_multipatterns; unsigned m_qi_max_lazy_multipattern_matching; bool m_qi_profile; unsigned m_qi_profile_freq; quick_checker_mode m_qi_quick_checker; bool m_qi_lazy_quick_checker; bool m_qi_promote_unsat; unsigned m_qi_max_instances; bool m_qi_lazy_instantiation; bool m_qi_conservative_final_check; bool m_mbqi; unsigned m_mbqi_max_cexs; unsigned m_mbqi_max_cexs_incr; unsigned m_mbqi_max_iterations; bool m_mbqi_trace; unsigned m_mbqi_force_template; const char * m_mbqi_id; qi_params(params_ref const & p = params_ref()): /* The "weight 0" performance bug ------------------------------ The parameters m_qi_cost and m_qi_new_gen influence quantifier instantiation. - m_qi_cost: specify the cost of a quantifier instantiation. Z3 will block instantiations using m_qi_eager_threshold and m_qi_lazy_threshold. - m_qi_new_gen: specify how the "generation" tag of an enode created by quantifier instantiation is set. Enodes in the input problem have generation 0. Some combinations of m_qi_cost and m_qi_new_gen will prevent Z3 from breaking matching loops. For example, the "Weight 0" peformace bug was triggered by the following combination: - m_qi_cost: (+ weight generation) - m_qi_new_gen: cost If a quantifier has weight 0, then the cost of instantiating it with a term in the input problem has cost 0. The new enodes created during the instantiation will be tagged with generation = const = 0. So, every enode will have generation 0, and consequently every quantifier instantiation will have cost 0. Although dangerous, this feature was requested by the Boogie team. In their case, the patterns are carefully constructred, and there are no matching loops. Moreover, the tag some quantifiers with weight 0 to instruct Z3 to never block their instances. An example is the select-store axiom. They need this feature to be able to analyze code that contains very long execution paths. So, unless requested by the user, the default weight must be > 0. Otherwise, Z3 will execute without any matching loop detection. */ m_qi_cost("(+ weight generation)"), m_qi_new_gen("cost"), m_qi_eager_threshold(10.0), m_qi_lazy_threshold(20.0), // reduced to give a chance to MBQI m_qi_max_eager_multipatterns(0), m_qi_max_lazy_multipattern_matching(2), m_qi_profile(false), m_qi_profile_freq(UINT_MAX), m_qi_quick_checker(MC_NO), m_qi_lazy_quick_checker(true), m_qi_promote_unsat(true), m_qi_max_instances(UINT_MAX), m_qi_lazy_instantiation(false), m_qi_conservative_final_check(false), m_mbqi(true), // enabled by default m_mbqi_max_cexs(1), m_mbqi_max_cexs_incr(1), m_mbqi_max_iterations(1000), m_mbqi_trace(false), m_mbqi_force_template(10), m_mbqi_id(0) { updt_params(p); } void updt_params(params_ref const & p); }; #endif /* QI_PARAMS_H_ */ z3-z3-4.4.1/src/smt/params/smt_params.cpp000066400000000000000000000037521260446376700201630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #include"smt_params.h" #include"smt_params_helper.hpp" #include"model_params.hpp" #include"gparams.h" void smt_params::updt_local_params(params_ref const & _p) { smt_params_helper p(_p); m_auto_config = p.auto_config() && gparams::get_value("auto_config") == "true"; // auto-config is not scoped by smt in gparams. m_random_seed = p.random_seed(); m_relevancy_lvl = p.relevancy(); m_ematching = p.ematching(); m_phase_selection = static_cast(p.phase_selection()); m_restart_strategy = static_cast(p.restart_strategy()); m_restart_factor = p.restart_factor(); m_case_split_strategy = static_cast(p.case_split()); m_delay_units = p.delay_units(); m_delay_units_threshold = p.delay_units_threshold(); m_preprocess = _p.get_bool("preprocess", true); // hidden parameter m_timeout = p.timeout(); m_rlimit = p.rlimit(); m_max_conflicts = p.max_conflicts(); m_core_validate = p.core_validate(); model_params mp(_p); m_model_compact = mp.compact(); if (_p.get_bool("arith.greatest_error_pivot", false)) m_arith_pivot_strategy = ARITH_PIVOT_GREATEST_ERROR; else if (_p.get_bool("arith.least_error_pivot", false)) m_arith_pivot_strategy = ARITH_PIVOT_LEAST_ERROR; theory_array_params::updt_params(_p); } void smt_params::updt_params(params_ref const & p) { preprocessor_params::updt_params(p); dyn_ack_params::updt_params(p); qi_params::updt_params(p); theory_arith_params::updt_params(p); theory_bv_params::updt_params(p); theory_pb_params::updt_params(p); // theory_array_params::updt_params(p); updt_local_params(p); } void smt_params::updt_params(context_params const & p) { m_auto_config = p.m_auto_config; m_model = p.m_model; } z3-z3-4.4.1/src/smt/params/smt_params.h000066400000000000000000000221711260446376700176240ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #ifndef SMT_PARAMS_H_ #define SMT_PARAMS_H_ #include"ast.h" #include"dyn_ack_params.h" #include"qi_params.h" #include"theory_arith_params.h" #include"theory_array_params.h" #include"theory_bv_params.h" #include"theory_pb_params.h" #include"theory_datatype_params.h" #include"preprocessor_params.h" #include"context_params.h" enum phase_selection { PS_ALWAYS_FALSE, PS_ALWAYS_TRUE, PS_CACHING, PS_CACHING_CONSERVATIVE, PS_CACHING_CONSERVATIVE2, // similar to the previous one, but alternated default config from time to time. PS_RANDOM, PS_OCCURRENCE }; enum restart_strategy { RS_GEOMETRIC, RS_IN_OUT_GEOMETRIC, RS_LUBY, RS_FIXED, RS_ARITHMETIC }; enum lemma_gc_strategy { LGC_FIXED, LGC_GEOMETRIC, LGC_AT_RESTART }; enum initial_activity { IA_ZERO, // initialized with 0 IA_RANDOM_WHEN_SEARCHING, // random when searching IA_RANDOM // always random }; enum case_split_strategy { CS_ACTIVITY, // case split based on activity CS_ACTIVITY_DELAY_NEW, // case split based on activity but delay new case splits created during the search CS_ACTIVITY_WITH_CACHE, // case split based on activity and cache the activity CS_RELEVANCY, // case split based on relevancy CS_RELEVANCY_ACTIVITY, // case split based on relevancy and activity CS_RELEVANCY_GOAL, // based on relevancy and the current goal }; struct smt_params : public preprocessor_params, public dyn_ack_params, public qi_params, public theory_arith_params, public theory_array_params, public theory_bv_params, public theory_pb_params, public theory_datatype_params { bool m_display_proof; bool m_display_dot_proof; bool m_display_unsat_core; bool m_check_proof; bool m_eq_propagation; bool m_binary_clause_opt; unsigned m_relevancy_lvl; bool m_relevancy_lemma; unsigned m_random_seed; double m_random_var_freq; double m_inv_decay; unsigned m_clause_decay; initial_activity m_random_initial_activity; phase_selection m_phase_selection; unsigned m_phase_caching_on; unsigned m_phase_caching_off; bool m_minimize_lemmas; unsigned m_max_conflicts; bool m_simplify_clauses; unsigned m_tick; bool m_display_features; bool m_new_core2th_eq; bool m_ematching; // ----------------------------------- // // Case split strategy // // ----------------------------------- case_split_strategy m_case_split_strategy; unsigned m_rel_case_split_order; bool m_lookahead_diseq; // ----------------------------------- // // Delay units... // // ----------------------------------- bool m_delay_units; unsigned m_delay_units_threshold; // ----------------------------------- // // Conflict resolution // // ----------------------------------- bool m_theory_resolve; // ----------------------------------- // // Restart // // ----------------------------------- restart_strategy m_restart_strategy; unsigned m_restart_initial; double m_restart_factor; bool m_restart_adaptive; double m_agility_factor; double m_restart_agility_threshold; // ----------------------------------- // // Lemma garbage collection // // ----------------------------------- lemma_gc_strategy m_lemma_gc_strategy; bool m_lemma_gc_half; unsigned m_recent_lemmas_size; unsigned m_lemma_gc_initial; double m_lemma_gc_factor; unsigned m_new_old_ratio; //!< the ratio of new and old clauses. unsigned m_new_clause_activity; unsigned m_old_clause_activity; unsigned m_new_clause_relevancy; //!< Max. number of unassigned literals to be considered relevant. unsigned m_old_clause_relevancy; //!< Max. number of unassigned literals to be considered relevant. double m_inv_clause_decay; //!< clause activity decay // ----------------------------------- // // SMT-LIB (debug) pretty printer // // ----------------------------------- bool m_smtlib_dump_lemmas; std::string m_smtlib_logic; // ----------------------------------- // // Statistics for Profiling // // ----------------------------------- bool m_profile_res_sub; bool m_display_bool_var2expr; bool m_display_ll_bool_var2expr; bool m_abort_after_preproc; // ----------------------------------- // // Model generation // // ----------------------------------- bool m_model; bool m_model_compact; bool m_model_on_timeout; bool m_model_on_final_check; // ----------------------------------- // // Progress sampling // // ----------------------------------- unsigned m_progress_sampling_freq; // ----------------------------------- // // Debugging goodies // // ----------------------------------- bool m_display_installed_theories; bool m_core_validate; // ----------------------------------- // // From front_end_params // // ----------------------------------- bool m_preprocess; // temporary hack for disabling all preprocessing.. bool m_user_theory_preprocess_axioms; bool m_user_theory_persist_axioms; unsigned m_timeout; unsigned m_rlimit; bool m_at_labels_cex; // only use labels which contains the @ symbol when building multiple counterexamples. bool m_check_at_labels; // check that @ labels are inserted to generate unique counter-examples. bool m_dump_goal_as_smt; bool m_auto_config; smt_params(params_ref const & p = params_ref()): m_display_proof(false), m_display_dot_proof(false), m_display_unsat_core(false), m_check_proof(false), m_eq_propagation(true), m_binary_clause_opt(true), m_relevancy_lvl(2), m_relevancy_lemma(false), m_random_seed(0), m_random_var_freq(0.01), m_inv_decay(1.052), m_clause_decay(1), m_random_initial_activity(IA_RANDOM_WHEN_SEARCHING), m_phase_selection(PS_CACHING_CONSERVATIVE), m_phase_caching_on(400), m_phase_caching_off(100), m_minimize_lemmas(true), m_max_conflicts(UINT_MAX), m_simplify_clauses(true), m_tick(1000), m_display_features(false), m_new_core2th_eq(true), m_ematching(true), m_case_split_strategy(CS_ACTIVITY_DELAY_NEW), m_rel_case_split_order(0), m_lookahead_diseq(false), m_delay_units(false), m_delay_units_threshold(32), m_theory_resolve(false), m_restart_strategy(RS_IN_OUT_GEOMETRIC), m_restart_initial(100), m_restart_factor(1.1), m_restart_adaptive(true), m_agility_factor(0.9999), m_restart_agility_threshold(0.18), m_lemma_gc_strategy(LGC_FIXED), m_lemma_gc_half(false), m_recent_lemmas_size(100), m_lemma_gc_initial(5000), m_lemma_gc_factor(1.1), m_new_old_ratio(16), m_new_clause_activity(10), m_old_clause_activity(500), m_new_clause_relevancy(45), m_old_clause_relevancy(6), m_inv_clause_decay(1), m_smtlib_dump_lemmas(false), m_smtlib_logic("AUFLIA"), m_profile_res_sub(false), m_display_bool_var2expr(false), m_display_ll_bool_var2expr(false), m_abort_after_preproc(false), m_model(true), m_model_compact(false), m_model_on_timeout(false), m_model_on_final_check(false), m_progress_sampling_freq(0), m_display_installed_theories(false), m_core_validate(false), m_preprocess(true), // temporary hack for disabling all preprocessing.. m_user_theory_preprocess_axioms(false), m_user_theory_persist_axioms(false), m_timeout(0), m_rlimit(0), m_at_labels_cex(false), m_check_at_labels(false), m_dump_goal_as_smt(false), m_auto_config(true) { updt_local_params(p); } void updt_local_params(params_ref const & p); void updt_params(params_ref const & p); void updt_params(context_params const & p); }; #endif /* SMT_PARAMS_H_ */ z3-z3-4.4.1/src/smt/params/smt_params_helper.pyg000066400000000000000000000176151260446376700215420ustar00rootroot00000000000000def_module_params(module_name='smt', class_name='smt_params_helper', description='smt solver based on lazy smt', export=True, params=(('auto_config', BOOL, True, 'automatically configure solver'), ('random_seed', UINT, 0, 'random seed for the smt solver'), ('relevancy', UINT, 2, 'relevancy propagation heuristic: 0 - disabled, 1 - relevancy is tracked by only affects quantifier instantiation, 2 - relevancy is tracked, and an atom is only asserted if it is relevant'), ('macro_finder', BOOL, False, 'try to find universally quantified formulas that can be viewed as macros'), ('ematching', BOOL, True, 'E-Matching based quantifier instantiation'), ('phase_selection', UINT, 3, 'phase selection heuristic: 0 - always false, 1 - always true, 2 - phase caching, 3 - phase caching conservative, 4 - phase caching conservative 2, 5 - random, 6 - number of occurrences'), ('restart_strategy', UINT, 1, '0 - geometric, 1 - inner-outer-geometric, 2 - luby, 3 - fixed, 4 - arithmetic'), ('restart_factor', DOUBLE, 1.1, 'when using geometric (or inner-outer-geometric) progression of restarts, it specifies the constant used to multiply the currect restart threshold'), ('case_split', UINT, 1, '0 - case split based on variable activity, 1 - similar to 0, but delay case splits created during the search, 2 - similar to 0, but cache the relevancy, 3 - case split based on relevancy (structural splitting), 4 - case split on relevancy and activity, 5 - case split on relevancy and current goal'), ('delay_units', BOOL, False, 'if true then z3 will not restart when a unit clause is learned'), ('delay_units_threshold', UINT, 32, 'maximum number of learned unit clauses before restarting, ingored if delay_units is false'), ('pull_nested_quantifiers', BOOL, False, 'pull nested quantifiers'), ('refine_inj_axioms', BOOL, True, 'refine injectivity axioms'), ('timeout', UINT, 0, 'timeout (0 means no timeout)'), ('rlimit', UINT, 0, 'resource limit (0 means no limit)'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts before giving up.'), ('mbqi', BOOL, True, 'model based quantifier instantiation (MBQI)'), ('mbqi.max_cexs', UINT, 1, 'initial maximal number of counterexamples used in MBQI, each counterexample generates a quantifier instantiation'), ('mbqi.max_cexs_incr', UINT, 0, 'increment for MBQI_MAX_CEXS, the increment is performed after each round of MBQI'), ('mbqi.max_iterations', UINT, 1000, 'maximum number of rounds of MBQI'), ('mbqi.trace', BOOL, False, 'generate tracing messages for Model Based Quantifier Instantiation (MBQI). It will display a message before every round of MBQI, and the quantifiers that were not satisfied'), ('mbqi.force_template', UINT, 10, 'some quantifiers can be used as templates for building interpretations for functions. Z3 uses heuristics to decide whether a quantifier will be used as a template or not. Quantifiers with weight >= mbqi.force_template are forced to be used as a template'), ('mbqi.id', STRING, '', 'Only use model-based instantiation for quantifiers with id\'s beginning with string'), ('qi.profile', BOOL, False, 'profile quantifier instantiation'), ('qi.profile_freq', UINT, UINT_MAX, 'how frequent results are reported by qi.profile'), ('qi.max_instances', UINT, UINT_MAX, 'maximum number of quantifier instantiations'), ('qi.eager_threshold', DOUBLE, 10.0, 'threshold for eager quantifier instantiation'), ('qi.lazy_threshold', DOUBLE, 20.0, 'threshold for lazy quantifier instantiation'), ('qi.cost', STRING, '(+ weight generation)', 'expression specifying what is the cost of a given quantifier instantiation'), ('qi.max_multi_patterns', UINT, 0, 'specify the number of extra multi patterns'), ('bv.reflect', BOOL, True, 'create enode for every bit-vector term'), ('bv.enable_int2bv', BOOL, False, 'enable support for int2bv and bv2int operators'), ('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'), ('arith.solver', UINT, 2, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation'), ('arith.nl.gb', BOOL, True, 'groebner Basis computation, this option is ignored when arith.nl=false'), ('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters'), ('arith.nl.rounds', UINT, 1024, 'threshold for number of (nested) final checks for non linear arithmetic'), ('arith.euclidean_solver', BOOL, False, 'eucliean solver for linear integer arithmetic'), ('arith.propagate_eqs', BOOL, True, 'propagate (cheap) equalities'), ('arith.propagation_mode', UINT, 2, '0 - no propagation, 1 - propagate existing literals, 2 - refine bounds'), ('arith.branch_cut_ratio', UINT, 2, 'branch/cut ratio for linear integer arithmetic'), ('arith.int_eq_branch', BOOL, False, 'branching using derived integer equations'), ('arith.ignore_int', BOOL, False, 'treat integer variables as real'), ('arith.dump_lemmas', BOOL, False, 'dump arithmetic theory lemmas to files'), ('arith.greatest_error_pivot', BOOL, False, 'Pivoting strategy'), ('pb.conflict_frequency', UINT, 1000, 'conflict frequency for Pseudo-Boolean theory'), ('pb.learn_complements', BOOL, True, 'learn complement literals for Pseudo-Boolean theory'), ('pb.enable_compilation', BOOL, True, 'enable compilation into sorting circuits for Pseudo-Boolean'), ('pb.enable_simplex', BOOL, False, 'enable simplex to check rational feasibility'), ('array.weak', BOOL, False, 'weak array theory'), ('array.extensional', BOOL, True, 'extensional array theory'), ('dack', UINT, 1, '0 - disable dynamic ackermannization, 1 - expand Leibniz\'s axiom if a congruence is the root of a conflict, 2 - expand Leibniz\'s axiom if a congruence is used during conflict resolution'), ('dack.eq', BOOL, False, 'enable dynamic ackermannization for transtivity of equalities'), ('dack.factor', DOUBLE, 0.1, 'number of instance per conflict'), ('dack.gc', UINT, 2000, 'Dynamic ackermannization garbage collection frequency (per conflict)'), ('dack.gc_inv_decay', DOUBLE, 0.8, 'Dynamic ackermannization garbage collection decay'), ('dack.threshold', UINT, 10, ' number of times the congruence rule must be used before Leibniz\'s axiom is expanded'), ('core.validate', BOOL, False, 'validate unsat core produced by SMT context') )) z3-z3-4.4.1/src/smt/params/theory_arith_params.cpp000066400000000000000000000021201260446376700220450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-06. Revision History: --*/ #include"theory_arith_params.h" #include"smt_params_helper.hpp" void theory_arith_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_arith_random_initial_value = p.arith_random_initial_value(); m_arith_random_seed = p.random_seed(); m_arith_mode = static_cast(p.arith_solver()); m_nl_arith = p.arith_nl(); m_nl_arith_gb = p.arith_nl_gb(); m_nl_arith_branching = p.arith_nl_branching(); m_nl_arith_rounds = p.arith_nl_rounds(); m_arith_euclidean_solver = p.arith_euclidean_solver(); m_arith_propagate_eqs = p.arith_propagate_eqs(); m_arith_branch_cut_ratio = p.arith_branch_cut_ratio(); m_arith_int_eq_branching = p.arith_int_eq_branch(); m_arith_ignore_int = p.arith_ignore_int(); m_arith_bound_prop = static_cast(p.arith_propagation_mode()); m_arith_dump_lemmas = p.arith_dump_lemmas(); } z3-z3-4.4.1/src/smt/params/theory_arith_params.h000066400000000000000000000120361260446376700215210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-06. Revision History: --*/ #ifndef THEORY_ARITH_PARAMS_H_ #define THEORY_ARITH_PARAMS_H_ #include #include"params.h" enum arith_solver_id { AS_NO_ARITH, AS_DIFF_LOGIC, AS_ARITH, AS_DENSE_DIFF_LOGIC, AS_UTVPI, AS_OPTINF }; enum bound_prop_mode { BP_NONE, BP_SIMPLE, // only used for implying literals BP_REFINE // refine known bounds }; enum arith_prop_strategy { ARITH_PROP_AGILITY, ARITH_PROP_PROPORTIONAL }; enum arith_pivot_strategy { ARITH_PIVOT_SMALLEST, ARITH_PIVOT_GREATEST_ERROR, ARITH_PIVOT_LEAST_ERROR }; struct theory_arith_params { arith_solver_id m_arith_mode; bool m_arith_auto_config_simplex; //!< force simplex solver in auto_config unsigned m_arith_blands_rule_threshold; bool m_arith_propagate_eqs; bound_prop_mode m_arith_bound_prop; bool m_arith_stronger_lemmas; bool m_arith_skip_rows_with_big_coeffs; unsigned m_arith_max_lemma_size; unsigned m_arith_small_lemma_size; bool m_arith_reflect; bool m_arith_ignore_int; unsigned m_arith_lazy_pivoting_lvl; unsigned m_arith_random_seed; bool m_arith_random_initial_value; int m_arith_random_lower; int m_arith_random_upper; bool m_arith_adaptive; double m_arith_adaptive_assertion_threshold; double m_arith_adaptive_propagation_threshold; bool m_arith_dump_lemmas; bool m_arith_eager_eq_axioms; unsigned m_arith_branch_cut_ratio; bool m_arith_int_eq_branching; bool m_arith_enum_const_mod; bool m_arith_gcd_test; bool m_arith_eager_gcd; bool m_arith_adaptive_gcd; unsigned m_arith_propagation_threshold; arith_pivot_strategy m_arith_pivot_strategy; // used in diff-logic bool m_arith_add_binary_bounds; arith_prop_strategy m_arith_propagation_strategy; // used arith_eq_adapter bool m_arith_eq_bounds; bool m_arith_lazy_adapter; // performance debugging flags bool m_arith_fixnum; bool m_arith_int_only; // non linear support bool m_nl_arith; bool m_nl_arith_gb; unsigned m_nl_arith_gb_threshold; bool m_nl_arith_gb_eqs; bool m_nl_arith_gb_perturbate; unsigned m_nl_arith_max_degree; bool m_nl_arith_branching; unsigned m_nl_arith_rounds; // euclidean solver for tighting bounds bool m_arith_euclidean_solver; theory_arith_params(params_ref const & p = params_ref()): m_arith_mode(AS_ARITH), m_arith_auto_config_simplex(false), m_arith_blands_rule_threshold(1000), m_arith_propagate_eqs(true), m_arith_bound_prop(BP_REFINE), m_arith_stronger_lemmas(true), m_arith_skip_rows_with_big_coeffs(true), m_arith_max_lemma_size(128), m_arith_small_lemma_size(16), m_arith_reflect(true), m_arith_ignore_int(false), m_arith_lazy_pivoting_lvl(0), m_arith_random_seed(0), m_arith_random_initial_value(false), m_arith_random_lower(-1000), m_arith_random_upper(1000), m_arith_adaptive(false), m_arith_adaptive_assertion_threshold(0.2), m_arith_adaptive_propagation_threshold(0.4), m_arith_dump_lemmas(false), m_arith_eager_eq_axioms(true), m_arith_branch_cut_ratio(2), m_arith_int_eq_branching(false), m_arith_enum_const_mod(false), m_arith_gcd_test(true), m_arith_eager_gcd(false), m_arith_adaptive_gcd(false), m_arith_propagation_threshold(UINT_MAX), m_arith_pivot_strategy(ARITH_PIVOT_SMALLEST), m_arith_add_binary_bounds(false), m_arith_propagation_strategy(ARITH_PROP_PROPORTIONAL), m_arith_eq_bounds(false), m_arith_lazy_adapter(false), m_arith_fixnum(false), m_arith_int_only(false), m_nl_arith(true), m_nl_arith_gb(true), m_nl_arith_gb_threshold(512), m_nl_arith_gb_eqs(false), m_nl_arith_gb_perturbate(true), m_nl_arith_max_degree(6), m_nl_arith_branching(true), m_nl_arith_rounds(1024), m_arith_euclidean_solver(false) { updt_params(p); } void updt_params(params_ref const & p); }; #endif /* THEORY_ARITH_PARAMS_H_ */ z3-z3-4.4.1/src/smt/params/theory_array_params.cpp000066400000000000000000000006711260446376700220650ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-06. Revision History: --*/ #include"theory_array_params.h" #include"smt_params_helper.hpp" void theory_array_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_array_weak = p.array_weak(); m_array_extensional = p.array_extensional(); } z3-z3-4.4.1/src/smt/params/theory_array_params.h000066400000000000000000000043771260446376700215410ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-01. Revision History: --*/ #ifndef THEORY_ARRAY_PARAMS_H_ #define THEORY_ARRAY_PARAMS_H_ #include"array_simplifier_params.h" enum array_solver_id { AR_NO_ARRAY, AR_SIMPLE, AR_MODEL_BASED, AR_FULL }; struct theory_array_params : public array_simplifier_params { array_solver_id m_array_mode; bool m_array_weak; bool m_array_extensional; unsigned m_array_laziness; bool m_array_delay_exp_axiom; bool m_array_cg; bool m_array_always_prop_upward; bool m_array_lazy_ieq; unsigned m_array_lazy_ieq_delay; theory_array_params(): m_array_mode(AR_FULL), m_array_weak(false), m_array_extensional(true), m_array_laziness(1), m_array_delay_exp_axiom(true), m_array_cg(false), m_array_always_prop_upward(true), // UPWARDs filter is broken... TODO: fix it m_array_lazy_ieq(false), m_array_lazy_ieq_delay(10) { } void updt_params(params_ref const & _p); #if 0 void register_params(ini_params & p) { p.register_int_param("array_solver", 0, 3, reinterpret_cast(m_array_mode), "0 - no array, 1 - simple, 2 - model based, 3 - full"); p.register_bool_param("array_weak", m_array_weak); p.register_bool_param("array_extensional", m_array_extensional); p.register_unsigned_param("array_laziness", m_array_laziness); p.register_bool_param("array_delay_exp_axiom", m_array_delay_exp_axiom); p.register_bool_param("array_cg", m_array_cg); p.register_bool_param("array_always_prop_upward", m_array_always_prop_upward, "Disable the built-in filter upwards propagation"); p.register_bool_param("array_lazy_ieq", m_array_lazy_ieq); p.register_unsigned_param("array_lazy_ieq_delay", m_array_lazy_ieq_delay); p.register_bool_param("array_canonize", m_array_canonize_simplify, "Normalize arrays into normal form during simplification"); } #endif }; #endif /* THEORY_ARRAY_PARAMS_H_ */ z3-z3-4.4.1/src/smt/params/theory_bv_params.cpp000066400000000000000000000006611260446376700213550ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: theory_bv_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include"theory_bv_params.h" #include"smt_params_helper.hpp" void theory_bv_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_bv_reflect = p.bv_reflect(); m_bv_enable_int2bv2int = p.bv_enable_int2bv(); } z3-z3-4.4.1/src/smt/params/theory_bv_params.h000066400000000000000000000016141260446376700210210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_bv_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-06. Revision History: --*/ #ifndef THEORY_BV_PARAMS_H_ #define THEORY_BV_PARAMS_H_ #include"params.h" enum bv_solver_id { BS_NO_BV, BS_BLASTER }; struct theory_bv_params { bv_solver_id m_bv_mode; bool m_bv_reflect; bool m_bv_lazy_le; bool m_bv_cc; unsigned m_bv_blast_max_size; bool m_bv_enable_int2bv2int; theory_bv_params(params_ref const & p = params_ref()): m_bv_mode(BS_BLASTER), m_bv_reflect(true), m_bv_lazy_le(false), m_bv_cc(false), m_bv_blast_max_size(INT_MAX), m_bv_enable_int2bv2int(false) { updt_params(p); } void updt_params(params_ref const & p); }; #endif /* THEORY_BV_PARAMS_H_ */ z3-z3-4.4.1/src/smt/params/theory_datatype_params.h000066400000000000000000000012321260446376700222210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_datatype_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-11-04. Revision History: --*/ #ifndef THEORY_DATATYPE_PARAMS_H_ #define THEORY_DATATYPE_PARAMS_H_ struct theory_datatype_params { unsigned m_dt_lazy_splits; theory_datatype_params(): m_dt_lazy_splits(1) { } #if 0 void register_params(ini_params & p) { p.register_unsigned_param("dt_lazy_splits", m_dt_lazy_splits, "How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy"); } #endif }; #endif /* THEORY_DATATYPE_PARAMS_H_ */ z3-z3-4.4.1/src/smt/params/theory_pb_params.cpp000066400000000000000000000010611260446376700213420ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: theory_pb_params.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-01 Revision History: --*/ #include"theory_pb_params.h" #include"smt_params_helper.hpp" void theory_pb_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_pb_conflict_frequency = p.pb_conflict_frequency(); m_pb_learn_complements = p.pb_learn_complements(); m_pb_enable_compilation = p.pb_enable_compilation(); m_pb_enable_simplex = p.pb_enable_simplex(); } z3-z3-4.4.1/src/smt/params/theory_pb_params.h000066400000000000000000000013471260446376700210160ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_pb_params.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-01 Revision History: --*/ #ifndef THEORY_PB_PARAMS_H_ #define THEORY_PB_PARAMS_H_ #include"params.h" struct theory_pb_params { unsigned m_pb_conflict_frequency; bool m_pb_learn_complements; bool m_pb_enable_compilation; bool m_pb_enable_simplex; theory_pb_params(params_ref const & p = params_ref()): m_pb_conflict_frequency(1000), m_pb_learn_complements(true), m_pb_enable_compilation(true), m_pb_enable_simplex(false) {} void updt_params(params_ref const & p); }; #endif /* THEORY_PB_PARAMS_H_ */ z3-z3-4.4.1/src/smt/proto_model/000077500000000000000000000000001260446376700163425ustar00rootroot00000000000000z3-z3-4.4.1/src/smt/proto_model/array_factory.cpp000066400000000000000000000153311260446376700217160ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array_factory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #include"array_factory.h" #include"array_decl_plugin.h" #include"proto_model.h" #include"func_interp.h" #include"ast_pp.h" func_decl * mk_aux_decl_for_array_sort(ast_manager & m, sort * s) { ptr_buffer domain; sort * range = get_array_range(s); unsigned arity = get_array_arity(s); for (unsigned i = 0; i < arity; i++) { domain.push_back(get_array_domain(s, i)); } return m.mk_fresh_func_decl(symbol::null, symbol::null, arity, domain.c_ptr(), range); } array_factory::array_factory(ast_manager & m, proto_model & md): struct_factory(m, m.mk_family_id("array"), md) { } /** \brieft Return as-array[f] where f is a fresh function symbol with the right domain and range for the array sort s. Store in fi the function interpretation for f. */ expr * array_factory::mk_array_interp(sort * s, func_interp * & fi) { func_decl * f = mk_aux_decl_for_array_sort(m_manager, s); fi = alloc(func_interp, m_manager, get_array_arity(s)); m_model.register_decl(f, fi); parameter p[1] = { parameter(f) }; expr * val = m_manager.mk_app(get_family_id(), OP_AS_ARRAY, 1, p); register_value(val); return val; } void array_factory::get_some_args_for(sort * s, ptr_buffer & args) { unsigned arity = get_array_arity(s); for (unsigned i = 0; i < arity; i++) { sort * d = get_array_domain(s, i); expr * a = m_model.get_some_value(d); args.push_back(a); } } expr * array_factory::get_some_value(sort * s) { TRACE("array_factory", tout << mk_pp(s, m_manager) << "\n";); value_set * set = 0; if (m_sort2value_set.find(s, set) && !set->empty()) return *(set->begin()); func_interp * fi; expr * val = mk_array_interp(s, fi); ptr_buffer args; get_some_args_for(s, args); fi->insert_entry(args.c_ptr(), m_model.get_some_value(get_array_range(s))); return val; } bool array_factory::mk_two_diff_values_for(sort * s) { DEBUG_CODE({ value_set * set = 0; SASSERT(!m_sort2value_set.find(s, set) || set->size() == 0); }); expr_ref r1(m_manager); expr_ref r2(m_manager); sort * range = get_array_range(s); if (!m_model.get_some_values(range, r1, r2)) return false; // failed... the range is probably unit. ptr_buffer args; get_some_args_for(s, args); func_interp * fi1; func_interp * fi2; mk_array_interp(s, fi1); mk_array_interp(s, fi2); fi1->insert_entry(args.c_ptr(), r1); fi2->insert_entry(args.c_ptr(), r2); DEBUG_CODE({ value_set * set = 0; SASSERT(m_sort2value_set.find(s, set) && set->size() == 2); }); return true; } bool array_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { value_set * set = 0; if (!m_sort2value_set.find(s, set) || set->size() == 0) { if (!mk_two_diff_values_for(s)) return false; } m_sort2value_set.find(s, set); SASSERT(set != 0); SASSERT(set->size() > 0); if (set->size() == 1) { v1 = *(set->begin()); v2 = get_fresh_value(s); return v2.get() != 0; } else { SASSERT(set->size() >= 2); value_set::iterator it = set->begin(); v1 = *it; ++it; v2 = *it; return true; } } // // TODO: I have to check if the following procedure is really correct. // I'm supporting partial arrays where the "else" can be set later by the model_finder or model classes. // Projection functions may be also used. // // If projections are not used, then the following code should work if the "else" of a partial array // is set with the result of some entry. // expr * array_factory::get_fresh_value(sort * s) { value_set * set = get_value_set(s); if (set->empty()) { // easy case return get_some_value(s); } sort * range = get_array_range(s); expr * range_val = m_model.get_fresh_value(range); if (range_val != 0) { // easy case func_interp * fi; expr * val = mk_array_interp(s, fi); ptr_buffer args; get_some_args_for(s, args); fi->insert_entry(args.c_ptr(), range_val); return val; } else { TRACE("array_factory_bug", tout << "array fresh value: using fresh index, range: " << mk_pp(range, m_manager) << "\n";); expr_ref v1(m_manager); expr_ref v2(m_manager); if (m_model.get_some_values(range, v1, v2)) { // Claim: A is fresh if A[i1] = v1 and A[i2] = v2 where i1 and i2 are fresh values, // and v1 and v2 are distinct. // // Proof: let assume there is an Array A' such that A' = A. // Then A[i1] == A'[i1] and A[i2] == A'[i2]. Since, i1 and i2 are fresh, // A' does not have an entry for i1 or i2, So A'[i1] == A'[i2] == A'.m_else. // Thus, A[i1] == A[i2] which is a contradiction since v1 != v2 and A[i1] = v1 and A[i2] = v2. TRACE("array_factory_bug", tout << "v1: " << mk_pp(v1, m_manager) << " v2: " << mk_pp(v2, m_manager) << "\n";); ptr_buffer args1; ptr_buffer args2; bool found = false; unsigned arity = get_array_arity(s); for (unsigned i = 0; i < arity; i++) { sort * d = get_array_domain(s, i); if (!found) { expr * arg1 = m_model.get_fresh_value(d); expr * arg2 = m_model.get_fresh_value(d); if (arg1 != 0 && arg2 != 0) { found = true; args1.push_back(arg1); args2.push_back(arg2); continue; } } expr * arg = m_model.get_some_value(d); args1.push_back(arg); args2.push_back(arg); } if (found) { func_interp * fi; expr * val = mk_array_interp(s, fi); fi->insert_entry(args1.c_ptr(), v1); fi->insert_entry(args2.c_ptr(), v2); return val; } } } // TODO: use more expensive procedures to create a fresh array value. // Example: track the values used in the domain. // Remark: in the current implementation, this function // will never fail, since if a type is finite, then // type_pred will be applied and get_fresh_value will not // need to be used. // failed to create a fresh array value TRACE("array_factory_bug", tout << "failed to build fresh array value\n";); return 0; } z3-z3-4.4.1/src/smt/proto_model/array_factory.h000066400000000000000000000015371260446376700213660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array_factory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #ifndef ARRAY_FACTORY_H_ #define ARRAY_FACTORY_H_ #include"struct_factory.h" class func_interp; func_decl * mk_aux_decl_for_array_sort(ast_manager & m, sort * s); class array_factory : public struct_factory { expr * mk_array_interp(sort * s, func_interp * & fi); void get_some_args_for(sort * s, ptr_buffer & args); bool mk_two_diff_values_for(sort * s); public: array_factory(ast_manager & m, proto_model & md); virtual ~array_factory() {} virtual expr * get_some_value(sort * s); virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2); virtual expr * get_fresh_value(sort * s); }; #endif /* ARRAY_FACTORY_H_ */ z3-z3-4.4.1/src/smt/proto_model/datatype_factory.cpp000066400000000000000000000246501260446376700224170ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: datatype_factory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-11-06. Revision History: --*/ #include"datatype_factory.h" #include"proto_model.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"expr_functors.h" datatype_factory::datatype_factory(ast_manager & m, proto_model & md): struct_factory(m, m.mk_family_id("datatype"), md), m_util(m) { } expr * datatype_factory::get_some_value(sort * s) { value_set * set = 0; if (m_sort2value_set.find(s, set) && !set->empty()) return *(set->begin()); func_decl * c = m_util.get_non_rec_constructor(s); ptr_vector args; unsigned num = c->get_arity(); for (unsigned i = 0; i < num; i++) { args.push_back(m_model.get_some_value(c->get_domain(i))); } expr * r = m_manager.mk_app(c, args.size(), args.c_ptr()); register_value(r); TRACE("datatype_factory", tout << mk_pp(r, m_util.get_manager()) << "\n";); return r; } /** \brief Return the last fresh (or almost) fresh value of sort s. */ expr * datatype_factory::get_last_fresh_value(sort * s) { expr * val = 0; if (m_last_fresh_value.find(s, val)) { TRACE("datatype_factory", tout << "cached fresh value: " << mk_pp(val, m_manager) << "\n";); return val; } value_set * set = get_value_set(s); if (set->empty()) val = get_some_value(s); else val = *(set->begin()); if (m_util.is_recursive(s)) m_last_fresh_value.insert(s, val); return val; } bool datatype_factory::is_subterm_of_last_value(app* e) { expr* last; if (!m_last_fresh_value.find(m_manager.get_sort(e), last)) { return false; } contains_app contains(m_manager, e); bool result = contains(last); TRACE("datatype_factory", tout << mk_pp(e, m_manager) << " in " << mk_pp(last, m_manager) << " " << result << "\n";); return result; } /** \brief Create an almost fresh value. If s is recursive, then the result is not 0. It also updates m_last_fresh_value */ expr * datatype_factory::get_almost_fresh_value(sort * s) { value_set * set = get_value_set(s); if (set->empty()) { expr * val = get_some_value(s); SASSERT(val); if (m_util.is_recursive(s)) m_last_fresh_value.insert(s, val); return val; } // Traverse constructors, and try to invoke get_fresh_value of one of the arguments (if the argument is not a sibling datatype of s). // If the argumet is a sibling datatype of s, then // use get_last_fresh_value. ptr_vector const * constructors = m_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; expr_ref_vector args(m_manager); bool found_fresh_arg = false; bool recursive = false; unsigned num = constructor->get_arity(); for (unsigned i = 0; i < num; i++) { sort * s_arg = constructor->get_domain(i); if (!found_fresh_arg && (!m_util.is_datatype(s_arg) || !m_util.are_siblings(s, s_arg))) { expr * new_arg = m_model.get_fresh_value(s_arg); if (new_arg != 0) { found_fresh_arg = true; args.push_back(new_arg); continue; } } if (!found_fresh_arg && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) { recursive = true; expr * last_fresh = get_last_fresh_value(s_arg); args.push_back(last_fresh); } else { expr * some_arg = m_model.get_some_value(s_arg); args.push_back(some_arg); } } if (recursive || found_fresh_arg) { app * new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); SASSERT(!found_fresh_arg || !set->contains(new_value)); register_value(new_value); if (m_util.is_recursive(s)) { if (is_subterm_of_last_value(new_value)) { new_value = static_cast(m_last_fresh_value.find(s)); } else { m_last_fresh_value.insert(s, new_value); } } TRACE("datatype_factory", tout << "almost fresh: " << mk_pp(new_value, m_manager) << "\n";); return new_value; } } SASSERT(!m_util.is_recursive(s)); return 0; } expr * datatype_factory::get_fresh_value(sort * s) { TRACE("datatype_factory", tout << "generating fresh value for: " << s->get_name() << "\n";); value_set * set = get_value_set(s); // Approach 0) // if no value for s was generated so far, then used get_some_value if (set->empty()) { expr * val = get_some_value(s); if (m_util.is_recursive(s)) m_last_fresh_value.insert(s, val); TRACE("datatype_factory", tout << "0. result: " << mk_pp(val, m_manager) << "\n";); return val; } // Approach 1) // Traverse constructors, and try to invoke get_fresh_value of one of the // arguments (if the argument is not a sibling datatype of s). // Two datatypes are siblings if they were defined together in the same mutually recursive definition. ptr_vector const * constructors = m_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; expr_ref_vector args(m_manager); bool found_fresh_arg = false; unsigned num = constructor->get_arity(); for (unsigned i = 0; i < num; i++) { sort * s_arg = constructor->get_domain(i); if (!found_fresh_arg && (!m_util.is_recursive(s) || !m_util.is_datatype(s_arg) || !m_util.are_siblings(s, s_arg))) { expr * new_arg = m_model.get_fresh_value(s_arg); if (new_arg != 0) { found_fresh_arg = true; args.push_back(new_arg); continue; } } expr * some_arg = m_model.get_some_value(s_arg); args.push_back(some_arg); } expr_ref new_value(m_manager); new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); CTRACE("datatype_factory", found_fresh_arg && set->contains(new_value), tout << mk_pp(new_value, m_manager) << "\n";); SASSERT(!found_fresh_arg || !set->contains(new_value)); if (!set->contains(new_value)) { register_value(new_value); if (m_util.is_recursive(s)) m_last_fresh_value.insert(s, new_value); TRACE("datatype_factory", tout << "1. result: " << mk_pp(new_value, m_manager) << "\n";); return new_value; } } // Approach 2) // For recursive datatypes. // search for constructor... unsigned num_iterations = 0; if (m_util.is_recursive(s)) { while(true) { ++num_iterations; TRACE("datatype_factory", tout << mk_pp(get_last_fresh_value(s), m_manager) << "\n";); ptr_vector const * constructors = m_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; expr_ref_vector args(m_manager); bool found_sibling = false; unsigned num = constructor->get_arity(); TRACE("datatype_factory", tout << "checking constructor: " << constructor->get_name() << "\n";); for (unsigned i = 0; i < num; i++) { sort * s_arg = constructor->get_domain(i); TRACE("datatype_factory", tout << mk_pp(s, m_manager) << " " << mk_pp(s_arg, m_manager) << " are_siblings " << m_util.are_siblings(s, s_arg) << " is_datatype " << m_util.is_datatype(s_arg) << " found_sibling " << found_sibling << "\n";); if (!found_sibling && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) { found_sibling = true; expr * maybe_new_arg = 0; if (num_iterations <= 1) { maybe_new_arg = get_almost_fresh_value(s_arg); } else { maybe_new_arg = get_fresh_value(s_arg); } if (!maybe_new_arg) { TRACE("datatype_factory", tout << "no argument found for " << mk_pp(s_arg, m_manager) << "\n";); maybe_new_arg = m_model.get_some_value(s_arg); found_sibling = false; } SASSERT(maybe_new_arg); args.push_back(maybe_new_arg); } else { expr * some_arg = m_model.get_some_value(s_arg); SASSERT(some_arg); args.push_back(some_arg); } } if (found_sibling) { expr_ref new_value(m_manager); new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); TRACE("datatype_factory", tout << "potential new value: " << mk_pp(new_value, m_manager) << "\n";); m_last_fresh_value.insert(s, new_value); if (!set->contains(new_value)) { register_value(new_value); TRACE("datatype_factory", tout << "2. result: " << mk_pp(new_value, m_manager) << "\n";); return new_value; } } } } } // Approach 3) // for non-recursive datatypes. // Search for value that was not created before. SASSERT(!m_util.is_recursive(s)); return 0; } z3-z3-4.4.1/src/smt/proto_model/datatype_factory.h000066400000000000000000000014451260446376700220610ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: datatype_factory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-11-06. Revision History: --*/ #ifndef DATATYPE_FACTORY_H_ #define DATATYPE_FACTORY_H_ #include"struct_factory.h" #include"datatype_decl_plugin.h" class datatype_factory : public struct_factory { datatype_util m_util; obj_map m_last_fresh_value; expr * get_last_fresh_value(sort * s); expr * get_almost_fresh_value(sort * s); bool is_subterm_of_last_value(app* e); public: datatype_factory(ast_manager & m, proto_model & md); virtual ~datatype_factory() {} virtual expr * get_some_value(sort * s); virtual expr * get_fresh_value(sort * s); }; #endif /* DATATYPE_FACTORY_H_ */ z3-z3-4.4.1/src/smt/proto_model/numeral_factory.cpp000066400000000000000000000020231260446376700222350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: numeral_factory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #include"numeral_factory.h" #include"ast_pp.h" app * arith_factory::mk_value_core(rational const & val, sort * s) { return m_util.mk_numeral(val, s); } arith_factory::arith_factory(ast_manager & m): numeral_factory(m, m.mk_family_id("arith")), m_util(m) { } arith_factory::~arith_factory() { } app * arith_factory::mk_value(rational const & val, bool is_int) { return numeral_factory::mk_value(val, is_int ? m_util.mk_int() : m_util.mk_real()); } bv_factory::bv_factory(ast_manager & m): numeral_factory(m, m.mk_family_id("bv")), m_util(m) { } bv_factory::~bv_factory() { } app * bv_factory::mk_value_core(rational const & val, sort * s) { return m_util.mk_numeral(val, s); } app * bv_factory::mk_value(rational const & val, unsigned bv_size) { return numeral_factory::mk_value(val, m_util.mk_sort(bv_size)); } z3-z3-4.4.1/src/smt/proto_model/numeral_factory.h000066400000000000000000000021211260446376700217010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: numeral_factory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #ifndef NUMERAL_FACTORY_H_ #define NUMERAL_FACTORY_H_ #include"value_factory.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" class numeral_factory : public simple_factory { public: numeral_factory(ast_manager & m, family_id fid):simple_factory(m, fid) {} virtual ~numeral_factory() {} }; class arith_factory : public numeral_factory { arith_util m_util; virtual app * mk_value_core(rational const & val, sort * s); public: arith_factory(ast_manager & m); virtual ~arith_factory(); app * mk_value(rational const & val, bool is_int); }; class bv_factory : public numeral_factory { bv_util m_util; virtual app * mk_value_core(rational const & val, sort * s); public: bv_factory(ast_manager & m); virtual ~bv_factory(); app * mk_value(rational const & val, unsigned bv_size); }; #endif /* NUMERAL_FACTORY_H_ */ z3-z3-4.4.1/src/smt/proto_model/proto_model.cpp000066400000000000000000000550531260446376700214010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: proto_model.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-03-08. Revision History: --*/ #include"proto_model.h" #include"model_params.hpp" #include"ast_pp.h" #include"ast_ll_pp.h" #include"var_subst.h" #include"array_decl_plugin.h" #include"well_sorted.h" #include"used_symbols.h" #include"model_v2_pp.h" #include"basic_simplifier_plugin.h" proto_model::proto_model(ast_manager & m, simplifier & s, params_ref const & p): model_core(m), m_asts(m), m_simplifier(s), m_afid(m.mk_family_id(symbol("array"))) { register_factory(alloc(basic_factory, m)); m_user_sort_factory = alloc(user_sort_factory, m); register_factory(m_user_sort_factory); m_model_partial = model_params(p).partial(); } void proto_model::reset_finterp() { decl2finterp::iterator it = m_finterp.begin(); decl2finterp::iterator end = m_finterp.end(); for (; it != end; ++it) { dealloc(it->m_value); } } proto_model::~proto_model() { reset_finterp(); } void proto_model::register_decl(func_decl * d, expr * v) { SASSERT(d->get_arity() == 0); if (m_interp.contains(d)) { DEBUG_CODE({ expr * old_v = 0; m_interp.find(d, old_v); SASSERT(old_v == v); }); return; } SASSERT(!m_interp.contains(d)); m_decls.push_back(d); m_asts.push_back(d); m_asts.push_back(v); m_interp.insert(d, v); m_const_decls.push_back(d); } void proto_model::register_decl(func_decl * d, func_interp * fi, bool aux) { SASSERT(d->get_arity() > 0); SASSERT(!m_finterp.contains(d)); m_decls.push_back(d); m_asts.push_back(d); m_finterp.insert(d, fi); m_func_decls.push_back(d); if (aux) m_aux_decls.insert(d); } /** \brief Set new_fi as the new interpretation for f. If f_aux != 0, then assign the old interpretation of f to f_aux. If f_aux == 0, then delete the old interpretation of f. f_aux is marked as a auxiliary declaration. */ void proto_model::reregister_decl(func_decl * f, func_interp * new_fi, func_decl * f_aux) { func_interp * fi = get_func_interp(f); if (fi == 0) { register_decl(f, new_fi); } else { if (f_aux != 0) { register_decl(f_aux, fi); m_aux_decls.insert(f_aux); } else { dealloc(fi); } m_finterp.insert(f, new_fi); } } expr * proto_model::mk_some_interp_for(func_decl * d) { SASSERT(!has_interpretation(d)); expr * r = get_some_value(d->get_range()); // if t is a function, then it will be the constant function. if (d->get_arity() == 0) { register_decl(d, r); } else { func_interp * new_fi = alloc(func_interp, m_manager, d->get_arity()); new_fi->set_else(r); register_decl(d, new_fi); } return r; } // Auxiliary function for computing fi(args[0], ..., args[fi.get_arity() - 1]). // The result is stored in result. // Return true if succeeded, and false otherwise. // It uses the simplifier s during the computation. bool eval(func_interp & fi, simplifier & s, expr * const * args, expr_ref & result) { bool actuals_are_values = true; if (fi.num_entries() != 0) { for (unsigned i = 0; actuals_are_values && i < fi.get_arity(); i++) { actuals_are_values = fi.m().is_value(args[i]); } } func_entry * entry = fi.get_entry(args); if (entry != 0) { result = entry->get_result(); return true; } TRACE("func_interp", tout << "failed to find entry for: "; for(unsigned i = 0; i < fi.get_arity(); i++) tout << mk_pp(args[i], fi.m()) << " "; tout << "\nis partial: " << fi.is_partial() << "\n";); if (!fi.eval_else(args, result)) { return false; } if (actuals_are_values && fi.args_are_values()) { // cheap case... we are done return true; } // build symbolic result... the actuals may be equal to the args of one of the entries. basic_simplifier_plugin * bs = static_cast(s.get_plugin(fi.m().get_basic_family_id())); for (unsigned k = 0; k < fi.num_entries(); k++) { func_entry const * curr = fi.get_entry(k); SASSERT(!curr->eq_args(fi.m(), fi.get_arity(), args)); if (!actuals_are_values || !curr->args_are_values()) { expr_ref_buffer eqs(fi.m()); unsigned i = fi.get_arity(); while (i > 0) { --i; expr_ref new_eq(fi.m()); bs->mk_eq(curr->get_arg(i), args[i], new_eq); eqs.push_back(new_eq); } SASSERT(eqs.size() == fi.get_arity()); expr_ref new_cond(fi.m()); bs->mk_and(eqs.size(), eqs.c_ptr(), new_cond); bs->mk_ite(new_cond, curr->get_result(), result, result); } } return true; } /** \brief Evaluate the expression e in the current model, and store the result in \c result. It returns \c true if succeeded, and false otherwise. If the evaluation fails, then r contains a term that is simplified as much as possible using the interpretations available in the model. When model_completion == true, if the model does not assign an interpretation to a declaration it will build one for it. Moreover, partial functions will also be completed. So, if model_completion == true, the evaluator never fails if it doesn't contain quantifiers. */ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) { bool is_ok = true; SASSERT(is_well_sorted(m_manager, e)); TRACE("model_eval", tout << mk_pp(e, m_manager) << "\n"; tout << "sort: " << mk_pp(m_manager.get_sort(e), m_manager) << "\n";); obj_map eval_cache; expr_ref_vector trail(m_manager); sbuffer, 128> todo; ptr_buffer args; expr * null = static_cast(0); todo.push_back(std::make_pair(e, null)); expr * a; expr * expanded_a; while (!todo.empty()) { std::pair & p = todo.back(); a = p.first; expanded_a = p.second; if (expanded_a != 0) { expr * r = 0; eval_cache.find(expanded_a, r); SASSERT(r != 0); todo.pop_back(); eval_cache.insert(a, r); TRACE("model_eval", tout << "orig:\n" << mk_pp(a, m_manager) << "\n"; tout << "after beta reduction:\n" << mk_pp(expanded_a, m_manager) << "\n"; tout << "new:\n" << mk_pp(r, m_manager) << "\n";); } else { switch(a->get_kind()) { case AST_APP: { app * t = to_app(a); bool visited = true; args.reset(); unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { expr * arg = 0; if (!eval_cache.find(t->get_arg(i), arg)) { visited = false; todo.push_back(std::make_pair(t->get_arg(i), null)); } else { args.push_back(arg); } } if (!visited) { continue; } SASSERT(args.size() == t->get_num_args()); expr_ref new_t(m_manager); func_decl * f = t->get_decl(); if (!has_interpretation(f)) { // the model does not assign an interpretation to f. SASSERT(new_t.get() == 0); if (f->get_family_id() == null_family_id) { if (model_completion) { // create an interpretation for f. new_t = mk_some_interp_for(f); } else { TRACE("model_eval", tout << f->get_name() << " is uninterpreted\n";); is_ok = false; } } if (new_t.get() == 0) { // t is interpreted or model completion is disabled. m_simplifier.mk_app(f, num_args, args.c_ptr(), new_t); trail.push_back(new_t); if (!is_app(new_t) || to_app(new_t)->get_decl() != f) { // if the result is not of the form (f ...), then assume we must simplify it. expr * new_new_t = 0; if (!eval_cache.find(new_t.get(), new_new_t)) { todo.back().second = new_t; todo.push_back(std::make_pair(new_t, null)); continue; } else { new_t = new_new_t; } } } } else { // the model has an interpretaion for f. if (num_args == 0) { // t is a constant new_t = get_const_interp(f); } else { // t is a function application SASSERT(new_t.get() == 0); // try to use function graph first func_interp * fi = get_func_interp(f); SASSERT(fi->get_arity() == num_args); expr_ref r1(m_manager); // fi may be partial... if (!::eval(*fi, m_simplifier, args.c_ptr(), r1)) { SASSERT(fi->is_partial()); // fi->eval only fails when fi is partial. if (model_completion) { expr * r = get_some_value(f->get_range()); fi->set_else(r); SASSERT(!fi->is_partial()); new_t = r; } else { // f is an uninterpreted function, there is no need to use m_simplifier.mk_app new_t = m_manager.mk_app(f, num_args, args.c_ptr()); trail.push_back(new_t); TRACE("model_eval", tout << f->get_name() << " is uninterpreted\n";); is_ok = false; } } else { SASSERT(r1); trail.push_back(r1); expr * r2 = 0; if (!eval_cache.find(r1.get(), r2)) { todo.back().second = r1; todo.push_back(std::make_pair(r1, null)); continue; } else { new_t = r2; } } } } TRACE("model_eval", tout << "orig:\n" << mk_pp(t, m_manager) << "\n"; tout << "new:\n" << mk_pp(new_t, m_manager) << "\n";); todo.pop_back(); SASSERT(new_t.get() != 0); eval_cache.insert(t, new_t); break; } case AST_VAR: SASSERT(a != 0); eval_cache.insert(a, a); todo.pop_back(); break; case AST_QUANTIFIER: TRACE("model_eval", tout << "found quantifier\n" << mk_pp(a, m_manager) << "\n";); is_ok = false; // evaluator does not handle quantifiers. SASSERT(a != 0); eval_cache.insert(a, a); todo.pop_back(); break; default: UNREACHABLE(); break; } } } if (!eval_cache.find(e, a)) { TRACE("model_eval", tout << "FAILED e: " << mk_bounded_pp(e, m_manager) << "\n";); UNREACHABLE(); } result = a; TRACE("model_eval", ast_ll_pp(tout << "original: ", m_manager, e); ast_ll_pp(tout << "evaluated: ", m_manager, a); ast_ll_pp(tout << "reduced: ", m_manager, result.get()); tout << "sort: " << mk_pp(m_manager.get_sort(e), m_manager) << "\n"; ); SASSERT(is_well_sorted(m_manager, result.get())); return is_ok; } /** \brief Replace uninterpreted constants occurring in fi->get_else() by their interpretations. */ void proto_model::cleanup_func_interp(func_interp * fi, func_decl_set & found_aux_fs) { if (fi->is_partial()) return; expr * fi_else = fi->get_else(); TRACE("model_bug", tout << "cleaning up:\n" << mk_pp(fi_else, m_manager) << "\n";); obj_map cache; expr_ref_vector trail(m_manager); ptr_buffer todo; ptr_buffer args; todo.push_back(fi_else); expr * a; while (!todo.empty()) { a = todo.back(); if (is_uninterp_const(a)) { todo.pop_back(); func_decl * a_decl = to_app(a)->get_decl(); expr * ai = get_const_interp(a_decl); if (ai == 0) { ai = get_some_value(a_decl->get_range()); register_decl(a_decl, ai); } cache.insert(a, ai); } else { switch(a->get_kind()) { case AST_APP: { app * t = to_app(a); bool visited = true; args.reset(); unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { expr * arg = 0; if (!cache.find(t->get_arg(i), arg)) { visited = false; todo.push_back(t->get_arg(i)); } else { args.push_back(arg); } } if (!visited) { continue; } func_decl * f = t->get_decl(); if (m_aux_decls.contains(f)) found_aux_fs.insert(f); expr_ref new_t(m_manager); m_simplifier.mk_app(f, num_args, args.c_ptr(), new_t); if (t != new_t.get()) trail.push_back(new_t); todo.pop_back(); cache.insert(t, new_t); break; } default: SASSERT(a != 0); cache.insert(a, a); todo.pop_back(); break; } } } if (!cache.find(fi_else, a)) { UNREACHABLE(); } fi->set_else(a); } void proto_model::remove_aux_decls_not_in_set(ptr_vector & decls, func_decl_set const & s) { unsigned sz = decls.size(); unsigned i = 0; unsigned j = 0; for (; i < sz; i++) { func_decl * f = decls[i]; if (!m_aux_decls.contains(f) || s.contains(f)) { decls[j] = f; j++; } } decls.shrink(j); } /** \brief Replace uninterpreted constants occurring in the func_interp's get_else() by their interpretations. */ void proto_model::cleanup() { func_decl_set found_aux_fs; decl2finterp::iterator it = m_finterp.begin(); decl2finterp::iterator end = m_finterp.end(); for (; it != end; ++it) { func_interp * fi = (*it).m_value; cleanup_func_interp(fi, found_aux_fs); } // remove auxiliary declarations that are not used. if (found_aux_fs.size() != m_aux_decls.size()) { remove_aux_decls_not_in_set(m_decls, found_aux_fs); remove_aux_decls_not_in_set(m_func_decls, found_aux_fs); func_decl_set::iterator it2 = m_aux_decls.begin(); func_decl_set::iterator end2 = m_aux_decls.end(); for (; it2 != end2; ++it2) { func_decl * faux = *it2; if (!found_aux_fs.contains(faux)) { TRACE("cleanup_bug", tout << "eliminating " << faux->get_name() << "\n";); func_interp * fi = 0; m_finterp.find(faux, fi); SASSERT(fi != 0); m_finterp.erase(faux); dealloc(fi); } } m_aux_decls.swap(found_aux_fs); } } value_factory * proto_model::get_factory(family_id fid) { return m_factories.get_plugin(fid); } void proto_model::freeze_universe(sort * s) { SASSERT(m_manager.is_uninterp(s)); m_user_sort_factory->freeze_universe(s); } /** \brief Return the known universe of an uninterpreted sort. */ obj_hashtable const & proto_model::get_known_universe(sort * s) const { SASSERT(m_manager.is_uninterp(s)); return m_user_sort_factory->get_known_universe(s); } ptr_vector const & proto_model::get_universe(sort * s) const { ptr_vector & tmp = const_cast(this)->m_tmp; tmp.reset(); obj_hashtable const & u = get_known_universe(s); obj_hashtable::iterator it = u.begin(); obj_hashtable::iterator end = u.end(); for (; it != end; ++it) tmp.push_back(*it); return tmp; } unsigned proto_model::get_num_uninterpreted_sorts() const { return m_user_sort_factory->get_num_sorts(); } sort * proto_model::get_uninterpreted_sort(unsigned idx) const { SASSERT(idx < get_num_uninterpreted_sorts()); return m_user_sort_factory->get_sort(idx); } /** \brief Return true if the given sort is uninterpreted and has a finite interpretation in the model. */ bool proto_model::is_finite(sort * s) const { return m_manager.is_uninterp(s) && m_user_sort_factory->is_finite(s); } expr * proto_model::get_some_value(sort * s) { if (m_manager.is_uninterp(s)) { return m_user_sort_factory->get_some_value(s); } else { family_id fid = s->get_family_id(); value_factory * f = get_factory(fid); if (f) return f->get_some_value(s); // there is no factory for the family id, then assume s is uninterpreted. return m_user_sort_factory->get_some_value(s); } } bool proto_model::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { if (m_manager.is_uninterp(s)) { return m_user_sort_factory->get_some_values(s, v1, v2); } else { family_id fid = s->get_family_id(); value_factory * f = get_factory(fid); if (f) return f->get_some_values(s, v1, v2); else return false; } } expr * proto_model::get_fresh_value(sort * s) { if (m_manager.is_uninterp(s)) { return m_user_sort_factory->get_fresh_value(s); } else { family_id fid = s->get_family_id(); value_factory * f = get_factory(fid); if (f) return f->get_fresh_value(s); else // Use user_sort_factory if the theory has no support for model construnction. // This is needed when dummy theories are used for arithmetic or arrays. return m_user_sort_factory->get_fresh_value(s); } } void proto_model::register_value(expr * n) { sort * s = m_manager.get_sort(n); if (m_manager.is_uninterp(s)) { m_user_sort_factory->register_value(n); } else { family_id fid = s->get_family_id(); value_factory * f = get_factory(fid); if (f) f->register_value(n); } } bool proto_model::is_array_value(expr * v) const { return is_app_of(v, m_afid, OP_AS_ARRAY); } void proto_model::compress() { ptr_vector::iterator it = m_func_decls.begin(); ptr_vector::iterator end = m_func_decls.end(); for (; it != end; ++it) { func_decl * f = *it; func_interp * fi = get_func_interp(f); SASSERT(fi != 0); fi->compress(); } } /** \brief Complete the interpretation fi of f if it is partial. If f does not have an interpretation in the given model, then this is a noop. */ void proto_model::complete_partial_func(func_decl * f) { func_interp * fi = get_func_interp(f); if (fi && fi->is_partial()) { expr * else_value = 0; #if 0 // For UFBV benchmarks, setting the "else" to false is not a good idea. // TODO: find a permanent solution. A possibility is to add another option. if (m_manager.is_bool(f->get_range())) { else_value = m_manager.mk_false(); } else { else_value = fi->get_max_occ_result(); if (else_value == 0) else_value = get_some_value(f->get_range()); } #else else_value = fi->get_max_occ_result(); if (else_value == 0) else_value = get_some_value(f->get_range()); #endif fi->set_else(else_value); } } /** \brief Set the (else) field of function interpretations... */ void proto_model::complete_partial_funcs() { if (m_model_partial) return; // m_func_decls may be "expanded" when we invoke get_some_value. // So, we must not use iterators to traverse it. for (unsigned i = 0; i < m_func_decls.size(); i++) { complete_partial_func(m_func_decls[i]); } } model * proto_model::mk_model() { TRACE("proto_model", tout << "mk_model\n"; model_v2_pp(tout, *this);); model * m = alloc(model, m_manager); decl2expr::iterator it1 = m_interp.begin(); decl2expr::iterator end1 = m_interp.end(); for (; it1 != end1; ++it1) { m->register_decl(it1->m_key, it1->m_value); } decl2finterp::iterator it2 = m_finterp.begin(); decl2finterp::iterator end2 = m_finterp.end(); for (; it2 != end2; ++it2) { m->register_decl(it2->m_key, it2->m_value); } m_finterp.reset(); // m took the ownership of the func_interp's unsigned sz = get_num_uninterpreted_sorts(); for (unsigned i = 0; i < sz; i++) { sort * s = get_uninterpreted_sort(i); TRACE("proto_model", tout << "copying uninterpreted sorts...\n" << mk_pp(s, m_manager) << "\n";); ptr_buffer buf; obj_hashtable const & u = get_known_universe(s); obj_hashtable::iterator it = u.begin(); obj_hashtable::iterator end = u.end(); for (; it != end; ++it) { buf.push_back(*it); } m->register_usort(s, buf.size(), buf.c_ptr()); } return m; } z3-z3-4.4.1/src/smt/proto_model/proto_model.h000066400000000000000000000065761260446376700210540ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: proto_model.h Abstract: This is the old model object. smt::context used it during model construction and for reporting the model for external consumers. The whole value_factory "business" was due to model construction and unnecessary for external consumers. Future solvers will not use value_factory objects for helping during model construction. After smt::context finishes building the model, it is converted into a new (light) model object. Author: Leonardo de Moura (leonardo) 2007-03-08. Revision History: --*/ #ifndef PROTO_MODEL_H_ #define PROTO_MODEL_H_ #include"model_core.h" #include"value_factory.h" #include"plugin_manager.h" #include"simplifier.h" #include"arith_decl_plugin.h" #include"func_decl_dependencies.h" #include"model.h" #include"params.h" class proto_model : public model_core { ast_ref_vector m_asts; plugin_manager m_factories; user_sort_factory * m_user_sort_factory; simplifier & m_simplifier; family_id m_afid; //!< array family id: hack for displaying models in V1.x style func_decl_set m_aux_decls; ptr_vector m_tmp; bool m_model_partial; void reset_finterp(); expr * mk_some_interp_for(func_decl * d); // Invariant: m_decls subset m_func_decls union m_const_decls union union m_value_decls // Invariant: m_func_decls subset m_decls // Invariant: m_const_decls subset m_decls void remove_aux_decls_not_in_set(ptr_vector & decls, func_decl_set const & s); void cleanup_func_interp(func_interp * fi, func_decl_set & found_aux_fs); public: proto_model(ast_manager & m, simplifier & s, params_ref const & p = params_ref()); virtual ~proto_model(); void register_factory(value_factory * f) { m_factories.register_plugin(f); } bool eval(expr * e, expr_ref & result, bool model_completion = false); bool is_array_value(expr * v) const; value_factory * get_factory(family_id fid); expr * get_some_value(sort * s); bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2); expr * get_fresh_value(sort * s); void register_value(expr * n); // // Primitives for building models // void register_decl(func_decl * d, expr * v); void register_decl(func_decl * f, func_interp * fi, bool aux = false); void reregister_decl(func_decl * f, func_interp * new_fi, func_decl * f_aux); void compress(); void cleanup(); // // Primitives for building finite interpretations for uninterpreted sorts, // and retrieving the known universe. // void freeze_universe(sort * s); bool is_finite(sort * s) const; obj_hashtable const & get_known_universe(sort * s) const; virtual ptr_vector const & get_universe(sort * s) const; virtual unsigned get_num_uninterpreted_sorts() const; virtual sort * get_uninterpreted_sort(unsigned idx) const; // // Complete partial function interps // void complete_partial_func(func_decl * f); void complete_partial_funcs(); // // Create final model object. // proto_model is corrupted after that. model * mk_model(); }; typedef ref proto_model_ref; #endif /* PROTO_MODEL_H_ */ z3-z3-4.4.1/src/smt/proto_model/struct_factory.cpp000066400000000000000000000031241260446376700221210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: struct_factory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-11-06. Revision History: --*/ #include"struct_factory.h" #include"proto_model.h" struct_factory::value_set * struct_factory::get_value_set(sort * s) { value_set * set = 0; if (!m_sort2value_set.find(s, set)) { set = alloc(value_set); m_sort2value_set.insert(s, set); m_sorts.push_back(s); m_sets.push_back(set); } SASSERT(set != 0); return set; } struct_factory::struct_factory(ast_manager & m, family_id fid, proto_model & md): value_factory(m, fid), m_model(md), m_values(m), m_sorts(m) { } struct_factory::~struct_factory() { std::for_each(m_sets.begin(), m_sets.end(), delete_proc()); } void struct_factory::register_value(expr * new_value) { sort * s = m_manager.get_sort(new_value); value_set * set = get_value_set(s); if (!set->contains(new_value)) { m_values.push_back(new_value); set->insert(new_value); } } bool struct_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { value_set * set = get_value_set(s); switch (set->size()) { case 0: v1 = get_fresh_value(s); v2 = get_fresh_value(s); return v1 != 0 && v2 != 0; case 1: v1 = get_some_value(s); v2 = get_fresh_value(s); return v2 != 0; default: obj_hashtable::iterator it = set->begin(); v1 = *it; ++it; v2 = *it; return true; } } z3-z3-4.4.1/src/smt/proto_model/struct_factory.h000066400000000000000000000020621260446376700215660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: struct_factory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-11-06. Revision History: --*/ #ifndef STRUCT_FACTORY_H_ #define STRUCT_FACTORY_H_ #include"value_factory.h" #include"obj_hashtable.h" class proto_model; /** \brief Abstract factory for structured values such as: arrays and algebraic datatypes. */ class struct_factory : public value_factory { protected: typedef obj_hashtable value_set; typedef obj_map sort2value_set; proto_model & m_model; sort2value_set m_sort2value_set; expr_ref_vector m_values; sort_ref_vector m_sorts; ptr_vector m_sets; value_set * get_value_set(sort * s); public: struct_factory(ast_manager & m, family_id fid, proto_model & md); virtual ~struct_factory(); virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2); virtual void register_value(expr * array_value); }; #endif /* STRUCT_FACTORY_H_ */ z3-z3-4.4.1/src/smt/proto_model/value_factory.cpp000066400000000000000000000055461260446376700217230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: value_factory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #include"value_factory.h" value_factory::value_factory(ast_manager & m, family_id fid): m_manager(m), m_fid(fid) { } value_factory::~value_factory() { } basic_factory::basic_factory(ast_manager & m): value_factory(m, m.get_basic_family_id()) { } expr * basic_factory::get_some_value(sort * s) { if (m_manager.is_bool(s)) return m_manager.mk_false(); return 0; } bool basic_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { if (m_manager.is_bool(s)) { v1 = m_manager.mk_false(); v2 = m_manager.mk_true(); return true; } return false; } expr * basic_factory::get_fresh_value(sort * s) { return 0; } user_sort_factory::user_sort_factory(ast_manager & m): simple_factory(m, m.mk_family_id("user-sort")) { } void user_sort_factory::freeze_universe(sort * s) { if (!m_finite.contains(s)) { value_set * set = 0; m_sort2value_set.find(s, set); if (!m_sort2value_set.find(s, set) || set->m_values.empty()) { // we cannot freeze an empty universe. get_some_value(s); // add one element to the universe... } SASSERT(m_sort2value_set.find(s, set) && set != 0 && !set->m_values.empty()); m_finite.insert(s); } } obj_hashtable const & user_sort_factory::get_known_universe(sort * s) const { value_set * set = 0; if (m_sort2value_set.find(s, set)) { return set->m_values; } return m_empty_universe; } expr * user_sort_factory::get_some_value(sort * s) { if (is_finite(s)) { value_set * set = 0; m_sort2value_set.find(s, set); SASSERT(set != 0); SASSERT(!set->m_values.empty()); return *(set->m_values.begin()); } return simple_factory::get_some_value(s); } bool user_sort_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { if (is_finite(s)) { value_set * set = 0; if (m_sort2value_set.find(s, set) && set->m_values.size() >= 2) { obj_hashtable::iterator it = set->m_values.begin(); v1 = *it; ++it; v2 = *it; return true; } return false; } return simple_factory::get_some_values(s, v1, v2); } expr * user_sort_factory::get_fresh_value(sort * s) { if (is_finite(s)) return 0; return simple_factory::get_fresh_value(s); } void user_sort_factory::register_value(expr * n) { SASSERT(!is_finite(m_manager.get_sort(n))); simple_factory::register_value(n); } app * user_sort_factory::mk_value_core(unsigned const & val, sort * s) { return m_manager.mk_model_value(val, s); } z3-z3-4.4.1/src/smt/proto_model/value_factory.h000066400000000000000000000166441260446376700213710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: value_factory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #ifndef VALUE_FACTORY_H_ #define VALUE_FACTORY_H_ #include"ast.h" #include"obj_hashtable.h" /** \brief Auxiliary object used during model construction. */ class value_factory { protected: ast_manager & m_manager; family_id m_fid; public: value_factory(ast_manager & m, family_id fid); virtual ~value_factory(); /** \brief Return some value of the given sort. The result is always different from zero. */ virtual expr * get_some_value(sort * s) = 0; /** \brief Return two distinct values of the given sort. The results are stored in v1 and v2. Return false if the intended interpretation of the given sort has only one element. */ virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) = 0; /** \brief Return a fresh value of the given sort. Return 0 if it is not possible to do that (e.g., the sort is finite). */ virtual expr * get_fresh_value(sort * s) = 0; /** \brief Used to register that the given value was used in model construction. So, get_fresh_value cannot return this value anymore. */ virtual void register_value(expr * n) = 0; family_id get_family_id() const { return m_fid; } }; class basic_factory : public value_factory { public: basic_factory(ast_manager & m); virtual expr * get_some_value(sort * s); virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2); virtual expr * get_fresh_value(sort * s); virtual void register_value(expr * n) { } }; /** \brief Template for value factories for numeric (and enumeration-like) sorts */ template class simple_factory : public value_factory { protected: struct value_set { obj_hashtable m_values; Number m_next; value_set(): m_next(0) { } }; typedef obj_map sort2value_set; sort2value_set m_sort2value_set; expr_ref_vector m_values; sort_ref_vector m_sorts; ptr_vector m_sets; value_set * get_value_set(sort * s) { value_set * set = 0; if (!m_sort2value_set.find(s, set)) { set = alloc(value_set); m_sort2value_set.insert(s, set); m_sorts.push_back(s); m_sets.push_back(set); } SASSERT(set != 0); DEBUG_CODE({ value_set * set2 = 0; SASSERT(m_sort2value_set.find(s, set2)); SASSERT(set == set2); }); return set; } virtual app * mk_value_core(Number const & val, sort * s) = 0; app * mk_value(Number const & val, sort * s, bool & is_new) { value_set * set = get_value_set(s); app * new_val = mk_value_core(val, s); is_new = false; if (!set->m_values.contains(new_val)) { m_values.push_back(new_val); set->m_values.insert(new_val); is_new = true; } SASSERT(set->m_values.contains(new_val)); return new_val; } public: simple_factory(ast_manager & m, family_id fid): value_factory(m, fid), m_values(m), m_sorts(m) { } virtual ~simple_factory() { std::for_each(m_sets.begin(), m_sets.end(), delete_proc()); } virtual expr * get_some_value(sort * s) { value_set * set = 0; expr * result = 0; if (m_sort2value_set.find(s, set) && !set->m_values.empty()) result = *(set->m_values.begin()); else result = mk_value(Number(0), s); return result; } virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { value_set * set = 0; if (m_sort2value_set.find(s, set)) { switch (set->m_values.size()) { case 0: v1 = mk_value(Number(0), s); v2 = mk_value(Number(1), s); break; case 1: v1 = *(set->m_values.begin()); v2 = mk_value(Number(0), s); if (v1 == v2) v2 = mk_value(Number(1), s); break; default: obj_hashtable::iterator it = set->m_values.begin(); v1 = *it; ++it; v2 = *it; break; } SASSERT(v1 != v2); return true; } v1 = mk_value(Number(0), s); v2 = mk_value(Number(1), s); return true; } virtual expr * get_fresh_value(sort * s) { value_set * set = get_value_set(s); bool is_new = false; expr * result = 0; sort_info* s_info = s->get_info(); sort_size const* sz = s_info?&s_info->get_num_elements():0; bool has_max = false; Number max_size; if (sz && sz->is_finite() && sz->size() < UINT_MAX) { unsigned usz = static_cast(sz->size()); max_size = Number(usz); has_max = true; } Number start = set->m_next; Number & next = set->m_next; while (!is_new) { result = mk_value(next, s, is_new); next++; if (has_max && next > max_size + start) { return 0; } } SASSERT(result != 0); return result; } virtual void register_value(expr * n) { sort * s = this->m_manager.get_sort(n); value_set * set = get_value_set(s); if (!set->m_values.contains(n)) { m_values.push_back(n); set->m_values.insert(n); } } virtual app * mk_value(Number const & val, sort * s) { bool is_new; return mk_value(val, s, is_new); } unsigned get_num_sorts() const { return m_sorts.size(); } sort * get_sort(unsigned idx) const { return m_sorts.get(idx); } }; /** \brief Factory for creating values for uninterpreted sorts and user declared (uninterpreted) sorts. */ class user_sort_factory : public simple_factory { obj_hashtable m_finite; //!< set of sorts that are marked as finite. obj_hashtable m_empty_universe; virtual app * mk_value_core(unsigned const & val, sort * s); public: user_sort_factory(ast_manager & m); virtual ~user_sort_factory() {} /** \brief Make the universe of \c s finite, by preventing new elements to be added to its universe. */ void freeze_universe(sort * s); /** \brief Return true if the universe of \c s is frozen and finite. */ bool is_finite(sort * s) const { return m_finite.contains(s); } /** \brief Return the "known" universe of \c s. It doesn't matter whether s is finite or not. If \c s is finite, then it is the whole universe. */ obj_hashtable const & get_known_universe(sort * s) const; /** \brief Return sorts with finite interpretations. */ obj_hashtable const & get_finite_sorts() const { return m_finite; } virtual expr * get_some_value(sort * s); virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2); virtual expr * get_fresh_value(sort * s); virtual void register_value(expr * n); }; #endif /* VALUE_FACTORY_H_ */ z3-z3-4.4.1/src/smt/qi_queue.cpp000066400000000000000000000513011260446376700163400ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: qi_queue.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-15. Revision History: --*/ #include"smt_context.h" #include"qi_queue.h" #include"warning.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"var_subst.h" #include"stats.h" namespace smt { qi_queue::qi_queue(quantifier_manager & qm, context & ctx, qi_params & params): m_qm(qm), m_context(ctx), m_manager(m_context.get_manager()), m_params(params), m_checker(m_context), m_cost_function(m_manager), m_new_gen_function(m_manager), m_parser(m_manager), m_evaluator(m_manager), m_subst(m_manager), m_instances(m_manager) { init_parser_vars(); m_vals.resize(15, 0.0f); } qi_queue::~qi_queue() { } void qi_queue::setup() { TRACE("qi_cost", tout << "qi_cost: " << m_params.m_qi_cost << "\n";); if (!m_parser.parse_string(m_params.m_qi_cost.c_str(), m_cost_function)) { // it is not reasonable to abort here during the creation of smt::context just because an invalid option was provided. // throw default_exception("invalid cost function %s", m_params.m_qi_cost.c_str()); // using warning message instead warning_msg("invalid cost function '%s', switching to default one", m_params.m_qi_cost.c_str()); // Trying again with default function VERIFY(m_parser.parse_string("(+ weight generation)", m_cost_function)); } if (!m_parser.parse_string(m_params.m_qi_new_gen.c_str(), m_new_gen_function)) { // See comment above // throw default_exception("invalid new-gen function %s", m_params.m_qi_new_gen.c_str()); warning_msg("invalid new_gen function '%s', switching to default one", m_params.m_qi_new_gen.c_str()); VERIFY(m_parser.parse_string("cost", m_new_gen_function)); } m_eager_cost_threshold = m_params.m_qi_eager_threshold; } void qi_queue::init_parser_vars() { #define COST 14 m_parser.add_var("cost"); #define MIN_TOP_GENERATION 13 m_parser.add_var("min_top_generation"); #define MAX_TOP_GENERATION 12 m_parser.add_var("max_top_generation"); #define INSTANCES 11 m_parser.add_var("instances"); #define SIZE 10 m_parser.add_var("size"); #define DEPTH 9 m_parser.add_var("depth"); #define GENERATION 8 m_parser.add_var("generation"); #define QUANT_GENERATION 7 m_parser.add_var("quant_generation"); #define WEIGHT 6 m_parser.add_var("weight"); #define VARS 5 m_parser.add_var("vars"); #define PATTERN_WIDTH 4 m_parser.add_var("pattern_width"); #define TOTAL_INSTANCES 3 m_parser.add_var("total_instances"); #define SCOPE 2 m_parser.add_var("scope"); #define NESTED_QUANTIFIERS 1 m_parser.add_var("nested_quantifiers"); #define CS_FACTOR 0 m_parser.add_var("cs_factor"); } quantifier_stat * qi_queue::set_values(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation, float cost) { quantifier_stat * stat = m_qm.get_stat(q); m_vals[COST] = cost; m_vals[MIN_TOP_GENERATION] = static_cast(min_top_generation); m_vals[MAX_TOP_GENERATION] = static_cast(max_top_generation); m_vals[INSTANCES] = static_cast(stat->get_num_instances_curr_branch()); m_vals[SIZE] = static_cast(stat->get_size()); m_vals[DEPTH] = static_cast(stat->get_depth()); m_vals[GENERATION] = static_cast(generation); m_vals[QUANT_GENERATION] = static_cast(stat->get_generation()); m_vals[WEIGHT] = static_cast(q->get_weight()); m_vals[VARS] = static_cast(q->get_num_decls()); m_vals[PATTERN_WIDTH] = pat ? static_cast(pat->get_num_args()) : 1.0f; m_vals[TOTAL_INSTANCES] = static_cast(stat->get_num_instances_curr_search()); m_vals[SCOPE] = static_cast(m_context.get_scope_level()); m_vals[NESTED_QUANTIFIERS] = static_cast(stat->get_num_nested_quantifiers()); m_vals[CS_FACTOR] = static_cast(stat->get_case_split_factor()); TRACE("qi_queue_detail", for (unsigned i = 0; i < m_vals.size(); i++) { tout << m_vals[i] << " "; } tout << "\n";); return stat; } float qi_queue::get_cost(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation) { quantifier_stat * stat = set_values(q, pat, generation, min_top_generation, max_top_generation, 0); float r = m_evaluator(m_cost_function, m_vals.size(), m_vals.c_ptr()); stat->update_max_cost(r); return r; } unsigned qi_queue::get_new_gen(quantifier * q, unsigned generation, float cost) { // max_top_generation and min_top_generation are not available for computing inc_gen set_values(q, 0, generation, 0, 0, cost); float r = m_evaluator(m_new_gen_function, m_vals.size(), m_vals.c_ptr()); return static_cast(r); } void qi_queue::insert(fingerprint * f, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation) { quantifier * q = static_cast(f->get_data()); float cost = get_cost(q, pat, generation, min_top_generation, max_top_generation); TRACE("qi_queue_detail", tout << "new instance of " << q->get_qid() << ", weight " << q->get_weight() << ", generation: " << generation << ", scope_level: " << m_context.get_scope_level() << ", cost: " << cost << "\n"; for (unsigned i = 0; i < f->get_num_args(); i++) { tout << "#" << f->get_arg(i)->get_owner_id() << " "; } tout << "\n";); TRACE("new_entries_bug", tout << "[qi:insert]\n";); m_new_entries.push_back(entry(f, cost, generation)); } void qi_queue::instantiate() { svector::iterator it = m_new_entries.begin(); svector::iterator end = m_new_entries.end(); unsigned since_last_check = 0; for (; it != end; ++it) { entry & curr = *it; fingerprint * f = curr.m_qb; quantifier * qa = static_cast(f->get_data()); if (curr.m_cost <= m_eager_cost_threshold) { instantiate(curr); } else if (m_params.m_qi_promote_unsat && m_checker.is_unsat(qa->get_expr(), f->get_num_args(), f->get_args())) { // do not delay instances that produce a conflict. TRACE("qi_unsat", tout << "promoting instance that produces a conflict\n" << mk_pp(qa, m_manager) << "\n";); instantiate(curr); } else { TRACE("qi_queue", tout << "delaying quantifier instantiation... " << f << "\n" << mk_pp(qa, m_manager) << "\ncost: " << curr.m_cost << "\n";); m_delayed_entries.push_back(curr); } // Periodically check if we didn't run out of time/memory. if (since_last_check++ > 100) { if (m_context.resource_limits_exceeded()) { // verbose_stream() << "EXCEEDED...\n"; break; } since_last_check = 0; } } m_new_entries.reset(); TRACE("new_entries_bug", tout << "[qi:instatiate]\n";); } void qi_queue::display_instance_profile(fingerprint * f, quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned proof_id, unsigned generation) { if (m_manager.has_trace_stream()) { m_manager.trace_stream() << "[instance] "; #if 1 m_manager.trace_stream() << static_cast(f); #else for (unsigned i = 0; i < num_bindings; i++) { // I don't want to use mk_pp because it creates expressions for pretty printing. // This nasty side-effect may change the behavior of Z3. m_manager.trace_stream() << " #" << bindings[i]->get_owner_id(); } #endif if (m_manager.proofs_enabled()) m_manager.trace_stream() << " #" << proof_id; m_manager.trace_stream() << " ; " << generation; m_manager.trace_stream() << "\n"; } } void qi_queue::instantiate(entry & ent) { fingerprint * f = ent.m_qb; quantifier * q = static_cast(f->get_data()); unsigned generation = ent.m_generation; unsigned num_bindings = f->get_num_args(); enode * const * bindings = f->get_args(); ent.m_instantiated = true; TRACE("qi_queue_profile", tout << q->get_qid() << ", gen: " << generation; for (unsigned i = 0; i < num_bindings; i++) tout << " #" << bindings[i]->get_owner_id(); tout << "\n";); if (m_checker.is_sat(q->get_expr(), num_bindings, bindings)) { TRACE("checker", tout << "instance already satisfied\n";); return; } expr_ref instance(m_manager); m_subst(q, num_bindings, bindings, instance); TRACE("qi_queue", tout << "new instance:\n" << mk_pp(instance, m_manager) << "\n";); TRACE("qi_queue_instance", tout << "new instance:\n" << mk_pp(instance, m_manager) << "\n";); expr_ref s_instance(m_manager); proof_ref pr(m_manager); simplifier & simp = m_context.get_simplifier(); simp(instance, s_instance, pr); TRACE("qi_queue_bug", tout << "new instance after simplification:\n" << mk_pp(s_instance, m_manager) << "\n";); if (m_manager.is_true(s_instance)) { TRACE("checker", tout << "reduced to true, before:\n" << mk_ll_pp(instance, m_manager);); if (m_manager.has_trace_stream()) m_manager.trace_stream() << "[end-of-instance]\n"; return; } quantifier_stat * stat = m_qm.get_stat(q); stat->inc_num_instances(); if (stat->get_num_instances() % m_params.m_qi_profile_freq == 0) { m_qm.display_stats(verbose_stream(), q); } expr_ref lemma(m_manager); if (m_manager.is_or(s_instance)) { ptr_vector args; args.push_back(m_manager.mk_not(q)); args.append(to_app(s_instance)->get_num_args(), to_app(s_instance)->get_args()); lemma = m_manager.mk_or(args.size(), args.c_ptr()); } else if (m_manager.is_false(s_instance)) { lemma = m_manager.mk_not(q); } else if (m_manager.is_true(s_instance)) { lemma = s_instance; } else { lemma = m_manager.mk_or(m_manager.mk_not(q), s_instance); } m_instances.push_back(lemma); proof_ref pr1(m_manager); unsigned proof_id = 0; if (m_manager.proofs_enabled()) { expr_ref_vector bindings_e(m_manager); for (unsigned i = 0; i < num_bindings; ++i) { bindings_e.push_back(bindings[i]->get_owner()); } app * bare_lemma = m_manager.mk_or(m_manager.mk_not(q), instance); proof * qi_pr = m_manager.mk_quant_inst(bare_lemma, num_bindings, bindings_e.c_ptr()); proof_id = qi_pr->get_id(); if (bare_lemma == lemma) { pr1 = qi_pr; } else if (instance == s_instance) { proof * rw = m_manager.mk_rewrite(bare_lemma, lemma); pr1 = m_manager.mk_modus_ponens(qi_pr, rw); } else { app * bare_s_lemma = m_manager.mk_or(m_manager.mk_not(q), s_instance); proof * prs[1] = { pr.get() }; proof * cg = m_manager.mk_congruence(bare_lemma, bare_s_lemma, 1, prs); proof * rw = m_manager.mk_rewrite(bare_s_lemma, lemma); proof * tr = m_manager.mk_transitivity(cg, rw); pr1 = m_manager.mk_modus_ponens(qi_pr, tr); } m_instances.push_back(pr1); } TRACE("qi_queue", tout << mk_pp(lemma, m_manager) << "\n#" << lemma->get_id() << ":=\n" << mk_ll_pp(lemma, m_manager);); m_stats.m_num_instances++; unsigned gen = get_new_gen(q, generation, ent.m_cost); display_instance_profile(f, q, num_bindings, bindings, proof_id, gen); m_context.internalize_instance(lemma, pr1, gen); TRACE_CODE({ static unsigned num_useless = 0; if (m_manager.is_or(lemma)) { app * n = to_app(lemma); bool has_unassigned = false; expr * true_child = 0; for (unsigned i = 0; i < n->get_num_args(); i++) { expr * arg = n->get_arg(i); switch(m_context.get_assignment(arg)) { case l_undef: has_unassigned = true; break; case l_true: true_child = arg; break; default: break; } } if (true_child && has_unassigned) { TRACE("qi_queue_profile_detail", tout << "missed:\n" << mk_ll_pp(s_instance, m_manager) << "\n#" << true_child->get_id() << "\n";); num_useless++; if (num_useless % 10 == 0) { TRACE("qi_queue_profile", tout << "num useless: " << num_useless << "\n";); } } } }); if (m_manager.has_trace_stream()) m_manager.trace_stream() << "[end-of-instance]\n"; } void qi_queue::push_scope() { TRACE("new_entries_bug", tout << "[qi:push-scope]\n";); m_scopes.push_back(scope()); SASSERT(m_new_entries.empty()); scope & s = m_scopes.back(); s.m_delayed_entries_lim = m_delayed_entries.size(); s.m_instances_lim = m_instances.size(); s.m_instantiated_trail_lim = m_instantiated_trail.size(); } void qi_queue::pop_scope(unsigned num_scopes) { unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; unsigned old_sz = s.m_instantiated_trail_lim; unsigned sz = m_instantiated_trail.size(); for (unsigned i = old_sz; i < sz; i++) m_delayed_entries[m_instantiated_trail[i]].m_instantiated = false; m_instantiated_trail.shrink(old_sz); m_delayed_entries.shrink(s.m_delayed_entries_lim); m_instances.shrink(s.m_instances_lim); m_new_entries.reset(); m_scopes.shrink(new_lvl); TRACE("new_entries_bug", tout << "[qi:pop-scope]\n";); } void qi_queue::reset() { m_new_entries.reset(); m_delayed_entries.reset(); m_instances.reset(); m_scopes.reset(); } void qi_queue::init_search_eh() { m_subst.reset(); } bool qi_queue::final_check_eh() { TRACE("qi_queue", display_delayed_instances_stats(tout); tout << "lazy threshold: " << m_params.m_qi_lazy_threshold << ", scope_level: " << m_context.get_scope_level() << "\n";); if (m_params.m_qi_conservative_final_check) { bool init = false; float min_cost; unsigned sz = m_delayed_entries.size(); for (unsigned i = 0; i < sz; i++) { entry & e = m_delayed_entries[i]; fingerprint * f = e.m_qb; TRACE("qi_queue", tout << f << ", cost: " << e.m_cost << ", instantiated: " << e.m_instantiated << "\n";); if (!e.m_instantiated && e.m_cost <= m_params.m_qi_lazy_threshold && (!init || e.m_cost < min_cost)) { init = true; min_cost = e.m_cost; } } TRACE("qi_queue_min_cost", tout << "min_cost: " << min_cost << ", scope_level: " << m_context.get_scope_level() << "\n";); bool result = true; for (unsigned i = 0; i < sz; i++) { entry & e = m_delayed_entries[i]; fingerprint * f = e.m_qb; quantifier * qa = static_cast(f->get_data()); TRACE("qi_queue", tout << f << ", cost: " << e.m_cost << ", instantiated: " << e.m_instantiated << "\n";); if (!e.m_instantiated && e.m_cost <= min_cost) { TRACE("qi_queue", tout << "lazy quantifier instantiation...\n" << mk_pp(qa, m_manager) << "\ncost: " << e.m_cost << "\n";); result = false; m_instantiated_trail.push_back(i); m_stats.m_num_lazy_instances++; instantiate(e); } } return result; } bool result = true; for (unsigned i = 0; i < m_delayed_entries.size(); i++) { entry & e = m_delayed_entries[i]; fingerprint * f = e.m_qb; quantifier * qa = static_cast(f->get_data()); TRACE("qi_queue", tout << f << ", cost: " << e.m_cost << ", instantiated: " << e.m_instantiated << "\n";); if (!e.m_instantiated && e.m_cost <= m_params.m_qi_lazy_threshold) { TRACE("qi_queue", tout << "lazy quantifier instantiation...\n" << mk_pp(qa, m_manager) << "\ncost: " << e.m_cost << "\n";); result = false; m_instantiated_trail.push_back(i); m_stats.m_num_lazy_instances++; instantiate(e); } } return result; } struct delayed_qa_info { unsigned m_num; float m_min_cost; float m_max_cost; delayed_qa_info():m_num(0), m_min_cost(0.0f), m_max_cost(0.0f) {} }; void qi_queue::display_delayed_instances_stats(std::ostream & out) const { obj_map qa2info; ptr_vector qas; svector::const_iterator it = m_delayed_entries.begin(); svector::const_iterator end = m_delayed_entries.end(); for (; it != end; ++it) { entry const & e = *it; if (e.m_instantiated) continue; quantifier * qa = static_cast(e.m_qb->get_data()); delayed_qa_info info; if (qa2info.find(qa, info)) { info.m_num++; info.m_min_cost = std::min(info.m_min_cost, e.m_cost); info.m_max_cost = std::min(info.m_max_cost, e.m_cost); } else { qas.push_back(qa); info.m_num = 1; info.m_min_cost = e.m_cost; info.m_max_cost = e.m_cost; } qa2info.insert(qa, info); } ptr_vector::iterator it2 = qas.begin(); ptr_vector::iterator end2 = qas.end(); for (; it2 != end2; ++it2) { quantifier * qa = *it2; delayed_qa_info info; qa2info.find(qa, info); out << qa->get_qid() << ": " << info.m_num << " [" << info.m_min_cost << ", " << info.m_max_cost << "]\n"; } } void qi_queue::get_min_max_costs(float & min, float & max) const { min = 0.0f; max = 0.0f; bool found = false; for (unsigned i = 0; i < m_delayed_entries.size(); i++) { if (!m_delayed_entries[i].m_instantiated) { float c = m_delayed_entries[i].m_cost; if (found) { min = std::min(min, c); max = std::max(max, c); } else { found = true; min = c; max = c; } } } } void qi_queue::collect_statistics(::statistics & st) const { st.update("quant instantiations", m_stats.m_num_instances); st.update("lazy quant instantiations", m_stats.m_num_lazy_instances); st.update("missed quant instantiations", m_delayed_entries.size()); float min, max; get_min_max_costs(min, max); st.update("min missed qa cost", min); st.update("max missed qa cost", max); #if 0 if (m_params.m_qi_profile) { out << "missed/delayed quantifier instances:\n"; display_delayed_instances_stats(out); } #endif } }; z3-z3-4.4.1/src/smt/qi_queue.h000066400000000000000000000070531260446376700160120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: qi_queue.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-15. Revision History: --*/ #ifndef QI_QUEUE_H_ #define QI_QUEUE_H_ #include"ast.h" #include"smt_quantifier_stat.h" #include"smt_checker.h" #include"smt_quantifier.h" #include"qi_params.h" #include"fingerprints.h" #include"cost_parser.h" #include"cost_evaluator.h" #include"cached_var_subst.h" #include"statistics.h" namespace smt { class context; struct qi_queue_stats { unsigned m_num_instances, m_num_lazy_instances; void reset() { memset(this, 0, sizeof(qi_queue_stats)); } qi_queue_stats() { reset(); } }; class qi_queue { quantifier_manager & m_qm; context & m_context; ast_manager & m_manager; qi_params & m_params; qi_queue_stats m_stats; checker m_checker; expr_ref m_cost_function; expr_ref m_new_gen_function; cost_parser m_parser; cost_evaluator m_evaluator; cached_var_subst m_subst; svector m_vals; double m_eager_cost_threshold; struct entry { fingerprint * m_qb; float m_cost; unsigned m_generation:31; unsigned m_instantiated:1; entry(fingerprint * f, float c, unsigned g):m_qb(f), m_cost(c), m_generation(g), m_instantiated(false) {} }; svector m_new_entries; svector m_delayed_entries; expr_ref_vector m_instances; unsigned_vector m_instantiated_trail; struct scope { unsigned m_delayed_entries_lim; unsigned m_instances_lim; unsigned m_instantiated_trail_lim; }; svector m_scopes; void init_parser_vars(); quantifier_stat * set_values(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation, float cost); float get_cost(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation); unsigned get_new_gen(quantifier * q, unsigned generation, float cost); void instantiate(entry & ent); void get_min_max_costs(float & min, float & max) const; void display_instance_profile(fingerprint * f, quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned proof_id, unsigned generation); public: qi_queue(quantifier_manager & qm, context & ctx, qi_params & params); ~qi_queue(); void setup(); /** \brief Insert a new quantifier in the queue, f contains the quantifier and bindings. f->get_data() is the quantifier. */ void insert(fingerprint * f, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation); void instantiate(); bool has_work() const { return !m_new_entries.empty(); } void init_search_eh(); bool final_check_eh(); void push_scope(); void pop_scope(unsigned num_scopes); void reset(); void display_delayed_instances_stats(std::ostream & out) const; void collect_statistics(::statistics & st) const; }; }; #endif /* QI_QUEUE_H_ */ z3-z3-4.4.1/src/smt/smt_almost_cg_table.cpp000066400000000000000000000064211260446376700205300ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_almost_cg_table.cpp Abstract: Author: Leonardo de Moura (leonardo) 2009-03-06. Revision History: --*/ #include"smt_almost_cg_table.h" namespace smt { inline unsigned almost_cg_table::cg_hash::arg_hash(enode * n, unsigned idx) const { enode * arg = n->get_arg(idx)->get_root(); if (arg == m_r1 || arg == m_r2) return 17; return arg->hash(); } unsigned almost_cg_table::cg_hash::operator()(enode * n) const { unsigned num_args = n->get_num_args(); unsigned kind_hash = n->get_decl_id(); if (num_args == 1) return kind_hash; unsigned a = 0x9e3779b9; unsigned b = 0x9e3779b9; unsigned c = 11; switch (num_args) { case 2: a += kind_hash; b += arg_hash(n, 0); c += arg_hash(n, 1); mix(a, b, c); return c; case 3: a += kind_hash; b += arg_hash(n, 0); c += arg_hash(n, 1); mix(a, b, c); c += arg_hash(n, 1); mix(a, b, c); return c; default: while (num_args >= 3) { num_args--; a += arg_hash(n, num_args); num_args--; b += arg_hash(n, num_args); num_args--; c += arg_hash(n, num_args); mix(a, b, c); } a += kind_hash; switch (num_args) { case 2: b += arg_hash(n, 1); __fallthrough; case 1: c += arg_hash(n, 0); } mix(a, b, c); return c; } } bool almost_cg_table::cg_eq::operator()(enode * n1, enode * n2) const { if (n1->get_owner()->get_decl() != n2->get_owner()->get_decl()) return false; unsigned num_args = n1->get_num_args(); if (num_args != n2->get_num_args()) return false; for (unsigned j = 0; j < num_args; j++) { enode * arg1 = n1->get_arg(j)->get_root(); enode * arg2 = n2->get_arg(j)->get_root(); if (arg1 == arg2) continue; if ((arg1 == m_r1 || arg1 == m_r2) && (arg2 == m_r1 || arg2 == m_r2)) continue; return false; } return true; } almost_cg_table::almost_cg_table(enode * r1, enode * r2): m_r1(r1), m_r2(r2), m_table(cg_hash(m_r1, m_r2), cg_eq(m_r1, m_r2)) { } void almost_cg_table::reset() { m_region.reset(); m_table.reset(); } void almost_cg_table::insert(enode * n) { table::entry * entry = m_table.find_core(n); if (entry == 0) { list * new_lst = new (m_region) list(n, 0); m_table.insert(n, new_lst); } else { list * new_lst = new (m_region) list(n, entry->get_data().m_value); entry->get_data().m_value = new_lst; } } list * almost_cg_table::find(enode * n) { list * result = 0; m_table.find(n, result); return result; } }; z3-z3-4.4.1/src/smt/smt_almost_cg_table.h000066400000000000000000000034671260446376700202040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_almost_cg_table.h Abstract: Author: Leonardo de Moura (leonardo) 2009-03-06. Revision History: --*/ #ifndef SMT_ALMOST_CG_TABLE_H_ #define SMT_ALMOST_CG_TABLE_H_ #include"smt_enode.h" #include"map.h" namespace smt { /** \brief An index for detecting 'almost' congruences. We say (f t_1 ... t_n) is almost congruent to (f s_1 ... s_n) with respect to (r1,r2) iff for all j in [1,n] j t_j = s_j or (t_j = r1 and s_j = r1) or (t_j = r1 and s_j = r2) or (t_j = r2 and s_j = r1) or (t_j = r2 and s_j = r2) This index is used to speedup is_ext_diseq. */ class almost_cg_table { struct cg_hash { enode * & m_r1; enode * & m_r2; unsigned arg_hash(enode * n, unsigned idx) const; public: cg_hash(enode * & r1, enode * & r2):m_r1(r1), m_r2(r2) {} unsigned operator()(enode * n) const; }; struct cg_eq { enode * & m_r1; enode * & m_r2; public: cg_eq(enode * & r1, enode * & r2):m_r1(r1), m_r2(r2) {} bool operator()(enode * n1, enode * n2) const; }; typedef map *, cg_hash, cg_eq> table; region m_region; enode * m_r1; enode * m_r2; table m_table; public: almost_cg_table(enode * r1 = 0, enode * r2 = 0); void reset(enode * r1, enode * r2) { m_r1 = r1->get_root(); m_r2 = r2->get_root(); reset(); } void reset(); void insert(enode *); void erase(enode * n) { m_table.erase(n); } list * find(enode *); bool empty() const { return m_table.empty(); } }; }; #endif /* SMT_ALMOST_CG_TABLE_H_ */ z3-z3-4.4.1/src/smt/smt_b_justification.h000066400000000000000000000050141260446376700202270ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_b_justification.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef SMT_B_JUSTIFICATION_H_ #define SMT_B_JUSTIFICATION_H_ #include"smt_literal.h" #include"smt_clause.h" namespace smt { /** \brief Proof like object used to track dependencies of boolean propagation. The idea is to reduce the cost of dependency tracking for the most common justifications used during boolean propagation: unit propagation */ class b_justification { void * m_data; public: enum kind { CLAUSE, //!< clause of arbitrary size BIN_CLAUSE, //!< binary clause AXIOM, //!< no justification, it is only use if proof generation is disabled JUSTIFICATION //!< fallback }; b_justification(): m_data(reinterpret_cast(static_cast(AXIOM))) {} b_justification(b_justification const & source): m_data(source.m_data) { } explicit b_justification(clause * c): m_data(TAG(void*, c, CLAUSE)) { } explicit b_justification(literal l): m_data(BOXTAGINT(void*, l.index(), BIN_CLAUSE)) { } explicit b_justification(justification * js): m_data(TAG(void*, js, JUSTIFICATION)) { SASSERT(js); } kind get_kind() const { return static_cast(GET_TAG(m_data)); } clause * get_clause() const { SASSERT(get_kind() == CLAUSE); return UNTAG(clause*, m_data); } justification * get_justification() const { SASSERT(get_kind() == JUSTIFICATION); return UNTAG(justification*, m_data); } literal get_literal() const { SASSERT(get_kind() == BIN_CLAUSE); return to_literal(UNBOXINT(m_data)); } bool operator==(b_justification const & other) const { return m_data == other.m_data; } bool operator!=(b_justification const & other) const { return !operator==(other); } static b_justification mk_axiom() { return b_justification(); } }; const b_justification null_b_justification(static_cast(0)); typedef std::pair justified_literal; }; #endif /* SMT_B_JUSTIFICATION_H_ */ z3-z3-4.4.1/src/smt/smt_bool_var_data.h000066400000000000000000000070761260446376700176610ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_bool_var_data.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-25. Revision History: --*/ #ifndef SMT_BOOL_VAR_DATA_H_ #define SMT_BOOL_VAR_DATA_H_ #include"smt_b_justification.h" namespace smt { struct bool_var_data { b_justification m_justification; unsigned m_scope_lvl:24; //!< scope level of when the variable was assigned. unsigned m_mark:1; unsigned m_assumption:1; unsigned m_phase_available:1; unsigned m_phase:1; private: unsigned m_eq:1; unsigned m_true_first:1; //!< If True, when case splitting try the true phase first. Otherwise, you default phase selection heuristic. unsigned m_enode:1; //!< has enode associated with it. unsigned m_quantifier:1; //!< true if bool var is attached to a quantifier unsigned m_iscope_lvl:23; //!< scope level of when the variable was internalized. unsigned m_atom:1; //!< logical or of m_eq, m_enode, m_quantifier, and m_notify_theory != 0 unsigned m_notify_theory:8; void update_atom_flag() { m_atom = m_eq || m_notify_theory != 0 || m_quantifier || m_enode; } public: unsigned get_intern_level() const { return m_iscope_lvl; } bool is_atom() const { return m_atom; } theory_id get_theory() const { return m_notify_theory == 0 ? null_theory_id : static_cast(m_notify_theory); } bool is_theory_atom() const { return m_notify_theory != 0; } void set_notify_theory(theory_id thid) { SASSERT(thid > 0 && thid <= 255); m_notify_theory = thid; m_atom = true; } void reset_notify_theory() { m_notify_theory = 0; update_atom_flag(); } bool is_enode() const { return m_enode; } void set_enode_flag() { m_enode = true; m_atom = true; } void reset_enode_flag() { m_enode = false; update_atom_flag(); } bool is_quantifier() const { return m_quantifier; } void set_quantifier_flag() { m_quantifier = true; m_atom = true; } bool is_eq() const { return m_eq; } void set_eq_flag() { m_eq = true; m_atom = true; } void reset_eq_flag() { m_eq = false; update_atom_flag(); } bool try_true_first() const { return m_true_first; } void set_true_first_flag() { m_true_first = true; } void reset_true_first_flag() { m_true_first = false; } void init(unsigned iscope_lvl) { m_justification = null_b_justification; m_scope_lvl = 0; m_mark = false; m_assumption = false; m_phase_available = false; m_phase = false; m_iscope_lvl = iscope_lvl; m_eq = false; m_true_first = false; m_notify_theory = 0; m_enode = false; m_quantifier = false; m_atom = false; } }; }; #endif /* SMT_BOOL_VAR_DATA_H_ */ z3-z3-4.4.1/src/smt/smt_case_split_queue.cpp000066400000000000000000001162301260446376700207430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_case_split_queue.cpp Abstract: Author: Leonardo de Moura (leonardo) 2009-01-20. Revision History: --*/ #include"smt_context.h" #include"smt_case_split_queue.h" #include"warning.h" #include"stopwatch.h" #include"for_each_expr.h" #include"ast_pp.h" namespace smt { struct bool_var_act_lt { svector const & m_activity; bool_var_act_lt(svector const & a):m_activity(a) {} bool operator()(bool_var v1, bool_var v2) const { return m_activity[v1] > m_activity[v2]; } }; typedef heap bool_var_act_queue; /** \brief Case split queue based on activity and random splits. */ class act_case_split_queue : public case_split_queue { protected: context & m_context; smt_params & m_params; bool_var_act_queue m_queue; public: act_case_split_queue(context & ctx, smt_params & p): m_context(ctx), m_params(p), m_queue(1024, bool_var_act_lt(ctx.get_activity_vector())) { } virtual void activity_increased_eh(bool_var v) { if (m_queue.contains(v)) m_queue.decreased(v); } virtual void mk_var_eh(bool_var v) { m_queue.reserve(v+1); m_queue.insert(v); } virtual void del_var_eh(bool_var v) { if (m_queue.contains(v)) m_queue.erase(v); } virtual void unassign_var_eh(bool_var v) { if (!m_queue.contains(v)) m_queue.insert(v); } virtual void relevant_eh(expr * n) {} virtual void init_search_eh() {} virtual void end_search_eh() {} virtual void reset() { m_queue.reset(); } virtual void push_scope() {} virtual void pop_scope(unsigned num_scopes) {} virtual void next_case_split(bool_var & next, lbool & phase) { phase = l_undef; if (m_context.get_random_value() < static_cast(m_params.m_random_var_freq * random_gen::max_value())) { next = m_context.get_random_value() % m_context.get_num_b_internalized(); TRACE("random_split", tout << "next: " << next << " get_assignment(next): " << m_context.get_assignment(next) << "\n";); if (m_context.get_assignment(next) == l_undef) return; } while (!m_queue.empty()) { next = m_queue.erase_min(); if (m_context.get_assignment(next) == l_undef) return; } next = null_bool_var; } virtual void display(std::ostream & out) { bool first = true; bool_var_act_queue::const_iterator it = m_queue.begin(); bool_var_act_queue::const_iterator end = m_queue.end(); for (; it != end ; ++it) { unsigned v = *it; if (m_context.get_assignment(v) == l_undef) { if (first) { out << "remaining case-splits:\n"; first = false; } out << "#" << m_context.bool_var2expr(v)->get_id() << " "; } } if (!first) out << "\n"; } virtual ~act_case_split_queue() {}; }; /** \brief Similar to dact_case_split_queue, but delay case splits created during the search. */ class dact_case_split_queue : public act_case_split_queue { bool_var_act_queue m_delayed_queue; public: dact_case_split_queue(context & ctx, smt_params & p): act_case_split_queue(ctx, p), m_delayed_queue(1024, bool_var_act_lt(ctx.get_activity_vector())) { } virtual void activity_increased_eh(bool_var v) { act_case_split_queue::activity_increased_eh(v); if (m_queue.contains(v)) m_queue.decreased(v); } virtual void mk_var_eh(bool_var v) { m_queue.reserve(v+1); m_delayed_queue.reserve(v+1); if (m_context.is_searching()) m_delayed_queue.insert(v); else m_queue.insert(v); } virtual void del_var_eh(bool_var v) { act_case_split_queue::del_var_eh(v); if (m_delayed_queue.contains(v)) m_delayed_queue.erase(v); } virtual void relevant_eh(expr * n) {} virtual void init_search_eh() {} virtual void end_search_eh() {} virtual void reset() { act_case_split_queue::reset(); m_delayed_queue.reset(); } virtual void push_scope() {} virtual void pop_scope(unsigned num_scopes) {} virtual void next_case_split(bool_var & next, lbool & phase) { act_case_split_queue::next_case_split(next, phase); if (next != null_bool_var) return; m_queue.swap(m_delayed_queue); SASSERT(m_delayed_queue.empty()); while (!m_queue.empty()) { next = m_queue.erase_min(); if (m_context.get_assignment(next) == l_undef) return; } next = null_bool_var; } }; /** \brief Case split queue based on activity and random splits. */ class cact_case_split_queue : public act_case_split_queue { obj_map m_cache; expr_ref_vector m_cache_domain; public: cact_case_split_queue(context & ctx, smt_params & p): act_case_split_queue(ctx, p), m_cache_domain(ctx.get_manager()) { } virtual void mk_var_eh(bool_var v) { expr * n = m_context.bool_var2expr(v); double act; if (m_cache.find(n, act)) m_context.set_activity(v, act); act_case_split_queue::mk_var_eh(v); } virtual void del_var_eh(bool_var v) { if (m_context.is_searching()) { double act = m_context.get_activity(v); if (act > 0.0) { expr * n = m_context.bool_var2expr(v); m_cache.insert(n, act); m_cache_domain.push_back(n); } } act_case_split_queue::del_var_eh(v); } virtual void init_search_eh() { m_cache.reset(); m_cache_domain.reset(); } virtual void end_search_eh() {} virtual void reset() { init_search_eh(); } }; static bool has_child_assigned_to(context & ctx, app * parent, lbool val, expr * & undef_child, unsigned order) { ptr_vector undef_children; bool found_undef = false; unsigned num_args = parent->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = parent->get_arg(i); lbool arg_val = ctx.get_assignment(arg); if (arg_val == val) return true; if (found_undef && order == 0) continue; if (arg_val == l_undef) { if (order == 1) undef_children.push_back(arg); else undef_child = arg; found_undef = true; } } if (order == 1) { if (undef_children.size() == 0) { // a bug? } else if (undef_children.size() == 1) { undef_child = undef_children[0]; } else { undef_child = undef_children[ctx.get_random_value() % undef_children.size()]; } } return false; } /** \brief Case split queue based on relevancy propagation */ class rel_case_split_queue : public case_split_queue { struct scope { unsigned m_queue_trail; unsigned m_head_old; unsigned m_queue2_trail; unsigned m_head2_old; }; typedef int_hashtable > bool_var_set; context & m_context; smt_params &m_params; ast_manager & m_manager; ptr_vector m_queue; unsigned m_head; int m_bs_num_bool_vars; //!< Number of boolean variable before starting to search. ptr_vector m_queue2; unsigned m_head2; svector m_scopes; public: rel_case_split_queue(context & ctx, smt_params & p): m_context(ctx), m_params(p), m_manager(ctx.get_manager()), m_head(0), m_bs_num_bool_vars(UINT_MAX), m_head2(0) { } virtual void activity_increased_eh(bool_var v) {} virtual void mk_var_eh(bool_var v) {} virtual void del_var_eh(bool_var v) {} virtual void unassign_var_eh(bool_var v) {} virtual void relevant_eh(expr * n) { if (!m_manager.is_bool(n)) return; bool is_or = m_manager.is_or(n); bool intern = m_context.b_internalized(n); if (!intern && !is_or) return; bool_var var = null_bool_var; if (intern) { var = m_context.get_bool_var(n); SASSERT(var != null_bool_var); bool is_and = m_manager.is_and(n); lbool val = m_context.get_assignment(var); if (!( val == l_undef || // n was not assigned yet (is_or && val == l_true) || // need to justify a child (is_and && val == l_false) // need to justify a child )) return; } if (!intern && m_context.is_searching()) { SASSERT(is_or); m_queue2.push_back(n); return; } if (var < m_bs_num_bool_vars) m_queue.push_back(n); else m_queue2.push_back(n); } virtual void init_search_eh() { m_bs_num_bool_vars = m_context.get_num_bool_vars(); } virtual void end_search_eh() { m_bs_num_bool_vars = UINT_MAX; } virtual void reset() { m_queue.reset(); m_head = 0; m_queue2.reset(); m_head2 = 0; m_scopes.reset(); } virtual void push_scope() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_queue_trail = m_queue.size(); s.m_head_old = m_head; s.m_queue2_trail = m_queue2.size(); s.m_head2_old = m_head2; TRACE("case_split", tout << "head: " << m_head << "\n";); } virtual void pop_scope(unsigned num_scopes) { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; m_queue.shrink(s.m_queue_trail); m_head = s.m_head_old; m_queue2.shrink(s.m_queue2_trail); m_head2 = s.m_head2_old; m_scopes.shrink(new_lvl); SASSERT(m_head <= m_queue.size()); TRACE("case_split", display(tout); tout << "head: " << m_head << "\n";); } void next_case_split_core(ptr_vector & queue, unsigned & head, bool_var & next, lbool & phase) { phase = l_undef; unsigned sz = queue.size(); for (; head < sz; head++) { expr * curr = queue[head]; bool is_or = m_manager.is_or(curr); bool is_and = m_manager.is_and(curr); bool intern = m_context.b_internalized(curr); SASSERT(intern || is_or); lbool val = l_undef; if (intern) { next = m_context.get_bool_var(curr); val = m_context.get_assignment(next); } else { SASSERT(is_or); // top level clause val = l_true; } if ((is_or && val == l_true) || (is_and && val == l_false)) { expr * undef_child = 0; if (!has_child_assigned_to(m_context, to_app(curr), val, undef_child, m_params.m_rel_case_split_order)) { if (m_manager.has_trace_stream()) { m_manager.trace_stream() << "[decide-and-or] #" << curr->get_id() << " #" << undef_child->get_id() << "\n"; } TRACE("case_split", tout << "found AND/OR candidate: #" << curr->get_id() << " #" << undef_child->get_id() << "\n";); literal l = m_context.get_literal(undef_child); next = l.var(); phase = l.sign() ? l_false : l_true; TRACE("case_split", display(tout);); return; } } else if (val == l_undef) { SASSERT(intern && m_context.get_bool_var(curr) == next); TRACE("case_split", tout << "found candidate: #" << curr->get_id() << "\n";); phase = l_undef; TRACE("case_split", display(tout);); return; } } next = null_bool_var; } virtual void next_case_split(bool_var & next, lbool & phase) { next_case_split_core(m_queue, m_head, next, phase); if (next == null_bool_var) next_case_split_core(m_queue2, m_head2, next, phase); // Force l_false is next is an equality that is known to be disequal in the logical context. if (m_params.m_lookahead_diseq && next != null_bool_var && phase != l_false && m_context.has_enode(next)) { enode * n = m_context.bool_var2enode(next); if (n->is_eq()) { enode * lhs = n->get_arg(0); enode * rhs = n->get_arg(1); if (m_context.is_ext_diseq(lhs, rhs, 2)) phase = l_false; } } } void display_core(std::ostream & out, ptr_vector & queue, unsigned head, unsigned idx) { if (queue.empty()) return; unsigned sz = queue.size(); for (unsigned i = 0; i < sz; i++) { if (i == head) out << "[HEAD" << idx << "]=> "; out << "#" << queue[i]->get_id() << " "; } out << "\n"; } virtual void display(std::ostream & out) { if (m_queue.empty() && m_queue2.empty()) return; out << "case-splits:\n"; display_core(out, m_queue, m_head, 1); display_core(out, m_queue2, m_head2, 2); } }; /** \brief Case split queue based on relevancy propagation */ class rel_act_case_split_queue : public case_split_queue { struct scope { unsigned m_queue_trail; unsigned m_head_old; }; typedef int_hashtable > bool_var_set; context & m_context; ast_manager & m_manager; smt_params &m_params; ptr_vector m_queue; unsigned m_head; int m_bs_num_bool_vars; //!< Number of boolean variable before starting to search. bool_var_act_queue m_delayed_queue; svector m_scopes; public: rel_act_case_split_queue(context & ctx, smt_params & p): m_context(ctx), m_manager(ctx.get_manager()), m_params(p), m_head(0), m_bs_num_bool_vars(UINT_MAX), m_delayed_queue(1024, bool_var_act_lt(ctx.get_activity_vector())) { } virtual void activity_increased_eh(bool_var v) {} virtual void mk_var_eh(bool_var v) { if (m_context.is_searching()) { SASSERT(v >= m_bs_num_bool_vars); m_delayed_queue.reserve(v+1); m_delayed_queue.insert(v); } } virtual void del_var_eh(bool_var v) { if (v >= m_bs_num_bool_vars && m_delayed_queue.contains(v)) m_delayed_queue.erase(v); } virtual void unassign_var_eh(bool_var v) { if (v < m_bs_num_bool_vars) return; if (!m_delayed_queue.contains(v)) m_delayed_queue.insert(v); } virtual void relevant_eh(expr * n) { if (!m_manager.is_bool(n)) return; bool is_or = m_manager.is_or(n); bool intern = m_context.b_internalized(n); if (!intern && !is_or) return; bool_var var = null_bool_var; if (intern) { var = m_context.get_bool_var(n); SASSERT(var != null_bool_var); bool is_and = m_manager.is_and(n); lbool val = m_context.get_assignment(var); if (!( val == l_undef || // n was not assigned yet (is_or && val == l_true) || // need to justify a child (is_and && val == l_false) // need to justify a child )) return; } if (!intern) { if (!m_context.is_searching()) m_queue.push_back(n); return; } if (var < m_bs_num_bool_vars) m_queue.push_back(n); } virtual void init_search_eh() { m_bs_num_bool_vars = m_context.get_num_bool_vars(); } virtual void end_search_eh() { m_bs_num_bool_vars = UINT_MAX; } virtual void reset() { m_queue.reset(); m_head = 0; m_delayed_queue.reset(); m_scopes.reset(); } virtual void push_scope() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_queue_trail = m_queue.size(); s.m_head_old = m_head; TRACE("case_split", tout << "head: " << m_head << "\n";); } virtual void pop_scope(unsigned num_scopes) { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; m_queue.shrink(s.m_queue_trail); m_head = s.m_head_old; m_scopes.shrink(new_lvl); SASSERT(m_head <= m_queue.size()); TRACE("case_split", display(tout); tout << "head: " << m_head << "\n";); } void next_case_split_core(bool_var & next, lbool & phase) { phase = l_undef; unsigned sz = m_queue.size(); for (; m_head < sz; m_head++) { expr * curr = m_queue[m_head]; bool is_or = m_manager.is_or(curr); bool is_and = m_manager.is_and(curr); bool intern = m_context.b_internalized(curr); SASSERT(intern || is_or); lbool val = l_undef; if (intern) { next = m_context.get_bool_var(curr); val = m_context.get_assignment(next); } else { SASSERT(is_or); // top level clause val = l_true; } if ((is_or && val == l_true) || (is_and && val == l_false)) { expr * undef_child = 0; if (!has_child_assigned_to(m_context, to_app(curr), val, undef_child, m_params.m_rel_case_split_order)) { TRACE("case_split", tout << "found AND/OR candidate: #" << curr->get_id() << " #" << undef_child->get_id() << "\n";); literal l = m_context.get_literal(undef_child); next = l.var(); phase = l.sign() ? l_false : l_true; TRACE("case_split", display(tout);); return; } } else if (val == l_undef) { SASSERT(intern && m_context.get_bool_var(curr) == next); TRACE("case_split", tout << "found candidate: #" << curr->get_id() << "\n";); phase = l_undef; TRACE("case_split", display(tout);); return; } } next = null_bool_var; } virtual void next_case_split(bool_var & next, lbool & phase) { if (m_context.get_random_value() < static_cast(0.02 * random_gen::max_value())) { next = m_context.get_random_value() % m_context.get_num_b_internalized(); TRACE("random_split", tout << "next: " << next << " get_assignment(next): " << m_context.get_assignment(next) << "\n";); if (m_context.get_assignment(next) == l_undef) return; } next_case_split_core(next, phase); if (next != null_bool_var) return; phase = l_undef; while (!m_delayed_queue.empty()) { next = m_delayed_queue.erase_min(); if (m_context.get_assignment(next) == l_undef) return; } next = null_bool_var; } void display_core(std::ostream & out) { if (m_queue.empty()) return; unsigned sz = m_queue.size(); for (unsigned i = 0; i < sz; i++) { if (i == m_head) out << "[HEAD]=> "; out << "#" << m_queue[i]->get_id() << " "; } out << "\n"; } virtual void display(std::ostream & out) { if (m_queue.empty()) return; out << "case-splits:\n"; display_core(out); } }; /** \brief Case split queue based on relevancy propagation and generation/goal-similarity */ class rel_goal_case_split_queue : public case_split_queue { #if 0 #define GOAL_START() m_goal_time.start() #define GOAL_STOP() m_goal_time.stop() #else #define GOAL_START() do {} while (0) #define GOAL_STOP() do {} while (0) #endif struct queue_entry { expr * m_expr; unsigned m_generation; int m_last_decided; queue_entry(expr * e, unsigned gen): m_expr(e), m_generation(gen), m_last_decided(-1) {} }; struct generation_lt { rel_goal_case_split_queue & m_parent; generation_lt(rel_goal_case_split_queue & p):m_parent(p) {} bool operator()(int v1, int v2) const { unsigned g1 = m_parent.m_queue2[v1].m_generation; unsigned g2 = m_parent.m_queue2[v2].m_generation; if (g1 == g2) return v1 < v2; else return g1 < g2; } }; struct scope { unsigned m_queue_trail; unsigned m_head_old; unsigned m_queue2_trail; unsigned m_generation; expr * m_goal; }; typedef int_hashtable > bool_var_set; context & m_context; smt_params & m_params; ast_manager & m_manager; ptr_vector m_queue; unsigned m_head; int m_bs_num_bool_vars; //!< Number of boolean variable before starting to search. svector m_queue2; svector m_scopes; unsigned m_current_generation; // The heap holds indices into m_queue2, i in m_priority_queue2 <==> m_queue2[i].m_last_assigned == -1 heap m_priority_queue2; expr * m_current_goal; stopwatch m_goal_time; static const unsigned start_gen = 0; static const unsigned goal_gen_decrement = 100; static const unsigned stop_gen = goal_gen_decrement + 1; public: rel_goal_case_split_queue(context & ctx, smt_params & p): m_context(ctx), m_params(p), m_manager(ctx.get_manager()), m_head(0), m_bs_num_bool_vars(UINT_MAX), m_priority_queue2(0, generation_lt(*this)), m_current_goal(0) { set_global_generation(); } virtual void activity_increased_eh(bool_var v) {} virtual void mk_var_eh(bool_var v) {} virtual void del_var_eh(bool_var v) {} virtual void unassign_var_eh(bool_var v) {} virtual void relevant_eh(expr * n) { if (get_generation(n) == 0 && m_current_generation != 0) set_generation_rec(n, m_current_generation); if (!m_manager.is_bool(n)) return; bool is_or = m_manager.is_or(n); bool intern = m_context.b_internalized(n); if (!intern && !is_or) return; bool_var var = null_bool_var; if (intern) { var = m_context.get_bool_var(n); SASSERT(var != null_bool_var); bool is_and = m_manager.is_and(n); lbool val = m_context.get_assignment(var); if (!( val == l_undef || // n was not assigned yet (is_or && val == l_true) || // need to justify a child (is_and && val == l_false) // need to justify a child )) return; } if (!intern && m_context.is_searching()) { SASSERT(is_or); add_to_queue2(n); return; } if (var < m_bs_num_bool_vars) m_queue.push_back(n); else add_to_queue2(n); } virtual void internalize_instance_eh(expr * e, unsigned gen) { //lower_generation(e, gen); } virtual void init_search_eh() { m_bs_num_bool_vars = m_context.get_num_bool_vars(); set_global_generation(); } virtual void end_search_eh() { m_bs_num_bool_vars = UINT_MAX; } virtual void reset() { m_queue.reset(); m_head = 0; m_queue2.reset(); m_scopes.reset(); m_priority_queue2.reset(); set_global_generation(); } virtual void push_scope() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_queue_trail = m_queue.size(); s.m_head_old = m_head; s.m_queue2_trail = m_queue2.size(); s.m_generation = m_current_generation; s.m_goal = m_current_goal; TRACE("case_split", tout << "head: " << m_head << "\n";); } virtual void pop_scope(unsigned num_scopes) { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; m_queue.shrink(s.m_queue_trail); m_head = s.m_head_old; m_current_generation = s.m_generation; m_current_goal = s.m_goal; for (unsigned i = s.m_queue2_trail; i < m_queue2.size(); i++) { //TRACE("case_split", tout << "ld[" << i << "] = " << m_queue2[i].m_last_decided << " cont " << SASSERT((m_queue2[i].m_last_decided == -1) == m_priority_queue2.contains(i)); if (m_priority_queue2.contains(i)) m_priority_queue2.erase(i); } for (unsigned i = 0; i < s.m_queue2_trail; i++) { queue_entry & e = m_queue2[i]; if (e.m_last_decided > static_cast(new_lvl)) { SASSERT(!m_priority_queue2.contains(i)); // Note that the generation might be reset by the pop, and we keep the heap // ordered by the old generation. It's unlikely to affect performance I think. m_priority_queue2.insert(i); e.m_last_decided = -1; } } m_queue2.shrink(s.m_queue2_trail); m_scopes.shrink(new_lvl); SASSERT(m_head <= m_queue.size()); TRACE("case_split", display(tout); tout << "head: " << m_head << "\n";); } void next_case_split_core(expr * curr, bool_var & next, lbool & phase) { bool is_or = m_manager.is_or(curr); bool is_and = m_manager.is_and(curr); bool intern = m_context.b_internalized(curr); SASSERT(intern || is_or); lbool val = l_undef; if (intern) { next = m_context.get_bool_var(curr); val = m_context.get_assignment(next); } else { SASSERT(is_or); // top level clause val = l_true; } if ((is_or && val == l_true) || (is_and && val == l_false)) { expr * undef_child = 0; if (!has_child_assigned_to(m_context, to_app(curr), val, undef_child, m_params.m_rel_case_split_order)) { if (m_manager.has_trace_stream()) { m_manager.trace_stream() << "[decide-and-or] #" << curr->get_id() << " #" << undef_child->get_id() << "\n"; } TRACE("case_split", tout << "found AND/OR candidate: #" << curr->get_id() << " #" << undef_child->get_id() << "\n";); literal l = m_context.get_literal(undef_child); next = l.var(); phase = l.sign() ? l_false : l_true; TRACE("case_split", display(tout);); return; } } else if (val == l_undef) { SASSERT(intern && m_context.get_bool_var(curr) == next); TRACE("case_split", tout << "found candidate: #" << curr->get_id() << "\n";); phase = l_undef; TRACE("case_split", display(tout);); return; } next = null_bool_var; } virtual void next_case_split(bool_var & next, lbool & phase) { phase = l_undef; next = null_bool_var; unsigned sz = m_queue.size(); for (; m_head < sz; m_head++) { expr * curr = m_queue[m_head]; next_case_split_core(curr, next, phase); if (next != null_bool_var) return; } while (!m_priority_queue2.empty()) { unsigned idx = static_cast(m_priority_queue2.erase_min()); TRACE("case_split", tout << "q " << m_queue2.size() << " idx " << idx << "\n"; ); SASSERT(idx < m_queue2.size()); queue_entry & e = m_queue2[idx]; SASSERT(e.m_last_decided == -1); e.m_last_decided = m_scopes.size(); next_case_split_core(e.m_expr, next, phase); if (next != null_bool_var) { // Push the last guy back in; the other queue doesn't increment // the m_head in case of return and the code in decide() actually // does the push after calling us m_priority_queue2.insert(idx); e.m_last_decided = -1; return; } } } void display_core(std::ostream & out, ptr_vector & queue, unsigned head, unsigned idx) { if (queue.empty()) return; unsigned sz = queue.size(); for (unsigned i = 0; i < sz; i++) { if (i == head) out << "[HEAD" << idx << "]=> "; out << "#" << queue[i]->get_id() << " "; } out << "\n"; } virtual void display(std::ostream & out) { if (m_queue.empty() && m_queue2.empty()) return; out << "case-splits:\n"; display_core(out, m_queue, m_head, 1); //display_core(out, m_queue2, m_head2, 2); } virtual void assign_lit_eh(literal l) { // if (m_current_generation > stop_gen) // m_current_generation--; expr * e = m_context.bool_var2expr(l.var()); if (e == m_current_goal) return; bool sign = l.sign(); if ( ((m_manager.is_and(e) && !sign) || (m_manager.is_or(e) && sign)) && to_app(e)->get_num_args() == 2) { expr * lablit = to_app(e)->get_arg(1); if (m_manager.is_not(lablit)) { sign = !sign; lablit = to_app(lablit)->get_arg(0); } if (sign) return; if (!m_manager.is_label_lit(lablit)) return; TRACE("case_split", tout << "Found goal\n" << mk_pp(e, m_manager) << "\n"; ); set_goal(e); } } private: unsigned get_generation(expr * e) { unsigned maxgen = 0; unsigned mingen = (unsigned)-1; ptr_vector stack; stack.push_back(e); while (!stack.empty()) { unsigned gen; expr * curr; curr = stack.back(); stack.pop_back(); if (m_context.e_internalized(curr)) { gen = m_context.get_enode(curr)->get_generation(); if (gen > maxgen) maxgen = gen; if (gen < mingen) mingen = gen; } else if (is_app(curr)) { app * a = to_app(curr); for (unsigned i = 0; i < a->get_num_args(); ++i) stack.push_back(a->get_arg(i)); } } return maxgen; } void add_to_queue2(expr * e) { int idx = m_queue2.size(); GOAL_START(); m_queue2.push_back(queue_entry(e, get_generation(e))); m_priority_queue2.reserve(idx+1); m_priority_queue2.insert(idx); GOAL_STOP(); } struct set_generation_fn { context & m_context; unsigned m_generation; set_generation_fn(context & ctx, unsigned gen) : m_context(ctx), m_generation(gen) { } void operator()(expr * e) { if (m_context.e_internalized(e)) { enode * n = m_context.get_enode(e); n->set_generation(m_context, m_generation); } } }; void set_generation_rec(expr * e, unsigned gen) { set_generation_fn proc(m_context, gen); for_each_expr(proc, e); } void lower_generation(expr * e, unsigned gen) { ptr_vector stack; stack.push_back(e); while (!stack.empty()) { expr * curr; curr = stack.back(); stack.pop_back(); if (m_context.e_internalized(curr)) { unsigned curr_gen = m_context.get_enode(curr)->get_generation(); if (curr_gen > gen) { // Lower it. set_generation_rec(e, gen); continue; // Don't add children. } else if (curr_gen < gen) { // All the children will be lower as well, don't add them. continue; } } if (is_app(curr)) { app * a = to_app(curr); for (unsigned i = 0; i < a->get_num_args(); ++i) stack.push_back(a->get_arg(i)); } } } void set_goal(expr * e) { if (e == m_current_goal) return; GOAL_START(); m_current_goal = e; #if 1 if (m_current_generation >= goal_gen_decrement) { set_generation_rec(m_current_goal, m_current_generation - goal_gen_decrement); /* m_priority_queue2.reset(); m_priority_queue2.reserve(m_queue2.size()); for (unsigned i = 0; i < m_queue2.size(); ++i) { queue_entry & e = m_queue2[i]; e.m_generation = get_generation(e.m_expr); if (e.m_last_decided == -1) m_priority_queue2.insert(i); } */ } #endif GOAL_STOP(); //std::cout << "goal set, time " << m_goal_time.get_seconds() << "\n"; } void set_global_generation() { m_current_generation = start_gen; m_context.set_global_generation(start_gen); if (m_params.m_qi_eager_threshold < start_gen) m_params.m_qi_eager_threshold += start_gen; } }; case_split_queue * mk_case_split_queue(context & ctx, smt_params & p) { if (p.m_relevancy_lvl < 2 && (p.m_case_split_strategy == CS_RELEVANCY || p.m_case_split_strategy == CS_RELEVANCY_ACTIVITY || p.m_case_split_strategy == CS_RELEVANCY_GOAL)) { warning_msg("relevancy must be enabled to use option CASE_SPLIT=3, 4 or 5"); p.m_case_split_strategy = CS_ACTIVITY; } if (p.m_auto_config && (p.m_case_split_strategy == CS_RELEVANCY || p.m_case_split_strategy == CS_RELEVANCY_ACTIVITY || p.m_case_split_strategy == CS_RELEVANCY_GOAL)) { warning_msg("auto configuration (option AUTO_CONFIG) must be disabled to use option CASE_SPLIT=3, 4 or 5"); p.m_case_split_strategy = CS_ACTIVITY; } switch (p.m_case_split_strategy) { case CS_ACTIVITY_DELAY_NEW: return alloc(dact_case_split_queue, ctx, p); case CS_ACTIVITY_WITH_CACHE: return alloc(cact_case_split_queue, ctx, p); case CS_RELEVANCY: return alloc(rel_case_split_queue, ctx, p); case CS_RELEVANCY_ACTIVITY: return alloc(rel_act_case_split_queue, ctx, p); case CS_RELEVANCY_GOAL: return alloc(rel_goal_case_split_queue, ctx, p); default: return alloc(act_case_split_queue, ctx, p); } } }; z3-z3-4.4.1/src/smt/smt_case_split_queue.h000066400000000000000000000024771260446376700204170ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_case_split_queue.h Abstract: Author: Leonardo de Moura (leonardo) 2009-01-20. Revision History: --*/ #ifndef SMT_CASE_SPLIT_QUEUE_H_ #define SMT_CASE_SPLIT_QUEUE_H_ #include"smt_types.h" #include"heap.h" #include"smt_params.h" namespace smt { class context; /** \brief Abstract case split queue. */ class case_split_queue { public: virtual void activity_increased_eh(bool_var v) = 0; virtual void mk_var_eh(bool_var v) = 0; virtual void del_var_eh(bool_var v) = 0; virtual void assign_lit_eh(literal l) {} virtual void unassign_var_eh(bool_var v) = 0; virtual void relevant_eh(expr * n) = 0; virtual void init_search_eh() = 0; virtual void end_search_eh() = 0; virtual void internalize_instance_eh(expr * e, unsigned gen) {} virtual void reset() = 0; virtual void push_scope() = 0; virtual void pop_scope(unsigned num_scopes) = 0; virtual void next_case_split(bool_var & next, lbool & phase) = 0; virtual void display(std::ostream & out) = 0; virtual ~case_split_queue() {} }; case_split_queue * mk_case_split_queue(context & ctx, smt_params & p); }; #endif /* SMT_CASE_SPLIT_QUEUE_H_ */ z3-z3-4.4.1/src/smt/smt_cg_table.cpp000066400000000000000000000162461260446376700171570ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_cg_table.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #include"smt_cg_table.h" #include"ast_pp.h" #include"ast_ll_pp.h" namespace smt { #if 0 unsigned cg_table::cg_hash::operator()(enode * n) const { if (n->is_commutative()) { return combine_hash(n->get_decl_id(), n->get_arg(0)->get_root()->hash() * n->get_arg(1)->get_root()->hash()); } else { unsigned num = n->get_num_args(); switch (num) { case 0: UNREACHABLE(); return 0; case 1: return combine_hash(n->get_decl_id(), n->get_arg(0)->get_root()->hash()); case 2: { unsigned a = n->get_decl_id(); unsigned b = n->get_arg(0)->get_root()->hash(); unsigned c = n->get_arg(1)->get_root()->hash(); mix(a,b,c); return c; } default: return get_composite_hash(n, n->get_num_args(), m_khasher, m_chasher); } } } bool cg_table::cg_eq::operator()(enode * n1, enode * n2) const { #if 0 static unsigned counter = 0; static unsigned failed = 0; bool r = congruent(n1, n2, m_commutativity); if (!r) failed++; counter++; if (counter % 100000 == 0) std::cout << "cg_eq: " << counter << " " << failed << "\n"; return r; #else return congruent(n1, n2, m_commutativity); #endif } cg_table::cg_table(ast_manager & m): m_manager(m), m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, cg_hash(), cg_eq(m_commutativity)) { } void cg_table::display(std::ostream & out) const { out << "congruence table:\n"; table::iterator it = m_table.begin(); table::iterator end = m_table.end(); for (; it != end; ++it) { enode * n = *it; out << mk_pp(n->get_owner(), m_manager) << "\n"; } } void cg_table::display_compact(std::ostream & out) const { if (!m_table.empty()) { out << "congruence table:\n"; table::iterator it = m_table.begin(); table::iterator end = m_table.end(); for (; it != end; ++it) { enode * n = *it; out << "#" << n->get_owner()->get_id() << " "; } out << "\n"; } } #ifdef Z3DEBUG bool cg_table::check_invariant() const { table::iterator it = m_table.begin(); table::iterator end = m_table.end(); for (; it != end; ++it) { enode * n = *it; CTRACE("cg_table", !contains_ptr(n), tout << "#" << n->get_owner_id() << "\n";); SASSERT(contains_ptr(n)); } return true; } #endif #else // one table per func_decl implementation unsigned cg_table::cg_hash::operator()(enode * n) const { SASSERT(n->get_decl()->is_flat_associative() || n->get_num_args() >= 3); unsigned a, b, c; a = b = 0x9e3779b9; c = 11; unsigned i = n->get_num_args(); while (i >= 3) { i--; a += n->get_arg(i)->get_root()->hash(); i--; b += n->get_arg(i)->get_root()->hash(); i--; c += n->get_arg(i)->get_root()->hash(); mix(a, b, c); } switch (i) { case 2: b += n->get_arg(1)->get_root()->hash(); __fallthrough; case 1: c += n->get_arg(0)->get_root()->hash(); } mix(a, b, c); return c; } bool cg_table::cg_eq::operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == n2->get_num_args()); SASSERT(n1->get_decl() == n2->get_decl()); unsigned num = n1->get_num_args(); for (unsigned i = 0; i < num; i++) if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) return false; return true; } cg_table::cg_table(ast_manager & m): m_manager(m) { } cg_table::~cg_table() { reset(); } void * cg_table::mk_table_for(func_decl * d) { void * r; SASSERT(d->get_arity() >= 1); switch (d->get_arity()) { case 1: r = TAG(void*, alloc(unary_table), UNARY); SASSERT(GET_TAG(r) == UNARY); return r; case 2: if (d->is_flat_associative()) { // applications of declarations that are flat-assoc (e.g., +) may have many arguments. r = TAG(void*, alloc(table), NARY); SASSERT(GET_TAG(r) == NARY); return r; } else if (d->is_commutative()) { r = TAG(void*, alloc(comm_table, cg_comm_hash(), cg_comm_eq(m_commutativity)), BINARY_COMM); SASSERT(GET_TAG(r) == BINARY_COMM); return r; } else { r = TAG(void*, alloc(binary_table), BINARY); SASSERT(GET_TAG(r) == BINARY); return r; } default: r = TAG(void*, alloc(table), NARY); SASSERT(GET_TAG(r) == NARY); return r; } } unsigned cg_table::set_func_decl_id(enode * n) { func_decl * f = n->get_decl(); unsigned tid; if (!m_func_decl2id.find(f, tid)) { tid = m_tables.size(); m_func_decl2id.insert(f, tid); m_manager.inc_ref(f); SASSERT(tid <= m_tables.size()); m_tables.push_back(mk_table_for(f)); } SASSERT(tid < m_tables.size()); n->set_func_decl_id(tid); DEBUG_CODE({ unsigned tid_prime; SASSERT(m_func_decl2id.find(n->get_decl(), tid_prime) && tid == tid_prime); }); return tid; } void cg_table::reset() { ptr_vector::iterator it = m_tables.begin(); ptr_vector::iterator end = m_tables.end(); for (; it != end; ++it) { void * t = *it; switch (GET_TAG(t)) { case UNARY: dealloc(UNTAG(unary_table*, t)); break; case BINARY: dealloc(UNTAG(binary_table*, t)); break; case BINARY_COMM: dealloc(UNTAG(comm_table*, t)); break; case NARY: dealloc(UNTAG(table*, t)); break; } } m_tables.reset(); obj_map::iterator it2 = m_func_decl2id.begin(); obj_map::iterator end2 = m_func_decl2id.end(); for (; it2 != end2; ++it2) m_manager.dec_ref(it2->m_key); m_func_decl2id.reset(); } void cg_table::display(std::ostream & out) const { } void cg_table::display_compact(std::ostream & out) const { } #ifdef Z3DEBUG bool cg_table::check_invariant() const { return true; } #endif #endif }; z3-z3-4.4.1/src/smt/smt_cg_table.h000066400000000000000000000263321260446376700166210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_cg_table.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef SMT_CG_TABLE_H_ #define SMT_CG_TABLE_H_ #include"smt_enode.h" #include"hashtable.h" #include"chashtable.h" namespace smt { typedef std::pair enode_bool_pair; #if 0 /** \brief Congruence table. */ class cg_table { struct cg_khasher { unsigned operator()(enode const * n) const { return n->get_decl_id(); } }; struct cg_chasher { unsigned operator()(enode const * n, unsigned idx) const { return n->get_arg(idx)->get_root()->hash(); } }; struct cg_hash { cg_khasher m_khasher; cg_chasher m_chasher; public: unsigned operator()(enode * n) const; }; struct cg_eq { bool & m_commutativity; cg_eq(bool & comm):m_commutativity(comm) {} bool operator()(enode * n1, enode * n2) const; }; typedef ptr_hashtable table; ast_manager & m_manager; bool m_commutativity; //!< true if the last found congruence used commutativity table m_table; public: cg_table(ast_manager & m); /** \brief Try to insert n into the table. If the table already contains an element n' congruent to n, then do nothing and return n' and a boolean indicating whether n and n' are congruence modulo commutativity, otherwise insert n and return (n,false). */ enode_bool_pair insert(enode * n) { // it doesn't make sense to insert a constant. SASSERT(n->get_num_args() > 0); m_commutativity = false; enode * n_prime = m_table.insert_if_not_there(n); SASSERT(contains(n)); return enode_bool_pair(n_prime, m_commutativity); } void erase(enode * n) { SASSERT(n->get_num_args() > 0); m_table.erase(n); SASSERT(!contains(n)); } bool contains(enode * n) const { return m_table.contains(n); } enode * find(enode * n) const { enode * r = 0; return m_table.find(n, r) ? r : 0; } bool contains_ptr(enode * n) const { enode * n_prime; return m_table.find(n, n_prime) && n == n_prime; } void reset() { m_table.reset(); } void display(std::ostream & out) const; void display_compact(std::ostream & out) const; #ifdef Z3DEBUG bool check_invariant() const; #endif }; #else // one table per function symbol /** \brief Congruence table. */ class cg_table { struct cg_unary_hash { unsigned operator()(enode * n) const { SASSERT(n->get_num_args() == 1); return n->get_arg(0)->get_root()->hash(); } }; struct cg_unary_eq { bool operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == 1); SASSERT(n2->get_num_args() == 1); SASSERT(n1->get_decl() == n2->get_decl()); return n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root(); } }; typedef chashtable unary_table; struct cg_binary_hash { unsigned operator()(enode * n) const { SASSERT(n->get_num_args() == 2); // too many collisions // unsigned r = 17 + n->get_arg(0)->get_root()->hash(); // return r * 31 + n->get_arg(1)->get_root()->hash(); return combine_hash(n->get_arg(0)->get_root()->hash(), n->get_arg(1)->get_root()->hash()); } }; struct cg_binary_eq { bool operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == 2); SASSERT(n2->get_num_args() == 2); SASSERT(n1->get_decl() == n2->get_decl()); #if 1 return n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root() && n1->get_arg(1)->get_root() == n2->get_arg(1)->get_root(); #else bool r = n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root() && n1->get_arg(1)->get_root() == n2->get_arg(1)->get_root(); static unsigned counter = 0; static unsigned failed = 0; if (!r) failed++; counter++; if (counter % 100000 == 0) std::cerr << "[cg_eq] " << counter << " " << failed << "\n"; return r; #endif } }; typedef chashtable binary_table; struct cg_comm_hash { unsigned operator()(enode * n) const { SASSERT(n->get_num_args() == 2); unsigned h1 = n->get_arg(0)->get_root()->hash(); unsigned h2 = n->get_arg(1)->get_root()->hash(); if (h1 > h2) std::swap(h1, h2); return hash_u((h1 << 16) | (h2 & 0xFFFF)); } }; struct cg_comm_eq { bool & m_commutativity; cg_comm_eq(bool & c):m_commutativity(c) {} bool operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == 2); SASSERT(n2->get_num_args() == 2); SASSERT(n1->get_decl() == n2->get_decl()); enode * c1_1 = n1->get_arg(0)->get_root(); enode * c1_2 = n1->get_arg(1)->get_root(); enode * c2_1 = n2->get_arg(0)->get_root(); enode * c2_2 = n2->get_arg(1)->get_root(); if (c1_1 == c2_1 && c1_2 == c2_2) { return true; } if (c1_1 == c2_2 && c1_2 == c2_1) { m_commutativity = true; return true; } return false; } }; typedef chashtable comm_table; struct cg_hash { unsigned operator()(enode * n) const; }; struct cg_eq { bool operator()(enode * n1, enode * n2) const; }; typedef chashtable table; ast_manager & m_manager; bool m_commutativity; //!< true if the last found congruence used commutativity ptr_vector m_tables; obj_map m_func_decl2id; enum table_kind { UNARY, BINARY, BINARY_COMM, NARY }; void * mk_table_for(func_decl * d); unsigned set_func_decl_id(enode * n); void * get_table(enode * n) { unsigned tid = n->get_func_decl_id(); if (tid == UINT_MAX) tid = set_func_decl_id(n); SASSERT(tid < m_tables.size()); return m_tables[tid]; } public: cg_table(ast_manager & m); ~cg_table(); /** \brief Try to insert n into the table. If the table already contains an element n' congruent to n, then do nothing and return n' and a boolean indicating whether n and n' are congruence modulo commutativity, otherwise insert n and return (n,false). */ enode_bool_pair insert(enode * n) { // it doesn't make sense to insert a constant. SASSERT(n->get_num_args() > 0); enode * n_prime; void * t = get_table(n); switch (static_cast(GET_TAG(t))) { case UNARY: n_prime = UNTAG(unary_table*, t)->insert_if_not_there(n); return enode_bool_pair(n_prime, false); case BINARY: n_prime = UNTAG(binary_table*, t)->insert_if_not_there(n); return enode_bool_pair(n_prime, false); case BINARY_COMM: m_commutativity = false; n_prime = UNTAG(comm_table*, t)->insert_if_not_there(n); return enode_bool_pair(n_prime, m_commutativity); default: n_prime = UNTAG(table*, t)->insert_if_not_there(n); return enode_bool_pair(n_prime, false); } } void erase(enode * n) { SASSERT(n->get_num_args() > 0); void * t = get_table(n); switch (static_cast(GET_TAG(t))) { case UNARY: UNTAG(unary_table*, t)->erase(n); break; case BINARY: UNTAG(binary_table*, t)->erase(n); break; case BINARY_COMM: UNTAG(comm_table*, t)->erase(n); break; default: UNTAG(table*, t)->erase(n); break; } } bool contains(enode * n) const { SASSERT(n->get_num_args() > 0); void * t = const_cast(this)->get_table(n); switch (static_cast(GET_TAG(t))) { case UNARY: return UNTAG(unary_table*, t)->contains(n); case BINARY: return UNTAG(binary_table*, t)->contains(n); case BINARY_COMM: return UNTAG(comm_table*, t)->contains(n); default: return UNTAG(table*, t)->contains(n); } } enode * find(enode * n) const { SASSERT(n->get_num_args() > 0); enode * r = 0; void * t = const_cast(this)->get_table(n); switch (static_cast(GET_TAG(t))) { case UNARY: return UNTAG(unary_table*, t)->find(n, r) ? r : 0; case BINARY: return UNTAG(binary_table*, t)->find(n, r) ? r : 0; case BINARY_COMM: return UNTAG(comm_table*, t)->find(n, r) ? r : 0; default: return UNTAG(table*, t)->find(n, r) ? r : 0; } } bool contains_ptr(enode * n) const { enode * r; SASSERT(n->get_num_args() > 0); void * t = const_cast(this)->get_table(n); switch (static_cast(GET_TAG(t))) { case UNARY: return UNTAG(unary_table*, t)->find(n, r) && n == r; case BINARY: return UNTAG(binary_table*, t)->find(n, r) && n == r; case BINARY_COMM: return UNTAG(comm_table*, t)->find(n, r) && n == r; default: return UNTAG(table*, t)->find(n, r) && n == r; } } void reset(); void display(std::ostream & out) const; void display_compact(std::ostream & out) const; #ifdef Z3DEBUG bool check_invariant() const; #endif }; #endif }; #endif /* SMT_CG_TABLE_H_ */ z3-z3-4.4.1/src/smt/smt_checker.cpp000066400000000000000000000140701260446376700170140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_checker.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-20. Revision History: --*/ #include"smt_context.h" #include"smt_checker.h" #include"ast_ll_pp.h" namespace smt { bool checker::all_args(app * a, bool is_true) { unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (!check(a->get_arg(i), is_true)) return false; } return true; } bool checker::any_arg(app * a, bool is_true) { unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (check(a->get_arg(i), is_true)) return true; } return false; } bool checker::check_core(expr * n, bool is_true) { SASSERT(m_manager.is_bool(n)); if (m_context.b_internalized(n) && m_context.is_relevant(n)) { lbool val = m_context.get_assignment(n); return val != l_undef && is_true == (val == l_true); } if (!is_app(n)) return false; app * a = to_app(n); if (a->get_family_id() == m_manager.get_basic_family_id()) { switch (a->get_decl_kind()) { case OP_TRUE: return is_true; case OP_FALSE: return !is_true; case OP_NOT: return check(a->get_arg(0), !is_true); case OP_OR: return is_true ? any_arg(a, true) : all_args(a, false); case OP_AND: return is_true ? all_args(a, true) : any_arg(a, false); case OP_IFF: if (is_true) { return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || (check(a->get_arg(0), false) && check(a->get_arg(1), false)); } else { return (check(a->get_arg(0), true) && check(a->get_arg(1), false)) || (check(a->get_arg(0), false) && check(a->get_arg(1), true)); } case OP_ITE: { if (m_context.lit_internalized(a->get_arg(0)) && m_context.is_relevant(a->get_arg(0))) { switch (m_context.get_assignment(a->get_arg(0))) { case l_false: return check(a->get_arg(2), is_true); case l_undef: return false; case l_true: return check(a->get_arg(1), is_true); } } return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true); } case OP_EQ: { enode * lhs = get_enode_eq_to(a->get_arg(0)); enode * rhs = get_enode_eq_to(a->get_arg(1)); if (lhs && rhs && m_context.is_relevant(lhs) && m_context.is_relevant(rhs)) { if (is_true && lhs->get_root() == rhs->get_root()) return true; // if (!is_true && m_context.is_ext_diseq(lhs, rhs, 2)) if (!is_true && m_context.is_diseq(lhs, rhs)) return true; } return false; } default: break; } } enode * e = get_enode_eq_to(a); if (e && e->is_bool() && m_context.is_relevant(e)) { lbool val = m_context.get_assignment(e->get_owner()); return val != l_undef && is_true == (val == l_true); } return false; } bool checker::check(expr * n, bool is_true) { bool r; if (n->get_ref_count() > 1 && m_is_true_cache[is_true].find(n, r)) return r; r = check_core(n, is_true); if (n->get_ref_count() > 1) m_is_true_cache[is_true].insert(n, r); return r; } enode * checker::get_enode_eq_to_core(app * n) { ptr_buffer buffer; unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { enode * arg = get_enode_eq_to(n->get_arg(i)); if (arg == 0) return 0; buffer.push_back(arg); } enode * e = m_context.get_enode_eq_to(n->get_decl(), num, buffer.c_ptr()); if (e == 0) return 0; return m_context.is_relevant(e) ? e : 0; } enode * checker::get_enode_eq_to(expr * n) { if (is_var(n)) { unsigned idx = to_var(n)->get_idx(); if (idx >= m_num_bindings) return 0; return m_bindings[m_num_bindings - idx - 1]; } if (m_context.e_internalized(n) && m_context.is_relevant(n)) return m_context.get_enode(n); if (!is_app(n) || to_app(n)->get_num_args() == 0) return 0; enode * r = 0; if (n->get_ref_count() > 1 && m_to_enode_cache.find(n, r)) return r; r = get_enode_eq_to_core(to_app(n)); if (n->get_ref_count() > 1) m_to_enode_cache.insert(n, r); return r; } bool checker::is_sat(expr * n, unsigned num_bindings, enode * const * bindings) { flet l1(m_num_bindings, num_bindings); flet l2(m_bindings, bindings); bool r = check(n, true); m_is_true_cache[0].reset(); m_is_true_cache[1].reset(); m_to_enode_cache.reset(); return r; } bool checker::is_unsat(expr * n, unsigned num_bindings, enode * const * bindings) { flet l1(m_num_bindings, num_bindings); flet l2(m_bindings, bindings); bool r = check(n, false); m_is_true_cache[0].reset(); m_is_true_cache[1].reset(); m_to_enode_cache.reset(); return r; } checker::checker(context & c): m_context(c), m_manager(c.get_manager()), m_num_bindings(0), m_bindings(0) { } }; z3-z3-4.4.1/src/smt/smt_checker.h000066400000000000000000000023371260446376700164640ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_checker.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-20. Revision History: --*/ #ifndef SMT_CHECKER_H_ #define SMT_CHECKER_H_ #include"ast.h" #include"obj_hashtable.h" namespace smt { class context; class checker { typedef obj_map expr2bool; typedef obj_map expr2enode; context & m_context; ast_manager & m_manager; expr2bool m_is_true_cache[2]; expr2enode m_to_enode_cache; unsigned m_num_bindings; enode * const * m_bindings; bool all_args(app * a, bool is_true); bool any_arg(app * a, bool is_true); bool check_core(expr * n, bool is_true); bool check(expr * n, bool is_true); enode * get_enode_eq_to_core(app * n); enode * get_enode_eq_to(expr * n); public: checker(context & c); bool is_sat(expr * n, unsigned num_bindings = 0, enode * const * bindings = 0); bool is_unsat(expr * n, unsigned num_bindings = 0, enode * const * bindings = 0); }; }; #endif /* SMT_CHECKER_H_ */ z3-z3-4.4.1/src/smt/smt_clause.cpp000066400000000000000000000100131260446376700166550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_clause.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #include"smt_clause.h" #include"smt_justification.h" #include"ast_ll_pp.h" namespace smt { /** \brief Create a new clause. bool_var2expr_map is a mapping from bool_var -> expr, it is only used if save_atoms == true. */ clause * clause::mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js, clause_del_eh * del_eh, bool save_atoms, expr * const * bool_var2expr_map) { SASSERT(k == CLS_AUX || js == 0 || !js->in_region()); SASSERT(num_lits >= 2); unsigned sz = get_obj_size(num_lits, k, save_atoms, del_eh != 0, js != 0); void * mem = m.get_allocator().allocate(sz); clause * cls = new (mem) clause(); cls->m_num_literals = num_lits; cls->m_capacity = num_lits; cls->m_kind = k; cls->m_reinit = save_atoms; cls->m_reinternalize_atoms = save_atoms; cls->m_has_atoms = save_atoms; cls->m_has_del_eh = del_eh != 0; cls->m_has_justification = js != 0; cls->m_deleted = false; SASSERT(!m.proofs_enabled() || js != 0); memcpy(cls->m_lits, lits, sizeof(literal) * num_lits); if (cls->is_lemma()) cls->set_activity(1); if (del_eh) *(const_cast(cls->get_del_eh_addr())) = del_eh; if (js) *(const_cast(cls->get_justification_addr())) = js; if (save_atoms) { for (unsigned i = 0; i < num_lits; i++) { expr * atom = bool_var2expr_map[lits[i].var()]; m.inc_ref(atom); const_cast(cls->get_atoms_addr())[i] = TAG(expr*, atom, lits[i].sign()); } } DEBUG_CODE({ SASSERT(!cls->is_lemma() || cls->get_activity() == 1); SASSERT(cls->get_del_eh() == del_eh); SASSERT(cls->get_justification() == js); for (unsigned i = 0; i < num_lits; i++) { SASSERT(cls->get_literal(i) == lits[i]); SASSERT(!save_atoms || cls->get_atom(i) == bool_var2expr_map[lits[i].var()]); }}); return cls; } void clause::deallocate(ast_manager & m) { clause_del_eh * del_eh = get_del_eh(); if (del_eh) (*del_eh)(m, this); if (is_lemma() && m_has_justification) { justification * js = get_justification(); if (js) { SASSERT(!js->in_region()); js->del_eh(m); dealloc(js); } } unsigned num_atoms = get_num_atoms(); for (unsigned i = 0; i < num_atoms; i++) { SASSERT(m_reinit || get_atom(i) == 0); m.dec_ref(get_atom(i)); } m.get_allocator().deallocate(get_obj_size(m_capacity, get_kind(), m_has_atoms, m_has_del_eh, m_has_justification), this); } void clause::release_atoms(ast_manager & m) { unsigned num_atoms = get_num_atoms(); for (unsigned i = 0; i < num_atoms; i++) { m.dec_ref(get_atom(i)); const_cast(get_atoms_addr())[i] = 0; } } void clause::display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const { out << "(clause"; for (unsigned i = 0; i < m_num_literals; i++) { out << " "; m_lits[i].display(out, m, bool_var2expr_map); } out << ")"; } void clause::display_compact(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const { out << "(clause"; for (unsigned i = 0; i < m_num_literals; i++) { out << " "; m_lits[i].display_compact(out, bool_var2expr_map); } out << ")"; } }; z3-z3-4.4.1/src/smt/smt_clause.h000066400000000000000000000205621260446376700163340ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_clause.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #ifndef SMT_CLAUSE_H_ #define SMT_CLAUSE_H_ #include"ast.h" #include"smt_literal.h" #include"tptr.h" #include"obj_hashtable.h" #include"smt_justification.h" namespace smt { class clause; /** \brief Abstract functor: clause deletion event handler. */ class clause_del_eh { public: virtual ~clause_del_eh() {} virtual void operator()(ast_manager & m, clause * cls) = 0; }; enum clause_kind { CLS_AUX, CLS_LEARNED, CLS_AUX_LEMMA }; /** \brief A SMT clause. A clause has several optional fields, I store space for them only if they are actually used. */ class clause { unsigned m_num_literals; unsigned m_capacity:24; //!< some of the clause literals can be simplified and removed, this field contains the original number of literals (used for GC). unsigned m_kind:2; //!< kind unsigned m_reinit:1; //!< true if the clause is in the reinit stack (only for learned clauses and aux_lemmas) unsigned m_reinternalize_atoms:1; //!< true if atoms must be reinitialized during reinitialization unsigned m_has_atoms:1; //!< true if the clause has memory space for storing atoms. unsigned m_has_del_eh:1; //!< true if must notify event handler when deleted. unsigned m_has_justification:1; //!< true if the clause has a justification attached to it. unsigned m_deleted:1; //!< true if the clause is marked for deletion by was not deleted yet because it is referenced by some data-structure (e.g., m_lemmas) literal m_lits[0]; static unsigned get_obj_size(unsigned num_lits, clause_kind k, bool has_atoms, bool has_del_eh, bool has_justification) { unsigned r = sizeof(clause) + sizeof(literal) * num_lits; if (k != CLS_AUX) r += sizeof(unsigned); /* dvitek: Fix alignment issues on 64-bit platforms. The * 'if' statement below probably isn't worthwhile since * I'm guessing the allocator is probably going to round * up internally anyway. */ //if (has_atoms || has_del_eh || has_justification) r = (r + (sizeof(void*)-1)) & ~(sizeof(void*)-1); if (has_atoms) r += sizeof(expr*) * num_lits; if (has_del_eh) r += sizeof(clause_del_eh *); if (has_justification) r += sizeof(justification *); return r; } unsigned const * get_activity_addr() const { return reinterpret_cast(m_lits + m_capacity); } unsigned * get_activity_addr() { return reinterpret_cast(m_lits + m_capacity); } clause_del_eh * const * get_del_eh_addr() const { unsigned const * addr = get_activity_addr(); if (is_lemma()) addr ++; /* dvitek: It would be better to use uintptr_t than * size_t, but we need to wait until c++11 support is * really available. */ size_t as_int = reinterpret_cast(addr); as_int = (as_int + sizeof(void*)-1) & ~static_cast(sizeof(void*)-1); return reinterpret_cast(as_int); } justification * const * get_justification_addr() const { clause_del_eh * const * addr = get_del_eh_addr(); if (m_has_del_eh) addr ++; return reinterpret_cast(addr); } expr * const * get_atoms_addr() const { justification * const * addr = get_justification_addr(); if (m_has_justification) addr ++; return reinterpret_cast(addr); } friend class context; void swap_lits(unsigned idx1, unsigned idx2) { literal tmp = get_literal(idx1); set_literal(idx1, get_literal(idx2)); set_literal(idx2, tmp); } bool is_watch(literal l) const { return m_lits[0] == l || m_lits[1] == l; } void set_literal(unsigned idx, literal l) { m_lits[idx] = l; } void set_num_literals(unsigned n) { SASSERT(n <= m_num_literals); SASSERT(!m_reinit); m_num_literals = n; } void set_justification(justification * new_js) { SASSERT(m_has_justification); SASSERT(!m_reinit); SASSERT(!is_lemma() || new_js == 0 || !new_js->in_region()); justification ** js_addr = const_cast(get_justification_addr()); *js_addr = new_js; } void release_atoms(ast_manager & m); public: static clause * mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js = 0, clause_del_eh * del_eh = 0, bool save_atoms = false, expr * const * bool_var2expr_map = 0); void deallocate(ast_manager & m); clause_kind get_kind() const { return static_cast(m_kind); } bool is_lemma() const { return get_kind() != CLS_AUX; } bool is_learned() const { return get_kind() == CLS_LEARNED; } bool is_aux_lemma() const { return get_kind() == CLS_AUX_LEMMA; } bool in_reinit_stack() const { return m_reinit; } bool reinternalize_atoms() const { return m_reinternalize_atoms; } unsigned get_num_literals() const { return m_num_literals; } literal get_literal(unsigned idx) const { SASSERT(idx < m_num_literals); return m_lits[idx]; } literal & get_literal(unsigned idx) { SASSERT(idx < m_num_literals); return m_lits[idx]; } literal * begin_literals() { return m_lits; } literal * end_literals() { return m_lits + m_num_literals; } literal const * begin_literals() const { return m_lits; } literal const * end_literals() const { return m_lits + m_num_literals; } unsigned get_activity() const { SASSERT(is_lemma()); return *(get_activity_addr()); } void set_activity(unsigned act) { SASSERT(is_lemma()); *(get_activity_addr()) = act; } clause_del_eh * get_del_eh() const { return m_has_del_eh ? *(get_del_eh_addr()) : 0; } justification * get_justification() const { return m_has_justification ? *(get_justification_addr()) : 0; } unsigned get_num_atoms() const { return m_reinternalize_atoms ? m_num_literals : 0; } expr * get_atom(unsigned idx) const { SASSERT(idx < get_num_atoms()); return UNTAG(expr*, get_atoms_addr()[idx]); } bool get_atom_sign(unsigned idx) const { SASSERT(idx < get_num_atoms()); return GET_TAG(get_atoms_addr()[idx]) == 1; } bool erase_atom(unsigned idx); void inc_clause_activity() { SASSERT(is_lemma()); set_activity(get_activity() + 1); } void display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const; void display_compact(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const; unsigned hash() const { return get_ptr_hash(this); } void mark_as_deleted(ast_manager & m) { SASSERT(!m_deleted); m_deleted = true; clause_del_eh * del_eh = get_del_eh(); if (del_eh) { (*del_eh)(m, this); *(const_cast(get_del_eh_addr())) = 0; } } bool deleted() const { return m_deleted; } }; typedef ptr_vector clause_vector; typedef obj_hashtable clause_set; }; #endif /* SMT_CLAUSE_H_ */ z3-z3-4.4.1/src/smt/smt_conflict_resolution.cpp000066400000000000000000001621111260446376700214740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_conflict_resolution.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-25. Revision History: --*/ #include"smt_context.h" #include"smt_conflict_resolution.h" #include"ast_pp.h" #include"ast_ll_pp.h" namespace smt { // --------------------------- // // Base class // // --------------------------- conflict_resolution::conflict_resolution(ast_manager & m, context & ctx, dyn_ack_manager & dyn_ack_manager, smt_params const & params, literal_vector const & assigned_literals, vector & watches ): m_manager(m), m_params(params), m_ctx(ctx), m_dyn_ack_manager(dyn_ack_manager), m_assigned_literals(assigned_literals), m_lemma_atoms(m), m_todo_js_qhead(0), m_antecedents(0), m_watches(watches), m_new_proofs(m), m_lemma_proof(m) { } /** \brief Mark all enodes in a 'proof' tree brach starting at n n -> ... -> root */ template void conflict_resolution::mark_enodes_in_trans(enode * n) { SASSERT(n->trans_reaches(n->get_root())); while (n) { if (Set) n->set_mark(); else n->unset_mark(); n = n->m_trans.m_target; } } /** \brief Find a common ancestor (anc) of n1 and n2 in the 'proof' tree. The common ancestor is used to produce irredundant transitivity proofs. n1 = a1 = ... = ai = ANC = ... = root n2 = b1 = ... = bj = ANC = ... = root The equalities ANC = ... = root should not be included in the proof of n1 = n2. The irredundant proof for n1 = n2 is: n1 = a1 = ... = ai = ANC = bj = ... = b1 = n2 */ enode * conflict_resolution::find_common_ancestor(enode * n1, enode * n2) { SASSERT(n1->get_root() == n2->get_root()); mark_enodes_in_trans(n1); while (true) { SASSERT(n2); if (n2->is_marked()) { mark_enodes_in_trans(n1); return n2; } n2 = n2->m_trans.m_target; } } /** \brief Process a eq_justification of lhs and rhs. This method executes a step of eq_justification2literals This method may update m_antecedents, m_todo_js and m_todo_eqs. */ void conflict_resolution::eq_justification2literals(enode * lhs, enode * rhs, eq_justification js) { SASSERT(m_antecedents); switch(js.get_kind()) { case eq_justification::AXIOM: TRACE("conflict_detail", tout << "#" << lhs->get_owner_id() << " = " << rhs->get_owner_id() << " axiom\n";); break; case eq_justification::EQUATION: TRACE("conflict_detail", tout << "#" << lhs->get_owner_id() << " = " << rhs->get_owner_id() << " was asserted\n" << "literal: "; m_ctx.display_literal(tout, js.get_literal()); tout << "\n";); m_antecedents->push_back(js.get_literal()); break; case eq_justification::JUSTIFICATION: TRACE("conflict_detail", tout << "#" << lhs->get_owner_id() << " = " << rhs->get_owner_id() << " justification\n";); mark_justification(js.get_justification()); break; case eq_justification::CONGRUENCE: { TRACE("conflict_detail", tout << "#" << lhs->get_owner_id() << " = " << rhs->get_owner_id() << " congruence\n";); CTRACE("dyn_ack_target", !lhs->is_eq(), tout << "dyn_ack_target2: " << lhs->get_owner_id() << " " << rhs->get_owner_id() << "\n";); m_dyn_ack_manager.used_cg_eh(lhs->get_owner(), rhs->get_owner()); unsigned num_args = lhs->get_num_args(); SASSERT(num_args == rhs->get_num_args()); if (js.used_commutativity()) { SASSERT(num_args == 2); mark_eq(lhs->get_arg(0), rhs->get_arg(1)); mark_eq(lhs->get_arg(1), rhs->get_arg(0)); } else { for (unsigned i = 0; i < num_args; i++) mark_eq(lhs->get_arg(i), rhs->get_arg(i)); } break; } default: UNREACHABLE(); } } /** \brief Process the transitivity 'proof' from n1 and n2, where n1 and n2 are in the same branch n1 -> ... -> n2 This method may update m_antecedents, m_todo_js and m_todo_eqs. The resultant set of literals is stored in m_antecedents. */ void conflict_resolution::eq_branch2literals(enode * n1, enode * n2) { SASSERT(n1->trans_reaches(n2)); while (n1 != n2) { eq_justification2literals(n1, n1->m_trans.m_target, n1->m_trans.m_justification); n1 = n1->m_trans.m_target; } } /** \brief Process the justification of n1 = n2. This method may update m_antecedents, m_todo_js and m_todo_eqs. The resultant set of literals is stored in m_antecedents. */ void conflict_resolution::eq2literals(enode * n1, enode * n2) { enode * c = find_common_ancestor(n1, n2); eq_branch2literals(n1, c); eq_branch2literals(n2, c); m_dyn_ack_manager.used_eq_eh(n1->get_owner(), n2->get_owner(), c->get_owner()); } /** \brief Extract the antecedent literals from a justification object. The result is stored in result. \remark This method does not reset the vectors m_antecedents, m_todo_js, m_todo_eqs, nor reset the marks in the justification objects. */ void conflict_resolution::justification2literals_core(justification * js, literal_vector & result) { SASSERT(m_todo_js_qhead <= m_todo_js.size()); m_antecedents = &result; mark_justification(js); while (true) { unsigned sz = m_todo_js.size(); while (m_todo_js_qhead < sz) { justification * js = m_todo_js[m_todo_js_qhead]; m_todo_js_qhead++; js->get_antecedents(*this); } while (!m_todo_eqs.empty()) { enode_pair p = m_todo_eqs.back(); m_todo_eqs.pop_back(); eq2literals(p.first, p.second); } if (m_todo_js_qhead == m_todo_js.size()) { m_antecedents = 0; return; } } } /** \brief Unset the mark of the justifications stored in m_todo_js */ void conflict_resolution::unmark_justifications(unsigned old_js_qhead) { SASSERT(old_js_qhead <= m_todo_js.size()); justification_vector::iterator it = m_todo_js.begin() + old_js_qhead; justification_vector::iterator end = m_todo_js.end(); for (; it != end; ++it) { TRACE("conflict_detail", tout << "unmarking: " << *it << "\n";); (*it)->unset_mark(); } m_todo_js.shrink(old_js_qhead); m_todo_js_qhead = old_js_qhead; m_todo_eqs.reset(); m_already_processed_eqs.reset(); } /** \brief Extract the antecedent literals from a justification object. */ void conflict_resolution::justification2literals(justification * js, literal_vector & result) { SASSERT(m_todo_js.empty()); SASSERT(m_todo_js_qhead == 0); SASSERT(m_todo_eqs.empty()); justification2literals_core(js, result); unmark_justifications(0); SASSERT(m_todo_eqs.empty()); } /** \brief Return maximum scope level of an antecedent literal of js. */ unsigned conflict_resolution::get_justification_max_lvl(justification * js) { unsigned r = 0; literal_vector & antecedents = m_tmp_literal_vector; antecedents.reset(); justification2literals(js, antecedents); literal_vector::iterator it = antecedents.begin(); literal_vector::iterator end = antecedents.end(); for(; it != end; ++it) r = std::max(r, m_ctx.get_assign_level(*it)); return r; } /** \brief Return the maximum scope level of the antecedent literals of the given justified literal. */ unsigned conflict_resolution::get_max_lvl(literal consequent, b_justification js) { unsigned r = 0; if (consequent != false_literal) r = m_ctx.get_assign_level(consequent); switch (js.get_kind()) { case b_justification::CLAUSE: { clause * cls = js.get_clause(); unsigned num_lits = cls->get_num_literals(); unsigned i = 0; if (consequent != false_literal) { SASSERT(cls->get_literal(0) == consequent || cls->get_literal(1) == consequent); if (cls->get_literal(0) == consequent) { i = 1; } else { r = std::max(r, m_ctx.get_assign_level(cls->get_literal(0))); i = 2; } } for(; i < num_lits; i++) r = std::max(r, m_ctx.get_assign_level(cls->get_literal(i))); justification * js = cls->get_justification(); if (js) r = std::max(r, get_justification_max_lvl(js)); break; } case b_justification::BIN_CLAUSE: r = std::max(r, m_ctx.get_assign_level(js.get_literal())); break; case b_justification::AXIOM: break; case b_justification::JUSTIFICATION: r = std::max(r, get_justification_max_lvl(js.get_justification())); break; default: UNREACHABLE(); } return r; } void conflict_resolution::process_antecedent(literal antecedent, unsigned & num_marks) { TRACE("conflict", tout << "processing antecedent: "; m_ctx.display_literal(tout, antecedent); tout << "\n";); bool_var var = antecedent.var(); unsigned lvl = m_ctx.get_assign_level(var); SASSERT(var < static_cast(m_ctx.get_num_bool_vars())); if (!m_ctx.is_marked(var) && lvl > m_ctx.get_base_level()) { m_ctx.set_mark(var); m_ctx.inc_bvar_activity(var); expr * n = m_ctx.bool_var2expr(var); if (is_app(n)) { family_id fid = to_app(n)->get_family_id(); theory * th = m_ctx.get_theory(fid); if (th) th->conflict_resolution_eh(to_app(n), var); } if (get_manager().has_trace_stream()) { get_manager().trace_stream() << "[resolve-lit] " << m_conflict_lvl - lvl << " "; m_ctx.display_literal(get_manager().trace_stream(), ~antecedent); get_manager().trace_stream() << "\n"; } if (lvl == m_conflict_lvl) { num_marks++; } else { m_lemma.push_back(~antecedent); m_lemma_atoms.push_back(m_ctx.bool_var2expr(var)); } } } void conflict_resolution::process_justification(justification * js, unsigned & num_marks) { literal_vector & antecedents = m_tmp_literal_vector; antecedents.reset(); justification2literals_core(js, antecedents); literal_vector::iterator it = antecedents.begin(); literal_vector::iterator end = antecedents.end(); for(; it != end; ++it) process_antecedent(*it, num_marks); } /** \brief Skip literals from levels above m_conflict_lvl. It returns an index idx such that m_assigned_literals[idx] <= m_conflict_lvl, and for all idx' > idx, m_assigned_literals[idx'] > m_conflict_lvl */ unsigned conflict_resolution::skip_literals_above_conflict_level() { unsigned idx = m_assigned_literals.size(); if (idx == 0) { return idx; } idx--; // skip literals from levels above the conflict level while (m_ctx.get_assign_level(m_assigned_literals[idx]) > m_conflict_lvl) { SASSERT(idx > 0); idx--; } return idx; } /** \brief Initialize conflict resolution data-structures. Return false if the conflict cannot be resolved (it is at the search level). */ bool conflict_resolution::initialize_resolve(b_justification conflict, literal not_l, b_justification & js, literal & consequent) { TRACE("conflict_detail", m_ctx.display(tout);); m_lemma.reset(); m_lemma_atoms.reset(); SASSERT(m_ctx.get_search_level() >= m_ctx.get_base_level()); js = conflict; consequent = false_literal; if (not_l != null_literal) consequent = ~not_l; m_conflict_lvl = get_max_lvl(consequent, js); TRACE("conflict_bug", tout << "conflict_lvl: " << m_conflict_lvl << " scope_lvl: " << m_ctx.get_scope_level() << " base_lvl: " << m_ctx.get_base_level() << " search_lvl: " << m_ctx.get_search_level() << "\n"; tout << "js.kind: " << js.get_kind() << "\n"; tout << "consequent: " << consequent << "\n"; for (unsigned i = 0; i < m_assigned_literals.size(); ++i) { tout << m_assigned_literals[i] << " "; } tout << "\n"; ); // m_conflict_lvl can be smaller than m_ctx.get_search_level() when: // there are user level scopes created using the Z3 API, and // the previous levels were already inconsistent, or the inconsistency was // triggered by an axiom or justification proof wrapper, this two kinds // of justification are considered level zero. if (m_conflict_lvl <= m_ctx.get_search_level()) { TRACE("conflict", tout << "problem is unsat\n";); if (m_manager.proofs_enabled()) mk_conflict_proof(conflict, not_l); if (m_ctx.tracking_assumptions()) mk_unsat_core(conflict, not_l); return false; } TRACE("conflict", tout << "conflict_lvl: " << m_conflict_lvl << "\n";); SASSERT(!m_assigned_literals.empty()); SASSERT(m_todo_js.empty()); SASSERT(m_todo_js_qhead == 0); SASSERT(m_todo_eqs.empty()); return true; } /** \brief Cleanup datastructures used during resolve(), minimize lemma (when minimization is enabled), compute m_new_scope_lvl and m_lemma_iscope_lvl, generate proof if needed. This method assumes that the lemma is stored in m_lemma (and the associated atoms in m_lemma_atoms). \warning This method assumes the literals in m_lemma[1] ... m_lemma[m_lemma.size() - 1] are marked. */ void conflict_resolution::finalize_resolve(b_justification conflict, literal not_l) { unmark_justifications(0); TRACE("conflict", tout << "before minimization:\n"; m_ctx.display_literals(tout, m_lemma.size(), m_lemma.c_ptr()); tout << "\n";); TRACE("conflict_verbose", tout << "before minimization:\n"; m_ctx.display_literals_verbose(tout, m_lemma.size(), m_lemma.c_ptr()); tout << "\n";); if (m_params.m_minimize_lemmas) minimize_lemma(); TRACE("conflict", tout << "after minimization:\n"; m_ctx.display_literals(tout, m_lemma.size(), m_lemma.c_ptr()); tout << "\n";); TRACE("conflict_verbose", tout << "after minimization:\n"; m_ctx.display_literals_verbose(tout, m_lemma.size(), m_lemma.c_ptr()); tout << "\n";); literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); m_new_scope_lvl = m_ctx.get_search_level(); m_lemma_iscope_lvl = m_ctx.get_intern_level((*it).var()); SASSERT(!m_ctx.is_marked((*it).var())); ++it; for(; it != end; ++it) { bool_var var = (*it).var(); if (var != null_bool_var) { m_ctx.unset_mark(var); unsigned lvl = m_ctx.get_assign_level(var); if (lvl > m_new_scope_lvl) m_new_scope_lvl = lvl; lvl = m_ctx.get_intern_level(var); if (lvl > m_lemma_iscope_lvl) m_lemma_iscope_lvl = lvl; } } TRACE("conflict", tout << "new scope level: " << m_new_scope_lvl << "\n"; tout << "intern. scope level: " << m_lemma_iscope_lvl << "\n";); if (m_manager.proofs_enabled()) mk_conflict_proof(conflict, not_l); } bool conflict_resolution::resolve(b_justification conflict, literal not_l) { b_justification js; literal consequent; if (!initialize_resolve(conflict, not_l, js, consequent)) return false; unsigned idx = skip_literals_above_conflict_level(); // save space for first uip m_lemma.push_back(null_literal); m_lemma_atoms.push_back(0); unsigned num_marks = 0; if (not_l != null_literal) { TRACE("conflict", tout << "not_l: "; m_ctx.display_literal(tout, not_l); tout << "\n";); process_antecedent(not_l, num_marks); } do { if (get_manager().has_trace_stream()) { get_manager().trace_stream() << "[resolve-process] "; m_ctx.display_literal(get_manager().trace_stream(), ~consequent); get_manager().trace_stream() << "\n"; } TRACE("conflict", tout << "processing consequent: "; m_ctx.display_literal(tout, consequent); tout << "\n"; tout << "num_marks: " << num_marks << ", js kind: " << js.get_kind() << "\n";); SASSERT(js != null_b_justification); switch (js.get_kind()) { case b_justification::CLAUSE: { clause * cls = js.get_clause(); if (cls->is_lemma()) cls->inc_clause_activity(); unsigned num_lits = cls->get_num_literals(); unsigned i = 0; if (consequent != false_literal) { SASSERT(cls->get_literal(0) == consequent || cls->get_literal(1) == consequent); if (cls->get_literal(0) == consequent) { i = 1; } else { literal l = cls->get_literal(0); SASSERT(consequent.var() != l.var()); process_antecedent(~l, num_marks); i = 2; } } for(; i < num_lits; i++) { literal l = cls->get_literal(i); SASSERT(consequent.var() != l.var()); process_antecedent(~l, num_marks); } justification * js = cls->get_justification(); if (js) process_justification(js, num_marks); break; } case b_justification::BIN_CLAUSE: SASSERT(consequent.var() != js.get_literal().var()); process_antecedent(js.get_literal(), num_marks); break; case b_justification::AXIOM: break; case b_justification::JUSTIFICATION: process_justification(js.get_justification(), num_marks); break; default: UNREACHABLE(); } while (true) { literal l = m_assigned_literals[idx]; if (m_ctx.is_marked(l.var())) break; CTRACE("conflict", m_ctx.get_assign_level(l) != m_conflict_lvl && m_ctx.get_assign_level(l) != m_ctx.get_base_level(), tout << "assign_level(l): " << m_ctx.get_assign_level(l) << ", conflict_lvl: " << m_conflict_lvl << ", l: "; m_ctx.display_literal(tout, l); tout << "\n";); SASSERT(m_ctx.get_assign_level(l) == m_conflict_lvl || // it may also be an (out-of-order) asserted literal m_ctx.get_assign_level(l) == m_ctx.get_base_level()); SASSERT(idx > 0); idx--; } consequent = m_assigned_literals[idx]; bool_var c_var = consequent.var(); SASSERT(m_ctx.get_assign_level(c_var) == m_conflict_lvl); js = m_ctx.get_justification(c_var); idx--; num_marks--; m_ctx.unset_mark(c_var); } while (num_marks > 0); TRACE("conflict", tout << "FUIP: "; m_ctx.display_literal(tout, consequent); tout << "\n";); m_lemma[0] = ~consequent; m_lemma_atoms.set(0, m_ctx.bool_var2expr(consequent.var())); // TODO: // // equality optimization should go here. // finalize_resolve(conflict, not_l); return true; } /** \brief Return an approximation for the set of scope levels where the literals in m_lemma were assigned. */ level_approx_set conflict_resolution::get_lemma_approx_level_set() { level_approx_set result; literal_vector::const_iterator it = m_lemma.begin(); literal_vector::const_iterator end = m_lemma.end(); for(; it != end; ++it) result.insert(m_ctx.get_assign_level(*it)); return result; } /** \brief Restore the size of m_unmark to old_size, and unmark literals at positions [old_size, m_unmark.size()). */ void conflict_resolution::reset_unmark(unsigned old_size) { unsigned curr_size = m_unmark.size(); for(unsigned i = old_size; i < curr_size; i++) m_ctx.unset_mark(m_unmark[i]); m_unmark.shrink(old_size); } /** \brief Restore the size of m_unmark to old_size, and unmark literals at positions [old_size, m_unmark.size()). And unmark all justifications at positions [old_js_qhead, m_todo_js.size()). */ void conflict_resolution::reset_unmark_and_justifications(unsigned old_size, unsigned old_js_qhead) { reset_unmark(old_size); unmark_justifications(old_js_qhead); } /** \brief Process an antecedent for lemma minimization. */ bool conflict_resolution::process_antecedent_for_minimization(literal antecedent) { bool_var var = antecedent.var(); unsigned lvl = m_ctx.get_assign_level(var); if (!m_ctx.is_marked(var) && lvl > m_ctx.get_base_level()) { if (m_lvl_set.may_contain(lvl)) { m_ctx.set_mark(var); m_unmark.push_back(var); m_lemma_min_stack.push_back(var); } else { return false; } } return true; } bool conflict_resolution::process_justification_for_minimization(justification * js) { literal_vector & antecedents = m_tmp_literal_vector; antecedents.reset(); // Invoking justification2literals_core will not reset the caches for visited justifications and eqs. // The method unmark_justifications must be invoked to reset these caches. // Remark: The method reset_unmark_and_justifications invokes unmark_justifications. justification2literals_core(js, antecedents); literal_vector::iterator it = antecedents.begin(); literal_vector::iterator end = antecedents.end(); for(; it != end; ++it) if (!process_antecedent_for_minimization(*it)) return false; return true; } /** \brief Return true if lit is implied by other marked literals and/or literals assigned at the base level. The set lvl_set is used as an optimization. The idea is to stop the recursive search with a failure as soon as we find a literal assigned in a level that is not in lvl_set. */ bool conflict_resolution::implied_by_marked(literal lit) { m_lemma_min_stack.reset(); // avoid recursive function m_lemma_min_stack.push_back(lit.var()); unsigned old_size = m_unmark.size(); unsigned old_js_qhead = m_todo_js_qhead; while (!m_lemma_min_stack.empty()) { bool_var var = m_lemma_min_stack.back(); m_lemma_min_stack.pop_back(); b_justification js = m_ctx.get_justification(var); SASSERT(js != null_b_justification); switch(js.get_kind()) { case b_justification::CLAUSE: { clause * cls = js.get_clause(); unsigned num_lits = cls->get_num_literals(); unsigned pos = cls->get_literal(1).var() == var; for (unsigned i = 0; i < num_lits; i++) { if (pos != i) { literal l = cls->get_literal(i); SASSERT(l.var() != var); if (!process_antecedent_for_minimization(~l)) { reset_unmark_and_justifications(old_size, old_js_qhead); return false; } } } justification * js = cls->get_justification(); if (js && !process_justification_for_minimization(js)) { reset_unmark_and_justifications(old_size, old_js_qhead); return false; } break; } case b_justification::BIN_CLAUSE: if (!process_antecedent_for_minimization(js.get_literal())) { reset_unmark_and_justifications(old_size, old_js_qhead); return false; } break; case b_justification::AXIOM: // it is a decision variable from a previous scope level or an assumption if (m_ctx.get_assign_level(var) > m_ctx.get_base_level()) { reset_unmark_and_justifications(old_size, old_js_qhead); return false; } break; case b_justification::JUSTIFICATION: if (m_ctx.is_assumption(var) || !process_justification_for_minimization(js.get_justification())) { reset_unmark_and_justifications(old_size, old_js_qhead); return false; } break; } } return true; } /** \brief Minimize the number of literals in learned_clause_lits. The main idea is to remove literals that are implied by other literals in m_lemma and/or literals assigned in the base levels. */ void conflict_resolution::minimize_lemma() { m_unmark.reset(); m_lvl_set = get_lemma_approx_level_set(); unsigned sz = m_lemma.size(); unsigned i = 1; // the first literal is the FUIP unsigned j = 1; for (; i < sz; i++) { literal l = m_lemma[i]; if (implied_by_marked(l)) { m_unmark.push_back(l.var()); } else { if (j != i) { m_lemma[j] = m_lemma[i]; m_lemma_atoms.set(j, m_lemma_atoms.get(i)); } j++; } } reset_unmark_and_justifications(0, 0); m_lemma .shrink(j); m_lemma_atoms.shrink(j); m_ctx.m_stats.m_num_minimized_lits += sz - j; } /** \brief Return the proof object associated with the equality (= n1 n2) if it already exists. Otherwise, return 0 and add p to the todo-list. */ proof * conflict_resolution::get_proof(enode * n1, enode * n2) { SASSERT(n1 != n2); proof * pr; if (m_eq2proof.find(n1, n2, pr)) { TRACE("proof_gen_bug", tout << "eq2_pr_cached: #" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";); return pr; } m_todo_pr.push_back(tp_elem(n1, n2)); return 0; } /** \brief Apply symmetry if pr is a proof of (= n2 n1). */ proof * conflict_resolution::norm_eq_proof(enode * n1, enode * n2, proof * pr) { if (!pr) return 0; SASSERT(m_manager.has_fact(pr)); app * fact = to_app(m_manager.get_fact(pr)); app * n1_owner = n1->get_owner(); app * n2_owner = n2->get_owner(); bool is_eq = m_manager.is_eq(fact) || m_manager.is_iff(fact); if (!is_eq || (fact->get_arg(0) != n2_owner && fact->get_arg(1) != n2_owner)) { CTRACE("norm_eq_proof_bug", !m_ctx.is_true(n2) && !m_ctx.is_false(n2), tout << "n1: #" << n1->get_owner_id() << ", n2: #" << n2->get_owner_id() << "\n"; if (fact->get_num_args() == 2) { tout << "fact(0): #" << fact->get_arg(0)->get_id() << ", fact(1): #" << fact->get_arg(1)->get_id() << "\n"; } tout << mk_bounded_pp(n1->get_owner(), m_manager, 10) << "\n"; tout << mk_bounded_pp(n2->get_owner(), m_manager, 10) << "\n"; tout << mk_bounded_pp(fact, m_manager, 10) << "\n"; tout << mk_ll_pp(pr, m_manager, true, false);); SASSERT(m_ctx.is_true(n2) || m_ctx.is_false(n2)); SASSERT(fact == n1_owner || (m_manager.is_not(fact) && fact->get_arg(0) == n1_owner)); if (m_ctx.is_true(n2)) pr = m_manager.mk_iff_true(pr); else pr = m_manager.mk_iff_false(pr); m_new_proofs.push_back(pr); return pr; } if (m_manager.coarse_grain_proofs()) return pr; TRACE("norm_eq_proof", tout << "#" << n1->get_owner_id() << " = #" << n2->get_owner_id() << "\n"; tout << mk_ll_pp(pr, m_manager, true, false);); SASSERT(m_manager.is_eq(fact) || m_manager.is_iff(fact)); SASSERT((fact->get_arg(0) == n1->get_owner() && fact->get_arg(1) == n2->get_owner()) || (fact->get_arg(1) == n1->get_owner() && fact->get_arg(0) == n2->get_owner())); if (fact->get_arg(0) == n1_owner && fact->get_arg(1) == n2_owner) return pr; pr = m_manager.mk_symmetry(pr); m_new_proofs.push_back(pr); return pr; } /** \brief Return a proof object for n1 = n2 using the given eq_justification if its antecedents are already available. Otherwise, return 0 and add the missing antecedents to the todo-list. */ proof * conflict_resolution::get_proof(enode * n1, enode * n2, eq_justification js) { unsigned num_args; switch (js.get_kind()) { case eq_justification::AXIOM: UNREACHABLE(); return 0; case eq_justification::EQUATION: TRACE("proof_gen_bug", tout << js.get_literal() << "\n"; m_ctx.display_literal_info(tout, js.get_literal());); return norm_eq_proof(n1, n2, get_proof(js.get_literal())); case eq_justification::JUSTIFICATION: return norm_eq_proof(n1, n2, get_proof(js.get_justification())); case eq_justification::CONGRUENCE: num_args = n1->get_num_args(); SASSERT(num_args == n2->get_num_args()); SASSERT(n1->get_owner()->get_decl() == n2->get_owner()->get_decl()); if (js.used_commutativity()) { bool visited = true; SASSERT(num_args == 2); enode * c1_1 = n1->get_arg(0); enode * c1_2 = n1->get_arg(1); enode * c2_1 = n2->get_arg(0); enode * c2_2 = n2->get_arg(1); ptr_buffer prs; if (c1_1 != c2_2) { proof * pr = get_proof(c1_1, c2_2); prs.push_back(pr); if (!pr) visited = false; } if (c1_2 != c2_1) { proof * pr = get_proof(c1_2, c2_1); prs.push_back(pr); if (!pr) visited = false; } if (!visited) return 0; app * e1 = n1->get_owner(); app * e2 = n2->get_owner(); app * e2_prime = m_manager.mk_app(e2->get_decl(), e2->get_arg(1), e2->get_arg(0)); proof * pr1 = 0; if (!prs.empty()) { pr1 = m_manager.mk_congruence(e1, e2_prime, prs.size(), prs.c_ptr()); m_new_proofs.push_back(pr1); } else { TRACE("comm_proof_bug", tout << "e1: #" << e1->get_id() << " e2: #" << e2->get_id() << "\n" << mk_bounded_pp(e1, m_manager, 10) << "\n" << mk_bounded_pp(e2, m_manager, 10) << "\n";); // SASSERT(e1 == e2); } proof * pr2 = m_manager.mk_commutativity(e2_prime); m_new_proofs.push_back(pr2); return m_manager.mk_transitivity(pr1, pr2); } else { bool visited = true; ptr_buffer prs; for (unsigned i = 0; i < num_args; i++) { enode * c1 = n1->get_arg(i); enode * c2 = n2->get_arg(i); if (c1 != c2) { proof * pr = get_proof(c1, c2); prs.push_back(pr); if (!pr) visited = false; } } if (!visited) return 0; proof * pr = m_manager.mk_congruence(n1->get_owner(), n2->get_owner(), prs.size(), prs.c_ptr()); m_new_proofs.push_back(pr); return pr; } default: UNREACHABLE(); return 0; } } /** \brief Return the proof object associated with the given literal if it already exists. Otherwise, return 0 and add l to the todo-list. */ proof * conflict_resolution::get_proof(literal l) { proof * pr; if (m_lit2proof.find(l, pr)) { TRACE("proof_gen_bug", tout << "lit2pr_cached: #" << l << "\n";); return pr; } m_todo_pr.push_back(tp_elem(l)); return 0; } /** \brief Return a proof object for l using the given b_justification if its antecedents are already available. Otherwise, return 0 and add the missing antecedents to the todo-list. */ proof * conflict_resolution::get_proof(literal l, b_justification js) { #ifdef _TRACE static unsigned invocation_counter = 0; invocation_counter++; #define DUMP_AFTER_NUM_INVOCATIONS 213473 CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, tout << "START get_proof\n";); #endif // l is a hypothesis: if it is marked, and the justification for the variable l.var() is js. // we need the second condition, because the core builds proofs as: // // p1: is a proof of "A" // p2: is a proof of "not A" // [unit-resolution p1 p2]: false // // Let us assume that "A" was assigned first during propagation. // Then, the "resolve" method will never select "not A" as a hypothesis. // "not_A" will be the not_l argument in this method. // Since we are assuming that "A" was assigned first", m_ctx.get_justification("A") will be // p1. // // So, the test "m_ctx.get_justification(l.var()) == js" is used to check // if l was assigned before ~l. if (m_ctx.is_marked(l.var()) && m_ctx.get_justification(l.var()) == js) { expr_ref l_expr(m_manager); m_ctx.literal2expr(l, l_expr); proof * pr = m_manager.mk_hypothesis(l_expr.get()); m_new_proofs.push_back(pr); return pr; } else { SASSERT(js.get_kind() != b_justification::BIN_CLAUSE); SASSERT(js.get_kind() != b_justification::AXIOM); if (js.get_kind() == b_justification::CLAUSE) { clause * cls = js.get_clause(); justification * js = cls->get_justification(); SASSERT(js); proof * pr = get_proof(js); ptr_buffer prs; bool visited = pr != 0; TRACE("get_proof_bug", if (pr != 0) tout << js->get_name() << "\n";); CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, if (pr != 0) tout << js->get_name() << "\n";); CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, if (pr != 0) js->display_debug_info(*this, tout);); prs.push_back(pr); unsigned num_lits = cls->get_num_literals(); unsigned i = 0; SASSERT(l == false_literal || l == cls->get_literal(0) || l == cls->get_literal(1)); if (l != false_literal) { if (cls->get_literal(0) == l) { i = 1; } else { SASSERT(l == cls->get_literal(1)); proof * pr = get_proof(~cls->get_literal(0)); prs.push_back(pr); if (!pr) visited = false; i = 2; } } for (; i < num_lits; i++) { proof * pr = get_proof(~cls->get_literal(i)); prs.push_back(pr); if (!pr) visited = false; } if (!visited) return 0; expr_ref l_exr(m_manager); m_ctx.literal2expr(l, l_exr); TRACE("get_proof_bug", tout << "clause:\n"; for (unsigned i = 0; i < num_lits; i++) { tout << cls->get_literal(i).index() << "\n"; expr_ref l_expr(m_manager); m_ctx.literal2expr(cls->get_literal(i), l_expr); tout << mk_pp(l_expr, m_manager) << "\n"; } tout << "antecedents:\n"; for (unsigned i = 0; i < prs.size(); i++) { tout << mk_pp(m_manager.get_fact(prs[i]), m_manager) << "\n"; } tout << "consequent:\n" << mk_pp(l_exr, m_manager) << "\n";); CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, tout << "clause, num_lits: " << num_lits << ":\n"; for (unsigned i = 0; i < num_lits; i++) { tout << cls->get_literal(i).index() << "\n"; expr_ref l_expr(m_manager); m_ctx.literal2expr(cls->get_literal(i), l_expr); tout << mk_pp(l_expr, m_manager) << "\n"; } tout << "antecedents:\n"; for (unsigned i = 0; i < prs.size(); i++) { tout << mk_pp(m_manager.get_fact(prs[i]), m_manager) << "\n"; } tout << "consequent:\n" << mk_pp(l_exr, m_manager) << "\n";); TRACE("get_proof", tout << l.index() << " " << true_literal.index() << " " << false_literal.index() << " "; m_ctx.display_literal(tout, l); tout << " --->\n"; tout << mk_ll_pp(l_exr, m_manager);); CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, tout << l.index() << " " << true_literal.index() << " " << false_literal.index() << " "; m_ctx.display_literal(tout, l); tout << " --->\n"; tout << mk_ll_pp(l_exr, m_manager);); pr = m_manager.mk_unit_resolution(prs.size(), prs.c_ptr(), l_exr); m_new_proofs.push_back(pr); return pr; } else { return get_proof(js.get_justification()); } } } /** \brief Return the proof object associated with the given justification if it already exists. Otherwise, return 0 and add js to the todo-list. */ proof * conflict_resolution::get_proof(justification * js) { proof * pr; if (m_js2proof.find(js, pr)) { TRACE("proof_gen_bug", tout << "js2pr_cached: #" << js << "\n";); return pr; } SASSERT(js != 0); m_todo_pr.push_back(tp_elem(js)); return 0; } void conflict_resolution::init_mk_proof() { TRACE("proof_gen_bug", tout << "reset_caches\n";); m_new_proofs.reset(); m_todo_pr.reset(); m_eq2proof.reset(); m_lit2proof.reset(); m_js2proof.reset(); literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); for (; it != end; ++it) m_ctx.set_mark((*it).var()); } bool conflict_resolution::visit_b_justification(literal l, b_justification js) { // l is a hypothesis: if it is marked, and the justification for the variable l.var() is js. // See: get_proof(literal l, b_justification js) if (m_ctx.is_marked(l.var()) && m_ctx.get_justification(l.var()) == js) return true; SASSERT(js.get_kind() != b_justification::BIN_CLAUSE); CTRACE("visit_b_justification_bug", js.get_kind() == b_justification::AXIOM, tout << "l: " << l << "\n"; m_ctx.display(tout);); SASSERT(js.get_kind() != b_justification::AXIOM); if (js.get_kind() == b_justification::CLAUSE) { clause * cls = js.get_clause(); bool visited = get_proof(cls->get_justification()) != 0; unsigned num_lits = cls->get_num_literals(); unsigned i = 0; if (l != false_literal) { if (cls->get_literal(0) == l) { i = 1; } else { if (get_proof(~cls->get_literal(0)) == 0) visited = false; i = 2; } } for (; i < num_lits; i++) if (get_proof(~cls->get_literal(i)) == 0) visited = false; return visited; } else return get_proof(js.get_justification()) != 0; } void conflict_resolution::mk_proof(literal l, b_justification js) { SASSERT(!m_lit2proof.contains(l)); proof * pr = get_proof(l, js); SASSERT(pr); TRACE("proof_gen_bug", tout << "lit2pr_saved: #" << l << "\n";); m_lit2proof.insert(l, pr); TRACE("mk_proof", tout << mk_bounded_pp(m_ctx.bool_var2expr(l.var()), m_manager, 10) << "\n"; tout << "storing proof for: "; m_ctx.display_literal(tout, l); tout << "\n"; tout << mk_ll_pp(pr, m_manager);); } /** \brief Given that lhs = ... = rhs, and lhs reaches rhs in the 'proof' tree branch. Then, return true if all proof objects needed to create the proof steps are already available. Otherwise return false and update m_todo_pr with info about the proof objects that need to be created. */ bool conflict_resolution::visit_trans_proof(enode * lhs, enode * rhs) { SASSERT(lhs->trans_reaches(rhs)); bool visited = true; while (lhs != rhs) { eq_justification js = lhs->m_trans.m_justification; switch (js.get_kind()) { case eq_justification::AXIOM: UNREACHABLE(); break; case eq_justification::EQUATION: if (get_proof(js.get_literal()) == 0) visited = false; break; case eq_justification::JUSTIFICATION: if (get_proof(js.get_justification()) == 0) visited = false; break; case eq_justification::CONGRUENCE: { enode * n1 = lhs; enode * n2 = lhs->m_trans.m_target; unsigned num_args = n1->get_num_args(); SASSERT(num_args == n2->get_num_args()); if (js.used_commutativity()) { SASSERT(num_args == 2); enode * c1_1 = n1->get_arg(0); enode * c1_2 = n1->get_arg(1); enode * c2_1 = n2->get_arg(0); enode * c2_2 = n2->get_arg(1); if (c1_1 != c2_2 && get_proof(c1_1, c2_2) == 0) visited = false; if (c1_2 != c2_1 && get_proof(c1_2, c2_1) == 0) visited = false; } else { for (unsigned i = 0; i < num_args; i++) { enode * c1 = n1->get_arg(i); enode * c2 = n2->get_arg(i); if (c1 != c2 && get_proof(c1, c2) == 0) visited = false; } } break; } default: UNREACHABLE(); } lhs = lhs->m_trans.m_target; } return visited; } /** \brief Return true if all proof objects that are used to build the proof that lhs = rhs were already built. If the result is false, then m_todo_pr is updated with info about the proof objects that need to be created. */ bool conflict_resolution::visit_eq_justications(enode * lhs, enode * rhs) { enode * c = find_common_ancestor(lhs, rhs); bool v1 = visit_trans_proof(lhs, c); bool v2 = visit_trans_proof(rhs, c); return v1 && v2; } /** \brief Given that lhs = ... = rhs, and lhs reaches rhs in the trans proof branch, then build a proof object for each equality in the sequence, and insert them into result. */ void conflict_resolution::mk_proof(enode * lhs, enode * rhs, ptr_buffer & result) { SASSERT(lhs->trans_reaches(rhs)); while (lhs != rhs) { eq_justification js = lhs->m_trans.m_justification; enode * n1 = lhs; enode * n2 = lhs->m_trans.m_target; proof * pr = get_proof(n1, n2, js); SASSERT(pr); result.push_back(pr); lhs = lhs->m_trans.m_target; } } void conflict_resolution::mk_proof(enode * lhs, enode * rhs) { SASSERT(!m_eq2proof.contains(lhs, rhs)); enode * c = find_common_ancestor(lhs, rhs); ptr_buffer prs1; mk_proof(lhs, c, prs1); ptr_buffer prs2; mk_proof(rhs, c, prs2); while (!prs2.empty()) { proof * pr = prs2.back(); if (m_manager.fine_grain_proofs()) { pr = m_manager.mk_symmetry(pr); m_new_proofs.push_back(pr); prs1.push_back(pr); } else { prs1.push_back(pr); } prs2.pop_back(); } proof * pr = 0; SASSERT(!prs1.empty()); if (prs1.size() == 1) pr = prs1[0]; else { TRACE("mk_transitivity", unsigned sz = prs1.size(); for (unsigned i = 0; i < sz; i++) { tout << mk_ll_pp(prs1[i], m_manager) << "\n"; }); pr = m_manager.mk_transitivity(prs1.size(), prs1.c_ptr(), lhs->get_owner(), rhs->get_owner()); } m_new_proofs.push_back(pr); TRACE("proof_gen_bug", tout << "eq2pr_saved: #" << lhs->get_owner_id() << " #" << rhs->get_owner_id() << "\n";); m_eq2proof.insert(lhs, rhs, pr); } void conflict_resolution::mk_conflict_proof(b_justification conflict, literal not_l) { SASSERT(conflict.get_kind() != b_justification::BIN_CLAUSE); SASSERT(conflict.get_kind() != b_justification::AXIOM); SASSERT(not_l == null_literal || conflict.get_kind() == b_justification::JUSTIFICATION); TRACE("mk_conflict_proof", tout << "lemma literals: "; literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); for (; it != end; ++it) { m_ctx.display_literal(tout, *it); tout << " "; } tout << "\n";); init_mk_proof(); literal consequent = false_literal; if (not_l != null_literal) consequent = ~not_l; visit_b_justification(consequent, conflict); if (not_l != null_literal) m_todo_pr.push_back(tp_elem(not_l)); while (!m_todo_pr.empty()) { tp_elem & elem = m_todo_pr.back(); switch (elem.m_kind) { case tp_elem::EQUALITY: { enode * lhs = elem.m_lhs; enode * rhs = elem.m_rhs; if (m_eq2proof.contains(lhs, rhs)) m_todo_pr.pop_back(); else if (visit_eq_justications(lhs, rhs)) { m_todo_pr.pop_back(); mk_proof(lhs, rhs); } break; } case tp_elem::JUSTIFICATION: { justification * js = elem.m_js; if (m_js2proof.contains(js)) m_todo_pr.pop_back(); else { proof * pr = js->mk_proof(*this); if (pr) { m_todo_pr.pop_back(); m_new_proofs.push_back(pr); TRACE("proof_gen_bug", tout << "js2pr_saved: #" << js << "\n";); m_js2proof.insert(js, pr); } } break; } case tp_elem::LITERAL: { literal l = to_literal(elem.m_lidx); if (m_lit2proof.contains(l)) m_todo_pr.pop_back(); else { b_justification js = m_ctx.get_justification(l.var()); if (visit_b_justification(l, js)) { m_todo_pr.pop_back(); mk_proof(l, js); } } break; } default: UNREACHABLE(); } } SASSERT(visit_b_justification(consequent, conflict)); proof * pr = 0; if (not_l == null_literal) { pr = get_proof(false_literal, conflict); SASSERT(pr); } else { proof * prs[2] = { 0, 0}; m_lit2proof.find(not_l, prs[0]); SASSERT(prs[0]); prs[1] = get_proof(consequent, conflict); SASSERT(prs[1]); pr = m_manager.mk_unit_resolution(2, prs); } expr_ref_buffer lits(m_manager); literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); for (; it != end; ++it) { m_ctx.unset_mark((*it).var()); expr_ref l_expr(m_manager); m_ctx.literal2expr(*it, l_expr); lits.push_back(l_expr); } expr * fact = 0; switch (lits.size()) { case 0: fact = 0; break; case 1: fact = lits[0]; break; default: fact = m_manager.mk_or(lits.size(), lits.c_ptr()); } if (fact == 0) m_lemma_proof = pr; else m_lemma_proof = m_manager.mk_lemma(pr, fact); m_new_proofs.reset(); } void conflict_resolution::process_antecedent_for_unsat_core(literal antecedent) { bool_var var = antecedent.var(); TRACE("conflict", tout << "processing antecedent: "; m_ctx.display_literal_info(tout << antecedent << " ", antecedent); tout << (m_ctx.is_marked(var)?"marked":"not marked"); tout << "\n";); if (!m_ctx.is_marked(var)) { m_ctx.set_mark(var); m_unmark.push_back(var); if (m_ctx.is_assumption(var)) m_assumptions.push_back(antecedent); } } void conflict_resolution::process_justification_for_unsat_core(justification * js) { literal_vector & antecedents = m_tmp_literal_vector; antecedents.reset(); justification2literals_core(js, antecedents); literal_vector::iterator it = antecedents.begin(); literal_vector::iterator end = antecedents.end(); for(; it != end; ++it) process_antecedent_for_unsat_core(*it); } void conflict_resolution::mk_unsat_core(b_justification conflict, literal not_l) { SASSERT(m_ctx.tracking_assumptions()); m_assumptions.reset(); m_unmark.reset(); SASSERT(m_conflict_lvl <= m_ctx.get_search_level()); unsigned search_lvl = m_ctx.get_search_level(); b_justification js = conflict; literal consequent = false_literal; if (not_l != null_literal) { consequent = ~not_l; } int idx = skip_literals_above_conflict_level(); if (not_l != null_literal) process_antecedent_for_unsat_core(consequent); if (m_assigned_literals.empty()) { goto end_unsat_core; } while (true) { TRACE("unsat_core_bug", tout << "js.get_kind(): " << js.get_kind() << ", idx: " << idx << "\n";); switch (js.get_kind()) { case b_justification::CLAUSE: { clause * cls = js.get_clause(); unsigned num_lits = cls->get_num_literals(); unsigned i = 0; if (consequent != false_literal) { SASSERT(cls->get_literal(0) == consequent || cls->get_literal(1) == consequent); if (cls->get_literal(0) == consequent) { i = 1; } else { process_antecedent_for_unsat_core(~cls->get_literal(0)); i = 2; } } for(; i < num_lits; i++) { literal l = cls->get_literal(i); process_antecedent_for_unsat_core(~l); } justification * js = cls->get_justification(); if (js) process_justification_for_unsat_core(js); break; } case b_justification::BIN_CLAUSE: SASSERT(consequent.var() != js.get_literal().var()); process_antecedent_for_unsat_core(js.get_literal()); break; case b_justification::AXIOM: break; case b_justification::JUSTIFICATION: process_justification_for_unsat_core(js.get_justification()); break; default: UNREACHABLE(); } while (true) { if (idx < 0) goto end_unsat_core; literal l = m_assigned_literals[idx]; TRACE("unsat_core_bug", tout << "l: " << l << ", get_assign_level(l): " << m_ctx.get_assign_level(l) << ", is_marked(l): " << m_ctx.is_marked(l.var()) << "\n";); if (m_ctx.get_assign_level(l) < search_lvl || idx == 0) goto end_unsat_core; if (m_ctx.is_marked(l.var())) break; idx--; } SASSERT(idx >= 0); consequent = m_assigned_literals[idx]; bool_var c_var = consequent.var(); SASSERT(m_ctx.get_assign_level(c_var) == search_lvl); js = m_ctx.get_justification(c_var); idx--; } end_unsat_core: TRACE("unsat_core", tout << "assumptions:\n"; m_ctx.display_literals(tout, m_assumptions.size(), m_assumptions.c_ptr()); tout << "\n";); reset_unmark_and_justifications(0, 0); } conflict_resolution * mk_conflict_resolution(ast_manager & m, context & ctx, dyn_ack_manager & dack_manager, smt_params const & params, literal_vector const & assigned_literals, vector & watches) { return alloc(conflict_resolution, m, ctx, dack_manager, params, assigned_literals, watches); } }; z3-z3-4.4.1/src/smt/smt_conflict_resolution.h000066400000000000000000000227221260446376700211440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_conflict_resolution.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-25. Revision History: --*/ #ifndef SMT_CONFLICT_RESOLUTION_H_ #define SMT_CONFLICT_RESOLUTION_H_ #include"smt_literal.h" #include"smt_bool_var_data.h" #include"smt_justification.h" #include"smt_enode.h" #include"dyn_ack.h" #include"obj_pair_hashtable.h" #include"smt_params.h" #include"obj_pair_hashtable.h" #include"map.h" #include"watch_list.h" #include"obj_pair_set.h" typedef approx_set_tpl level_approx_set; namespace smt { typedef std::pair enode_pair; /** \brief Base conflict resolution class. It implements the FUIP strategy. */ class conflict_resolution { protected: typedef obj_pair_set enode_pair_set; ast_manager & m_manager; smt_params const & m_params; context & m_ctx; dyn_ack_manager & m_dyn_ack_manager; literal_vector const & m_assigned_literals; unsigned m_conflict_lvl; literal_vector m_lemma; expr_ref_vector m_lemma_atoms; unsigned m_new_scope_lvl; unsigned m_lemma_iscope_lvl; justification_vector m_todo_js; unsigned m_todo_js_qhead; svector m_todo_eqs; enode_pair_set m_already_processed_eqs; literal_vector * m_antecedents; // Reference for watch lists are used to implement subsumption resolution vector & m_watches; //!< per literal // --------------------------- // // Proof generation // // --------------------------- typedef obj_map js2proof; typedef obj_pair_map eq2proof; typedef map, default_eq > lit2proof; /** \brief Element for the todo-list used to build proofs. */ struct tp_elem { enum { JUSTIFICATION, EQUALITY, LITERAL } m_kind; union { justification * m_js; unsigned m_lidx; struct { enode * m_lhs; enode * m_rhs; }; }; tp_elem(literal l):m_kind(LITERAL), m_lidx(l.index()) {} tp_elem(enode * lhs, enode * rhs):m_kind(EQUALITY), m_lhs(lhs), m_rhs(rhs) {} tp_elem(justification * js):m_kind(JUSTIFICATION), m_js(js) {} }; svector m_todo_pr; js2proof m_js2proof; eq2proof m_eq2proof; lit2proof m_lit2proof; proof_ref_vector m_new_proofs; proof_ref m_lemma_proof; literal_vector m_assumptions; public: void setup() { } void mark_justification(justification * js) { if (!js->is_marked()) { TRACE("conflict_detail", tout << "marking: " << js << "\n";); js->set_mark(); m_todo_js.push_back(js); } } void mark_eq(enode * n1, enode * n2) { if (n1 != n2) { if (n1->get_owner_id() > n2->get_owner_id()) std::swap(n1, n2); enode_pair p(n1, n2); if (m_already_processed_eqs.insert_if_not_there(p)) { TRACE("conflict_detail", tout << "marking eq #" << p.first->get_owner_id() << " = #" << p.second->get_owner_id() << "\n";); m_todo_eqs.push_back(p); SASSERT(m_already_processed_eqs.contains(p)); } } } void mark_literal(literal l) { SASSERT(m_antecedents); m_antecedents->push_back(l); } void mark_justified_eq(enode * lhs, enode * rhs, eq_justification js) { eq_justification2literals(lhs, rhs, js); } proof * norm_eq_proof(enode * n1, enode * n2, proof * pr); proof * get_proof(enode_pair const & p); proof * get_proof(enode * n1, enode * n2); proof * get_proof(enode * n1, enode * n2, eq_justification js); proof * get_proof(literal l); proof * get_proof(literal l, b_justification js); proof * get_proof(justification * js); bool visit_b_justification(literal l, b_justification js); void mk_proof(literal l, b_justification js); bool visit_trans_proof(enode * lhs, enode * rhs); bool visit_eq_justications(enode * lhs, enode * rhs); void mk_proof(enode * lhs, enode * rhs, ptr_buffer & result); void mk_proof(enode * lhs, enode * rhs); void init_mk_proof(); void mk_conflict_proof(b_justification conflict, literal not_l); protected: template void mark_enodes_in_trans(enode * n); enode * find_common_ancestor(enode * n1, enode * n2); void eq_justification2literals(enode * lhs, enode * rhs, eq_justification js); void eq_branch2literals(enode * n1, enode * n2); void eq2literals(enode * n1, enode * n2); void justification2literals_core(justification * js, literal_vector & result); void unmark_justifications(unsigned old_js_qhead); void justification2literals(justification * js, literal_vector & result); literal_vector m_tmp_literal_vector; unsigned get_justification_max_lvl(justification * js); unsigned get_max_lvl(literal consequent, b_justification js); unsigned skip_literals_above_conflict_level(); void process_antecedent(literal antecedent, unsigned & num_marks); void process_justification(justification * js, unsigned & num_marks); bool_var_vector m_unmark; bool_var_vector m_lemma_min_stack; level_approx_set m_lvl_set; level_approx_set get_lemma_approx_level_set(); void reset_unmark(unsigned old_size); void reset_unmark_and_justifications(unsigned old_size, unsigned old_js_qhead); bool process_antecedent_for_minimization(literal antecedent); bool process_justification_for_minimization(justification * js); bool implied_by_marked(literal lit); void minimize_lemma(); void structural_minimization(); void process_antecedent_for_unsat_core(literal antecedent); void process_justification_for_unsat_core(justification * js); void mk_unsat_core(b_justification conflict, literal not_l); bool initialize_resolve(b_justification conflict, literal not_l, b_justification & js, literal & consequent); void finalize_resolve(b_justification conflict, literal not_l); public: conflict_resolution(ast_manager & m, context & ctx, dyn_ack_manager & dack_manager, smt_params const & params, literal_vector const & assigned_literals, vector & watches ); virtual ~conflict_resolution() {} virtual bool resolve(b_justification conflict, literal not_l); context & get_context() { return m_ctx; } ast_manager & get_manager() { return m_manager; } unsigned get_new_scope_lvl() const { return m_new_scope_lvl; } unsigned get_lemma_intern_lvl() const { return m_lemma_iscope_lvl; } unsigned get_lemma_num_literals() const { return m_lemma.size(); } literal * get_lemma_literals() { return m_lemma.c_ptr(); } expr * * get_lemma_atoms() { return m_lemma_atoms.c_ptr(); } void release_lemma_atoms() { m_lemma_atoms.reset(); } proof * get_lemma_proof() { return m_lemma_proof; } literal_vector::const_iterator begin_unsat_core() const { return m_assumptions.begin(); } literal_vector::const_iterator end_unsat_core() const { return m_assumptions.end(); } }; inline void mark_literals(conflict_resolution & cr, unsigned sz, literal const * ls) { for (unsigned i = 0; i < sz; i++) cr.mark_literal(ls[i]); } conflict_resolution * mk_conflict_resolution(ast_manager & m, context & ctx, dyn_ack_manager & dack_manager, smt_params const & params, literal_vector const & assigned_literals, vector & watches ); }; #endif /* SMT_CONFLICT_RESOLUTION_H_ */ z3-z3-4.4.1/src/smt/smt_context.cpp000066400000000000000000005050331260446376700171000ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_context.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #include #include"smt_context.h" #include"luby.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"warning.h" #include"smt_quick_checker.h" #include"proof_checker.h" #include"ast_util.h" #include"uses_theory.h" #include"model.h" #include"smt_for_each_relevant_expr.h" #include"timeit.h" #include"well_sorted.h" #include"union_find.h" #include"smt_model_generator.h" #include"smt_model_checker.h" #include"smt_model_finder.h" #include"model_pp.h" #include"ast_smt2_pp.h" namespace smt { context::context(ast_manager & m, smt_params & p, params_ref const & _p): m_manager(m), m_fparams(p), m_params(_p), m_setup(*this, p), m_cancel_flag(false), m_asserted_formulas(m, p), m_qmanager(alloc(quantifier_manager, *this, p, _p)), m_model_generator(alloc(model_generator, m)), m_relevancy_propagator(mk_relevancy_propagator(*this)), m_random(p.m_random_seed), m_flushing(false), m_progress_callback(0), m_next_progress_sample(0), m_fingerprints(m_region), m_b_internalized_stack(m), m_e_internalized_stack(m), m_final_check_idx(0), m_cg_table(m), m_dyn_ack_manager(*this, p), m_is_diseq_tmp(0), m_units_to_reassert(m_manager), m_qhead(0), m_simp_qhead(0), m_simp_counter(0), m_bvar_inc(1.0), m_phase_cache_on(true), m_phase_counter(0), m_phase_default(false), m_conflict(null_b_justification), m_not_l(null_literal), m_conflict_resolution(mk_conflict_resolution(m, *this, m_dyn_ack_manager, p, m_assigned_literals, m_watches)), m_unsat_proof(m), m_unsat_core(m), #ifdef Z3DEBUG m_trail_enabled(true), #endif m_scope_lvl(0), m_base_lvl(0), m_search_lvl(0), m_generation(0), m_last_search_result(l_undef), m_last_search_failure(UNKNOWN), m_searching(false) { SASSERT(m_scope_lvl == 0); SASSERT(m_base_lvl == 0); SASSERT(m_search_lvl == 0); m_case_split_queue = mk_case_split_queue(*this, p); init(); if (!relevancy()) m_fparams.m_relevancy_lemma = false; m_model_generator->set_context(this); } context::~context() { flush(); } context * context::mk_fresh(symbol const * l, smt_params * p) { context * new_ctx = alloc(context, m_manager, p == 0 ? m_fparams : *p); new_ctx->set_logic(l == 0 ? m_setup.get_logic() : *l); // copy missing simplifier_plugins // remark: some simplifier_plugins are automatically created by the asserted_formulas class. simplifier & s = get_simplifier(); simplifier & new_s = new_ctx->get_simplifier(); ptr_vector::const_iterator it1 = s.begin_plugins(); ptr_vector::const_iterator end1 = s.end_plugins(); for (; it1 != end1; ++it1) { simplifier_plugin * p = *it1; if (new_s.get_plugin(p->get_family_id()) == 0) { new_ctx->register_plugin(p->mk_fresh()); } SASSERT(new_s.get_plugin(p->get_family_id()) != 0); } // copy theory plugins ptr_vector::iterator it2 = m_theory_set.begin(); ptr_vector::iterator end2 = m_theory_set.end(); for (; it2 != end2; ++it2) { theory * new_th = (*it2)->mk_fresh(new_ctx); new_ctx->register_plugin(new_th); } new_ctx->m_setup.mark_already_configured(); return new_ctx; } void context::init() { app * t = m_manager.mk_true(); mk_bool_var(t); SASSERT(get_bool_var(t) == true_bool_var); SASSERT(true_literal.var() == true_bool_var); m_assignment[true_literal.index()] = l_true; m_assignment[false_literal.index()] = l_false; if (m_manager.proofs_enabled()) { proof * pr = m_manager.mk_true_proof(); m_bdata[true_bool_var].m_justification = b_justification(mk_justification(justification_proof_wrapper(*this, pr))); } else { m_bdata[true_bool_var].m_justification = b_justification::mk_axiom(); } m_true_enode = mk_enode(t, true, true, false); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. // m_true_enode->mark_as_interpreted(); app * f = m_manager.mk_false(); m_false_enode = mk_enode(f, true, true, false); // m_false_enode->mark_as_interpreted(); } void context::set_progress_callback(progress_callback *cb) { m_progress_callback = cb; } /** \brief This method should be used to create equality atoms during the search. See comments in theory::mk_eq_atom */ app * context::mk_eq_atom(expr * lhs, expr * rhs) { family_id fid = m_manager.get_sort(lhs)->get_family_id(); theory * th = get_theory(fid); if (th) return th->mk_eq_atom(lhs, rhs); if (lhs->get_id() > rhs->get_id()) std::swap(lhs, rhs); return m_manager.mk_eq(lhs, rhs); } void context::assign_core(literal l, b_justification j, bool decision) { TRACE("assign_core", tout << "assigning: " << l << " "; display_literal(tout, l); tout << "\n";); SASSERT(l.var() < static_cast(m_b_internalized_stack.size())); m_assigned_literals.push_back(l); m_assignment[l.index()] = l_true; m_assignment[(~l).index()] = l_false; bool_var_data & d = get_bdata(l.var()); d.m_justification = j; d.m_scope_lvl = m_scope_lvl; if (m_fparams.m_restart_adaptive && d.m_phase_available) { m_agility *= m_fparams.m_agility_factor; if (!decision && d.m_phase == l.sign()) m_agility += (1.0 - m_fparams.m_agility_factor); } d.m_phase_available = true; d.m_phase = !l.sign(); TRACE("phase_selection", tout << "saving phase, is_pos: " << d.m_phase << " l: " << l << "\n";); if (d.is_atom() && (m_fparams.m_relevancy_lvl == 0 || (m_fparams.m_relevancy_lvl == 1 && !d.is_quantifier()) || is_relevant_core(bool_var2expr(l.var())))) m_atom_propagation_queue.push_back(l); if (m_manager.has_trace_stream()) trace_assign(l, j, decision); m_case_split_queue->assign_lit_eh(l); } bool context::bcp() { SASSERT(!inconsistent()); while (m_qhead < m_assigned_literals.size()) { if (get_cancel_flag()) { return true; } literal l = m_assigned_literals[m_qhead]; SASSERT(get_assignment(l) == l_true); m_qhead++; m_simp_counter--; literal not_l = ~l; SASSERT(get_assignment(not_l) == l_false); watch_list & w = m_watches[l.index()]; if (binary_clause_opt_enabled()) { // binary clause propagation b_justification js(l); literal * it = w.begin_literals(); literal * end = w.end_literals(); for(; it != end; ++it) { literal l = *it; switch (get_assignment(l)) { case l_false: m_stats.m_num_bin_propagations++; set_conflict(js, ~l); return false; case l_undef: m_stats.m_num_bin_propagations++; assign_core(l, js); break; case l_true: break; } } } // non-binary clause propagation watch_list::clause_iterator it = w.begin_clause(); watch_list::clause_iterator it2 = it; watch_list::clause_iterator end = w.end_clause(); for(; it != end; ++it) { clause * cls = *it; CTRACE("bcp_bug", cls->get_literal(0) != not_l && cls->get_literal(1) != not_l, display_clause_detail(tout, cls); tout << "not_l: "; display_literal(tout, not_l); tout << " " << not_l << "\n";); SASSERT(cls->get_literal(0) == not_l || cls->get_literal(1) == not_l); if (cls->get_literal(0) == not_l) { cls->set_literal(0, cls->get_literal(1)); cls->set_literal(1, not_l); } SASSERT(cls->get_literal(1) == not_l); literal first_lit = cls->get_literal(0); lbool first_lit_val = get_assignment(first_lit); if (first_lit_val == l_true) { *it2 = *it; // clause is already satisfied, keep it it2++; } else { literal * it3 = cls->begin_literals() + 2; literal * end3 = cls->end_literals(); for(; it3 != end3; ++it3) { if (get_assignment(*it3) != l_false) { // swap literal *it3 with literal at position 0 // the negation of literal *it3 will watch clause cls. m_watches[(~(*it3)).index()].insert_clause(cls); cls->set_literal(1, *it3); *it3 = not_l; goto found_watch; } } // did not find watch... if (first_lit_val == l_false) { // CONFLICT // copy remaining watches while (it < end) { *it2 = *it; it2++; it++; } SASSERT(it2 <= end); w.set_end_clause(it2); SASSERT(is_empty_clause(cls)); set_conflict(b_justification(cls)); return false; } else { // PROPAGATION SASSERT(first_lit_val == l_undef); SASSERT(get_assignment(first_lit) == l_undef); SASSERT(is_unit_clause(cls)); *it2 = *it; it2++; // keep clause m_stats.m_num_propagations++; // It is safe to call assign_core instead of assign because first_lit is unassigned assign_core(first_lit, b_justification(cls)); if (m_fparams.m_relevancy_lemma && cls->is_lemma()) { expr * e = bool_var2expr(first_lit.var()); // IMPORTANT: this kind of propagation asserts negative literals of the form (= A1 A2) where // A1 and A2 are array terms. So, it may be interesting to disable it for array eqs. //if (!(m_manager.is_eq(e) && m_manager.get_sort(to_app(e)->get_arg(0))->get_family_id() == m_manager.get_family_id("array"))) mark_as_relevant(e); } } found_watch:; } } SASSERT(it2 <= end); w.set_end_clause(it2); } return true; } /** \brief Push a new equality for theory th, into the theory equality propagation queue. */ void context::push_new_th_eq(theory_id th, theory_var lhs, theory_var rhs) { SASSERT(lhs != rhs); SASSERT(lhs != null_theory_var); SASSERT(rhs != null_theory_var); SASSERT(th != null_theory_id); SASSERT(get_theory(th)->get_enode(lhs) != get_theory(th)->get_enode(rhs)); m_th_eq_propagation_queue.push_back(new_th_eq(th, lhs, rhs)); } /** \brief Push a new disequality for theory th, into the theory disequality propagation queue. */ void context::push_new_th_diseq(theory_id th, theory_var lhs, theory_var rhs) { SASSERT(lhs != rhs); SASSERT(lhs != null_theory_var); SASSERT(rhs != null_theory_var); SASSERT(th != null_theory_id); theory * t = get_theory(th); if (t->get_enode(lhs)->is_interpreted() && t->get_enode(rhs)->is_interpreted()) return; TRACE("add_diseq", tout << "#" << t->get_enode(lhs)->get_owner_id() << " != " << "#" << t->get_enode(rhs)->get_owner_id() << "\n";); m_th_diseq_propagation_queue.push_back(new_th_eq(th, lhs, rhs)); } class add_eq_trail : public trail { enode * m_r1; enode * m_n1; unsigned m_r2_num_parents; public: add_eq_trail(enode * r1, enode * n1, unsigned r2_num_parents): m_r1(r1), m_n1(n1), m_r2_num_parents(r2_num_parents) { } virtual void undo(context & ctx) { ctx.undo_add_eq(m_r1, m_n1, m_r2_num_parents); } }; /** \brief Add the equality n1 = n2 with justification js into the logical context. */ void context::add_eq(enode * n1, enode * n2, eq_justification js) { unsigned old_trail_size = m_trail_stack.size(); try { TRACE("add_eq", tout << "assigning: #" << n1->get_owner_id() << " = #" << n2->get_owner_id() << "\n";); TRACE("add_eq_detail", tout << "assigning\n" << mk_pp(n1->get_owner(), m_manager) << "\n" << mk_pp(n2->get_owner(), m_manager) << "\n"; tout << "kind: " << js.get_kind() << "\n";); m_stats.m_num_add_eq++; enode * r1 = n1->get_root(); enode * r2 = n2->get_root(); if (r1 == r2) { TRACE("add_eq", tout << "redundant constraint.\n";); return; } if (r1->is_interpreted() && r2->is_interpreted()) { TRACE("add_eq", tout << "interpreted roots conflict.\n";); set_conflict(mk_justification(eq_conflict_justification(n1, n2, js))); return; } // Swap r1 and r2: // 1. if the "equivalence" class of r1 is bigger than the equivalence class of r2 // OR // 2. r1 is interpreted but r2 is not. // // The second condition is used to enforce the invariant that if a class contain // an interepreted enode then the root is also interpreted. if ((r1->get_class_size() > r2->get_class_size() && !r2->is_interpreted()) || r1->is_interpreted()) { SASSERT(!r2->is_interpreted()); std::swap(n1, n2); std::swap(r1, r2); } TRACE("add_eq", tout << "merging: #" << r1->get_owner_id() << " #" << r2->get_owner_id() << " n1: #" << n1->get_owner_id() << "\n";); // It is necessary to propagate relevancy to other elements of // the equivalence class. This is nessary to enforce the invariant // in the field m_parent of the enode class. if (is_relevant(r1)) { // && !m_manager.is_eq(r1->get_owner())) !is_eq HACK // NOTE for !is_eq HACK... the !is_eq HACK does not propagate relevancy when two // equality enodes are congruent. I tested this optimization because in V1 // relevancy is not propagated for congruent equalities. // This occurs in V2, because we use the congruence table to propagate disequalities // efficiently. // I disabled this optimization HACK because it breaks invariants in the rest of the code. // To use it, I need to go over the code and analyze all affected places. mark_as_relevant(r2); } else if (is_relevant(r2)) { // && !m_manager.is_eq(r2->get_owner())) { // !is_eq HACK mark_as_relevant(r1); } push_trail(add_eq_trail(r1, n1, r2->get_num_parents())); m_qmanager->add_eq_eh(r1, r2); merge_theory_vars(n2, n1, js); // 'Proof' tree // n1 -> ... -> r1 // n2 -> ... -> r2 SASSERT(n1->trans_reaches(r1)); invert_trans(n1); n1->m_trans.m_target = n2; n1->m_trans.m_justification = js; SASSERT(r1->trans_reaches(n1)); // --------------- // r1 -> .. -> n1 -> n2 -> ... -> r2 #if 0 { static unsigned counter = 0; static unsigned num_adds = 0; static unsigned num_bad_adds = 0; num_adds++; if (r1->get_class_size() <= r2->get_class_size() && r1->m_parents.size() > r2->m_parents.size()) { num_bad_adds++; } if (num_adds % 100000 == 0) { verbose_stream() << "[add-eq]: " << num_bad_adds << " " << num_adds << " " << static_cast(num_bad_adds)/static_cast(num_adds) << "\n"; } } #endif remove_parents_from_cg_table(r1); enode * curr = r1; do { curr->m_root = r2; curr = curr->m_next; } while(curr != r1); SASSERT(r1->get_root() == r2); reinsert_parents_into_cg_table(r1, r2, n1, n2, js); if (n2->is_bool()) propagate_bool_enode_assignment(r1, r2, n1, n2); // Merge "equivalence" classes std::swap(r1->m_next, r2->m_next); // Update "equivalence" class size r2->m_class_size += r1->m_class_size; CASSERT("add_eq", check_invariant()); } catch (...) { // Restore trail size since procedure was interrupted in the middle. // If the add_eq_trail remains on the trail stack, then Z3 may crash when the destructor is invoked. m_trail_stack.shrink(old_trail_size); throw; } } /** \brief When merging to equivalence classes, the parents of the smallest one (that are congruence roots), must be removed from the congruence table since their hash code will change. */ void context::remove_parents_from_cg_table(enode * r1) { // Remove parents from the congruence table enode_vector::iterator it = r1->begin_parents(); enode_vector::iterator end = r1->end_parents(); for (; it != end; ++it) { enode * parent = *it; #if 0 { static unsigned num_eqs = 0; static unsigned num_parents = 0; static unsigned counter = 0; if (parent->is_eq()) num_eqs++; num_parents++; if (num_parents % 100000 == 0) { verbose_stream() << "[remove-cg] " << num_eqs << " " << num_parents << " " << static_cast(num_eqs)/static_cast(num_parents) << "\n"; } } #endif SASSERT(parent->is_marked() || !parent->is_cgc_enabled() || (!parent->is_true_eq() && parent->is_cgr() == m_cg_table.contains_ptr(parent)) || (parent->is_true_eq() && !m_cg_table.contains_ptr(parent))); if (!parent->is_marked() && parent->is_cgr() && !parent->is_true_eq()) { TRACE("add_eq_parents", tout << "add_eq removing: #" << parent->get_owner_id() << "\n";); SASSERT(!parent->is_cgc_enabled() || m_cg_table.contains_ptr(parent)); parent->set_mark(); if (parent->is_cgc_enabled()) { m_cg_table.erase(parent); SASSERT(!m_cg_table.contains_ptr(parent)); } } } } /** \brief Reinsert the parents of r1 that were removed from the cg_table at remove_parents_from_cg_table. Some of these parents will become congruent to other enodes, and a new equality will be propagated. Moreover, this method is also used for doing equality propagation. The parents of r1 that remain as congruence roots are copied to the r2->m_parents. The n1, n2, js arguments are used to implement dynamic ackermanization. js is a justification for n1 and n2 being equal, and the equality n1 = n2 is the one that implied r1 = r2. */ void context::reinsert_parents_into_cg_table(enode * r1, enode * r2, enode * n1, enode * n2, eq_justification js) { enode_vector & r2_parents = r2->m_parents; enode_vector::iterator it = r1->begin_parents(); enode_vector::iterator end = r1->end_parents(); for (; it != end; ++it) { enode * parent = *it; if (!parent->is_marked()) continue; parent->unset_mark(); if (parent->is_eq()) { SASSERT(parent->get_num_args() == 2); TRACE("add_eq_bug", tout << "visiting: #" << parent->get_owner_id() << "\n";); enode * lhs = parent->get_arg(0); enode * rhs = parent->get_arg(1); if (lhs->get_root() == rhs->get_root()) { SASSERT(parent->is_true_eq()); unsigned expr_id = parent->get_owner_id(); bool_var v = get_bool_var_of_id(expr_id); lbool val = get_assignment(v); if (val != l_true) { if (val == l_false && js.get_kind() == eq_justification::CONGRUENCE) m_dyn_ack_manager.cg_conflict_eh(n1->get_owner(), n2->get_owner()); assign(literal(v), mk_justification(eq_propagation_justification(lhs, rhs))); } // It is not necessary to reinsert the equality to the congruence table continue; } } if (parent->is_cgc_enabled()) { enode_bool_pair pair = m_cg_table.insert(parent); enode * parent_prime = pair.first; if (parent_prime == parent) { TRACE("add_eq_parents", tout << "add_eq reinserting: #" << parent->get_owner_id() << "\n";); SASSERT(parent); SASSERT(parent->is_cgr()); SASSERT(m_cg_table.contains_ptr(parent)); r2_parents.push_back(parent); continue; } parent->m_cg = parent_prime; SASSERT(!m_cg_table.contains_ptr(parent)); if (parent_prime->m_root != parent->m_root) { bool used_commutativity = pair.second; TRACE("cg", tout << "found new congruence: #" << parent->get_owner_id() << " = #" << parent_prime->get_owner_id() << " used_commutativity: " << used_commutativity << "\n";); push_new_congruence(parent, parent_prime, used_commutativity); } } else { // If congruence closure is not enabled for parent, then I just copy it // to r2_parents r2_parents.push_back(parent); } } } /** \brief A transitivity 'proof' branch is represented by the following sequence starting at n and ending at n->get_root. N1 = n N_{i+1} = N_i->m_trans.m_target and, there is an k such that N_k = n->get_root() This method will invert this branch. */ void context::invert_trans(enode * n) { enode * curr = n->m_trans.m_target; enode * prev = n; eq_justification js = n->m_trans.m_justification; prev->m_trans.m_target = 0; prev->m_trans.m_justification = null_eq_justification; while (curr != 0) { SASSERT(prev->trans_reaches(n)); enode * new_curr = curr->m_trans.m_target; eq_justification new_js = curr->m_trans.m_justification; curr->m_trans.m_target = prev; curr->m_trans.m_justification = js; prev = curr; js = new_js; curr = new_curr; } } /** \brief Given that r is the root of n, and r contains a theory variable for theory th_id, this method returns a theory variable that is 'closer' to n in the 'proof branch' n -> ... -> r. This method is used to improve the quality of the conflict clauses produced by the logical context. Consider the following example: - Consider the following sequence of equalities: n1 = n2 = n3 = n4 = n5 = n6 - Now, assume that n1 is the root of the equivalence class after each merge. So, the 'proof' branch will have the following shape: n1 <- n2 <- n3 <- n4 <- n5 <- n6 - Assuming that all nodes are attached to theory variable, then the following sequence of equalities is sent to the theory if the method get_closest_var is not used: n1 = n2, n1 = n3, n1 = n4, n1 = n5, n1 = n6 - This sequence is bad, and bad justifications may be produced by theory. For example, assume the following arithmetic constraint n5 < n6 For the arithmetic module, the best justification will be: n1 = n5, n1 = n6 and n5 < n6 This justification contains unnecessary 'junk' to justify that n5 = n6. That is, it proves n5 = n6 using the proofs for n1 = n5 and n1 = n6. When the method get_closest_var is used in the communication with theories, the logical context will send the natural sequence of equalities to the theories: n1 = n2 = n3 = n4 = n5 = n6 */ theory_var context::get_closest_var(enode * n, theory_id th_id) { if (th_id == null_theory_id) return null_theory_var; while (n != 0) { theory_var v = n->get_th_var(th_id); if (v != null_theory_var) return v; n = n->m_trans.m_target; } return null_theory_var; } /** \brief Merge the theory variables of n2->get_root() and n1->get_root(), the result is stored in n2->get_root(). New th_var equalities are propagated to the theories. \remark In most cases, an enode is attached to at most one theory var. */ void context::merge_theory_vars(enode * n2, enode * n1, eq_justification js) { enode * r2 = n2->get_root(); enode * r1 = n1->get_root(); if (!r1->has_th_vars() && !r2->has_th_vars()) return; theory_id from_th = null_theory_id; if (js.get_kind() == eq_justification::JUSTIFICATION) from_th = js.get_justification()->get_from_theory(); if (r2->m_th_var_list.get_next() == 0 && r1->m_th_var_list.get_next() == 0) { // Common case: r2 and r1 have at most one theory var. theory_id t2 = r2->m_th_var_list.get_th_id(); theory_id t1 = r1->m_th_var_list.get_th_id(); // verbose_stream() << "[merge_theory_vars] t2: " << t2 << ", t1: " << t1 << "\n"; theory_var v2 = m_fparams.m_new_core2th_eq ? get_closest_var(n2, t2) : r2->m_th_var_list.get_th_var(); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t1) : r1->m_th_var_list.get_th_var(); TRACE("merge_theory_vars", tout << "v2: " << v2 << " #" << r1->get_owner_id() << ", v1: " << v1 << " #" << r2->get_owner_id() << ", t2: " << t2 << ", t1: " << t1 << "\n";); if (v2 != null_theory_var && v1 != null_theory_var) { if (t1 == t2) { // only send the equality to the theory, if the equality was not propagated by it. if (t1 != from_th) push_new_th_eq(t1, v2, v1); } else { // uncommon case: r2 will have two theory_vars attached to it. r2->add_th_var(v1, t1, m_region); push_new_th_diseqs(r2, v1, get_theory(t1)); push_new_th_diseqs(r1, v2, get_theory(t2)); } } else if (v1 == null_theory_var && v2 != null_theory_var) { push_new_th_diseqs(r1, v2, get_theory(t2)); } else if (v1 != null_theory_var && v2 == null_theory_var) { r2->m_th_var_list.set_th_var(v1); r2->m_th_var_list.set_th_id(t1); TRACE("merge_theory_vars", tout << "push_new_th_diseqs v1: " << v1 << ", t1: " << t1 << "\n";); push_new_th_diseqs(r2, v1, get_theory(t1)); } } else { // r1 and/or r2 have more than one theory variable. TRACE("merge_theory_vars", tout << "#" << r1->get_owner_id() << " == #" << r2->get_owner_id() << "\n";); theory_var_list * l2 = r2->get_th_var_list(); while (l2) { theory_id t2 = l2->get_th_id(); theory_var v2 = m_fparams.m_new_core2th_eq ? get_closest_var(n2, t2) : l2->get_th_var(); SASSERT(v2 != null_theory_var); SASSERT(t2 != null_theory_id); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t2) : r1->get_th_var(t2); if (v1 != null_theory_var) { // only send the equality to the theory, if the equality was not propagated by it. if (t2 != from_th) push_new_th_eq(t2, v2, v1); } else { push_new_th_diseqs(r1, v2, get_theory(t2)); } l2 = l2->get_next(); } theory_var_list * l1 = r1->get_th_var_list(); while (l1) { theory_id t1 = l1->get_th_id(); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t1) : l1->get_th_var(); SASSERT(v1 != null_theory_var); SASSERT(t1 != null_theory_id); theory_var v2 = r2->get_th_var(t1); if (v2 == null_theory_var) { r2->add_th_var(v1, t1, m_region); push_new_th_diseqs(r2, v1, get_theory(t1)); } l1 = l1->get_next(); } } } /** \brief Propabate the boolean assignment when two equivalence classes are merged. */ void context::propagate_bool_enode_assignment(enode * r1, enode * r2, enode * n1, enode * n2) { SASSERT(n1->is_bool()); SASSERT(n2->is_bool()); SASSERT(r1->is_bool()); SASSERT(r2->is_bool()); if (r2 == m_false_enode || r2 == m_true_enode) { bool sign = r2 == m_false_enode; enode * curr = r1; do { SASSERT(curr != m_false_enode); bool_var v = enode2bool_var(curr); literal l(v, sign); if (get_assignment(l) != l_true) assign(l, mk_justification(eq_root_propagation_justification(curr))); curr = curr->m_next; } while(curr != r1); } else { bool_var v1 = enode2bool_var(n1); bool_var v2 = enode2bool_var(n2); lbool val1 = get_assignment(v1); lbool val2 = get_assignment(v2); if (val1 != val2) { if (val2 == l_undef) propagate_bool_enode_assignment_core(n1, n2); else propagate_bool_enode_assignment_core(n2, n1); } } } /** \brief source and target are boolean enodes, they were proved to be equal, and the boolean variable associated with source is assigned. Then, copy the assignment to the boolean variables associated with target. */ void context::propagate_bool_enode_assignment_core(enode * source, enode * target) { SASSERT(source->is_bool()); SASSERT(target->is_bool()); SASSERT(source->get_root() == target->get_root()); bool_var v_source = enode2bool_var(source); lbool val = get_assignment(v_source); SASSERT(val != l_undef); bool sign = val == l_false; enode * first = target; do { bool_var v2 = enode2bool_var(target); lbool val2 = get_assignment(v2); if (val2 != val) { if (val2 != l_undef && congruent(source, target) && source->get_num_args() > 0) m_dyn_ack_manager.cg_conflict_eh(source->get_owner(), target->get_owner()); assign(literal(v2, sign), mk_justification(mp_iff_justification(source, target))); } target = target->get_next(); } while (first != target); } void context::undo_add_eq(enode * r1, enode * n1, unsigned r2_num_parents) { enode * r2 = r1->get_root(); TRACE("add_eq", tout << "undo_add_eq #" << r1->get_owner_id() << " #" << r2->get_owner_id() << "\n";); // restore r2 class size r2->m_class_size -= r1->m_class_size; // unmerge "equivalence" classes std::swap(r1->m_next, r2->m_next); // remove the parents of r1 that remained as congruence roots enode_vector::iterator it = r2->begin_parents(); enode_vector::iterator end = r2->end_parents(); it += r2_num_parents; for (; it != end; ++it) { enode * parent = *it; if (parent->is_cgc_enabled()) { TRACE("add_eq_parents", tout << "removing: #" << parent->get_owner_id() << "\n";); CTRACE("add_eq", !parent->is_cgr(), tout << "old num_parents: " << r2_num_parents << ", num_parents: " << r2->m_parents.size() << ", parent: #" << parent->get_owner_id() << ", parents: \n"; for (unsigned i = 0; i < r2->m_parents.size(); i++) { tout << "#" << r2->m_parents[i]->get_owner_id() << " "; } display(tout);); SASSERT(parent->is_cgr()); SASSERT(m_cg_table.contains_ptr(parent)); m_cg_table.erase(parent); } } enode * curr = r1; do { curr->m_root = r1; curr = curr->m_next; } while(curr != r1); // restore parents of r2 r2->m_parents.shrink(r2_num_parents); // try to reinsert parents of r1 that are not cgr it = r1->begin_parents(); end = r1->end_parents(); for (; it != end; ++it) { enode * parent = *it; TRACE("add_eq_parents", tout << "visiting: #" << parent->get_owner_id() << "\n";); if (parent->is_cgc_enabled()) { enode * cg = parent->m_cg; if (!parent->is_true_eq() && (parent == cg || // parent was root of the congruence class before and after the merge !congruent(parent, cg) // parent was root of the congruence class before but not after the merge )) { TRACE("add_eq_parents", tout << "trying to reinsert\n";); m_cg_table.insert(parent); parent->m_cg = parent; } } } // restore theory vars if (r2->m_th_var_list.get_next() == 0) { // common case: r2 has at most one variable theory_var v2 = r2->m_th_var_list.get_th_var(); if (v2 != null_theory_var) { theory_id t2 = r2->m_th_var_list.get_th_id(); if (get_theory(t2)->get_enode(v2)->get_root() != r2) { SASSERT(get_theory(t2)->get_enode(v2)->get_root() == r1); r2->m_th_var_list.set_th_var(null_theory_var); //remove variable from r2 r2->m_th_var_list.set_th_id(null_theory_id); } } } else { restore_theory_vars(r2, r1); } // 'Proof' tree // r1 -> .. -> n1 -> n2 -> ... -> r2 SASSERT(r1->trans_reaches(r2)); SASSERT(r1->trans_reaches(n1)); n1->m_trans.m_target = 0; n1->m_trans.m_justification = null_eq_justification; invert_trans(r1); // --------------- // n1 -> ... -> r1 // n2 -> ... -> r2 SASSERT(n1->trans_reaches(r1)); SASSERT(r1->m_trans.m_target == 0); CASSERT("add_eq", check_invariant()); } /** \brief Auxiliary method for undo_add_eq. It restores the theory variables of a given root enode. This method deletes any theory variable v2 of r2 (for a theory t2) whenever: get_theory(t2)->get_enode(v2)->get_root() != r2 That is, v2 is not equivalent to r2 anymore. */ void context::restore_theory_vars(enode * r2, enode * r1) { SASSERT(r2->get_root() == r2); theory_var_list * new_l2 = 0; theory_var_list * l2 = r2->get_th_var_list(); while (l2) { theory_var v2 = l2->get_th_var(); theory_id t2 = l2->get_th_id(); if (get_theory(t2)->get_enode(v2)->get_root() != r2) { SASSERT(get_theory(t2)->get_enode(v2)->get_root() == r1); l2 = l2->get_next(); } else { if (new_l2) { new_l2->set_next(l2); new_l2 = l2; } else { r2->m_th_var_list = *l2; new_l2 = &(r2->m_th_var_list); } l2 = l2->get_next(); } } if (new_l2) { new_l2->set_next(0); } else { r2->m_th_var_list.set_th_var(null_theory_var); r2->m_th_var_list.set_next(0); } } /** \brief This method is invoked when a new disequality is asserted. The disequality is propagated to the theories. */ void context::add_diseq(enode * n1, enode * n2) { enode * r1 = n1->get_root(); enode * r2 = n2->get_root(); TRACE("add_diseq", tout << "assigning: #" << n1->get_owner_id() << " != #" << n2->get_owner_id() << "\n"; tout << mk_ll_pp(n1->get_owner(), m_manager) << " != "; tout << mk_ll_pp(n2->get_owner(), m_manager) << "\n"; tout << mk_ll_pp(r1->get_owner(), m_manager) << " != "; tout << mk_ll_pp(r2->get_owner(), m_manager) << "\n"; ); #ifdef Z3DEBUG push_trail(push_back_trail(m_diseq_vector)); m_diseq_vector.push_back(enode_pair(n1, n2)); #endif if (r1 == r2) { TRACE("add_diseq_inconsistent", tout << "add_diseq #" << n1->get_owner_id() << " #" << n2->get_owner_id() << " inconsistency, scope_lvl: " << m_scope_lvl << "\n";); return; // context is inconsistent } // Propagate disequalities to theories if (r1->m_th_var_list.get_next() == 0 && r2->m_th_var_list.get_next() == 0) { // common case: r2 and r1 have at most one theory var. theory_id t1 = r1->m_th_var_list.get_th_id(); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t1) : r1->m_th_var_list.get_th_var(); theory_var v2 = m_fparams.m_new_core2th_eq ? get_closest_var(n2, t1) : r2->m_th_var_list.get_th_var(); TRACE("add_diseq", tout << "one theory diseq\n"; tout << v1 << " != " << v2 << "\n"; tout << "th1: " << t1 << " th2: " << r2->m_th_var_list.get_th_id() << "\n"; ); if (t1 != null_theory_id && v1 != null_theory_var && v2 != null_theory_var && t1 == r2->m_th_var_list.get_th_id()) { if (get_theory(t1)->use_diseqs()) push_new_th_diseq(t1, v1, v2); } } else { theory_var_list * l1 = r1->get_th_var_list(); while (l1) { theory_id t1 = l1->get_th_id(); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t1) : l1->get_th_var(); theory * th = get_theory(t1); TRACE("add_diseq", tout << m_manager.get_family_name(t1) << "\n";); if (th->use_diseqs()) { theory_var v2 = m_fparams.m_new_core2th_eq ? get_closest_var(n2, t1) : r2->get_th_var(t1); if (v2 != null_theory_var) push_new_th_diseq(t1, v1, v2); } l1 = l1->get_next(); } } } /** \brief Return true if n1 and n2 are known to be disequal in the logical context. */ bool context::is_diseq(enode * n1, enode * n2) const { SASSERT(m_manager.get_sort(n1->get_owner()) == m_manager.get_sort(n2->get_owner())); context * _this = const_cast(this); if (!m_is_diseq_tmp) { app * eq = m_manager.mk_eq(n1->get_owner(), n2->get_owner()); m_manager.inc_ref(eq); _this->m_is_diseq_tmp = enode::mk_dummy(m_manager, m_app2enode, eq); } else if (m_manager.get_sort(m_is_diseq_tmp->get_owner()->get_arg(0)) != m_manager.get_sort(n1->get_owner())) { m_manager.dec_ref(m_is_diseq_tmp->get_owner()); app * eq = m_manager.mk_eq(n1->get_owner(), n2->get_owner()); m_manager.inc_ref(eq); m_is_diseq_tmp->m_func_decl_id = UINT_MAX; m_is_diseq_tmp->m_owner = eq; } m_is_diseq_tmp->m_args[0] = n1; m_is_diseq_tmp->m_args[1] = n2; SASSERT(m_is_diseq_tmp->get_num_args() == 2); enode * r = m_cg_table.find(m_is_diseq_tmp); SASSERT((r != 0) == m_cg_table.contains(m_is_diseq_tmp)); TRACE("is_diseq", tout << "r: " << r << "\n";); if (r) { SASSERT(r->is_eq()); literal l = enode2literal(r->get_root()); // SASSERT(result == is_diseq_slow(n1, n2)); return l == false_literal || (is_relevant(l) && get_assignment(l) == l_false); } CTRACE("is_diseq_bug", is_diseq_slow(n1, n2), tout << "#" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";); return false; } /** \brief Slow version of is_diseq */ bool context::is_diseq_slow(enode * n1, enode * n2) const { if (n1->get_num_parents() > n2->get_num_parents()) std::swap(n1, n2); enode_vector::iterator it = n1->begin_parents(); enode_vector::iterator end = n1->end_parents(); for (; it != end; ++it) { enode * parent = *it; if (parent->is_eq() && is_relevant(parent->get_owner()) && get_assignment(enode2bool_var(parent)) == l_false && ((parent->get_arg(0)->get_root() == n1->get_root() && parent->get_arg(1)->get_root() == n2->get_root()) || (parent->get_arg(1)->get_root() == n1->get_root() && parent->get_arg(0)->get_root() == n2->get_root()))) { TRACE("is_diseq_bug", tout << "parent: #" << parent->get_owner_id() << ", parent->root: #" << parent->get_root()->get_owner_id() << " assignment(parent): " << get_assignment(enode2bool_var(parent)) << " args: #" << parent->get_arg(0)->get_owner_id() << " #" << parent->get_arg(1)->get_owner_id() << "\n";); return true; } } return false; } #define SMALL_NUM_PARENTS 3 bool context::is_ext_diseq(enode * n1, enode * n2, unsigned depth) { enode * r1 = n1->get_root(); enode * r2 = n2->get_root(); if (r1 == r2) return false; if (r1->is_interpreted() && r2->is_interpreted()) return true; if (is_diseq(n1, n2)) return true; if (r1->get_num_parents() > r2->get_num_parents()) { std::swap(n1, n2); std::swap(r1, r2); } if (depth == 0) return false; if (r1->get_num_parents() < SMALL_NUM_PARENTS) { TRACE("is_ext_diseq", tout << mk_bounded_pp(n1->get_owner(), m_manager) << " " << mk_bounded_pp(n2->get_owner(), m_manager) << " " << depth << "\n";); enode_vector::iterator it1 = r1->begin_parents(); enode_vector::iterator end1 = r1->end_parents(); for (; it1 != end1; ++it1) { enode * p1 = *it1; if (!is_relevant(p1)) continue; if (p1->is_eq()) continue; if (!p1->is_cgr()) continue; func_decl * f = p1->get_decl(); TRACE("is_ext_diseq", tout << "p1: " << mk_bounded_pp(p1->get_owner(), m_manager) << "\n";); unsigned num_args = p1->get_num_args(); enode_vector::iterator it2 = r2->begin_parents(); enode_vector::iterator end2 = r2->end_parents(); for (; it2 != end2; ++it2) { enode * p2 = *it2; if (!is_relevant(p2)) continue; if (p2->is_eq()) continue; if (!p2->is_cgr()) continue; TRACE("is_ext_diseq", tout << "p2: " << mk_bounded_pp(p2->get_owner(), m_manager) << "\n";); if (p1->get_root() != p2->get_root() && p2->get_decl() == f && p2->get_num_args() == num_args) { unsigned j = 0; for (j = 0; j < num_args; j++) { enode * arg1 = p1->get_arg(j)->get_root(); enode * arg2 = p2->get_arg(j)->get_root(); if (arg1 == arg2) continue; if ((arg1 == r1 || arg1 == r2) && (arg2 == r1 || arg2 == r2)) continue; break; } if (j == num_args) { TRACE("is_ext_diseq", tout << "found parents: " << mk_bounded_pp(p1->get_owner(), m_manager) << " " << mk_bounded_pp(p2->get_owner(), m_manager) << "\n";); if (is_ext_diseq(p1, p2, depth - 1)) return true; } } } } } else { if (depth >= m_almost_cg_tables.size()) { unsigned old_sz = m_almost_cg_tables.size(); m_almost_cg_tables.resize(depth+1, 0); for (unsigned i = old_sz; i < depth + 1; i++) m_almost_cg_tables[i] = alloc(almost_cg_table); } almost_cg_table & table = *(m_almost_cg_tables[depth]); table.reset(r1, r2); enode_vector::iterator it1 = r1->begin_parents(); enode_vector::iterator end1 = r1->end_parents(); for (; it1 != end1; ++it1) { enode * p1 = *it1; if (!is_relevant(p1)) continue; if (p1->is_eq()) continue; if (!p1->is_cgr()) continue; table.insert(p1); } if (table.empty()) return false; enode_vector::iterator it2 = r2->begin_parents(); enode_vector::iterator end2 = r2->end_parents(); for (; it2 != end2; ++it2) { enode * p2 = *it2; if (!is_relevant(p2)) continue; if (p2->is_eq()) continue; if (!p2->is_cgr()) continue; list * ps = table.find(p2); if (ps) { while (ps) { enode * p1 = ps->head(); if (p1->get_root() != p2->get_root() && is_ext_diseq(p1, p2, depth - 1)) return true; ps = ps->tail(); } } } } return false; } /** \brief Return an enode n congruent to (f args). If there is no such enode in the E-graph, then return 0. */ enode * context::get_enode_eq_to(func_decl * f, unsigned num_args, enode * const * args) { enode * tmp = m_tmp_enode.set(f, num_args, args); enode * r = m_cg_table.find(tmp); #ifdef Z3DEBUG if (r != 0) { SASSERT(r->get_owner()->get_decl() == f); SASSERT(r->get_num_args() == num_args); if (r->is_commutative()) { // TODO } else { for (unsigned i = 0; i < num_args; i++) { expr * arg = r->get_owner()->get_arg(i); SASSERT(e_internalized(arg)); enode * _arg = get_enode(arg); CTRACE("eq_to_bug", args[i]->get_root() != _arg->get_root(), tout << "#" << args[i]->get_owner_id() << " #" << args[i]->get_root()->get_owner_id() << " #" << _arg->get_owner_id() << " #" << _arg->get_root()->get_owner_id() << "\n"; tout << "#" << r->get_owner_id() << "\n"; display(tout);); SASSERT(args[i]->get_root() == _arg->get_root()); } } } #endif return r; } /** \brief Process the equality propagation queue. \remark The method assign_eq adds a new entry on this queue. */ bool context::propagate_eqs() { for (unsigned i = 0; i < m_eq_propagation_queue.size(); i++) { new_eq & entry = m_eq_propagation_queue[i]; #if 0 static unsigned counter1 = 0; static unsigned counter2 = 0; if (entry.m_lhs->is_eq() || entry.m_rhs->is_eq()) counter1++; else counter2++; if ((counter1 + counter2) % 10000 == 0) std::cout << counter1 << " " << counter2 << "\n"; #endif add_eq(entry.m_lhs, entry.m_rhs, entry.m_justification); if (inconsistent()) return false; } m_eq_propagation_queue.reset(); return true; } /** \brief Process equalities, theory atoms, etc. */ bool context::propagate_atoms() { SASSERT(!inconsistent()); for (unsigned i = 0; i < m_atom_propagation_queue.size(); i++) { SASSERT(!inconsistent()); literal l = m_atom_propagation_queue[i]; bool_var v = l.var(); bool_var_data & d = get_bdata(v); lbool val = get_assignment(v); TRACE("propagate_atoms", tout << "propagating atom, #" << bool_var2expr(v)->get_id() << ", is_enode(): " << d.is_enode() << " " << l << "\n";); SASSERT(val != l_undef); if (d.is_enode()) propagate_bool_var_enode(v); if (inconsistent()) return false; if (d.is_eq()) { app * n = to_app(m_bool_var2expr[v]); SASSERT(m_manager.is_eq(n)); expr * lhs = n->get_arg(0); expr * rhs = n->get_arg(1); if (val == l_true) { add_eq(get_enode(lhs), get_enode(rhs), eq_justification(l)); } else { TRACE("add_diseq", display_eq_detail(tout, bool_var2enode(v));); add_diseq(get_enode(lhs), get_enode(rhs)); } } else if (d.is_theory_atom()) { theory * th = m_theories.get_plugin(d.get_theory()); SASSERT(th); th->assign_eh(v, val == l_true); } else if (d.is_quantifier()) { // Remark: when RELEVANCY_LEMMA is true, a quantifier can be asserted to false and marked as relevant. // This happens when a quantifier is part of a lemma (conflict-clause), and this conflict clause // becomes an unit-clause and the remaining literal is the negation of a quantifier. CTRACE("assign_quantifier_bug", get_assignment(v) != l_true, tout << "#" << bool_var2expr(v)->get_id() << " val: " << get_assignment(v) << "\n"; tout << mk_pp(bool_var2expr(v), m_manager) << "\n"; display(tout);); SASSERT(is_quantifier(m_bool_var2expr[v])); if (get_assignment(v) == l_true) { // All universal quantifiers have positive polarity in the input formula. // So, we can ignore quantifiers assigned to false. assign_quantifier(to_quantifier(m_bool_var2expr[v])); } } if (inconsistent()) return false; } m_atom_propagation_queue.reset(); return true; } class set_var_theory_trail : public trail { bool_var m_var; public: set_var_theory_trail(bool_var v):m_var(v) {} virtual void undo(context & ctx) { bool_var_data & d = ctx.m_bdata[m_var]; d.reset_notify_theory(); } }; void context::set_var_theory(bool_var v, theory_id tid) { SASSERT(get_var_theory(v) == null_theory_var); SASSERT(tid > 0 && tid <= 255); SASSERT(get_intern_level(v) <= m_scope_lvl); if (m_scope_lvl > get_intern_level(v)) push_trail(set_var_theory_trail(v)); bool_var_data & d = m_bdata[v]; d.set_notify_theory(tid); } /** \brief Propagate the truth value assigned to v, to the enode associated with v. Let n be the enode associated with v. Then, this method merges n = true_term (n = false_term) if v was assigned to true (false). */ void context::propagate_bool_var_enode(bool_var v) { SASSERT(get_assignment(v) != l_undef); SASSERT(get_bdata(v).is_enode()); lbool val = get_assignment(v); TRACE("propagate_bool_var_enode_bug", tout << "var: " << v << " #" << bool_var2expr(v)->get_id() << "\n";); SASSERT(v < static_cast(m_b_internalized_stack.size())); enode * n = bool_var2enode(v); CTRACE("mk_bool_var", !n, tout << "No enode for " << v << "\n";); bool sign = val == l_false; if (n->merge_tf()) add_eq(n, sign ? m_false_enode : m_true_enode, eq_justification(literal(v, sign))); enode * r = n->get_root(); if (r == m_true_enode || r == m_false_enode) return; // Move truth value to other elements of the equivalence class if: // 1) n is the root of the equivalence class // 2) n is not the root, but the variable associated with the root is unassigned. if (n == r || !is_relevant(r) || // <<<< added to fix propagation bug. get_assignment(enode2bool_var(r)) != val) { enode * first = n; n = n->get_next(); while (n != first) { bool_var v2 = enode2bool_var(n); if (get_assignment(v2) != val) assign(literal(v2, sign), mk_justification(mp_iff_justification(first, n))); n = n->get_next(); } } } /** \brief Traverse the disequalities of r's equivalence class, and propagate them to the theory. */ void context::push_new_th_diseqs(enode * r, theory_var v, theory * th) { if (!th->use_diseqs()) { TRACE("push_new_th_diseqs", tout << m_manager.get_family_name(th->get_id()) << " not using diseqs\n";); return; } TRACE("push_new_th_diseqs", tout << "#" << r->get_owner_id() << " v" << v << "\n";); theory_id th_id = th->get_id(); enode_vector::iterator it = r->begin_parents(); enode_vector::iterator end = r->end_parents(); for (; it != end; ++it) { enode * parent = *it; CTRACE("parent_bug", parent == 0, tout << "#" << r->get_owner_id() << ", num_parents: " << r->get_num_parents() << "\n"; display(tout);); if (parent->is_eq()) { bool_var bv = get_bool_var_of_id(parent->get_owner_id()); if (get_assignment(bv) == l_false) { enode * lhs = parent->get_arg(0); enode * rhs = parent->get_arg(1); TRACE("push_new_th_diseqs", tout << "#" << parent->get_owner_id() << " "; tout << "lhs: #" << lhs->get_owner_id() << ", rhs: #" << rhs->get_owner_id() << ", lhs->root: #" << lhs->get_root()->get_owner_id() << ", rhs->root: #" << rhs->get_root()->get_owner_id() << ", r: #" << r->get_owner_id() << ", r->root: #" << r->get_root()->get_owner_id() << "\n"; ); CTRACE("push_new_th_diseqs", lhs->get_root() != r->get_root() && rhs->get_root() != r->get_root(), tout << "lhs: #" << lhs->get_owner_id() << ", rhs: #" << rhs->get_owner_id() << ", lhs->root: #" << lhs->get_root()->get_owner_id() << ", rhs->root: #" << rhs->get_root()->get_owner_id() << ", r: #" << r->get_owner_id() << ", r->root: #" << r->get_root()->get_owner_id() << "\n"; display(tout);); SASSERT(lhs->get_root() == r->get_root() || rhs->get_root() == r->get_root()); if (rhs->get_root() == r->get_root()) std::swap(lhs, rhs); enode * rhs_root = rhs->get_root(); theory_var rhs_var = m_fparams.m_new_core2th_eq ? get_closest_var(rhs, th_id) : rhs_root->get_th_var(th_id); if (m_fparams.m_new_core2th_eq) { theory_var _v = get_closest_var(lhs, th_id); if (_v != null_theory_var) v = _v; } if (rhs_var != null_theory_var && v != rhs_var /* if v == rhs_var then the context will detect the inconsistency. */) push_new_th_diseq(th_id, v, rhs_var); } } } } /** \brief Return the truth assignment for an expression that is attached to a boolean variable. \pre The expression must be attached to a boolean variable. */ inline lbool context::get_assignment_core(expr * n) const { CTRACE("get_assignment_bug", !b_internalized(n), tout << "n:\n" << mk_pp(n, m_manager) << "\n"; display(tout);); SASSERT(b_internalized(n)); unsigned id = n->get_id(); bool_var var = get_bool_var_of_id(id); SASSERT(var != null_bool_var); return get_assignment(var); } /** \brief Return the truth assignment for an expression. If the expression is a not-application, then its child is inspected instead. */ lbool context::get_assignment(expr * n) const { if (m_manager.is_false(n)) return l_false; if (m_manager.is_not(n)) return ~get_assignment_core(to_app(n)->get_arg(0)); return get_assignment_core(n); } lbool context::find_assignment(expr * n) const { if (m_manager.is_false(n)) return l_false; if (m_manager.is_not(n)) { expr * arg = to_app(n)->get_arg(0); if (b_internalized(arg)) return ~get_assignment_core(arg); return l_undef; } if (b_internalized(n)) return get_assignment(n); return l_undef; } /** \brief Return the assignment of a 'boolean' enode. If the enode is not boolean, then return l_undef. */ lbool context::get_assignment(enode * n) const { expr * owner = n->get_owner(); if (!m_manager.is_bool(owner)) return l_undef; if (n == m_false_enode) return l_false; bool_var v = get_bool_var_of_id(owner->get_id()); CTRACE("missing_propagation", v == null_bool_var, tout << mk_pp(owner, m_manager) << "\n";); return get_assignment(v); } /** \brief Return set of assigned literals as expressions. */ void context::get_assignments(expr_ref_vector& assignments) { literal_vector::const_iterator it = m_assigned_literals.begin(); literal_vector::const_iterator end = m_assigned_literals.end(); for (; it != end; ++it) { expr_ref e(m_manager); literal2expr(*it, e); assignments.push_back(e); } } void context::relevant_eh(expr * n) { if (b_internalized(n)) { bool_var v = get_bool_var(n); bool_var_data & d = get_bdata(v); SASSERT(relevancy()); // Quantifiers are only asserted when marked as relevant. // Other atoms are only asserted when marked as relevant if m_relevancy_lvl >= 2 if (d.is_atom() && (d.is_quantifier() || m_fparams.m_relevancy_lvl >= 2)) { lbool val = get_assignment(v); if (val != l_undef) m_atom_propagation_queue.push_back(literal(v, val == l_false)); } } TRACE("propagate_relevancy", tout << "marking as relevant:\n" << mk_bounded_pp(n, m_manager) << "\n";); #ifndef SMTCOMP m_case_split_queue->relevant_eh(n); #endif if (is_app(n)) { if (e_internalized(n)) { SASSERT(relevancy()); enode * e = get_enode(n); m_qmanager->relevant_eh(e); } theory * propagated_th = 0; family_id fid = to_app(n)->get_family_id(); if (fid != m_manager.get_basic_family_id()) { theory * th = get_theory(fid); if (th != 0) { th->relevant_eh(to_app(n)); propagated_th = th; // <<< mark that relevancy_eh was already invoked for theory th. } } if (e_internalized(n)) { enode * e = get_enode(n); theory_var_list * l = e->get_th_var_list(); while (l) { theory_id th_id = l->get_th_id(); theory * th = get_theory(th_id); // I don't want to invoke relevant_eh twice for the same n. if (th != propagated_th) th->relevant_eh(to_app(n)); l = l->get_next(); } } } } /** \brief Propagate relevancy using the queue of new assigned literals located at [qhead, m_assigned_literals.size()). */ void context::propagate_relevancy(unsigned qhead) { if (!relevancy()) return; unsigned sz = m_assigned_literals.size(); while (qhead < sz) { literal l = m_assigned_literals[qhead]; SASSERT(get_assignment(l) == l_true); qhead++; bool_var var = l.var(); expr * n = m_bool_var2expr[var]; m_relevancy_propagator->assign_eh(n, !l.sign()); } m_relevancy_propagator->propagate(); } bool context::propagate_theories() { ptr_vector::iterator it = m_theory_set.begin(); ptr_vector::iterator end = m_theory_set.end(); for (; it != end; ++it) { (*it)->propagate(); if (inconsistent()) return false; } return true; } void context::propagate_th_eqs() { for (unsigned i = 0; i < m_th_eq_propagation_queue.size() && !inconsistent(); i++) { new_th_eq curr = m_th_eq_propagation_queue[i]; theory * th = get_theory(curr.m_th_id); SASSERT(th); th->new_eq_eh(curr.m_lhs, curr.m_rhs); #ifdef Z3DEBUG push_trail(push_back_trail(m_propagated_th_eqs)); m_propagated_th_eqs.push_back(curr); #endif } m_th_eq_propagation_queue.reset(); } void context::propagate_th_diseqs() { for (unsigned i = 0; i < m_th_diseq_propagation_queue.size() && !inconsistent(); i++) { new_th_eq curr = m_th_diseq_propagation_queue[i]; theory * th = get_theory(curr.m_th_id); SASSERT(th); th->new_diseq_eh(curr.m_lhs, curr.m_rhs); #ifdef Z3DEBUG push_trail(push_back_trail(m_propagated_th_diseqs)); m_propagated_th_diseqs.push_back(curr); #endif } m_th_diseq_propagation_queue.reset(); } bool context::can_theories_propagate() const { ptr_vector::const_iterator it = m_theory_set.begin(); ptr_vector::const_iterator end = m_theory_set.end(); for (; it != end; ++it) { if ((*it)->can_propagate()) { return true; } } return false; } bool context::can_propagate() const { return m_qhead != m_assigned_literals.size() || m_relevancy_propagator->can_propagate() || !m_atom_propagation_queue.empty() || m_qmanager->can_propagate() || can_theories_propagate() || !m_eq_propagation_queue.empty() || !m_th_eq_propagation_queue.empty() || !m_th_diseq_propagation_queue.empty(); } bool context::propagate() { TRACE("propagate", tout << "propagating...\n";); while (true) { if (inconsistent()) return false; unsigned qhead = m_qhead; if (!bcp()) return false; if (get_cancel_flag()) return true; SASSERT(!inconsistent()); propagate_relevancy(qhead); if (inconsistent()) return false; if (!propagate_atoms()) return false; if (!propagate_eqs()) return false; propagate_th_eqs(); propagate_th_diseqs(); if (inconsistent()) return false; if (!propagate_theories()) return false; m_qmanager->propagate(); if (inconsistent()) return false; if (resource_limits_exceeded()) return true; if (!can_propagate()) { CASSERT("diseq_bug", inconsistent() || check_missing_diseq_conflict()); CASSERT("eqc_bool", check_eqc_bool_assignment()); return true; } } } void context::set_conflict(b_justification js, literal not_l) { if (!inconsistent()) { m_conflict = js; m_not_l = not_l; } } void context::assign_quantifier(quantifier * q) { TRACE("assumption", tout << mk_pp(q, m_manager) << "\n";); m_qmanager->assign_eh(q); } bool context::contains_instance(quantifier * q, unsigned num_bindings, enode * const * bindings) { return m_fingerprints.contains(q, q->get_id(), num_bindings, bindings); } bool context::add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, ptr_vector & used_enodes) { return m_qmanager->add_instance(q, pat, num_bindings, bindings, max_generation, min_top_generation, max_top_generation, used_enodes); } void context::rescale_bool_var_activity() { svector::iterator it = m_activity.begin(); svector::iterator end = m_activity.end(); for (; it != end; ++it) *it *= INV_ACTIVITY_LIMIT; m_bvar_inc *= INV_ACTIVITY_LIMIT; } /** \brief Execute next clase split, return false if there are no more case splits to be performed. */ bool context::decide() { bool_var var; lbool phase; m_case_split_queue->next_case_split(var, phase); if (var == null_bool_var) { return false; } TRACE_CODE({ static unsigned counter = 0; counter++; if (counter % 100 == 0) { TRACE("activity_profile", for (unsigned i=0; i m_lit_occs[(~l).index()].size(); break; } default: is_pos = false; UNREACHABLE(); } } } TRACE("decide", tout << "case split pos: " << is_pos << " p" << var << "\n" << "activity: " << get_activity(var) << "\n";); assign(literal(var, !is_pos), b_justification::mk_axiom(), true); return true; } /** \brief Update counter that is used to enable/disable phase caching. */ void context::update_phase_cache_counter() { m_phase_counter++; if (m_phase_cache_on) { if (m_phase_counter >= m_fparams.m_phase_caching_on) { m_phase_counter = 0; m_phase_cache_on = false; if (m_fparams.m_phase_selection == PS_CACHING_CONSERVATIVE2) m_phase_default = !m_phase_default; } } else { if (m_phase_counter >= m_fparams.m_phase_caching_off) { m_phase_counter = 0; m_phase_cache_on = true; if (m_fparams.m_phase_selection == PS_CACHING_CONSERVATIVE2) m_phase_default = !m_phase_default; } } } /** \brief Create an internal backtracking point */ void context::push_scope() { if (m_manager.has_trace_stream()) m_manager.trace_stream() << "[push] " << m_scope_lvl << "\n"; m_scope_lvl++; m_region.push_scope(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); m_relevancy_propagator->push(); s.m_assigned_literals_lim = m_assigned_literals.size(); s.m_trail_stack_lim = m_trail_stack.size(); s.m_aux_clauses_lim = m_aux_clauses.size(); s.m_justifications_lim = m_justifications.size(); s.m_units_to_reassert_lim = m_units_to_reassert.size(); m_qmanager->push(); m_fingerprints.push_scope(); m_case_split_queue->push_scope(); m_asserted_formulas.push_scope(); ptr_vector::iterator it = m_theory_set.begin(); ptr_vector::iterator end = m_theory_set.end(); for (; it != end; ++it) (*it)->push_scope_eh(); CASSERT("context", check_invariant()); } /** \brief Execute generic undo-objects. */ void context::undo_trail_stack(unsigned old_size) { ::undo_trail_stack(*this, m_trail_stack, old_size); } /** \brief Remove watch literal idx from the given clause. \pre idx must be 0 or 1. */ void context::remove_watch_literal(clause * cls, unsigned idx) { m_watches[(~cls->get_literal(idx)).index()].remove_clause(cls); } /** \brief Update the index used for backward subsumption. */ void context::remove_lit_occs(clause * cls) { unsigned num_lits = cls->get_num_literals(); for (unsigned i = 0; i < num_lits; i++) { literal l = cls->get_literal(i); m_lit_occs[l.index()].erase(cls); } } void context::remove_cls_occs(clause * cls) { remove_watch_literal(cls, 0); remove_watch_literal(cls, 1); if (lit_occs_enabled()) remove_lit_occs(cls); } /** \brief Delete the given clause. \pre Clause is not in the reinit stack. */ void context::del_clause(clause * cls) { SASSERT(m_flushing || !cls->in_reinit_stack()); if (!cls->deleted()) remove_cls_occs(cls); cls->deallocate(m_manager); m_stats.m_num_del_clause++; } /** \brief Delete the clauses in v at locations [old_size .. v.size()) Reduce the size of v to old_size. */ void context::del_clauses(clause_vector & v, unsigned old_size) { SASSERT(old_size <= v.size()); clause_vector::iterator begin = v.begin() + old_size; clause_vector::iterator it = v.end(); while (it != begin) { --it; del_clause(*it); } v.shrink(old_size); } void context::mark_as_deleted(clause * cls) { SASSERT(!cls->deleted()); remove_cls_occs(cls); cls->mark_as_deleted(m_manager); } /** \brief Undo variable assignments. */ void context::unassign_vars(unsigned old_lim) { SASSERT(old_lim <= m_assigned_literals.size()); unsigned i = m_assigned_literals.size(); while (i != old_lim) { --i; literal l = m_assigned_literals[i]; m_assignment[l.index()] = l_undef; m_assignment[(~l).index()] = l_undef; bool_var v = l.var(); bool_var_data & d = get_bdata(v); d.m_justification = null_b_justification; m_case_split_queue->unassign_var_eh(v); } m_assigned_literals.shrink(old_lim); m_qhead = old_lim; SASSERT(m_qhead == m_assigned_literals.size()); } /** \brief Invoke method del_eh for the justification that will be deleted. If the method in_region() returns false, then delete operator is invoked. */ void context::del_justifications(ptr_vector & justifications, unsigned old_lim) { SASSERT(old_lim <= justifications.size()); unsigned i = justifications.size(); while (i != old_lim) { --i; justification * js = justifications[i]; js->del_eh(m_manager); if (!js->in_region()) { dealloc(js); } else { // If the justification is in a region, then explicitly invoke the destructor. // This is needed because some justification objects contains vectors. // The destructors of these vectors need to be invoked. js->~justification(); } } justifications.shrink(old_lim); } /** \brief Return true if all literals of c are assigned to false. */ bool context::is_empty_clause(clause const * c) const { unsigned num_lits = c->get_num_literals(); for(unsigned i = 0; i < num_lits; i++) { literal l = c->get_literal(i); if (get_assignment(l) != l_false) return false; } return true; } /** \brief Return true if the given clause contains one and only one unassigned literal. */ bool context::is_unit_clause(clause const * c) const { bool found = false; unsigned num_lits = c->get_num_literals(); for(unsigned i = 0; i < num_lits; i++) { literal l = c->get_literal(i); switch (get_assignment(l)) { case l_false: break; // skip case l_undef: if (found) return false; else found = true; break; case l_true: return false; // clause is already satisfied. } } return found; } /** \brief When a clause is reinitialized (see reinit_clauses) enodes and literals may need to be recreated. When an enode is recreated, I want to use the same generation number it had before being deleted. Otherwise the generation will be 0, and will affect the loop prevetion heuristics used to control quantifier instantiation. Thus, I cache the generation number of enodes that will be deleted during backtracking and recreated by reinit_clauses. */ void context::cache_generation(unsigned new_scope_lvl) { if (!m_clauses_to_reinit.empty()) { unsigned lim = m_scope_lvl; if (m_clauses_to_reinit.size() <= lim) { SASSERT(!m_clauses_to_reinit.empty()); lim = m_clauses_to_reinit.size() - 1; } for (unsigned i = new_scope_lvl; i <= lim; i++) { clause_vector & v = m_clauses_to_reinit[i]; clause_vector::iterator it = v.begin(); clause_vector::iterator end = v.end(); for (; it != end; ++it) { clause * cls = *it; cache_generation(cls, new_scope_lvl); } } } if (!m_units_to_reassert.empty()) { scope & s = m_scopes[new_scope_lvl]; unsigned i = s.m_units_to_reassert_lim; unsigned sz = m_units_to_reassert.size(); for (; i < sz; i++) { expr * unit = m_units_to_reassert.get(i); cache_generation(unit, new_scope_lvl); } } } /** \brief See cache_generation(unsigned new_scope_lvl) */ void context::cache_generation(clause const * cls, unsigned new_scope_lvl) { cache_generation(cls->get_num_literals(), cls->begin_literals(), new_scope_lvl); } /** \brief See cache_generation(unsigned new_scope_lvl) */ void context::cache_generation(unsigned num_lits, literal const * lits, unsigned new_scope_lvl) { for(unsigned i = 0; i < num_lits; i++) { bool_var v = lits[i].var(); unsigned ilvl = get_intern_level(v); if (ilvl > new_scope_lvl) cache_generation(bool_var2expr(v), new_scope_lvl); } } /** \brief See cache_generation(unsigned new_scope_lvl) */ void context::cache_generation(expr * n, unsigned new_scope_lvl) { ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { expr * n = todo.back(); todo.pop_back(); if (m_cache_generation_visited.contains(n)) continue; m_cache_generation_visited.insert(n); if (is_app(n)) { if (e_internalized(n)) { enode * e = get_enode(n); unsigned ilvl = e->get_iscope_lvl(); if (ilvl <= new_scope_lvl) continue; // node and its children will not be recreated during backtracking TRACE("cached_generation", tout << "caching: #" << n->get_id() << " " << e->get_generation() << "\n";); m_cached_generation.insert(n, e->get_generation()); } 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_app(arg) || is_quantifier(arg)) todo.push_back(arg); } } else if (is_quantifier(n) && b_internalized(n)) { m_cached_generation.insert(n, m_qmanager->get_generation(to_quantifier(n))); todo.push_back(to_quantifier(n)->get_expr()); } } } /** \brief See cache_generation(unsigned new_scope_lvl) */ void context::reset_cache_generation() { m_cache_generation_visited.reset(); m_cached_generation.reset(); } /** \brief Reinitialize learned clauses (lemmas) that contain boolean variables that were deleted during backtracking. \remark num_bool_vars contains the number of boolean variables alive after backtracking. So, a clause contains a dead variable if it contains a literal l where l.var() >= num_bool_vars. */ void context::reinit_clauses(unsigned num_scopes, unsigned num_bool_vars) { TRACE("reinit_clauses_bug", display_watch_lists(tout);); if (m_clauses_to_reinit.empty()) return; unsigned lim = m_scope_lvl + num_scopes; if (m_clauses_to_reinit.size() <= lim) { SASSERT(!m_clauses_to_reinit.empty()); lim = m_clauses_to_reinit.size() - 1; } for (unsigned i = m_scope_lvl+1; i <= lim; i++) { clause_vector & v = m_clauses_to_reinit[i]; clause_vector::iterator it = v.begin(); clause_vector::iterator end = v.end(); for (; it != end; ++it) { clause * cls = *it; if (cls->deleted()) { cls->release_atoms(m_manager); cls->m_reinit = false; cls->m_reinternalize_atoms = false; continue; } SASSERT(cls->in_reinit_stack()); bool keep = false; if (cls->reinternalize_atoms()) { SASSERT(cls->get_num_atoms() == cls->get_num_literals()); for (unsigned j = 0; j < 2; j++) { literal l = cls->get_literal(j); if (l.var() < static_cast(num_bool_vars)) { // This boolean variable was not deleted during backtracking // // So, it is still a watch literal. I remove the watch, since // the clause may have new watch-literals after reinitialization. remove_watch_literal(cls, j); } } unsigned num = cls->get_num_literals(); if (lit_occs_enabled()) { for (unsigned j = 0; j < num; j++) { literal l = cls->get_literal(j); if (l.var() < static_cast(num_bool_vars)) { // This boolean variable was not deleted during backtracking // // So, remove it from lit_occs. m_lit_occs[l.index()].erase(cls); } } } unsigned ilvl = 0; for (unsigned j = 0; j < num; j++) { expr * atom = cls->get_atom(j); bool sign = cls->get_atom_sign(j); // Atom can be (NOT foo). This can happen, for example, when // the NOT-application is a child of an uninterpreted function symbol. // So, when reinternalizing the NOT-atom I should set the gate_ctx to false, // and force expression to be reinternalized. // Otherwise I set gate_ctx to true bool gate_ctx = !m_manager.is_not(atom); internalize(atom, gate_ctx); SASSERT(b_internalized(atom)); bool_var v = get_bool_var(atom); DEBUG_CODE({ if (get_intern_level(v) > ilvl) ilvl = get_intern_level(v); }); literal l(v, sign); cls->set_literal(j, l); } SASSERT(ilvl <= m_scope_lvl); int w1_idx = select_watch_lit(cls, 0); cls->swap_lits(0, w1_idx); int w2_idx = select_watch_lit(cls, 1); cls->swap_lits(1, w2_idx); add_watch_literal(cls, 0); add_watch_literal(cls, 1); if (lit_occs_enabled()) add_lit_occs(cls); literal l1 = cls->get_literal(0); literal l2 = cls->get_literal(1); if (get_assignment(l1) == l_false) set_conflict(b_justification(cls)); else if (get_assignment(l2) == l_false) assign(l1, b_justification(cls)); TRACE("reinit_clauses", tout << "reinit clause:\n"; display_clause_detail(tout, cls); tout << "\n"; tout << "activity: " << cls->get_activity() << ", num_bool_vars: " << num_bool_vars << ", scope_lvl: " << m_scope_lvl << "\n";); keep = true; } else { SASSERT(!cls->reinternalize_atoms()); literal l1 = cls->get_literal(0); literal l2 = cls->get_literal(1); if (get_assignment(l1) == l_false && is_empty_clause(cls)) { set_conflict(b_justification(cls)); keep = true; } else if (get_assignment(l2) == l_false && get_assignment(l1) == l_undef && is_unit_clause(cls)) { assign(l1, b_justification(cls)); keep = true; } } // SASSERT(!(cls->get_num_literals() == 3 && // (cls->get_literal(0).index() == 624 || cls->get_literal(0).index() == 103 || cls->get_literal(0).index() == 629) && // (cls->get_literal(1).index() == 624 || cls->get_literal(1).index() == 103 || cls->get_literal(1).index() == 629) && // (cls->get_literal(2).index() == 624 || cls->get_literal(2).index() == 103 || cls->get_literal(2).index() == 629))); if (keep && m_scope_lvl > m_base_lvl) { m_clauses_to_reinit[m_scope_lvl].push_back(cls); } else { // clause do not need to be in the reinit stack anymore, // because it will be deleted when the base level is // backtracked. cls->release_atoms(m_manager); cls->m_reinit = false; cls->m_reinternalize_atoms = false; } } v.reset(); } CASSERT("reinit_clauses", check_clauses(m_lemmas)); CASSERT("reinit_clauses", check_lit_occs()); TRACE("reinit_clauses_bug", display_watch_lists(tout);); } void context::reassert_units(unsigned units_to_reassert_lim) { unsigned i = units_to_reassert_lim; unsigned sz = m_units_to_reassert.size(); for (; i < sz; i++) { expr * unit = m_units_to_reassert.get(i); bool gate_ctx = true; internalize(unit, gate_ctx); bool_var v = get_bool_var(unit); bool sign = m_units_to_reassert_sign[i] != 0; literal l(v, sign); assign(l, b_justification::mk_axiom()); TRACE("reassert_units", tout << "reasserting #" << unit->get_id() << " " << sign << " @ " << m_scope_lvl << "\n";); } if (at_base_level()) { m_units_to_reassert.reset(); m_units_to_reassert_sign.reset(); } } /** \brief Backtrack 'num_scopes' scope levels. Return the number of boolean variables before reinitializing clauses. This value is useful because it can be used to detect which boolean variables were deleted. \warning This method will not invoke reset_cache_generation. */ unsigned context::pop_scope_core(unsigned num_scopes) { if (m_manager.has_trace_stream()) m_manager.trace_stream() << "[pop] " << num_scopes << " " << m_scope_lvl << "\n"; TRACE("context", tout << "backtracking: " << num_scopes << "\n";); TRACE("pop_scope_detail", display(tout);); SASSERT(num_scopes > 0); SASSERT(num_scopes <= m_scope_lvl); SASSERT(m_scopes.size() == m_scope_lvl); unsigned new_lvl = m_scope_lvl - num_scopes; cache_generation(new_lvl); m_qmanager->pop(num_scopes); m_case_split_queue->pop_scope(num_scopes); TRACE("pop_scope", tout << "backtracking: " << num_scopes << ", new_lvl: " << new_lvl << "\n";); scope & s = m_scopes[new_lvl]; TRACE("context", tout << "backtracking new_lvl: " << new_lvl << "\n";); unsigned units_to_reassert_lim = s.m_units_to_reassert_lim; if (new_lvl < m_base_lvl) { base_scope & bs = m_base_scopes[new_lvl]; del_clauses(m_lemmas, bs.m_lemmas_lim); m_simp_qhead = bs.m_simp_qhead_lim; if (!bs.m_inconsistent) { m_conflict = null_b_justification; m_not_l = null_literal; m_unsat_proof = 0; } m_base_scopes.shrink(new_lvl); } else { m_conflict = null_b_justification; m_not_l = null_literal; } del_clauses(m_aux_clauses, s.m_aux_clauses_lim); m_relevancy_propagator->pop(num_scopes); m_fingerprints.pop_scope(num_scopes); unassign_vars(s.m_assigned_literals_lim); undo_trail_stack(s.m_trail_stack_lim); ptr_vector::iterator it = m_theory_set.begin(); ptr_vector::iterator end = m_theory_set.end(); for (; it != end; ++it) (*it)->pop_scope_eh(num_scopes); del_justifications(m_justifications, s.m_justifications_lim); m_asserted_formulas.pop_scope(num_scopes); m_eq_propagation_queue.reset(); m_th_eq_propagation_queue.reset(); m_th_diseq_propagation_queue.reset(); m_atom_propagation_queue.reset(); m_region.pop_scope(num_scopes); m_scopes.shrink(new_lvl); m_scope_lvl = new_lvl; if (new_lvl < m_base_lvl) { m_base_lvl = new_lvl; m_search_lvl = new_lvl; // Remark: not really necessary } unsigned num_bool_vars = get_num_bool_vars(); // any variable >= num_bool_vars was deleted during backtracking. reinit_clauses(num_scopes, num_bool_vars); reassert_units(units_to_reassert_lim); TRACE("pop_scope_detail", tout << "end of pop_scope: \n"; display(tout);); CASSERT("context", check_invariant()); return num_bool_vars; } void context::pop_scope(unsigned num_scopes) { pop_scope_core(num_scopes); reset_cache_generation(); } void context::pop_to_base_lvl() { SASSERT(m_scope_lvl >= m_base_lvl); if (!at_base_level()) { unsigned num_lvls = m_scope_lvl - m_base_lvl; pop_scope(num_lvls); } SASSERT(m_scope_lvl == m_base_lvl); } /** \brief Simplify the given clause using the assignment. Return true if the clause was already satisfied, and false otherwise. \remark This method should only be invoked if we are at the base level. */ bool context::simplify_clause(clause * cls) { SASSERT(m_scope_lvl == m_base_lvl); unsigned s = cls->get_num_literals(); if (get_assignment(cls->get_literal(0)) == l_true || get_assignment(cls->get_literal(1)) == l_true) { // clause is already satisfied. return true; } literal_buffer simp_lits; unsigned i = 2; unsigned j = i; for(; i < s; i++) { literal l = cls->get_literal(i); switch(get_assignment(l)) { case l_false: if (m_manager.proofs_enabled()) simp_lits.push_back(~l); if (lit_occs_enabled()) m_lit_occs[l.index()].erase(cls); break; case l_undef: cls->set_literal(j, l); j++; break; case l_true: return true; } } if (j < s) { cls->set_num_literals(j); SASSERT(j >= 2); } if (m_manager.proofs_enabled() && !simp_lits.empty()) { SASSERT(m_scope_lvl == m_base_lvl); justification * js = cls->get_justification(); justification * new_js = 0; if (js->in_region()) new_js = mk_justification(unit_resolution_justification(m_region, js, simp_lits.size(), simp_lits.c_ptr())); else new_js = alloc(unit_resolution_justification, js, simp_lits.size(), simp_lits.c_ptr()); cls->set_justification(new_js); } return false; } /** \brief Simplify the given vector of clauses starting at the given position. Return the number of deleted (already satisfied) clauses. */ unsigned context::simplify_clauses(clause_vector & clauses, unsigned starting_at) { unsigned num_del_clauses = 0; clause_vector::iterator it = clauses.begin(); clause_vector::iterator end = clauses.end(); it += starting_at; clause_vector::iterator it2 = it; for(; it != end; ++it) { clause * cls = *it; SASSERT(!cls->in_reinit_stack()); TRACE("simplify_clauses_bug", display_clause(tout, cls); tout << "\n";); if (cls->deleted()) { del_clause(cls); num_del_clauses++; } else if (simplify_clause(cls)) { for (unsigned idx = 0; idx < 2; idx++) { literal l0 = cls->get_literal(idx); b_justification l0_js = get_justification(l0.var()); if (l0_js != null_b_justification && l0_js.get_kind() == b_justification::CLAUSE && l0_js.get_clause() == cls) { // cls is the explanation of l0 // it is safe to replace with axiom, we are at the base level. SASSERT(m_scope_lvl == m_base_lvl); bool_var v0 = l0.var(); if (m_manager.proofs_enabled()) { SASSERT(m_search_lvl == m_base_lvl); literal_buffer simp_lits; unsigned num_lits = cls->get_num_literals(); for(unsigned i = 0; i < num_lits; i++) { if (i != idx) { literal l = cls->get_literal(i); SASSERT(l != l0); simp_lits.push_back(~l); } } justification * cls_js = cls->get_justification(); justification * js = 0; if (!cls_js || cls_js->in_region()) { // If cls_js is 0 or is allocated in a region, then // we can allocate the new justification in a region too. js = mk_justification(unit_resolution_justification(m_region, cls_js, simp_lits.size(), simp_lits.c_ptr())); } else { js = alloc(unit_resolution_justification, cls_js, simp_lits.size(), simp_lits.c_ptr()); // js took ownership of the justification object. cls->set_justification(0); m_justifications.push_back(js); } m_bdata[v0].m_justification = b_justification(js); } else m_bdata[v0].m_justification = b_justification::mk_axiom(); } } del_clause(cls); num_del_clauses++; } else { *it2 = *it; ++it2; m_simp_counter += cls->get_num_literals(); } } clauses.set_end(it2); CASSERT("simplify_clauses", check_invariant()); return num_del_clauses; } /** \brief Simplify the set of clauses if possible (solver is at base level). */ void context::simplify_clauses() { // Remark: when assumptions are used m_scope_lvl >= m_search_lvl > m_base_lvl. Therefore, no simplification is performed. if (m_scope_lvl > m_base_lvl) return; unsigned sz = m_assigned_literals.size(); SASSERT(m_simp_qhead <= sz); if (m_simp_qhead == sz || m_simp_counter > 0) { TRACE("simplify_clauses", tout << "m_simp_qhead: " << m_simp_qhead << " m_simp_counter: " << m_simp_counter << "\n";); return; } if (m_aux_clauses.empty() && m_lemmas.empty()) { TRACE("simplify_clauses", tout << "no clauses to simplify\n";); return; } TRACE("simplify_clauses_detail", tout << "before:\n"; display_clauses(tout, m_lemmas);); SASSERT(check_clauses(m_lemmas)); SASSERT(check_clauses(m_aux_clauses)); IF_VERBOSE(2, verbose_stream() << "(smt.simplifying-clause-set"; verbose_stream().flush();); // m_simp_counter is used to balance the cost of simplify_clause. // // After executing simplify_clauses, the counter will contain // an approximation of the cost of executing simplify_clauses again. // That is, the number of literals that will need to be visited. // // The value of the counter is decremented each time we visit // a variable during propagation. // m_simp_counter = 0; // the field m_simp_qhead is used to check whether there are // new assigned literals at the base level. m_simp_qhead = m_assigned_literals.size(); unsigned num_del_clauses = 0; SASSERT(m_scope_lvl == m_base_lvl); if (m_base_lvl == 0) { num_del_clauses += simplify_clauses(m_aux_clauses, 0); num_del_clauses += simplify_clauses(m_lemmas, 0); } else { scope & s = m_scopes[m_base_lvl - 1]; base_scope & bs = m_base_scopes[m_base_lvl - 1]; num_del_clauses += simplify_clauses(m_aux_clauses, s.m_aux_clauses_lim); num_del_clauses += simplify_clauses(m_lemmas, bs.m_lemmas_lim); } TRACE("simp_counter", tout << "simp_counter: " << m_simp_counter << " scope_lvl: " << m_scope_lvl << "\n";); IF_VERBOSE(2, verbose_stream() << " :num-deleted-clauses " << num_del_clauses << ")" << std::endl;); TRACE("simplify_clauses_detail", tout << "after:\n"; display_clauses(tout, m_lemmas);); SASSERT(check_clauses(m_lemmas) && check_clauses(m_aux_clauses)); } struct clause_lt { bool operator()(clause * cls1, clause * cls2) const { return cls1->get_activity() > cls2->get_activity(); } }; /** \brief Delete low activity lemmas */ inline void context::del_inactive_lemmas() { if (m_fparams.m_lemma_gc_half) del_inactive_lemmas1(); else del_inactive_lemmas2(); m_num_conflicts_since_lemma_gc = 0; if (m_fparams.m_lemma_gc_strategy == LGC_GEOMETRIC) m_lemma_gc_threshold = static_cast(m_lemma_gc_threshold * m_fparams.m_lemma_gc_factor); } /** \brief Delete (approx.) half of low activity lemmas */ void context::del_inactive_lemmas1() { unsigned sz = m_lemmas.size(); unsigned start_at = m_base_lvl == 0 ? 0 : m_base_scopes[m_base_lvl - 1].m_lemmas_lim; SASSERT(start_at <= sz); if (start_at + m_fparams.m_recent_lemmas_size >= sz) return; IF_VERBOSE(2, verbose_stream() << "(smt.delete-inactive-lemmas"; verbose_stream().flush();); SASSERT (m_fparams.m_recent_lemmas_size < sz); unsigned end_at = sz - m_fparams.m_recent_lemmas_size; SASSERT(start_at < end_at); std::stable_sort(m_lemmas.begin() + start_at, m_lemmas.begin() + end_at, clause_lt()); unsigned start_del_at = (start_at + end_at) / 2; unsigned i = start_del_at; unsigned j = i; unsigned num_del_cls = 0; TRACE("del_inactive_lemmas", tout << "sz: " << sz << ", start_at: " << start_at << ", end_at: " << end_at << ", start_del_at: " << start_del_at << "\n";); for (; i < end_at; i++) { clause * cls = m_lemmas[i]; if (can_delete(cls)) { TRACE("del_inactive_lemmas", tout << "deleting: "; display_clause(tout, cls); tout << ", activity: " << cls->get_activity() << "\n";); del_clause(cls); num_del_cls++; } else { m_lemmas[j] = cls; j++; } } // keep recent clauses for (; i < sz; i++) { clause * cls = m_lemmas[i]; if (cls->deleted() && can_delete(cls)) { del_clause(cls); num_del_cls++; } else { m_lemmas[j] = cls; j++; } } m_lemmas.shrink(j); if (m_fparams.m_clause_decay > 1) { // rescale activity for (i = start_at; i < j; i++) { clause * cls = m_lemmas[i]; cls->set_activity(cls->get_activity() / m_fparams.m_clause_decay); } } IF_VERBOSE(2, verbose_stream() << " :num-deleted-clauses " << num_del_cls << ")" << std::endl;); } /** \brief More sophisticated version of del_inactive_lemmas. Here the lemmas are divided in two groups (old and new) based on the value of m_new_old_ratio parameter. A clause is deleted/retained based on its activity and relevancy. Clauses with several unassigned literals are considered less relevant. The threshold used for activity and relevancy depends on which group the clauses is in. */ void context::del_inactive_lemmas2() { IF_VERBOSE(2, verbose_stream() << "(smt.delete-inactive-clauses "; verbose_stream().flush();); unsigned sz = m_lemmas.size(); unsigned start_at = m_base_lvl == 0 ? 0 : m_base_scopes[m_base_lvl - 1].m_lemmas_lim; SASSERT(start_at <= sz); unsigned real_sz = sz - start_at; // idx of the first learned clause considered "new" unsigned new_first_idx = start_at + (real_sz / m_fparams.m_new_old_ratio) * (m_fparams.m_new_old_ratio - 1); SASSERT(new_first_idx <= sz); unsigned i = start_at; unsigned j = i; unsigned num_del_cls = 0; for (; i < sz; i++) { clause * cls = m_lemmas[i]; if (can_delete(cls)) { if (cls->deleted()) { // clause is already marked for deletion del_clause(cls); num_del_cls++; continue; } // A clause is deleted if it has low activity and the number of unknowns is greater than a threshold. // The activity threshold depends on how old the clause is. unsigned act_threshold = m_fparams.m_old_clause_activity - (m_fparams.m_old_clause_activity - m_fparams.m_new_clause_activity) * ((i - start_at) / real_sz); if (cls->get_activity() < act_threshold) { unsigned rel_threshold = (i >= new_first_idx ? m_fparams.m_new_clause_relevancy : m_fparams.m_old_clause_relevancy); if (more_than_k_unassigned_literals(cls, rel_threshold)) { del_clause(cls); num_del_cls++; continue; } } } m_lemmas[j] = cls; j++; cls->set_activity(static_cast(cls->get_activity() / m_fparams.m_inv_clause_decay)); } SASSERT(j <= sz); m_lemmas.shrink(j); IF_VERBOSE(2, verbose_stream() << " :num-deleted-clauses " << num_del_cls << ")" << std::endl;); } /** \brief Return true if "cls" has more than (or equal to) k unassigned literals. */ bool context::more_than_k_unassigned_literals(clause * cls, unsigned k) { SASSERT(k > 0); unsigned num_lits = cls->get_num_literals(); for(unsigned i = 0; i < num_lits; i++) { literal l = cls->get_literal(i); if (get_assignment(l) == l_undef) { k--; if (k == 0) { return true; } } } return false; } void context::register_plugin(simplifier_plugin * s) { SASSERT(!already_internalized()); SASSERT(m_scope_lvl == 0); m_asserted_formulas.register_simplifier_plugin(s); } #ifdef Z3DEBUG /** \brief Return true if a symbol of the given theory was already internalized. */ bool context::already_internalized_theory(theory * th) const { return already_internalized_theory_core(th, m_b_internalized_stack) || already_internalized_theory_core(th, m_e_internalized_stack); } /** \brief Auxiliary method for #already_internalized_theory. */ bool context::already_internalized_theory_core(theory * th, expr_ref_vector const & s) const { expr_mark visited; family_id fid = th->get_id(); unsigned sz = s.size(); for (unsigned i = 0; i < sz; i++) { expr * n = s.get(i); if (uses_theory(n, fid, visited)) return true; } return false; } #endif void context::register_plugin(theory * th) { if (m_theories.get_plugin(th->get_family_id()) != 0) { dealloc(th); return; // context already has a theory for the given family id. } SASSERT(std::find(m_theory_set.begin(), m_theory_set.end(), th) == m_theory_set.end()); SASSERT(!already_internalized_theory(th)); th->init(this); m_theories.register_plugin(th); m_theory_set.push_back(th); { #ifdef Z3DEBUG // It is unsafe to invoke push_trail from the method push_scope_eh. flet l(m_trail_enabled, false); #endif for (unsigned i = 0; i < m_scope_lvl; ++i) th->push_scope_eh(); } } void context::push() { TRACE("trigger_bug", tout << "context::push()\n";); pop_to_base_lvl(); setup_context(false); bool was_consistent = !inconsistent(); internalize_assertions(); // internalize assertions before invoking m_asserted_formulas.push_scope propagate(); if (was_consistent && inconsistent()) { // logical context became inconsistent during user PUSH bool res = resolve_conflict(); // build the proof SASSERT(!res); } push_scope(); m_base_scopes.push_back(base_scope()); base_scope & bs = m_base_scopes.back(); bs.m_lemmas_lim = m_lemmas.size(); bs.m_inconsistent = inconsistent(); bs.m_simp_qhead_lim = m_simp_qhead; m_base_lvl++; m_search_lvl++; // Not really necessary. But, it is useful to enforce the invariant m_search_lvl >= m_base_lvl SASSERT(m_base_lvl <= m_scope_lvl); } void context::pop(unsigned num_scopes) { SASSERT (num_scopes > 0); pop_to_base_lvl(); pop_scope(num_scopes); } /** \brief Free memory allocated by logical context. */ void context::flush() { flet l1(m_flushing, true); TRACE("flush", tout << "m_scope_lvl: " << m_scope_lvl << "\n";); m_relevancy_propagator = 0; m_model_generator->reset(); ptr_vector::iterator it = m_theory_set.begin(); ptr_vector::iterator end = m_theory_set.end(); for (; it != end; ++it) (*it)->flush_eh(); undo_trail_stack(0); m_qmanager = 0; del_clauses(m_aux_clauses, 0); del_clauses(m_lemmas, 0); del_justifications(m_justifications, 0); if (m_is_diseq_tmp) { m_is_diseq_tmp->del_eh(m_manager, false); m_manager.dec_ref(m_is_diseq_tmp->get_owner()); enode::del_dummy(m_is_diseq_tmp); m_is_diseq_tmp = 0; } std::for_each(m_almost_cg_tables.begin(), m_almost_cg_tables.end(), delete_proc()); } void context::assert_expr_core(expr * e, proof * pr) { if (get_cancel_flag()) return; SASSERT(is_well_sorted(m_manager, e)); TRACE("begin_assert_expr", tout << mk_pp(e, m_manager) << "\n";); TRACE("begin_assert_expr_ll", tout << mk_ll_pp(e, m_manager) << "\n";); pop_to_base_lvl(); if (pr == 0) m_asserted_formulas.assert_expr(e); else m_asserted_formulas.assert_expr(e, pr); TRACE("end_assert_expr_ll", ast_mark m; m_asserted_formulas.display_ll(tout, m);); } void context::assert_expr(expr * e) { assert_expr(e, 0); } void context::assert_expr(expr * e, proof * pr) { timeit tt(get_verbosity_level() >= 100, "smt.simplifying"); assert_expr_core(e, pr); } bool context::reduce_assertions() { if (!m_asserted_formulas.inconsistent()) { SASSERT(at_base_level()); m_asserted_formulas.reduce(); } return m_asserted_formulas.inconsistent(); } void context::internalize_assertions() { if (get_cancel_flag()) return; TRACE("internalize_assertions", tout << "internalize_assertions()...\n";); timeit tt(get_verbosity_level() >= 100, "smt.preprocessing"); reduce_assertions(); if (!m_asserted_formulas.inconsistent()) { unsigned sz = m_asserted_formulas.get_num_formulas(); unsigned qhead = m_asserted_formulas.get_qhead(); while (qhead < sz) { expr * f = m_asserted_formulas.get_formula(qhead); proof * pr = m_asserted_formulas.get_formula_proof(qhead); internalize_assertion(f, pr, 0); qhead++; } m_asserted_formulas.commit(); } if (m_asserted_formulas.inconsistent() && !inconsistent()) { proof * pr = m_asserted_formulas.get_inconsistency_proof(); if (pr == 0) { set_conflict(b_justification::mk_axiom()); } else { set_conflict(mk_justification(justification_proof_wrapper(*this, pr))); m_unsat_proof = pr; } } TRACE("internalize_assertions", tout << "after internalize_assertions()...\n"; tout << "inconsistent: " << inconsistent() << "\n";); } bool is_valid_assumption(ast_manager & m, expr * assumption) { if (!m.is_bool(assumption)) return false; if (is_uninterp_const(assumption)) return true; if (m.is_not(assumption) && is_uninterp_const(to_app(assumption)->get_arg(0))) return true; return false; } /** \brief Assumptions must be uninterpreted boolean constants (aka propositional variables). */ bool context::validate_assumptions(unsigned num_assumptions, expr * const * assumptions) { for (unsigned i = 0; i < num_assumptions; i++) { SASSERT(assumptions[i]); if (!is_valid_assumption(m_manager, assumptions[i])) { warning_msg("an assumption must be a propositional variable or the negation of one"); return false; } } return true; } void context::init_assumptions(unsigned num_assumptions, expr * const * assumptions) { reset_assumptions(); m_literal2assumption.reset(); m_unsat_core.reset(); if (num_assumptions > 0) { // We must give a chance to the theories to propagate before we create a new scope... propagate(); // Internal backtracking scopes (created with push_scope()) must only be created when we are // in a consistent context. if (inconsistent()) return; push_scope(); for (unsigned i = 0; i < num_assumptions; i++) { expr * curr_assumption = assumptions[i]; SASSERT(is_valid_assumption(m_manager, curr_assumption)); proof * pr = m_manager.mk_asserted(curr_assumption); internalize_assertion(curr_assumption, pr, 0); literal l = get_literal(curr_assumption); m_literal2assumption.insert(l.index(), curr_assumption); // mark_as_relevant(l); <<< not needed // internalize_assertion marked l as relevant. SASSERT(is_relevant(l)); TRACE("assumptions", tout << l << ":" << mk_pp(curr_assumption, m_manager) << "\n";); if (m_manager.proofs_enabled()) assign(l, mk_justification(justification_proof_wrapper(*this, pr))); else assign(l, b_justification::mk_axiom()); m_assumptions.push_back(l); get_bdata(l.var()).m_assumption = true; } } m_search_lvl = m_scope_lvl; SASSERT(!(num_assumptions > 0) || m_search_lvl > m_base_lvl); SASSERT(!(num_assumptions == 0) || m_search_lvl == m_base_lvl); } void context::reset_assumptions() { literal_vector::iterator it = m_assumptions.begin(); literal_vector::iterator end = m_assumptions.end(); for (; it != end; ++it) get_bdata(it->var()).m_assumption = false; m_assumptions.reset(); } void context::mk_unsat_core() { SASSERT(inconsistent()); if (!tracking_assumptions()) { SASSERT(m_assumptions.empty()); return; } obj_hashtable already_found_assumptions; literal_vector::const_iterator it = m_conflict_resolution->begin_unsat_core(); literal_vector::const_iterator end = m_conflict_resolution->end_unsat_core(); for (; it != end; ++it) { literal l = *it; TRACE("unsat_core_bug", tout << "answer literal: " << l << "\n";); SASSERT(get_bdata(l.var()).m_assumption); if (!m_literal2assumption.contains(l.index())) l.neg(); SASSERT(m_literal2assumption.contains(l.index())); expr * a = m_literal2assumption[l.index()]; if (!already_found_assumptions.contains(a)) { already_found_assumptions.insert(a); m_unsat_core.push_back(a); } } reset_assumptions(); pop_to_base_lvl(); // undo the push_scope() performed by init_assumptions m_search_lvl = m_base_lvl; std::sort(m_unsat_core.c_ptr(), m_unsat_core.c_ptr() + m_unsat_core.size(), ast_lt_proc()); TRACE("unsat_core_bug", tout << "unsat core:\n"; unsigned sz = m_unsat_core.size(); for (unsigned i = 0; i < sz; i++) { tout << mk_pp(m_unsat_core.get(i), m_manager) << "\n"; }); validate_unsat_core(); } /** \brief Make some checks before starting the search. Return true if succeeded. */ bool context::check_preamble(bool reset_cancel) { if (m_manager.has_trace_stream()) m_manager.trace_stream() << "[begin-check] " << m_scope_lvl << "\n"; if (reset_cancel) { m_cancel_flag = false; m_asserted_formulas.set_cancel_flag(false); } if (memory::above_high_watermark()) { m_last_search_failure = MEMOUT; return false; } return true; } /** \brief Execute some finalization code after performing the search. */ lbool context::check_finalize(lbool r) { TRACE("after_search", display(tout << "result: " << r << "\n");); display_profile(verbose_stream()); if (r == l_true && get_cancel_flag()) { r = l_undef; } return r; } /** \brief Setup the logical context based on the current set of asserted formulas and execute the check command. \remark A logical context can only be configured at scope level 0, and before internalizing any formulas. */ lbool context::setup_and_check(bool reset_cancel) { if (!check_preamble(reset_cancel)) return l_undef; SASSERT(m_scope_lvl == 0); SASSERT(!m_setup.already_configured()); setup_context(m_fparams.m_auto_config); internalize_assertions(); lbool r = l_undef; if (m_asserted_formulas.inconsistent()) { r = l_false; } else { TRACE("after_internalization", display(tout);); if (inconsistent()) { bool res = resolve_conflict(); // build the proof SASSERT(!res); r = l_false; } else { r = search(); } } r = check_finalize(r); return r; } config_mode context::get_config_mode(bool use_static_features) const { if (!m_fparams.m_auto_config) return CFG_BASIC; if (use_static_features) return CFG_AUTO; return CFG_LOGIC; } void context::setup_context(bool use_static_features) { if (m_setup.already_configured()) return; m_setup(get_config_mode(use_static_features)); setup_components(); #ifndef _EXTERNAL_RELEASE if (m_fparams.m_display_installed_theories) { std::cout << "(theories"; ptr_vector::iterator it = m_theory_set.begin(); ptr_vector::iterator end = m_theory_set.end(); for (; it != end; ++it) { std::cout << " " << (*it)->get_name(); } std::cout << ")" << std::endl; } #endif } void context::setup_components() { m_asserted_formulas.setup(); m_random.set_seed(m_fparams.m_random_seed); m_dyn_ack_manager.setup(); m_conflict_resolution->setup(); if (!relevancy()) m_fparams.m_relevancy_lemma = false; // setup all the theories ptr_vector::iterator it = m_theory_set.begin(); ptr_vector::iterator end = m_theory_set.end(); for (; it != end; ++it) (*it)->setup(); } lbool context::check(unsigned num_assumptions, expr * const * assumptions, bool reset_cancel) { m_stats.m_num_checks++; TRACE("check_bug", tout << "STARTING check(num_assumptions, assumptions)\n"; tout << "inconsistent: " << inconsistent() << ", m_unsat_core.empty(): " << m_unsat_core.empty() << "\n"; m_asserted_formulas.display(tout); tout << "-----------------------\n"; display(tout);); if (!m_unsat_core.empty()) m_unsat_core.reset(); if (!check_preamble(reset_cancel)) return l_undef; if (!validate_assumptions(num_assumptions, assumptions)) return l_undef; TRACE("check_bug", tout << "inconsistent: " << inconsistent() << ", m_unsat_core.empty(): " << m_unsat_core.empty() << "\n";); TRACE("unsat_core_bug", for (unsigned i = 0; i < num_assumptions; i++) { tout << mk_pp(assumptions[i], m_manager) << "\n";}); pop_to_base_lvl(); TRACE("before_search", display(tout);); SASSERT(at_base_level()); lbool r = l_undef; if (inconsistent()) { r = l_false; } else { setup_context(false); internalize_assertions(); TRACE("after_internalize_assertions", display(tout);); if (m_asserted_formulas.inconsistent()) { r = l_false; } else { init_assumptions(num_assumptions, assumptions); TRACE("after_internalization", display(tout);); if (inconsistent()) { bool res = resolve_conflict(); // build the proof SASSERT(!res); mk_unsat_core(); r = l_false; } else { r = search(); if (r == l_false) mk_unsat_core(); } } } r = check_finalize(r); return r; } void context::init_search() { ptr_vector::iterator it = m_theory_set.begin(); ptr_vector::iterator end = m_theory_set.end(); for (; it != end; ++it) (*it)->init_search_eh(); m_qmanager->init_search_eh(); m_assumption_core.reset(); m_incomplete_theories.reset(); m_num_conflicts = 0; m_num_conflicts_since_restart = 0; m_num_conflicts_since_lemma_gc = 0; m_restart_threshold = m_fparams.m_restart_initial; m_restart_outer_threshold = m_fparams.m_restart_initial; m_agility = 0.0; m_luby_idx = 1; m_lemma_gc_threshold = m_fparams.m_lemma_gc_initial; m_last_search_failure = OK; m_unsat_proof = 0; m_unsat_core .reset(); m_dyn_ack_manager .init_search_eh(); m_final_check_idx = 0; m_phase_default = false; m_case_split_queue ->init_search_eh(); m_next_progress_sample = 0; TRACE("literal_occ", display_literal_num_occs(tout);); m_timer.start(); } void context::end_search() { m_case_split_queue ->end_search_eh(); } void context::inc_limits() { if (m_num_conflicts_since_restart >= m_restart_threshold) { switch (m_fparams.m_restart_strategy) { case RS_GEOMETRIC: m_restart_threshold = static_cast(m_restart_threshold * m_fparams.m_restart_factor); break; case RS_IN_OUT_GEOMETRIC: m_restart_threshold = static_cast(m_restart_threshold * m_fparams.m_restart_factor); if (m_restart_threshold > m_restart_outer_threshold) { m_restart_threshold = m_fparams.m_restart_initial; m_restart_outer_threshold = static_cast(m_restart_outer_threshold * m_fparams.m_restart_factor); } break; case RS_LUBY: m_luby_idx ++; m_restart_threshold = static_cast(get_luby(m_luby_idx) * m_fparams.m_restart_initial); break; case RS_FIXED: break; case RS_ARITHMETIC: m_restart_threshold = static_cast(m_restart_threshold + m_fparams.m_restart_factor); break; default: break; } } m_num_conflicts_since_restart = 0; } struct context::scoped_mk_model { context & m_ctx; scoped_mk_model(context & ctx):m_ctx(ctx) { m_ctx.m_proto_model = 0; m_ctx.m_model = 0; } ~scoped_mk_model() { if (m_ctx.m_proto_model.get() != 0) { m_ctx.m_model = m_ctx.m_proto_model->mk_model(); m_ctx.m_proto_model = 0; // proto_model is not needed anymore. } } }; lbool context::search() { #ifndef _EXTERNAL_RELEASE if (m_fparams.m_abort_after_preproc) { exit(1); } #endif timeit tt(get_verbosity_level() >= 100, "smt.stats"); scoped_mk_model smk(*this); SASSERT(at_search_level()); TRACE("search", display(tout); display_enodes_lbls(tout);); TRACE("search_detail", m_asserted_formulas.display(tout);); init_search(); flet l(m_searching, true); TRACE("after_init_search", display(tout);); IF_VERBOSE(2, verbose_stream() << "(smt.searching)\n";); TRACE("search_lite", tout << "searching...\n";); lbool status = l_undef; unsigned curr_lvl = m_scope_lvl; while (true) { SASSERT(!inconsistent()); status = bounded_search(); TRACE("search_bug", tout << "status: " << status << ", inconsistent: " << inconsistent() << "\n";); TRACE("assigned_literals_per_lvl", display_num_assigned_literals_per_lvl(tout); tout << ", num_assigned: " << m_assigned_literals.size() << "\n";); if (m_last_search_failure != OK) { if (status != l_false) { // build candidate model before returning mk_proto_model(status); // status = l_undef; } break; } bool force_restart = false; if (status == l_false) { break; } else if (status == l_true) { SASSERT(!inconsistent()); mk_proto_model(l_true); // possible outcomes DONE l_true, DONE l_undef, CONTINUE quantifier_manager::check_model_result cmr = m_qmanager->check_model(m_proto_model.get(), m_model_generator->get_root2value()); if (cmr == quantifier_manager::SAT) { // done break; } if (cmr == quantifier_manager::UNKNOWN) { // giving up m_last_search_failure = QUANTIFIERS; status = l_undef; break; } status = l_undef; force_restart = true; } SASSERT(status == l_undef); inc_limits(); if (force_restart || !m_fparams.m_restart_adaptive || m_agility < m_fparams.m_restart_agility_threshold) { SASSERT(!inconsistent()); IF_VERBOSE(1, verbose_stream() << "(smt.restarting :propagations " << m_stats.m_num_propagations << " :decisions " << m_stats.m_num_decisions << " :conflicts " << m_stats.m_num_conflicts << " :restart " << m_restart_threshold; if (m_fparams.m_restart_strategy == RS_IN_OUT_GEOMETRIC) { verbose_stream() << " :restart-outer " << m_restart_outer_threshold; } if (m_fparams.m_restart_adaptive) { verbose_stream() << " :agility " << m_agility; } verbose_stream() << ")" << std::endl; verbose_stream().flush();); // execute the restart m_stats.m_num_restarts++; if (m_scope_lvl > curr_lvl) { pop_scope(m_scope_lvl - curr_lvl); SASSERT(at_search_level()); } ptr_vector::iterator it = m_theory_set.begin(); ptr_vector::iterator end = m_theory_set.end(); for (; it != end && !inconsistent(); ++it) (*it)->restart_eh(); TRACE("mbqi_bug_detail", tout << "before instantiating quantifiers...\n";); if (!inconsistent()) { m_qmanager->restart_eh(); } if (inconsistent()) { VERIFY(!resolve_conflict()); status = l_false; break; } } if (m_fparams.m_simplify_clauses) simplify_clauses(); if (m_fparams.m_lemma_gc_strategy == LGC_AT_RESTART) del_inactive_lemmas(); } TRACE("search_lite", tout << "status: " << status << "\n";); TRACE("guessed_literals", expr_ref_vector guessed_lits(m_manager); get_guessed_literals(guessed_lits); unsigned sz = guessed_lits.size(); for (unsigned i = 0; i < sz; i++) { tout << mk_pp(guessed_lits.get(i), m_manager) << "\n"; }); end_search(); return status; } void context::tick(unsigned & counter) const { counter++; if (counter > m_fparams.m_tick) { IF_VERBOSE(3, verbose_stream() << "(smt.working"; verbose_stream() << " :conflicts " << m_num_conflicts; // verbose_stream() << " lemma avg. activity: " << get_lemma_avg_activity(); if (m_fparams.m_restart_adaptive) verbose_stream() << " :agility " << m_agility; verbose_stream() << ")" << std::endl; verbose_stream().flush();); TRACE("assigned_literals_per_lvl", display_num_assigned_literals_per_lvl(tout); tout << "\n";); counter = 0; } } lbool context::bounded_search() { SASSERT(!inconsistent()); unsigned counter = 0; TRACE("bounded_search", tout << "starting bounded search...\n";); while (true) { while (!propagate()) { TRACE_CODE({ static bool first_propagate = true; if (first_propagate) { first_propagate = false; TRACE("after_first_propagate", display(tout);); } }); tick(counter); if (!resolve_conflict()) return l_false; SASSERT(m_scope_lvl >= m_base_lvl); if (!inconsistent()) { if (resource_limits_exceeded()) return l_undef; if (get_cancel_flag()) return l_undef; if (m_num_conflicts_since_restart > m_restart_threshold && m_scope_lvl - m_base_lvl > 2) { TRACE("search_bug", tout << "bounded-search return undef, inconsistent: " << inconsistent() << "\n";); return l_undef; // restart } if (m_num_conflicts > m_fparams.m_max_conflicts) { TRACE("search_bug", tout << "bounded-search return undef, inconsistent: " << inconsistent() << "\n";); m_last_search_failure = NUM_CONFLICTS; return l_undef; } } if (m_num_conflicts_since_lemma_gc > m_lemma_gc_threshold && (m_fparams.m_lemma_gc_strategy == LGC_FIXED || m_fparams.m_lemma_gc_strategy == LGC_GEOMETRIC)) { del_inactive_lemmas(); } m_dyn_ack_manager.propagate_eh(); CASSERT("dyn_ack", check_clauses(m_lemmas) && check_clauses(m_aux_clauses)); } if (resource_limits_exceeded() && !inconsistent()) { return l_undef; } if (get_cancel_flag()) return l_undef; if (m_base_lvl == m_scope_lvl && m_fparams.m_simplify_clauses) simplify_clauses(); if (!decide()) { final_check_status fcs = final_check(); TRACE("final_check_result", tout << "fcs: " << fcs << " last_search_failure: " << m_last_search_failure << "\n";); switch (fcs) { case FC_DONE: return l_true; case FC_CONTINUE: break; case FC_GIVEUP: return l_undef; } } if (resource_limits_exceeded() && !inconsistent()) { return l_undef; } } } bool context::resource_limits_exceeded() { if (m_searching) { // Some of the flags only make sense to check when searching. // For example, the timer is only started in init_search(). if (m_last_search_failure != OK) return true; if (m_timer.ms_timeout(m_fparams.m_timeout)) { m_last_search_failure = TIMEOUT; return true; } if (m_progress_callback) { m_progress_callback->fast_progress_sample(); if (m_fparams.m_progress_sampling_freq > 0 && m_timer.ms_timeout(m_next_progress_sample + 1)) { m_progress_callback->slow_progress_sample(); m_next_progress_sample = (unsigned)(m_timer.get_seconds() * 1000) + m_fparams.m_progress_sampling_freq; } } } if (get_cancel_flag()) { m_last_search_failure = CANCELED; return true; } if (memory::above_high_watermark()) { m_last_search_failure = MEMOUT; return true; } return false; } final_check_status context::final_check() { TRACE("final_check", tout << "final_check inconsistent: " << inconsistent() << "\n"; display(tout); display_normalized_enodes(tout);); CASSERT("relevancy", check_relevancy()); if (m_fparams.m_model_on_final_check) { mk_proto_model(l_undef); model_pp(std::cout, *m_proto_model); std::cout << "END_OF_MODEL\n"; std::cout.flush(); } m_stats.m_num_final_checks++; final_check_status ok = m_qmanager->final_check_eh(false); if (ok != FC_DONE) return ok; m_incomplete_theories.reset(); unsigned old_idx = m_final_check_idx; unsigned num_th = m_theory_set.size(); unsigned range = num_th + 1; final_check_status result = FC_DONE; failure f = OK; do { TRACE("final_check_step", tout << "processing: " << m_final_check_idx << ", result: " << result << "\n";); final_check_status ok; if (m_final_check_idx < num_th) { theory * th = m_theory_set[m_final_check_idx]; IF_VERBOSE(100, verbose_stream() << "(smt.final-check \"" << th->get_name() << "\")\n";); ok = th->final_check_eh(); TRACE("final_check_step", tout << "final check '" << th->get_name() << " ok: " << ok << " inconsistent " << inconsistent() << "\n";); if (ok == FC_GIVEUP) { f = THEORY; m_incomplete_theories.push_back(th); } } else { ok = m_qmanager->final_check_eh(true); TRACE("final_check_step", tout << "quantifier ok: " << ok << " " << "inconsistent " << inconsistent() << "\n";); } m_final_check_idx = (m_final_check_idx + 1) % range; // IF_VERBOSE(1000, verbose_stream() << "final check status: " << ok << "\n";); switch (ok) { case FC_DONE: break; case FC_GIVEUP: result = FC_GIVEUP; break; case FC_CONTINUE: return FC_CONTINUE; break; } } while (m_final_check_idx != old_idx); TRACE("final_check_step", tout << "result: " << result << "\n";); if (can_propagate()) { TRACE("final_check_step", tout << "can propagate: continue...\n";); return FC_CONTINUE; } SASSERT(result != FC_DONE || check_th_diseq_propagation()); TRACE("final_check_step", tout << "RESULT final_check: " << result << "\n";); if (result == FC_GIVEUP && f != OK) m_last_search_failure = f; return result; } void context::check_proof(proof * pr) { if (m_manager.proofs_enabled() && m_fparams.m_check_proof) { proof_checker pf(m_manager); expr_ref_vector side_conditions(m_manager); pf.check(pr, side_conditions); } } void context::forget_phase_of_vars_in_current_level() { unsigned head = m_scope_lvl == 0 ? 0 : m_scopes[m_scope_lvl - 1].m_assigned_literals_lim; unsigned sz = m_assigned_literals.size(); for (unsigned i = head; i < sz; i++) { literal l = m_assigned_literals[i]; bool_var v = l.var(); TRACE("forget_phase", tout << "forgeting phase of l: " << l << "\n";); m_bdata[v].m_phase_available = false; } } bool context::resolve_conflict() { m_stats.m_num_conflicts++; m_num_conflicts ++; m_num_conflicts_since_restart ++; m_num_conflicts_since_lemma_gc ++; switch (m_conflict.get_kind()) { case b_justification::CLAUSE: case b_justification::BIN_CLAUSE: m_stats.m_num_sat_conflicts++; break; default: break; } if (m_fparams.m_phase_selection == PS_CACHING_CONSERVATIVE || m_fparams.m_phase_selection == PS_CACHING_CONSERVATIVE2) forget_phase_of_vars_in_current_level(); m_atom_propagation_queue.reset(); m_eq_propagation_queue.reset(); m_th_eq_propagation_queue.reset(); m_th_diseq_propagation_queue.reset(); if (m_conflict_resolution->resolve(m_conflict, m_not_l)) { unsigned new_lvl = m_conflict_resolution->get_new_scope_lvl(); unsigned num_lits = m_conflict_resolution->get_lemma_num_literals(); literal * lits = m_conflict_resolution->get_lemma_literals(); SASSERT(num_lits > 0); unsigned conflict_lvl = get_assign_level(lits[0]); SASSERT(conflict_lvl <= m_scope_lvl); // When num_lits == 1, then the default behavior is to go // to base-level. If the problem has quantifiers, it may be // too expensive to do that, since all instances will need to // be recreated. If that is the case, I store the assertions in // a special vector and keep reasserting whenever I backtrack. // Moreover, I backtrack only one level. bool delay_forced_restart = m_fparams.m_delay_units && internalized_quantifiers() && num_lits == 1 && conflict_lvl > m_search_lvl + 1 && !m_manager.proofs_enabled() && m_units_to_reassert.size() < m_fparams.m_delay_units_threshold; if (delay_forced_restart) { new_lvl = conflict_lvl - 1; } // Some of the literals/enodes of the conflict clause will be destroyed during // backtracking, and will need to be recreated. However, I want to keep // the generation number for enodes that are going to be recreated. See // comment in cache_generation(unsigned). if (m_conflict_resolution->get_lemma_intern_lvl() > new_lvl) cache_generation(num_lits, lits, new_lvl); SASSERT(new_lvl < m_scope_lvl); TRACE("resolve_conflict_bug", tout << "m_scope_lvl: " << m_scope_lvl << ", new_lvl: " << new_lvl << ", lemma_intern_lvl: " << m_conflict_resolution->get_lemma_intern_lvl() << "\n"; tout << "num_lits: " << num_lits << "\n"; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; tout << l << " "; display_literal(tout, l); tout << ", ilvl: " << get_intern_level(l.var()) << "\n" << mk_pp(bool_var2expr(l.var()), m_manager) << "\n"; }); if (m_manager.has_trace_stream()) { m_manager.trace_stream() << "[conflict] "; display_literals(m_manager.trace_stream(), num_lits, lits); m_manager.trace_stream() << "\n"; } #ifdef Z3DEBUG expr_ref_vector expr_lits(m_manager); svector expr_signs; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; SASSERT(get_assignment(l) == l_false); expr_lits.push_back(bool_var2expr(l.var())); expr_signs.push_back(l.sign()); } #endif proof * pr = 0; if (m_manager.proofs_enabled()) { pr = m_conflict_resolution->get_lemma_proof(); // check_proof(pr); TRACE("context_proof", tout << mk_ll_pp(pr, m_manager);); TRACE("context_proof_hack", static ast_mark visited; ast_ll_pp(tout, m_manager, pr, visited);); } // I invoke pop_scope_core instead of pop_scope because I don't want // to reset cached generations... I need them to rebuild the literals // of the new conflict clause. unsigned num_bool_vars = pop_scope_core(m_scope_lvl - new_lvl); SASSERT(m_scope_lvl == new_lvl); // the logical context may still be in conflict after // clauses are reinitialized in pop_scope. if (m_conflict_resolution->get_lemma_intern_lvl() > m_scope_lvl) { expr * * atoms = m_conflict_resolution->get_lemma_atoms(); for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; if (l.var() >= static_cast(num_bool_vars)) { // This boolean variable was deleted during backtracking, it need to be recreated. // Remark: atom may be a negative literal (not a). Z3 creates Boolean variables for not-gates that // are nested in terms. Example: let f be a uninterpreted function from Bool -> Int. // Then, given the term (f (not a)), Z3 will create a boolean variable for (not a) when internalizing (f (not a)). expr * atom = atoms[i]; internalize(atom, true); // If atom is actually a negative literal (not a), then get_bool_var will return return null_bool_var. // Thus, we must use get_literal instead. This was a bug/crash in Z3 <= 4.0 literal new_l = get_literal(atom); if (l.sign()) new_l.neg(); // For reference, here is the buggy version // BEGIN BUGGY VERSION // bool_var v = get_bool_var(atom); // CTRACE("resolve_conflict_crash", v == null_bool_var, tout << mk_ismt2_pp(atom, m_manager) << "\n";); // SASSERT(v != null_bool_var); // literal new_l = literal(v, l.sign()); // END BUGGY VERSION lits[i] = new_l; } } } // Resetting the cache manually because I did not invoke pop_scope, but pop_scope_core reset_cache_generation(); TRACE("resolve_conflict_bug", tout << "AFTER m_scope_lvl: " << m_scope_lvl << ", new_lvl: " << new_lvl << ", lemma_intern_lvl: " << m_conflict_resolution->get_lemma_intern_lvl() << "\n"; tout << "num_lits: " << num_lits << "\n"; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; tout << l << " "; display_literal(tout, l); tout << ", ilvl: " << get_intern_level(l.var()) << "\n" << mk_pp(bool_var2expr(l.var()), m_manager) << "\n"; }); #ifdef Z3DEBUG for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; if (m_manager.is_not(expr_lits.get(i))) { // the sign must have flipped when internalizing expr * real_atom = to_app(expr_lits.get(i))->get_arg(0); SASSERT(real_atom == bool_var2expr(l.var())); SASSERT(expr_signs[i] != l.sign()); } else { SASSERT(expr_lits.get(i) == bool_var2expr(l.var())); SASSERT(expr_signs[i] == l.sign()); } } #endif justification * js = 0; if (m_manager.proofs_enabled()) { js = alloc(justification_proof_wrapper, *this, pr, false); } #if 0 { static unsigned counter = 0; static uint64 total = 0; static unsigned max = 0; counter++; total += num_lits; if (num_lits > max) { max = num_lits; } if (counter % 1000 == 0) { verbose_stream() << "[sat] avg. clause size: " << ((double) total/(double) counter) << ", max: " << max << std::endl; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; verbose_stream() << l.sign() << " " << mk_pp(bool_var2expr(l.var()), m_manager) << "\n"; } } } #endif mk_clause(num_lits, lits, js, CLS_LEARNED); if (delay_forced_restart) { SASSERT(num_lits == 1); expr * unit = bool_var2expr(lits[0].var()); bool unit_sign = lits[0].sign(); m_units_to_reassert.push_back(unit); m_units_to_reassert_sign.push_back(unit_sign); TRACE("reassert_units", tout << "asserting #" << unit->get_id() << " " << unit_sign << " @ " << m_scope_lvl << "\n";); } m_conflict_resolution->release_lemma_atoms(); TRACE("context_lemma", tout << "new lemma: "; literal_vector v(num_lits, lits); std::sort(v.begin(), v.end()); for (unsigned i = 0; i < num_lits; i++) { display_literal(tout, v[i]); tout << "\n"; v[i].display(tout, m_manager, m_bool_var2expr.c_ptr()); tout << "\n\n"; } tout << "\n";); decay_bvar_activity(); update_phase_cache_counter(); return true; } else if (m_manager.proofs_enabled()) { m_unsat_proof = m_conflict_resolution->get_lemma_proof(); check_proof(m_unsat_proof); } return false; } void context::get_relevant_labels(expr* cnstr, buffer & result) { if (m_fparams.m_check_at_labels) { check_at_labels checker(m_manager); if (cnstr && !checker.check(cnstr)) { warning_msg("Boogie generated formula that can require multiple '@' labels in a counter-example"); } else { unsigned nf = m_asserted_formulas.get_num_formulas(); for (unsigned i = 0; i < nf; ++i) { expr* fml = m_asserted_formulas.get_formula(i); if (!checker.check(fml)) { warning_msg("Boogie generated formula that can require multiple '@' labels in a counter-example"); break; } } } } SASSERT(!inconsistent()); unsigned sz = m_b_internalized_stack.size(); for (unsigned i = 0; i < sz; i++) { expr * curr = m_b_internalized_stack.get(i); if (is_relevant(curr) && get_assignment(curr) == l_true) { // if curr is a label literal, then its tags will be copied to result. m_manager.is_label_lit(curr, result); } } } /** \brief Collect relevant literals that may be used to block the current assignment. If at_lbls is true, then only labels that contains '@' are considered. (This is a hack for Boogie). This hack is also available in the Simplify theorem prover. */ void context::get_relevant_labeled_literals(bool at_lbls, expr_ref_vector & result) { SASSERT(!inconsistent()); buffer lbls; unsigned sz = m_b_internalized_stack.size(); for (unsigned i = 0; i < sz; i++) { expr * curr = m_b_internalized_stack.get(i); if (is_relevant(curr) && get_assignment(curr) == l_true) { lbls.reset(); if (m_manager.is_label_lit(curr, lbls)) { bool include = false; if (at_lbls) { // include if there is a label with the '@' sign. buffer::const_iterator it = lbls.begin(); buffer::const_iterator end = lbls.end(); for (; it != end; ++it) { symbol const & s = *it; if (s.contains('@')) { include = true; break; } } } else { include = true; } if (include) result.push_back(curr); } } } } /** \brief Store in result the (relevant) literal assigned by the logical context. */ void context::get_relevant_literals(expr_ref_vector & result) { SASSERT(!inconsistent()); unsigned sz = m_b_internalized_stack.size(); for (unsigned i = 0; i < sz; i++) { expr * curr = m_b_internalized_stack.get(i); if (is_relevant(curr)) { switch (get_assignment(curr)) { case l_true: result.push_back(curr); break; case l_false: result.push_back(m_manager.mk_not(curr)); break; default: break; } } } } /** \brief Store the current set of guessed literals (i.e., case splits). */ void context::get_guessed_literals(expr_ref_vector & result) { // The literals between [m_base_lvl, m_search_lvl) are not guesses but assumptions. SASSERT(m_base_lvl <= m_scopes.size()); if (m_search_lvl == m_scopes.size()) { // do nothing... there are guesses... } for (unsigned i = m_search_lvl; i < m_scope_lvl; i++) { // This method assumes the first literal assigned in a non base scope level is a guess. scope & s = m_scopes[i]; unsigned guess_idx = s.m_assigned_literals_lim; literal guess = m_assigned_literals[guess_idx]; SASSERT(get_justification(guess.var()).get_kind() == b_justification::AXIOM); expr_ref lit(m_manager); literal2expr(guess, lit); result.push_back(lit); } } /** \brief Undo object for bool var m_true_first field update. */ class set_true_first_trail : public trail { bool_var m_var; public: set_true_first_trail(bool_var v):m_var(v) {} virtual void undo(context & ctx) { ctx.m_bdata[m_var].reset_true_first_flag(); } }; void context::set_true_first_flag(bool_var v) { push_trail(set_true_first_trail(v)); bool_var_data & d = m_bdata[v]; d.set_true_first_flag(); } bool context::assume_eq(enode * lhs, enode * rhs) { if (lhs->get_root() == rhs->get_root()) return false; // it is not necessary to assume the eq. expr * _lhs = lhs->get_owner(); expr * _rhs = rhs->get_owner(); expr * eq = mk_eq_atom(_lhs, _rhs); TRACE("assume_eq", tout << "creating interface eq:\n" << mk_pp(eq, m_manager) << "\n";); if (m_manager.is_false(eq)) { return false; } bool r = false; if (!b_internalized(eq)) { // I do not invoke internalize(eq, true), because I want to // mark the try_true_first flag before invoking theory::internalize_eq_eh. // Reason: Theories like arithmetic should be able to know if the try_true_first flag is // marked or not. They use this information to also mark auxiliary atoms such as: // (<= (- x y) 0) // (>= (- y x) 0) // for the new equality atom (= x y). if (m_manager.is_eq(eq)) { internalize_formula_core(to_app(eq), true); bool_var v = get_bool_var(eq); bool_var_data & d = get_bdata(v); d.set_eq_flag(); set_true_first_flag(v); sort * s = m_manager.get_sort(to_app(eq)->get_arg(0)); theory * th = m_theories.get_plugin(s->get_family_id()); if (th) th->internalize_eq_eh(to_app(eq), v); } else { internalize(eq, true); } r = true; m_stats.m_num_interface_eqs++; TRACE("assume_eq", tout << "new internalization.\n";); } bool_var v = get_bool_var(eq); bool_var_data & d = m_bdata[v]; if (!d.try_true_first()) { set_true_first_flag(v); r = true; TRACE("assume_eq", tout << "marked as ieq.\n";); } if (get_assignment(v) == l_undef) { TRACE("assume_eq", tout << "variable is unassigned.\n";); r = true; } if (relevancy()) { if (!is_relevant(eq)) { TRACE("assume_eq", tout << "marking eq as relevant.\n";); mark_as_relevant(eq); r = true; } } TRACE("assume_eq", tout << "variable value: " << get_assignment(v) << "\n";); TRACE("assume_eq", tout << "assume_eq result: " << r << "\n";); return r; } bool context::is_shared(enode * n) const { n = n->get_root(); unsigned num_th_vars = n->get_num_th_vars(); if (m_manager.is_ite(n->get_owner())) { return true; } switch (num_th_vars) { case 0: { return false; } case 1: { if (m_qmanager->is_shared(n)) return true; // the variabe is shared if the equivalence class of n // contains a parent application. theory_var_list * l = n->get_th_var_list(); theory_id th_id = l->get_th_id(); enode_vector::const_iterator it = n->begin_parents(); enode_vector::const_iterator end = n->end_parents(); for (; it != end; ++it) { enode * parent = *it; family_id fid = parent->get_owner()->get_family_id(); if (fid != th_id && fid != m_manager.get_basic_family_id()) { TRACE("is_shared", tout << mk_pp(n->get_owner(), m_manager) << "\nis shared because of:\n" << mk_pp(parent->get_owner(), m_manager) << "\n";); return true; } } // Some theories implement families of theories. Examples: // Arrays and Tuples. For example, array theory is a // parametric theory, that is, it implements several theories: // (array int int), (array int (array int int)), ... // // Example: // // a : (array int int) // b : (array int int) // x : int // y : int // v : int // w : int // A : (array (array int int) int) // // assert (= b (store a x v)) // assert (= b (store a y w)) // assert (not (= x y)) // assert (not (select A a)) // assert (not (select A b)) // check // // In the example above, 'a' and 'b' are shared variables between // the theories of (array int int) and (array (array int int) int). // Remark: The inconsistency is not going to be detected if they are // not marked as shared. return get_theory(th_id)->is_shared(l->get_th_var()); } default: return true; } } bool context::get_value(enode * n, expr_ref & value) { sort * s = m_manager.get_sort(n->get_owner()); family_id fid = s->get_family_id(); theory * th = get_theory(fid); if (th == 0) return false; return th->get_value(n, value); } bool context::update_model(bool refinalize) { final_check_status fcs = FC_DONE; if (refinalize) { fcs = final_check(); } TRACE("opt", tout << (refinalize?"refinalize":"no-op") << " " << fcs << "\n";); if (fcs == FC_DONE) { mk_proto_model(l_true); m_model = m_proto_model->mk_model(); } return fcs == FC_DONE; } void context::mk_proto_model(lbool r) { TRACE("get_model", display(tout); display_normalized_enodes(tout); display_enodes_lbls(tout); m_fingerprints.display(tout); ); failure fl = get_last_search_failure(); if (fl == TIMEOUT || fl == MEMOUT || fl == CANCELED || fl == NUM_CONFLICTS) { // don't generate model. return; } if (m_fparams.m_model || m_fparams.m_model_on_final_check || m_qmanager->model_based()) { m_model_generator->reset(); m_proto_model = m_model_generator->mk_model(); m_qmanager->adjust_model(m_proto_model.get()); TRACE("mbqi_bug", tout << "before complete_partial_funcs:\n"; model_pp(tout, *m_proto_model);); m_proto_model->complete_partial_funcs(); TRACE("mbqi_bug", tout << "before cleanup:\n"; model_pp(tout, *m_proto_model);); m_proto_model->cleanup(); if (m_fparams.m_model_compact) m_proto_model->compress(); TRACE("mbqi_bug", tout << "after cleanup:\n"; model_pp(tout, *m_proto_model);); } } proof * context::get_proof() { if (!m_manager.proofs_enabled()) return 0; return m_unsat_proof; } void context::get_model(model_ref & m) const { if (inconsistent()) m = 0; else m = const_cast(m_model.get()); } void context::get_proto_model(proto_model_ref & m) const { m = const_cast(m_proto_model.get()); } failure context::get_last_search_failure() const { return m_last_search_failure; } void context::set_cancel_flag(bool f) { m_cancel_flag = f; m_asserted_formulas.set_cancel_flag(f); m_qmanager->set_cancel(f); } }; #ifdef Z3DEBUG void pp(smt::context & c) { c.display(std::cout); } #endif z3-z3-4.4.1/src/smt/smt_context.h000066400000000000000000001362401260446376700165450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_context.h Abstract: Logical context Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #ifndef SMT_CONTEXT_H_ #define SMT_CONTEXT_H_ #include"smt_clause.h" #include"smt_setup.h" #include"smt_enode.h" #include"smt_cg_table.h" #include"smt_b_justification.h" #include"smt_eq_justification.h" #include"smt_justification.h" #include"smt_bool_var_data.h" #include"smt_theory.h" #include"smt_quantifier.h" #include"smt_quantifier_stat.h" #include"smt_statistics.h" #include"smt_conflict_resolution.h" #include"smt_relevancy.h" #include"smt_case_split_queue.h" #include"smt_almost_cg_table.h" #include"smt_failure.h" #include"asserted_formulas.h" #include"smt_types.h" #include"dyn_ack.h" #include"ast_smt_pp.h" #include"watch_list.h" #include"trail.h" #include"fingerprints.h" #include"ref.h" #include"proto_model.h" #include"model.h" #include"timer.h" #include"statistics.h" #include"progress_callback.h" // there is a significant space overhead with allocating 1000+ contexts in // the case that each context only references a few expressions. // Using a map instead of a vector for the literals can compress space // consumption. #ifdef SPARSE_MAP #define USE_BOOL_VAR_VECTOR 0 #else #define USE_BOOL_VAR_VECTOR 1 #endif namespace smt { class model_generator; class context { friend class model_generator; public: statistics m_stats; std::ostream& display_last_failure(std::ostream& out) const; std::string last_failure_as_string() const; void set_progress_callback(progress_callback *callback); protected: ast_manager & m_manager; smt_params & m_fparams; params_ref m_params; setup m_setup; volatile bool m_cancel_flag; timer m_timer; asserted_formulas m_asserted_formulas; scoped_ptr m_qmanager; scoped_ptr m_model_generator; scoped_ptr m_relevancy_propagator; random_gen m_random; bool m_flushing; // (debug support) true when flushing progress_callback * m_progress_callback; unsigned m_next_progress_sample; region m_region; fingerprint_set m_fingerprints; expr_ref_vector m_b_internalized_stack; // stack of the boolean expressions already internalized. // Remark: boolean expressions can also be internalized as // enodes. Examples: boolean expression nested in an // uninterpreted function. expr_ref_vector m_e_internalized_stack; // stack of the expressions already internalized as enodes. ptr_vector m_justifications; unsigned m_final_check_idx; // circular counter used for implementing fairness // ----------------------------------- // // Equality & Uninterpreted functions // // ----------------------------------- enode * m_true_enode; enode * m_false_enode; app2enode_t m_app2enode; // app -> enode ptr_vector m_enodes; plugin_manager m_theories; // mapping from theory_id -> theory ptr_vector m_theory_set; // set of theories for fast traversal vector m_decl2enodes; // decl -> enode (for decls with arity > 0) cg_table m_cg_table; dyn_ack_manager m_dyn_ack_manager; struct new_eq { enode * m_lhs; enode * m_rhs; eq_justification m_justification; new_eq() {} new_eq(enode * lhs, enode * rhs, eq_justification const & js): m_lhs(lhs), m_rhs(rhs), m_justification(js) {} }; svector m_eq_propagation_queue; struct new_th_eq { theory_id m_th_id; theory_var m_lhs; theory_var m_rhs; new_th_eq():m_th_id(null_theory_id), m_lhs(null_theory_var), m_rhs(null_theory_var) {} new_th_eq(theory_id id, theory_var l, theory_var r):m_th_id(id), m_lhs(l), m_rhs(r) {} }; svector m_th_eq_propagation_queue; svector m_th_diseq_propagation_queue; #ifdef Z3DEBUG svector m_propagated_th_eqs; svector m_propagated_th_diseqs; svector m_diseq_vector; #endif enode * m_is_diseq_tmp; // auxiliary enode used to find congruent equality atoms. tmp_enode m_tmp_enode; ptr_vector m_almost_cg_tables; // temporary field for is_ext_diseq // ----------------------------------- // // Boolean engine // // ----------------------------------- #if USE_BOOL_VAR_VECTOR svector m_expr2bool_var; // expr id -> bool_var #else u_map m_expr2bool_var; #endif ptr_vector m_bool_var2expr; // bool_var -> expr signed_char_vector m_assignment; //!< mapping literal id -> assignment lbool vector m_watches; //!< per literal vector m_lit_occs; //!< index for backward subsumption svector m_bdata; //!< mapping bool_var -> data svector m_activity; clause_vector m_aux_clauses; clause_vector m_lemmas; vector m_clauses_to_reinit; expr_ref_vector m_units_to_reassert; svector m_units_to_reassert_sign; literal_vector m_assigned_literals; unsigned m_qhead; unsigned m_simp_qhead; int m_simp_counter; //!< can become negative scoped_ptr m_case_split_queue; double m_bvar_inc; bool m_phase_cache_on; unsigned m_phase_counter; //!< auxiliary variable used to decide when to turn on/off phase caching bool m_phase_default; //!< default phase when using phase caching // A conflict is usually a single justification. That is, a justification // for false. If m_not_l is not null_literal, then m_conflict is a // justification for l, and the conflict is union of m_no_l and m_conflict; b_justification m_conflict; literal m_not_l; scoped_ptr m_conflict_resolution; proof_ref m_unsat_proof; literal_vector m_atom_propagation_queue; obj_map m_cached_generation; obj_hashtable m_cache_generation_visited; // ----------------------------------- // // Model generation // // ----------------------------------- proto_model_ref m_proto_model; model_ref m_model; void mk_proto_model(lbool r); struct scoped_mk_model; // ----------------------------------- // // Unsat core extraction // // ----------------------------------- typedef u_map literal2assumption; literal_vector m_assumptions; literal2assumption m_literal2assumption; // maps an expression associated with a literal to the original assumption expr_ref_vector m_unsat_core; // ----------------------------------- // // Accessors // // ----------------------------------- public: ast_manager & get_manager() const { return m_manager; } simplifier & get_simplifier() { return m_asserted_formulas.get_simplifier(); } smt_params & get_fparams() { return m_fparams; } params_ref const & get_params() { return m_params; } virtual void set_cancel_flag(bool f = true); bool get_cancel_flag() { return m_cancel_flag || !m_manager.limit().inc(); } region & get_region() { return m_region; } bool relevancy() const { return m_fparams.m_relevancy_lvl > 0; } enode * get_enode(expr const * n) const { SASSERT(e_internalized(n)); return m_app2enode[n->get_id()]; } /** \brief Similar to get_enode, but returns 0 if n is to e_internalized. */ enode * find_enode(expr const * n) const { return m_app2enode.get(n->get_id(), 0); } void reset_bool_vars() { m_expr2bool_var.reset(); } bool_var get_bool_var(expr const * n) const { return m_expr2bool_var[n->get_id()]; } bool_var get_bool_var_of_id(unsigned id) const { return m_expr2bool_var[id]; } bool_var get_bool_var_of_id_option(unsigned id) const { return m_expr2bool_var.get(id, null_bool_var); } #if USE_BOOL_VAR_VECTOR void set_bool_var(unsigned id, bool_var v) { m_expr2bool_var.setx(id, v, null_bool_var); } #else void set_bool_var(unsigned id, bool_var v) { if (v == null_bool_var) { m_expr2bool_var.erase(id); } else { m_expr2bool_var.insert(id, v); } } #endif literal get_literal(expr * n) const; bool has_enode(bool_var v) const { return m_bdata[v].is_enode(); } enode * bool_var2enode(bool_var v) const { SASSERT(m_bdata[v].is_enode()); return m_app2enode[m_bool_var2expr[v]->get_id()]; } bool_var enode2bool_var(enode const * n) const { SASSERT(n->is_bool()); SASSERT(n != m_false_enode); return get_bool_var_of_id(n->get_owner_id()); } literal enode2literal(enode const * n) const { SASSERT(n->is_bool()); return n == m_false_enode ? false_literal : literal(enode2bool_var(n)); } unsigned get_num_bool_vars() const { return m_b_internalized_stack.size(); } bool_var_data & get_bdata(bool_var v) { return m_bdata[v]; } bool_var_data const & get_bdata(bool_var v) const { return m_bdata[v]; } lbool get_lit_assignment(unsigned lit_idx) const { return static_cast(m_assignment[lit_idx]); } lbool get_assignment(literal l) const { return get_lit_assignment(l.index()); } lbool get_assignment(bool_var v) const { return get_assignment(literal(v)); } literal_vector const & assigned_literals() const { return m_assigned_literals; } lbool get_assignment(expr * n) const; // Similar to get_assignment, but returns l_undef if n is not internalized. lbool find_assignment(expr * n) const; lbool get_assignment(enode * n) const; void get_assignments(expr_ref_vector& assignments); b_justification get_justification(bool_var v) const { return get_bdata(v).m_justification; } bool has_th_justification(bool_var v, theory_id th_id) const { b_justification js = get_justification(v); return js.get_kind() == b_justification::JUSTIFICATION && js.get_justification()->get_from_theory() == th_id; } int get_random_value() { return m_random(); } bool is_searching() const { return m_searching; } svector const & get_activity_vector() const { return m_activity; } double get_activity(bool_var v) const { return m_activity[v]; } void set_activity(bool_var v, double & act) { m_activity[v] = act; } bool is_assumption(bool_var v) const { return get_bdata(v).m_assumption; } bool is_assumption(literal l) const { return is_assumption(l.var()); } bool is_marked(bool_var v) const { return get_bdata(v).m_mark; } void set_mark(bool_var v) { SASSERT(!is_marked(v)); get_bdata(v).m_mark = true; } void unset_mark(bool_var v) { SASSERT(is_marked(v)); get_bdata(v).m_mark = false; } /** \brief Return the scope level when v was assigned. */ unsigned get_assign_level(bool_var v) const { return get_bdata(v).m_scope_lvl; } unsigned get_assign_level(literal l) const { return get_assign_level(l.var()); } /** \brief Return the scope level when v was internalized. */ unsigned get_intern_level(bool_var v) const { return get_bdata(v).get_intern_level(); } theory * get_theory(theory_id th_id) const { return m_theories.get_plugin(th_id); } ptr_vector::const_iterator begin_theories() const { return m_theories.begin(); } ptr_vector::const_iterator end_theories() const { return m_theories.end(); } unsigned get_scope_level() const { return m_scope_lvl; } unsigned get_base_level() const { return m_base_lvl; } bool at_base_level() const { return m_scope_lvl == m_base_lvl; } unsigned get_search_level() const { return m_search_lvl; } bool at_search_level() const { return m_scope_lvl == m_search_lvl; } bool tracking_assumptions() const { return m_search_lvl > m_base_lvl; } expr * bool_var2expr(bool_var v) const { return m_bool_var2expr[v]; } void literal2expr(literal l, expr_ref & result) const { if (l == true_literal) result = m_manager.mk_true(); else if (l == false_literal) result = m_manager.mk_false(); else if (l.sign()) result = m_manager.mk_not(bool_var2expr(l.var())); else result = bool_var2expr(l.var()); } bool is_true(enode const * n) const { return n == m_true_enode; } bool is_false(enode const * n) const { return n == m_false_enode; } unsigned get_num_enodes_of(func_decl const * decl) const { unsigned id = decl->get_decl_id(); return id < m_decl2enodes.size() ? m_decl2enodes[id].size() : 0; } enode_vector::const_iterator begin_enodes_of(func_decl const * decl) const { unsigned id = decl->get_decl_id(); return id < m_decl2enodes.size() ? m_decl2enodes[id].begin() : 0; } enode_vector::const_iterator end_enodes_of(func_decl const * decl) const { unsigned id = decl->get_decl_id(); return id < m_decl2enodes.size() ? m_decl2enodes[id].end() : 0; } ptr_vector::const_iterator begin_enodes() const { return m_enodes.begin(); } ptr_vector::const_iterator end_enodes() const { return m_enodes.end(); } unsigned get_generation(quantifier * q) const { return m_qmanager->get_generation(q); } /** \brief Return true if the logical context internalized universal quantifiers. */ bool internalized_quantifiers() const { return !m_qmanager->empty(); } /** \brief Return true if the logical context internalized or will internalize universal quantifiers. */ bool has_quantifiers() const { return m_asserted_formulas.has_quantifiers(); } fingerprint * add_fingerprint(void * data, unsigned data_hash, unsigned num_args, enode * const * args) { return m_fingerprints.insert(data, data_hash, num_args, args); } theory_id get_var_theory(bool_var v) const { return get_bdata(v).get_theory(); } friend class set_var_theory_trail; void set_var_theory(bool_var v, theory_id tid); // ----------------------------------- // // Backtracking support // // ----------------------------------- protected: typedef ptr_vector > trail_stack; trail_stack m_trail_stack; #ifdef Z3DEBUG bool m_trail_enabled; #endif public: template void push_trail(const TrailObject & obj) { SASSERT(m_trail_enabled); m_trail_stack.push_back(new (m_region) TrailObject(obj)); } void push_trail_ptr(trail * ptr) { m_trail_stack.push_back(ptr); } protected: unsigned m_scope_lvl; unsigned m_base_lvl; unsigned m_search_lvl; // It is greater than m_base_lvl when assumptions are used. Otherwise, it is equals to m_base_lvl struct scope { unsigned m_assigned_literals_lim; unsigned m_trail_stack_lim; unsigned m_aux_clauses_lim; unsigned m_justifications_lim; unsigned m_units_to_reassert_lim; }; struct base_scope { unsigned m_lemmas_lim; unsigned m_simp_qhead_lim; unsigned m_inconsistent; }; svector m_scopes; svector m_base_scopes; void push_scope(); unsigned pop_scope_core(unsigned num_scopes); void pop_scope(unsigned num_scopes); void undo_trail_stack(unsigned old_size); void unassign_vars(unsigned old_lim); void remove_watch_literal(clause * cls, unsigned idx); void remove_lit_occs(clause * cls); void remove_cls_occs(clause * cls); void mark_as_deleted(clause * cls); void del_clause(clause * cls); void del_clauses(clause_vector & v, unsigned old_size); void del_justifications(ptr_vector & justifications, unsigned old_lim); bool is_unit_clause(clause const * c) const; bool is_empty_clause(clause const * c) const; void cache_generation(unsigned new_scope_lvl); void cache_generation(clause const * cls, unsigned new_scope_lvl); void cache_generation(unsigned num_lits, literal const * lits, unsigned new_scope_lvl); void cache_generation(expr * n, unsigned new_scope_lvl); void reset_cache_generation(); void reinit_clauses(unsigned num_scopes, unsigned num_bool_vars); void reassert_units(unsigned units_to_reassert_lim); // ----------------------------------- // // Internalization // // ----------------------------------- public: bool b_internalized(expr const * n) const { return get_bool_var_of_id_option(n->get_id()) != null_bool_var; } bool lit_internalized(expr const * n) const { return m_manager.is_false(n) || (m_manager.is_not(n) ? b_internalized(to_app(n)->get_arg(0)) : b_internalized(n)); } bool e_internalized(expr const * n) const { return m_app2enode.get(n->get_id(), 0) != 0; } unsigned get_num_b_internalized() const { return m_b_internalized_stack.size(); } expr * get_b_internalized(unsigned idx) const { return m_b_internalized_stack.get(idx); } unsigned get_num_e_internalized() const { return m_e_internalized_stack.size(); } expr * get_e_internalized(unsigned idx) const { return m_e_internalized_stack.get(idx); } /** \brief Return the position (in the assignment stack) of the decision literal at the given scope level. */ unsigned get_decision_literal_pos(unsigned scope_lvl) const { SASSERT(scope_lvl > m_base_lvl); return m_scopes[scope_lvl - 1].m_assigned_literals_lim; } protected: unsigned m_generation; //!< temporary variable used during internalization public: bool binary_clause_opt_enabled() const { return !m_manager.proofs_enabled() && m_fparams.m_binary_clause_opt; } protected: bool_var_data & get_bdata(expr const * n) { return get_bdata(get_bool_var(n)); } bool_var_data const & get_bdata(expr const * n) const { return get_bdata(get_bool_var(n)); } typedef std::pair expr_bool_pair; void ts_visit_child(expr * n, bool gate_ctx, svector & tcolors, svector & fcolors, svector & todo, bool & visited); bool ts_visit_children(expr * n, bool gate_ctx, svector & tcolors, svector & fcolors, svector & todo); void top_sort_expr(expr * n, svector & sorted_exprs); void assert_default(expr * n, proof * pr); void assert_distinct(app * n, proof * pr); void internalize_formula(expr * n, bool gate_ctx); void internalize_eq(app * n, bool gate_ctx); void internalize_distinct(app * n, bool gate_ctx); bool internalize_theory_atom(app * n, bool gate_ctx); void internalize_quantifier(quantifier * q, bool gate_ctx); void internalize_formula_core(app * n, bool gate_ctx); void set_merge_tf(enode * n, bool_var v, bool is_new_var); friend class set_enode_flag_trail; public: void set_enode_flag(bool_var v, bool is_new_var); protected: void internalize_term(app * n); void internalize_ite_term(app * n); bool internalize_theory_term(app * n); void internalize_uninterpreted(app * n); friend class mk_bool_var_trail; class mk_bool_var_trail : public trail { public: virtual void undo(context & ctx) { ctx.undo_mk_bool_var(); } }; mk_bool_var_trail m_mk_bool_var_trail; void undo_mk_bool_var(); friend class mk_enode_trail; class mk_enode_trail : public trail { public: virtual void undo(context & ctx) { ctx.undo_mk_enode(); } }; mk_enode_trail m_mk_enode_trail; void undo_mk_enode(); void apply_sort_cnstr(app * term, enode * e); bool simplify_aux_clause_literals(unsigned & num_lits, literal * lits, literal_buffer & simp_lits); bool simplify_aux_lemma_literals(unsigned & num_lits, literal * lits); void mark_for_reinit(clause * cls, unsigned scope_lvl, bool reinternalize_atoms); unsigned get_max_iscope_lvl(unsigned num_lits, literal const * lits) const; bool use_binary_clause_opt(literal l1, literal l2, bool lemma) const; int select_learned_watch_lit(clause const * cls) const; int select_watch_lit(clause const * cls, int starting_at) const; void add_watch_literal(clause * cls, unsigned idx); proof * mk_clause_def_axiom(unsigned num_lits, literal * lits, expr * root_gate); public: void mk_gate_clause(unsigned num_lits, literal * lits); void mk_gate_clause(literal l1, literal l2); void mk_gate_clause(literal l1, literal l2, literal l3); void mk_gate_clause(literal l1, literal l2, literal l3, literal l4); protected: void mk_root_clause(unsigned num_lits, literal * lits, proof * pr); void mk_root_clause(literal l1, literal l2, proof * pr); void mk_root_clause(literal l1, literal l2, literal l3, proof * pr); void add_and_rel_watches(app * n); void add_or_rel_watches(app * n); void add_ite_rel_watches(app * n); void mk_not_cnstr(app * n); void mk_and_cnstr(app * n); void mk_or_cnstr(app * n); void mk_iff_cnstr(app * n); void mk_ite_cnstr(app * n); bool lit_occs_enabled() const { return m_fparams.m_phase_selection==PS_OCCURRENCE; } void add_lit_occs(clause * cls); public: void internalize(expr * n, bool gate_ctx); void internalize(expr * n, bool gate_ctx, unsigned generation); clause * mk_clause(unsigned num_lits, literal * lits, justification * j, clause_kind k = CLS_AUX, clause_del_eh * del_eh = 0); void mk_clause(literal l1, literal l2, justification * j); void mk_clause(literal l1, literal l2, literal l3, justification * j); void mk_th_axiom(theory_id tid, unsigned num_lits, literal * lits, unsigned num_params = 0, parameter * params = 0); void mk_th_axiom(theory_id tid, literal l1, literal l2, unsigned num_params = 0, parameter * params = 0); void mk_th_axiom(theory_id tid, literal l1, literal l2, literal l3, unsigned num_params = 0, parameter * params = 0); bool_var mk_bool_var(expr * n); enode * mk_enode(app * n, bool suppress_args, bool merge_tf, bool cgc_enabled); void attach_th_var(enode * n, theory * th, theory_var v); template justification * mk_justification(Justification const & j) { justification * js = new (m_region) Justification(j); SASSERT(js->in_region()); if (js->has_del_eh()) m_justifications.push_back(js); return js; } // ----------------------------------- // // Engine // // ----------------------------------- protected: lbool m_last_search_result; failure m_last_search_failure; ptr_vector m_incomplete_theories; //!< theories that failed to produce a model bool m_searching; ptr_vector m_assumption_core; unsigned m_num_conflicts; unsigned m_num_conflicts_since_restart; unsigned m_num_conflicts_since_lemma_gc; unsigned m_restart_threshold; unsigned m_restart_outer_threshold; unsigned m_luby_idx; double m_agility; unsigned m_lemma_gc_threshold; void assign_core(literal l, b_justification j, bool decision = false); void trace_assign(literal l, b_justification j, bool decision) const; public: void assign(literal l, b_justification j, bool decision = false) { SASSERT(l != false_literal); SASSERT(l != null_literal); switch (get_assignment(l)) { case l_false: set_conflict(j, ~l); break; case l_undef: assign_core(l, j, decision); break; case l_true: return; } } void assign(literal l, justification * j, bool decision = false) { assign(l, j ? b_justification(j) : b_justification::mk_axiom(), decision); } friend class set_true_first_trail; void set_true_first_flag(bool_var v); bool try_true_first(bool_var v) const { return get_bdata(v).try_true_first(); } bool assume_eq(enode * lhs, enode * rhs); bool is_shared(enode * n) const; void assign_eq(enode * lhs, enode * rhs, eq_justification const & js) { push_eq(lhs, rhs, js); } /** \brief Force the given phase next time we case split v. This method has no effect if phase caching is disabled. */ void force_phase(bool_var v, bool phase) { bool_var_data & d = get_bdata(v); d.m_phase_available = true; d.m_phase = phase; } void force_phase(literal l) { force_phase(l.var(), !l.sign()); } bool contains_instance(quantifier * q, unsigned num_bindings, enode * const * bindings); bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, ptr_vector & used_enodes); void set_global_generation(unsigned generation) { m_generation = generation; } #ifdef Z3DEBUG bool slow_contains_instance(quantifier const * q, unsigned num_bindings, enode * const * bindings) const { return m_fingerprints.slow_contains(q, q->get_id(), num_bindings, bindings); } #endif protected: void push_new_th_eq(theory_id th, theory_var lhs, theory_var rhs); void push_new_th_diseq(theory_id th, theory_var lhs, theory_var rhs); friend class add_eq_trail; void add_eq(enode * n1, enode * n2, eq_justification js); void remove_parents_from_cg_table(enode * r1); void reinsert_parents_into_cg_table(enode * r1, enode * r2, enode * n1, enode * n2, eq_justification js); void invert_trans(enode * n); theory_var get_closest_var(enode * n, theory_id th_id); void merge_theory_vars(enode * r2, enode * r1, eq_justification js); void propagate_bool_enode_assignment(enode * r1, enode * r2, enode * n1, enode * n2); void propagate_bool_enode_assignment_core(enode * source, enode * target); void undo_add_eq(enode * r1, enode * n1, unsigned r2_num_parents); void restore_theory_vars(enode * r2, enode * r1); void push_eq(enode * lhs, enode * rhs, eq_justification const & js) { SASSERT(lhs != rhs); m_eq_propagation_queue.push_back(new_eq(lhs, rhs, js)); } void push_new_congruence(enode * n1, enode * n2, bool used_commutativity) { SASSERT(n1->m_cg == n2); // if (is_relevant(n1)) mark_as_relevant(n2); push_eq(n1, n2, eq_justification::mk_cg(used_commutativity)); } void add_diseq(enode * n1, enode * n2); void assign_quantifier(quantifier * q); void set_conflict(b_justification js, literal not_l); void set_conflict(b_justification js) { set_conflict(js, null_literal); } public: void set_conflict(justification * js) { SASSERT(js); set_conflict(b_justification(js)); } bool inconsistent() const { return m_conflict != null_b_justification; } unsigned get_num_conflicts() const { return m_num_conflicts; } static bool is_eq(enode const * n1, enode const * n2) { return n1->get_root() == n2->get_root(); } bool is_diseq(enode * n1, enode * n2) const; bool is_diseq_slow(enode * n1, enode * n2) const; bool is_ext_diseq(enode * n1, enode * n2, unsigned depth); enode * get_enode_eq_to(func_decl * f, unsigned num_args, enode * const * args); protected: bool decide(); void update_phase_cache_counter(); #define ACTIVITY_LIMIT 1e100 #define INV_ACTIVITY_LIMIT 1e-100 void rescale_bool_var_activity(); public: void inc_bvar_activity(bool_var v) { double & act = m_activity[v]; act += m_bvar_inc; if (act > ACTIVITY_LIMIT) rescale_bool_var_activity(); m_case_split_queue->activity_increased_eh(v); } protected: void decay_bvar_activity() { m_bvar_inc *= m_fparams.m_inv_decay; } bool simplify_clause(clause * cls); unsigned simplify_clauses(clause_vector & clauses, unsigned starting_at); void simplify_clauses(); /** \brief Return true if the give clause is justifying some literal. */ bool is_justifying(clause * cls) const { for (unsigned i = 0; i < 2; i++) { b_justification js; js = get_justification(cls->get_literal(i).var()); if (js.get_kind() == b_justification::CLAUSE && js.get_clause() == cls) return true; } return false; } bool can_delete(clause * cls) const { if (cls->in_reinit_stack()) return false; return !is_justifying(cls); } void del_inactive_lemmas(); void del_inactive_lemmas1(); void del_inactive_lemmas2(); bool more_than_k_unassigned_literals(clause * cls, unsigned k); void internalize_assertions(); void assert_assumption(expr * a); bool validate_assumptions(unsigned num_assumptions, expr * const * assumptions); void init_assumptions(unsigned num_assumptions, expr * const * assumptions); void reset_assumptions(); void mk_unsat_core(); void validate_unsat_core(); void init_search(); void end_search(); lbool search(); void inc_limits(); void tick(unsigned & counter) const; lbool bounded_search(); final_check_status final_check(); void check_proof(proof * pr); void forget_phase_of_vars_in_current_level(); virtual bool resolve_conflict(); // ----------------------------------- // // Propagation // // ----------------------------------- protected: bool bcp(); bool propagate_eqs(); bool propagate_atoms(); void push_new_th_diseqs(enode * r, theory_var v, theory * th); void propagate_bool_var_enode(bool_var v); bool is_relevant_core(expr * n) const { return m_relevancy_propagator->is_relevant(n); } public: // event handler for relevancy_propagator class void relevant_eh(expr * n); bool is_relevant(expr * n) const { return !relevancy() || is_relevant_core(n); } bool is_relevant(enode * n) const { return is_relevant(n->get_owner()); } bool is_relevant(bool_var v) const { return is_relevant(bool_var2expr(v)); } bool is_relevant(literal l) const { SASSERT(l != true_literal && l != false_literal); return is_relevant(l.var()); } void mark_as_relevant(expr * n) { m_relevancy_propagator->mark_as_relevant(n); m_relevancy_propagator->propagate(); } void mark_as_relevant(enode * n) { mark_as_relevant(n->get_owner()); } void mark_as_relevant(bool_var v) { mark_as_relevant(bool_var2expr(v)); } void mark_as_relevant(literal l) { mark_as_relevant(l.var()); } template relevancy_eh * mk_relevancy_eh(Eh const & eh) { return m_relevancy_propagator->mk_relevancy_eh(eh); } void add_relevancy_eh(expr * source, relevancy_eh * eh) { m_relevancy_propagator->add_handler(source, eh); } void add_relevancy_dependency(expr * source, expr * target) { m_relevancy_propagator->add_dependency(source, target); } void add_rel_watch(literal l, relevancy_eh * eh) { m_relevancy_propagator->add_watch(bool_var2expr(l.var()), !l.sign(), eh); } void add_rel_watch(literal l, expr * n) { m_relevancy_propagator->add_watch(bool_var2expr(l.var()), !l.sign(), n); } protected: lbool get_assignment_core(expr * n) const; void propagate_relevancy(unsigned qhead); bool propagate_theories(); void propagate_th_eqs(); void propagate_th_diseqs(); bool can_theories_propagate() const; bool propagate(); public: bool can_propagate() const; // ----------------------------------- // // Model checking... (must be improved) // // ----------------------------------- public: bool get_value(enode * n, expr_ref & value); // ----------------------------------- // // Pretty Printing // // ----------------------------------- protected: ast_mark m_pp_visited; ast_mark & get_pp_visited() const { return const_cast(m_pp_visited); } public: void display_enode_defs(std::ostream & out) const; void display_bool_var_defs(std::ostream & out) const; void display_asserted_formulas(std::ostream & out) const; void display_literal(std::ostream & out, literal l) const; void display_detailed_literal(std::ostream & out, literal l) const { l.display(out, m_manager, m_bool_var2expr.c_ptr()); } void display_literal_info(std::ostream & out, literal l) const; void display_literals(std::ostream & out, unsigned num_lits, literal const * lits) const; void display_literals_verbose(std::ostream & out, unsigned num_lits, literal const * lits) const; void display_watch_list(std::ostream & out, literal l) const; void display_watch_lists(std::ostream & out) const; void display_clause_detail(std::ostream & out, clause const * cls) const; void display_clause(std::ostream & out, clause const * cls) const; void display_clauses(std::ostream & out, ptr_vector const & v) const; void display_binary_clauses(std::ostream & out) const; void display_assignment(std::ostream & out) const; void display_eqc(std::ostream & out) const; void display_app_enode_map(std::ostream & out) const; void display_expr_bool_var_map(std::ostream & out) const; void display_relevant_exprs(std::ostream & out) const; void display_theories(std::ostream & out) const; void display_eq_detail(std::ostream & out, enode * n) const; void display_parent_eqs(std::ostream & out, enode * n) const; void display_hot_bool_vars(std::ostream & out) const; void display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, literal consequent = false_literal, const char * logic = "AUFLIRA") const; void display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, literal consequent = false_literal, const char * logic = "AUFLIRA") const; void display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, unsigned num_antecedent_eqs, enode_pair const * antecedent_eqs, literal consequent = false_literal, const char * logic = "AUFLIRA") const; void display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, unsigned num_antecedent_eqs, enode_pair const * antecedent_eqs, literal consequent = false_literal, const char * logic = "AUFLIRA") const; void display_assignment_as_smtlib2(std::ostream& out, const char * logic = "AUFLIRA") const; void display_normalized_enodes(std::ostream & out) const; void display_enodes_lbls(std::ostream & out) const; void display_decl2enodes(std::ostream & out) const; void display_subexprs_info(std::ostream & out, expr * n) const; void display_var_occs_histogram(std::ostream & out) const; void display_num_min_occs(std::ostream & out) const; void display_profile_res_sub(std::ostream & out) const; void display_profile(std::ostream & out) const; // ----------------------------------- // // Debugging support // // ----------------------------------- protected: #ifdef Z3DEBUG bool is_watching_clause(literal l, clause const * cls) const; bool check_clause(clause const * cls) const; bool check_clauses(clause_vector const & v) const; bool check_watch_list(literal l) const; bool check_watch_list(unsigned l_idx) const; bool check_bin_watch_lists() const; bool check_enode(enode * n) const; bool check_enodes() const; bool check_invariant() const; bool check_eqc_bool_assignment() const; bool check_missing_clause_propagation(clause_vector const & v) const; bool check_missing_bin_clause_propagation() const; bool check_missing_eq_propagation() const; bool check_missing_congruence() const; bool check_missing_bool_enode_propagation() const; bool check_missing_propagation() const; bool check_relevancy(expr_ref_vector const & v) const; bool check_relevancy() const; bool check_bool_var_vector_sizes() const; bool check_th_diseq_propagation() const; bool check_missing_diseq_conflict() const; bool check_lit_occs(literal l) const; bool check_lit_occs() const; #endif // ----------------------------------- // // Introspection // // ----------------------------------- unsigned get_lemma_avg_activity() const; void display_literal_num_occs(std::ostream & out) const; void display_num_assigned_literals_per_lvl(std::ostream & out) const; // ----------------------------------- // // Auxiliary // // ----------------------------------- void init(); void flush(); config_mode get_config_mode(bool use_static_features) const; virtual void setup_context(bool use_static_features); void setup_components(void); void pop_to_base_lvl(); #ifdef Z3DEBUG bool already_internalized_theory(theory * th) const; bool already_internalized_theory_core(theory * th, expr_ref_vector const & s) const; #endif bool check_preamble(bool reset_cancel); lbool check_finalize(lbool r); // ----------------------------------- // // API // // ----------------------------------- void assert_expr_core(expr * e, proof * pr); public: context(ast_manager & m, smt_params & fp, params_ref const & p = params_ref()); virtual ~context(); /** \brief Return a new context containing the same theories and simplifier plugins, but with an empty set of assertions. If l == 0, then the logic of this context is used in the new context. If p == 0, then this->m_params is used */ context * mk_fresh(symbol const * l = 0, smt_params * p = 0); app * mk_eq_atom(expr * lhs, expr * rhs); bool set_logic(symbol logic) { return m_setup.set_logic(logic); } void register_plugin(simplifier_plugin * s); void register_plugin(theory * th); void assert_expr(expr * e); void assert_expr(expr * e, proof * pr); void push(); void pop(unsigned num_scopes); lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0, bool reset_cancel = true); lbool setup_and_check(bool reset_cancel = true); // return 'true' if assertions are inconsistent. bool reduce_assertions(); bool resource_limits_exceeded(); failure get_last_search_failure() const; proof * get_proof(); void get_relevant_labels(expr* cnstr, buffer & result); void get_relevant_labeled_literals(bool at_lbls, expr_ref_vector & result); void get_relevant_literals(expr_ref_vector & result); void get_guessed_literals(expr_ref_vector & result); void internalize_assertion(expr * n, proof * pr, unsigned generation); void internalize_instance(expr * body, proof * pr, unsigned generation) { internalize_assertion(body, pr, generation); #ifndef SMTCOMP if (relevancy()) m_case_split_queue->internalize_instance_eh(body, generation); #endif } bool already_internalized() const { return m_e_internalized_stack.size() > 2 || m_b_internalized_stack.size() > 1; } unsigned get_unsat_core_size() const { return m_unsat_core.size(); } expr * get_unsat_core_expr(unsigned idx) const { return m_unsat_core.get(idx); } void get_model(model_ref & m) const; bool update_model(bool refinalize); void get_proto_model(proto_model_ref & m) const; unsigned get_num_asserted_formulas() const { return m_asserted_formulas.get_num_formulas(); } unsigned get_asserted_formulas_last_level() const { return m_asserted_formulas.get_formulas_last_level(); } expr * get_asserted_formula(unsigned idx) const { return m_asserted_formulas.get_formula(idx); } proof * get_asserted_formula_proof(unsigned idx) const { return m_asserted_formulas.get_formula_proof(idx); } expr * const * get_asserted_formulas() const { return m_asserted_formulas.get_formulas(); } proof * const * get_asserted_formula_proofs() const { return m_asserted_formulas.get_formula_proofs(); } void get_assumptions_core(ptr_vector & result); void get_assertions(ptr_vector & result) { m_asserted_formulas.get_assertions(result); } void display(std::ostream & out) const; void display_unsat_core(std::ostream & out) const; void collect_statistics(::statistics & st) const; void display_statistics(std::ostream & out) const; void display_istatistics(std::ostream & out) const; // ----------------------------------- // // Macros // // ----------------------------------- public: unsigned get_num_macros() const { return m_asserted_formulas.get_num_macros(); } unsigned get_first_macro_last_level() const { return m_asserted_formulas.get_first_macro_last_level(); } func_decl * get_macro_func_decl(unsigned i) const { return m_asserted_formulas.get_macro_func_decl(i); } func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const { return m_asserted_formulas.get_macro_interpretation(i, interp); } quantifier * get_macro_quantifier(func_decl * f) const { return m_asserted_formulas.get_macro_quantifier(f); } void insert_macro(func_decl * f, quantifier * m, proof * pr) { m_asserted_formulas.insert_macro(f, m, pr); } }; }; #endif /* SMT_CONTEXT_H_ */ z3-z3-4.4.1/src/smt/smt_context_inv.cpp000066400000000000000000000433171260446376700177560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_context_inv.cpp Abstract: SMT logical contexts: invariant Author: Leonardo de Moura (leonardo) 2008-02-21. Revision History: --*/ #include"smt_context.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"ast_smt2_pp.h" namespace smt { #ifdef Z3DEBUG bool context::is_watching_clause(literal l, clause const * cls) const { watch_list & wl = const_cast(m_watches[l.index()]); return wl.find_clause(cls) != wl.end_clause(); } bool context::check_clause(clause const * cls) const { SASSERT(is_watching_clause(~cls->get_literal(0), cls)); SASSERT(is_watching_clause(~cls->get_literal(1), cls)); if (lit_occs_enabled()) { unsigned num_lits = cls->get_num_literals(); for (unsigned i = 0; i < num_lits; i++) { literal l = cls->get_literal(i); SASSERT(m_lit_occs[l.index()].contains(const_cast(cls))); } } return true; } bool context::check_clauses(clause_vector const & v) const { clause_vector::const_iterator it = v.begin(); clause_vector::const_iterator end = v.end(); for (; it != end; ++it) { clause * cls = *it; if (!cls->deleted()) check_clause(cls); } return true; } bool context::check_watch_list(literal l) const { watch_list & wl = const_cast(m_watches[l.index()]); l.neg(); watch_list::clause_iterator it = wl.begin_clause(); watch_list::clause_iterator end = wl.end_clause(); for (; it != end; ++it) { clause * cls = *it; TRACE("watch_list", tout << "l: "; display_literal(tout, l); tout << "\n"; display_clause(tout, cls); tout << "\n";); SASSERT(l == cls->get_literal(0) || l == cls->get_literal(1)); } return true; } bool context::check_watch_list(unsigned l_idx) const { return check_watch_list(to_literal(l_idx)); } bool context::check_bin_watch_lists() const { if (binary_clause_opt_enabled()) { vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l1 = to_literal(l_idx); watch_list const & wl = *it; literal const * it2 = wl.begin_literals(); literal const * end2 = wl.end_literals(); for (; it2 != end2; ++it2) { literal l2 = *it2; watch_list const & wl = m_watches[(~l2).index()]; SASSERT(wl.find_literal(~l1) != wl.end_literals()); } } } return true; } bool context::check_lit_occs(literal l) const { clause_set const & v = m_lit_occs[l.index()]; clause_set::iterator it = v.begin(); clause_set::iterator end = v.end(); for (; it != end; ++it) { clause * cls = *it; unsigned num = cls->get_num_literals(); unsigned i = 0; for (; i < num; i++) if (cls->get_literal(i) == l) break; CTRACE("lit_occs", !(i < num), tout << i << " " << num << "\n"; display_literal(tout, l); tout << "\n"; display_clause(tout, cls); tout << "\n"; tout << "l: " << l.index() << " cls: "; for (unsigned j = 0; j < num; j++) { tout << cls->get_literal(j).index() << " "; } tout << "\n"; display_clause_detail(tout, cls); tout << "\n";); SASSERT(i < num); } return true; } bool context::check_lit_occs() const { if (lit_occs_enabled()) { unsigned num_lits = get_num_bool_vars() * 2; for (unsigned l_idx = 0; l_idx < num_lits; ++l_idx) { check_lit_occs(to_literal(l_idx)); } } return true; } bool context::check_enode(enode * n) const { SASSERT(n->check_invariant()); bool is_true_eq = n->is_true_eq(); bool cg_inv = n->get_num_args() == 0 || (!is_true_eq && (!n->is_cgc_enabled() || n->is_cgr() == (m_cg_table.contains_ptr(n)))) || (is_true_eq && !m_cg_table.contains_ptr(n)); CTRACE("check_enode", !cg_inv, tout << "n: #" << n->get_owner_id() << ", m_cg: #" << n->m_cg->get_owner_id() << ", contains: " << m_cg_table.contains(n) << "\n"; display(tout);); SASSERT(cg_inv); return true; } bool context::check_enodes() const { ptr_vector::const_iterator it = m_enodes.begin(); ptr_vector::const_iterator end = m_enodes.end(); for (; it != end; ++it) { check_enode(*it); } return true; } bool context::check_invariant() const { check_lit_occs(); check_bin_watch_lists(); check_clauses(m_aux_clauses); check_clauses(m_lemmas); check_enodes(); SASSERT(m_cg_table.check_invariant()); return true; } bool context::check_missing_clause_propagation(clause_vector const & v) const { clause_vector::const_iterator it = v.begin(); clause_vector::const_iterator end = v.end(); for (; it != end; ++it) { CTRACE("missing_propagation", is_unit_clause(*it), display_clause_detail(tout, *it); tout << "\n";); SASSERT(!is_unit_clause(*it)); } return true; } bool context::check_missing_bin_clause_propagation() const { if (binary_clause_opt_enabled()) { SASSERT(m_watches.size() == m_assignment.size()); vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l = to_literal(l_idx); watch_list const & wl = *it; if (get_assignment(l) == l_true) { literal const * it2 = wl.begin_literals(); literal const * end2 = wl.end_literals(); for (; it2 != end2; ++it2) { literal l2 = *it2; SASSERT(get_assignment(l2) == l_true); } } } } return true; } bool context::check_missing_eq_propagation() const { ptr_vector::const_iterator it = m_enodes.begin(); ptr_vector::const_iterator end = m_enodes.end(); for (; it != end; ++it) { enode * n = *it; SASSERT(!n->is_true_eq() || get_assignment(n) == l_true); if (n->is_eq() && get_assignment(n) == l_true) { SASSERT(n->is_true_eq()); } } return true; } bool context::check_missing_congruence() const { ptr_vector::const_iterator it = m_enodes.begin(); ptr_vector::const_iterator end = m_enodes.end(); for (; it != end; ++it) { enode * n = *it; ptr_vector::const_iterator it2 = m_enodes.begin(); for (; it2 != end; ++it2) { enode * n2 = *it2; if (n->get_root() != n2->get_root()) { if (n->is_true_eq() && n2->is_true_eq()) continue; CTRACE("missing_propagation", congruent(n, n2), tout << mk_pp(n->get_owner(), m_manager) << "\n" << mk_pp(n2->get_owner(), m_manager) << "\n"; display(tout);); SASSERT(!congruent(n, n2)); } } } return true; } bool context::check_missing_bool_enode_propagation() const { ptr_vector::const_iterator it = m_enodes.begin(); ptr_vector::const_iterator end = m_enodes.end(); for (; it != end; ++it) { enode * n = *it; if (m_manager.is_bool(n->get_owner()) && get_assignment(n) == l_undef) { enode * first = n; do { CTRACE("missing_propagation", get_assignment(n) != l_undef, tout << mk_pp(first->get_owner(), m_manager) << "\nassignment: " << get_assignment(first) << "\n" << mk_pp(n->get_owner(), m_manager) << "\nassignment: " << get_assignment(n) << "\n";); SASSERT(get_assignment(n) == l_undef); n = n->get_next(); } while (n != first); } } return true; } bool context::check_missing_propagation() const { check_missing_clause_propagation(m_lemmas); check_missing_clause_propagation(m_aux_clauses); check_missing_bin_clause_propagation(); // check_missing_eq_propagation(); check_missing_congruence(); check_missing_bool_enode_propagation(); return true; } bool context::check_relevancy(expr_ref_vector const & v) const { return m_relevancy_propagator->check_relevancy(v); } bool context::check_relevancy() const { if (!relevancy()) return true; check_relevancy(m_b_internalized_stack); check_relevancy(m_e_internalized_stack); unsigned sz = m_asserted_formulas.get_num_formulas(); for (unsigned i = 0; i < sz; i++) { expr * n = m_asserted_formulas.get_formula(i); if (m_manager.is_or(n)) { CTRACE("relevancy_bug", !is_relevant(n), tout << "n: " << mk_ismt2_pp(n, m_manager) << "\n";); SASSERT(is_relevant(n)); TRACE("check_relevancy", tout << "checking:\n" << mk_ll_pp(n, m_manager) << "\n";); SASSERT(m_relevancy_propagator->check_relevancy_or(to_app(n), true)); } else if (m_manager.is_not(n)) { CTRACE("relevancy_bug", !is_relevant(to_app(n)->get_arg(0)), tout << "n: " << mk_ismt2_pp(n, m_manager) << "\n";); SASSERT(is_relevant(to_app(n)->get_arg(0))); } else { CTRACE("relevancy_bug", !is_relevant(n), tout << "n: " << mk_ismt2_pp(n, m_manager) << "\n";); SASSERT(is_relevant(n)); } } return true; } /** \brief Check if expressions attached to bool_variables and enodes have a consistent assignment. For all a, b. root(a) == root(b) ==> get_assignment(a) == get_assignment(b) */ bool context::check_eqc_bool_assignment() const { ptr_vector::const_iterator it = m_enodes.begin(); ptr_vector::const_iterator end = m_enodes.end(); for (; it != end; ++it) { enode * e = *it; if (m_manager.is_bool(e->get_owner())) { enode * r = e->get_root(); CTRACE("eqc_bool", get_assignment(e) != get_assignment(r), tout << "#" << e->get_owner_id() << "\n" << mk_pp(e->get_owner(), m_manager) << "\n"; tout << "#" << r->get_owner_id() << "\n" << mk_pp(r->get_owner(), m_manager) << "\n"; tout << "assignments: " << get_assignment(e) << " " << get_assignment(r) << "\n"; display(tout);); SASSERT(get_assignment(e) == get_assignment(r)); } } return true; } bool context::check_bool_var_vector_sizes() const { SASSERT(m_assignment.size() == 2 * m_bdata.size()); SASSERT(m_watches.size() == 2 * m_bdata.size()); SASSERT(m_bdata.size() == m_activity.size()); SASSERT(m_bool_var2expr.size() == m_bdata.size()); return true; } /** \brief Check the following property: - for every equality atom (= lhs rhs) assigned to false, relevant: if lhs->get_root() and rhs->get_root() are attached to theory variables v1 and v2 of theory t, then there is an entry (t, v1', v2') in m_propagated_th_diseqs such that, (= get_enode(v1') get_enode(v2')) is congruent to (= lhs rhs). */ bool context::check_th_diseq_propagation() const { TRACE("check_th_diseq_propagation", tout << "m_propagated_th_diseqs.size() " << m_propagated_th_diseqs.size() << "\n";); int num = get_num_bool_vars(); for (bool_var v = 0; v < num; v++) { if (has_enode(v)) { enode * n = bool_var2enode(v); if (n->is_eq() && is_relevant(n) && get_assignment(v) == l_false) { TRACE("check_th_diseq_propagation", tout << "checking: #" << n->get_owner_id() << " " << mk_bounded_pp(n->get_owner(), m_manager) << "\n";); enode * lhs = n->get_arg(0)->get_root(); enode * rhs = n->get_arg(1)->get_root(); if (rhs->is_interpreted() && lhs->is_interpreted()) continue; TRACE("check_th_diseq_propagation", tout << "num. theory_vars: " << lhs->get_num_th_vars() << " " << mk_pp(m_manager.get_sort(lhs->get_owner()), m_manager) << "\n";); theory_var_list * l = lhs->get_th_var_list(); while (l) { theory_id th_id = l->get_th_id(); theory * th = get_theory(th_id); TRACE("check_th_diseq_propagation", tout << "checking theory: " << m_manager.get_family_name(th_id) << "\n";); // if the theory doesn't use diseqs, then the diseqs are not propagated. if (th->use_diseqs() && rhs->get_th_var(th_id) != null_theory_var) { // lhs and rhs are attached to theory th_id svector::const_iterator it = m_propagated_th_diseqs.begin(); svector::const_iterator end = m_propagated_th_diseqs.end(); for (; it != end; ++it) { if (it->m_th_id == th_id) { enode * lhs_prime = th->get_enode(it->m_lhs)->get_root(); enode * rhs_prime = th->get_enode(it->m_rhs)->get_root(); TRACE("check_th_diseq_propagation", tout << m_manager.get_family_name(it->m_th_id) << "\n";); if ((lhs == lhs_prime && rhs == rhs_prime) || (rhs == lhs_prime && lhs == rhs_prime)) { TRACE("check_th_diseq_propagation", tout << "ok v" << v << " " << get_assignment(v) << "\n";); break; } } } if (it == end) { // missed theory diseq propagation display(std::cout); std::cout << "checking theory: " << m_manager.get_family_name(th_id) << "\n"; std::cout << "root: #" << n->get_root()->get_owner_id() << " node: #" << n->get_owner_id() << "\n"; std::cout << mk_pp(n->get_owner(), m_manager) << "\n"; std::cout << "lhs: #" << lhs->get_owner_id() << ", rhs: #" << rhs->get_owner_id() << "\n"; std::cout << mk_bounded_pp(lhs->get_owner(), m_manager) << " " << mk_bounded_pp(rhs->get_owner(), m_manager) << "\n"; } SASSERT(it != end); } l = l->get_next(); } } } } return true; } bool context::check_missing_diseq_conflict() const { svector::const_iterator it = m_diseq_vector.begin(); svector::const_iterator end = m_diseq_vector.end(); for (; it != end; ++it) { enode * n1 = it->first; enode * n2 = it->second; if (n1->get_root() == n2->get_root()) { TRACE("diseq_bug", tout << "n1: #" << n1->get_owner_id() << ", n2: #" << n2->get_owner_id() << ", r: #" << n1->get_root()->get_owner_id() << "\n"; tout << "n1 parents:\n"; display_parent_eqs(tout, n1); tout << "n2 parents:\n"; display_parent_eqs(tout, n2); tout << "r parents:\n"; display_parent_eqs(tout, n1->get_root()); ); UNREACHABLE(); } } return true; } #endif /** \brief validate unsat core returned by */ void context::validate_unsat_core() { if (!get_fparams().m_core_validate) { return; } context ctx(get_manager(), get_fparams(), get_params()); ptr_vector assertions; get_assertions(assertions); unsigned sz = assertions.size(); for (unsigned i = 0; i < sz; ++i) { ctx.assert_expr(assertions[i]); } sz = m_unsat_core.size(); for (unsigned i = 0; i < sz; ++i) { ctx.assert_expr(m_unsat_core.get(i)); } lbool res = ctx.check(); switch (res) { case l_false: break; default: throw default_exception("Core could not be validated"); } } }; z3-z3-4.4.1/src/smt/smt_context_pp.cpp000066400000000000000000000553631260446376700176050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_context_pp.cpp Abstract: SMT logical context: pretty printing Author: Leonardo de Moura (leonardo) 2008-02-21. Revision History: --*/ #include"smt_context.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"ast_smt_pp.h" #include"stats.h" namespace smt { std::ostream& context::display_last_failure(std::ostream& out) const { switch(m_last_search_failure) { case OK: return out << "OK"; case UNKNOWN: return out << "UNKNOWN"; case TIMEOUT: return out << "TIMEOUT"; case MEMOUT: return out << "MEMOUT"; case CANCELED: return out << "CANCELED"; case NUM_CONFLICTS: return out << "NUM_CONFLICTS"; case THEORY: if (!m_incomplete_theories.empty()) { ptr_vector::const_iterator it = m_incomplete_theories.begin(); ptr_vector::const_iterator end = m_incomplete_theories.end(); for (bool first = true; it != end; ++it) { if (first) first = false; else out << " "; out << (*it)->get_name(); } } else { out << "THEORY"; } return out; case QUANTIFIERS: return out << "QUANTIFIERS"; } UNREACHABLE(); return out << "?"; } std::string context::last_failure_as_string() const { std::string r; switch(m_last_search_failure) { case OK: r = "ok"; break; case TIMEOUT: r = "timeout"; break; case MEMOUT: r = "memout"; break; case CANCELED: r = "canceled"; break; case NUM_CONFLICTS: r = "max-conflicts-reached"; break; case THEORY: { r = "(incomplete (theory"; ptr_vector::const_iterator it = m_incomplete_theories.begin(); ptr_vector::const_iterator end = m_incomplete_theories.end(); for (; it != end; ++it) { r += " "; r += (*it)->get_name(); } r += "))"; break; } case QUANTIFIERS: r = "(incomplete quantifiers)"; break; case UNKNOWN: r = "incomplete"; break; } return r; } void context::display_asserted_formulas(std::ostream & out) const { m_asserted_formulas.display_ll(out, get_pp_visited()); } void context::display_literal(std::ostream & out, literal l) const { l.display_compact(out, m_bool_var2expr.c_ptr()); } void context::display_literals(std::ostream & out, unsigned num_lits, literal const * lits) const { display_compact(out, num_lits, lits, m_bool_var2expr.c_ptr()); } void context::display_literals_verbose(std::ostream & out, unsigned num_lits, literal const * lits) const { display_verbose(out, m_manager, num_lits, lits, m_bool_var2expr.c_ptr()); } void context::display_literal_info(std::ostream & out, literal l) const { l.display_compact(out, m_bool_var2expr.c_ptr()); if (l.sign()) out << " (not " << mk_bounded_pp(bool_var2expr(l.var()), m_manager, 10) << ") "; else out << " " << mk_bounded_pp(bool_var2expr(l.var()), m_manager, 10) << " "; out << "relevant: " << is_relevant(bool_var2expr(l.var())) << ", val: " << get_assignment(l) << "\n"; } void context::display_watch_list(std::ostream & out, literal l) const { display_literal(out, l); out << " watch_list:\n"; watch_list & wl = const_cast(m_watches[l.index()]); watch_list::clause_iterator it = wl.begin_clause(); watch_list::clause_iterator end = wl.end_clause(); for (; it != end; ++it) { display_clause(out, *it); out << "\n"; } } void context::display_watch_lists(std::ostream & out) const { unsigned s = m_watches.size(); for (unsigned l_idx = 0; l_idx < s; l_idx++) { literal l = to_literal(l_idx); display_watch_list(out, l); out << "\n"; } } void context::display_enode_defs(std::ostream & out) const { ptr_vector::const_iterator it = m_enodes.begin(); ptr_vector::const_iterator end = m_enodes.end(); for (; it != end; ++it) { expr * n = (*it)->get_owner(); ast_def_ll_pp(out, m_manager, n, get_pp_visited(), true, false); } } void context::display_bool_var_defs(std::ostream & out) const { unsigned num = get_num_bool_vars(); for (unsigned v = 0; v < num; v++) { expr * n = m_bool_var2expr[v]; ast_def_ll_pp(out, m_manager, n, get_pp_visited(), true, false); } } void context::display_clause_detail(std::ostream & out, clause const * cls) const { out << "lemma: " << cls->is_lemma() << "\n"; unsigned num_lits = cls->get_num_literals(); for (unsigned i = 0; i < num_lits; i++) { literal l = cls->get_literal(i); display_literal(out, l); out << ", val: " << get_assignment(l) << ", lvl: " << get_assign_level(l) << ", ilvl: " << get_intern_level(l.var()) << ", var: " << l.var() << "\n" << mk_pp(bool_var2expr(l.var()), m_manager) << "\n\n"; } } void context::display_clause(std::ostream & out, clause const * cls) const { cls->display_compact(out, m_manager, m_bool_var2expr.c_ptr()); } void context::display_clauses(std::ostream & out, ptr_vector const & v) const { ptr_vector::const_iterator it = v.begin(); ptr_vector::const_iterator end = v.end(); for (; it != end; ++it) { display_clause(out, *it); out << "\n"; } } void context::display_binary_clauses(std::ostream & out) const { bool first = true; vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l1 = to_literal(l_idx); literal neg_l1 = ~l1; watch_list const & wl = *it; literal const * it2 = wl.begin_literals(); literal const * end2 = wl.end_literals(); for (; it2 != end2; ++it2) { literal l2 = *it2; if (l1.index() < l2.index()) { if (first) { out << "binary clauses:\n"; first = false; } out << "(clause "; display_literal(out, neg_l1); out << " "; display_literal(out, l2); out << ")\n"; } } } } void context::display_assignment(std::ostream & out) const { if (!m_assigned_literals.empty()) { out << "current assignment:\n"; literal_vector::const_iterator it = m_assigned_literals.begin(); literal_vector::const_iterator end = m_assigned_literals.end(); for (; it != end; ++it) { display_literal(out, *it); out << " "; } out << "\n"; } } void context::display_assignment_as_smtlib2(std::ostream& out, char const* logic) const { ast_smt_pp pp(m_manager); pp.set_benchmark_name("lemma"); pp.set_status("unknown"); pp.set_logic(logic); literal_vector::const_iterator it = m_assigned_literals.begin(); literal_vector::const_iterator end = m_assigned_literals.end(); for (; it != end; ++it) { expr_ref n(m_manager); literal2expr(*it, n); pp.add_assumption(n); } pp.display_smt2(out, m_manager.mk_true()); } void context::display_eqc(std::ostream & out) const { bool first = true; ptr_vector::const_iterator it = m_enodes.begin(); ptr_vector::const_iterator end = m_enodes.end(); for (; it != end; ++it) { expr * n = (*it)->get_owner(); expr * r = (*it)->get_root()->get_owner(); if (n != r) { if (first) { out << "equivalence classes:\n"; first = false; } out << "#" << n->get_id() << " -> #" << r->get_id() << "\n"; } } } void context::display_app_enode_map(std::ostream & out) const { if (!m_e_internalized_stack.empty()) { out << "expresion -> enode:\n"; unsigned sz = m_e_internalized_stack.size(); for (unsigned i = 0; i < sz; i++) { expr * n = m_e_internalized_stack.get(i); out << "(#" << n->get_id() << " -> e!" << i << ") "; } out << "\n"; } } void context::display_expr_bool_var_map(std::ostream & out) const { if (!m_b_internalized_stack.empty()) { out << "expresion -> bool_var:\n"; unsigned sz = m_b_internalized_stack.size(); for (unsigned i = 0; i < sz; i++) { expr * n = m_b_internalized_stack.get(i); bool_var v = get_bool_var_of_id(n->get_id()); out << "(#" << n->get_id() << " -> p!" << v << ") "; } out << "\n"; } } void context::display_hot_bool_vars(std::ostream & out) const { out << "hot bool vars:\n"; int num = get_num_bool_vars(); for (bool_var v = 0; v < num; v++) { double val = get_activity(v)/m_bvar_inc; if (val > 10.00) { expr * n = m_b_internalized_stack.get(v); out << "#"; out.width(5); out << std::left; out << n->get_id(); out << " "; out.width(12); out << std::right; out << get_activity(v) << " "; out.width(12); out << val; out << "\n"; } } } void context::display_relevant_exprs(std::ostream & out) const { m_relevancy_propagator->display(out); } void context::display_theories(std::ostream & out) const { ptr_vector::const_iterator it = m_theory_set.begin(); ptr_vector::const_iterator end = m_theory_set.end(); for (; it != end; ++it) { theory * th = *it; th->display(out); } } void context::display(std::ostream & out) const { get_pp_visited().reset(); out << "Logical context:\n"; out << "scope-lvl: " << m_scope_lvl << "\n"; out << "base-lvl: " << m_base_lvl << "\n"; out << "search-lvl: " << m_search_lvl << "\n"; out << "inconsistent(): " << inconsistent() << "\n"; out << "m_asserted_formulas.inconsistent(): " << m_asserted_formulas.inconsistent() << "\n"; display_bool_var_defs(out); display_enode_defs(out); display_asserted_formulas(out); if (!m_aux_clauses.empty()) { out << "auxiliary clauses:\n"; display_clauses(out, m_aux_clauses); } if (!m_lemmas.empty()) { out << "lemmas:\n"; display_clauses(out, m_lemmas); } display_binary_clauses(out); display_assignment(out); display_eqc(out); m_cg_table.display_compact(out); m_case_split_queue->display(out); display_expr_bool_var_map(out); display_app_enode_map(out); display_relevant_exprs(out); display_theories(out); display_decl2enodes(out); display_hot_bool_vars(out); } void context::display_eq_detail(std::ostream & out, enode * n) const { SASSERT(n->is_eq()); out << "#" << n->get_owner_id() << ", root: #" << n->get_root()->get_owner_id() << ", cg: #" << n->m_cg->get_owner_id() << ", val: " << get_assignment(enode2bool_var(n)) << ", lhs: #" << n->get_arg(0)->get_owner_id() << ", rhs: #" << n->get_arg(1)->get_owner_id() << ", lhs->root: #" << n->get_arg(0)->get_root()->get_owner_id() << ", rhs->root: #" << n->get_arg(1)->get_root()->get_owner_id() << ", is_marked: " << n->is_marked() << ", is_relevant: " << is_relevant(n) << ", iscope_lvl: " << n->get_iscope_lvl() << "\n"; } void context::display_parent_eqs(std::ostream & out, enode * n) const { enode_vector::iterator it = n->begin_parents(); enode_vector::iterator end = n->end_parents(); for (; it != end; ++it) { enode * parent = *it; if (parent->is_eq()) display_eq_detail(out, parent); } } void context::display_unsat_core(std::ostream & out) const { unsigned sz = m_unsat_core.size(); for (unsigned i = 0; i < sz; i++) out << mk_pp(m_unsat_core.get(i), m_manager) << "\n"; } void context::collect_statistics(::statistics & st) const { st.update("conflicts", m_stats.m_num_conflicts); st.update("decisions", m_stats.m_num_decisions); st.update("propagations", m_stats.m_num_propagations + m_stats.m_num_bin_propagations); st.update("binary propagations", m_stats.m_num_bin_propagations); st.update("restarts", m_stats.m_num_restarts); st.update("final checks", m_stats.m_num_final_checks); st.update("added eqs", m_stats.m_num_add_eq); st.update("mk clause", m_stats.m_num_mk_clause); st.update("del clause", m_stats.m_num_del_clause); st.update("dyn ack", m_stats.m_num_dyn_ack); st.update("interface eqs", m_stats.m_num_interface_eqs); st.update("max generation", m_stats.m_max_generation); st.update("minimized lits", m_stats.m_num_minimized_lits); st.update("num checks", m_stats.m_num_checks); #if 0 // missing? st.update("sat conflicts", m_stats.m_num_sat_conflicts); st.update("mk bool var", m_stats.m_num_mk_bool_var); st.update("del bool var", m_stats.m_num_del_bool_var); st.update("mk enode", m_stats.m_num_mk_enode); st.update("del enode", m_stats.m_num_del_enode); st.update("mk bin clause", m_stats.m_num_mk_bin_clause); st.update("mk lit", m_stats.m_num_mk_lits); st.update("backwd subs", m_stats.m_num_bs); st.update("backwd subs res", m_stats.m_num_bsr); st.update("frwrd subs res", m_stats.m_num_fsr); #endif m_qmanager->collect_statistics(st); m_asserted_formulas.collect_statistics(st); ptr_vector::const_iterator it = m_theory_set.begin(); ptr_vector::const_iterator end = m_theory_set.end(); for (; it != end; ++it) { (*it)->collect_statistics(st); } } void context::display_statistics(std::ostream & out) const { ::statistics st; collect_statistics(st); st.display(out); } void context::display_istatistics(std::ostream & out) const { ::statistics st; collect_statistics(st); st.display_internal(out); } void context::display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, literal consequent, const char * logic) const { ast_smt_pp pp(m_manager); pp.set_benchmark_name("lemma"); pp.set_status("unsat"); pp.set_logic(logic); for (unsigned i = 0; i < num_antecedents; i++) { literal l = antecedents[i]; expr_ref n(m_manager); literal2expr(l, n); pp.add_assumption(n); } expr_ref n(m_manager); literal2expr(~consequent, n); pp.display_smt2(out, n); } static unsigned g_lemma_id = 0; #define BUFFER_SZ 128 void context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, literal consequent, const char * logic) const { char buffer[BUFFER_SZ]; #ifdef _WINDOWS sprintf_s(buffer, BUFFER_SZ, "lemma_%d.smt2", g_lemma_id); #else sprintf(buffer, "lemma_%d.smt2", g_lemma_id); #endif std::ofstream out(buffer); display_lemma_as_smt_problem(out, num_antecedents, antecedents, consequent, logic); out.close(); g_lemma_id++; } void context::display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, unsigned num_eq_antecedents, enode_pair const * eq_antecedents, literal consequent, const char * logic) const { ast_smt_pp pp(m_manager); pp.set_benchmark_name("lemma"); pp.set_status("unsat"); pp.set_logic(logic); for (unsigned i = 0; i < num_antecedents; i++) { literal l = antecedents[i]; expr_ref n(m_manager); literal2expr(l, n); pp.add_assumption(n); } for (unsigned i = 0; i < num_eq_antecedents; i++) { enode_pair const & p = eq_antecedents[i]; expr_ref eq(m_manager); eq = m_manager.mk_eq(p.first->get_owner(), p.second->get_owner()); pp.add_assumption(eq); } expr_ref n(m_manager); literal2expr(~consequent, n); pp.display_smt2(out, n); } void context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, unsigned num_eq_antecedents, enode_pair const * eq_antecedents, literal consequent, const char * logic) const { char buffer[BUFFER_SZ]; #ifdef _WINDOWS sprintf_s(buffer, BUFFER_SZ, "lemma_%d.smt2", g_lemma_id); #else sprintf(buffer, "lemma_%d.smt2", g_lemma_id); #endif std::ofstream out(buffer); display_lemma_as_smt_problem(out, num_antecedents, antecedents, num_eq_antecedents, eq_antecedents, consequent, logic); out.close(); g_lemma_id++; } /** \brief Display enode definitions #n := (f #i_1 ... #i_n), where #i_k is the root of the k-th argument of the enode #n. */ void context::display_normalized_enodes(std::ostream & out) const { out << "normalized enodes:\n"; ptr_vector::const_iterator it = m_enodes.begin(); ptr_vector::const_iterator end = m_enodes.end(); for (; it != end; ++it) { enode * n = *it; out << "#"; out.width(5); out << std::left << n->get_owner_id() << " #"; out.width(5); out << n->get_root()->get_owner_id() << " := " << std::right; unsigned num = n->get_owner()->get_num_args(); if (num > 0) out << "("; out << n->get_decl()->get_name(); if (!n->get_decl()->private_parameters()) display_parameters(out, n->get_decl()->get_num_parameters(), n->get_decl()->get_parameters()); for (unsigned i = 0; i < num; i++) { expr * arg = n->get_owner()->get_arg(i); if (e_internalized(arg)) { enode * n = get_enode(arg)->get_root(); out << " #" << n->get_owner_id(); } else { out << " #" << arg->get_id(); } } if (num > 0) out << ")"; if (is_relevant(n)) out << "\t*"; out << "\n"; } } void context::display_enodes_lbls(std::ostream & out) const { ptr_vector::const_iterator it = m_enodes.begin(); ptr_vector::const_iterator end = m_enodes.end(); for (; it != end; ++it) { enode * n = *it; n->display_lbls(out); } } void context::display_decl2enodes(std::ostream & out) const { out << "decl2enodes:\n"; vector::const_iterator it1 = m_decl2enodes.begin(); vector::const_iterator end1 = m_decl2enodes.end(); for (unsigned id = 0; it1 != end1; ++it1, ++id) { enode_vector const & v = *it1; if (!v.empty()) { out << "id " << id << " ->"; enode_vector::const_iterator it2 = v.begin(); enode_vector::const_iterator end2 = v.end(); for (; it2 != end2; ++it2) out << " #" << (*it2)->get_owner_id(); out << "\n"; } } } void context::display_subexprs_info(std::ostream & out, expr * n) const { ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { expr * n = todo.back(); todo.pop_back(); out << "#"; out.width(6); out << std::left << n->get_id(); out << ", relevant: " << is_relevant(n); if (m_manager.is_bool(n)) { out << ", val: "; out.width(7); out << std::right; if (lit_internalized(n)) out << get_assignment(n); else out << "l_undef"; } if (e_internalized(n)) { enode * e = get_enode(n); out << ", root: #" << e->get_root()->get_owner_id(); } out << "\n"; if (is_app(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) todo.push_back(to_app(n)->get_arg(i)); } } } void context::trace_assign(literal l, b_justification j, bool decision) const { SASSERT(m_manager.has_trace_stream()); std::ostream & out = m_manager.trace_stream(); out << "[assign] "; display_literal(out, l); if (decision) out << " decision"; out << " "; switch (j.get_kind()) { case b_justification::AXIOM: out << "axiom"; break; case b_justification::BIN_CLAUSE: { literal l2 = j.get_literal(); out << "bin-clause "; display_literal(out, l); out << " "; display_literal(out, l2); break; } case b_justification::CLAUSE: { clause * cls = j.get_clause(); out << "clause "; display_literals(out, cls->get_num_literals(), cls->begin_literals()); break; } case b_justification::JUSTIFICATION: out << "justification"; break; default: UNREACHABLE(); break; } out << "\n"; } }; z3-z3-4.4.1/src/smt/smt_context_stat.cpp000066400000000000000000000125011260446376700201240ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_context_stat.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-03-06. Revision History: --*/ #include"smt_context.h" #include"ast_pp.h" namespace smt { unsigned context::get_lemma_avg_activity() const { if (m_lemmas.empty()) return 0; unsigned long long acc = 0; clause_vector::const_iterator it = m_lemmas.begin(); clause_vector::const_iterator end = m_lemmas.end(); for (; it != end; ++it) acc += (*it)->get_activity(); return static_cast(acc / m_lemmas.size()); } void acc_num_occs(clause * cls, unsigned_vector & lit2num_occs) { unsigned num_lits = cls->get_num_literals(); for (unsigned i = 0; i < num_lits; i++) { literal l = cls->get_literal(i); lit2num_occs[l.index()]++; } } void acc_num_occs(clause_vector const & v, unsigned_vector & lit2num_occs) { clause_vector::const_iterator it = v.begin(); clause_vector::const_iterator end = v.end(); for (; it != end; ++it) acc_num_occs(*it, lit2num_occs); } void context::display_literal_num_occs(std::ostream & out) const { unsigned num_lits = m_assignment.size(); unsigned_vector lit2num_occs; lit2num_occs.resize(num_lits, 0); acc_num_occs(m_aux_clauses, lit2num_occs); acc_num_occs(m_lemmas, lit2num_occs); for (unsigned lidx = 0; lidx < num_lits; lidx++) { literal l = to_literal(lidx); if (lit2num_occs[lidx] > 0) { out << lit2num_occs[lidx] << " "; // display_literal(out, l); out << l.sign() << " " << mk_pp(bool_var2expr(l.var()), m_manager); out << "\n"; } } } void context::display_num_assigned_literals_per_lvl(std::ostream & out) const { unsigned n = 0; svector::const_iterator it = m_scopes.begin(); svector::const_iterator end = m_scopes.end(); out << "["; for (; it != end; ++it) { scope const & s = *it; SASSERT(n <= s.m_assigned_literals_lim); out << (s.m_assigned_literals_lim - n) << " "; n = s.m_assigned_literals_lim; } SASSERT(n <= m_assigned_literals.size()); out << (m_assigned_literals.size() - n) << "]"; } void acc_var_num_occs(clause * cls, unsigned_vector & var2num_occs) { unsigned num_lits = cls->get_num_literals(); for (unsigned i = 0; i < num_lits; i++) { literal l = cls->get_literal(i); var2num_occs[l.var()]++; } } void acc_var_num_occs(clause_vector const & v, unsigned_vector & var2num_occs) { clause_vector::const_iterator it = v.begin(); clause_vector::const_iterator end = v.end(); for (; it != end; ++it) acc_var_num_occs(*it, var2num_occs); } void context::display_var_occs_histogram(std::ostream & out) const { unsigned num_vars = get_num_bool_vars(); unsigned_vector var2num_occs; var2num_occs.resize(num_vars, 0); acc_var_num_occs(m_aux_clauses, var2num_occs); acc_var_num_occs(m_lemmas, var2num_occs); unsigned_vector histogram; for (unsigned v = 0; v < num_vars; v++) { unsigned num_occs = var2num_occs[v]; histogram.reserve(num_occs+1, 0); histogram[num_occs]++; } out << "number of atoms having k occs:\n"; unsigned sz = histogram.size(); for (unsigned i = 1; i < sz; i++) if (histogram[i] > 0) out << i << ":" << histogram[i] << " "; out << "\n"; } void acc_var_num_min_occs(clause * cls, unsigned_vector & var2num_min_occs) { unsigned num_lits = cls->get_num_literals(); bool_var min_var = cls->get_literal(0).var(); for (unsigned i = 1; i < num_lits; i++) { bool_var v = cls->get_literal(i).var(); if (v < min_var) min_var = v; } var2num_min_occs[min_var]++; } void acc_var_num_min_occs(clause_vector const & v, unsigned_vector & var2num_min_occs) { clause_vector::const_iterator it = v.begin(); clause_vector::const_iterator end = v.end(); for (; it != end; ++it) acc_var_num_min_occs(*it, var2num_min_occs); } void context::display_num_min_occs(std::ostream & out) const { unsigned num_vars = get_num_bool_vars(); unsigned_vector var2num_min_occs; var2num_min_occs.resize(num_vars, 0); acc_var_num_min_occs(m_aux_clauses, var2num_min_occs); acc_var_num_min_occs(m_lemmas, var2num_min_occs); out << "number of min occs:\n"; for (unsigned v = 0; v < num_vars; v++) { if (var2num_min_occs[v] > 0) out << v << ":" << var2num_min_occs[v] << " "; } out << "\n"; } void context::display_profile_res_sub(std::ostream & out) const { display_var_occs_histogram(std::cerr); display_num_min_occs(std::cerr); std::cerr << "\n"; } void context::display_profile(std::ostream & out) const { if (m_fparams.m_profile_res_sub) display_profile_res_sub(out); } }; z3-z3-4.4.1/src/smt/smt_enode.cpp000066400000000000000000000354601260446376700165100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_enode.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #include"smt_context.h" #include"smt_enode.h" namespace smt { /** \brief Initialize an enode in the given memory position. */ enode * enode::init(ast_manager & m, void * mem, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent) { SASSERT(m.is_bool(owner) || !merge_tf); enode * n = new (mem) enode(); n->m_owner = owner; n->m_root = n; n->m_next = n; n->m_cg = 0; n->m_class_size = 1; n->m_generation = generation; n->m_func_decl_id = UINT_MAX; n->m_mark = false; n->m_mark2 = false; n->m_interpreted = false; n->m_suppress_args = suppress_args; n->m_eq = m.is_eq(owner); n->m_commutative = n->get_num_args() == 2 && owner->get_decl()->is_commutative(); n->m_bool = m.is_bool(owner); n->m_merge_tf = merge_tf; n->m_cgc_enabled = cgc_enabled; n->m_iscope_lvl = iscope_lvl; n->m_lbl_hash = -1; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { enode * arg = app2enode[owner->get_arg(i)->get_id()]; n->m_args[i] = arg; SASSERT(n->get_arg(i) == arg); if (update_children_parent) arg->get_root()->m_parents.push_back(n); } TRACE("mk_enode_detail", tout << "new enode suppress_args: " << n->m_suppress_args << "\n";); SASSERT(n->m_suppress_args == suppress_args); return n; } enode * enode::mk(ast_manager & m, region & r, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent) { SASSERT(m.is_bool(owner) || !merge_tf); unsigned sz = get_enode_size(suppress_args ? 0 : owner->get_num_args()); void * mem = r.allocate(sz); return init(m, mem, app2enode, owner, generation, suppress_args, merge_tf, iscope_lvl, cgc_enabled, update_children_parent); } enode * enode::mk_dummy(ast_manager & m, app2enode_t const & app2enode, app * owner) { unsigned sz = get_enode_size(owner->get_num_args()); void * mem = alloc_svect(char, sz); return init(m, mem, app2enode, owner, 0, false, false, 0, true, false); } void enode::del_eh(ast_manager & m, bool update_children_parent) { SASSERT(m_class_size == 1); SASSERT(m_root == this); SASSERT(m_next == this); unsigned num_args = get_num_args(); for (unsigned i = 0; i < num_args; i++) { enode * arg = get_arg(i); if (update_children_parent) { SASSERT(arg->get_root()->m_parents.back() == this); arg->get_root()->m_parents.pop_back(); } } this->~enode(); } unsigned enode::get_num_th_vars() const { unsigned r = 0; theory_var_list const * l = get_th_var_list(); while(l) { r++; l = l->get_next(); } return r; } /** \brief Return the theory var (in theory th_id) associated with the enode. Return null_theory_var if the enode is not associated with a variable of theory th_id */ theory_var enode::get_th_var(theory_id th_id) const { if (m_th_var_list.get_th_var() == null_theory_var) return null_theory_var; theory_var_list const * l = &m_th_var_list; while (l) { if (l->get_th_id() == th_id) { return l->get_th_var(); } l = l->get_next(); } return null_theory_var; } /** \brief Add the entry (v, id) to the list of theory variables. */ void enode::add_th_var(theory_var v, theory_id id, region & r) { #ifdef Z3DEBUG unsigned old_size = get_num_th_vars(); #endif SASSERT(get_th_var(id) == null_theory_var); if (m_th_var_list.get_th_var() == null_theory_var) { m_th_var_list.set_th_var(v); m_th_var_list.set_th_id(id); m_th_var_list.set_next(0); } else { theory_var_list * l = &m_th_var_list; while (l->get_next() != 0) { SASSERT(l->get_th_id() != id); l = l->get_next(); } SASSERT(l); SASSERT(l->get_next() == 0); theory_var_list * new_cell = new (r) theory_var_list(id, v); l->set_next(new_cell); } SASSERT(get_num_th_vars() == old_size + 1); SASSERT(get_th_var(id) == v); } /** \brief Replace the entry (v', id) with the entry (v, id). The enode must have an entry (v', id) */ void enode::replace_th_var(theory_var v, theory_id id) { SASSERT(get_th_var(id) != null_theory_var); theory_var_list * l = get_th_var_list(); while (l) { if (l->get_th_id() == id) { l->set_th_var(v); return; } l = l->get_next(); } UNREACHABLE(); } /** \brief Delete theory variable. It assumes the enode is associated with a variable of the given theory. */ void enode::del_th_var(theory_id id) { SASSERT(get_th_var(id) != null_theory_var); if (m_th_var_list.get_th_id() == id) { theory_var_list * next = m_th_var_list.get_next(); if (next == 0) { // most common case m_th_var_list.set_th_var(null_theory_var); m_th_var_list.set_th_id(null_theory_id); m_th_var_list.set_next(0); } else { m_th_var_list = *next; } } else { theory_var_list * prev = get_th_var_list(); theory_var_list * l = prev->get_next(); while (l) { SASSERT(prev->get_next() == l); if (l->get_th_id() == id) { prev->set_next(l->get_next()); return; } prev = l; l = l->get_next(); } UNREACHABLE(); } } /** \brief Push old value of generation on the context trail stack and update the generation. */ void enode::set_generation(context & ctx, unsigned generation) { if (m_generation == generation) return; ctx.push_trail(value_trail(m_generation)); m_generation = generation; } void enode::set_lbl_hash(context & ctx) { SASSERT(m_lbl_hash == -1); // m_lbl_hash should be different from -1, if and only if, // there is a pattern that contains the enode. So, // I use a trail to restore the value of m_lbl_hash to -1. ctx.push_trail(value_trail(m_lbl_hash)); unsigned h = hash_u(get_owner_id()); m_lbl_hash = h & (APPROX_SET_CAPACITY - 1); // propagate modification to the root m_lbls set. approx_set & r_lbls = m_root->m_lbls; if (!r_lbls.may_contain(m_lbl_hash)) { ctx.push_trail(value_trail(r_lbls)); r_lbls.insert(m_lbl_hash); } } enode * enode::get_eq_enode_with_min_gen() { if (m_generation == 0) return this; enode * r = this; enode * curr = this; do { if (curr->m_generation < r->m_generation) { r = curr; if (r->m_generation == 0) return r; } curr = curr->m_next; } while (curr != this); return r; } #ifdef Z3DEBUG bool enode::check_invariant() const { unsigned class_size = 0; bool found_root = false; bool found_this = false; bool has_interpreted = false; // "Equivalence" class structure. enode const * curr = this; do { SASSERT(curr->m_root == m_root); class_size++; if (curr == m_root) found_root = true; if (curr == this) found_this = true; if (curr->is_interpreted()) has_interpreted = true; curr = curr->m_next; } while (curr != this); SASSERT(found_root); SASSERT(found_this); SASSERT(this != m_root || class_size == m_class_size); SASSERT(!has_interpreted || m_root->is_interpreted()); // Parent use-list if (this == m_root) { enode_vector::const_iterator it2 = m_parents.begin(); enode_vector::const_iterator end2 = m_parents.end(); for (; it2 != end2; ++it2) { enode const * parent = *it2; unsigned i = 0; unsigned num_args = parent->get_num_args(); SASSERT(num_args > 0); for (; i < num_args; i++) { enode * arg = parent->get_arg(i); if (arg->get_root() == m_root) break; } SASSERT(i < num_args); } } // Proof tree // m_root is reachable from "this" by following the transitivity proof SASSERT(trans_reaches(m_root)); SASSERT(check_parent_invariant()); return true; } /** \brief Return true if the node is n or n is reached following the m_proof.m_target pointers */ bool enode::trans_reaches(enode * n) const { const enode * curr = this; while (curr != 0) { if (curr == n) { return true; } curr = curr->m_trans.m_target; } return false; } bool enode::check_parent_invariant() const { if (this != m_root) return true; enode const * curr = m_root; do { if (curr != m_root) { enode_vector::const_iterator it = curr->m_parents.begin(); enode_vector::const_iterator end = curr->m_parents.end(); for (; it != end; ++it) { enode * p = *it; if (!p->is_true_eq() && !m_root->contains_parent_congruent_to(p)) { UNREACHABLE(); } } } curr = curr->m_next; } while (curr != m_root); return true; } bool enode::contains_parent_congruent_to(enode * p) const { enode_vector::const_iterator it = m_parents.begin(); enode_vector::const_iterator end = m_parents.end(); for (; it != end; ++it) { enode * curr = *it; if (congruent(curr, p)) return true; } return false; } #endif void enode::display_lbls(std::ostream & out) const { out << "#" << get_owner_id() << " -> #" << get_root()->get_owner_id() << ", lbls: " << get_lbls() << ", plbls: " << get_plbls() << ", root->lbls: " << get_root()->get_lbls() << ", root->plbls: " << get_root()->get_plbls(); if (has_lbl_hash()) out << ", lbl-hash: " << static_cast(get_lbl_hash()); out << "\n"; } bool congruent(enode * n1, enode * n2, bool & comm) { comm = false; if (n1->get_owner()->get_decl() != n2->get_owner()->get_decl()) return false; unsigned num_args = n1->get_num_args(); if (num_args != n2->get_num_args()) return false; if (n1->is_commutative()) { enode * c1_1 = n1->get_arg(0)->get_root(); enode * c1_2 = n1->get_arg(1)->get_root(); enode * c2_1 = n2->get_arg(0)->get_root(); enode * c2_2 = n2->get_arg(1)->get_root(); if (c1_1 == c2_1 && c1_2 == c2_2) { return true; } if (c1_1 == c2_2 && c1_2 == c2_1) { comm = true; return true; } return false; } else { for (unsigned i = 0; i < num_args; i++) if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) return false; return true; } } unsigned get_max_generation(unsigned num_enodes, enode * const * enodes) { unsigned max = 0; for (unsigned i = 0; i < num_enodes; i++) { unsigned curr = enodes[i]->get_generation(); if (curr > max) max = curr; } return max; } void unmark_enodes(unsigned num_enodes, enode * const * enodes) { for (unsigned i = 0; i < num_enodes; i++) enodes[i]->unset_mark(); } void unmark_enodes2(unsigned num_enodes, enode * const * enodes) { for (unsigned i = 0; i < num_enodes; i++) enodes[i]->unset_mark2(); } tmp_enode::tmp_enode(): m_app(0), m_capacity(0), m_enode_data(0) { SASSERT(m_app.get_app()->get_decl() == 0); set_capacity(5); } tmp_enode::~tmp_enode() { dealloc_svect(m_enode_data); } void tmp_enode::set_capacity(unsigned new_capacity) { SASSERT(new_capacity > m_capacity); if (m_enode_data) dealloc_svect(m_enode_data); m_capacity = new_capacity; unsigned sz = sizeof(enode) + m_capacity * sizeof(enode*); m_enode_data = alloc_svect(char, sz); memset(m_enode_data, 0, sz); enode * n = get_enode(); n->m_owner = m_app.get_app(); n->m_root = n; n->m_next = n; n->m_class_size = 1; n->m_cgc_enabled = true; n->m_func_decl_id = UINT_MAX; } enode * tmp_enode::set(func_decl * f, unsigned num_args, enode * const * args) { if (num_args > m_capacity) set_capacity(num_args * 2); enode * r = get_enode(); if (m_app.get_app()->get_decl() != f) { r->m_func_decl_id = UINT_MAX; } m_app.set_decl(f); m_app.set_num_args(num_args); r->m_commutative = num_args == 2 && f->is_commutative(); memcpy(get_enode()->m_args, args, sizeof(enode*)*num_args); return r; } void tmp_enode::reset() { get_enode()->m_func_decl_id = UINT_MAX; } }; z3-z3-4.4.1/src/smt/smt_enode.h000066400000000000000000000304021260446376700161440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_enode.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #ifndef SMT_ENODE_H_ #define SMT_ENODE_H_ #include"ast.h" #include"smt_types.h" #include"smt_eq_justification.h" #include"smt_theory_var_list.h" #include"approx_set.h" namespace smt { /** \brief Justification for the transitivity rule. */ struct trans_justification { enode * m_target; eq_justification m_justification; trans_justification(): m_target(0), m_justification(null_eq_justification) { } }; /** \ brief Use sparse maps in SMT solver. Define this to use hash maps rather than vectors over ast nodes. This is useful in the case there are many solvers, each referencing few nodes from a large ast manager. There is some unknown performance penalty for this. */ // #define SPARSE_MAP #ifndef SPARSE_MAP typedef ptr_vector app2enode_t; // app -> enode #else class app2enode_t : public u_map { public: void setx(unsigned x, enode *val, enode *def){ if (val == 0) erase(x); else insert(x,val); } }; #endif class tmp_enode; /** \brief Aditional data-structure for implementing congruence closure, equality propagation, and the theory central bus of equalities. */ class enode { app * m_owner; //!< The application that 'owns' this enode. enode * m_root; //!< Representative of the equivalence class enode * m_next; //!< Next element in the equivalence class. enode * m_cg; unsigned m_class_size; //!< Size of the equivalence class if the enode is the root. unsigned m_generation; //!< Tracks how many quantifier instantiation rounds were needed to generate this enode. unsigned m_func_decl_id; //!< Id generated by the congruence table for fast indexing. unsigned m_mark:1; //!< Multi-purpose auxiliary mark. unsigned m_mark2:1; //!< Multi-purpose auxiliary mark. unsigned m_interpreted:1; //!< True if the node is an interpreted constant. unsigned m_suppress_args:1; //!< True if the arguments of m_owner should not be accessed by this enode. unsigned m_eq:1; //!< True if it is an equality unsigned m_commutative:1; //!< True if commutative app unsigned m_bool:1; //!< True if it is a boolean enode unsigned m_merge_tf:1; //!< True if the enode should be merged with true/false when the associated boolean variable is assigned. unsigned m_cgc_enabled:1; //!< True if congruence closure is enabled for this enode. unsigned m_iscope_lvl; //!< When the enode was internalized /* The following property is valid for m_parents If this = m_root, then for every term f(a) such that a->get_root() == m_root, there is a f(b) in m_parents such that b->get_root() == m_root, and f(a) and f(b) are congruent. Remark: f(a) and f(b) may have other arguments. Exception: If f(a) and f(b) are terms of the form (= a c) and (= b d), then m_parents will not contains (= b d) if b->get_root() == d->get_root(). Remark regarding relevancy propagation: relevancy is propagated to all elements of an equivalence class. So, if there is a f(a) that is relevant, then the congruent f(b) in m_parents will also be relevant. */ enode_vector m_parents; //!< Parent enodes of the equivalence class. theory_var_list m_th_var_list; //!< List of theories that 'care' about this enode. trans_justification m_trans; //!< A justification for the enode being equal to its root. signed char m_lbl_hash; //!< It is different from -1, if enode is used in a pattern approx_set m_lbls; approx_set m_plbls; enode * m_args[0]; //!< Cached args friend class context; friend class euf_manager; friend class conflict_resolution; theory_var_list * get_th_var_list() { return m_th_var_list.get_th_var() == null_theory_var ? 0 : &m_th_var_list; } friend class set_merge_tf_trail; /** \brief Return true if the enode should be merged with the true (false) enodes when the associated boolean variable is assigned to true (false). */ bool merge_tf() const { return m_merge_tf; } friend class add_th_var_trail; friend class replace_th_var_trail; void add_th_var(theory_var v, theory_id id, region & r); void replace_th_var(theory_var v, theory_id id); void del_th_var(theory_id id); friend class tmp_enode; static enode * init(ast_manager & m, void * mem, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent); public: static unsigned get_enode_size(unsigned num_args) { return sizeof(enode) + num_args * sizeof(enode*); } static enode * mk(ast_manager & m, region & r, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent); static enode * mk_dummy(ast_manager & m, app2enode_t const & app2enode, app * owner); static void del_dummy(enode * n) { dealloc_svect(reinterpret_cast(n)); } unsigned get_func_decl_id() const { return m_func_decl_id; } void set_func_decl_id(unsigned id) { m_func_decl_id = id; } void mark_as_interpreted() { SASSERT(!m_interpreted); SASSERT(m_owner->get_num_args() == 0); SASSERT(m_class_size == 1); m_interpreted = true; } void del_eh(ast_manager & m, bool update_children_parent = true); app * get_owner() const { return m_owner; } unsigned get_owner_id() const { return m_owner->get_id(); } func_decl * get_decl() const { return m_owner->get_decl(); } unsigned get_decl_id() const { return m_owner->get_decl()->get_decl_id(); } unsigned hash() const { return m_owner->hash(); } enode * get_root() const { return m_root; } enode * get_next() const { return m_next; } unsigned get_num_args() const { return m_suppress_args ? 0 : m_owner->get_num_args(); } enode * get_arg(unsigned idx) const { SASSERT(idx < get_num_args()); return m_args[idx]; } enode * const * get_args() const { return m_args; } // unsigned get_id() const { // return m_id; // } unsigned get_class_size() const { return m_class_size; } bool is_bool() const { return m_bool; } bool is_eq() const { return m_eq; } bool is_true_eq() const { return m_eq && get_arg(0)->get_root() == get_arg(1)->get_root(); } bool is_marked() const { return m_mark; } void set_mark() { SASSERT(!m_mark); m_mark = true; } void unset_mark() { SASSERT(m_mark); m_mark = false; } bool is_marked2() const { return m_mark2; } void set_mark2() { SASSERT(!m_mark2); m_mark2 = true; } void unset_mark2() { SASSERT(m_mark2); m_mark2 = false; } bool is_interpreted() const { return m_interpreted; } /** \brief Return true if node is not a constant and it is the root of its congruence class. \remark if get_num_args() == 0, then is_cgr() = false. */ bool is_cgr() const { return m_cg == this; } enode * get_cg() const { return m_cg; } bool is_cgc_enabled() const { return m_cgc_enabled; } bool is_commutative() const { return m_commutative; } unsigned get_num_parents() const { return m_parents.size(); } enode_vector::iterator begin_parents() { return m_parents.begin(); } enode_vector::iterator end_parents() { return m_parents.end(); } enode_vector::const_iterator begin_parents() const { return m_parents.begin(); } enode_vector::const_iterator end_parents() const { return m_parents.end(); } theory_var_list const * get_th_var_list() const { return m_th_var_list.get_th_var() == null_theory_var ? 0 : &m_th_var_list; } bool has_th_vars() const { return m_th_var_list.get_th_var() != null_theory_var; } unsigned get_num_th_vars() const; theory_var get_th_var(theory_id th_id) const; unsigned get_generation() const { return m_generation; } void set_generation(context & ctx, unsigned generation); /** \brief Return the enode n that is in the eqc of *this, and has the minimal generation. That is, there is no other enode with smaller generation. */ enode * get_eq_enode_with_min_gen(); unsigned get_iscope_lvl() const { return m_iscope_lvl; } void set_lbl_hash(context & ctx); bool has_lbl_hash() const { return m_lbl_hash >= 0; } unsigned char get_lbl_hash() const { SASSERT(m_lbl_hash >= 0 && static_cast(m_lbl_hash) < approx_set_traits::capacity); return static_cast(m_lbl_hash); } approx_set & get_lbls() { return m_lbls; } approx_set & get_plbls() { return m_plbls; } const approx_set & get_lbls() const { return m_lbls; } const approx_set & get_plbls() const { return m_plbls; } void display_lbls(std::ostream & out) const; #ifdef Z3DEBUG bool check_invariant() const; bool trans_reaches(enode * n) const; bool check_parent_invariant() const; bool contains_parent_congruent_to(enode * p) const; #endif }; inline bool same_eqc(enode const * n1 , enode const * n2) { return n1->get_root() == n2->get_root(); } /** \brief Return true, if n1 and n2 are congruent. Set comm to true, if the nodes are congruent modulo commutativity. */ bool congruent(enode * n1, enode * n2, bool & comm); inline bool congruent(enode * n1, enode * n2) { bool aux; return congruent(n1, n2, aux); } unsigned get_max_generation(unsigned num_enodes, enode * const * enodes); void unmark_enodes(unsigned num_enodes, enode * const * enodes); void unmark_enodes2(unsigned num_enodes, enode * const * enodes); class tmp_enode { tmp_app m_app; unsigned m_capacity; char * m_enode_data; enode * get_enode() { return reinterpret_cast(m_enode_data); } void set_capacity(unsigned new_capacity); public: tmp_enode(); ~tmp_enode(); enode * set(func_decl * f, unsigned num_args, enode * const * args); void reset(); }; }; #endif /* SMT_ENODE_H_ */ z3-z3-4.4.1/src/smt/smt_eq_justification.h000066400000000000000000000046161260446376700204220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_eq_justification.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef SMT_EQ_JUSTIFICATION_H_ #define SMT_EQ_JUSTIFICATION_H_ #include"smt_literal.h" #include"tptr.h" namespace smt { /** \brief Proof like object used to track dependencies of equality propagation. The idea is to reduce the cost of dependency tracking for the most common justifications used during equality propagation: (asserted equality & congruence). */ class eq_justification { void * m_data; public: enum kind { AXIOM, //!< no justification, it is only used when proof generation is disabled CONGRUENCE, EQUATION, //!< asserted equation JUSTIFICATION //!< fallback }; explicit eq_justification(): m_data(reinterpret_cast(static_cast(AXIOM))) { } /** \brief Create a justification for the congruence rule. If commutativity == true, then it means it is a combined justification: commutativity + congruence. */ explicit eq_justification(bool commutativity): m_data(BOXTAGINT(void*, static_cast(commutativity), CONGRUENCE)) { } explicit eq_justification(literal l): m_data(BOXTAGINT(void*, l.index(), EQUATION)) { } explicit eq_justification(justification * js): m_data(TAG(void*, js, JUSTIFICATION)) { } kind get_kind() const { return static_cast(GET_TAG(m_data)); } literal get_literal() const { SASSERT(get_kind() == EQUATION); return to_literal(UNBOXINT(m_data)); } justification * get_justification() const { SASSERT(get_kind() == JUSTIFICATION); return UNTAG(justification*, m_data); } bool used_commutativity() const { SASSERT(get_kind() == CONGRUENCE); return UNBOXINT(m_data) != 0; } static eq_justification mk_axiom() { return eq_justification(); } static eq_justification mk_cg(bool comm = false) { return eq_justification(comm); } }; const eq_justification null_eq_justification(static_cast(0)); }; #endif /* SMT_EQ_JUSTIFICATION_H_ */ z3-z3-4.4.1/src/smt/smt_failure.h000066400000000000000000000012611260446376700165020ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_failure.h Abstract: Failures Author: Leonardo de Moura (leonardo) 2012-02-09. Revision History: --*/ #ifndef SMT_FAILURE_H_ #define SMT_FAILURE_H_ namespace smt { /** \brief Reason for a l_undef result in the check method. */ enum failure { OK, UNKNOWN, TIMEOUT, MEMOUT, CANCELED, //!< External cancel flag was set NUM_CONFLICTS, //!< Maximum number of conflicts was reached THEORY, //!< Theory is incomplete QUANTIFIERS //!< Logical context contains universal quantifiers. }; }; #endif z3-z3-4.4.1/src/smt/smt_farkas_util.cpp000066400000000000000000000240021260446376700177100ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: smt_farkas_util.cpp Abstract: Utility for combining inequalities using coefficients obtained from Farkas lemmas. Author: Nikolaj Bjorner (nbjorner) 2013-11-2. Revision History: NB. This utility is specialized to proofs generated by the arithmetic solvers. --*/ #include "smt_farkas_util.h" #include "ast_pp.h" #include "th_rewriter.h" #include "bool_rewriter.h" namespace smt { farkas_util::farkas_util(ast_manager& m): m(m), a(m), m_ineqs(m), m_split_literals(false), m_time(0) { } void farkas_util::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); } } // TBD: arith_decl_util now supports coercion, so this should be deprecated. app* farkas_util::mk_add(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_add(e1, e2); } app* farkas_util::mk_mul(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_mul(e1, e2); } app* farkas_util::mk_le(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_le(e1, e2); } app* farkas_util::mk_ge(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_gt(e1, e2); } app* farkas_util::mk_gt(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_gt(e1, e2); } app* farkas_util::mk_lt(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_lt(e1, e2); } void farkas_util::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 farkas_util::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 farkas_util::is_int_sort() { SASSERT(!m_ineqs.empty()); return is_int_sort(m_ineqs[0].get()); } void farkas_util::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; } } m_normalize_factor = l; } app* farkas_util::mk_one() { return a.mk_numeral(rational(1), true); } app* farkas_util::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; } void farkas_util::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 farkas_util::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 farkas_util::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 farkas_util::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 farkas_util::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; } void farkas_util::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); } } } void farkas_util::reset() { m_ineqs.reset(); m_coeffs.reset(); } void farkas_util::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)); } } expr_ref farkas_util::get() { m_normalize_factor = rational::one(); expr_ref res(m); if (m_coeffs.empty()) { res = m.mk_false(); return res; } bool is_int = is_int_sort(); if (is_int) { normalize_coeffs(); } if (m_split_literals) { // 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; } bool_rewriter(m).mk_or(lits.size(), lits.c_ptr(), res); IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << mk_pp(res, m) << "\n"; } }); } else { res = extract_consequence(0, m_coeffs.size()); } TRACE("arith", for (unsigned i = 0; i < m_coeffs.size(); ++i) { tout << m_coeffs[i] << " * (" << mk_pp(m_ineqs[i].get(), m) << ") "; } tout << "\n"; tout << res << "\n"; ); return res; } } z3-z3-4.4.1/src/smt/smt_farkas_util.h000066400000000000000000000053121260446376700173600ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: smt_farkas_util.h Abstract: Utility for combining inequalities using coefficients obtained from Farkas lemmas. Author: Nikolaj Bjorner (nbjorne) 2013-11-2. Revision History: NB. This utility is specialized to proofs generated by the arithmetic solvers. --*/ #ifndef FARKAS_UTIL_H_ #define FARKAS_UTIL_H_ #include "arith_decl_plugin.h" namespace smt { class farkas_util { ast_manager& m; arith_util a; app_ref_vector m_ineqs; vector m_coeffs; rational m_normalize_factor; // utilities for separating coefficients bool m_split_literals; unsigned m_time; unsigned_vector m_roots, m_size, m_his, m_reps, m_ts; void mk_coerce(expr*& e1, expr*& e2); app* mk_add(expr* e1, expr* e2); app* mk_mul(expr* e1, expr* e2); app* mk_le(expr* e1, expr* e2); app* mk_ge(expr* e1, expr* e2); app* mk_gt(expr* e1, expr* e2); app* mk_lt(expr* e1, expr* e2); void mul(rational const& c, expr* e, expr_ref& res); bool is_int_sort(app* c); bool is_int_sort(); void normalize_coeffs(); app* mk_one(); app* fix_sign(bool is_pos, app* c); void partition_ineqs(); unsigned find(unsigned idx); void merge(unsigned i, unsigned j); unsigned process_term(expr* e); expr_ref extract_consequence(unsigned lo, unsigned hi); void fix_dl(expr_ref& r); public: farkas_util(ast_manager& m); /** \brief Reset state */ void reset(); /** \brief add a multiple of constraint c to the current state */ void add(rational const & coef, app * c); /** \brief Extract the complement of premises multiplied by Farkas coefficients. */ expr_ref get(); /** \brief Coefficients are normalized for integer problems. Retrieve multiplicant for normalization. */ rational const& get_normalize_factor() const { return m_normalize_factor; } /** \brief extract one or multiple consequences based on literal partition. Multiple consequences are strongst modulo a partition of variables. Consequence generation under literal partitioning maintains difference logic constraints. That is, if the original constraints are difference logic, then the consequent produced by literal partitioning is also difference logic. */ void set_split_literals(bool f) { m_split_literals = f; } }; } #endif z3-z3-4.4.1/src/smt/smt_for_each_relevant_expr.cpp000066400000000000000000000220051260446376700221110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_for_each_relevant_expr.cpp Abstract: Author: Leonardo de Moura (leonardo) 2009-01-05. Revision History: --*/ #include"smt_context.h" #include"smt_for_each_relevant_expr.h" #include"ast_pp.h" #include"ast_ll_pp.h" namespace smt { bool check_at_labels::check(expr* n) { m_first = true; return count_at_labels_pos(n) <= 1; } unsigned check_at_labels::count_at_labels_lit(expr* n, bool polarity) { unsigned count = 0; buffer lbls; bool pos; if (m_manager.is_label_lit(n, lbls) || (m_manager.is_label(n, pos, lbls) && pos == polarity)) { buffer::const_iterator it = lbls.begin(); buffer::const_iterator end = lbls.end(); for (; it != end; ++it) { symbol const & s = *it; if (s.contains('@')) { TRACE("for_each_relevant_expr", tout << "@ label: " << mk_pp(n, m_manager) << "\n";); count += 1; } } } return count; } unsigned check_at_labels::count_at_labels_neg(expr* n) { if (!is_app(n)) { return 0; } app* a = to_app(n); unsigned sz = a->get_num_args(); unsigned count = count_at_labels_lit(n, false); if (m_manager.is_or(n)) { for (unsigned i = 0; i < sz; ++i) { count += count_at_labels_neg(a->get_arg(i)); } } else if (m_manager.is_not(n)) { count = count_at_labels_pos(a->get_arg(0)); } else if (m_manager.is_implies(n)) { count += count_at_labels_pos(a->get_arg(0)); count += count_at_labels_neg(a->get_arg(1)); } else if (m_manager.is_and(n)) { for (unsigned i = 0; i < sz; ++i) { count = std::max(count, count_at_labels_neg(a->get_arg(i))); } } if (count > 1 && m_first) { TRACE("for_each_relevant_expr", tout << mk_pp(n, m_manager) << "\n";); m_first = false; } return count; } unsigned check_at_labels::count_at_labels_pos(expr* n) { if (!is_app(n)) { return 0; } app* a = to_app(n); unsigned sz = a->get_num_args(); unsigned count = count_at_labels_lit(n, true); if (m_manager.is_and(n)) { for (unsigned i = 0; i < sz; ++i) { count += count_at_labels_pos(a->get_arg(i)); } } else if (m_manager.is_not(n)) { count = count_at_labels_neg(a->get_arg(0)); } else if (m_manager.is_implies(n)) { count = std::max(count, count_at_labels_neg(a->get_arg(0))); count = std::max(count, count_at_labels_pos(a->get_arg(1))); } else if (m_manager.is_or(n)) { for (unsigned i = 0; i < sz; ++i) { count = std::max(count, count_at_labels_pos(a->get_arg(i))); } } if (count > 1 && m_first) { TRACE("for_each_relevant_expr", tout << mk_pp(n, m_manager) << "\n";); m_first = false; } return count; } for_each_relevant_expr::for_each_relevant_expr(context & ctx): m_manager(ctx.get_manager()), m_context(ctx) { } void for_each_relevant_expr::operator()(expr * n) { TRACE("for_each_relevant_expr", tout << "#" << n->get_id() << "\n";); } void for_each_relevant_expr::reset() { m_todo.reset(); m_cache.reset(); } inline bool for_each_relevant_expr::is_relevant(expr * n) { return m_context.is_relevant(n); } inline lbool for_each_relevant_expr::get_assignment(expr * n) { if (!m_context.lit_internalized(n)) return l_true; // assume it is a top-level label return m_context.get_assignment(n); } void for_each_relevant_expr::process(expr * n) { TRACE("for_each_relevant_expr", tout << "processing:\n" << mk_bounded_pp(n, m_manager) << "\n";); TRACE("for_each_relevant_expr", tout << "processing:\n" << mk_pp(n, m_manager) << "\n";); if (m_cache.contains(n)) return; m_todo.reset(); m_todo.push_back(n); while (!m_todo.empty()) { expr * curr = m_todo.back(); m_todo.pop_back(); SASSERT(is_relevant(curr)); if (m_cache.contains(curr)) continue; operator()(curr); m_cache.insert(curr); if (!is_app(curr)) continue; if (to_app(curr)->get_family_id() == m_manager.get_basic_family_id()) { switch (to_app(curr)->get_decl_kind()) { case OP_OR: process_or(to_app(curr)); break; case OP_AND: process_and(to_app(curr)); break; case OP_ITE: process_ite(to_app(curr)); break; default: process_app(to_app(curr)); } } else { process_app(to_app(curr)); } } } void for_each_relevant_expr::process_app(app * n) { unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; i++) { expr * arg = n->get_arg(i); if (m_cache.contains(arg)) continue; SASSERT(is_relevant(arg)); m_todo.push_back(arg); } } /** \brief Add a relevant child of n (that is assigned to val) to m_todo. \remark Give preference to a child that is already in the cache. */ void for_each_relevant_expr::process_relevant_child(app * n, lbool val) { unsigned sz = n->get_num_args(); TRACE("for_each_relevant_expr", tout << val << " " << mk_bounded_pp(n, m_manager) << "\n";); for (unsigned i = 0; i < sz; i++) { expr * arg = n->get_arg(i); if (!is_relevant(arg)) continue; if (get_assignment(arg) != val) continue; if (m_cache.contains(arg)) { TRACE("for_each_relevant_expr", tout << "justified by: " << mk_bounded_pp(arg, m_manager) << "\n";); return; // the current child justifies n. } } for (unsigned i = 0; i < sz; i++) { expr * arg = n->get_arg(i); if (!is_relevant(arg)) continue; if (get_assignment(arg) != val) continue; TRACE("for_each_relevant_expr", tout << "to_process: " << mk_bounded_pp(arg, m_manager) << "\n";); m_todo.push_back(arg); return; } UNREACHABLE(); } void for_each_relevant_expr::process_and(app * n) { switch (get_assignment(n)) { case l_undef: UNREACHABLE(); break; case l_false: process_relevant_child(n, l_false); break; case l_true: process_app(n); break; } } void for_each_relevant_expr::process_or(app * n) { switch (get_assignment(n)) { case l_undef: UNREACHABLE(); break; case l_false: process_app(n); break; case l_true: process_relevant_child(n, l_true); break; } } void for_each_relevant_expr::process_ite(app * n) { if (!m_cache.contains(n->get_arg(0))) m_todo.push_back(n->get_arg(0)); switch (get_assignment(n->get_arg(0))) { case l_false: if (!m_cache.contains(n->get_arg(2))) m_todo.push_back(n->get_arg(2)); break; case l_undef: UNREACHABLE(); break; case l_true: if (!m_cache.contains(n->get_arg(1))) m_todo.push_back(n->get_arg(1)); break; } } void collect_relevant_label_lits::operator()(expr * n) { TRACE("for_each_relevant_expr", tout << "label: " << m_manager.is_label_lit(n) << " " << " " << get_assignment(n) << " " << mk_bounded_pp(n, m_manager) << "\n";); if (!m_manager.is_label_lit(n)) return; if (get_assignment(n) != l_true) return; m_manager.is_label_lit(n, m_buffer); // copy symbols to buffer } void collect_relevant_labels::operator()(expr * n) { bool pos; TRACE("for_each_relevant_expr", tout << "label: " << m_manager.is_label(n) << " " << get_assignment(n) << " " << mk_bounded_pp(n, m_manager) << "\n";); if (!m_manager.is_label(n, pos)) return; if (pos && (get_assignment(n) != l_true)) return; if (!pos && (get_assignment(n) != l_false)) return; m_manager.is_label(n, pos, m_buffer); // copy symbols to buffer } }; z3-z3-4.4.1/src/smt/smt_for_each_relevant_expr.h000066400000000000000000000056641260446376700215720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_for_each_relevant_expr.h Abstract: Author: Leonardo de Moura (leonardo) 2009-01-05. Revision History: --*/ #ifndef SMT_FOR_EACH_RELEVANT_EXPR_H_ #define SMT_FOR_EACH_RELEVANT_EXPR_H_ #include"ast.h" #include"obj_hashtable.h" #include"vector.h" namespace smt { class context; class check_at_labels { ast_manager & m_manager; bool m_first; unsigned count_at_labels_pos(expr* n); unsigned count_at_labels_neg(expr* n); unsigned count_at_labels_lit(expr* n, bool polarity); public: check_at_labels(ast_manager& m) : m_manager(m) {}; /** \brief Check that 'n' as a formula contains at most one @ label within each and-or path. */ bool check(expr* cnstr); }; /** \brief Functor used to traverse the relevant expressions in a logical context. */ class for_each_relevant_expr { protected: ast_manager & m_manager; context & m_context; obj_hashtable m_cache; ptr_vector m_todo; bool m_first; void process_app(app * n); void process_relevant_child(app * n, lbool val); void process_and(app * n); void process_or(app * n); void process_ite(app * n); lbool get_assignment(expr * n); bool is_relevant(expr * n); public: for_each_relevant_expr(context & ctx); virtual ~for_each_relevant_expr() {} /** \brief Visit the relevant sub-expressions of n. That is, only subexpressions m of n, such that m_context.is_relevant(m). This method also tries to minimize the number of subexpressions visited. For each visited expression the method operator() is invoked. Only not-already-visited expressions are visited. */ void process(expr * n); /** \see process */ virtual void operator()(expr * n); /** \brief Reset the cache of already visited expressions. */ void reset(); }; class collect_relevant_label_lits : public for_each_relevant_expr { buffer & m_buffer; public: collect_relevant_label_lits(context & ctx, buffer & b): for_each_relevant_expr(ctx), m_buffer(b) { } virtual ~collect_relevant_label_lits() {} virtual void operator()(expr * n); }; class collect_relevant_labels : public for_each_relevant_expr { buffer & m_buffer; public: collect_relevant_labels(context & ctx, buffer & b): for_each_relevant_expr(ctx), m_buffer(b) { } virtual ~collect_relevant_labels() {} virtual void operator()(expr * n); }; }; #endif /* SMT_FOR_EACH_RELEVANT_EXPR_H_ */ z3-z3-4.4.1/src/smt/smt_implied_equalities.cpp000066400000000000000000000524411260446376700212640ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_implied_equalities.cpp Abstract: Procedure for obtaining implied equalities relative to the state of a solver. Author: Nikolaj Bjorner (nbjorner) 2012-02-29 Revision History: --*/ #include "smt_implied_equalities.h" #include "union_find.h" #include "ast_pp.h" #include "array_decl_plugin.h" #include "uint_set.h" #include "smt_value_sort.h" #include "model_smt2_pp.h" #include "stopwatch.h" #include "model.h" #include "solver.h" namespace smt { class get_implied_equalities_impl { ast_manager& m; solver& m_solver; union_find_default_ctx m_df; union_find m_uf; array_util m_array_util; stopwatch m_stats_timer; unsigned m_stats_calls; stopwatch m_stats_val_eq_timer; static stopwatch s_timer; static stopwatch s_stats_val_eq_timer; struct term_id { expr_ref term; unsigned id; term_id(expr_ref t, unsigned id): term(t), id(id) {} }; typedef vector term_ids; typedef obj_map sort2term_ids; // partition of terms by sort. void partition_terms(unsigned num_terms, expr* const* terms, sort2term_ids& termids) { for (unsigned i = 0; i < num_terms; ++i) { sort* s = m.get_sort(terms[i]); term_ids& vec = termids.insert_if_not_there2(s, term_ids())->get_data().m_value; vec.push_back(term_id(expr_ref(terms[i],m), i)); } } /** \brief Basic implied equalities method. It performs a simple N^2 loop over all pairs of terms. n1, .., n_k, t1, .., t_l */ void get_implied_equalities_filter_basic(uint_set const& non_values, term_ids& terms) { m_stats_timer.start(); uint_set root_indices; for (unsigned j = 0; j < terms.size(); ++j) { if (terms[j].id == m_uf.find(terms[j].id)) { root_indices.insert(j); } } uint_set::iterator it = non_values.begin(), end = non_values.end(); for (; it != end; ++it) { unsigned i = *it; expr* t = terms[i].term; uint_set::iterator it2 = root_indices.begin(), end2 = root_indices.end(); bool found_root_value = false; for (; it2 != end2; ++it2) { unsigned j = *it2; if (j == i) continue; if (j < i && non_values.contains(j)) continue; if (found_root_value && !non_values.contains(j)) continue; expr* s = terms[j].term; SASSERT(m.get_sort(t) == m.get_sort(s)); ++m_stats_calls; m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(s, t))); bool is_eq = l_false == m_solver.check_sat(0,0); m_solver.pop(1); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << (is_eq?"eq":"unrelated") << "\n";); if (is_eq) { m_uf.merge(terms[i].id, terms[j].id); if (!non_values.contains(j)) { found_root_value = true; } } } } m_stats_timer.stop(); } void get_implied_equalities_basic(term_ids& terms) { for (unsigned i = 0; i < terms.size(); ++i) { if (terms[i].id != m_uf.find(terms[i].id)) { continue; } expr* t = terms[i].term; for (unsigned j = 0; j < i; ++j) { expr* s = terms[j].term; SASSERT(m.get_sort(t) == m.get_sort(s)); ++m_stats_calls; m_stats_timer.start(); m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(s, t))); bool is_eq = l_false == m_solver.check_sat(0,0); m_solver.pop(1); m_stats_timer.stop(); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << (is_eq?"eq":"unrelated") << "\n";); if (is_eq) { m_uf.merge(terms[i].id, terms[j].id); break; } } } } /** \brief Extract implied equalities for a collection of terms in the current context. The routine relies on model values being unique for equal terms. So in particular, arrays that are equal should be canonized to the same value. This is not the case for Z3's models of arrays. Arrays are treated by extensionality: introduce a fresh index and compare the select of the arrays. */ void get_implied_equalities_model_based(model_ref& model, term_ids& terms) { SASSERT(!terms.empty()); sort* srt = m.get_sort(terms[0].term); if (m_array_util.is_array(srt)) { m_solver.push(); unsigned arity = get_array_arity(srt); expr_ref_vector args(m); args.push_back(0); for (unsigned i = 0; i < arity; ++i) { sort* srt_i = get_array_domain(srt, i); expr* idx = m.mk_fresh_const("index", srt_i); args.push_back(idx); } for (unsigned i = 0; i < terms.size(); ++i) { args[0] = terms[i].term; terms[i].term = m.mk_app(m_array_util.get_family_id(), OP_SELECT, 0, 0, args.size(), args.c_ptr()); } assert_relevant(terms); VERIFY(m_solver.check_sat(0,0) != l_false); model_ref model1; m_solver.get_model(model1); SASSERT(model1.get()); get_implied_equalities_model_based(model1, terms); m_solver.pop(1); return; } uint_set non_values; if (!is_value_sort(m, srt)) { for (unsigned i = 0; i < terms.size(); ++i) { non_values.insert(i); } get_implied_equalities_filter_basic(non_values, terms); //get_implied_equalities_basic(terms); return; } expr_ref_vector vals(m); expr_ref vl(m), eq(m); obj_map vals_map; m_stats_val_eq_timer.start(); s_stats_val_eq_timer.start(); params_ref p; p.set_bool("produce_models", false); m_solver.updt_params(p); for (unsigned i = 0; i < terms.size(); ++i) { expr* t = terms[i].term; model->eval(t, vl); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " |-> " << mk_pp(vl, m) << "\n";); reduce_value(model, vl); if (!m.is_value(vl)) { TRACE("get_implied_equalities", tout << "Not a value: " << mk_pp(vl, m) << "\n";); non_values.insert(i); continue; } vals.push_back(vl); unsigned_vector& vec = vals_map.insert_if_not_there2(vl, unsigned_vector())->get_data().m_value; bool found = false; for (unsigned j = 0; !found && j < vec.size(); ++j) { expr* s = terms[vec[j]].term; m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(t, s))); lbool is_sat = m_solver.check_sat(0,0); m_solver.pop(1); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << is_sat << "\n";); if (is_sat == l_false) { found = true; m_uf.merge(terms[i].id, terms[vec[j]].id); } } if (!found) { vec.push_back(i); } } m_stats_val_eq_timer.stop(); s_stats_val_eq_timer.stop(); p.set_bool("produce_models", true); m_solver.updt_params(p); if (!non_values.empty()) { TRACE("get_implied_equalities", model_smt2_pp(tout, m, *model, 0);); get_implied_equalities_filter_basic(non_values, terms); //get_implied_equalities_basic(terms); } } void get_implied_equalities_core(model_ref& model, term_ids& terms) { get_implied_equalities_model_based(model, terms); //get_implied_equalities_basic(terms); } void assert_relevant(unsigned num_terms, expr* const* terms) { for (unsigned i = 0; i < num_terms; ++i) { sort* srt = m.get_sort(terms[i]); if (!m_array_util.is_array(srt)) { m_solver.assert_expr(m.mk_app(m.mk_func_decl(symbol("Relevant!"), 1, &srt, m.mk_bool_sort()), terms[i])); } } } void assert_relevant(term_ids& terms) { for (unsigned i = 0; i < terms.size(); ++i) { expr* t = terms[i].term; sort* srt = m.get_sort(t); if (!m_array_util.is_array(srt)) { m_solver.assert_expr(m.mk_app(m.mk_func_decl(symbol("Relevant!"), 1, &srt, m.mk_bool_sort()), t)); } } } void reduce_value(model_ref& model, expr_ref& vl) { expr* c, *e1, *e2; while (m.is_ite(vl, c, e1, e2)) { lbool r = reduce_cond(model, c); switch(r) { case l_true: vl = e1; break; case l_false: vl = e2; break; default: return; } } } lbool reduce_cond(model_ref& model, expr* e) { expr* e1, *e2; if (m.is_eq(e, e1, e2) && m_array_util.is_as_array(e1) && m_array_util.is_as_array(e2)) { if (e1 == e2) { return l_true; } func_decl* f1 = m_array_util.get_as_array_func_decl(to_app(e1)); func_decl* f2 = m_array_util.get_as_array_func_decl(to_app(e2)); func_interp* fi1 = model->get_func_interp(f1); func_interp* fi2 = model->get_func_interp(f2); if (fi1 == fi2) { return l_true; } unsigned n1 = fi1->num_entries(); for (unsigned i = 0; i < n1; ++i) { func_entry const* h1 = fi1->get_entry(i); for (unsigned j = 0; j < fi1->get_arity(); ++j) { if (!m.is_value(h1->get_arg(j))) { return l_undef; } } func_entry* h2 = fi2->get_entry(h1->get_args()); if (h2 && h1->get_result() != h2->get_result() && m.is_value(h1->get_result()) && m.is_value(h2->get_result())) { return l_false; } } } return l_undef; } public: get_implied_equalities_impl(ast_manager& m, solver& s) : m(m), m_solver(s), m_uf(m_df), m_array_util(m), m_stats_calls(0) {} lbool operator()(unsigned num_terms, expr* const* terms, unsigned* class_ids) { params_ref p; p.set_bool("produce_models", true); m_solver.updt_params(p); sort2term_ids termids; stopwatch timer; timer.start(); s_timer.start(); for (unsigned i = 0; i < num_terms; ++i) { m_uf.mk_var(); } m_solver.push(); assert_relevant(num_terms, terms); lbool is_sat = m_solver.check_sat(0,0); if (is_sat != l_false) { model_ref model; m_solver.get_model(model); SASSERT(model.get()); partition_terms(num_terms, terms, termids); sort2term_ids::iterator it = termids.begin(), end = termids.end(); for (; it != end; ++it) { term_ids& term_ids = it->m_value; get_implied_equalities_core(model, term_ids); for (unsigned i = 0; i < term_ids.size(); ++i) { class_ids[term_ids[i].id] = m_uf.find(term_ids[i].id); } } TRACE("get_implied_equalities", for (unsigned i = 0; i < num_terms; ++i) { tout << mk_pp(terms[i], m) << " |-> " << class_ids[i] << "\n"; }); } m_solver.pop(1); timer.stop(); s_timer.stop(); IF_VERBOSE(1, verbose_stream() << s_timer.get_seconds() << "\t" << num_terms << "\t" << timer.get_seconds() << "\t" << m_stats_calls << "\t" << m_stats_timer.get_seconds() << "\t" << m_stats_val_eq_timer.get_seconds() << "\t" << s_stats_val_eq_timer.get_seconds() << "\n";); return is_sat; } }; stopwatch get_implied_equalities_impl::s_timer; stopwatch get_implied_equalities_impl::s_stats_val_eq_timer; lbool implied_equalities(ast_manager& m, solver& solver, unsigned num_terms, expr* const* terms, unsigned* class_ids) { get_implied_equalities_impl gi(m, solver); return gi(num_terms, terms, class_ids); } }; #if 0 // maxsat class for internal purposes. class maxsat { ast_manager& m; solver& m_solver; public: maxsat(solver& s) : m(s.m()), m_solver(s) {} lbool operator()(ptr_vector& soft_cnstrs) { return l_undef; } }; class term_equivs { union_find_default_ctx m_df; union_find m_uf; obj_map m_term2idx; ptr_vector m_idx2term; public: term_equivs(): m_uf(m_df) {} void merge(expr* t, expr* s) { m_uf.merge(var(t), var(s)); } private: unsigned var(expr* t) { map::obj_map_entry* e = m_term2idx.insert_if_not_there(t, m_idx2term.size()); unsigned idx = e->get_data().m_value; if (idx == m_idx2term.size()) { m_idx2term.push_back(t); } return idx; } }; /** \brief class to find implied equalities. It implements the following half-naive algorithm. The algorithm is half-naive because the terms being checked for equivalence class membership are foreign and it is up to the theory integration whether pairs of interface equalities are checked. The idea is that the model-based combination would avoid useless equality literals in the core. An alternative algorithm could use 'distinct' and an efficient solver for 'distinct'. Given terms t1, ..., tn, of the same type. - assert f(t1) = 1, .., f(tn) = n. - find MAX-SAT set A1, let the other literals be in B. - find MAX-SAT set of B, put it in A2, etc. - we now have MAX-SAT sets A1, A2, ... A_m. - terms in each set A_i can be different, but cannot be different at the same time as elements in A_{i+1}. - for i = m to 2 do: - Let A = A_i B = A_{i-1} - assert g(A) = 0, g(B) = 1 - find MAX-SAT set C over this constraint. - For each element t from A\C - check if g(t) = 0 and g(B) = 1 is unsat - minimize core, if there is pair such that - g(t) = 0, g(b) = 1 is unsat, then equality is forced. */ class implied_equalities_finder { ast_manager& m; solver& m_solver; term_equivs m_find; expr_ref_vector m_refs; obj_map m_fs; // t_i -> f(t_i) = i obj_map m_gs; // t_i -> g(t_i) public: implied_equalities_finder(solver& solver): m(solver.m()), m_solver(solver), m_refs(m) {} lbool operator()(unsigned num_terms, expr* const* terms, unsigned* class_ids) { m_find.reset(); // return l_undef; } private: void initialize(unsigned num_terms, expr* const* terms) { sort_ref bv(m); expr_ref eq(m), g(m), eq_proxy(m); symbol f("f"), g("g"); unsigned log_terms = 1, nt = num_terms; while (nt > 0) { log_terms++; nt /= 2; } bv = m_bv.mk_bv_sort(log_terms); for (unsigned i = 0; i < num_terms; ++i) { expr* t = terms[i]; sort* s = m.get_sort(t); eq = m.mk_eq(m.mk_app(m.mk_func_decl(f, 1, &s, bv), t), m_bv.mk_numeral(rational(i), bv)); eq_proxy = m.mk_fresh_const("f", m.mk_bool_sort()); m_solver.assert_expr(m.mk_iff(eq, eq_proxy)); g = m.mk_app(m.mk_func_decl(g, 1, &s, bv), t) m_fs.insert(t, eq_proxy); m_gs.insert(t, g); } } // // For each t in src, check if t can be different from all s in dst. // - if it can, then add t to dst. // - if it cannot, then record equivalence class. // void merge_classes(expr_ref_vector& src, expr_ref_vector& dst, equivs& eqs) { } }; lbool implied_equalities_core_based( solver& solver, unsigned num_terms, expr* const* terms, unsigned* class_ids, unsigned num_assumptions, expr * const * assumptions) { implied_equalities_finder ief(solver); solver.push(); for (unsigned i = 0; i < num_assumptions; ++i) { solver.assert_expr(assumptions[i]); } lbool is_sat = ief(num_terms, terms, class_ids); solver.pop(1); return is_sat; } /** \brief Extract implied equalities for a collection of terms in the current context. The routine uses a partition refinement approach. It assumes that all terms have the same sort. Initially, create the equalities E_1: t0 = t1, E_2: t1 = t2, ..., E_n: t_{n-1} = t_n Check if ! (E_1 & E_2 & ... & E_n) is satisfiable. if it is unsat, then all terms are equal. Otherwise, partition the terms by the equalities that are true in the current model, iterate. This version does not attempt to be economical on how many equalities are introduced and the size of the resulting clauses. The more advanced version of this approach re-uses equalities from a previous iteration and also represents a binary tree of propositional variables that cover multiple equalities. Eg., E_12 => E_1 & E_2, E_34 => E_3 & E_4, ... */ void get_implied_equalities_eq_based(term_ids& terms) { expr_ref_vector eqs(m); if (terms.size() == 1) { return; } m_solver.push(); for (unsigned i = 0; i + 1 < terms.size(); ++i) { expr* eq = m.mk_eq(terms[i].term, terms[i+1].term); expr* eq_lit = m.mk_fresh_const("E", m.mk_bool_sort()); eqs.push_back(eq_lit); m_solver.assert_expr(m.mk_implies(eq_lit, eq)); } m_solver.assert_expr(m.mk_not(m.mk_and(eqs.size(), eqs.c_ptr()))); lbool is_sat = m_solver.check_sat(0,0); switch(is_sat) { case l_false: for (unsigned i = 0; i + 1 < terms.size(); ++i) { m_uf.merge(terms[i].id, terms[i+1].id); } break; default: { term_ids tems2; for (unsigned i = 0; i + 1 < terms.size(); ++i) { expr_ref vl(m); model->eval(terms[i].term, vl); if (m.is_false(vl)) { } } break; } } m_solver.pop(1); } #endif z3-z3-4.4.1/src/smt/smt_implied_equalities.h000066400000000000000000000011431260446376700207220ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_implied_equalities.h Abstract: Procedure for obtaining implied equalities relative to the state of a solver. Author: Nikolaj Bjorner (nbjorner) 2012-02-29 Revision History: --*/ #ifndef SMT_IMPLIED_EQUALITIES_H_ #define SMT_IMPLIED_EQUALITIES_H_ #include"smt_solver.h" #include"lbool.h" #include"ast.h" namespace smt { lbool implied_equalities( ast_manager & m, solver & solver, unsigned num_terms, expr* const* terms, unsigned* class_ids); }; #endif z3-z3-4.4.1/src/smt/smt_internalizer.cpp000066400000000000000000002026521260446376700201230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_internalizer.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #include"smt_context.h" #include"expr_stat.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"ast_smt2_pp.h" #include"smt_model_finder.h" #include"for_each_expr.h" namespace smt { /** \brief Return true if the expression is viewed as a logical gate. */ inline bool is_gate(ast_manager const & m, expr * n) { if (is_app(n) && to_app(n)->get_family_id() == m.get_basic_family_id()) { switch (to_app(n)->get_decl_kind()) { case OP_AND: case OP_OR: case OP_IFF: case OP_ITE: return true; default: return false; } } return false; } #define White 0 #define Grey 1 #define Black 2 static int get_color(svector & tcolors, svector & fcolors, expr * n, bool gate_ctx) { svector & colors = gate_ctx ? tcolors : fcolors; if (colors.size() > n->get_id()) return colors[n->get_id()]; return White; } static void set_color(svector & tcolors, svector & fcolors, expr * n, bool gate_ctx, int color) { svector & colors = gate_ctx ? tcolors : fcolors; if (colors.size() <= n->get_id()) { colors.resize(n->get_id()+1, White); } colors[n->get_id()] = color; } /** \brief Return the foreign descendants of n. That is, the descendants of n where the family_id is different from fid. For example the descendants of (+ a (+ (f b) (* 2 (h (+ c d))))) are: - a - (f b) - (h (+ c d)) */ static void get_foreign_descendants(app * n, family_id fid, ptr_buffer & descendants) { SASSERT(n->get_family_id() == fid); SASSERT(fid != null_family_id); ptr_buffer todo; todo.push_back(n); ast_mark visited; while (!todo.empty()) { expr * curr = todo.back(); todo.pop_back(); if (visited.is_marked(n)) { continue; } visited.mark(n, true); if (!is_app(curr) || to_app(curr)->get_family_id() != fid) { descendants.push_back(curr); continue; } SASSERT(is_app(curr)); SASSERT(to_app(curr)->get_family_id() == fid); unsigned j = to_app(curr)->get_num_args(); while (j > 0) { --j; todo.push_back(to_app(curr)->get_arg(j)); } } } void context::ts_visit_child(expr * n, bool gate_ctx, svector & tcolors, svector< int> & fcolors, svector & todo, bool & visited) { if (get_color(tcolors, fcolors, n, gate_ctx) == White) { todo.push_back(expr_bool_pair(n, gate_ctx)); visited = false; } } bool context::ts_visit_children(expr * n, bool gate_ctx, svector & tcolors, svector & fcolors, svector & todo) { if (is_quantifier(n)) return true; SASSERT(is_app(n)); if (m_manager.is_bool(n)) { if (b_internalized(n)) return true; } else { if (e_internalized(n)) return true; } bool visited = true; family_id fid = to_app(n)->get_family_id(); theory * th = m_theories.get_plugin(fid); bool def_int = th == 0 || th->default_internalizer(); if (!def_int) { ptr_buffer descendants; get_foreign_descendants(to_app(n), fid, descendants); ptr_buffer::iterator it = descendants.begin(); ptr_buffer::iterator end = descendants.end(); for (; it != end; ++it) { expr * arg = *it; ts_visit_child(arg, false, tcolors, fcolors, todo, visited); } return visited; } SASSERT(def_int); if (m_manager.is_term_ite(n)) { ts_visit_child(to_app(n)->get_arg(0), true, tcolors, fcolors, todo, visited); ts_visit_child(to_app(n)->get_arg(1), false, tcolors, fcolors, todo, visited); ts_visit_child(to_app(n)->get_arg(2), false, tcolors, fcolors, todo, visited); return visited; } bool new_gate_ctx = m_manager.is_bool(n) && (is_gate(m_manager, n) || m_manager.is_not(n)); unsigned j = to_app(n)->get_num_args(); while (j > 0) { --j; expr * arg = to_app(n)->get_arg(j); ts_visit_child(arg, new_gate_ctx, tcolors, fcolors, todo, visited); } return visited; } void context::top_sort_expr(expr * n, svector & sorted_exprs) { svector todo; svector tcolors; svector fcolors; todo.push_back(expr_bool_pair(n, true)); while (!todo.empty()) { expr_bool_pair & p = todo.back(); expr * curr = p.first; bool gate_ctx = p.second; switch (get_color(tcolors, fcolors, curr, gate_ctx)) { case White: set_color(tcolors, fcolors, curr, gate_ctx, Grey); ts_visit_children(curr, gate_ctx, tcolors, fcolors, todo); break; case Grey: SASSERT(ts_visit_children(curr, gate_ctx, tcolors, fcolors, todo)); set_color(tcolors, fcolors, curr, gate_ctx, Black); if (n != curr && !m_manager.is_not(curr)) sorted_exprs.push_back(expr_bool_pair(curr, gate_ctx)); break; case Black: todo.pop_back(); break; default: UNREACHABLE(); } } } #define DEEP_EXPR_THRESHOLD 1024 /** \brief Internalize an expression asserted into the logical context using the given proof as a justification. \remark pr is 0 if proofs are disabled. */ void context::internalize_assertion(expr * n, proof * pr, unsigned generation) { TRACE("internalize_assertion", tout << mk_pp(n, m_manager) << "\n";); TRACE("internalize_assertion_ll", tout << mk_ll_pp(n, m_manager) << "\n";); TRACE("generation", tout << "generation: " << m_generation << "\n";); TRACE("incompleteness_bug", tout << "[internalize-assertion]: #" << n->get_id() << "\n";); flet l(m_generation, generation); m_stats.m_max_generation = std::max(m_generation, m_stats.m_max_generation); if (get_depth(n) > DEEP_EXPR_THRESHOLD) { // if the expression is deep, then execute topological sort to avoid // stack overflow. TRACE("deep_internalize", tout << "expression is deep: #" << n->get_id() << "\n" << mk_ll_pp(n, m_manager);); svector sorted_exprs; top_sort_expr(n, sorted_exprs); TRACE("deep_internalize", svector::const_iterator it = sorted_exprs.begin(); svector::const_iterator end = sorted_exprs.end(); for (; it != end; ++it) { tout << "#" << it->first->get_id() << " " << it->second << "\n"; }); svector::const_iterator it = sorted_exprs.begin(); svector::const_iterator end = sorted_exprs.end(); for (; it != end; ++it) internalize(it->first, it->second); } SASSERT(m_manager.is_bool(n)); if (is_gate(m_manager, n)) { switch(to_app(n)->get_decl_kind()) { case OP_AND: UNREACHABLE(); case OP_OR: { literal_buffer lits; unsigned num = to_app(n)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(n)->get_arg(i); internalize(arg, true); lits.push_back(get_literal(arg)); } mk_root_clause(lits.size(), lits.c_ptr(), pr); add_or_rel_watches(to_app(n)); break; } case OP_IFF: { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); internalize(lhs, true); internalize(rhs, true); literal l1 = get_literal(lhs); literal l2 = get_literal(rhs); mk_root_clause(l1, ~l2, pr); mk_root_clause(~l1, l2, pr); break; } case OP_ITE: { expr * c = to_app(n)->get_arg(0); expr * t = to_app(n)->get_arg(1); expr * e = to_app(n)->get_arg(2); internalize(c, true); internalize(t, true); internalize(e, true); literal cl = get_literal(c); literal tl = get_literal(t); literal el = get_literal(e); mk_root_clause(~cl, tl, pr); mk_root_clause(cl, el, pr); add_ite_rel_watches(to_app(n)); break; } default: UNREACHABLE(); } mark_as_relevant(n); } else if (m_manager.is_distinct(n)) { assert_distinct(to_app(n), pr); mark_as_relevant(n); } else { assert_default(n, pr); } } void context::assert_default(expr * n, proof * pr) { internalize(n, true); literal l = get_literal(n); if (l == false_literal) { set_conflict(mk_justification(justification_proof_wrapper(*this, pr))); } else { assign(l, mk_justification(justification_proof_wrapper(*this, pr))); mark_as_relevant(l); } } #define DISTINCT_SZ_THRESHOLD 32 void context::assert_distinct(app * n, proof * pr) { TRACE("assert_distinct", tout << mk_pp(n, m_manager) << "\n";); unsigned num_args = n->get_num_args(); if (num_args == 0 || num_args <= DISTINCT_SZ_THRESHOLD || m_manager.proofs_enabled()) { assert_default(n, pr); return; } sort * s = m_manager.get_sort(n->get_arg(0)); sort * u = m_manager.mk_fresh_sort("distinct-elems"); func_decl * f = m_manager.mk_fresh_func_decl("distinct-aux-f", "", 1, &s, u); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); app * fapp = m_manager.mk_app(f, arg); app * val = m_manager.mk_fresh_const("unique-value", u); enode * e = mk_enode(val, false, false, true); e->mark_as_interpreted(); app * eq = m_manager.mk_eq(fapp, val); TRACE("assert_distinct", tout << "eq: " << mk_pp(eq, m_manager) << "\n";); assert_default(eq, 0); mark_as_relevant(eq); // TODO: we may want to hide the auxiliary values val and the function f from the model. } } void context::internalize(expr * n, bool gate_ctx, unsigned generation) { flet l(m_generation, generation); m_stats.m_max_generation = std::max(m_generation, m_stats.m_max_generation); internalize(n, gate_ctx); } /** \brief Internalize the given expression into the logical context. - gate_ctx is true if the expression is in the context of a logical gate. */ void context::internalize(expr * n, bool gate_ctx) { TRACE("internalize", tout << "internalizing:\n" << mk_pp(n, m_manager) << "\n";); TRACE("internalize_bug", tout << "internalizing:\n" << mk_bounded_pp(n, m_manager) << "\n";); if (m_manager.is_bool(n)) { SASSERT(is_quantifier(n) || is_app(n)); internalize_formula(n, gate_ctx); } else { SASSERT(is_app(n)); SASSERT(!gate_ctx); internalize_term(to_app(n)); } } bool find_arg(app * n, expr * t, expr * & other) { SASSERT(n->get_num_args() == 2); if (n->get_arg(0) == t) { other = n->get_arg(1); return true; } else if (n->get_arg(1) == t) { other = n->get_arg(0); return true; } return false; } bool check_args(app * n, expr * t1, expr * t2) { SASSERT(n->get_num_args() == 2); return (n->get_arg(0) == t1 && n->get_arg(1) == t2) || (n->get_arg(1) == t1 && n->get_arg(0) == t2); } /** \brief Internalize the given formula into the logical context. */ void context::internalize_formula(expr * n, bool gate_ctx) { TRACE("internalize_bug", tout << "internalize formula: #" << n->get_id() << ", gate_ctx: " << gate_ctx << "\n" << mk_pp(n, m_manager) << "\n";); SASSERT(m_manager.is_bool(n)); if (m_manager.is_true(n) || m_manager.is_false(n)) return; if (m_manager.is_not(n) && gate_ctx) { // a boolean variable does not need to be created if n a NOT gate is in // the context of a gate. internalize(to_app(n)->get_arg(0), true); return; } if (b_internalized(n)) { // n was already internalized as a boolean. bool_var v = get_bool_var(n); TRACE("internalize_bug", tout << "#" << n->get_id() << " already has bool_var v" << v << "\n";); // n was already internalized as boolean, but an enode was // not associated with it. So, an enode is necessary, if // n is not in the context of a gate and is an application. if (!gate_ctx && is_app(n)) { if (e_internalized(n)) { TRACE("internalize_bug", tout << "forcing enode #" << n->get_id() << " to merge with t/f\n";); enode * e = get_enode(to_app(n)); set_merge_tf(e, v, false); } else { TRACE("internalize_bug", tout << "creating enode for #" << n->get_id() << "\n";); mk_enode(to_app(n), true, /* supress arguments, we not not use CC for this kind of enode */ true, /* bool enode must be merged with true/false, since it is not in the context of a gate */ false /* CC is not enabled */ ); set_enode_flag(v, false); if (get_assignment(v) != l_undef) propagate_bool_var_enode(v); } SASSERT(has_enode(v)); } return; } if (m_manager.is_eq(n)) internalize_eq(to_app(n), gate_ctx); else if (m_manager.is_distinct(n)) internalize_distinct(to_app(n), gate_ctx); else if (is_app(n) && internalize_theory_atom(to_app(n), gate_ctx)) return; else if (is_quantifier(n)) internalize_quantifier(to_quantifier(n), gate_ctx); else internalize_formula_core(to_app(n), gate_ctx); } /** \brief Internalize an equality. */ void context::internalize_eq(app * n, bool gate_ctx) { TRACE("internalize", tout << mk_pp(n, m_manager) << "\n";); SASSERT(!b_internalized(n)); SASSERT(m_manager.is_eq(n)); internalize_formula_core(n, gate_ctx); bool_var v = get_bool_var(n); bool_var_data & d = get_bdata(v); d.set_eq_flag(); sort * s = m_manager.get_sort(n->get_arg(0)); theory * th = m_theories.get_plugin(s->get_family_id()); if (th) th->internalize_eq_eh(n, v); } /** \brief Internalize distinct constructor. */ void context::internalize_distinct(app * n, bool gate_ctx) { TRACE("distinct", tout << "internalizing distinct: " << mk_pp(n, m_manager) << "\n";); SASSERT(!b_internalized(n)); SASSERT(m_manager.is_distinct(n)); expr * def = m_manager.mk_distinct_expanded(n->get_num_args(), n->get_args()); internalize(def, true); bool_var v = mk_bool_var(n); literal l(v); literal l_def = get_literal(def); mk_gate_clause(~l, l_def); mk_gate_clause(l, ~l_def); add_relevancy_dependency(n, def); if (!gate_ctx) { mk_enode(n, true, true, false); set_enode_flag(v, true); SASSERT(get_assignment(v) == l_undef); } } /** \brief Try to internalize n as a theory atom. Return true if succeeded. The application can be internalize as a theory atom, if there is a theory (plugin) that can internalize n. */ bool context::internalize_theory_atom(app * n, bool gate_ctx) { SASSERT(!b_internalized(n)); theory * th = m_theories.get_plugin(n->get_family_id()); TRACE("datatype_bug", tout << "internalizing theory atom:\n" << mk_pp(n, m_manager) << "\n";); if (!th || !th->internalize_atom(n, gate_ctx)) return false; TRACE("datatype_bug", tout << "internalization succeeded\n" << mk_pp(n, m_manager) << "\n";); SASSERT(b_internalized(n)); TRACE("internalize_theory_atom", tout << "internalizing theory atom: #" << n->get_id() << "\n";); bool_var v = get_bool_var(n); if (!gate_ctx) { // if the formula is not in the context of a gate, then it // must be associated with an enode. if (!e_internalized(n)) { mk_enode(to_app(n), true, /* supress arguments, we not not use CC for this kind of enode */ true /* bool enode must be merged with true/false, since it is not in the context of a gate */, false /* CC is not enabled */); } else { SASSERT(e_internalized(n)); enode * e = get_enode(n); set_enode_flag(v, true); set_merge_tf(e, v, true); } } if (e_internalized(n)) { set_enode_flag(v, true); if (get_assignment(v) != l_undef) propagate_bool_var_enode(v); } SASSERT(!e_internalized(n) || has_enode(v)); return true; } #ifdef Z3DEBUG struct check_pattern_proc { void operator()(var * v) {} void operator()(quantifier * q) {} void operator()(app * n) { if (is_ground(n)) return; SASSERT(n->get_decl()->is_flat_associative() || n->get_num_args() == n->get_decl()->get_arity()); } }; /** Debugging code: check whether for all (non-ground) applications (f a_1 ... a_n) in t, f->get_arity() == n */ static bool check_pattern(expr * t) { check_pattern_proc p; for_each_expr(p, t); return true; } static bool check_patterns(quantifier * q) { for (unsigned i = 0; i < q->get_num_patterns(); i++) { SASSERT(check_pattern(q->get_pattern(i))); } for (unsigned i = 0; i < q->get_num_no_patterns(); i++) { SASSERT(check_pattern(q->get_no_pattern(i))); } return true; } #endif /** \brief Internalize the given quantifier into the logical context. */ void context::internalize_quantifier(quantifier * q, bool gate_ctx) { TRACE("internalize_quantifier", tout << mk_pp(q, m_manager) << "\n";); CTRACE("internalize_quantifier_zero", q->get_weight() == 0, tout << mk_pp(q, m_manager) << "\n";); SASSERT(gate_ctx); // limitation of the current implementation SASSERT(!b_internalized(q)); SASSERT(q->is_forall()); SASSERT(check_patterns(q)); bool_var v = mk_bool_var(q); unsigned generation = m_generation; unsigned _generation; if (!m_cached_generation.empty() && m_cached_generation.find(q, _generation)) { generation = _generation; } // TODO: do we really need this flag? bool_var_data & d = get_bdata(v); d.set_quantifier_flag(); m_qmanager->add(q, generation); } /** \brief Internalize gates and (uninterpreted and equality) predicates. */ void context::internalize_formula_core(app * n, bool gate_ctx) { SASSERT(!b_internalized(n)); SASSERT(!e_internalized(n)); CTRACE("resolve_conflict_crash", m_manager.is_not(n), tout << mk_ismt2_pp(n, m_manager) << "\ngate_ctx: " << gate_ctx << "\n";); bool _is_gate = is_gate(m_manager, n) || m_manager.is_not(n); // process args unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = n->get_arg(i); internalize(arg, _is_gate); } CTRACE("internalize_bug", b_internalized(n), tout << mk_ll_pp(n, m_manager) << "\n";); bool is_new_var = false; bool_var v; // n can be already internalized after its children are internalized. // Example (ite-term): (= (ite c 1 0) 1) // // When (ite c 1 0) is internalized, it will force the internalization of (= (ite c 1 0) 1) and (= (ite c 1 0) 0) // // TODO: avoid the problem by delaying the internalization of (= (ite c 1 0) 1) and (= (ite c 1 0) 0). // Add them to a queue. if (!b_internalized(n)) { is_new_var = true; v = mk_bool_var(n); } else { v = get_bool_var(n); } // a formula needs to be associated with an enode when: // 1) it is not in the context of a gate, or // 2) it has arguments and it is not a gate (i.e., uninterpreted predicate or equality). if (!e_internalized(n) && (!gate_ctx || (!_is_gate && n->get_num_args() > 0))) { bool suppress_args = _is_gate || m_manager.is_not(n); bool merge_tf = !gate_ctx; mk_enode(n, suppress_args, merge_tf, true); set_enode_flag(v, is_new_var); SASSERT(has_enode(v)); } // The constraints associated with node 'n' should be asserted // after the bool_var and enode associated with are created. // Reason: incompleteness. An assigned boolean variable is only inserted // in m_atom_propagation_queue if the predicate is_atom() is true. // When the constraints for n are created, they may force v to be assigned. // Now, if v is assigned before being associated with an enode, then // v is not going to be inserted in m_atom_propagation_queue, and // propagate_bool_var_enode() method is not going to be invoked for v. if (is_new_var && n->get_family_id() == m_manager.get_basic_family_id()) { switch (n->get_decl_kind()) { case OP_NOT: SASSERT(!gate_ctx); mk_not_cnstr(to_app(n)); break; case OP_AND: mk_and_cnstr(to_app(n)); add_and_rel_watches(to_app(n)); break; case OP_OR: mk_or_cnstr(to_app(n)); add_or_rel_watches(to_app(n)); break; case OP_IFF: mk_iff_cnstr(to_app(n)); break; case OP_ITE: mk_ite_cnstr(to_app(n)); add_ite_rel_watches(to_app(n)); break; case OP_DISTINCT: UNREACHABLE(); default: break; } } CTRACE("internalize_bug", e_internalized(n), tout << "#" << n->get_id() << ", merge_tf: " << get_enode(n)->merge_tf() << "\n";); } /** \brief Trail object to disable the m_merge_tf flag of an enode. */ class set_merge_tf_trail : public trail { enode * m_node; public: set_merge_tf_trail(enode * n): m_node(n) { } virtual void undo(context & ctx) { m_node->m_merge_tf = false; } }; /** \brief Enable the flag m_merge_tf in the given enode. When the flag m_merge_tf is enabled, the enode n will be merged with the true_enode (false_enode) whenever the boolean variable v is assigned to true (false). If is_new_var is true, then trail is not created for the flag uodate. */ void context::set_merge_tf(enode * n, bool_var v, bool is_new_var) { SASSERT(bool_var2enode(v) == n); if (!n->m_merge_tf) { if (!is_new_var) push_trail(set_merge_tf_trail(n)); n->m_merge_tf = true; lbool val = get_assignment(v); if (val != l_undef) push_eq(n, val == l_true ? m_true_enode : m_false_enode, eq_justification(literal(v, val == l_false))); } } /** \brief Trail object to disable the m_enode flag of a boolean variable. The flag m_enode is true for a boolean variable v, if there is an enode n associated with it. */ class set_enode_flag_trail : public trail { bool_var m_var; public: set_enode_flag_trail(bool_var v): m_var(v) { } virtual void undo(context & ctx) { bool_var_data & data = ctx.m_bdata[m_var]; data.reset_enode_flag(); } }; /** \brief Enable the flag m_enode in the given boolean variable. That is, the boolean variable is associated with an enode. If is_new_var is true, then trail is not created for the flag uodate. */ void context::set_enode_flag(bool_var v, bool is_new_var) { SASSERT(e_internalized(bool_var2expr(v))); bool_var_data & data = m_bdata[v]; if (!data.is_enode()) { if (!is_new_var) push_trail(set_enode_flag_trail(v)); data.set_enode_flag(); } } /** \brief Internalize the given term into the logical context. */ void context::internalize_term(app * n) { if (e_internalized(n)) { theory * th = m_theories.get_plugin(n->get_family_id()); if (th != 0) { // This code is necessary because some theories may decide // not to create theory variables for a nested application. // Example: // Suppose (+ (* 2 x) y) is internalized by arithmetic // and an enode is created for the + and * applications, // but a theory variable is only created for the + application. // The (* 2 x) is internal to the arithmetic module. // Later, the core tries to internalize (f (* 2 x)). // Now, (* 2 x) is not internal to arithmetic anymore, // and a theory variable must be created for it. enode * e = get_enode(n); if (!th->is_attached_to_var(e)) internalize_theory_term(n); } return; } if (m_manager.is_term_ite(n)) { internalize_ite_term(n); return; // it is not necessary to apply sort constraint } else if (internalize_theory_term(n)) { // skip } else { internalize_uninterpreted(n); } SASSERT(e_internalized(n)); enode * e = get_enode(n); apply_sort_cnstr(n, e); } /** \brief Internalize an if-then-else term. */ void context::internalize_ite_term(app * n) { SASSERT(!e_internalized(n)); expr * c = n->get_arg(0); expr * t = n->get_arg(1); expr * e = n->get_arg(2); app * eq1 = mk_eq_atom(n, t); app * eq2 = mk_eq_atom(n, e); mk_enode(n, true /* supress arguments, I don't want to apply CC on ite terms */, false /* it is a term, so it should not be merged with true/false */, false /* CC is not enabled */); internalize(c, true); internalize(t, false); internalize(e, false); internalize(eq1, true); internalize(eq2, true); literal c_lit = get_literal(c); literal eq1_lit = get_literal(eq1); literal eq2_lit = get_literal(eq2); TRACE("internalize_ite_term_bug", tout << mk_ismt2_pp(n, m_manager) << "\n"; tout << mk_ismt2_pp(c, m_manager) << "\n"; tout << mk_ismt2_pp(t, m_manager) << "\n"; tout << mk_ismt2_pp(e, m_manager) << "\n"; tout << mk_ismt2_pp(eq1, m_manager) << "\n"; tout << mk_ismt2_pp(eq2, m_manager) << "\n"; tout << "literals:\n" << c_lit << " " << eq1_lit << " " << eq2_lit << "\n";); mk_gate_clause(~c_lit, eq1_lit); mk_gate_clause( c_lit, eq2_lit); if (relevancy()) { relevancy_eh * eh = m_relevancy_propagator->mk_term_ite_relevancy_eh(n, eq1, eq2); TRACE("ite_term_relevancy", tout << "#" << n->get_id() << " #" << eq1->get_id() << " #" << eq2->get_id() << "\n";); add_rel_watch(c_lit, eh); add_rel_watch(~c_lit, eh); add_relevancy_eh(n, eh); } SASSERT(e_internalized(n)); } /** \brief Try to internalize a theory term. That is, a theory (plugin) will be invoked to internalize n. Return true if succeeded. It may fail because there is no plugin or the plugin does not support it. */ bool context::internalize_theory_term(app * n) { theory * th = m_theories.get_plugin(n->get_family_id()); if (!th || !th->internalize_term(n)) return false; return true; } /** \brief Internalize an uninterpreted function application or constant. */ void context::internalize_uninterpreted(app * n) { SASSERT(!e_internalized(n)); // process args unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = n->get_arg(i); internalize(arg, false); SASSERT(e_internalized(arg)); } enode * e = mk_enode(n, false, /* do not supress args */ false, /* it is a term, so it should not be merged with true/false */ true); apply_sort_cnstr(n, e); } /** \brief Create a new boolean variable and associate it with n. */ bool_var context::mk_bool_var(expr * n) { SASSERT(!b_internalized(n)); //SASSERT(!m_manager.is_not(n)); unsigned id = n->get_id(); bool_var v = m_b_internalized_stack.size(); #ifndef _EXTERNAL_RELEASE if (m_fparams.m_display_bool_var2expr) { char const * header = "(iff z3@"; int id_sz = 6; std::cerr.width(id_sz); std::cerr << header << std::left << v << " " << mk_pp(n, m_manager, static_cast(strlen(header)) + id_sz + 1) << ")\n"; } if (m_fparams.m_display_ll_bool_var2expr) { std::cerr << v << " ::=\n" << mk_ll_pp(n, m_manager) << "\n"; } #endif TRACE("mk_bool_var", tout << "creating boolean variable: " << v << " for:\n" << mk_pp(n, m_manager) << "\n";); TRACE("mk_var_bug", tout << "mk_bool: " << v << "\n";); set_bool_var(id, v); m_bdata.reserve(v+1); m_activity.reserve(v+1); m_bool_var2expr.reserve(v+1); m_bool_var2expr[v] = n; literal l(v, false); literal not_l(v, true); unsigned aux = std::max(l.index(), not_l.index()) + 1; m_assignment.reserve(aux); m_assignment[l.index()] = l_undef; m_assignment[not_l.index()] = l_undef; m_watches.reserve(aux); SASSERT(m_assignment.size() == m_watches.size()); m_watches[l.index()] .reset(); m_watches[not_l.index()] .reset(); if (lit_occs_enabled()) { m_lit_occs.reserve(aux); m_lit_occs[l.index()] .reset(); m_lit_occs[not_l.index()] .reset(); } bool_var_data & data = m_bdata[v]; unsigned iscope_lvl = m_scope_lvl; // record when the boolean variable was internalized. data.init(iscope_lvl); if (m_fparams.m_random_initial_activity == IA_RANDOM || (m_fparams.m_random_initial_activity == IA_RANDOM_WHEN_SEARCHING && m_searching)) m_activity[v] = -((m_random() % 1000) / 1000.0); else m_activity[v] = 0.0; m_case_split_queue->mk_var_eh(v); m_b_internalized_stack.push_back(n); m_trail_stack.push_back(&m_mk_bool_var_trail); m_stats.m_num_mk_bool_var++; SASSERT(check_bool_var_vector_sizes()); return v; } void context::undo_mk_bool_var() { SASSERT(!m_b_internalized_stack.empty()); m_stats.m_num_del_bool_var++; expr * n = m_b_internalized_stack.back(); unsigned n_id = n->get_id(); bool_var v = get_bool_var_of_id(n_id); TRACE("undo_mk_bool_var", tout << "undo_bool: " << v << "\n" << mk_pp(n, m_manager) << "\n" << "m_bdata.size: " << m_bdata.size() << " m_assignment.size: " << m_assignment.size() << "\n";); TRACE("mk_var_bug", tout << "undo_mk_bool: " << v << "\n";); // bool_var_data & d = m_bdata[v]; m_case_split_queue->del_var_eh(v); if (is_quantifier(n)) m_qmanager->del(to_quantifier(n)); set_bool_var(n_id, null_bool_var); m_b_internalized_stack.pop_back(); } /** \brief Create an new enode. \remark If suppress_args is true, then the enode is viewed as a constant in the egraph. */ enode * context::mk_enode(app * n, bool suppress_args, bool merge_tf, bool cgc_enabled) { TRACE("mk_enode_detail", tout << mk_pp(n, m_manager) << "\nsuppress_args: " << suppress_args << ", merge_tf: " << merge_tf << ", cgc_enabled: " << cgc_enabled << "\n";); SASSERT(!e_internalized(n)); unsigned id = n->get_id(); unsigned generation = m_generation; unsigned _generation = 0; if (!m_cached_generation.empty() && m_cached_generation.find(n, _generation)) { generation = _generation; CTRACE("cached_generation", generation != m_generation, tout << "cached_generation: #" << n->get_id() << " " << generation << " " << m_generation << "\n";); } enode * e = enode::mk(m_manager, m_region, m_app2enode, n, generation, suppress_args, merge_tf, m_scope_lvl, cgc_enabled, true); TRACE("mk_enode_detail", tout << "e.get_num_args() = " << e->get_num_args() << "\n";); if (n->get_num_args() == 0 && m_manager.is_unique_value(n)) e->mark_as_interpreted(); TRACE("mk_var_bug", tout << "mk_enode: " << id << "\n";); TRACE("generation", tout << "mk_enode: " << id << " " << generation << "\n";); m_app2enode.setx(id, e, 0); m_e_internalized_stack.push_back(n); m_trail_stack.push_back(&m_mk_enode_trail); m_enodes.push_back(e); if (e->get_num_args() > 0) { if (e->is_true_eq()) { bool_var v = enode2bool_var(e); assign(literal(v), mk_justification(eq_propagation_justification(e->get_arg(0), e->get_arg(1)))); e->m_cg = e; } else { if (cgc_enabled) { enode_bool_pair pair = m_cg_table.insert(e); enode * e_prime = pair.first; if (e != e_prime) { e->m_cg = e_prime; bool used_commutativity = pair.second; push_new_congruence(e, e_prime, used_commutativity); } else { e->m_cg = e; } } else { e->m_cg = e; } } if (!e->is_eq()) { unsigned decl_id = n->get_decl()->get_decl_id(); if (decl_id >= m_decl2enodes.size()) m_decl2enodes.resize(decl_id+1); m_decl2enodes[decl_id].push_back(e); } } SASSERT(e_internalized(n)); m_stats.m_num_mk_enode++; TRACE("mk_enode", tout << "created enode: #" << e->get_owner_id() << " for:\n" << mk_pp(n, m_manager) << "\n"; if (e->get_num_args() > 0) { tout << "is_true_eq: " << e->is_true_eq() << " in cg_table: " << m_cg_table.contains_ptr(e) << " is_cgr: " << e->is_cgr() << "\n"; }); if (m_manager.has_trace_stream()) m_manager.trace_stream() << "[attach-enode] #" << n->get_id() << " " << m_generation << "\n"; return e; } void context::undo_mk_enode() { SASSERT(!m_e_internalized_stack.empty()); m_stats.m_num_del_enode++; expr * n = m_e_internalized_stack.back(); TRACE("undo_mk_enode", tout << "undo_enode: #" << n->get_id() << "\n" << mk_pp(n, m_manager) << "\n";); TRACE("mk_var_bug", tout << "undo_mk_enode: " << n->get_id() << "\n";); unsigned n_id = n->get_id(); SASSERT(is_app(n)); enode * e = m_app2enode[n_id]; m_app2enode[n_id] = 0; if (e->is_cgr() && !e->is_true_eq() && e->is_cgc_enabled()) { SASSERT(m_cg_table.contains_ptr(e)); m_cg_table.erase(e); } if (e->get_num_args() > 0 && !e->is_eq()) { unsigned decl_id = to_app(n)->get_decl()->get_decl_id(); SASSERT(decl_id < m_decl2enodes.size()); SASSERT(m_decl2enodes[decl_id].back() == e); m_decl2enodes[decl_id].pop_back(); } e->del_eh(m_manager); SASSERT(m_e_internalized_stack.size() == m_enodes.size()); m_enodes.pop_back(); m_e_internalized_stack.pop_back(); } /** \brief Apply sort constraints on e. */ void context::apply_sort_cnstr(app * term, enode * e) { sort * s = term->get_decl()->get_range(); theory * th = m_theories.get_plugin(s->get_family_id()); if (th) th->apply_sort_cnstr(e, s); } /** \brief Return the literal associated with n. */ literal context::get_literal(expr * n) const { if (m_manager.is_not(n)) { CTRACE("get_literal_bug", !b_internalized(to_app(n)->get_arg(0)), tout << mk_ll_pp(n, m_manager) << "\n";); SASSERT(b_internalized(to_app(n)->get_arg(0))); return literal(get_bool_var(to_app(n)->get_arg(0)), true); } else if (m_manager.is_true(n)) { return true_literal; } else if (m_manager.is_false(n)) { return false_literal; } else { SASSERT(b_internalized(n)); return literal(get_bool_var(n), false); } } /** \brief Simplify the literals of an auxiliary clause. An auxiliary clause is transient. So, the current assignment can be used for simplification. The following simplifications are applied: - Duplicates are removed. - Literals assigned to false are removed - If l and ~l are in lits, then return false (the clause is equivalent to true) - If a literal in source is assigned to true, then return false. \remark The removed literals are stored in simp_lits It is safe to use the current assignment to simplify aux clauses because they are deleted during backtracking. */ bool context::simplify_aux_clause_literals(unsigned & num_lits, literal * lits, literal_buffer & simp_lits) { std::sort(lits, lits + num_lits); literal prev = null_literal; unsigned i = 0; unsigned j = 0; for (; i < num_lits; i++) { literal curr = lits[i]; lbool val = get_assignment(curr); if (val == l_false) simp_lits.push_back(~curr); switch(val) { case l_false: break; // ignore literal case l_undef: if (curr == ~prev) return false; // clause is equivalent to true if (curr != prev) { prev = curr; if (i != j) lits[j] = lits[i]; j++; } break; case l_true: return false; // clause is equivalent to true } } num_lits = j; return true; } /** \brief Simplify the literals of an auxiliary lemma. An auxiliary lemma has the status of a learned clause, but it is not created by conflict resolution. A dynamic ackermann clause is an example of auxiliary lemma. The following simplifications are applied: - Duplicates are removed. - If a literal is assigned to true at a base level, then return false (the clause is equivalent to true). - If l and ~l are in lits, then return false (source is irrelevant, that is, it is equivalent to true) \remark Literals assigned to false at the base level are not removed because I don't want to create a justification for this kind of simplification. */ bool context::simplify_aux_lemma_literals(unsigned & num_lits, literal * lits) { TRACE("simplify_aux_lemma_literals", tout << "1) "; display_literals(tout, num_lits, lits); tout << "\n";); std::sort(lits, lits + num_lits); TRACE("simplify_aux_lemma_literals", tout << "2) "; display_literals(tout, num_lits, lits); tout << "\n";); literal prev = null_literal; unsigned i = 0; unsigned j = 0; for (; i < num_lits; i++) { literal curr = lits[i]; bool_var var = curr.var(); lbool val = l_undef; if (get_assign_level(var) <= m_base_lvl) val = get_assignment(curr); if (val == l_true) return false; // clause is equivalent to true if (curr == ~prev) return false; // clause is equivalent to true if (curr != prev) { prev = curr; if (i != j) lits[j] = lits[i]; j++; } } num_lits = j; TRACE("simplify_aux_lemma_literals", tout << "3) "; display_literals(tout, num_lits, lits); tout << "\n";); return true; } /** \brief A clause (lemma or aux lemma) may need to be reinitialized for two reasons: 1) Lemmas and aux lemmas may contain literals that were created during the search, and the maximum internalization scope level of its literals is scope_lvl. Since the clauses may remain alive when scope_lvl is backtracked, it must be reinitialised. In this case, reinitialize_atoms must be true. 2) An aux lemma is in conflict or propagated a literal when it was created. Then, we should check whether the aux lemma is still in conflict or propagating a literal after backtracking the current scope level. */ void context::mark_for_reinit(clause * cls, unsigned scope_lvl, bool reinternalize_atoms) { SASSERT(scope_lvl >= m_base_lvl); cls->m_reinit = true; cls->m_reinternalize_atoms = reinternalize_atoms; if (scope_lvl >= m_clauses_to_reinit.size()) m_clauses_to_reinit.resize(scope_lvl+1, clause_vector()); m_clauses_to_reinit[scope_lvl].push_back(cls); } /** \brief Return max({ get_intern_level(var) | var \in lits }) */ unsigned context::get_max_iscope_lvl(unsigned num_lits, literal const * lits) const { unsigned r = 0; for (unsigned i = 0; i < num_lits; i++) { unsigned ilvl = get_intern_level(lits[i].var()); if (ilvl > r) r = ilvl; } return r; } /** \brief Return true if it safe to use the binary clause optimization at this point in time. */ bool context::use_binary_clause_opt(literal l1, literal l2, bool lemma) const { if (!binary_clause_opt_enabled()) return false; // When relevancy is enable binary clauses should not be used. // Reason: when a learned clause becomes unit, it should mark the // unit literal as relevant. When binary_clause_opt is used, // it is not possible to distinguish between learned and non-learned clauses. if (lemma && m_fparams.m_relevancy_lvl >= 2) return false; if (m_base_lvl > 0) return false; if (!lemma && m_scope_lvl > 0) return false; if (get_intern_level(l1.var()) > 0) return false; if (get_intern_level(l2.var()) > 0) return false; return true; } /** \brief The learned clauses (lemmas) produced by the SAT solver have the property that the first literal will be implied by it after backtracking. All other literals are assigned to (or implied to be) false when the learned clause is created. The first watch literal will always be the first literal. The second watch literal is computed by this method. It should be the literal with the highest decision level. If a literal is not assigned, it means it was re-initialized after backtracking. So, its level is assumed to be m_scope_lvl. */ int context::select_learned_watch_lit(clause const * cls) const { SASSERT(cls->get_num_literals() >= 2); int max_false_idx = -1; unsigned max_lvl = UINT_MAX; int num_lits = cls->get_num_literals(); for (int i = 1; i < num_lits; i++) { literal l = cls->get_literal(i); lbool val = get_assignment(l); SASSERT(val == l_false || val == l_undef); unsigned lvl = val == l_false ? get_assign_level(l) : m_scope_lvl; if (max_false_idx == -1 || lvl > max_lvl) { max_false_idx = i; max_lvl = lvl; } } return max_false_idx; } /** \brief Select a watch literal from a set of literals which is different from the literal in position other_watch_lit. I use the following rules to select a watch literal. 1- select a literal l in idx >= starting_at such that get_assignment(l) = l_true, and for all l' in idx' >= starting_at . get_assignment(l') = l_true implies get_level(l) <= get_level(l') The purpose of this rule is to make the clause inactive for as long as possible. A clause is inactive when it contains a literal assigned to true. 2- if there isn't a literal assigned to true, then select an unassigned literal l is in idx >= starting_at 3- if there isn't a literal l in idx >= starting_at such that get_assignment(l) = l_true or get_assignment(l) = l_undef (that is, all literals different from other_watch_lit are assigned to false), then peek the literal l different starting at starting_at such that for all l' starting at starting_at get_level(l) >= get_level(l') Without rule 3, boolean propagation is incomplete, that is, it may miss possible propagations. \remark The method select_lemma_watch_lit is used to select the watch literal for regular learned clauses. */ int context::select_watch_lit(clause const * cls, int starting_at) const { SASSERT(cls->get_num_literals() >= 2); int min_true_idx = -1; int max_false_idx = -1; int unknown_idx = -1; int n = cls->get_num_literals(); for (int i = starting_at; i < n; i++) { literal l = cls->get_literal(i); switch(get_assignment(l)) { case l_false: if (max_false_idx == -1 || get_assign_level(l.var()) > get_assign_level(cls->get_literal(max_false_idx).var())) max_false_idx = i; break; case l_undef: unknown_idx = i; break; case l_true: if (min_true_idx == -1 || get_assign_level(l.var()) < get_assign_level(cls->get_literal(min_true_idx).var())) min_true_idx = i; break; } } if (min_true_idx != -1) return min_true_idx; if (unknown_idx != -1) return unknown_idx; SASSERT(max_false_idx != -1); return max_false_idx; } /** \brief Add watch literal to the given clause. \pre idx must be 0 or 1. */ void context::add_watch_literal(clause * cls, unsigned idx) { SASSERT(idx == 0 || idx == 1); literal l = cls->get_literal(idx); unsigned l_idx = (~l).index(); watch_list & wl = const_cast(m_watches[l_idx]); wl.insert_clause(cls); CASSERT("watch_list", check_watch_list(l_idx)); } /** \brief Create a new clause using the given literals, justification, kind and deletion event handler. The deletion event handler is ignored if binary clause optimization is applicable. */ clause * context::mk_clause(unsigned num_lits, literal * lits, justification * j, clause_kind k, clause_del_eh * del_eh) { TRACE("mk_clause", tout << "creating clause:\n"; display_literals(tout, num_lits, lits); tout << "\n";); switch (k) { case CLS_AUX: { unsigned old_num_lits = num_lits; literal_buffer simp_lits; if (!simplify_aux_clause_literals(num_lits, lits, simp_lits)) return 0; // clause is equivalent to true; DEBUG_CODE({ for (unsigned i = 0; i < simp_lits.size(); i++) { SASSERT(get_assignment(simp_lits[i]) == l_true); } }); if (old_num_lits != num_lits) j = mk_justification(unit_resolution_justification(m_region, j, simp_lits.size(), simp_lits.c_ptr())); break; } case CLS_AUX_LEMMA: { if (!simplify_aux_lemma_literals(num_lits, lits)) return 0; // clause is equivalent to true // simplify_aux_lemma_literals does not delete literals assigned to false, so // it is not necessary to create a unit_resolution_justification break; } default: break; } TRACE("mk_clause", tout << "after simplification:\n"; display_literals(tout, num_lits, lits); tout << "\n";); unsigned activity = 0; if (activity == 0) activity = 1; bool lemma = k != CLS_AUX; m_stats.m_num_mk_lits += num_lits; switch (num_lits) { case 0: if (j && !j->in_region()) m_justifications.push_back(j); TRACE("mk_clause", tout << "empty clause... setting conflict\n";); set_conflict(j == 0 ? b_justification::mk_axiom() : b_justification(j)); SASSERT(inconsistent()); return 0; case 1: if (j && !j->in_region()) m_justifications.push_back(j); assign(lits[0], j); return 0; case 2: if (use_binary_clause_opt(lits[0], lits[1], lemma)) { literal l1 = lits[0]; literal l2 = lits[1]; m_watches[(~l1).index()].insert_literal(l2); m_watches[(~l2).index()].insert_literal(l1); if (get_assignment(l2) == l_false) assign(l1, b_justification(~l2)); m_stats.m_num_mk_bin_clause++; return 0; } default: { m_stats.m_num_mk_clause++; unsigned iscope_lvl = lemma ? get_max_iscope_lvl(num_lits, lits) : 0; SASSERT(m_scope_lvl >= iscope_lvl); bool save_atoms = lemma && iscope_lvl > m_base_lvl; bool reinit = save_atoms; SASSERT(!lemma || j == 0 || !j->in_region()); clause * cls = clause::mk(m_manager, num_lits, lits, k, j, del_eh, save_atoms, m_bool_var2expr.c_ptr()); if (lemma) { cls->set_activity(activity); if (k == CLS_LEARNED) { int w2_idx = select_learned_watch_lit(cls); cls->swap_lits(1, w2_idx); } else { SASSERT(k == CLS_AUX_LEMMA); int w1_idx = select_watch_lit(cls, 0); cls->swap_lits(0, w1_idx); int w2_idx = select_watch_lit(cls, 1); cls->swap_lits(1, w2_idx); TRACE("mk_th_lemma", display_clause(tout, cls); tout << "\n";); } m_lemmas.push_back(cls); add_watch_literal(cls, 0); add_watch_literal(cls, 1); if (get_assignment(cls->get_literal(0)) == l_false) { set_conflict(b_justification(cls)); if (k == CLS_AUX_LEMMA && m_scope_lvl > m_base_lvl) { reinit = true; iscope_lvl = m_scope_lvl; } } else if (get_assignment(cls->get_literal(1)) == l_false) { assign(cls->get_literal(0), b_justification(cls)); if (k == CLS_AUX_LEMMA && m_scope_lvl > m_base_lvl) { reinit = true; iscope_lvl = m_scope_lvl; } } if (reinit) mark_for_reinit(cls, iscope_lvl, save_atoms); } else { m_aux_clauses.push_back(cls); add_watch_literal(cls, 0); add_watch_literal(cls, 1); if (get_assignment(cls->get_literal(0)) == l_false) set_conflict(b_justification(cls)); else if (get_assignment(cls->get_literal(1)) == l_false) assign(cls->get_literal(0), b_justification(cls)); } if (lit_occs_enabled()) add_lit_occs(cls); TRACE("add_watch_literal_bug", display_clause_detail(tout, cls);); TRACE("mk_clause_result", display_clause_detail(tout, cls);); CASSERT("mk_clause", check_clause(cls)); return cls; }} } void context::add_lit_occs(clause * cls) { unsigned num_lits = cls->get_num_literals(); for (unsigned i = 0; i < num_lits; i++) { literal l = cls->get_literal(i); m_lit_occs[l.index()].insert(cls); } } void context::mk_clause(literal l1, literal l2, justification * j) { literal ls[2] = { l1, l2 }; mk_clause(2, ls, j); } void context::mk_clause(literal l1, literal l2, literal l3, justification * j) { literal ls[3] = { l1, l2, l3 }; mk_clause(3, ls, j); } void context::mk_th_axiom(theory_id tid, unsigned num_lits, literal * lits, unsigned num_params, parameter * params) { justification * js = 0; TRACE("mk_th_axiom", display_literals_verbose(tout, num_lits, lits); tout << "\n";); if (m_manager.proofs_enabled()) { js = mk_justification(theory_axiom_justification(tid, m_region, num_lits, lits, num_params, params)); } if (m_fparams.m_smtlib_dump_lemmas) { literal_buffer tmp; neg_literals(num_lits, lits, tmp); SASSERT(tmp.size() == num_lits); display_lemma_as_smt_problem(tmp.size(), tmp.c_ptr(), false_literal, m_fparams.m_smtlib_logic.c_str()); } mk_clause(num_lits, lits, js); } void context::mk_th_axiom(theory_id tid, literal l1, literal l2, unsigned num_params, parameter * params) { literal ls[2] = { l1, l2 }; mk_th_axiom(tid, 2, ls, num_params, params); } void context::mk_th_axiom(theory_id tid, literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { literal ls[3] = { l1, l2, l3 }; mk_th_axiom(tid, 3, ls, num_params, params); } proof * context::mk_clause_def_axiom(unsigned num_lits, literal * lits, expr * root_gate) { ptr_buffer new_lits; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; bool_var v = l.var(); expr * atom = m_bool_var2expr[v]; new_lits.push_back(l.sign() ? m_manager.mk_not(atom) : atom); } if (root_gate) new_lits.push_back(m_manager.mk_not(root_gate)); SASSERT(num_lits > 1); expr * fact = m_manager.mk_or(new_lits.size(), new_lits.c_ptr()); return m_manager.mk_def_axiom(fact); } void context::mk_gate_clause(unsigned num_lits, literal * lits) { if (m_manager.proofs_enabled()) { proof * pr = mk_clause_def_axiom(num_lits, lits, 0); TRACE("gate_clause", tout << mk_ll_pp(pr, m_manager);); mk_clause(num_lits, lits, mk_justification(justification_proof_wrapper(*this, pr))); } else { mk_clause(num_lits, lits, 0); } } void context::mk_gate_clause(literal l1, literal l2) { literal ls[2] = { l1, l2 }; mk_gate_clause(2, ls); } void context::mk_gate_clause(literal l1, literal l2, literal l3) { literal ls[3] = { l1, l2, l3 }; mk_gate_clause(3, ls); } void context::mk_gate_clause(literal l1, literal l2, literal l3, literal l4) { literal ls[4] = { l1, l2, l3, l4 }; mk_gate_clause(4, ls); } void context::mk_root_clause(unsigned num_lits, literal * lits, proof * pr) { if (m_manager.proofs_enabled()) { SASSERT(m_manager.get_fact(pr)); expr * fact = m_manager.get_fact(pr); if (!m_manager.is_or(fact)) { proof * def = mk_clause_def_axiom(num_lits, lits, m_manager.get_fact(pr)); TRACE("gate_clause", tout << mk_ll_pp(def, m_manager) << "\n"; tout << mk_ll_pp(pr, m_manager);); proof * prs[2] = { def, pr }; pr = m_manager.mk_unit_resolution(2, prs); } mk_clause(num_lits, lits, mk_justification(justification_proof_wrapper(*this, pr))); } else { mk_clause(num_lits, lits, 0); } } void context::mk_root_clause(literal l1, literal l2, proof * pr) { literal ls[2] = { l1, l2 }; mk_root_clause(2, ls, pr); } void context::mk_root_clause(literal l1, literal l2, literal l3, proof * pr) { literal ls[3] = { l1, l2, l3 }; mk_root_clause(3, ls, pr); } void context::add_and_rel_watches(app * n) { if (relevancy()) { relevancy_eh * eh = m_relevancy_propagator->mk_and_relevancy_eh(n); unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { // if one child is assigned to false, the the and-parent must be notified literal l = get_literal(n->get_arg(i)); add_rel_watch(~l, eh); } } } void context::add_or_rel_watches(app * n) { if (relevancy()) { relevancy_eh * eh = m_relevancy_propagator->mk_or_relevancy_eh(n); unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { // if one child is assigned to true, the the or-parent must be notified literal l = get_literal(n->get_arg(i)); add_rel_watch(l, eh); } } } void context::add_ite_rel_watches(app * n) { if (relevancy()) { relevancy_eh * eh = m_relevancy_propagator->mk_ite_relevancy_eh(n); literal l = get_literal(n->get_arg(0)); // when the condition of an ite is assigned to true or false, the ite-parent must be notified. TRACE("propagate_relevant_ite", tout << "#" << n->get_id() << ", eh: " << eh << "\n";); add_rel_watch(l, eh); add_rel_watch(~l, eh); } } void context::mk_not_cnstr(app * n) { SASSERT(b_internalized(n)); bool_var v = get_bool_var(n); literal l(v, false); literal c = get_literal(n->get_arg(0)); mk_gate_clause(~l, ~c); mk_gate_clause(l, c); } void context::mk_and_cnstr(app * n) { literal l = get_literal(n); TRACE("mk_and_cnstr", tout << "l: "; display_literal(tout, l); tout << "\n";); literal_buffer buffer; buffer.push_back(l); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { literal l_arg = get_literal(n->get_arg(i)); TRACE("mk_and_cnstr", tout << "l_arg: "; display_literal(tout, l_arg); tout << "\n";); mk_gate_clause(~l, l_arg); buffer.push_back(~l_arg); } mk_gate_clause(buffer.size(), buffer.c_ptr()); } void context::mk_or_cnstr(app * n) { literal l = get_literal(n); literal_buffer buffer; buffer.push_back(~l); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { literal l_arg = get_literal(n->get_arg(i)); mk_gate_clause(l, ~l_arg); buffer.push_back(l_arg); } mk_gate_clause(buffer.size(), buffer.c_ptr()); } void context::mk_iff_cnstr(app * n) { literal l = get_literal(n); literal l1 = get_literal(n->get_arg(0)); literal l2 = get_literal(n->get_arg(1)); TRACE("mk_iff_cnstr", tout << "l: " << l << ", l1: " << l1 << ", l2: " << l2 << "\n";); mk_gate_clause(~l, l1, ~l2); mk_gate_clause(~l, ~l1 , l2); mk_gate_clause( l, l1, l2); mk_gate_clause( l, ~l1, ~l2); } void context::mk_ite_cnstr(app * n) { literal l = get_literal(n); literal l1 = get_literal(n->get_arg(0)); literal l2 = get_literal(n->get_arg(1)); literal l3 = get_literal(n->get_arg(2)); mk_gate_clause(~l, ~l1, l2); mk_gate_clause(~l, l1, l3); mk_gate_clause(l, ~l1, ~l2); mk_gate_clause(l, l1, ~l3); } /** \brief Trail for add_th_var */ class add_th_var_trail : public trail { enode * m_enode; theory_id m_th_id; #ifdef Z3DEBUG theory_var m_th_var; #endif public: add_th_var_trail(enode * n, theory_id th_id): m_enode(n), m_th_id(th_id) { DEBUG_CODE(m_th_var = n->get_th_var(th_id);); SASSERT(m_th_var != null_theory_var); } virtual void undo(context & ctx) { theory_var v = m_enode->get_th_var(m_th_id); SASSERT(v != null_theory_var); SASSERT(m_th_var == v); m_enode->del_th_var(m_th_id); enode * root = m_enode->get_root(); if (root != m_enode && root->get_th_var(m_th_id) == v) root->del_th_var(m_th_id); } }; /** \brief Trail for replace_th_var */ class replace_th_var_trail : public trail { enode * m_enode; unsigned m_th_id:8; unsigned m_old_th_var:24; public: replace_th_var_trail(enode * n, theory_id th_id, theory_var old_var): m_enode(n), m_th_id(th_id), m_old_th_var(old_var) { } virtual void undo(context & ctx) { SASSERT(m_enode->get_th_var(m_th_id) != null_theory_var); m_enode->replace_th_var(m_old_th_var, m_th_id); } }; /** \brief Attach theory var v to the enode n. Enode n is to attached to any theory variable of th. This method should be invoked whenever the theory creates a new theory variable. \remark The methods new_eq_eh and new_diseq_eh of th may be invoked before this method returns. */ void context::attach_th_var(enode * n, theory * th, theory_var v) { SASSERT(!th->is_attached_to_var(n)); theory_id th_id = th->get_id(); theory_var old_v = n->get_th_var(th_id); if (old_v == null_theory_var) { enode * r = n->get_root(); theory_var v2 = r->get_th_var(th_id); n->add_th_var(v, th_id, m_region); push_trail(add_th_var_trail(n, th_id)); if (v2 == null_theory_var) { if (r != n) r->add_th_var(v, th_id, m_region); push_new_th_diseqs(r, v, th); } else if (r != n) { push_new_th_eq(th_id, v2, v); } } else { // Case) there is a variable old_v in the var-list of n. // // Remark: This variable was moved to the var-list of n due to a add_eq. SASSERT(th->get_enode(old_v) != n); // this varialbe is not owned by n SASSERT(n->get_root()->get_th_var(th_id) != null_theory_var); // the root has also a variable in its var-list. n->replace_th_var(v, th_id); push_trail(replace_th_var_trail(n, th_id, old_v)); push_new_th_eq(th_id, v, old_v); } SASSERT(th->is_attached_to_var(n)); } }; z3-z3-4.4.1/src/smt/smt_justification.cpp000066400000000000000000000356751260446376700203010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_justification.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-25. Revision History: --*/ #include"smt_context.h" #include"smt_conflict_resolution.h" #include"ast_pp.h" #include"ast_ll_pp.h" namespace smt { justification_proof_wrapper::justification_proof_wrapper(context & ctx, proof * pr, bool in_region): justification(in_region), m_proof(pr) { ctx.get_manager().inc_ref(pr); } void justification_proof_wrapper::del_eh(ast_manager & m) { m.dec_ref(m_proof); } proof * justification_proof_wrapper::mk_proof(conflict_resolution & cr) { return m_proof; } unit_resolution_justification::unit_resolution_justification(region & r, justification * js, unsigned num_lits, literal const * lits): m_antecedent(js), m_num_literals(num_lits) { SASSERT(!js || js->in_region()); m_literals = new (r) literal[num_lits]; memcpy(m_literals, lits, sizeof(literal) * num_lits); TRACE("unit_resolution_justification_bug", for (unsigned i = 0; i < num_lits; i++) { tout << lits[i] << " "; } tout << "\n";); } unit_resolution_justification::unit_resolution_justification(justification * js, unsigned num_lits, literal const * lits): justification(false), // object is not allocated in a region m_antecedent(js), m_num_literals(num_lits) { SASSERT(!js || !js->in_region()); m_literals = alloc_vect(num_lits); memcpy(m_literals, lits, sizeof(literal) * num_lits); TRACE("unit_resolution_justification_bug", for (unsigned i = 0; i < num_lits; i++) { tout << lits[i] << " "; } tout << "\n";); } unit_resolution_justification::~unit_resolution_justification() { if (!in_region()) { dealloc_svect(m_literals); // I don't need to invoke destructor... dealloc(m_antecedent); } } void unit_resolution_justification::get_antecedents(conflict_resolution & cr) { if (m_antecedent) cr.mark_justification(m_antecedent); for (unsigned i = 0; i < m_num_literals; i++) cr.mark_literal(m_literals[i]); } proof * unit_resolution_justification::mk_proof(conflict_resolution & cr) { SASSERT(m_antecedent); ptr_buffer prs; proof * pr = cr.get_proof(m_antecedent); bool visited = pr != 0; prs.push_back(pr); for (unsigned i = 0; i < m_num_literals; i++) { proof * pr = cr.get_proof(m_literals[i]); if (pr == 0) visited = false; else prs.push_back(pr); } if (!visited) return 0; ast_manager & m = cr.get_manager(); TRACE("unit_resolution_justification_bug", tout << "in mk_proof\n"; for (unsigned i = 0; i < m_num_literals; i++) { tout << m_literals[i] << " "; } tout << "\n"; for (unsigned i = 0; i < prs.size(); i++) { tout << mk_ll_pp(m.get_fact(prs[i]), m); }); return m.mk_unit_resolution(prs.size(), prs.c_ptr()); } void eq_conflict_justification::get_antecedents(conflict_resolution & cr) { SASSERT(m_node1->get_root()->is_interpreted()); SASSERT(m_node2->get_root()->is_interpreted()); cr.mark_eq(m_node1, m_node1->get_root()); cr.mark_eq(m_node2, m_node2->get_root()); cr.mark_justified_eq(m_node1, m_node2, m_js); } proof * eq_conflict_justification::mk_proof(conflict_resolution & cr) { ast_manager & m = cr.get_manager(); bool visited = true; ptr_buffer prs; if (m_node1 != m_node1->get_root()) { proof * pr = cr.get_proof(m_node1, m_node1->get_root()); if (pr && m.fine_grain_proofs()) pr = m.mk_symmetry(pr); prs.push_back(pr); if (!pr) visited = false; } SASSERT(m_node1 != m_node2); proof * pr = cr.get_proof(m_node1, m_node2, m_js); prs.push_back(pr); if (!pr) visited = false; if (m_node2 != m_node2->get_root()) { proof * pr = cr.get_proof(m_node2, m_node2->get_root()); prs.push_back(pr); if (!pr) visited = false; } if (!visited) return 0; expr * lhs = m_node1->get_root()->get_owner(); expr * rhs = m_node2->get_root()->get_owner(); proof * pr1 = m.mk_transitivity(prs.size(), prs.c_ptr(), lhs, rhs); proof * pr2 = m.mk_rewrite(m.mk_eq(lhs, rhs), m.mk_false()); return m.mk_modus_ponens(pr1, pr2); } void eq_root_propagation_justification::get_antecedents(conflict_resolution & cr) { cr.mark_eq(m_node, m_node->get_root()); } proof * eq_root_propagation_justification::mk_proof(conflict_resolution & cr) { ast_manager & m = cr.get_manager(); expr * var = m_node->get_owner(); expr * val = m_node->get_root()->get_owner(); SASSERT(m.is_true(val) || m.is_false(val)); proof * pr1 = cr.get_proof(m_node, m_node->get_root()); if (pr1) { expr * lit; if (m.is_true(val)) lit = var; else lit = m.mk_not(var); proof * pr2 = m.mk_rewrite(m.get_fact(pr1), lit); return m.mk_modus_ponens(pr1, pr2); } return 0; } void eq_propagation_justification::get_antecedents(conflict_resolution & cr) { cr.mark_eq(m_node1, m_node2); } proof * eq_propagation_justification::mk_proof(conflict_resolution & cr) { return cr.get_proof(m_node1, m_node2); } void mp_iff_justification::get_antecedents(conflict_resolution & cr) { SASSERT(m_node1 != m_node2); cr.mark_eq(m_node1, m_node2); context & ctx = cr.get_context(); bool_var v = ctx.enode2bool_var(m_node1); lbool val = ctx.get_assignment(v); literal l(v, val == l_false); cr.mark_literal(l); } proof * mp_iff_justification::mk_proof(conflict_resolution & cr) { proof * pr1 = cr.get_proof(m_node1, m_node2); context & ctx = cr.get_context(); bool_var v = ctx.enode2bool_var(m_node1); lbool val = ctx.get_assignment(v); literal l(v, val == l_false); proof * pr2 = cr.get_proof(l); if (pr1 && pr2) { ast_manager & m = cr.get_manager(); proof * pr; SASSERT(m.has_fact(pr1)); SASSERT(m.has_fact(pr2)); app* fact1 = to_app(m.get_fact(pr1)); app* fact2 = to_app(m.get_fact(pr2)); SASSERT(m.is_iff(fact1)); if (fact1->get_arg(1) == fact2) { pr1 = m.mk_symmetry(pr1); fact1 = to_app(m.get_fact(pr1)); } SASSERT(m.is_iff(fact1)); if (l.sign()) { SASSERT(m.is_not(fact2)); expr* lhs = fact1->get_arg(0); expr* rhs = fact1->get_arg(1); if (lhs != fact2->get_arg(0)) { pr1 = m.mk_symmetry(pr1); fact1 = to_app(m.get_fact(pr1)); std::swap(lhs, rhs); } SASSERT(lhs == fact2->get_arg(0)); app* new_lhs = fact2; app* new_rhs = m.mk_not(rhs); pr1 = m.mk_congruence(new_lhs, new_rhs, 1, &pr1); } pr = m.mk_modus_ponens(pr2, pr1); TRACE("mp_iff_justification", tout << mk_pp(fact1, m) << "\n" << mk_pp(fact2, m) << "\n" << mk_pp(m.get_fact(pr), m) << "\n";); return pr; } return 0; } simple_justification::simple_justification(region & r, unsigned num_lits, literal const * lits): m_num_literals(num_lits) { m_literals = new (r) literal[num_lits]; memcpy(m_literals, lits, sizeof(literal) * num_lits); #ifdef Z3DEBUG for (unsigned i = 0; i < num_lits; i++) { SASSERT(lits[i] != null_literal); } #endif } void simple_justification::get_antecedents(conflict_resolution & cr) { for (unsigned i = 0; i < m_num_literals; i++) cr.mark_literal(m_literals[i]); } bool simple_justification::antecedent2proof(conflict_resolution & cr, ptr_buffer & result) { bool visited = true; for (unsigned i = 0; i < m_num_literals; i++) { proof * pr = cr.get_proof(m_literals[i]); if (pr == 0) visited = false; else result.push_back(pr); } return visited; } proof * theory_axiom_justification::mk_proof(conflict_resolution & cr) { context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref_vector lits(m); for (unsigned i = 0; i < m_num_literals; i++) { expr_ref l(m); ctx.literal2expr(m_literals[i], l); lits.push_back(l); } if (lits.size() == 1) return m.mk_th_lemma(m_th_id, lits.get(0), 0, 0, m_params.size(), m_params.c_ptr()); else return m.mk_th_lemma(m_th_id, m.mk_or(lits.size(), lits.c_ptr()), 0, 0, m_params.size(), m_params.c_ptr()); } proof * theory_propagation_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) return 0; context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref fact(m); ctx.literal2expr(m_consequent, fact); return m.mk_th_lemma(m_th_id, fact, prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } proof * theory_conflict_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) return 0; ast_manager & m = cr.get_manager(); return m.mk_th_lemma(m_th_id, m.mk_false(), prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } ext_simple_justification::ext_simple_justification(region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs): simple_justification(r, num_lits, lits), m_num_eqs(num_eqs) { m_eqs = new (r) enode_pair[num_eqs]; if (num_eqs != 0) memcpy(m_eqs, eqs, sizeof(enode_pair) * num_eqs); DEBUG_CODE({ for (unsigned i = 0; i < num_eqs; i++) { SASSERT(eqs[i].first->get_root() == eqs[i].second->get_root()); } }); } void ext_simple_justification::get_antecedents(conflict_resolution & cr) { simple_justification::get_antecedents(cr); for (unsigned i = 0; i < m_num_eqs; i++) { enode_pair const & p = m_eqs[i]; cr.mark_eq(p.first, p.second); } } bool ext_simple_justification::antecedent2proof(conflict_resolution & cr, ptr_buffer & result) { bool visited = simple_justification::antecedent2proof(cr, result); for (unsigned i = 0; i < m_num_eqs; i++) { enode_pair const & p = m_eqs[i]; proof * pr = cr.get_proof(p.first, p.second); if (pr == 0) visited = false; else result.push_back(pr); } return visited; } proof * ext_theory_propagation_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) return 0; context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref fact(m); ctx.literal2expr(m_consequent, fact); return m.mk_th_lemma(m_th_id, fact, prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } proof * ext_theory_conflict_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) return 0; ast_manager & m = cr.get_manager(); return m.mk_th_lemma(m_th_id, m.mk_false(), prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } proof * ext_theory_eq_propagation_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) return 0; ast_manager & m = cr.get_manager(); context & ctx = cr.get_context(); expr * fact = ctx.mk_eq_atom(m_lhs->get_owner(), m_rhs->get_owner()); return m.mk_th_lemma(m_th_id, fact, prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } theory_lemma_justification::theory_lemma_justification(family_id fid, context & ctx, unsigned num_lits, literal const * lits, unsigned num_params, parameter* params): justification(false), m_th_id(fid), m_params(num_params, params), m_num_literals(num_lits) { ast_manager & m = ctx.get_manager(); m_literals = alloc_svect(expr*, num_lits); for (unsigned i = 0; i < num_lits; i++) { bool sign = lits[i].sign(); expr * v = ctx.bool_var2expr(lits[i].var()); m.inc_ref(v); m_literals[i] = TAG(expr*, v, sign); } SASSERT(!in_region()); } theory_lemma_justification::~theory_lemma_justification() { SASSERT(!in_region()); dealloc_svect(m_literals); } void theory_lemma_justification::del_eh(ast_manager & m) { for (unsigned i = 0; i < m_num_literals; i++) { m.dec_ref(UNTAG(expr*, m_literals[i])); } m_params.reset(); } proof * theory_lemma_justification::mk_proof(conflict_resolution & cr) { ast_manager & m = cr.get_manager(); expr_ref_vector lits(m); for (unsigned i = 0; i < m_num_literals; i++) { bool sign = GET_TAG(m_literals[i]) != 0; expr * v = UNTAG(expr*, m_literals[i]); expr_ref l(m); if (sign) l = m.mk_not(v); else l = v; lits.push_back(l); } if (lits.size() == 1) return m.mk_th_lemma(m_th_id, lits.get(0), 0, 0, m_params.size(), m_params.c_ptr()); else return m.mk_th_lemma(m_th_id, m.mk_or(lits.size(), lits.c_ptr()), 0, 0, m_params.size(), m_params.c_ptr()); } }; z3-z3-4.4.1/src/smt/smt_justification.h000066400000000000000000000346701260446376700177400ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_justification.h Abstract: Proof-like objects for tracking dependencies in the SMT engine, and generating proofs. Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #ifndef SMT_JUSTIFICATION_H_ #define SMT_JUSTIFICATION_H_ #include"ast.h" #include"smt_types.h" #include"smt_literal.h" #include"smt_eq_justification.h" namespace smt { class conflict_resolution; typedef ptr_vector justification_vector; /** \brief Pseudo-proof objects. They are mainly used to track dependencies. When proof generation is enabled, they are also used to produce proofs. Justification objects are allocated using a stack based policy. Actually, there is one exception: justification of lemmas. Lemmas created at scope level n may remain alive even after scope level n is backtracked. Lemmas are deleted by a GC that runs from time to time. So, a justification attached to a lemma will may remain alive after scope level n is backtracked. So, I allow justification objects to be allocated in regions and in the regular heap. The method in_region() should return true if the object is allocated in a region. */ class justification { unsigned m_mark:1; unsigned m_in_region:1; // true if the object was allocated in a region. public: justification(bool in_region = true):m_mark(false), m_in_region(in_region) {} virtual ~justification() {} /** \brief This method should return true if the method del_eh needs to be invoked to free resources. */ virtual bool has_del_eh() const { return false; } /** \brief Free the resources allocated by this object. */ virtual void del_eh(ast_manager & m) { } /** \brief Mark the antecedents the justification object. The antecedents are marked using the mark methods of the conflict_resolution object. */ virtual void get_antecedents(conflict_resolution & cr){ } /** \brief Return the id of the theory that produced the proof object. */ virtual theory_id get_from_theory() const { return null_theory_id; } void set_mark() { SASSERT(!m_mark); m_mark = true; } void unset_mark() { SASSERT(m_mark); m_mark = false; } bool is_marked() const { return m_mark; } unsigned hash() const { return get_ptr_hash(this); } virtual proof * mk_proof(conflict_resolution & cr) = 0; bool in_region() const { return m_in_region; } virtual char const * get_name() const { return "unknown"; } virtual void display_debug_info(conflict_resolution & cr, std::ostream & out) { /* do nothing */ } }; class justification_proof_wrapper : public justification { proof * m_proof; public: justification_proof_wrapper(context & ctx, proof * pr, bool in_region = true); virtual bool has_del_eh() const { return true; } virtual void del_eh(ast_manager & m); virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "proof-wrapper"; } }; class unit_resolution_justification : public justification { justification * m_antecedent; unsigned m_num_literals; literal * m_literals; public: unit_resolution_justification(region & r, justification * js, unsigned num_lits, literal const * lits); unit_resolution_justification(justification * js, unsigned num_lits, literal const * lits); ~unit_resolution_justification(); virtual bool has_del_eh() const { return !in_region() && m_antecedent && m_antecedent->has_del_eh(); } virtual void del_eh(ast_manager & m) { if (!in_region() && m_antecedent) m_antecedent->del_eh(m); } virtual void get_antecedents(conflict_resolution & cr); virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "unit-resolution"; } }; class eq_conflict_justification : public justification { enode * m_node1; enode * m_node2; eq_justification m_js; public: eq_conflict_justification(enode * n1, enode * n2, eq_justification js): m_node1(n1), m_node2(n2), m_js(js) { } virtual void get_antecedents(conflict_resolution & cr); virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "eq-conflict"; } }; /** \brief Justification for m_node = root */ class eq_root_propagation_justification : public justification { enode * m_node; public: eq_root_propagation_justification(enode * n):m_node(n) { } virtual void get_antecedents(conflict_resolution & cr); virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "eq-root"; } }; /** \brief Justification for m_node1 = m_node2 */ class eq_propagation_justification : public justification { enode * m_node1; enode * m_node2; public: eq_propagation_justification(enode * n1, enode * n2):m_node1(n1), m_node2(n2) { } virtual void get_antecedents(conflict_resolution & cr); virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "eq-propagation"; } }; /** \brief Justification for p(x) <=> p(y), p(x) ===> p(y) */ class mp_iff_justification : public justification { enode * m_node1; // p(x) enode * m_node2; // p(y) public: mp_iff_justification(enode * n1, enode * n2):m_node1(n1), m_node2(n2) { } virtual void get_antecedents(conflict_resolution & cr); virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "mp-iff"; } }; /** \brief Abstract class for justifications that contains set of literals. */ class simple_justification : public justification { protected: unsigned m_num_literals; literal * m_literals; bool antecedent2proof(conflict_resolution & cr, ptr_buffer & result); public: simple_justification(region & r, unsigned num_lits, literal const * lits); virtual void get_antecedents(conflict_resolution & cr); virtual proof * mk_proof(conflict_resolution & cr) = 0; virtual char const * get_name() const { return "simple"; } }; class simple_theory_justification : public simple_justification { protected: family_id m_th_id; vector m_params; public: simple_theory_justification( family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_params, parameter* params): simple_justification(r, num_lits, lits), m_th_id(fid), m_params(num_params, params) {} virtual ~simple_theory_justification() {} virtual bool has_del_eh() const { return !m_params.empty(); } virtual void del_eh(ast_manager & m) { m_params.reset(); } virtual theory_id get_from_theory() const { return m_th_id; } }; class theory_axiom_justification : public simple_theory_justification { public: theory_axiom_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_params = 0, parameter* params = 0): simple_theory_justification(fid, r, num_lits, lits, num_params, params) {} virtual void get_antecedents(conflict_resolution & cr) {} virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "theory-axiom"; } }; class theory_propagation_justification : public simple_theory_justification { literal m_consequent; public: theory_propagation_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, literal consequent, unsigned num_params = 0, parameter* params = 0): simple_theory_justification(fid, r, num_lits, lits, num_params, params), m_consequent(consequent) {} virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "theory-propagation"; } }; class theory_conflict_justification : public simple_theory_justification { public: theory_conflict_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_params = 0, parameter* params = 0): simple_theory_justification(fid, r, num_lits, lits, num_params, params) {} virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "theory-conflict"; } }; /** \brief Abstract class for justifications that contains set of literals and equalities. */ class ext_simple_justification : public simple_justification { protected: unsigned m_num_eqs; enode_pair * m_eqs; bool antecedent2proof(conflict_resolution & cr, ptr_buffer & result); public: ext_simple_justification(region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs); virtual void get_antecedents(conflict_resolution & cr); virtual proof * mk_proof(conflict_resolution & cr) = 0; virtual char const * get_name() const { return "ext-simple"; } }; /** \brief Abstract class for justifications that contains set of literals and equalities. */ class ext_theory_simple_justification : public ext_simple_justification { protected: family_id m_th_id; vector m_params; public: ext_theory_simple_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, unsigned num_params = 0, parameter* params = 0): ext_simple_justification(r, num_lits, lits, num_eqs, eqs), m_th_id(fid), m_params(num_params, params) {} virtual ~ext_theory_simple_justification() {} virtual bool has_del_eh() const { return !m_params.empty(); } virtual void del_eh(ast_manager & m) { m_params.reset(); } virtual theory_id get_from_theory() const { return m_th_id; } }; class ext_theory_propagation_justification : public ext_theory_simple_justification { literal m_consequent; public: ext_theory_propagation_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, literal consequent, unsigned num_params = 0, parameter* params = 0): ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params), m_consequent(consequent) {} virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "ext-theory-propagation"; } }; class ext_theory_conflict_justification : public ext_theory_simple_justification { public: ext_theory_conflict_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, unsigned num_params = 0, parameter* params = 0): ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params) {} virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "ext-theory-conflict"; } }; class ext_theory_eq_propagation_justification : public ext_theory_simple_justification { enode * m_lhs; enode * m_rhs; public: ext_theory_eq_propagation_justification( family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, enode * lhs, enode * rhs, unsigned num_params = 0, parameter* params = 0): ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params), m_lhs(lhs), m_rhs(rhs) {} virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "ext-theory-eq-propagation"; } }; /** \brief A theory lemma is similar to a theory axiom, but it is attached to a CLS_AUX_LEMMA clause instead of CLS_AUX. So, it cannot be stored in the heap, and it is unsafe to store literals, since it may be deleted during backtracking. Instead, they store a set of pairs (sign, expr). This pair is represented as a tagged pointer. */ class theory_lemma_justification : public justification { family_id m_th_id; vector m_params; unsigned m_num_literals; expr ** m_literals; public: theory_lemma_justification(family_id fid, context & ctx, unsigned num_lits, literal const * lits, unsigned num_params = 0, parameter* params = 0); virtual ~theory_lemma_justification(); virtual bool has_del_eh() const { return true; } virtual void del_eh(ast_manager & m); virtual void get_antecedents(conflict_resolution & cr) {} virtual proof * mk_proof(conflict_resolution & cr); virtual char const * get_name() const { return "theory-lemma"; } }; }; #endif /* SMT_JUSTIFICATION_H_ */ z3-z3-4.4.1/src/smt/smt_kernel.cpp000066400000000000000000000215721260446376700166750ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_kernel.cpp Abstract: New frontend for smt::context. Author: Leonardo de Moura (leonardo) 2012-02-09. Revision History: --*/ #include"smt_kernel.h" #include"smt_context.h" #include"ast_smt2_pp.h" #include"smt_params_helper.hpp" namespace smt { struct kernel::imp { smt::context m_kernel; params_ref m_params; imp(ast_manager & m, smt_params & fp, params_ref const & p): m_kernel(m, fp, p), m_params(p) { } smt_params & fparams() { return m_kernel.get_fparams(); } params_ref const & params() { return m_params; } ast_manager & m() const { return m_kernel.get_manager(); } bool set_logic(symbol logic) { return m_kernel.set_logic(logic); } void set_progress_callback(progress_callback * callback) { return m_kernel.set_progress_callback(callback); } void assert_expr(expr * e) { TRACE("smt_kernel", tout << "assert:\n" << mk_ismt2_pp(e, m()) << "\n";); m_kernel.assert_expr(e); } void assert_expr(expr * e, proof * pr) { m_kernel.assert_expr(e, pr); } unsigned size() const { return m_kernel.get_num_asserted_formulas(); } expr * const * get_formulas() const { return m_kernel.get_asserted_formulas(); } void push() { TRACE("smt_kernel", tout << "push()\n";); m_kernel.push(); } void pop(unsigned num_scopes) { TRACE("smt_kernel", tout << "pop()\n";); m_kernel.pop(num_scopes); } unsigned get_scope_level() const { return m_kernel.get_scope_level(); } lbool setup_and_check() { return m_kernel.setup_and_check(); } bool inconsistent() { return m_kernel.inconsistent(); } lbool check(unsigned num_assumptions, expr * const * assumptions) { return m_kernel.check(num_assumptions, assumptions); } void get_model(model_ref & m) const { m_kernel.get_model(m); } proof * get_proof() { return m_kernel.get_proof(); } unsigned get_unsat_core_size() const { return m_kernel.get_unsat_core_size(); } expr * get_unsat_core_expr(unsigned idx) const { return m_kernel.get_unsat_core_expr(idx); } failure last_failure() const { return m_kernel.get_last_search_failure(); } std::string last_failure_as_string() const { return m_kernel.last_failure_as_string(); } void get_assignments(expr_ref_vector & result) { m_kernel.get_assignments(result); } void get_relevant_labels(expr * cnstr, buffer & result) { m_kernel.get_relevant_labels(cnstr, result); } void get_relevant_labeled_literals(bool at_lbls, expr_ref_vector & result) { m_kernel.get_relevant_labeled_literals(at_lbls, result); } void get_relevant_literals(expr_ref_vector & result) { m_kernel.get_relevant_literals(result); } void get_guessed_literals(expr_ref_vector & result) { m_kernel.get_guessed_literals(result); } void display(std::ostream & out) const { // m_kernel.display(out); <<< for external users it is just junk // TODO: it will be replaced with assertion_stack.display unsigned num = m_kernel.get_num_asserted_formulas(); expr * const * fms = m_kernel.get_asserted_formulas(); out << "(kernel"; for (unsigned i = 0; i < num; i++) { out << "\n " << mk_ismt2_pp(fms[i], m(), 2); } out << ")"; } void collect_statistics(::statistics & st) const { m_kernel.collect_statistics(st); } void reset_statistics() { } void display_statistics(std::ostream & out) const { m_kernel.display_statistics(out); } void display_istatistics(std::ostream & out) const { m_kernel.display_istatistics(out); } void set_cancel(bool f) { m_kernel.set_cancel_flag(f); } bool canceled() { return m_kernel.get_cancel_flag(); } void updt_params(params_ref const & p) { // We don't need params2smt_params anymore. smt_params has support for reading params_ref. // The update is performed at smt_kernel "users". // params2smt_params(p, fparams()); } }; kernel::kernel(ast_manager & m, smt_params & fp, params_ref const & p) { m_imp = alloc(imp, m, fp, p); } kernel::~kernel() { dealloc(m_imp); } ast_manager & kernel::m() const { return m_imp->m(); } bool kernel::set_logic(symbol logic) { return m_imp->set_logic(logic); } void kernel::set_progress_callback(progress_callback * callback) { m_imp->set_progress_callback(callback); } void kernel::assert_expr(expr * e) { m_imp->assert_expr(e); } void kernel::assert_expr(expr * e, proof * pr) { m_imp->assert_expr(e, pr); } unsigned kernel::size() const { return m_imp->size(); } expr * const * kernel::get_formulas() const { return m_imp->get_formulas(); } void kernel::push() { m_imp->push(); } void kernel::pop(unsigned num_scopes) { m_imp->pop(num_scopes); } unsigned kernel::get_scope_level() const { return m_imp->get_scope_level(); } void kernel::reset() { ast_manager & _m = m(); smt_params & fps = m_imp->fparams(); params_ref ps = m_imp->params(); #pragma omp critical (smt_kernel) { m_imp->~imp(); m_imp = new (m_imp) imp(_m, fps, ps); } } bool kernel::inconsistent() { return m_imp->inconsistent(); } lbool kernel::setup_and_check() { return m_imp->setup_and_check(); } lbool kernel::check(unsigned num_assumptions, expr * const * assumptions) { lbool r = m_imp->check(num_assumptions, assumptions); TRACE("smt_kernel", tout << "check result: " << r << "\n";); return r; } void kernel::get_model(model_ref & m) const { m_imp->get_model(m); } proof * kernel::get_proof() { return m_imp->get_proof(); } unsigned kernel::get_unsat_core_size() const { return m_imp->get_unsat_core_size(); } expr * kernel::get_unsat_core_expr(unsigned idx) const { return m_imp->get_unsat_core_expr(idx); } failure kernel::last_failure() const { return m_imp->last_failure(); } std::string kernel::last_failure_as_string() const { return m_imp->last_failure_as_string(); } void kernel::get_assignments(expr_ref_vector & result) { m_imp->get_assignments(result); } void kernel::get_relevant_labels(expr * cnstr, buffer & result) { m_imp->get_relevant_labels(cnstr, result); } void kernel::get_relevant_labeled_literals(bool at_lbls, expr_ref_vector & result) { m_imp->get_relevant_labeled_literals(at_lbls, result); } void kernel::get_relevant_literals(expr_ref_vector & result) { m_imp->get_relevant_literals(result); } void kernel::get_guessed_literals(expr_ref_vector & result) { m_imp->get_guessed_literals(result); } void kernel::display(std::ostream & out) const { m_imp->display(out); } void kernel::collect_statistics(::statistics & st) const { m_imp->collect_statistics(st); } void kernel::reset_statistics() { m_imp->reset_statistics(); } void kernel::display_statistics(std::ostream & out) const { m_imp->display_statistics(out); } void kernel::display_istatistics(std::ostream & out) const { m_imp->display_istatistics(out); } void kernel::set_cancel(bool f) { #pragma omp critical (smt_kernel) { if (m_imp) m_imp->set_cancel(f); } } bool kernel::canceled() const { return m_imp->canceled(); } void kernel::updt_params(params_ref const & p) { return m_imp->updt_params(p); } void kernel::collect_param_descrs(param_descrs & d) { smt_params_helper::collect_param_descrs(d); } context & kernel::get_context() { return m_imp->m_kernel; } }; z3-z3-4.4.1/src/smt/smt_kernel.h000066400000000000000000000151521260446376700163370ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_kernel.h Abstract: New frontend for smt::context. The "kernel" tries to hide details of the smt::context object. From now on, clients (code outside of the smt module) should be use smt::kernel instead of smt::context. Author: Leonardo de Moura (leonardo) 2012-02-09. Revision History: I initially called it smt::solver. This was confusing to others since we have the abstract solver API, and smt::kernel is not a subclass of ::solver. To increase the confusion I had a class default_solver that implemented the solver API on top of smt::context. To avoid this problem I renamed them in the following way: smt::solver ---> smt::kernel default_solver ---> smt::solver --*/ #ifndef SMT_KERNEL_H_ #define SMT_KERNEL_H_ #include"ast.h" #include"params.h" #include"model.h" #include"lbool.h" #include"statistics.h" #include"smt_failure.h" struct smt_params; class progress_callback; namespace smt { class enode; class context; class kernel { struct imp; imp * m_imp; public: kernel(ast_manager & m, smt_params & fp, params_ref const & p = params_ref()); ~kernel(); ast_manager & m() const; /** \brief Set logic. It must be invoked before any assertions. Return true if succeeded. */ bool set_logic(symbol logic); /** brief Set progress meter. Kernel will invoke the callback from time to time. */ void set_progress_callback(progress_callback * callback); /** \brief Assert the given assetion into the logical context. This method uses the "asserted" proof as a justification for e. */ void assert_expr(expr * e); /** \brief Assert the given assertion with the given proof as a justification. */ void assert_expr(expr * e, proof * pr); /** \brief Return the number of asserted formulas in the kernel. */ unsigned size() const; /** \brief Return the array of asserted formulas. */ expr * const * get_formulas() const; /** \brief Create a backtracking point (aka scope level). */ void push(); /** \brief Backtrack the given number of scope levels. */ void pop(unsigned num_scopes); /** \brief Return the number of backtracking points. */ unsigned get_scope_level() const; /** \brief Reset the kernel. All assertions are erased. */ void reset(); /** \brief Return true if the set of asserted formulas is known to be inconsistent. */ bool inconsistent(); /** \brief Setup the logical context and invoke check. */ lbool setup_and_check(); /** \brief Satisfiability check. */ lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0); /** \brief Return the model associated with the last check command. */ void get_model(model_ref & m) const; /** \brief Return the proof of unsatisfiability associated with the last check command. */ proof * get_proof(); /** \brief Return the size of the unsat core associated with the last check command. */ unsigned get_unsat_core_size() const; /** \brief Return the i-th expression in the unsat core associated with the last check command. \pre i < get_unsat_core_size() */ expr * get_unsat_core_expr(unsigned i) const; /** \brief Return the reason for failure for the last check command. Failure means, it returned l_undef */ failure last_failure() const; /** \brief Return a string describing the failure. */ std::string last_failure_as_string() const; /** \brief Return the set of formulas assigned by the kernel. */ void get_assignments(expr_ref_vector & result); /** \brief Return the set of relevant labels in the last check command. */ void get_relevant_labels(expr * cnstr, buffer & result); /** \brief Return the relevant labeled_literals in the last check command. */ void get_relevant_labeled_literals(bool at_lbls, expr_ref_vector & result); /** \brief Return the relevant literals in the last check command. */ void get_relevant_literals(expr_ref_vector & result); /** \brief Return the set of guessed literals (decisions) performed in the last check command. */ void get_guessed_literals(expr_ref_vector & result); /** \brief (For debubbing purposes) Prints the state of the kernel */ void display(std::ostream & out) const; /** \brief Collect runtime statistics. */ void collect_statistics(::statistics & st) const; /** \brief Reset kernel statistics. */ void reset_statistics(); /** \brief Display statistics. */ void display_statistics(std::ostream & out) const; /** \brief Display statistics in low level format. */ void display_istatistics(std::ostream & out) const; /** \brief Interrupt the kernel. */ void set_cancel(bool f = true); void cancel() { set_cancel(true); } /** \brief Reset interruption. */ void reset_cancel() { set_cancel(false); } /** \brief Return true if the kernel was interrupted. */ bool canceled() const; /** \brief Update configuration parameters. */ void updt_params(params_ref const & p); /** \brief Collect a description of the configuration parameters. */ static void collect_param_descrs(param_descrs & d); /** \brief Return a reference to smt::context. This is a temporary hack to support user theories. TODO: remove this hack. We need to revamp user theories too. This method breaks the abstraction barrier. \warning We should not use this method */ context & get_context(); }; }; #endif z3-z3-4.4.1/src/smt/smt_literal.cpp000066400000000000000000000052771260446376700170550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_literal.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #include"smt_literal.h" #include"ast_pp.h" #include"ast_ll_pp.h" namespace smt { void literal::display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const { if (*this == true_literal) out << "true"; else if (*this == false_literal) out << "false"; else if (sign()) out << "(not " << mk_pp(bool_var2expr_map[var()], m) << ")"; else out << mk_pp(bool_var2expr_map[var()], m); } void literal::display_compact(std::ostream & out, expr * const * bool_var2expr_map) const { if (*this == true_literal) out << "true"; else if (*this == false_literal) out << "false"; else if (sign()) out << "(not #" << bool_var2expr_map[var()]->get_id() << ")"; else out << "#" << bool_var2expr_map[var()]->get_id(); } std::ostream & operator<<(std::ostream & out, literal l) { if (l == true_literal) out << "true"; else if (l == false_literal) out << "false"; else if (l.sign()) out << "(not p" << l.var() << ")"; else out << "p" << l.var(); return out; } std::ostream & operator<<(std::ostream & out, const literal_vector & v) { display(out, v.begin(), v.end()); return out; } void display_compact(std::ostream & out, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map) { for (unsigned i = 0; i < num_lits; i++) { if (i > 0) out << " "; lits[i].display_compact(out, bool_var2expr_map); } } void display_verbose(std::ostream & out, ast_manager& m, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map) { for (unsigned i = 0; i < num_lits; i++) { if (i > 0) out << " "; lits[i].display(out, m, bool_var2expr_map); } } /** \brief Return true if lits1 subsumes lits2. That is every literal in lits1 is in lits2 */ bool backward_subsumption(unsigned num_lits1, literal const * lits1, unsigned num_lits2, literal const * lits2) { unsigned i = 0; for (; i < num_lits1; i++) { literal l1 = lits1[i]; unsigned j = 0; for (; j < num_lits2; j++) if (l1 == lits2[j]) break; if (j == num_lits2) break; } return i == num_lits1; } }; z3-z3-4.4.1/src/smt/smt_literal.h000066400000000000000000000060131260446376700165070ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_literal.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #ifndef SMT_LITERAL_H_ #define SMT_LITERAL_H_ #include"ast.h" #include"smt_types.h" #include"approx_set.h" namespace smt { /** \brief The literal b is represented by the value 2*b, and the literal (not b) by the value 2*b + 1 */ class literal { int m_val; public: literal():m_val(-2) { SASSERT(var() == null_bool_var && !sign()); } explicit literal(bool_var v, bool sign = false): m_val((v << 1) + static_cast(sign)) { } bool_var var() const { return m_val >> 1; } bool sign() const { return m_val & 1; } int index() const { return m_val; } void neg() { m_val = m_val ^ 1; } friend literal operator~(literal l); friend literal to_literal(int x); void display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const; void display_compact(std::ostream & out, expr * const * bool_var2expr_map) const; unsigned hash() const { return m_val; } }; inline bool operator==(literal l1, literal l2) { return l1.index() == l2.index(); } inline bool operator!=(literal l1, literal l2) { return l1.index() != l2.index(); } inline bool operator<(literal l1, literal l2) { return l1.index() < l2.index(); } inline literal operator~(literal l) { literal r; r.m_val = l.m_val ^ 1; return r; } inline literal to_literal(int x) { literal l; l.m_val = x; return l; } const literal null_literal; const literal true_literal(true_bool_var, false); const literal false_literal(true_bool_var, true); typedef svector literal_vector; typedef sbuffer literal_buffer; std::ostream & operator<<(std::ostream & out, literal l); std::ostream & operator<<(std::ostream & out, const literal_vector & v); void display_compact(std::ostream & out, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map); void display_verbose(std::ostream & out, ast_manager& m, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map); template void neg_literals(unsigned num_lits, literal const * lits, T & result) { for (unsigned i = 0; i < num_lits; ++i) result.push_back(~lits[i]); } struct literal2unsigned { unsigned operator()(literal l) const { return l.index(); } }; typedef approx_set_tpl literal_approx_set; bool backward_subsumption(unsigned num_lits1, literal const * lits1, unsigned num_lits2, literal const * lits2); }; #endif /* SMT_LITERAL_H_ */ z3-z3-4.4.1/src/smt/smt_model_checker.cpp000066400000000000000000000377331260446376700202070ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_checker.cpp Abstract: Model checker Author: Leonardo de Moura (leonardo) 2010-12-03. Revision History: --*/ #include"smt_model_checker.h" #include"smt_context.h" #include"smt_model_finder.h" #include"pull_quant.h" #include"for_each_expr.h" #include"var_subst.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"model_pp.h" #include"ast_smt2_pp.h" namespace smt { model_checker::model_checker(ast_manager & m, qi_params const & p, model_finder & mf): m_manager(m), m_params(p), m_qm(0), m_context(0), m_root2value(0), m_model_finder(mf), m_max_cexs(1), m_iteration_idx(0), m_curr_model(0), m_cancel(false), m_new_instances_bindings(m) { } model_checker::~model_checker() { m_aux_context = 0; // delete aux context before fparams m_fparams = 0; } quantifier * model_checker::get_flat_quantifier(quantifier * q) { return m_model_finder.get_flat_quantifier(q); } void model_checker::set_qm(quantifier_manager & qm) { SASSERT(m_qm == 0); SASSERT(m_context == 0); m_qm = &qm; m_context = &(m_qm->get_context()); } /** \brief Return a term in the context that evaluates to val. */ expr * model_checker::get_term_from_ctx(expr * val) { if (m_value2expr.empty()) { // populate m_value2expr obj_map::iterator it = m_root2value->begin(); obj_map::iterator end = m_root2value->end(); for (; it != end; ++it) { enode * n = (*it).m_key; expr * val = (*it).m_value; n = n->get_eq_enode_with_min_gen(); m_value2expr.insert(val, n->get_owner()); } } expr * t = 0; m_value2expr.find(val, t); return t; } /** \brief Assert in m_aux_context, the constraint sk = e_1 OR ... OR sk = e_n where {e_1, ..., e_n} is the universe. */ void model_checker::restrict_to_universe(expr * sk, obj_hashtable const & universe) { SASSERT(!universe.empty()); ptr_buffer eqs; obj_hashtable::iterator it = universe.begin(); obj_hashtable::iterator end = universe.end(); for (; it != end; ++it) { expr * e = *it; eqs.push_back(m_manager.mk_eq(sk, e)); } m_aux_context->assert_expr(m_manager.mk_or(eqs.size(), eqs.c_ptr())); } #define PP_DEPTH 8 /** \brief Assert the negation of q after applying the interpretation in m_curr_model to the uninterpreted symbols in q. The variables are replaced by skolem constants. These constants are stored in sks. */ void model_checker::assert_neg_q_m(quantifier * q, expr_ref_vector & sks) { expr_ref tmp(m_manager); m_curr_model->eval(q->get_expr(), tmp, true); TRACE("model_checker", tout << "q after applying interpretation:\n" << mk_ismt2_pp(tmp, m_manager) << "\n";); ptr_buffer subst_args; unsigned num_decls = q->get_num_decls(); subst_args.resize(num_decls, 0); sks.resize(num_decls, 0); for (unsigned i = 0; i < num_decls; i++) { sort * s = q->get_decl_sort(num_decls - i - 1); expr * sk = m_manager.mk_fresh_const(0, s); sks[num_decls - i - 1] = sk; subst_args[num_decls - i - 1] = sk; if (m_curr_model->is_finite(s)) { restrict_to_universe(sk, m_curr_model->get_known_universe(s)); } } expr_ref sk_body(m_manager); var_subst s(m_manager); s(tmp, subst_args.size(), subst_args.c_ptr(), sk_body); expr_ref r(m_manager); r = m_manager.mk_not(sk_body); TRACE("model_checker", tout << "mk_neg_q_m:\n" << mk_ismt2_pp(r, m_manager) << "\n";); m_aux_context->assert_expr(r); } bool model_checker::add_instance(quantifier * q, model * cex, expr_ref_vector & sks, bool use_inv) { if (cex == 0) return false; // no model available. unsigned num_decls = q->get_num_decls(); // Remark: sks were created for the flat version of q. SASSERT(sks.size() >= num_decls); expr_ref_buffer bindings(m_manager); bindings.resize(num_decls); unsigned max_generation = 0; for (unsigned i = 0; i < num_decls; i++) { expr * sk = sks.get(num_decls - i - 1); func_decl * sk_d = to_app(sk)->get_decl(); expr_ref sk_value(m_manager); sk_value = cex->get_const_interp(sk_d); if (sk_value == 0) { sk_value = cex->get_some_value(sk_d->get_range()); if (sk_value == 0) return false; // get_some_value failed... giving up } if (use_inv) { unsigned sk_term_gen; expr * sk_term = m_model_finder.get_inv(q, i, sk_value, sk_term_gen); if (sk_term != 0) { SASSERT(!m_manager.is_model_value(sk_term)); if (sk_term_gen > max_generation) max_generation = sk_term_gen; sk_value = sk_term; } else { return false; } } else { expr * sk_term = get_term_from_ctx(sk_value); if (sk_term != 0) { sk_value = sk_term; } } if (contains_model_value(sk_value)) return false; bindings.set(num_decls - i - 1, sk_value); } TRACE("model_checker", tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance:\n"; for (unsigned i = 0; i < num_decls; i++) { tout << mk_ismt2_pp(bindings[i], m_manager) << "\n"; }); for (unsigned i = 0; i < num_decls; i++) m_new_instances_bindings.push_back(bindings[i]); void * mem = m_new_instances_region.allocate(instance::get_obj_size(q->get_num_decls())); instance * new_inst = new (mem) instance(q, bindings.c_ptr(), max_generation); m_new_instances.push_back(new_inst); return true; } void model_checker::operator()(expr *n) { if (m_manager.is_model_value(n)) { throw is_model_value(); } } bool model_checker::contains_model_value(expr* n) { if (m_manager.is_model_value(n)) { return true; } if (is_app(n) && to_app(n)->get_num_args() == 0) { return false; } m_visited.reset(); try { for_each_expr(*this, m_visited, n); } catch (is_model_value) { return true; } return false; } bool model_checker::add_blocking_clause(model * cex, expr_ref_vector & sks) { SASSERT(cex != 0); unsigned num_sks = sks.size(); expr_ref_buffer diseqs(m_manager); for (unsigned i = 0; i < num_sks; i++) { expr * sk = sks.get(i); func_decl * sk_d = to_app(sk)->get_decl(); expr_ref sk_value(m_manager); sk_value = cex->get_const_interp(sk_d); if (sk_value == 0) { sk_value = cex->get_some_value(sk_d->get_range()); if (sk_value == 0) return false; // get_some_value failed... aborting add_blocking_clause } diseqs.push_back(m_manager.mk_not(m_manager.mk_eq(sk, sk_value))); } expr_ref blocking_clause(m_manager); blocking_clause = m_manager.mk_or(diseqs.size(), diseqs.c_ptr()); TRACE("model_checker", tout << "blocking clause:\n" << mk_ismt2_pp(blocking_clause, m_manager) << "\n";); m_aux_context->assert_expr(blocking_clause); return true; } /** \brief Return true if q is satisfied by m_curr_model. */ bool model_checker::check(quantifier * q) { SASSERT(!m_aux_context->relevancy()); m_aux_context->push(); quantifier * flat_q = get_flat_quantifier(q); TRACE("model_checker", tout << "model checking:\n" << mk_ismt2_pp(q->get_expr(), m_manager) << "\n" << mk_ismt2_pp(flat_q->get_expr(), m_manager) << "\n";); expr_ref_vector sks(m_manager); assert_neg_q_m(flat_q, sks); TRACE("model_checker", tout << "skolems:\n"; for (unsigned i = 0; i < sks.size(); i++) { expr * sk = sks.get(i); tout << mk_ismt2_pp(sk, m_manager) << " " << mk_pp(m_manager.get_sort(sk), m_manager) << "\n"; }); lbool r = m_aux_context->check(); TRACE("model_checker", tout << "[complete] model-checker result: " << to_sat_str(r) << "\n";); if (r == l_false) { m_aux_context->pop(1); return true; // quantifier is satisfied by m_curr_model } model_ref complete_cex; m_aux_context->get_model(complete_cex); // try to find new instances using instantiation sets. m_model_finder.restrict_sks_to_inst_set(m_aux_context.get(), q, sks); unsigned num_new_instances = 0; while (true) { lbool r = m_aux_context->check(); TRACE("model_checker", tout << "[restricted] model-checker (" << (num_new_instances+1) << ") result: " << to_sat_str(r) << "\n";); if (r == l_false) break; model_ref cex; m_aux_context->get_model(cex); if (add_instance(q, cex.get(), sks, true)) { num_new_instances++; if (num_new_instances < m_max_cexs) { if (!add_blocking_clause(cex.get(), sks)) break; // add_blocking_clause failed... stop the search for new counter-examples... } } else { break; } if (num_new_instances >= m_max_cexs) break; } if (num_new_instances == 0) { // failed to create instances when restricting to inst sets... then use result of the complete model check TRACE("model_checker", tout << "using complete_cex result:\n"; model_pp(tout, *complete_cex);); add_instance(q, complete_cex.get(), sks, false); } m_aux_context->pop(1); return false; } void model_checker::init_aux_context() { if (!m_fparams) { m_fparams = alloc(smt_params, m_context->get_fparams()); m_fparams->m_relevancy_lvl = 0; // no relevancy since the model checking problems are quantifier free m_fparams->m_case_split_strategy = CS_ACTIVITY; // avoid warning messages about smt.case_split >= 3. } if (!m_aux_context) { symbol logic; m_aux_context = m_context->mk_fresh(&logic, m_fparams.get()); } } struct scoped_set_relevancy { }; bool model_checker::check(proto_model * md, obj_map const & root2value) { SASSERT(md != 0); m_root2value = &root2value; ptr_vector::const_iterator it = m_qm->begin_quantifiers(); ptr_vector::const_iterator end = m_qm->end_quantifiers(); if (it == end) return true; if (m_iteration_idx >= m_params.m_mbqi_max_iterations) return false; m_curr_model = md; m_value2expr.reset(); md->compress(); TRACE("model_checker", tout << "MODEL_CHECKER INVOKED\n"; tout << "model:\n"; model_pp(tout, *m_curr_model);); if (m_params.m_mbqi_trace) { verbose_stream() << "(smt.mbqi \"started\")\n"; } init_aux_context(); bool found_relevant = false; unsigned num_failures = 0; for (; it != end; ++it) { quantifier * q = *it; if(!m_qm->mbqi_enabled(q)) continue; if (m_context->is_relevant(q) && m_context->get_assignment(q) == l_true) { if (m_params.m_mbqi_trace && q->get_qid() != symbol::null) { verbose_stream() << "(smt.mbqi :checking " << q->get_qid() << ")\n"; } found_relevant = true; if (!check(q)) { if (m_params.m_mbqi_trace || get_verbosity_level() >= 5) { verbose_stream() << "(smt.mbqi :failed " << q->get_qid() << ")\n"; } num_failures++; } } } if (found_relevant) m_iteration_idx++; TRACE("model_checker", tout << "model after check:\n"; model_pp(tout, *md);); TRACE("model_checker", tout << "model checker result: " << (num_failures == 0) << "\n";); m_max_cexs += m_params.m_mbqi_max_cexs; if (num_failures == 0) m_curr_model->cleanup(); if (m_params.m_mbqi_trace) { if (num_failures == 0) verbose_stream() << "(smt.mbqi :succeeded true)\n"; else verbose_stream() << "(smt.mbqi :num-failures " << num_failures << ")\n"; } return num_failures == 0; } void model_checker::init_search_eh() { m_max_cexs = m_params.m_mbqi_max_cexs; m_iteration_idx = 0; } void model_checker::restart_eh() { IF_VERBOSE(100, verbose_stream() << "(smt.mbqi \"instantiating new instances...\")\n";); assert_new_instances(); reset_new_instances(); } bool model_checker::has_new_instances() { return !m_new_instances.empty(); } void model_checker::reset_new_instances() { m_new_instances_region.reset(); m_new_instances.reset(); } void model_checker::reset() { reset_new_instances(); } void model_checker::assert_new_instances() { TRACE("model_checker_bug_detail", tout << "assert_new_instances, inconsistent: " << m_context->inconsistent() << "\n";); ptr_buffer bindings; ptr_vector dummy; ptr_vector::iterator it = m_new_instances.begin(); ptr_vector::iterator end = m_new_instances.end(); for (; it != end; ++it) { instance * inst = *it; quantifier * q = inst->m_q; if (m_context->b_internalized(q)) { bindings.reset(); unsigned num_decls = q->get_num_decls(); unsigned gen = inst->m_generation; for (unsigned i = 0; i < num_decls; i++) { expr * b = inst->m_bindings[i]; if (!m_context->e_internalized(b)) { TRACE("model_checker_bug_detail", tout << "internalizing b:\n" << mk_pp(b, m_manager) << "\n";); m_context->internalize(b, false, gen); } bindings.push_back(m_context->get_enode(b)); } TRACE("model_checker_bug_detail", tout << "instantiating... q:\n" << mk_pp(q, m_manager) << "\n"; tout << "inconsistent: " << m_context->inconsistent() << "\n"; tout << "bindings:\n"; for (unsigned i = 0; i < num_decls; i++) { expr * b = inst->m_bindings[i]; tout << mk_pp(b, m_manager) << "\n"; }); TRACE("model_checker_instance", expr_ref inst_expr(m_manager); instantiate(m_manager, q, inst->m_bindings, inst_expr); tout << "(assert " << mk_ismt2_pp(inst_expr, m_manager) << ")\n";); m_context->add_instance(q, 0, num_decls, bindings.c_ptr(), gen, gen, gen, dummy); TRACE("model_checker_bug_detail", tout << "after instantiating, inconsistent: " << m_context->inconsistent() << "\n";); } } } }; z3-z3-4.4.1/src/smt/smt_model_checker.h000066400000000000000000000071421260446376700176430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_checker.h Abstract: Model checker AND Model-based quantifier instantiation. Author: Leonardo de Moura (leonardo) 2010-12-03. Revision History: --*/ #ifndef SMT_MODEL_CHECKER_H_ #define SMT_MODEL_CHECKER_H_ #include"ast.h" #include"obj_hashtable.h" #include"qi_params.h" #include"smt_params.h" #include"region.h" class proto_model; class model; namespace smt { class context; class enode; class model_finder; class quantifier_manager; class model_checker { ast_manager & m_manager; qi_params const & m_params; // copy of smt_params for auxiliary context. // the idea is to use a different configuration for the aux context (e.g., disable relevancy) scoped_ptr m_fparams; quantifier_manager * m_qm; context * m_context; // owner of the model checker obj_map const * m_root2value; // temp field to store mapping received in the check method. model_finder & m_model_finder; scoped_ptr m_aux_context; // Auxiliary context used for model checking quantifiers. unsigned m_max_cexs; unsigned m_iteration_idx; proto_model * m_curr_model; obj_map m_value2expr; bool m_cancel; friend class instantiation_set; void init_aux_context(); expr * get_term_from_ctx(expr * val); void restrict_to_universe(expr * sk, obj_hashtable const & universe); void assert_neg_q_m(quantifier * q, expr_ref_vector & sks); bool add_blocking_clause(model * cex, expr_ref_vector & sks); bool check(quantifier * q); struct instance { quantifier * m_q; unsigned m_generation; expr * m_bindings[0]; static unsigned get_obj_size(unsigned num_bindings) { return sizeof(instance) + num_bindings * sizeof(expr*); } instance(quantifier * q, expr * const * bindings, unsigned gen):m_q(q), m_generation(gen) { memcpy(m_bindings, bindings, q->get_num_decls() * sizeof(expr*)); } }; region m_new_instances_region; expr_ref_vector m_new_instances_bindings; ptr_vector m_new_instances; bool add_instance(quantifier * q, model * cex, expr_ref_vector & sks, bool use_inv); void reset_new_instances(); void assert_new_instances(); quantifier * get_flat_quantifier(quantifier * q); struct is_model_value {}; expr_mark m_visited; bool contains_model_value(expr* e); public: model_checker(ast_manager & m, qi_params const & p, model_finder & mf); ~model_checker(); void set_qm(quantifier_manager & qm); context * get_context() const { return m_context; } bool check(proto_model * md, obj_map const & root2value); bool has_new_instances(); void init_search_eh(); void restart_eh(); void reset(); void set_cancel(bool f) { m_cancel = f; } void operator()(expr* e); }; }; #endif // _SMT_MODEL_CHECKER_H_ z3-z3-4.4.1/src/smt/smt_model_finder.cpp000066400000000000000000004516721260446376700200540ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_finder.cpp Abstract: Model finding goodies for universally quantified formulas. Author: Leonardo de Moura (leonardo) 2010-12-17. Revision History: --*/ #include"smt_model_finder.h" #include"smt_context.h" #include"ast_util.h" #include"macro_util.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"array_decl_plugin.h" #include"arith_simplifier_plugin.h" #include"bv_simplifier_plugin.h" #include"pull_quant.h" #include"var_subst.h" #include"backtrackable_set.h" #include"for_each_expr.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"well_sorted.h" #include"model_pp.h" #include"ast_smt2_pp.h" #include"cooperate.h" #include"tactic_exception.h" namespace smt { namespace mf { // ----------------------------------- // // Auxiliary stuff // // ----------------------------------- // Append the new elements of v2 into v1. v2 should not be used after this operation, since it may suffer destructive updates. template void dappend(ptr_vector & v1, ptr_vector & v2) { if (v2.empty()) return; if (v1.empty()) { v1.swap(v2); return; } typename ptr_vector::iterator it = v2.begin(); typename ptr_vector::iterator end = v2.end(); for (; it != end; ++it) { if (!v1.contains(*it)) v1.push_back(*it); } v2.finalize(); } class evaluator { public: virtual expr * eval(expr * n, bool model_completion) = 0; }; // ----------------------------------- // // Instantiation sets // // ----------------------------------- /** \brief Instantiation sets are the S_{k,j} sets in the Complete quantifier instantiation paper. */ class instantiation_set { ast_manager & m_manager; obj_map m_elems; // and the associated generation obj_map m_inv; expr_mark m_visited; public: instantiation_set(ast_manager & m):m_manager(m) {} ~instantiation_set() { obj_map::iterator it = m_elems.begin(); obj_map::iterator end = m_elems.end(); for (; it != end; ++it) { m_manager.dec_ref((*it).m_key); } m_elems.reset(); } obj_map const & get_elems() const { return m_elems; } void insert(expr * n, unsigned generation) { if (m_elems.contains(n) || contains_model_value(n)) return; TRACE("model_finder", tout << mk_pp(n, m_manager) << "\n";); m_manager.inc_ref(n); m_elems.insert(n, generation); SASSERT(!m_manager.is_model_value(n)); } void remove(expr * n) { // We can only remove n if it is in m_elems, AND m_inv was not initialized yet. SASSERT(m_elems.contains(n)); SASSERT(m_inv.empty()); m_elems.erase(n); m_manager.dec_ref(n); } void display(std::ostream & out) const { obj_map::iterator it = m_elems.begin(); obj_map::iterator end = m_elems.end(); for (; it != end; ++it) { out << mk_bounded_pp((*it).m_key, m_manager) << " [" << (*it).m_value << "]\n"; } out << "inverse:\n"; obj_map::iterator it2 = m_inv.begin(); obj_map::iterator end2 = m_inv.end(); for (; it2 != end2; ++it2) { out << mk_bounded_pp((*it2).m_key, m_manager) << " -> " << mk_bounded_pp((*it2).m_value, m_manager) << "\n"; } } expr * get_inv(expr * v) const { expr * t = 0; m_inv.find(v, t); return t; } unsigned get_generation(expr * t) const { unsigned gen = 0; m_elems.find(t, gen); return gen; } void mk_inverse(evaluator & ev) { obj_map::iterator it = m_elems.begin(); obj_map::iterator end = m_elems.end(); for (; it != end; ++it) { expr * t = (*it).m_key; SASSERT(!contains_model_value(t)); unsigned gen = (*it).m_value; expr * t_val = ev.eval(t, true); TRACE("model_finder", tout << mk_pp(t, m_manager) << "\n";); expr * old_t = 0; if (m_inv.find(t_val, old_t)) { unsigned old_t_gen = 0; SASSERT(m_elems.contains(old_t)); m_elems.find(old_t, old_t_gen); if (gen < old_t_gen) { m_inv.insert(t_val, t); } } else { m_inv.insert(t_val, t); } } } obj_map const & get_inv_map() const { return m_inv; } struct is_model_value {}; void operator()(expr *n) { if (m_manager.is_model_value(n)) { throw is_model_value(); } } bool contains_model_value(expr* n) { if (m_manager.is_model_value(n)) { return true; } if (is_app(n) && to_app(n)->get_num_args() == 0) { return false; } m_visited.reset(); try { for_each_expr(*this, m_visited, n); } catch (is_model_value) { return true; } return false; } }; /** During model construction time, we solve several constraints that impose restrictions on how the model for the ground formulas may be extended to a model to the relevant universal quantifiers. The class node and its subclasses are used to solve these constraints. */ // ----------------------------------- // // nodes // // ----------------------------------- /** \brief Base class used to solve model construction constraints. */ class node { unsigned m_id; node * m_find; unsigned m_eqc_size; sort * m_sort; // sort of the elements in the instantiation set. bool m_mono_proj; // relevant for integers & reals & bit-vectors bool m_signed_proj; // relevant for bit-vectors. ptr_vector m_avoid_set; ptr_vector m_exceptions; instantiation_set * m_set; expr * m_else; func_decl * m_proj; public: node(unsigned id, sort * s): m_id(id), m_find(0), m_eqc_size(1), m_sort(s), m_mono_proj(false), m_signed_proj(false), m_set(0), m_else(0), m_proj(0) { } ~node() { if (m_set) dealloc(m_set); } unsigned get_id() const { return m_id; } sort * get_sort() const { return m_sort; } bool is_root() const { return m_find == 0; } node * get_root() const { node * curr = const_cast(this); while (!curr->is_root()) { curr = curr->m_find; } SASSERT(curr->is_root()); return curr; } void merge(node * other) { node * r1 = get_root(); node * r2 = other->get_root(); SASSERT(r1->m_set == 0); SASSERT(r2->m_set == 0); SASSERT(r1->get_sort() == r2->get_sort()); if (r1 == r2) return; if (r1->m_eqc_size > r2->m_eqc_size) std::swap(r1, r2); r1->m_find = r2; r2->m_eqc_size += r1->m_eqc_size; if (r1->m_mono_proj) r2->m_mono_proj = true; if (r1->m_signed_proj) r2->m_signed_proj = true; dappend(r2->m_avoid_set, r1->m_avoid_set); dappend(r2->m_exceptions, r1->m_exceptions); } void insert_avoid(node * n) { ptr_vector & as = get_root()->m_avoid_set; if (!as.contains(n)) as.push_back(n); } void insert_exception(expr * n) { ptr_vector & ex = get_root()->m_exceptions; if (!ex.contains(n)) ex.push_back(n); } void set_mono_proj() { get_root()->m_mono_proj = true; } bool is_mono_proj() const { return get_root()->m_mono_proj; } void set_signed_proj() { get_root()->m_signed_proj = true; } bool is_signed_proj() const { return get_root()->m_signed_proj; } void mk_instantiation_set(ast_manager & m) { SASSERT(is_root()); m_set = alloc(instantiation_set, m); } void insert(expr * n, unsigned generation) { SASSERT(is_ground(n)); get_root()->m_set->insert(n, generation); } void display(std::ostream & out, ast_manager & m) const { if (is_root()) { out << "root node ------\n"; out << "@" << m_id << " mono: " << m_mono_proj << " signed: " << m_signed_proj << ", sort: " << mk_pp(m_sort, m) << "\n"; out << "avoid-set: "; ptr_vector::const_iterator it1 = m_avoid_set.begin(); ptr_vector::const_iterator end1 = m_avoid_set.end(); for (; it1 != end1; ++it1) { out << "@" << (*it1)->get_root()->get_id() << " "; } out << "\n"; out << "exceptions: "; ptr_vector::const_iterator it2 = m_exceptions.begin(); ptr_vector::const_iterator end2 = m_exceptions.end(); for (; it2 != end2; ++it2) { out << mk_bounded_pp((*it2), m) << " "; } out << "\n"; if (m_else) out << "else: " << mk_pp(m_else, m, 6) << "\n"; if (m_proj) out << "projection: " << m_proj->get_name() << "\n"; if (m_set) { out << "instantiation-set:\n"; m_set->display(out); } out << "----------------\n"; } else { out << "@" << m_id << " -> @" << get_root()->get_id() << "\n"; } } instantiation_set const * get_instantiation_set() const { return get_root()->m_set; } instantiation_set * get_instantiation_set() { return get_root()->m_set; } ptr_vector const & get_exceptions() const { return get_root()->m_exceptions; } ptr_vector const & get_avoid_set() const { return get_root()->m_avoid_set; } // return true if m_avoid_set.contains(this) bool must_avoid_itself() const { node * r = get_root(); ptr_vector::const_iterator it = m_avoid_set.begin(); ptr_vector::const_iterator end = m_avoid_set.end(); for (; it != end; ++it) { if (r == (*it)->get_root()) return true; } return false; } void set_else(expr * e) { SASSERT(!is_mono_proj()); SASSERT(get_root()->m_else == 0); get_root()->m_else = e; } expr * get_else() const { return get_root()->m_else; } void set_proj(func_decl * f) { SASSERT(get_root()->m_proj == 0); get_root()->m_proj = f; } func_decl * get_proj() const { return get_root()->m_proj; } }; typedef std::pair ast_idx_pair; typedef pair_hash, unsigned_hash> ast_idx_pair_hash; typedef map > key2node; /** \brief Auxiliary class for processing the "Almost uninterpreted fragment" described in the paper: Complete instantiation for quantified SMT formulas The idea is to create node objects based on the information produced by the quantifier_analyzer. */ class auf_solver : public evaluator { ast_manager & m_manager; arith_simplifier_plugin * m_asimp; bv_simplifier_plugin * m_bvsimp; ptr_vector m_nodes; unsigned m_next_node_id; key2node m_uvars; key2node m_A_f_is; context * m_context; // Mapping from sort to auxiliary constant. // This auxiliary constant is used as a "witness" that is asserted as different from a // finite number of terms. // It is only safe to use this constant for infinite sorts. obj_map m_sort2k; expr_ref_vector m_ks; // range of m_sort2k // Support for evaluating expressions in the current model. proto_model * m_model; obj_map m_eval_cache[2]; expr_ref_vector m_eval_cache_range; ptr_vector m_root_nodes; expr_ref_vector * m_new_constraints; void reset_sort2k() { m_sort2k.reset(); m_ks.reset(); } void reset_eval_cache() { m_eval_cache[0].reset(); m_eval_cache[1].reset(); m_eval_cache_range.reset(); } node * mk_node(key2node & m, ast * n, unsigned i, sort * s) { node * r = 0; ast_idx_pair k(n, i); if (m.find(k, r)) { SASSERT(r->get_sort() == s); return r; } r = alloc(node, m_next_node_id, s); m_next_node_id++; m.insert(k, r); m_nodes.push_back(r); return r; } void display_key2node(std::ostream & out, key2node const & m) const { key2node::iterator it = m.begin(); key2node::iterator end = m.end(); for (; it != end; ++it) { ast * a = (*it).m_key.first; unsigned i = (*it).m_key.second; node * n = (*it).m_value; out << "#" << a->get_id() << ":" << i << " -> @" << n->get_id() << "\n"; } } void display_A_f_is(std::ostream & out) const { key2node::iterator it = m_A_f_is.begin(); key2node::iterator end = m_A_f_is.end(); for (; it != end; ++it) { func_decl * f = static_cast((*it).m_key.first); unsigned i = (*it).m_key.second; node * n = (*it).m_value; out << f->get_name() << ":" << i << " -> @" << n->get_id() << "\n"; } } void flush_nodes() { std::for_each(m_nodes.begin(), m_nodes.end(), delete_proc()); } public: auf_solver(ast_manager & m, simplifier & s): m_manager(m), m_next_node_id(0), m_context(0), m_ks(m), m_model(0), m_eval_cache_range(m), m_new_constraints(0) { m_asimp = static_cast(s.get_plugin(m.mk_family_id("arith"))); m_bvsimp = static_cast(s.get_plugin(m.mk_family_id("bv"))); } ~auf_solver() { flush_nodes(); reset_eval_cache(); } void set_context(context * ctx) { SASSERT(m_context==0); m_context = ctx; } ast_manager & get_manager() const { return m_manager; } arith_simplifier_plugin * get_arith_simp() const { return m_asimp; } bv_simplifier_plugin * get_bv_simp() const { return m_bvsimp; } void reset() { flush_nodes(); m_nodes.reset(); m_next_node_id = 0; m_uvars.reset(); m_A_f_is.reset(); m_root_nodes.reset(); reset_sort2k(); } void set_model(proto_model * m) { reset_eval_cache(); m_model = m; } proto_model * get_model() const { SASSERT(m_model); return m_model; } node * get_uvar(quantifier * q, unsigned i) { SASSERT(i < q->get_num_decls()); sort * s = q->get_decl_sort(q->get_num_decls() - i - 1); return mk_node(m_uvars, q, i, s); } node * get_A_f_i(func_decl * f, unsigned i) { SASSERT(i < f->get_arity()); sort * s = f->get_domain(i); return mk_node(m_A_f_is, f, i, s); } instantiation_set const * get_uvar_inst_set(quantifier * q, unsigned i) const { SASSERT(!has_quantifiers(q->get_expr())); ast_idx_pair k(q, i); node * r = 0; if (m_uvars.find(k, r)) return r->get_instantiation_set(); return 0; } void mk_instantiation_sets() { ptr_vector::const_iterator it = m_nodes.begin(); ptr_vector::const_iterator end = m_nodes.end(); for (; it != end; ++it) { node * curr = *it; if (curr->is_root()) { curr->mk_instantiation_set(m_manager); } } } // For each instantiation_set, reemove entries that do not evaluate to values. void cleanup_instantiation_sets() { ptr_vector to_delete; ptr_vector::const_iterator it = m_nodes.begin(); ptr_vector::const_iterator end = m_nodes.end(); for (; it != end; ++it) { node * curr = *it; if (curr->is_root()) { instantiation_set * s = curr->get_instantiation_set(); to_delete.reset(); obj_map const & elems = s->get_elems(); for (obj_map::iterator it = elems.begin(); it != elems.end(); it++) { expr * n = it->m_key; expr * n_val = eval(n, true); if (!n_val || !m_manager.is_value(n_val)) to_delete.push_back(n); } for (ptr_vector::iterator it = to_delete.begin(); it != to_delete.end(); it++) { s->remove(*it); } } } } void display_nodes(std::ostream & out) const { display_key2node(out, m_uvars); display_A_f_is(out); ptr_vector::const_iterator it = m_nodes.begin(); ptr_vector::const_iterator end = m_nodes.end(); for (; it != end; ++it) { (*it)->display(out, m_manager); } } virtual expr * eval(expr * n, bool model_completion) { expr * r = 0; if (m_eval_cache[model_completion].find(n, r)) { return r; } expr_ref tmp(m_manager); if (!m_model->eval(n, tmp, model_completion)) { r = 0; TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n-----> null\n";); } else { r = tmp; TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n----->\n" << mk_pp(r, m_manager) << "\n";); } m_eval_cache[model_completion].insert(n, r); m_eval_cache_range.push_back(r); return r; } private: /** \brief Collect the interpretations of n->get_exceptions() and the interpretations of the m_else of nodes in n->get_avoid_set() */ void collect_exceptions_values(node * n, ptr_buffer & r) { ptr_vector const & exceptions = n->get_exceptions(); ptr_vector const & avoid_set = n->get_avoid_set(); ptr_vector::const_iterator it1 = exceptions.begin(); ptr_vector::const_iterator end1 = exceptions.end(); for (; it1 != end1; ++it1) { expr * val = eval(*it1, true); SASSERT(val != 0); r.push_back(val); } ptr_vector::const_iterator it2 = avoid_set.begin(); ptr_vector::const_iterator end2 = avoid_set.end(); for (; it2 != end2; ++it2) { node * n = (*it2)->get_root(); if (!n->is_mono_proj() && n->get_else() != 0) { expr * val = eval(n->get_else(), true); SASSERT(val != 0); r.push_back(val); } } } /** \brief Return an expr t from the instantiation set of \c n s.t. forall e in n.get_exceptions() eval(t) != eval(e) and forall m in n.get_avoid_set() eval(t) != eval(m.get_else()) If there t1 and t2 satisfying this condition, break ties using the generation of them. Return 0 if such t does not exist. */ expr * pick_instance_diff_exceptions(node * n, ptr_buffer const & ex_vals) { instantiation_set const * s = n->get_instantiation_set(); obj_map const & elems = s->get_elems(); expr * t_result = 0; unsigned gen_result = UINT_MAX; obj_map::iterator it1 = elems.begin(); obj_map::iterator end1 = elems.end(); for (; it1 != end1; ++it1) { expr * t = (*it1).m_key; unsigned gen = (*it1).m_value; expr * t_val = eval(t, true); SASSERT(t_val != 0); ptr_buffer::const_iterator it2 = ex_vals.begin(); ptr_buffer::const_iterator end2 = ex_vals.end(); for (; it2 != end2; ++it2) { if (!m_manager.are_distinct(t_val, *it2)) break; } if (it2 == end2 && (t_result == 0 || gen < gen_result)) { t_result = t; gen_result = gen; } } return t_result; } bool is_infinite(sort * s) const { // we should not assume that uninterpreted sorts are infinite in benchmarks with quantifiers. return !m_manager.is_uninterp(s) && s->is_infinite(); } /** \brief Return a fresh constant k that is used as a witness for elements that must be different from a set of values. */ app * get_k_for(sort * s) { SASSERT(is_infinite(s)); app * r = 0; if (m_sort2k.find(s, r)) return r; r = m_manager.mk_fresh_const("k", s); m_sort2k.insert(s, r); m_ks.push_back(r); return r; } /** \brief Get the interpretation for k in m_model. If m_model does not provide an interpretation for k, then create a fresh one. Remark: this method uses get_fresh_value, so it may fail. */ expr * get_k_interp(app * k) { sort * s = m_manager.get_sort(k); SASSERT(is_infinite(s)); func_decl * k_decl = k->get_decl(); expr * r = m_model->get_const_interp(k_decl); if (r != 0) return r; r = m_model->get_fresh_value(s); if (r == 0) return 0; m_model->register_decl(k_decl, r); SASSERT(m_model->get_const_interp(k_decl) == r); TRACE("model_finder", tout << mk_pp(r, m_manager) << "\n";); return r; } /** \brief Assert k to be different from the set of exceptions. It invokes get_k_interp that may fail. */ bool assert_k_diseq_exceptions(app * k, ptr_vector const & exceptions) { TRACE("assert_k_diseq_exceptions", tout << "assert_k_diseq_exceptions, " << "k: " << mk_pp(k, m_manager) << "\nexceptions:\n"; for (unsigned i = 0; i < exceptions.size(); i++) tout << mk_pp(exceptions[i], m_manager) << "\n";); expr * k_interp = get_k_interp(k); if (k_interp == 0) return false; ptr_vector::const_iterator it = exceptions.begin(); ptr_vector::const_iterator end = exceptions.end(); for (; it != end; ++it) { expr * ex = *it; expr * ex_val = eval(ex, true); if (!m_manager.are_distinct(k_interp, ex_val)) { SASSERT(m_new_constraints); // This constraint cannot be asserted into m_context during model construction. // We must save it, and assert it during a restart. m_new_constraints->push_back(m_manager.mk_not(m_manager.mk_eq(k, ex))); } } return true; } void set_projection_else(node * n) { SASSERT(n->is_root()); SASSERT(!n->is_mono_proj()); instantiation_set const * s = n->get_instantiation_set(); ptr_vector const & exceptions = n->get_exceptions(); ptr_vector const & avoid_set = n->get_avoid_set(); obj_map const & elems = s->get_elems(); SASSERT(!elems.empty()); if (!exceptions.empty() || !avoid_set.empty()) { ptr_buffer ex_vals; collect_exceptions_values(n, ex_vals); expr * e = pick_instance_diff_exceptions(n, ex_vals); if (e != 0) { n->set_else(e); return; } sort * s = n->get_sort(); TRACE("model_finder", tout << "trying to create k for " << mk_pp(s, m_manager) << ", is_infinite: " << is_infinite(s) << "\n";); if (is_infinite(s)) { app * k = get_k_for(s); if (assert_k_diseq_exceptions(k, exceptions)) { n->insert(k, 0); // add k to the instantiation set n->set_else(k); return; } } // TBD: add support for the else of bitvectors. // Idea: get the term t with the minimal interpreation and use t - 1. } n->set_else((*(elems.begin())).m_key); } /** \brief If m_mono_proj is true and n is int or bv, then for each e in n->get_exceptions(), we must add e-1 and e+1 to the instantiation set. If sort->get_sort() is real, then we do nothing and hope for the best. */ void add_mono_exceptions(node * n) { SASSERT(n->is_mono_proj()); sort * s = n->get_sort(); arith_simplifier_plugin * as = get_arith_simp(); bv_simplifier_plugin * bs = get_bv_simp(); bool is_int = as->is_int_sort(s); bool is_bv = bs->is_bv_sort(s); if (!is_int && !is_bv) return; poly_simplifier_plugin * ps = as; if (is_bv) ps = bs; ps->set_curr_sort(s); expr_ref one(m_manager); one = ps->mk_one(); ptr_vector const & exceptions = n->get_exceptions(); ptr_vector::const_iterator it = exceptions.begin(); ptr_vector::const_iterator end = exceptions.end(); for (; it != end; ++it) { expr * e = *it; expr_ref e_plus_1(m_manager); expr_ref e_minus_1(m_manager); TRACE("mf_simp_bug", tout << "e:\n" << mk_ismt2_pp(e, m_manager) << "\none:\n" << mk_ismt2_pp(one, m_manager) << "\n";); ps->mk_add(e, one, e_plus_1); ps->mk_sub(e, one, e_minus_1); // Note: exceptions come from quantifiers bodies. So, they have generation 0. n->insert(e_plus_1, 0); n->insert(e_minus_1, 0); } } void get_instantiation_set_values(node * n, ptr_buffer & values) { instantiation_set const * s = n->get_instantiation_set(); obj_hashtable already_found; obj_map const & elems = s->get_elems(); obj_map::iterator it = elems.begin(); obj_map::iterator end = elems.end(); for (; it != end; ++it) { expr * t = (*it).m_key; expr * t_val = eval(t, true); if (!already_found.contains(t_val)) { values.push_back(t_val); already_found.insert(t_val); } } TRACE("model_finder_bug", tout << "values for the instantiation_set of @" << n->get_id() << "\n"; ptr_buffer::const_iterator it = values.begin(); ptr_buffer::const_iterator end = values.end(); for (; it != end; ++it) { expr * v = *it; tout << mk_pp(v, m_manager) << "\n"; }); } struct numeral_lt { poly_simplifier_plugin * m_p; numeral_lt(poly_simplifier_plugin * p):m_p(p) {} bool operator()(expr * e1, expr * e2) { rational v1, v2; if (m_p->is_numeral(e1, v1) && m_p->is_numeral(e2, v2)) { return v1 < v2; } else { return e1->get_id() < e2->get_id(); } } }; struct signed_bv_lt { bv_simplifier_plugin * m_bs; unsigned m_bv_size; signed_bv_lt(bv_simplifier_plugin * bs, unsigned sz):m_bs(bs), m_bv_size(sz) {} bool operator()(expr * e1, expr * e2) { rational v1, v2; if (m_bs->is_numeral(e1, v1) && m_bs->is_numeral(e2, v2)) { v1 = m_bs->norm(v1, m_bv_size, true); v2 = m_bs->norm(v2, m_bv_size, true); return v1 < v2; } else { return e1->get_id() < e2->get_id(); } } }; void sort_values(node * n, ptr_buffer & values) { sort * s = n->get_sort(); if (get_arith_simp()->is_arith_sort(s)) { std::sort(values.begin(), values.end(), numeral_lt(get_arith_simp())); } else if (!n->is_signed_proj()) { std::sort(values.begin(), values.end(), numeral_lt(get_bv_simp())); } else { bv_simplifier_plugin * bs = get_bv_simp(); std::sort(values.begin(), values.end(), signed_bv_lt(bs, bs->get_bv_size(s))); } } void mk_mono_proj(node * n) { add_mono_exceptions(n); ptr_buffer values; get_instantiation_set_values(n, values); sort_values(n, values); sort * s = n->get_sort(); arith_simplifier_plugin * as = get_arith_simp(); bv_simplifier_plugin * bs = get_bv_simp(); bool is_arith = as->is_arith_sort(s); bool is_signed = n->is_signed_proj(); unsigned sz = values.size(); SASSERT(sz > 0); func_decl * p = m_manager.mk_fresh_func_decl(1, &s, s); expr * pi = values[sz - 1]; expr_ref var(m_manager); var = m_manager.mk_var(0, s); for (unsigned i = sz - 1; i >= 1; i--) { expr_ref c(m_manager); if (is_arith) as->mk_lt(var, values[i], c); else if (!is_signed) bs->mk_ult(var, values[i], c); else bs->mk_slt(var, values[i], c); pi = m_manager.mk_ite(c, values[i-1], pi); } func_interp * rpi = alloc(func_interp, m_manager, 1); rpi->set_else(pi); m_model->register_decl(p, rpi, true); n->set_proj(p); } void mk_simple_proj(node * n) { set_projection_else(n); ptr_buffer values; get_instantiation_set_values(n, values); sort * s = n->get_sort(); expr * else_val = eval(n->get_else(), true); func_decl * p = m_manager.mk_fresh_func_decl(1, &s, s); func_interp * pi = alloc(func_interp, m_manager, 1); pi->set_else(else_val); m_model->register_decl(p, pi, true); ptr_buffer::const_iterator it = values.begin(); ptr_buffer::const_iterator end = values.end(); for (; it != end; ++it) { expr * v = *it; pi->insert_new_entry(&v, v); } n->set_proj(p); } void mk_projections() { ptr_vector::const_iterator it = m_root_nodes.begin(); ptr_vector::const_iterator end = m_root_nodes.end(); for (; it != end; ++it) { node * n = *it; SASSERT(n->is_root()); if (n->is_mono_proj()) mk_mono_proj(n); else mk_simple_proj(n); } } /** \brief Store in r the partial functions that have A_f_i nodes. */ void collect_partial_funcs(func_decl_set & r) { key2node::iterator it = m_A_f_is.begin(); key2node::iterator end = m_A_f_is.end(); for (; it != end; ++it) { func_decl * f = to_func_decl((*it).m_key.first); if (!r.contains(f)) { func_interp * fi = m_model->get_func_interp(f); if (fi == 0) { fi = alloc(func_interp, m_manager, f->get_arity()); m_model->register_decl(f, fi); SASSERT(fi->is_partial()); } if (fi->is_partial()) { r.insert(f); } } } } /** \brief Make sorts associated with nodes that must avoid themselves finite. Only uninterpreted sorts are considered. This is a trick to be able to handle atoms of the form X = Y where X and Y are variables. See paper "Complete Quantifier Instantiation" for more details. */ void mk_sorts_finite() { ptr_vector::const_iterator it = m_root_nodes.begin(); ptr_vector::const_iterator end = m_root_nodes.end(); for (; it != end; ++it) { node * n = *it; SASSERT(n->is_root()); sort * s = n->get_sort(); if (m_manager.is_uninterp(s) && // Making all uninterpreted sorts finite. // n->must_avoid_itself() && !m_model->is_finite(s)) { m_model->freeze_universe(s); } } } void add_elem_to_empty_inst_sets() { ptr_vector::const_iterator it = m_root_nodes.begin(); ptr_vector::const_iterator end = m_root_nodes.end(); for (; it != end; ++it) { node * n = *it; SASSERT(n->is_root()); instantiation_set const * s = n->get_instantiation_set(); obj_map const & elems = s->get_elems(); if (elems.empty()) { // The method get_some_value cannot be used if n->get_sort() is an uninterpreted sort or is a sort built using uninterpreted sorts // (e.g., (Array S S) where S is uninterpreted). The problem is that these sorts do not have a fixed interpretation. // Moreover, a model assigns an arbitrary intepretation to these sorts using "model_values" a model value. // If these module values "leak" inside the logical context, they may affect satisfiability. // sort * ns = n->get_sort(); if (m_manager.is_fully_interp(ns)) n->insert(m_model->get_some_value(ns), 0); else n->insert(m_manager.mk_fresh_const("elem", ns), 0); } } } /** \brief Store in m_root_nodes the roots from m_nodes. */ void collect_root_nodes() { m_root_nodes.reset(); ptr_vector::const_iterator it = m_nodes.begin(); ptr_vector::const_iterator end = m_nodes.end(); for (; it != end; ++it) { node * n = *it; if (n->is_root()) m_root_nodes.push_back(n); } } /** \brief Return the projection function for f at argument i. Return 0, if there is none. \remark This method assumes that mk_projections was already invoked. \remark f may have a non partial interpretation on m_model. This may happen because the evaluator uses model_completion. In the beginning of fix_model() we collected all f with partial interpretations. During the process of computing the projections we used the evalutator with model_completion, and it may have fixed the "else" case of some partial interpretations. This is ok, because in the "limit" the "else" of the interpretation is irrelevant after the projections are applied. */ func_decl * get_f_i_proj(func_decl * f, unsigned i) { node * r = 0; ast_idx_pair k(f, i); if (!m_A_f_is.find(k, r)) return 0; return r->get_proj(); } /** \brief Complete the interpretation of the functions that were collected in the beginning of fix_model(). */ void complete_partial_funcs(func_decl_set const & partial_funcs) { func_decl_set::iterator it = partial_funcs.begin(); func_decl_set::iterator end = partial_funcs.end(); for (; it != end; ++it) { func_decl * f = *it; // Complete the current interpretation m_model->complete_partial_func(f); unsigned arity = f->get_arity(); func_interp * fi = m_model->get_func_interp(f); if (fi->is_constant()) continue; // there is no point in using the projection for fi, since fi is the constant function. expr_ref_vector args(m_manager); bool has_proj = false; for (unsigned i = 0; i < arity; i++) { var * v = m_manager.mk_var(i, f->get_domain(i)); func_decl * pi = get_f_i_proj(f, i); if (pi != 0) { args.push_back(m_manager.mk_app(pi, v)); has_proj = true; } else { args.push_back(v); } } if (has_proj) { // f_aux will be assigned to the old interpretation of f. func_decl * f_aux = m_manager.mk_fresh_func_decl(f->get_name(), symbol::null, arity, f->get_domain(), f->get_range()); func_interp * new_fi = alloc(func_interp, m_manager, arity); new_fi->set_else(m_manager.mk_app(f_aux, args.size(), args.c_ptr())); TRACE("model_finder", tout << "Setting new interpretation for " << f->get_name() << "\n" << mk_pp(new_fi->get_else(), m_manager) << "\n";); m_model->reregister_decl(f, new_fi, f_aux); } } } void mk_inverse(node * n) { SASSERT(n->is_root()); instantiation_set * s = n->get_instantiation_set(); s->mk_inverse(*this); } void mk_inverses() { ptr_vector::const_iterator it = m_root_nodes.begin(); ptr_vector::const_iterator end = m_root_nodes.end(); for (; it != end; ++it) { node * n = *it; SASSERT(n->is_root()); mk_inverse(n); } } public: void fix_model(expr_ref_vector & new_constraints) { cleanup_instantiation_sets(); m_new_constraints = &new_constraints; func_decl_set partial_funcs; collect_partial_funcs(partial_funcs); reset_eval_cache(); // will start using model_completion collect_root_nodes(); add_elem_to_empty_inst_sets(); mk_sorts_finite(); mk_projections(); mk_inverses(); complete_partial_funcs(partial_funcs); TRACE("model_finder", tout << "after auf_solver fixing the model\n"; display_nodes(tout); tout << "NEW MODEL:\n"; model_pp(tout, *m_model);); } }; // ----------------------------------- // // qinfo class & subclasses // // ----------------------------------- /* During quantifier internalizations time, we collect bits of information about the quantifier structure. These bits are instances of subclasses of qinfo. */ /** \brief Generic bit of information collected when analyzing quantifiers. The subclasses are defined in the .cpp file. */ class qinfo { public: virtual ~qinfo() {} virtual char const * get_kind() const = 0; virtual bool is_equal(qinfo const * qi) const = 0; virtual void display(std::ostream & out) const { out << "[" << get_kind() << "]"; } // AUF fragment solver virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) = 0; virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) = 0; // second pass... actually we may need to reach a fixpoint, but if it cannot be found // in two passes, the fixpoint is not finite. virtual void populate_inst_sets2(quantifier * q, auf_solver & s, context * ctx) {} // Macro/Hint support virtual void populate_inst_sets(quantifier * q, func_decl * mhead, ptr_vector & uvar_inst_sets, context * ctx) {} }; class f_var : public qinfo { protected: func_decl * m_f; unsigned m_arg_i; unsigned m_var_j; public: f_var(func_decl * f, unsigned i, unsigned j):m_f(f), m_arg_i(i), m_var_j(j) {} virtual ~f_var() {} virtual char const * get_kind() const { return "f_var"; } virtual bool is_equal(qinfo const * qi) const { if (qi->get_kind() != get_kind()) return false; f_var const * other = static_cast(qi); return m_f == other->m_f && m_arg_i == other->m_arg_i && m_var_j == other->m_var_j; } virtual void display(std::ostream & out) const { out << "(" << m_f->get_name() << ":" << m_arg_i << " -> v!" << m_var_j << ")"; } virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { node * n1 = s.get_A_f_i(m_f, m_arg_i); node * n2 = s.get_uvar(q, m_var_j); CTRACE("model_finder", n1->get_sort() != n2->get_sort(), ast_manager & m = ctx->get_manager(); tout << "sort bug:\n" << mk_ismt2_pp(q->get_expr(), m) << "\n" << mk_ismt2_pp(q, m) << "\n"; tout << "decl(0): " << q->get_decl_name(0) << "\n"; tout << "f: " << m_f->get_name() << " i: " << m_arg_i << "\n"; tout << "v: " << m_var_j << "\n"; n1->get_root()->display(tout, m); n2->get_root()->display(tout, m); tout << "f signature: "; for (unsigned i = 0; i < m_f->get_arity(); i++) tout << mk_pp(m_f->get_domain(i), m) << " "; tout << "-> " << mk_pp(m_f->get_range(), m) << "\n"; ); n1->merge(n2); } virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { node * A_f_i = s.get_A_f_i(m_f, m_arg_i); enode_vector::const_iterator it = ctx->begin_enodes_of(m_f); enode_vector::const_iterator end = ctx->end_enodes_of(m_f); for (; it != end; it++) { enode * n = *it; if (ctx->is_relevant(n)) { // Remark: it is incorrect to use // n->get_arg(m_arg_i)->get_root() // instead of // n->get_arg(m_arg_i) // // Due to model based quantifier instantiation, some equivalence // classes are merged by accident. // So, using n->get_arg(m_arg_i)->get_root(), we may miss // a necessary instantiation. enode * e_arg = n->get_arg(m_arg_i); expr * arg = e_arg->get_owner(); A_f_i->insert(arg, e_arg->get_generation()); } } } virtual void populate_inst_sets(quantifier * q, func_decl * mhead, ptr_vector & uvar_inst_sets, context * ctx) { if (m_f != mhead) return; uvar_inst_sets.reserve(m_var_j+1, 0); if (uvar_inst_sets[m_var_j] == 0) uvar_inst_sets[m_var_j] = alloc(instantiation_set, ctx->get_manager()); instantiation_set * s = uvar_inst_sets[m_var_j]; SASSERT(s != 0); enode_vector::const_iterator it = ctx->begin_enodes_of(m_f); enode_vector::const_iterator end = ctx->end_enodes_of(m_f); for (; it != end; it++) { enode * n = *it; if (ctx->is_relevant(n)) { enode * e_arg = n->get_arg(m_arg_i); expr * arg = e_arg->get_owner(); s->insert(arg, e_arg->get_generation()); } } } }; class f_var_plus_offset : public f_var { expr_ref m_offset; public: f_var_plus_offset(ast_manager & m, func_decl * f, unsigned i, unsigned j, expr * offset): f_var(f, i, j), m_offset(offset, m) { } virtual ~f_var_plus_offset() {} virtual char const * get_kind() const { return "f_var_plus_offset"; } virtual bool is_equal(qinfo const * qi) const { if (qi->get_kind() != get_kind()) return false; f_var_plus_offset const * other = static_cast(qi); return m_f == other->m_f && m_arg_i == other->m_arg_i && m_var_j == other->m_var_j && m_offset.get() == other->m_offset.get(); } virtual void display(std::ostream & out) const { out << "(" << m_f->get_name() << ":" << m_arg_i << " - " << mk_bounded_pp(m_offset.get(), m_offset.get_manager()) << " -> v!" << m_var_j << ")"; } virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { // just create the nodes /* node * A_f_i = */ s.get_A_f_i(m_f, m_arg_i); /* node * S_j = */ s.get_uvar(q, m_var_j); } virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { // S_j is not necessary equal to A_f_i. node * A_f_i = s.get_A_f_i(m_f, m_arg_i)->get_root(); node * S_j = s.get_uvar(q, m_var_j)->get_root(); if (A_f_i == S_j) { // there is no finite fixpoint... we just copy the i-th arguments of A_f_i - m_offset // hope for the best... arith_simplifier_plugin * as = s.get_arith_simp(); bv_simplifier_plugin * bs = s.get_bv_simp(); node * S_j = s.get_uvar(q, m_var_j); enode_vector::const_iterator it = ctx->begin_enodes_of(m_f); enode_vector::const_iterator end = ctx->end_enodes_of(m_f); for (; it != end; it++) { enode * n = *it; if (ctx->is_relevant(n)) { enode * e_arg = n->get_arg(m_arg_i); expr * arg = e_arg->get_owner(); expr_ref arg_minus_k(ctx->get_manager()); if (bs->is_bv(arg)) bs->mk_sub(arg, m_offset, arg_minus_k); else as->mk_sub(arg, m_offset, arg_minus_k); S_j->insert(arg_minus_k, e_arg->get_generation()); } } } else { // A_f_i != S_j, there is hope for a finite fixpoint. // So, we just populate A_f_i f_var::populate_inst_sets(q, s, ctx); // I must also propagate the monotonicity flag since A_f_i and S_j are not in the // same equivalence class. if (A_f_i->is_mono_proj()) S_j->set_mono_proj(); if (S_j->is_mono_proj()) A_f_i->set_mono_proj(); } } template void copy_instances(node * from, node * to, auf_solver & s) { arith_simplifier_plugin * as = s.get_arith_simp(); bv_simplifier_plugin * bs = s.get_bv_simp(); poly_simplifier_plugin * ps = as; if (bs->is_bv_sort(from->get_sort())) ps = bs; instantiation_set const * from_s = from->get_instantiation_set(); obj_map const & elems_s = from_s->get_elems(); obj_map::iterator it = elems_s.begin(); obj_map::iterator end = elems_s.end(); for (; it != end; ++it) { expr * n = (*it).m_key; expr_ref n_k(m_offset.get_manager()); if (PLUS) ps->mk_add(n, m_offset, n_k); else ps->mk_sub(n, m_offset, n_k); to->insert(n_k, (*it).m_value); } } virtual void populate_inst_sets2(quantifier * q, auf_solver & s, context * ctx) { node * A_f_i = s.get_A_f_i(m_f, m_arg_i)->get_root(); node * S_j = s.get_uvar(q, m_var_j)->get_root(); // If A_f_i == S_j, then there is no finite fixpoint, so we do nothing here. if (A_f_i != S_j) { // enforce // A_f_i - k \subset S_j // S_j + k \subset A_f_i copy_instances(A_f_i, S_j, s); copy_instances(S_j, A_f_i, s); } } virtual void populate_inst_sets(quantifier * q, func_decl * mhead, ptr_vector & uvar_inst_sets, context * ctx) { // ignored when in macro } }; /** \brief auf_arr is a term (pattern) of the form: FORM := GROUND-TERM | (select FORM VAR) Store in arrays, all enodes that match the pattern */ void get_auf_arrays(app * auf_arr, context * ctx, ptr_buffer & arrays) { if (is_ground(auf_arr)) { if (ctx->e_internalized(auf_arr)) { enode * e = ctx->get_enode(auf_arr); if (ctx->is_relevant(e)) { arrays.push_back(e); } } } else { app * nested_array = to_app(auf_arr->get_arg(0)); ptr_buffer nested_arrays; get_auf_arrays(nested_array, ctx, nested_arrays); ptr_buffer::const_iterator it = nested_arrays.begin(); ptr_buffer::const_iterator end = nested_arrays.end(); for (; it != end; ++it) { enode * curr = *it; enode_vector::iterator it2 = curr->begin_parents(); enode_vector::iterator end2 = curr->end_parents(); for (; it2 != end2; ++it2) { enode * p = *it2; if (ctx->is_relevant(p) && p->get_owner()->get_decl() == auf_arr->get_decl()) { arrays.push_back(p); } } } } } class select_var : public qinfo { protected: ast_manager & m_manager; app * m_select; // It must satisfy is_auf_select... see bool is_auf_select(expr * t) const unsigned m_arg_i; unsigned m_var_j; app * get_array() const { return to_app(m_select->get_arg(0)); } func_decl * get_array_func_decl(app * ground_array, auf_solver & s) { expr * ground_array_interp = s.eval(ground_array, false); if (ground_array_interp != 0 && s.get_model()->is_array_value(ground_array_interp)) return to_func_decl(to_app(ground_array_interp)->get_decl()->get_parameter(0).get_ast()); return 0; } public: select_var(ast_manager & m, app * s, unsigned i, unsigned j):m_manager(m), m_select(s), m_arg_i(i), m_var_j(j) {} virtual ~select_var() {} virtual char const * get_kind() const { return "select_var"; } virtual bool is_equal(qinfo const * qi) const { if (qi->get_kind() != get_kind()) return false; select_var const * other = static_cast(qi); return m_select == other->m_select && m_arg_i == other->m_arg_i && m_var_j == other->m_var_j; } virtual void display(std::ostream & out) const { out << "(" << mk_bounded_pp(m_select, m_manager) << ":" << m_arg_i << " -> v!" << m_var_j << ")"; } virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { ptr_buffer arrays; get_auf_arrays(get_array(), ctx, arrays); TRACE("select_var", tout << "enodes matching: "; display(tout); tout << "\n"; ptr_buffer::const_iterator it = arrays.begin(); ptr_buffer::const_iterator end = arrays.end(); for (; it != end; ++it) { tout << "#" << (*it)->get_owner()->get_id() << "\n" << mk_pp((*it)->get_owner(), m_manager) << "\n"; }); node * n1 = s.get_uvar(q, m_var_j); ptr_buffer::const_iterator it = arrays.begin(); ptr_buffer::const_iterator end = arrays.end(); for (; it != end; ++it) { app * ground_array = (*it)->get_owner(); func_decl * f = get_array_func_decl(ground_array, s); if (f) { SASSERT(m_arg_i >= 1); node * n2 = s.get_A_f_i(f, m_arg_i - 1); n1->merge(n2); } } } virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { ptr_buffer arrays; get_auf_arrays(get_array(), ctx, arrays); ptr_buffer::const_iterator it = arrays.begin(); ptr_buffer::const_iterator end = arrays.end(); for (; it != end; ++it) { enode * curr = (*it); app * ground_array = curr->get_owner(); func_decl * f = get_array_func_decl(ground_array, s); if (f) { node * A_f_i = s.get_A_f_i(f, m_arg_i - 1); enode_vector::iterator it2 = curr->begin_parents(); enode_vector::iterator end2 = curr->end_parents(); for (; it2 != end2; ++it2) { enode * p = *it2; if (ctx->is_relevant(p) && p->get_owner()->get_decl() == m_select->get_decl()) { SASSERT(m_arg_i < p->get_owner()->get_num_args()); enode * e_arg = p->get_arg(m_arg_i); A_f_i->insert(e_arg->get_owner(), e_arg->get_generation()); } } } } } }; class var_pair : public qinfo { protected: unsigned m_var_i; unsigned m_var_j; public: var_pair(unsigned i, unsigned j):m_var_i(i), m_var_j(j) { if (m_var_i > m_var_j) std::swap(m_var_i, m_var_j); } virtual ~var_pair() {} virtual bool is_equal(qinfo const * qi) const { if (qi->get_kind() != get_kind()) return false; var_pair const * other = static_cast(qi); return m_var_i == other->m_var_i && m_var_j == other->m_var_j; } virtual void display(std::ostream & out) const { out << "(" << get_kind() << ":v!" << m_var_i << ":v!" << m_var_j << ")"; } virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { // do nothing } }; class x_eq_y : public var_pair { public: x_eq_y(unsigned i, unsigned j):var_pair(i, j) {} virtual char const * get_kind() const { return "x_eq_y"; } virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->insert_avoid(n2); if (n1 != n2) n2->insert_avoid(n1); } }; class x_neq_y : public var_pair { public: x_neq_y(unsigned i, unsigned j):var_pair(i, j) {} virtual char const * get_kind() const { return "x_neq_y"; } virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->merge(n2); } }; class x_leq_y : public var_pair { public: x_leq_y(unsigned i, unsigned j):var_pair(i, j) {} virtual char const * get_kind() const { return "x_leq_y"; } virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->merge(n2); n1->set_mono_proj(); } }; // signed bit-vector comparison class x_sleq_y : public x_leq_y { public: x_sleq_y(unsigned i, unsigned j):x_leq_y(i, j) {} virtual char const * get_kind() const { return "x_sleq_y"; } virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->merge(n2); n1->set_mono_proj(); n1->set_signed_proj(); } }; class var_expr_pair : public qinfo { protected: unsigned m_var_i; expr_ref m_t; public: var_expr_pair(ast_manager & m, unsigned i, expr * t): m_var_i(i), m_t(t, m) {} ~var_expr_pair() {} virtual bool is_equal(qinfo const * qi) const { if (qi->get_kind() != get_kind()) return false; var_expr_pair const * other = static_cast(qi); return m_var_i == other->m_var_i && m_t.get() == other->m_t.get(); } virtual void display(std::ostream & out) const { out << "(" << get_kind() << ":v!" << m_var_i << ":" << mk_bounded_pp(m_t.get(), m_t.get_manager()) << ")"; } }; class x_eq_t : public var_expr_pair { public: x_eq_t(ast_manager & m, unsigned i, expr * t): var_expr_pair(m, i, t) {} virtual char const * get_kind() const { return "x_eq_t"; } virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { node * n1 = s.get_uvar(q, m_var_i); n1->insert_exception(m_t); } virtual void populate_inst_sets(quantifier * q, auf_solver & slv, context * ctx) { unsigned num_vars = q->get_num_decls(); ast_manager & m = ctx->get_manager(); sort * s = q->get_decl_sort(num_vars - m_var_i - 1); if (m.is_uninterp(s)) { // For uninterpreted sorst, we add all terms in the context. // See Section 4.1 in the paper "Complete Quantifier Instantiation" node * S_q_i = slv.get_uvar(q, m_var_i); ptr_vector::const_iterator it = ctx->begin_enodes(); ptr_vector::const_iterator end = ctx->end_enodes(); for (; it != end; ++it) { enode * n = *it; if (ctx->is_relevant(n) && get_sort(n->get_owner()) == s) { S_q_i->insert(n->get_owner(), n->get_generation()); } } } } }; class x_neq_t : public var_expr_pair { public: x_neq_t(ast_manager & m, unsigned i, expr * t): var_expr_pair(m, i, t) {} virtual char const * get_kind() const { return "x_neq_t"; } virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { // make sure that S_q_i is create. s.get_uvar(q, m_var_i); } virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { node * S_q_i = s.get_uvar(q, m_var_i); S_q_i->insert(m_t, 0); } }; class x_gle_t : public var_expr_pair { public: x_gle_t(ast_manager & m, unsigned i, expr * t): var_expr_pair(m, i, t) {} virtual char const * get_kind() const { return "x_gle_t"; } virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { // make sure that S_q_i is create. node * n1 = s.get_uvar(q, m_var_i); n1->set_mono_proj(); } virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { node * S_q_i = s.get_uvar(q, m_var_i); S_q_i->insert(m_t, 0); } }; class cond_macro { protected: ast_manager & m_manager; func_decl * m_f; expr * m_def; expr * m_cond; bool m_ineq; bool m_satisfy_atom; bool m_hint; unsigned m_weight; public: cond_macro(ast_manager & m, func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, unsigned weight): m_manager(m), m_f(f), m_def(def), m_cond(cond), m_ineq(ineq), m_satisfy_atom(satisfy_atom), m_hint(hint), m_weight(weight) { m_manager.inc_ref(m_def); m_manager.inc_ref(m_cond); SASSERT(!m_hint || m_cond == 0); } ~cond_macro() { m_manager.dec_ref(m_def); m_manager.dec_ref(m_cond); } func_decl * get_f() const { return m_f; } expr * get_def() const { return m_def; } expr * get_cond() const { return m_cond; } bool is_unconditional() const { return m_cond == 0 || m_manager.is_true(m_cond); } bool satisfy_atom() const { return m_satisfy_atom; } bool is_hint() const { return m_hint; } bool is_equal(cond_macro const * other) const { return m_f == other->m_f && m_def == other->m_def && m_cond == other->m_cond; } void display(std::ostream & out) const { out << "[" << m_f->get_name() << " -> " << mk_bounded_pp(m_def, m_manager, 6); if (m_hint) out << " *hint*"; else out << " when " << mk_bounded_pp(m_cond, m_manager, 6); out << "] weight: " << m_weight; } unsigned get_weight() const { return m_weight; } }; // ----------------------------------- // // quantifier_info & quantifier_analyzer // // ----------------------------------- class quantifier_analyzer; /** \brief Store relevant information regarding a particular universal quantifier. This information is populated by quantifier_analyzer. The information is used to (try to) build a model that satisfy the universal quantifier (when it is marked as relevant in the end of the search). */ class quantifier_info { quantifier_ref m_flat_q; // flat version of the quantifier bool m_is_auf; bool m_has_x_eq_y; ptr_vector m_qinfo_vect; func_decl_set m_ng_decls; // declarations used in non-ground applications (basic_family operators are ignored here). ptr_vector m_cond_macros; func_decl * m_the_one; // the macro head used to satisfy the quantifier. this is useful for model checking // when the quantifier is satisfied by a macro/hint, it may not be processed by the AUF solver. // in this case, the quantifier_info stores the instantiation sets. ptr_vector * m_uvar_inst_sets; bool m_cancel; friend class quantifier_analyzer; void checkpoint() { cooperate("quantifier_info"); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } void insert_qinfo(qinfo * qi) { // I'm assuming the number of qinfo's per quantifier is small. So, the linear search is not a big deal. ptr_vector::iterator it = m_qinfo_vect.begin(); ptr_vector::iterator end = m_qinfo_vect.end(); for (; it != end; ++it) { checkpoint(); if (qi->is_equal(*it)) { dealloc(qi); return; } } m_qinfo_vect.push_back(qi); TRACE("model_finder", tout << "new quantifier qinfo: "; qi->display(tout); tout << "\n";); } void insert_macro(cond_macro * m) { m_cond_macros.push_back(m); } public: typedef ptr_vector::const_iterator macro_iterator; quantifier_info(ast_manager & m, quantifier * q): m_flat_q(m), m_is_auf(true), m_has_x_eq_y(false), m_the_one(0), m_uvar_inst_sets(0), m_cancel(false) { if (has_quantifiers(q->get_expr())) { static bool displayed_flat_msg = false; if (!displayed_flat_msg) { // [Leo]: This warning message is not usefult. // warning_msg("For problems containing quantifiers, the model finding capabilities of Z3 work better when the formula does not contain nested quantifiers. You can use PULL_NESTED_QUANTIFIERS=true to eliminate nested quantifiers."); displayed_flat_msg = true; } proof_ref pr(m); expr_ref new_q(m); pull_quant pull(m); pull(q, new_q, pr); SASSERT(is_well_sorted(m, new_q)); m_flat_q = to_quantifier(new_q); } else { m_flat_q = q; } CTRACE("model_finder_bug", has_quantifiers(m_flat_q->get_expr()), tout << mk_pp(q, m) << "\n" << mk_pp(m_flat_q, m) << "\n";); SASSERT(!has_quantifiers(m_flat_q->get_expr())); } ~quantifier_info() { std::for_each(m_qinfo_vect.begin(), m_qinfo_vect.end(), delete_proc()); std::for_each(m_cond_macros.begin(), m_cond_macros.end(), delete_proc()); reset_the_one(); } bool is_auf() const { return m_is_auf; } quantifier * get_flat_q() const { return m_flat_q; } bool unary_function_fragment() const { unsigned sz = m_ng_decls.size(); if (sz > 1) return false; if (sz == 0) return true; func_decl * f = *(m_ng_decls.begin()); return f->get_arity() == 1; } bool has_cond_macros() const { return !m_cond_macros.empty(); } macro_iterator begin_macros() const { return m_cond_macros.begin(); } macro_iterator end_macros() const { return m_cond_macros.end(); } void set_the_one(func_decl * m) { m_the_one = m; } func_decl * get_the_one() const { return m_the_one; } bool contains_ng_decl(func_decl * f) const { return m_ng_decls.contains(f); } void display(std::ostream & out) const { ast_manager & m = m_flat_q.get_manager(); out << "info for (flat) quantifier:\n" << mk_pp(m_flat_q.get(), m) << "\n"; out << "IS_AUF: " << m_is_auf << ", has x=y: " << m_has_x_eq_y << "\n"; out << "unary function fragment: " << unary_function_fragment() << "\n"; out << "ng decls: "; func_decl_set::iterator it1 = m_ng_decls.begin(); func_decl_set::iterator end1 = m_ng_decls.end(); for (; it1 != end1; ++it1) { out << (*it1)->get_name() << " "; } out << "\ninfo bits:\n"; ptr_vector::const_iterator it2 = m_qinfo_vect.begin(); ptr_vector::const_iterator end2 = m_qinfo_vect.end(); for (; it2 != end2; ++it2) { out << " "; (*it2)->display(out); out << "\n"; } out << "\nmacros:\n"; ptr_vector::const_iterator it3 = m_cond_macros.begin(); ptr_vector::const_iterator end3 = m_cond_macros.end(); for (; it3 != end3; ++it3) { out << " "; (*it3)->display(out); out << "\n"; } } void process_auf(auf_solver & s, context * ctx) { for (unsigned i = 0; i < m_flat_q->get_num_decls(); i++) { // make sure a node exists for each variable. s.get_uvar(m_flat_q, i); } ptr_vector::const_iterator it = m_qinfo_vect.begin(); ptr_vector::const_iterator end = m_qinfo_vect.end(); for (; it != end; ++it) { (*it)->process_auf(m_flat_q, s, ctx); } } void populate_inst_sets(auf_solver & s, context * ctx) { ptr_vector::const_iterator it = m_qinfo_vect.begin(); ptr_vector::const_iterator end = m_qinfo_vect.end(); for (; it != end; ++it) (*it)->populate_inst_sets(m_flat_q, s, ctx); // second pass it = m_qinfo_vect.begin(); for (; it != end; ++it) (*it)->populate_inst_sets2(m_flat_q, s, ctx); } func_decl_set const & get_ng_decls() const { return m_ng_decls; } void populate_macro_based_inst_sets(context * ctx, evaluator & ev) { SASSERT(m_the_one != 0); if (m_uvar_inst_sets != 0) return; m_uvar_inst_sets = alloc(ptr_vector); ptr_vector::const_iterator it = m_qinfo_vect.begin(); ptr_vector::const_iterator end = m_qinfo_vect.end(); for (; it != end; ++it) (*it)->populate_inst_sets(m_flat_q, m_the_one, *m_uvar_inst_sets, ctx); ptr_vector::iterator it2 = m_uvar_inst_sets->begin(); ptr_vector::iterator end2 = m_uvar_inst_sets->end(); for (; it2 != end2; ++it2) { instantiation_set * s = *it2; if (s != 0) s->mk_inverse(ev); } } instantiation_set * get_macro_based_inst_set(unsigned vidx, context * ctx, evaluator & ev) { if (m_the_one == 0) return 0; populate_macro_based_inst_sets(ctx, ev); return m_uvar_inst_sets->get(vidx, 0); } void reset_the_one() { m_the_one = 0; if (m_uvar_inst_sets) { std::for_each(m_uvar_inst_sets->begin(), m_uvar_inst_sets->end(), delete_proc()); dealloc(m_uvar_inst_sets); m_uvar_inst_sets = 0; } } void set_cancel(bool f) { m_cancel = f; } }; /** \brief Functor used to traverse/analyze a quantifier and fill the structure quantifier_info. */ class quantifier_analyzer { ast_manager & m_manager; macro_util m_mutil; array_util m_array_util; arith_util m_arith_util; bv_util m_bv_util; bool m_cancel; quantifier_info * m_info; typedef enum { POS, NEG } polarity; polarity neg(polarity p) { return p == POS ? NEG : POS; } obj_hashtable m_pos_cache; obj_hashtable m_neg_cache; typedef std::pair entry; svector m_ftodo; ptr_vector m_ttodo; void insert_qinfo(qinfo * qi) { SASSERT(m_info); m_info->insert_qinfo(qi); } arith_simplifier_plugin * get_arith_simp() const { return m_mutil.get_arith_simp(); } bv_simplifier_plugin * get_bv_simp() const { return m_mutil.get_bv_simp(); } bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) const { return get_arith_simp()->is_var_plus_ground(n, inv, v, t) || get_bv_simp()->is_var_plus_ground(n, inv, v, t); } bool is_var_plus_ground(expr * n, var * & v, expr_ref & t) { bool inv; TRACE("is_var_plus_ground", tout << mk_pp(n, m_manager) << "\n"; tout << "is_var_plus_ground: " << is_var_plus_ground(n, inv, v, t) << "\n"; tout << "inv: " << inv << "\n";); return is_var_plus_ground(n, inv, v, t) && !inv; } bool is_add(expr * n) const { return m_mutil.is_add(n); } bool is_zero(expr * n) const { if (get_bv_simp()->is_bv(n)) return get_bv_simp()->is_zero_safe(n); else return get_arith_simp()->is_zero_safe(n); } bool is_times_minus_one(expr * n, expr * & arg) const { return m_mutil.is_times_minus_one(n, arg); } bool is_le(expr * n) const { return m_mutil.is_le(n); } bool is_le_ge(expr * n) const { return m_mutil.is_le_ge(n); } bool is_signed_le(expr * n) const { return m_bv_util.is_bv_sle(n); } expr * mk_one(sort * s) { return m_bv_util.is_bv_sort(s) ? m_bv_util.mk_numeral(rational(1), s) : m_arith_util.mk_numeral(rational(1), s); } void mk_sub(expr * t1, expr * t2, expr_ref & r) const { m_mutil.mk_sub(t1, t2, r); } void mk_add(expr * t1, expr * t2, expr_ref & r) const { m_mutil.mk_add(t1, t2, r); } bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t, bool & inv) const { inv = false; // true if invert the sign TRACE("is_var_and_ground", tout << "is_var_and_ground: " << mk_ismt2_pp(lhs, m_manager) << " " << mk_ismt2_pp(rhs, m_manager) << "\n";); if (is_var(lhs) && is_ground(rhs)) { v = to_var(lhs); t = rhs; TRACE("is_var_and_ground", tout << "var and ground\n";); return true; } else if (is_var(rhs) && is_ground(lhs)) { v = to_var(rhs); t = lhs; TRACE("is_var_and_ground", tout << "ground and var\n";); return true; } else { expr_ref tmp(m_manager); if (is_var_plus_ground(lhs, inv, v, tmp) && is_ground(rhs)) { if (inv) mk_sub(tmp, rhs, t); else mk_sub(rhs, tmp, t); return true; } if (is_var_plus_ground(rhs, inv, v, tmp) && is_ground(lhs)) { if (inv) mk_sub(tmp, lhs, t); else mk_sub(lhs, tmp, t); return true; } } return false; } bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t) const { bool inv; return is_var_and_ground(lhs, rhs, v, t, inv); } bool is_x_eq_t_atom(expr * n, var * & v, expr_ref & t) const { if (!is_app(n)) return false; if (m_manager.is_eq(n)) return is_var_and_ground(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v, t); return false; } bool is_var_minus_var(expr * n, var * & v1, var * & v2) const { if (!is_add(n)) return false; expr * arg1 = to_app(n)->get_arg(0); expr * arg2 = to_app(n)->get_arg(1); if (!is_var(arg1)) std::swap(arg1, arg2); if (!is_var(arg1)) return false; expr * arg2_2; if (!is_times_minus_one(arg2, arg2_2)) return false; if (!is_var(arg2_2)) return false; v1 = to_var(arg1); v2 = to_var(arg2_2); return true; } bool is_var_and_var(expr * lhs, expr * rhs, var * & v1, var * & v2) const { if (is_var(lhs) && is_var(rhs)) { v1 = to_var(lhs); v2 = to_var(rhs); return true; } return (is_var_minus_var(lhs, v1, v2) && is_zero(rhs)) || (is_var_minus_var(rhs, v1, v2) && is_zero(lhs)); } bool is_x_eq_y_atom(expr * n, var * & v1, var * & v2) const { return m_manager.is_eq(n) && is_var_and_var(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v1, v2); } bool is_x_gle_y_atom(expr * n, var * & v1, var * & v2) const { return is_le_ge(n) && is_var_and_var(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v1, v2); } bool is_x_gle_t_atom(expr * atom, bool sign, var * & v, expr_ref & t) { if (!is_app(atom)) return false; if (sign) { bool r = is_le_ge(atom) && is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, t); CTRACE("is_x_gle_t", r, tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m_manager) << "\n--->\n" << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n"; tout << "sign: " << sign << "\n";); return r; } else { if (is_le_ge(atom)) { expr_ref tmp(m_manager); bool le = is_le(atom); bool inv = false; if (is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, tmp, inv)) { if (inv) le = !le; sort * s = m_manager.get_sort(tmp); expr_ref one(m_manager); one = mk_one(s); if (le) mk_add(tmp, one, t); else mk_sub(tmp, one, t); TRACE("is_x_gle_t", tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m_manager) << "\n--->\n" << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n"; tout << "sign: " << sign << "\n";); return true; } } return false; } } void reset_cache() { m_pos_cache.reset(); m_neg_cache.reset(); } obj_hashtable & get_cache(polarity pol) { return pol == POS ? m_pos_cache : m_neg_cache; } void visit_formula(expr * n, polarity pol) { if (is_ground(n)) return; // ground terms do not need to be visited. obj_hashtable & c = get_cache(pol); if (!c.contains(n)) { m_ftodo.push_back(entry(n, pol)); c.insert(n); } } void visit_term(expr * n) { // ground terms do not need to be visited. if (!is_ground(n) && !m_pos_cache.contains(n)) { m_ttodo.push_back(n); m_pos_cache.insert(n); } } /** \brief Process unintrepreted applications. */ void process_u_app(app * t) { unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); if (is_var(arg)) { SASSERT(t->get_decl()->get_domain(i) == to_var(arg)->get_sort()); insert_qinfo(alloc(f_var, t->get_decl(), i, to_var(arg)->get_idx())); continue; } var * v; expr_ref k(m_manager); if (is_var_plus_ground(arg, v, k)) { insert_qinfo(alloc(f_var_plus_offset, m_manager, t->get_decl(), i, v->get_idx(), k.get())); continue; } visit_term(arg); } } /** \brief A term \c t is said to be a auf_select if it is of ther form (select a i) Where: where a is ground or is_auf_select(a) == true and the indices are ground terms or variables. */ bool is_auf_select(expr * t) const { if (!m_array_util.is_select(t)) return false; expr * a = to_app(t)->get_arg(0); if (!is_ground(a) && !is_auf_select(a)) return false; unsigned num_args = to_app(t)->get_num_args(); for (unsigned i = 1; i < num_args; i++) { expr * arg = to_app(t)->get_arg(i); if (!is_ground(arg) && !is_var(arg)) return false; } return true; } /** \brief Process intrepreted applications. */ void process_i_app(app * t) { if (is_auf_select(t)) { unsigned num_args = t->get_num_args(); app * array = to_app(t->get_arg(0)); visit_term(array); // array may be a nested array. for (unsigned i = 1; i < num_args; i++) { expr * arg = t->get_arg(i); if (is_var(arg)) { insert_qinfo(alloc(select_var, m_manager, t, i, to_var(arg)->get_idx())); } else { SASSERT(is_ground(arg)); } } } else { unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; i++) visit_term(t->get_arg(i)); } } void process_app(app * t) { SASSERT(!is_ground(t)); if (t->get_family_id() != m_manager.get_basic_family_id()) { m_info->m_ng_decls.insert(t->get_decl()); } if (is_uninterp(t)) { process_u_app(t); } else { process_i_app(t); } } void process_terms_on_stack() { while (!m_ttodo.empty()) { expr * curr = m_ttodo.back(); m_ttodo.pop_back(); if (m_manager.is_bool(curr)) { // formula nested in a term. visit_formula(curr, POS); visit_formula(curr, NEG); continue; } if (is_app(curr)) { process_app(to_app(curr)); } else if (is_var(curr)) { m_info->m_is_auf = false; // unexpected occurrence of variable. } else { SASSERT(is_quantifier(curr)); // no nested quantifiers UNREACHABLE(); } } } void process_literal(expr * atom, bool sign) { CTRACE("model_finder_bug", is_ground(atom), tout << mk_pp(atom, m_manager) << "\n";); SASSERT(!is_ground(atom)); SASSERT(m_manager.is_bool(atom)); if (is_var(atom)) { if (sign) { // atom (not X) can be viewed as X != true insert_qinfo(alloc(x_neq_t, m_manager, to_var(atom)->get_idx(), m_manager.mk_true())); } else { // atom X can be viewed as X != false insert_qinfo(alloc(x_neq_t, m_manager, to_var(atom)->get_idx(), m_manager.mk_false())); } return; } if (is_app(atom)) { var * v, * v1, * v2; expr_ref t(m_manager); if (is_x_eq_t_atom(atom, v, t)) { if (sign) insert_qinfo(alloc(x_neq_t, m_manager, v->get_idx(), t)); else insert_qinfo(alloc(x_eq_t, m_manager, v->get_idx(), t)); } else if (is_x_eq_y_atom(atom, v1, v2)) { if (sign) insert_qinfo(alloc(x_neq_y, v1->get_idx(), v2->get_idx())); else { m_info->m_has_x_eq_y = true; // this atom is in the fringe of AUF insert_qinfo(alloc(x_eq_y, v1->get_idx(), v2->get_idx())); } } else if (sign && is_x_gle_y_atom(atom, v1, v2)) { if (is_signed_le(atom)) insert_qinfo(alloc(x_sleq_y, v1->get_idx(), v2->get_idx())); else insert_qinfo(alloc(x_leq_y, v1->get_idx(), v2->get_idx())); } else if (is_x_gle_t_atom(atom, sign, v, t)) { insert_qinfo(alloc(x_gle_t, m_manager, v->get_idx(), t)); } else { process_app(to_app(atom)); } return; } SASSERT(is_quantifier(atom)); UNREACHABLE(); } void process_literal(expr * atom, polarity pol) { process_literal(atom, pol == NEG); } void process_or(app * n, polarity p) { unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) visit_formula(n->get_arg(i), p); } void process_ite(app * n, polarity p) { visit_formula(n->get_arg(0), p); visit_formula(n->get_arg(0), neg(p)); visit_formula(n->get_arg(1), p); visit_formula(n->get_arg(2), p); } void process_iff(app * n) { visit_formula(n->get_arg(0), POS); visit_formula(n->get_arg(0), NEG); visit_formula(n->get_arg(1), POS); visit_formula(n->get_arg(1), NEG); } void checkpoint() { cooperate("quantifier_analyzer"); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } void process_formulas_on_stack() { while (!m_ftodo.empty()) { checkpoint(); entry & e = m_ftodo.back(); expr * curr = e.first; polarity pol = e.second; m_ftodo.pop_back(); if (is_app(curr)) { if (to_app(curr)->get_family_id() == m_manager.get_basic_family_id() && m_manager.is_bool(curr)) { switch (static_cast(to_app(curr)->get_decl_kind())) { case OP_AND: case OP_IMPLIES: case OP_XOR: UNREACHABLE(); // simplifier eliminated ANDs, IMPLIEs, and XORs break; case OP_OR: process_or(to_app(curr), pol); break; case OP_NOT: visit_formula(to_app(curr)->get_arg(0), neg(pol)); break; case OP_ITE: process_ite(to_app(curr), pol); break; case OP_IFF: process_iff(to_app(curr)); break; case OP_EQ: if (m_manager.is_bool(to_app(curr)->get_arg(0))) { process_iff(to_app(curr)); } else { process_literal(curr, pol); } break; default: process_literal(curr, pol); break; } } else { process_literal(curr, pol); } } else if (is_var(curr)) { SASSERT(m_manager.is_bool(curr)); process_literal(curr, pol); } else { SASSERT(is_quantifier(curr)); UNREACHABLE(); // can't happen, the quantifier is supposed to be flat. } } } void process_formula(expr * n) { SASSERT(m_manager.is_bool(n)); visit_formula(n, POS); } void process_clause(expr * cls) { SASSERT(is_clause(m_manager, cls)); unsigned num_lits = get_clause_num_literals(m_manager, cls); for (unsigned i = 0; i < num_lits; i++) { expr * lit = get_clause_literal(m_manager, cls, i); SASSERT(is_literal(m_manager, lit)); expr * atom; bool sign; get_literal_atom_sign(m_manager, lit, atom, sign); if (!is_ground(atom)) process_literal(atom, sign); } } void collect_macro_candidates(quantifier * q) { macro_util::macro_candidates candidates(m_manager); m_mutil.collect_macro_candidates(q, candidates); unsigned num_candidates = candidates.size(); for (unsigned i = 0; i < num_candidates; i++) { cond_macro * m = alloc(cond_macro, m_manager, candidates.get_f(i), candidates.get_def(i), candidates.get_cond(i), candidates.ineq(i), candidates.satisfy_atom(i), candidates.hint(i), q->get_weight()); m_info->insert_macro(m); } } public: quantifier_analyzer(ast_manager & m, simplifier & s): m_manager(m), m_mutil(m, s), m_array_util(m), m_arith_util(m), m_bv_util(m), m_cancel(false), m_info(0) { } void operator()(quantifier_info * d) { m_info = d; quantifier * q = d->get_flat_q(); expr * e = q->get_expr(); SASSERT(!has_quantifiers(e)); reset_cache(); SASSERT(m_ttodo.empty()); SASSERT(m_ftodo.empty()); if (is_clause(m_manager, e)) { process_clause(e); } else { process_formula(e); } while (!m_ftodo.empty() || !m_ttodo.empty()) { process_formulas_on_stack(); process_terms_on_stack(); } collect_macro_candidates(q); m_info = 0; } void set_cancel(bool f) { m_cancel = f; if (m_info) m_info->set_cancel(f); } }; /** \brief Base class for macro solvers. */ class base_macro_solver { protected: ast_manager & m_manager; obj_map const & m_q2info; proto_model * m_model; quantifier_info * get_qinfo(quantifier * q) const { quantifier_info * qi = 0; m_q2info.find(q, qi); SASSERT(qi != 0); return qi; } void set_else_interp(func_decl * f, expr * f_else) { SASSERT(f_else != 0); func_interp * fi = m_model->get_func_interp(f); if (fi == 0) { fi = alloc(func_interp, m_manager, f->get_arity()); m_model->register_decl(f, fi); } fi->set_else(f_else); } virtual bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) = 0; public: base_macro_solver(ast_manager & m, obj_map const & q2i): m_manager(m), m_q2info(q2i), m_model(0) { } virtual ~base_macro_solver() {} /** \brief Try to satisfy quantifiers in qs by using macro definitions. Store in new_qs the quantifiers that were not satisfied. Store in residue a subset of the quantifiers that were satisfied but contain information useful for the auf_solver. */ void operator()(proto_model * m, ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) { m_model = m; ptr_vector curr_qs(qs); while (process(curr_qs, new_qs, residue)) { curr_qs.swap(new_qs); new_qs.reset(); } } }; /** \brief The simple macro solver satisfies quantifiers that contain (conditional) macros for a function f that does not occur in any other quantifier. Since f does not occur in any other quantifier, I don't need to track the dependencies of f. That is, recursive definition cannot be created. */ class simple_macro_solver : public base_macro_solver { protected: /** \brief Return true if \c f is in (qs\{q}) */ bool contains(func_decl * f, ptr_vector const & qs, quantifier * q) { ptr_vector::const_iterator it = qs.begin(); ptr_vector::const_iterator end = qs.end(); for (; it != end; ++it) { quantifier * other = *it; if (q == other) continue; quantifier_info * other_qi = get_qinfo(other); if (other_qi->contains_ng_decl(f)) return true; } return false; } bool process(quantifier * q, ptr_vector const & qs) { quantifier_info * qi = get_qinfo(q); quantifier_info::macro_iterator it = qi->begin_macros(); quantifier_info::macro_iterator end = qi->end_macros(); for (; it != end; ++it) { cond_macro * m = *it; if (!m->satisfy_atom()) continue; func_decl * f = m->get_f(); if (!contains(f, qs, q)) { qi->set_the_one(f); expr * f_else = m->get_def(); SASSERT(f_else != 0); // Remark: I can ignore the conditions of m because // I know the (partial) interpretation of f satisfied the ground part. // MBQI will force extra instantiations if the the (partial) interpretation of f // does not satisfy the quantifier. // In all other cases the "else" of f will satisfy the quantifier. set_else_interp(f, f_else); TRACE("model_finder", tout << "satisfying the quantifier using simple macro:\n"; m->display(tout); tout << "\n";); return true; // satisfied quantifier } } return false; } virtual bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) { bool removed = false; ptr_vector::const_iterator it = qs.begin(); ptr_vector::const_iterator end = qs.end(); for (; it != end; ++it) { if (process(*it, qs)) removed = true; else new_qs.push_back(*it); } return removed; } public: simple_macro_solver(ast_manager & m, obj_map const & q2i): base_macro_solver(m, q2i) {} }; class hint_solver : public base_macro_solver { /* This solver tries to satisfy quantifiers by using macros, cond_macros and hints. The idea is to satisfy a set of quantifiers Q = Q_{f_1} union ... union Q_{f_n} where Q_{f_i} is the set of quantifiers that contain the function f_i. Let f_i = def_i be macros (in this solver conditions are ignored). Let Q_{f_i = def_i} be the set of quantifiers where f_i = def_i is a macro. Then, the set Q can be satisfied using f_1 = def_1 ... f_n = d_n when Q_{f_1} union ... union Q_{f_n} = Q_{f_1 = def_1} ... Q_{f_n = d_n} (*) So, given a set of macros f_1 = def_1, ..., f_n = d_n, it is very easy to check whether they can be used to satisfy all quantifiers that use f_1, ..., f_n in non ground terms. We can find the sets of f_1 = def_1, ..., f_n = def_n that satisfy Q using the following search procedure find(Q) for each f_i = def_i in Q R = greedy(Q_{f_i = def_1}, Q_f_i \ Q_{f_i = def_i}, {f_i}, {f_i = def_i}) if (R != empty-set) return R greedy(Satisfied, Residue, F, F_DEF) if Residue = empty-set return F_DEF for each f_j = def_j in Residue such that f_j not in F New-Satisfied = Satisfied union Q_{f_j = def_j} New-Residue = (Residue union Q_{f_j}) \ New-Satisfied R = greedy(New-Satisfied, New-Residue, F \union {f_j}, F_DEF union {f_j = def_j}) if (R != empty-set) return R This search may be too expensive, and is exponential on the number of different function symbols. Some observations to prune the search space. 1) If f_i occurs in a quantifier without macros, then f_i and any macro using it can be ignored during the search. 2) If f_i = def_i is not a macro in a quantifier q, and there is no other f_j = def_j (i != j) in q, then f_i = def_i can be ignored during the search. */ typedef obj_hashtable quantifier_set; typedef obj_map q_f; typedef obj_pair_map q_f_def; typedef obj_pair_hashtable f_def_set; typedef obj_hashtable expr_set; typedef obj_map f2defs; q_f m_q_f; q_f_def m_q_f_def; ptr_vector m_qsets; f2defs m_f2defs; ptr_vector m_esets; void insert_q_f(quantifier * q, func_decl * f) { SASSERT(!m_forbidden.contains(f)); quantifier_set * s = 0; if (!m_q_f.find(f, s)) { s = alloc(quantifier_set); m_q_f.insert(f, s); m_qsets.push_back(s); } SASSERT(s != 0); s->insert(q); } void insert_f2def(func_decl * f, expr * def) { expr_set * s = 0; if (!m_f2defs.find(f, s)) { s = alloc(expr_set); m_f2defs.insert(f, s); m_esets.push_back(s); } SASSERT(s != 0); s->insert(def); } void insert_q_f_def(quantifier * q, func_decl * f, expr * def) { SASSERT(!m_forbidden.contains(f)); quantifier_set * s = 0; if (!m_q_f_def.find(f, def, s)) { s = alloc(quantifier_set); m_q_f_def.insert(f, def, s); insert_f2def(f, def); m_qsets.push_back(s); } SASSERT(s != 0); s->insert(q); } quantifier_set * get_q_f(func_decl * f) { quantifier_set * s = 0; m_q_f.find(f, s); SASSERT(s != 0); return s; } quantifier_set * get_q_f_def(func_decl * f, expr * def) { quantifier_set * s = 0; m_q_f_def.find(f, def, s); SASSERT(s != 0); return s; } expr_set * get_f_defs(func_decl * f) { expr_set * s = 0; m_f2defs.find(f, s); SASSERT(s != 0); return s; } void reset_q_fs() { std::for_each(m_qsets.begin(), m_qsets.end(), delete_proc()); std::for_each(m_esets.begin(), m_esets.end(), delete_proc()); m_q_f.reset(); m_q_f_def.reset(); m_qsets.reset(); m_f2defs.reset(); m_esets.reset(); } func_decl_set m_forbidden; func_decl_set m_candidates; bool is_candidate(quantifier * q) const { quantifier_info * qi = get_qinfo(q); quantifier_info::macro_iterator it = qi->begin_macros(); quantifier_info::macro_iterator end = qi->end_macros(); for (; it != end; ++it) { cond_macro * m = *it; if (m->satisfy_atom() && !m_forbidden.contains(m->get_f())) return true; } return false; } void register_decls_as_forbidden(quantifier * q) { quantifier_info * qi = get_qinfo(q); func_decl_set const & ng_decls = qi->get_ng_decls(); func_decl_set::iterator it = ng_decls.begin(); func_decl_set::iterator end = ng_decls.end(); for (; it != end; ++it) { m_forbidden.insert(*it); } } void preprocess(ptr_vector const & qs, ptr_vector & qcandidates, ptr_vector & non_qcandidates) { ptr_vector curr(qs); while (true) { ptr_vector::iterator it = curr.begin(); ptr_vector::iterator end = curr.end(); for (; it != end; ++it) { quantifier * q = *it; if (is_candidate(q)) { qcandidates.push_back(q); } else { register_decls_as_forbidden(q); non_qcandidates.push_back(q); } } if (curr.size() == qcandidates.size()) return; SASSERT(qcandidates.size() < curr.size()); curr.swap(qcandidates); qcandidates.reset(); } } void mk_q_f_defs(ptr_vector const & qs) { ptr_vector::const_iterator it = qs.begin(); ptr_vector::const_iterator end = qs.end(); for (; it != end; ++it) { quantifier * q = *it; quantifier_info * qi = get_qinfo(q); func_decl_set const & ng_decls = qi->get_ng_decls(); func_decl_set::iterator it2 = ng_decls.begin(); func_decl_set::iterator end2 = ng_decls.end(); for (; it2 != end2; ++it2) { func_decl * f = *it2; if (!m_forbidden.contains(f)) insert_q_f(q, f); } quantifier_info::macro_iterator it3 = qi->begin_macros(); quantifier_info::macro_iterator end3 = qi->end_macros(); for (; it3 != end3; ++it3) { cond_macro * m = *it3; if (m->satisfy_atom() && !m_forbidden.contains(m->get_f())) { insert_q_f_def(q, m->get_f(), m->get_def()); m_candidates.insert(m->get_f()); } } } } static void display_quantifier_set(std::ostream & out, quantifier_set const * s) { quantifier_set::iterator it = s->begin(); quantifier_set::iterator end = s->end(); for (; it != end; ++it) { quantifier * q = *it; out << q->get_qid() << " "; } out << "\n"; } void display_qcandidates(std::ostream & out, ptr_vector const & qcandidates) const { ptr_vector::const_iterator it1 = qcandidates.begin(); ptr_vector::const_iterator end1 = qcandidates.end(); for (; it1 != end1; ++it1) { quantifier * q = *it1; out << q->get_qid() << " ->\n" << mk_pp(q, m_manager) << "\n"; quantifier_info * qi = get_qinfo(q); qi->display(out); out << "------\n"; } out << "Sets Q_f\n"; q_f::iterator it2 = m_q_f.begin(); q_f::iterator end2 = m_q_f.end(); for (; it2 != end2; ++it2) { func_decl * f = (*it2).m_key; quantifier_set * s = (*it2).m_value; out << f->get_name() << " -> "; display_quantifier_set(out, s); } out << "Sets Q_{f = def}\n"; q_f_def::iterator it3 = m_q_f_def.begin(); q_f_def::iterator end3 = m_q_f_def.end(); for (; it3 != end3; ++it3) { func_decl * f = (*it3).get_key1(); expr * def = (*it3).get_key2(); quantifier_set * s = (*it3).get_value(); out << f->get_name() << " " << mk_pp(def, m_manager) << " ->\n"; display_quantifier_set(out, s); } } // // Search: main procedures // struct ev_handler { hint_solver * m_owner; void operator()(quantifier * q, bool ins) { quantifier_info * qi = m_owner->get_qinfo(q); qi->set_the_one(0); } ev_handler(hint_solver * o): m_owner(o) { } }; typedef backtrackable_set qset; typedef backtrackable_set qsset; typedef obj_map f2def; qset m_residue; qsset m_satisfied; f2def m_fs; // set of function symbols (and associated interpretation) that were used to satisfy the quantifiers in m_satisfied. struct found_satisfied_subset {}; void display_search_state(std::ostream & out) const { out << "fs:\n"; f2def::iterator it3 = m_fs.begin(); f2def::iterator end3 = m_fs.end(); for (; it3 != end3; ++it3) { out << (*it3).m_key->get_name() << " "; } out << "\nsatisfied:\n"; qsset::iterator it = m_satisfied.begin(); qsset::iterator end = m_satisfied.end(); for (; it != end; ++it) { out << (*it)->get_qid() << " "; } out << "\nresidue:\n"; qset::iterator it2 = m_residue.begin(); qset::iterator end2 = m_residue.end(); for (; it2 != end2; ++it2) { out << (*it2)->get_qid() << " "; } out << "\n"; } bool check_satisfied_residue_invariant() { qsset::iterator it = m_satisfied.begin(); qsset::iterator end = m_satisfied.end(); for (; it != end; ++it) { quantifier * q = *it; SASSERT(!m_residue.contains(q)); quantifier_info * qi = get_qinfo(q); SASSERT(qi != 0); SASSERT(qi->get_the_one() != 0); } return true; } bool update_satisfied_residue(func_decl * f, expr * def) { bool useful = false; SASSERT(check_satisfied_residue_invariant()); quantifier_set * q_f = get_q_f(f); quantifier_set * q_f_def = get_q_f_def(f, def); quantifier_set::iterator it = q_f_def->begin(); quantifier_set::iterator end = q_f_def->end(); for (; it != end; ++it) { quantifier * q = *it; if (!m_satisfied.contains(q)) { useful = true; m_residue.erase(q); m_satisfied.insert(q); quantifier_info * qi = get_qinfo(q); SASSERT(qi->get_the_one() == 0); qi->set_the_one(f); // remark... event handler will reset it during backtracking. } } if (!useful) return false; it = q_f->begin(); end = q_f->end(); for (; it != end; ++it) { quantifier * q = *it; if (!m_satisfied.contains(q)) { m_residue.insert(q); } } SASSERT(check_satisfied_residue_invariant()); return true; } /** \brief Extract from m_residue, func_decls that can be used as macros to satisfy it. The candidates must not be elements of m_fs. */ void get_candidates_from_residue(func_decl_set & candidates) { qset::iterator it = m_residue.begin(); qset::iterator end = m_residue.end(); for (; it != end; ++it) { quantifier * q = *it; quantifier_info * qi = get_qinfo(q); quantifier_info::macro_iterator it2 = qi->begin_macros(); quantifier_info::macro_iterator end2 = qi->end_macros(); for (; it2 != end2; ++it2) { cond_macro * m = *it2; func_decl * f = m->get_f(); if (m->satisfy_atom() && !m_forbidden.contains(f) && !m_fs.contains(f)) { candidates.insert(f); } } } } #define GREEDY_MAX_DEPTH 10 /* to avoid too expensive search spaces */ /** \brief Try to reduce m_residue using the macros of f. */ void greedy(func_decl * f, unsigned depth) { if (depth >= GREEDY_MAX_DEPTH) return; // failed TRACE("model_finder_hint", tout << "greedy depth: " << depth << ", f: " << f->get_name() << "\n"; display_search_state(tout);); expr_set * s = get_f_defs(f); expr_set::iterator it = s->begin(); expr_set::iterator end = s->end(); for (; it != end; ++it) { expr * def = *it; SASSERT(!m_fs.contains(f)); m_satisfied.push_scope(); m_residue.push_scope(); m_fs.insert(f, def); if (update_satisfied_residue(f, def)) { // update was useful greedy(depth + 1); // greedy throws exception in case of success // reachable iff greedy failed. } m_satisfied.pop_scope(); m_residue.pop_scope(); m_fs.erase(f); } } /** \brief Try to reduce m_residue (if not empty) by selecting a function f that is a macro in the residue. */ void greedy(unsigned depth) { if (m_residue.empty()) { TRACE("model_finder_hint", tout << "found subset that is satisfied by macros\n"; display_search_state(tout);); throw found_satisfied_subset(); } func_decl_set candidates; get_candidates_from_residue(candidates); TRACE("model_finder_hint", tout << "candidates from residue:\n"; func_decl_set::iterator it = candidates.begin(); func_decl_set::iterator end = candidates.end(); for (; it != end; ++it) { tout << (*it)->get_name() << " "; } tout << "\n";); func_decl_set::iterator it = candidates.begin(); func_decl_set::iterator end = candidates.end(); for (; it != end; ++it) { greedy(*it, depth); } } /** \brief Try to find a set of quantifiers by starting to use the macros of f. This is the "find" procedure in the comments above. The set of satisfied quantifiers is in m_satisfied, and the remaining to be satisfied in m_residue. When the residue becomes empty we throw the exception found_satisfied_subset. */ void process(func_decl * f) { SASSERT(m_satisfied.empty()); SASSERT(m_residue.empty()); greedy(f, 0); } /** \brief Copy the quantifiers from qcandidates to new_qs that are not in m_satisfied. */ void copy_non_satisfied(ptr_vector const & qcandidates, ptr_vector & new_qs) { ptr_vector::const_iterator it = qcandidates.begin(); ptr_vector::const_iterator end = qcandidates.end(); for (; it != end; ++it) { quantifier * q = *it; if (!m_satisfied.contains(q)) new_qs.push_back(q); } } /** \brief Use m_fs to set the interpreation of the function symbols that were used to satisfy the quantifiers in m_satisfied. */ void set_interp() { f2def::iterator it = m_fs.begin(); f2def::iterator end = m_fs.end(); for (; it != end; ++it) { func_decl * f = (*it).m_key; expr * def = (*it).m_value; set_else_interp(f, def); } } void reset() { reset_q_fs(); m_forbidden.reset(); m_candidates.reset(); m_satisfied.reset(); m_residue.reset(); m_fs.reset(); } virtual bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) { reset(); ptr_vector qcandidates; preprocess(qs, qcandidates, new_qs); if (qcandidates.empty()) { SASSERT(new_qs.size() == qs.size()); return false; } mk_q_f_defs(qcandidates); TRACE("model_finder_hint", tout << "starting hint-solver search using:\n"; display_qcandidates(tout, qcandidates);); func_decl_set::iterator it = m_candidates.begin(); func_decl_set::iterator end = m_candidates.end(); for (; it != end; ++it) { func_decl * f = *it; try { process(f); } catch (found_satisfied_subset) { set_interp(); copy_non_satisfied(qcandidates, new_qs); return true; } } // failed... copy everything to new_qs new_qs.append(qcandidates); return false; } public: hint_solver(ast_manager & m, obj_map const & q2i): base_macro_solver(m, q2i), m_satisfied(ev_handler(this)) { } virtual ~hint_solver() { reset(); } }; /** \brief Satisfy clauses that are not in the AUF fragment but define conditional macros. These clauses are eliminated even if the symbol being defined occurs in other quantifiers. The auf_solver is ineffective in these clauses. \remark Full macros are used even if they are in the AUF fragment. */ class non_auf_macro_solver : public base_macro_solver { func_decl_dependencies & m_dependencies; qi_params const * m_qi_params; bool add_macro(func_decl * f, expr * f_else) { TRACE("non_auf_macro_solver", tout << "trying to add macro for " << f->get_name() << "\n" << mk_pp(f_else, m_manager) << "\n";); func_decl_set * s = m_dependencies.mk_func_decl_set(); m_dependencies.collect_ng_func_decls(f_else, s); if (!m_dependencies.insert(f, s)) { TRACE("non_auf_macro_solver", tout << "failed to add macro\n";); return false; // cyclic dependency } set_else_interp(f, f_else); return true; } // Return true if r1 is a better macro than r2. bool is_better_macro(cond_macro * r1, cond_macro * r2) { if (r2 == 0 || !r1->is_hint()) return true; if (!r2->is_hint()) return false; SASSERT(r1->is_hint() && r2->is_hint()); if (is_ground(r1->get_def()) && !is_ground(r2->get_def())) return true; return false; } cond_macro * get_macro_for(func_decl * f, quantifier * q) { cond_macro * r = 0; quantifier_info * qi = get_qinfo(q); quantifier_info::macro_iterator it = qi->begin_macros(); quantifier_info::macro_iterator end = qi->end_macros(); for (; it != end; ++it) { cond_macro * m = *it; if (m->get_f() == f && !m->is_hint() && is_better_macro(m, r)) r = m; } return r; } typedef std::pair mq_pair; void collect_candidates(ptr_vector const & qs, obj_map & full_macros, func_decl_set & cond_macros) { ptr_vector::const_iterator it = qs.begin(); ptr_vector::const_iterator end = qs.end(); for (; it != end; ++it) { quantifier * q = *it; quantifier_info * qi = get_qinfo(q); quantifier_info::macro_iterator it2 = qi->begin_macros(); quantifier_info::macro_iterator end2 = qi->end_macros(); for (; it2 != end2; ++it2) { cond_macro * m = *it2; if (!m->is_hint()) { func_decl * f = m->get_f(); TRACE("non_auf_macro_solver", tout << "considering macro for: " << f->get_name() << "\n"; m->display(tout); tout << "\n";); SASSERT(m_qi_params != 0); if (m->is_unconditional() && (!qi->is_auf() || m->get_weight() >= m_qi_params->m_mbqi_force_template)) { full_macros.insert(f, std::make_pair(m, q)); cond_macros.erase(f); } else if (!full_macros.contains(f) && !qi->is_auf()) cond_macros.insert(f); } } } } void process_full_macros(obj_map const & full_macros, obj_hashtable & removed) { obj_map::iterator it = full_macros.begin(); obj_map::iterator end = full_macros.end(); for (; it != end; ++it) { func_decl * f = (*it).m_key; cond_macro * m = (*it).m_value.first; quantifier * q = (*it).m_value.second; SASSERT(m->is_unconditional()); if (add_macro(f, m->get_def())) { get_qinfo(q)->set_the_one(f); removed.insert(q); } } } void process(func_decl * f, ptr_vector const & qs, obj_hashtable & removed) { expr_ref fi_else(m_manager); ptr_buffer to_remove; ptr_vector::const_iterator it = qs.begin(); ptr_vector::const_iterator end = qs.end(); for (; it != end; ++it) { quantifier * q = *it; if (removed.contains(q)) continue; cond_macro * m = get_macro_for(f, q); if (!m) continue; SASSERT(!m->is_hint()); if (m->is_unconditional()) return; // f is part of a full macro... ignoring it. to_remove.push_back(q); if (fi_else.get() == 0) { fi_else = m->get_def(); } else { fi_else = m_manager.mk_ite(m->get_cond(), m->get_def(), fi_else); } } if (fi_else.get() != 0 && add_macro(f, fi_else)) { ptr_buffer::iterator it2 = to_remove.begin(); ptr_buffer::iterator end2 = to_remove.end(); for (; it2 != end2; ++it2) { get_qinfo(*it2)->set_the_one(f); removed.insert(*it2); } } } void process_cond_macros(func_decl_set const & cond_macros, ptr_vector const & qs, obj_hashtable & removed) { func_decl_set::iterator it = cond_macros.begin(); func_decl_set::iterator end = cond_macros.end(); for (; it != end; ++it) { process(*it, qs, removed); } } virtual bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) { obj_map full_macros; func_decl_set cond_macros; obj_hashtable removed; // Possible improvement: sort full_macros & cond_macros using an user provided precedence function. collect_candidates(qs, full_macros, cond_macros); process_full_macros(full_macros, removed); process_cond_macros(cond_macros, qs, removed); ptr_vector::const_iterator it = qs.begin(); ptr_vector::const_iterator end = qs.end(); for (; it != end; ++it) { quantifier * q = *it; if (removed.contains(q)) continue; new_qs.push_back(q); residue.push_back(q); } return !removed.empty(); } public: non_auf_macro_solver(ast_manager & m, obj_map const & q2i, func_decl_dependencies & d): base_macro_solver(m, q2i), m_dependencies(d), m_qi_params(0) { } void set_params(qi_params const & p) { SASSERT(m_qi_params == 0); m_qi_params = &p; } }; }; // ----------------------------------- // // model finder // // ----------------------------------- model_finder::model_finder(ast_manager & m, simplifier & s): m_manager(m), m_context(0), m_analyzer(alloc(quantifier_analyzer, m, s)), m_auf_solver(alloc(auf_solver, m, s)), m_dependencies(m), m_sm_solver(alloc(simple_macro_solver, m, m_q2info)), m_hint_solver(alloc(hint_solver, m, m_q2info)), m_nm_solver(alloc(non_auf_macro_solver, m, m_q2info, m_dependencies)), m_cancel(false), m_new_constraints(m) { } model_finder::~model_finder() { reset(); } mf::quantifier_info * model_finder::get_quantifier_info(quantifier * q) const { quantifier_info * info = 0; m_q2info.find(q, info); SASSERT(info != 0); return info; } void model_finder::set_context(context * ctx) { SASSERT(m_context == 0); m_context = ctx; m_auf_solver->set_context(ctx); m_nm_solver->set_params(ctx->get_fparams()); } void model_finder::register_quantifier(quantifier * q) { TRACE("model_finder", tout << "registering:\n" << mk_pp(q, m_manager) << "\n";); quantifier_info * new_info = alloc(quantifier_info, m_manager, q); m_q2info.insert(q, new_info); m_quantifiers.push_back(q); m_analyzer->operator()(new_info); TRACE("model_finder", tout << "after analyzer:\n"; new_info->display(tout);); } void model_finder::push_scope() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_quantifiers_lim = m_quantifiers.size(); } void model_finder::restore_quantifiers(unsigned old_size) { unsigned curr_size = m_quantifiers.size(); SASSERT(old_size <= curr_size); for (unsigned i = old_size; i < curr_size; i++) { quantifier * q = m_quantifiers[i]; SASSERT(m_q2info.contains(q)); quantifier_info * info = get_quantifier_info(q); dealloc(info); m_q2info.erase(q); } m_quantifiers.shrink(old_size); } void model_finder::pop_scope(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; restore_quantifiers(s.m_quantifiers_lim); m_scopes.shrink(new_lvl); } void model_finder::reset() { m_scopes.reset(); m_dependencies.reset(); restore_quantifiers(0); SASSERT(m_q2info.empty()); SASSERT(m_quantifiers.empty()); } void model_finder::init_search_eh() { // do nothing in the current version } void model_finder::collect_relevant_quantifiers(ptr_vector & qs) const { ptr_vector::const_iterator it = m_quantifiers.begin(); ptr_vector::const_iterator end = m_quantifiers.end(); for (; it != end; ++it) { quantifier * q = *it; if (m_context->is_relevant(q) && m_context->get_assignment(q) == l_true) qs.push_back(q); } } void model_finder::process_auf(ptr_vector const & qs, proto_model * m) { m_auf_solver->reset(); m_auf_solver->set_model(m); ptr_vector::const_iterator it = qs.begin(); ptr_vector::const_iterator end = qs.end(); for (; it != end; ++it) { quantifier * q = *it; quantifier_info * qi = get_quantifier_info(q); qi->process_auf(*(m_auf_solver.get()), m_context); } m_auf_solver->mk_instantiation_sets(); it = qs.begin(); for (; it != end; ++it) { quantifier * q = *it; quantifier_info * qi = get_quantifier_info(q); qi->populate_inst_sets(*(m_auf_solver.get()), m_context); } m_auf_solver->fix_model(m_new_constraints); TRACE("model_finder", ptr_vector::const_iterator it = qs.begin(); ptr_vector::const_iterator end = qs.end(); for (; it != end; ++it) { quantifier * q = *it; quantifier_info * qi = get_quantifier_info(q); quantifier * fq = qi->get_flat_q(); tout << "#" << fq->get_id() << " ->\n" << mk_pp(fq, m_manager) << "\n"; } m_auf_solver->display_nodes(tout);); } void model_finder::process_simple_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m) { ptr_vector new_qs; m_sm_solver->operator()(m, qs, new_qs, residue); qs.swap(new_qs); TRACE("model_finder", tout << "model after processing simple macros:\n"; model_pp(tout, *m);); } void model_finder::process_hint_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m) { ptr_vector new_qs; m_hint_solver->operator()(m, qs, new_qs, residue); qs.swap(new_qs); TRACE("model_finder", tout << "model after processing simple macros:\n"; model_pp(tout, *m);); } void model_finder::process_non_auf_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m) { ptr_vector new_qs; m_nm_solver->operator()(m, qs, new_qs, residue); qs.swap(new_qs); TRACE("model_finder", tout << "model after processing non auf macros:\n"; model_pp(tout, *m);); } /** \brief Clean leftovers from previous invocations to fix_model. */ void model_finder::cleanup_quantifier_infos(ptr_vector const & qs) { ptr_vector::const_iterator it = qs.begin(); ptr_vector::const_iterator end = qs.end(); for (; it != end; ++it) { quantifier_info * qi = get_quantifier_info(*it); qi->reset_the_one(); } } /** \brief Try to satisfy quantifiers by modifying the model while preserving the satisfiability of all ground formulas asserted into the logical context. */ void model_finder::fix_model(proto_model * m) { if (m_quantifiers.empty()) return; ptr_vector qs; ptr_vector residue; collect_relevant_quantifiers(qs); if (qs.empty()) return; TRACE("model_finder", tout << "trying to satisfy quantifiers, given model:\n"; model_pp(tout, *m);); cleanup_quantifier_infos(qs); m_dependencies.reset(); process_simple_macros(qs, residue, m); process_hint_macros(qs, residue, m); process_non_auf_macros(qs, residue, m); qs.append(residue); process_auf(qs, m); } quantifier * model_finder::get_flat_quantifier(quantifier * q) const { quantifier_info * qinfo = get_quantifier_info(q); SASSERT(qinfo); return qinfo->get_flat_q(); } /** \brief Return the instantiation set associated with var i of q. \remark q is the quantifier before flattening. */ mf::instantiation_set const * model_finder::get_uvar_inst_set(quantifier * q, unsigned i) const { quantifier * flat_q = get_flat_quantifier(q); SASSERT(flat_q->get_num_decls() >= q->get_num_decls()); instantiation_set const * r = m_auf_solver->get_uvar_inst_set(flat_q, flat_q->get_num_decls() - q->get_num_decls() + i); TRACE("model_finder", tout << "q: #" << q->get_id() << "\n" << mk_pp(q,m_manager) << "\nflat_q: " << mk_pp(flat_q, m_manager) << "\ni: " << i << " " << flat_q->get_num_decls() - q->get_num_decls() + i << "\n";); if (r != 0) return r; // quantifier was not processed by AUF solver... // it must have been satisfied by "macro"/"hint". quantifier_info * qinfo = get_quantifier_info(q); SASSERT(qinfo); SASSERT(qinfo->get_the_one() != 0); return qinfo->get_macro_based_inst_set(i, m_context, *(m_auf_solver.get())); } /** \brief Return an expression in the instantiation-set of q:i that evaluates to val. \remark q is the quantifier before flattening. Store in generation the generation of the result */ expr * model_finder::get_inv(quantifier * q, unsigned i, expr * val, unsigned & generation) const { instantiation_set const * s = get_uvar_inst_set(q, i); if (s == 0) return 0; expr * t = s->get_inv(val); if (t != 0) { generation = s->get_generation(t); } return t; } /** \brief Assert constraints restricting the possible values of the skolem constants can be assigned to. The idea is to restrict them to the values in the instantiation sets. \remark q is the quantifier before flattening. Return true if something was asserted. */ bool model_finder::restrict_sks_to_inst_set(context * aux_ctx, quantifier * q, expr_ref_vector const & sks) { // Note: we currently add instances of q instead of flat_q. // If the user wants instances of flat_q, it should use PULL_NESTED_QUANTIFIERS=true. This option // will guarantee that q == flat_q. // // Since we only care about q (and its bindings), it only makes sense to restrict the variables of q. bool asserted_something = false; quantifier * flat_q = get_flat_quantifier(q); unsigned num_decls = q->get_num_decls(); unsigned flat_num_decls = flat_q->get_num_decls(); unsigned num_sks = sks.size(); // Remark: sks were created for the flat version of q. SASSERT(num_sks == flat_num_decls); SASSERT(flat_num_decls >= num_decls); SASSERT(num_sks >= num_decls); for (unsigned i = 0; i < num_decls; i++) { expr * sk = sks.get(num_decls - i - 1); instantiation_set const * s = get_uvar_inst_set(q, i); if (s == 0) continue; // nothing to do obj_map const & inv = s->get_inv_map(); if (inv.empty()) continue; // nothing to do ptr_buffer eqs; obj_map::iterator it = inv.begin(); obj_map::iterator end = inv.end(); for (; it != end; ++it) { expr * val = (*it).m_key; eqs.push_back(m_manager.mk_eq(sk, val)); } expr_ref new_cnstr(m_manager); new_cnstr = m_manager.mk_or(eqs.size(), eqs.c_ptr()); TRACE("model_finder", tout << "assert_restriction:\n" << mk_pp(new_cnstr, m_manager) << "\n";); aux_ctx->assert_expr(new_cnstr); asserted_something = true; } return asserted_something; } void model_finder::restart_eh() { unsigned sz = m_new_constraints.size(); if (sz > 0) { for (unsigned i = 0; i < sz; i++) { expr * c = m_new_constraints.get(i); TRACE("model_finder_bug_detail", tout << "asserting new constraint: " << mk_pp(c, m_manager) << "\n";); m_context->internalize(c, true); literal l(m_context->get_literal(c)); m_context->mark_as_relevant(l); // asserting it as an AXIOM m_context->assign(l, b_justification()); } m_new_constraints.reset(); } } void model_finder::set_cancel(bool f) { m_cancel = f; m_analyzer->set_cancel(f); } }; z3-z3-4.4.1/src/smt/smt_model_finder.h000066400000000000000000000117261260446376700175110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_finder.h Abstract: Model finding goodies for universally quantified formulas. During the search, the finder store information about the quantifiers that are internalized. In an ideal world, quantifiers are only internalized at base level. Given a satisfiable ground formula, Z3 will restrict the interpretation of uninterpreted functions in a finite subset of its domain. The model finder tries to produce a complete interpretation that will also satisfy all universally quantified formulas. During model construction, the model finder will complete the interpretation of uninterpreted functions by propagating basic constraints induced by the body of universally quantified formulas. More information can be found in the following papers: - Complete instantiation for quantified SMT formulas, Yeting Ge and Leonardo de Moura, Conference on Computer Aided Verification (CAV 2009), Grenoble, France, 2009. - Efficiently Solving Quantified Bit-Vector Formula, Christoph Wintersteiger, Youssef Hamadi and Leonardo de Moura, FMCAD, Lugano, Switzerland, 2010. - Bugs, Moles and Skeletons: Symbolic Reasoning for Software Development, Leonardo de Moura, Nikolaj Bjorner, IJCAR, Edinburgh, Scotland, 2010. Author: Leonardo de Moura (leonardo) 2010-12-17. Revision History: --*/ #ifndef SMT_MODEL_FINDER_H_ #define SMT_MODEL_FINDER_H_ #include"ast.h" #include"func_decl_dependencies.h" #include"simplifier.h" #include"proto_model.h" #include"cooperate.h" #include"tactic_exception.h" namespace smt { class context; namespace mf { class quantifier_info; class quantifier_analyzer; class auf_solver; class simple_macro_solver; class hint_solver; class non_auf_macro_solver; class instantiation_set; }; class model_finder { typedef mf::quantifier_analyzer quantifier_analyzer; typedef mf::quantifier_info quantifier_info; typedef mf::auf_solver auf_solver; typedef mf::simple_macro_solver simple_macro_solver; typedef mf::hint_solver hint_solver; typedef mf::non_auf_macro_solver non_auf_macro_solver; typedef mf::instantiation_set instantiation_set; ast_manager & m_manager; context * m_context; scoped_ptr m_analyzer; scoped_ptr m_auf_solver; obj_map m_q2info; ptr_vector m_quantifiers; func_decl_dependencies m_dependencies; scoped_ptr m_sm_solver; scoped_ptr m_hint_solver; scoped_ptr m_nm_solver; bool m_cancel; struct scope { unsigned m_quantifiers_lim; }; svector m_scopes; expr_ref_vector m_new_constraints; // new constraints for fresh constants created by the model finder void restore_quantifiers(unsigned old_size); quantifier_info * get_quantifier_info(quantifier * q) const; void collect_relevant_quantifiers(ptr_vector & qs) const; void cleanup_quantifier_infos(ptr_vector const & qs); void process_simple_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m); void process_hint_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m); void process_non_auf_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m); void process_auf(ptr_vector const & qs, proto_model * m); instantiation_set const * get_uvar_inst_set(quantifier * q, unsigned i) const; void checkpoint() { cooperate("model_finder"); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } public: model_finder(ast_manager & m, simplifier & s); ~model_finder(); void set_context(context * ctx); void register_quantifier(quantifier * q); void push_scope(); void pop_scope(unsigned num_scopes); void reset(); void init_search_eh(); void fix_model(proto_model * m); quantifier * get_flat_quantifier(quantifier * q) const; expr * get_inv(quantifier * q, unsigned i, expr * val, unsigned & generation) const; bool restrict_sks_to_inst_set(context * aux_ctx, quantifier * q, expr_ref_vector const & sks); void restart_eh(); void set_cancel(bool f); }; }; #endif z3-z3-4.4.1/src/smt/smt_model_generator.cpp000066400000000000000000000550011260446376700205550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_generator.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-29. Revision History: --*/ #include"smt_context.h" #include"smt_model_generator.h" #include"proto_model.h" #include"for_each_expr.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"ast_smt2_pp.h" namespace smt { model_generator::model_generator(ast_manager & m): m_manager(m), m_context(0), m_fresh_idx(1), m_asts(m_manager), m_model(0) { } model_generator::~model_generator() { } void model_generator::reset() { m_extra_fresh_values.reset(); m_fresh_idx = 1; m_root2value.reset(); m_asts.reset(); m_model = 0; } void model_generator::init_model() { SASSERT(!m_model); // PARAM-TODO smt_params ---> params_ref m_model = alloc(proto_model, m_manager, m_context->get_simplifier()); // , m_context->get_fparams()); ptr_vector::const_iterator it = m_context->begin_theories(); ptr_vector::const_iterator end = m_context->end_theories(); for (; it != end; ++it) { TRACE("model_generator_bug", tout << "init_model for theory: " << (*it)->get_name() << "\n";); (*it)->init_model(*this); } } /** \brief Create the boolean assignment. */ void model_generator::mk_bool_model() { unsigned sz = m_context->get_num_b_internalized(); for (unsigned i = 0; i < sz; i++) { expr * p = m_context->get_b_internalized(i); if (is_uninterp_const(p) && m_context->is_relevant(p)) { SASSERT(m_manager.is_bool(p)); func_decl * d = to_app(p)->get_decl(); lbool val = m_context->get_assignment(p); expr * v = val == l_true ? m_manager.mk_true() : m_manager.mk_false(); m_model->register_decl(d, v); } } } /** \brief Create the mapping root2proc: enode-root -> model_value_proc, and roots. Store the new model_value_proc at procs. */ void model_generator::mk_value_procs(obj_map & root2proc, ptr_vector & roots, ptr_vector & procs) { ptr_vector::const_iterator it = m_context->begin_enodes(); ptr_vector::const_iterator end = m_context->end_enodes(); for (; it != end; ++it) { enode * r = *it; if (r == r->get_root() && m_context->is_relevant(r)) { roots.push_back(r); sort * s = m_manager.get_sort(r->get_owner()); model_value_proc * proc = 0; if (m_manager.is_bool(s)) { SASSERT(m_context->get_assignment(r) == l_true || m_context->get_assignment(r) == l_false); if (m_context->get_assignment(r) == l_true) proc = alloc(expr_wrapper_proc, m_manager.mk_true()); else proc = alloc(expr_wrapper_proc, m_manager.mk_false()); } else { family_id fid = s->get_family_id(); theory * th = m_context->get_theory(fid); if (th && th->build_models()) { if (r->get_th_var(th->get_id()) != null_theory_var) { proc = th->mk_value(r, *this); SASSERT(proc); } else { TRACE("model_bug", tout << "creating fresh value for #" << r->get_owner_id() << "\n";); proc = alloc(fresh_value_proc, mk_extra_fresh_value(m_manager.get_sort(r->get_owner()))); } } else { proc = mk_model_value(r); SASSERT(proc); } } SASSERT(proc); procs.push_back(proc); root2proc.insert(r, proc); } } } model_value_proc* model_generator::mk_model_value(enode* r) { SASSERT(r == r->get_root()); expr * n = r->get_owner(); if (!m_manager.is_model_value(n)) { sort * s = m_manager.get_sort(r->get_owner()); n = m_model->get_fresh_value(s); CTRACE("model_generator_bug", n == 0, tout << mk_pp(r->get_owner(), m_manager) << "\nsort:\n" << mk_pp(s, m_manager) << "\n"; tout << "is_finite: " << m_model->is_finite(s) << "\n";); } return alloc(expr_wrapper_proc, to_app(n)); } #define White 0 #define Grey 1 #define Black 2 static int get_color(source2color const & colors, source const & s) { int color; if (colors.find(s, color)) return color; return White; } static void set_color(source2color & colors, source const & s, int c) { colors.insert(s, c); } static void visit_child(source const & s, source2color & colors, svector & todo, bool & visited) { if (get_color(colors, s) == White) { todo.push_back(s); visited = false; } } bool model_generator::visit_children(source const & src, ptr_vector const & roots, obj_map const & root2proc, source2color & colors, obj_hashtable & already_traversed, svector & todo) { if (src.is_fresh_value()) { // there is an implicit dependency between a fresh value stub of sort S and the root enodes of sort S that are not associated with fresh values. sort * s = src.get_value()->get_sort(); if (already_traversed.contains(s)) return true; bool visited = true; unsigned sz = roots.size(); for (unsigned i = 0; i < sz; i++) { enode * r = roots[i]; if (m_manager.get_sort(r->get_owner()) != s) continue; SASSERT(r == r->get_root()); model_value_proc * proc = 0; root2proc.find(r, proc); SASSERT(proc); if (proc->is_fresh()) continue; // r is associated with a fresh value... SASSERT(r == r->get_root()); TRACE("mg_top_sort", tout << "fresh!" << src.get_value()->get_idx() << " -> #" << r->get_owner_id() << " " << mk_pp(m_manager.get_sort(r->get_owner()), m_manager) << "\n";); visit_child(source(r), colors, todo, visited); TRACE("mg_top_sort", tout << "visited: " << visited << ", todo.size(): " << todo.size() << "\n";); } already_traversed.insert(s); return visited; } SASSERT(!src.is_fresh_value()); enode * n = src.get_enode(); SASSERT(n == n->get_root()); bool visited = true; model_value_proc * proc = 0; root2proc.find(n, proc); SASSERT(proc); buffer dependencies; proc->get_dependencies(dependencies); buffer::const_iterator it = dependencies.begin(); buffer::const_iterator end = dependencies.end(); for (; it != end; ++it) { model_value_dependency const & dep = *it; visit_child(dep, colors, todo, visited); TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << " -> "; if (dep.is_fresh_value()) tout << "fresh!" << dep.get_value()->get_idx(); else tout << "#" << dep.get_enode()->get_owner_id(); tout << "\n";); } return visited; } void model_generator::process_source(source const & src, ptr_vector const & roots, obj_map const & root2proc, source2color & colors, obj_hashtable & already_traversed, svector & todo, svector & sorted_sources) { TRACE("mg_top_sort", tout << "process source, is_fresh: " << src.is_fresh_value() << " "; if (src.is_fresh_value()) tout << "fresh!" << src.get_value()->get_idx(); else tout << "#" << src.get_enode()->get_owner_id(); tout << ", todo.size(): " << todo.size() << "\n";); int color = get_color(colors, src); SASSERT(color != Grey); if (color == Black) return; SASSERT(color == White); todo.push_back(src); while (!todo.empty()) { source curr = todo.back(); TRACE("mg_top_sort", tout << "current source, is_fresh: " << curr.is_fresh_value() << " "; if (curr.is_fresh_value()) tout << "fresh!" << curr.get_value()->get_idx(); else tout << "#" << curr.get_enode()->get_owner_id(); tout << ", todo.size(): " << todo.size() << "\n";); switch (get_color(colors, curr)) { case White: set_color(colors, curr, Grey); visit_children(curr, roots, root2proc, colors, already_traversed, todo); break; case Grey: SASSERT(visit_children(curr, roots, root2proc, colors, already_traversed, todo)); set_color(colors, curr, Black); sorted_sources.push_back(curr); break; case Black: todo.pop_back(); break; default: UNREACHABLE(); } } TRACE("mg_top_sort", tout << "END process_source, todo.size(): " << todo.size() << "\n";); } /** \brief Topological sort of 'sources'. Store result in sorted_sources. */ void model_generator::top_sort_sources(ptr_vector const & roots, obj_map const & root2proc, svector & sorted_sources) { svector todo; source2color colors; // The following 'set' of sorts is used to avoid traversing roots looking for enodes of sort S. // That is, a sort S is in already_traversed, if all enodes of sort S in roots were already traversed. obj_hashtable already_traversed; // topological sort // traverse all extra fresh values... unsigned sz = m_extra_fresh_values.size(); for (unsigned i = 0; i < sz; i++) { extra_fresh_value * f = m_extra_fresh_values[i]; process_source(source(f), roots, root2proc, colors, already_traversed, todo, sorted_sources); } // traverse all enodes that are associated with fresh values... sz = roots.size(); for (unsigned i = 0; i < sz; i++) { enode * r = roots[i]; model_value_proc * proc = 0; root2proc.find(r, proc); SASSERT(proc); if (!proc->is_fresh()) continue; process_source(source(r), roots, root2proc, colors, already_traversed, todo, sorted_sources); } sz = roots.size(); for (unsigned i = 0; i < sz; i++) { enode * r = roots[i]; process_source(source(r), roots, root2proc, colors, already_traversed, todo, sorted_sources); } } void model_generator::mk_values() { obj_map root2proc; ptr_vector roots; ptr_vector procs; svector sources; buffer dependencies; ptr_vector dependency_values; mk_value_procs(root2proc, roots, procs); top_sort_sources(roots, root2proc, sources); TRACE("sorted_sources", svector::const_iterator it = sources.begin(); svector::const_iterator end = sources.end(); for (; it != end; ++it) { source const & curr = *it; if (curr.is_fresh_value()) { tout << "fresh!" << curr.get_value()->get_idx() << " " << mk_pp(curr.get_value()->get_sort(), m_manager) << "\n"; } else { enode * n = curr.get_enode(); SASSERT(n->get_root() == n); sort * s = m_manager.get_sort(n->get_owner()); tout << "#" << n->get_owner_id() << " " << mk_pp(s, m_manager); model_value_proc * proc = 0; root2proc.find(n, proc); SASSERT(proc); tout << " is_fresh: " << proc->is_fresh() << "\n"; } }); svector::const_iterator it = sources.begin(); svector::const_iterator end = sources.end(); for (; it != end; ++it) { source const & curr = *it; if (curr.is_fresh_value()) { sort * s = curr.get_value()->get_sort(); TRACE("model_fresh_bug", tout << "mk fresh!" << curr.get_value()->get_idx() << " : " << mk_pp(s, m_manager) << "\n";); expr * val = m_model->get_fresh_value(s); TRACE("model_fresh_bug", tout << "mk fresh!" << curr.get_value()->get_idx() << " := #" << (val == 0 ? UINT_MAX : val->get_id()) << "\n";); m_asts.push_back(val); curr.get_value()->set_value(val); } else { enode * n = curr.get_enode(); SASSERT(n->get_root() == n); TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << "\n";); dependencies.reset(); dependency_values.reset(); model_value_proc * proc = 0; VERIFY(root2proc.find(n, proc)); SASSERT(proc); proc->get_dependencies(dependencies); buffer::const_iterator it2 = dependencies.begin(); buffer::const_iterator end2 = dependencies.end(); for (; it2 != end2; ++it2) { model_value_dependency const & d = *it2; if (d.is_fresh_value()) { CTRACE("mg_top_sort", !d.get_value()->get_value(), tout << "#" << n->get_owner_id() << " -> "; tout << "fresh!" << d.get_value()->get_idx() << "\n";); SASSERT(d.get_value()->get_value()); dependency_values.push_back(d.get_value()->get_value()); } else { enode * child = d.get_enode(); child = child->get_root(); app * val = 0; m_root2value.find(child, val); SASSERT(val); dependency_values.push_back(val); } } app * val = proc->mk_value(*this, dependency_values); register_value(val); m_asts.push_back(val); m_root2value.insert(n, val); } } std::for_each(procs.begin(), procs.end(), delete_proc()); std::for_each(m_extra_fresh_values.begin(), m_extra_fresh_values.end(), delete_proc()); m_extra_fresh_values.reset(); // send model ptr_vector::const_iterator it3 = m_context->begin_enodes(); ptr_vector::const_iterator end3 = m_context->end_enodes(); for (; it3 != end3; ++it3) { enode * n = *it3; if (is_uninterp_const(n->get_owner()) && m_context->is_relevant(n)) { func_decl * d = n->get_owner()->get_decl(); expr * val = get_value(n); m_model->register_decl(d, val); } } } app * model_generator::get_value(enode * n) const { app * val = 0; m_root2value.find(n->get_root(), val); SASSERT(val); return val; } /** \brief Return true if the interpretation of the function should be included in the model. */ bool model_generator::include_func_interp(func_decl * f) const { return f->get_family_id() == null_family_id; } /** \brief Create (partial) interpretation of function symbols. The "else" is missing. */ void model_generator::mk_func_interps() { unsigned sz = m_context->get_num_e_internalized(); for (unsigned i = 0; i < sz; i++) { expr * t = m_context->get_e_internalized(i); if (!m_context->is_relevant(t)) continue; enode * n = m_context->get_enode(t); unsigned num_args = n->get_num_args(); func_decl * f = n->get_decl(); if (num_args > 0 && n->get_cg() == n && include_func_interp(f)) { ptr_buffer args; expr * result = get_value(n); SASSERT(result); for (unsigned j = 0; j < num_args; j++) { app * arg = get_value(n->get_arg(j)); SASSERT(arg); args.push_back(arg); } func_interp * fi = m_model->get_func_interp(f); if (fi == 0) { fi = alloc(func_interp, m_manager, f->get_arity()); m_model->register_decl(f, fi); } SASSERT(m_model->has_interpretation(f)); SASSERT(m_model->get_func_interp(f) == fi); // The entry must be new because n->get_cg() == n TRACE("func_interp_bug", tout << "insert new entry for:\n" << mk_ismt2_pp(n->get_owner(), m_manager) << "\nargs: "; for (unsigned i = 0; i < num_args; i++) { tout << "#" << n->get_arg(i)->get_owner_id() << " "; } tout << "\n"; tout << "value: #" << n->get_owner_id() << "\n" << mk_ismt2_pp(result, m_manager) << "\n";); if (m_context->get_last_search_failure() == smt::THEORY) { // if the theory solvers are incomplete, then we cannot assume the e-graph is close under congruence if (fi->get_entry(args.c_ptr()) == 0) fi->insert_new_entry(args.c_ptr(), result); } else { fi->insert_new_entry(args.c_ptr(), result); } } } } extra_fresh_value * model_generator::mk_extra_fresh_value(sort * s) { SASSERT(s->is_infinite()); extra_fresh_value * r = alloc(extra_fresh_value, s, m_fresh_idx); m_fresh_idx++; m_extra_fresh_values.push_back(r); return r; } expr * model_generator::get_some_value(sort * s) { SASSERT(m_model); return m_model->get_some_value(s); } void model_generator::register_value(expr * val) { SASSERT(m_model); m_model->register_value(val); } void model_generator::finalize_theory_models() { ptr_vector::const_iterator it = m_context->begin_theories(); ptr_vector::const_iterator end = m_context->end_theories(); for (; it != end; ++it) (*it)->finalize_model(*this); } void model_generator::register_existing_model_values() { ptr_vector::const_iterator it = m_context->begin_enodes(); ptr_vector::const_iterator end = m_context->end_enodes(); for (; it != end; ++it) { enode * r = *it; if (r == r->get_root() && m_context->is_relevant(r)) { expr * n = r->get_owner(); if (m_manager.is_model_value(n)) { register_value(n); } } } } void model_generator::register_factory(value_factory * f) { m_model->register_factory(f); } void model_generator::register_macros() { unsigned num = m_context->get_num_macros(); TRACE("register_macros", tout << "num. macros: " << num << "\n";); expr_ref v(m_manager); for (unsigned i = 0; i < num; i++) { func_decl * f = m_context->get_macro_interpretation(i, v); func_interp * fi = alloc(func_interp, m_manager, f->get_arity()); fi->set_else(v); TRACE("register_macros", tout << f->get_name() << "\n" << mk_pp(v, m_manager) << "\n";); m_model->register_decl(f, fi); } } /** \brief Auxiliary functor for method register_indirect_elim_decls. */ class mk_interp_proc { context & m_context; proto_model & m_model; public: mk_interp_proc(context & ctx, proto_model & m): m_context(ctx), m_model(m) { } void operator()(var * n) { } void operator()(app * n) { if (!is_uninterp(n)) return; // n is interpreted func_decl * d = n->get_decl(); if (m_model.has_interpretation(d)) return; // declaration already has an interpretation. if (n->get_num_args() == 0) { sort * r = d->get_range(); expr * v = m_model.get_some_value(r); m_model.register_decl(d, v); } else { func_interp * fi = alloc(func_interp, m_context.get_manager(), d->get_arity()); m_model.register_decl(d, fi); } } void operator()(quantifier * n) { } }; proto_model * model_generator::mk_model() { SASSERT(!m_model); TRACE("func_interp_bug", m_context->display(tout);); init_model(); register_existing_model_values(); mk_bool_model(); mk_values(); mk_func_interps(); finalize_theory_models(); register_macros(); return m_model; } }; z3-z3-4.4.1/src/smt/smt_model_generator.h000066400000000000000000000217671260446376700202360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_generator.h Abstract: The model generator builds a (partial) model for the ground formulas in the logical context. The model finder (smt_model_finder.h) tries to extend the partial model to satisfy the quantifiers. Main invariant: the new model still satisfies the ground formulas. The model checker (smt_model_checker.h) checks whether the (new) model satisfies the quantifiers. If it doesn't, new instances are added. Author: Leonardo de Moura (leonardo) 2008-10-29. Revision History: --*/ #ifndef SMT_MODEL_GENERATOR_H_ #define SMT_MODEL_GENERATOR_H_ #include"ast.h" #include"smt_types.h" #include"obj_hashtable.h" #include"map.h" class value_factory; class proto_model; namespace smt { // ----------------------------- // // This module builds an interpretation for each relevant expression in the logical context. // // 1) The interpretation of boolean constants is their truth value in the logical context. // // 2) The interpretation of expressions associated with enodes is built using functors (model_value_proc). // Theories as arrays and datatypes many need the interpretation of some expressions to be built before the interpretation of others. // We say this is a dependency. Moreover, some values must be fresh. That is, they should be different // from all other values associated with enodes of a given sort. For example, the array theory // uses fresh values to make sure that some array constants are different from each other. // // So a dependency for building the interpretation of an enode N can be: // a) a fresh value (stub) of sort S: it must be built after the interpretation of all enodes of sort S were assigned. // // b) an enode N': the interpretation of N' must be built before the interpretation of N. // // We say a 'source' is an fresh value or an enode. Note that every dependency is a source, // but not every source is a dependency. // // We use these dependencies to sort (using a topological sort) the sources. The sorted 'sources' specify the // order the interpretations will be built. // // Assumptions regarding dependencies: // // - They are acyclic. // // - All dependencies are marked as relevant. // // - A fresh value stub of sort S depends (implicitly) on all enodes of sort S (that are not associated with fresh values). // So an enode of sort S may not have a dependency of sort S. // // ------------------------------ /** \brief Stub for extra fresh value. */ struct extra_fresh_value { sort * m_sort; unsigned m_idx; expr * m_value; public: extra_fresh_value(sort * s, unsigned idx):m_sort(s), m_idx(idx), m_value(0) {} sort * get_sort() const { return m_sort; } unsigned get_idx() const { return m_idx; } void set_value(expr * n) { SASSERT(m_value == 0); m_value = n; } expr * get_value() const { return m_value; } }; /** \brief Theories such as arrays and datatypes may need some values to be already available when building a value. We say this a dependency. Object of this class are used to track such dependencies. Example: to build the value (cons 10 nil), the values 10 and nil should be already available. */ class model_value_dependency { bool m_fresh; //!< True if the dependency is a new fresh value; union { enode * m_enode; //!< When m_fresh == false, contains an enode depedency. extra_fresh_value * m_value; //!< When m_fresh == true, contains the sort of the fresh value }; public: model_value_dependency():m_fresh(true), m_value(0) {} model_value_dependency(enode * n):m_fresh(false), m_enode(n->get_root()) {} model_value_dependency(extra_fresh_value * v):m_fresh(true), m_value(v) {} bool is_fresh_value() const { return m_fresh; } enode * get_enode() const { SASSERT(!is_fresh_value()); return m_enode; } extra_fresh_value * get_value() const { SASSERT(is_fresh_value()); return m_value; } }; typedef model_value_dependency source; struct source_hash_proc { unsigned operator()(source const & s) const { return s.is_fresh_value() ? hash_u_u(17, s.get_value()->get_idx()) : hash_u_u(13, s.get_enode()->get_owner_id()); } }; struct source_eq_proc { bool operator()(source const & s1, source const & s2) const { if (s1.is_fresh_value() != s2.is_fresh_value()) return false; if (s1.is_fresh_value()) return s1.get_value()->get_idx() == s2.get_value()->get_idx(); else return s1.get_enode() == s2.get_enode(); } }; typedef map source2color; /** \brief Model value builder. This functor is used to specify the dependencies needed to build a value, and to build the actual value. */ class model_value_proc { public: virtual ~model_value_proc() {} /** \brief Fill result with the dependencies of this functor. That is, to invoke mk_value, the dependencies in result must be constructed. */ virtual void get_dependencies(buffer & result) {} /** \brief The array values has size equal to the size of the argument \c result in get_dependencies. It contain the values built for the dependencies. */ virtual app * mk_value(model_generator & m, ptr_vector & values) = 0; /** \brief Return true if it is associated with a fresh value. */ virtual bool is_fresh() const { return false; } }; /** \brief Simple model_value_proc. It has no dependencies, and just returns a given expression. */ class expr_wrapper_proc : public model_value_proc { app * m_value; public: expr_wrapper_proc(app * v):m_value(v) {} virtual app * mk_value(model_generator & m, ptr_vector & values) { return m_value; } }; class fresh_value_proc : public model_value_proc { extra_fresh_value * m_value; public: fresh_value_proc(extra_fresh_value * v):m_value(v) {} virtual void get_dependencies(buffer & result) { result.push_back(m_value); } virtual app * mk_value(model_generator & m, ptr_vector & values) { return to_app(values[0]); } virtual bool is_fresh() const { return true; } }; /** \brief Auxiliary class used during model generation. */ class model_generator { ast_manager & m_manager; context * m_context; ptr_vector m_extra_fresh_values; unsigned m_fresh_idx; obj_map m_root2value; ast_ref_vector m_asts; proto_model * m_model; void init_model(); void mk_bool_model(); void mk_value_procs(obj_map & root2proc, ptr_vector & roots, ptr_vector & procs); void mk_values(); bool include_func_interp(func_decl * f) const; void mk_func_interps(); void finalize_theory_models(); void display(std::ostream & out); void register_existing_model_values(); void register_macros(); bool visit_children(source const & src, ptr_vector const & roots, obj_map const & root2proc, source2color & colors, obj_hashtable & already_traversed, svector & todo); void process_source(source const & src, ptr_vector const & roots, obj_map const & root2proc, source2color & colors, obj_hashtable & already_traversed, svector & todo, svector & sorted_sources); void top_sort_sources(ptr_vector const & roots, obj_map const & root2proc, svector & sorted_sources); public: model_generator(ast_manager & m); ~model_generator(); void reset(); void set_context(context * c) { SASSERT(m_context == 0); m_context = c; } void register_factory(value_factory * f); extra_fresh_value * mk_extra_fresh_value(sort * s); model_value_proc * mk_model_value(enode* r); expr * get_some_value(sort * s); proto_model & get_model() { SASSERT(m_model); return *m_model; } void register_value(expr * val); ast_manager & get_manager() { return m_manager; } proto_model * mk_model(); obj_map const & get_root2value() const { return m_root2value; } app * get_value(enode * n) const; }; }; #endif /* SMT_MODEL_GENERATOR_H_ */ z3-z3-4.4.1/src/smt/smt_quantifier.cpp000066400000000000000000000537061260446376700175700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_quantifier.cpp Abstract: Quantifier reasoning support for smt::context. Author: Leonardo de Moura (leonardo) 2012-02-16. Revision History: --*/ #include"smt_quantifier.h" #include"smt_context.h" #include"smt_quantifier_stat.h" #include"smt_model_finder.h" #include"smt_model_checker.h" #include"smt_quick_checker.h" #include"mam.h" #include"qi_queue.h" #include"ast_smt2_pp.h" namespace smt { quantifier_manager_plugin * mk_default_plugin(); struct quantifier_manager::imp { quantifier_manager & m_wrapper; context & m_context; smt_params & m_params; qi_queue m_qi_queue; obj_map m_quantifier_stat; quantifier_stat_gen m_qstat_gen; ptr_vector m_quantifiers; scoped_ptr m_plugin; unsigned m_num_instances; imp(quantifier_manager & wrapper, context & ctx, smt_params & p, quantifier_manager_plugin * plugin): m_wrapper(wrapper), m_context(ctx), m_params(p), m_qi_queue(m_wrapper, ctx, p), m_qstat_gen(ctx.get_manager(), ctx.get_region()), m_plugin(plugin) { m_num_instances = 0; m_qi_queue.setup(); } bool has_trace_stream() const { return m_context.get_manager().has_trace_stream(); } std::ostream & trace_stream() { return m_context.get_manager().trace_stream(); } quantifier_stat * get_stat(quantifier * q) const { return m_quantifier_stat.find(q); } unsigned get_generation(quantifier * q) const { return get_stat(q)->get_generation(); } void add(quantifier * q, unsigned generation) { quantifier_stat * stat = m_qstat_gen(q, generation); m_quantifier_stat.insert(q, stat); m_quantifiers.push_back(q); m_plugin->add(q); } void display_stats(std::ostream & out, quantifier * q) { quantifier_stat * s = get_stat(q); unsigned num_instances = s->get_num_instances(); unsigned max_generation = s->get_max_generation(); float max_cost = s->get_max_cost(); if (num_instances > 0) { out << "[quantifier_instances] "; out.width(10); out << q->get_qid().str().c_str() << " : "; out.width(6); out << num_instances << " : "; out.width(3); out << max_generation << " : " << max_cost << "\n"; } } void del(quantifier * q) { if (m_params.m_qi_profile) { display_stats(verbose_stream(), q); } m_quantifiers.pop_back(); m_quantifier_stat.erase(q); } bool empty() const { return m_quantifiers.empty(); } bool is_shared(enode * n) const { return m_plugin->is_shared(n); } bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, ptr_vector & used_enodes) { max_generation = std::max(max_generation, get_generation(q)); if (m_num_instances > m_params.m_qi_max_instances) return false; get_stat(q)->update_max_generation(max_generation); fingerprint * f = m_context.add_fingerprint(q, q->get_id(), num_bindings, bindings); if (f) { if (has_trace_stream()) { std::ostream & out = trace_stream(); out << "[new-match] " << static_cast(f) << " #" << q->get_id(); for (unsigned i = 0; i < num_bindings; i++) { // I don't want to use mk_pp because it creates expressions for pretty printing. // This nasty side-effect may change the behavior of Z3. out << " #" << bindings[i]->get_owner_id(); } out << " ;"; ptr_vector::const_iterator it = used_enodes.begin(); ptr_vector::const_iterator end = used_enodes.end(); for (; it != end; ++it) out << " #" << (*it)->get_owner_id(); out << "\n"; } m_qi_queue.insert(f, pat, max_generation, min_top_generation, max_top_generation); // TODO m_num_instances++; return true; } return false; } void init_search_eh() { m_num_instances = 0; ptr_vector::iterator it2 = m_quantifiers.begin(); ptr_vector::iterator end2 = m_quantifiers.end(); for (; it2 != end2; ++it2) { quantifier * q = *it2; get_stat(q)->reset_num_instances_curr_search(); } m_qi_queue.init_search_eh(); m_plugin->init_search_eh(); } void assign_eh(quantifier * q) { m_plugin->assign_eh(q); } void add_eq_eh(enode * n1, enode * n2) { m_plugin->add_eq_eh(n1, n2); } void relevant_eh(enode * n) { m_plugin->relevant_eh(n); } void restart_eh() { m_plugin->restart_eh(); } void push() { m_plugin->push(); m_qi_queue.push_scope(); } void pop(unsigned num_scopes) { m_plugin->pop(num_scopes); m_qi_queue.pop_scope(num_scopes); } bool can_propagate() { return m_qi_queue.has_work() || m_plugin->can_propagate(); } void propagate() { m_plugin->propagate(); m_qi_queue.instantiate(); } bool quick_check_quantifiers() { if (m_params.m_qi_quick_checker == MC_NO) return true; if (m_quantifiers.empty()) return true; IF_VERBOSE(10, verbose_stream() << "quick checking quantifiers (unsat)...\n";); quick_checker mc(m_context); bool result = true; ptr_vector::const_iterator it = m_quantifiers.begin(); ptr_vector::const_iterator end = m_quantifiers.end(); for (; it != end; ++it) if (m_context.is_relevant(*it) && m_context.get_assignment(*it) == l_true && mc.instantiate_unsat(*it)) result = false; if (m_params.m_qi_quick_checker == MC_UNSAT || !result) { m_qi_queue.instantiate(); return result; } // MC_NO_SAT is too expensive (it creates too many irrelevant instances). // we should use MBQI=true instead. IF_VERBOSE(10, verbose_stream() << "quick checking quantifiers (not sat)...\n";); it = m_quantifiers.begin(); for (; it != end; ++it) if (m_context.is_relevant(*it) && m_context.get_assignment(*it) == l_true && mc.instantiate_not_sat(*it)) result = false; m_qi_queue.instantiate(); return result; } final_check_status final_check_eh(bool full) { if (full) { IF_VERBOSE(100, verbose_stream() << "(smt.final-check \"quantifiers\")\n";); final_check_status result = m_qi_queue.final_check_eh() ? FC_DONE : FC_CONTINUE; final_check_status presult = m_plugin->final_check_eh(full); if (presult != FC_DONE) result = presult; if (m_context.can_propagate()) result = FC_CONTINUE; if (result == FC_DONE && !m_params.m_qi_lazy_quick_checker && !quick_check_quantifiers()) result = FC_CONTINUE; return result; } else { return m_plugin->final_check_eh(false); } } check_model_result check_model(proto_model * m, obj_map const & root2value) { if (empty()) return SAT; return m_plugin->check_model(m, root2value); } void set_cancel(bool f) { m_plugin->set_cancel(f); } }; quantifier_manager::quantifier_manager(context & ctx, smt_params & fp, params_ref const & p) { m_imp = alloc(imp, *this, ctx, fp, mk_default_plugin()); m_imp->m_plugin->set_manager(*this); } quantifier_manager::~quantifier_manager() { dealloc(m_imp); } context & quantifier_manager::get_context() const { return m_imp->m_context; } void quantifier_manager::set_plugin(quantifier_manager_plugin * plugin) { m_imp->m_plugin = plugin; plugin->set_manager(*this); } void quantifier_manager::add(quantifier * q, unsigned generation) { m_imp->add(q, generation); } void quantifier_manager::del(quantifier * q) { m_imp->del(q); } bool quantifier_manager::empty() const { return m_imp->empty(); } bool quantifier_manager::is_shared(enode * n) const { return m_imp->is_shared(n); } quantifier_stat * quantifier_manager::get_stat(quantifier * q) const { return m_imp->get_stat(q); } unsigned quantifier_manager::get_generation(quantifier * q) const { return m_imp->get_generation(q); } bool quantifier_manager::add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, ptr_vector & used_enodes) { return m_imp->add_instance(q, pat, num_bindings, bindings, max_generation, min_top_generation, max_generation, used_enodes); } bool quantifier_manager::add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation) { ptr_vector tmp; return add_instance(q, 0, num_bindings, bindings, generation, generation, generation, tmp); } void quantifier_manager::init_search_eh() { m_imp->init_search_eh(); } void quantifier_manager::assign_eh(quantifier * q) { m_imp->assign_eh(q); } void quantifier_manager::add_eq_eh(enode * n1, enode * n2) { m_imp->add_eq_eh(n1, n2); } void quantifier_manager::relevant_eh(enode * n) { m_imp->relevant_eh(n); } final_check_status quantifier_manager::final_check_eh(bool full) { return m_imp->final_check_eh(full); } void quantifier_manager::restart_eh() { m_imp->restart_eh(); } bool quantifier_manager::can_propagate() const { return m_imp->can_propagate(); } void quantifier_manager::propagate() { m_imp->propagate(); } bool quantifier_manager::model_based() const { return m_imp->m_plugin->model_based(); } bool quantifier_manager::mbqi_enabled(quantifier *q) const { return m_imp->m_plugin->mbqi_enabled(q); } void quantifier_manager::adjust_model(proto_model * m) { m_imp->m_plugin->adjust_model(m); } quantifier_manager::check_model_result quantifier_manager::check_model(proto_model * m, obj_map const & root2value) { return m_imp->check_model(m, root2value); } void quantifier_manager::push() { m_imp->push(); } void quantifier_manager::pop(unsigned num_scopes) { m_imp->pop(num_scopes); } void quantifier_manager::reset() { #pragma omp critical (quantifier_manager) { context & ctx = m_imp->m_context; smt_params & p = m_imp->m_params; quantifier_manager_plugin * plugin = m_imp->m_plugin->mk_fresh(); m_imp->~imp(); m_imp = new (m_imp) imp(*this, ctx, p, plugin); plugin->set_manager(*this); } } void quantifier_manager::set_cancel(bool f) { #pragma omp critical (quantifier_manager) { m_imp->set_cancel(f); } } void quantifier_manager::display(std::ostream & out) const { } void quantifier_manager::collect_statistics(::statistics & st) const { m_imp->m_qi_queue.collect_statistics(st); } void quantifier_manager::reset_statistics() { } void quantifier_manager::display_stats(std::ostream & out, quantifier * q) const { m_imp->display_stats(out, q); } ptr_vector::const_iterator quantifier_manager::begin_quantifiers() const { return m_imp->m_quantifiers.begin(); } ptr_vector::const_iterator quantifier_manager::end_quantifiers() const { return m_imp->m_quantifiers.end(); } // The default plugin uses E-matching, MBQI and quick-checker class default_qm_plugin : public quantifier_manager_plugin { quantifier_manager * m_qm; smt_params * m_fparams; context * m_context; scoped_ptr m_mam; scoped_ptr m_lazy_mam; scoped_ptr m_model_finder; scoped_ptr m_model_checker; unsigned m_new_enode_qhead; unsigned m_lazy_matching_idx; public: default_qm_plugin(): m_qm(0), m_context(0), m_new_enode_qhead(0), m_lazy_matching_idx(0) { } virtual ~default_qm_plugin() { } virtual void set_manager(quantifier_manager & qm) { SASSERT(m_qm == 0); m_qm = &qm; m_context = &(qm.get_context()); m_fparams = &(m_context->get_fparams()); ast_manager & m = m_context->get_manager(); m_mam = mk_mam(*m_context); m_lazy_mam = mk_mam(*m_context); m_model_finder = alloc(model_finder, m, m_context->get_simplifier()); m_model_checker = alloc(model_checker, m, *m_fparams, *(m_model_finder.get())); m_model_finder->set_context(m_context); m_model_checker->set_qm(qm); } virtual quantifier_manager_plugin * mk_fresh() { return alloc(default_qm_plugin); } virtual bool model_based() const { return m_fparams->m_mbqi; } virtual bool mbqi_enabled(quantifier *q) const { if(!m_fparams->m_mbqi_id) return true; const symbol &s = q->get_qid(); size_t len = strlen(m_fparams->m_mbqi_id); if(s == symbol::null || s.is_numerical()) return len == 0; return strncmp(s.bare_str(),m_fparams->m_mbqi_id,len) == 0; } /* Quantifier id's must begin with the prefix specified by parameter mbqi.id to be instantiated with MBQI. The default value is the empty string, so all quantifiers are instantiated. */ virtual void add(quantifier * q) { if (m_fparams->m_mbqi && mbqi_enabled(q)) { m_model_finder->register_quantifier(q); } } virtual void del(quantifier * q) { } virtual void push() { m_mam->push_scope(); m_lazy_mam->push_scope(); if (m_fparams->m_mbqi) { m_model_finder->push_scope(); } } virtual void pop(unsigned num_scopes) { m_mam->pop_scope(num_scopes); m_lazy_mam->pop_scope(num_scopes); if (m_fparams->m_mbqi) { m_model_finder->pop_scope(num_scopes); } } virtual void init_search_eh() { m_lazy_matching_idx = 0; if (m_fparams->m_mbqi) { m_model_finder->init_search_eh(); m_model_checker->init_search_eh(); } } virtual void assign_eh(quantifier * q) { if (m_fparams->m_ematching) { bool has_unary_pattern = false; unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) { app * mp = to_app(q->get_pattern(i)); if (mp->get_num_args() == 1) { has_unary_pattern = true; break; } } unsigned num_eager_multi_patterns = m_fparams->m_qi_max_eager_multipatterns; if (!has_unary_pattern) num_eager_multi_patterns++; for (unsigned i = 0, j = 0; i < num_patterns; i++) { app * mp = to_app(q->get_pattern(i)); SASSERT(m_context->get_manager().is_pattern(mp)); bool unary = (mp->get_num_args() == 1); if (!unary && j >= num_eager_multi_patterns) { TRACE("assign_quantifier", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n" << "j: " << j << " unary: " << unary << " m_params.m_qi_max_eager_multipatterns: " << m_fparams->m_qi_max_eager_multipatterns << " num_eager_multi_patterns: " << num_eager_multi_patterns << "\n";); m_lazy_mam->add_pattern(q, mp); } else { TRACE("assign_quantifier", tout << "adding:\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n";); m_mam->add_pattern(q, mp); } if (!unary) j++; } } } bool use_ematching() const { return m_fparams->m_ematching && !m_qm->empty(); } virtual void add_eq_eh(enode * e1, enode * e2) { if (use_ematching()) m_mam->add_eq_eh(e1, e2); } virtual void relevant_eh(enode * e) { if (use_ematching()) { m_mam->relevant_eh(e, false); m_lazy_mam->relevant_eh(e, true); } } virtual bool can_propagate() const { return m_mam->has_work(); } virtual void restart_eh() { if (m_fparams->m_mbqi) { m_model_finder->restart_eh(); m_model_checker->restart_eh(); } TRACE("mam_stats", m_mam->display(tout);); } virtual bool is_shared(enode * n) const { return (m_mam->is_shared(n) || m_lazy_mam->is_shared(n)); } virtual void adjust_model(proto_model * m) { if (m_fparams->m_mbqi) { m_model_finder->fix_model(m); } } virtual void propagate() { m_mam->match(); if (!m_context->relevancy() && use_ematching()) { ptr_vector::const_iterator it = m_context->begin_enodes(); ptr_vector::const_iterator end = m_context->end_enodes(); unsigned sz = static_cast(end - it); if (sz > m_new_enode_qhead) { m_context->push_trail(value_trail(m_new_enode_qhead)); it += m_new_enode_qhead; while (m_new_enode_qhead < sz) { enode * e = *it; m_mam->relevant_eh(e, false); m_lazy_mam->relevant_eh(e, true); m_new_enode_qhead++; it++; } } } } virtual quantifier_manager::check_model_result check_model(proto_model * m, obj_map const & root2value) { if (m_fparams->m_mbqi) { IF_VERBOSE(10, verbose_stream() << "(smt.mbqi)\n";); if (m_model_checker->check(m, root2value)) { return quantifier_manager::SAT; } else if (m_model_checker->has_new_instances()) { return quantifier_manager::RESTART; } } return quantifier_manager::UNKNOWN; } virtual void set_cancel(bool f) { // TODO: interrupt MAM and MBQI m_model_finder->set_cancel(f); m_model_checker->set_cancel(f); } virtual final_check_status final_check_eh(bool full) { if (!full) { if (m_fparams->m_qi_lazy_instantiation) return final_check_quant(); return FC_DONE; } else { return final_check_quant(); } } /** \brief Final check related with quantifiers... */ final_check_status final_check_quant() { if (use_ematching()) { if (m_lazy_matching_idx < m_fparams->m_qi_max_lazy_multipattern_matching) { m_lazy_mam->rematch(); m_context->push_trail(value_trail(m_lazy_matching_idx)); m_lazy_matching_idx++; } } return FC_DONE; } }; quantifier_manager_plugin * mk_default_plugin() { return alloc(default_qm_plugin); } }; z3-z3-4.4.1/src/smt/smt_quantifier.h000066400000000000000000000124171260446376700172270ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_quantifier.h Abstract: Quantifier reasoning support for smt::context. Author: Leonardo de Moura (leonardo) 2012-02-16. Revision History: --*/ #ifndef SMT_QUANTIFIER_H_ #define SMT_QUANTIFIER_H_ #include"ast.h" #include"statistics.h" #include"params.h" #include"smt_types.h" class proto_model; struct smt_params; namespace smt { class quantifier_manager_plugin; class quantifier_stat; class quantifier_manager { struct imp; imp * m_imp; public: quantifier_manager(context & ctx, smt_params & fp, params_ref const & p); ~quantifier_manager(); context & get_context() const; void set_plugin(quantifier_manager_plugin * plugin); void add(quantifier * q, unsigned generation); void del(quantifier * q); bool empty() const; bool is_shared(enode * n) const; quantifier_stat * get_stat(quantifier * q) const; unsigned get_generation(quantifier * q) const; bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, ptr_vector & used_enodes); bool add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation = 0); void init_search_eh(); void assign_eh(quantifier * q); void add_eq_eh(enode * n1, enode * n2); void relevant_eh(enode * n); final_check_status final_check_eh(bool full); void restart_eh(); bool can_propagate() const; void propagate(); enum check_model_result { SAT, UNKNOWN, RESTART }; bool model_based() const; bool mbqi_enabled(quantifier *q) const; // can mbqi instantiate this quantifier? void adjust_model(proto_model * m); check_model_result check_model(proto_model * m, obj_map const & root2value); void push(); void pop(unsigned num_scopes); void reset(); void set_cancel(bool f); void display(std::ostream & out) const; void display_stats(std::ostream & out, quantifier * q) const; void collect_statistics(::statistics & st) const; void reset_statistics(); ptr_vector::const_iterator begin_quantifiers() const; ptr_vector::const_iterator end_quantifiers() const; }; class quantifier_manager_plugin { public: quantifier_manager_plugin() {} virtual ~quantifier_manager_plugin() {} virtual void set_manager(quantifier_manager & qm) = 0; virtual quantifier_manager_plugin * mk_fresh() = 0; virtual void add(quantifier * q) = 0; virtual void del(quantifier * q) = 0; virtual bool is_shared(enode * n) const = 0; /** \brief This method is invoked whenever q is assigned to true. */ virtual void assign_eh(quantifier * q) = 0; /** \brief This method is invoked whenever n1 and n2 are merged into the same equivalence class. */ virtual void add_eq_eh(enode * n1, enode * n2) = 0; /** \brief This method is invoked whenever n is marked as relevant. */ virtual void relevant_eh(enode * n) = 0; /** \brief This method is invoked when a new search() is started. */ virtual void init_search_eh() = 0; /** \brief Final_check event handler. */ virtual final_check_status final_check_eh(bool full) = 0; /** \brief This method is invoked whenever the solver restarts. */ virtual void restart_eh() = 0; /** \brief Return true if the quantifier_manager can propagate information information back into the core. */ virtual bool can_propagate() const = 0; virtual void propagate() = 0; /** \brief Return true if the plugin is "model based" */ virtual bool model_based() const = 0; /** \brief Is "model based" instantiate allowed to instantiate this quantifier? */ virtual bool mbqi_enabled(quantifier *q) const {return true;} /** \brief Give a change to the plugin to adjust the interpretation of unintepreted functions. It can basically change the "else" of each uninterpreted function. */ virtual void adjust_model(proto_model * m) = 0; /** \brief Core invokes this method to check whether the candidate interpretation satisfies the quantifiers in the manager. It also provides a mapping from enodes to their interpretations. */ virtual quantifier_manager::check_model_result check_model(proto_model * m, obj_map const & root2value) = 0; virtual void push() = 0; virtual void pop(unsigned num_scopes) = 0; virtual void set_cancel(bool f) = 0; }; }; #endif z3-z3-4.4.1/src/smt/smt_quantifier_instances.h000066400000000000000000000034401260446376700212720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_quantifier_instances.h Abstract: Author: Leonardo de Moura (leonardo) 2008-03-26. Revision History: --*/ #ifndef SMT_QUANTIFIER_INSTANCES_H_ #define SMT_QUANTIFIER_INSTANCES_H_ namespace smt { class quantifier_instances; class quantifier_instance { quantifier * m_quantifier; double m_cost; enode * m_enodes[0]; quantifier_instance(quantifier * q, enode * const * enodes); friend class quantifier_instances; public: quantifier * get_quantifier() const { return m_quantifier; } unsigned get_num_decls() const { return m_quantifier->get_num_decls(); } double get_cost() const { return m_cost; } enode * const * get_enodes() const { return m_enodes; } bool operator==(quantifier_instance const & other) const; unsigned hash() const; }; class quantifier_instances { struct instance_lt { ptr_vector const & m_stack; instance_lt(ptr_vector const & s): m_stack(s) { } bool operator()(int i1, int i2) const { return m_stack[i1]->get_cost() < m_stack[i2]->get_cost(); } }; context & m_context; ast_manager & m_manager; obj_hashtable m_instances; //!< Set of instances. ptr_vector m_stack; //!< Stack for backtracking. heap m_queue; //!< Instantiation priority queue. unsigned_vector m_scopes; }; }; #endif /* SMT_QUANTIFIER_INSTANCES_H_ */ z3-z3-4.4.1/src/smt/smt_quantifier_stat.cpp000066400000000000000000000070151260446376700206130ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_quantifier_stat.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #include"smt_quantifier_stat.h" namespace smt { quantifier_stat::quantifier_stat(unsigned generation): m_size(0), m_depth(0), m_generation(generation), m_case_split_factor(1), m_num_nested_quantifiers(0), m_num_instances(0), m_num_instances_curr_search(0), m_num_instances_curr_branch(0), m_max_generation(0), m_max_cost(0.0f) { } quantifier_stat_gen::quantifier_stat_gen(ast_manager & m, region & r): m_manager(m), m_region(r) { } void quantifier_stat_gen::reset() { m_already_found.reset(); m_todo.reset(); m_case_split_factor = 1; } quantifier_stat * quantifier_stat_gen::operator()(quantifier * q, unsigned generation) { reset(); quantifier_stat * r = new (m_region) quantifier_stat(generation); m_todo.push_back(entry(q->get_expr())); while (!m_todo.empty()) { entry & e = m_todo.back(); expr * n = e.m_expr; unsigned depth = e.m_depth; bool depth_only = e.m_depth_only; m_todo.pop_back(); unsigned old_depth; if (m_already_found.find(n, old_depth)) { if (old_depth >= depth) continue; depth_only = true; } m_already_found.insert(n, depth); if (depth >= r->m_depth) r->m_depth = depth; if (!depth_only) { r->m_size++; if (is_quantifier(n)) r->m_num_nested_quantifiers ++; if (is_app(n) && to_app(n)->get_family_id() == m_manager.get_basic_family_id()) { unsigned num_args = to_app(n)->get_num_args(); // Remark: I'm approximating the case_split factor. // I'm also ignoring the case split factor due to theories. switch (to_app(n)->get_decl_kind()) { case OP_OR: if (depth == 0) m_case_split_factor *= num_args; else m_case_split_factor *= (num_args + 1); break; case OP_AND: if (depth > 0) m_case_split_factor *= (num_args + 1); break; case OP_IFF: if (depth == 0) m_case_split_factor *= 4; else m_case_split_factor *= 9; break; case OP_ITE: if (depth == 0) m_case_split_factor *= 4; else m_case_split_factor *= 9; break; default: break; } } } if (is_app(n)) { unsigned j = to_app(n)->get_num_args(); while (j > 0) { --j; m_todo.push_back(entry(to_app(n)->get_arg(j), depth + 1, depth_only)); } } } r->m_case_split_factor = m_case_split_factor.get_value(); return r; } }; z3-z3-4.4.1/src/smt/smt_quantifier_stat.h000066400000000000000000000071541260446376700202640ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_quantifier_stat.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #ifndef SMT_QUANTIFIER_STAT_H_ #define SMT_QUANTIFIER_STAT_H_ #include"ast.h" #include"obj_hashtable.h" #include"approx_nat.h" #include"region.h" namespace smt { /** \brief Store statistics for quantifiers. This information is used to implement heuristics for quantifier instantiation. */ class quantifier_stat { unsigned m_size; unsigned m_depth; unsigned m_generation; unsigned m_case_split_factor; //!< the product of the size of the clauses created by this quantifier. unsigned m_num_nested_quantifiers; unsigned m_num_instances; unsigned m_num_instances_curr_search; unsigned m_num_instances_curr_branch; //!< only updated if QI_TRACK_INSTANCES is true unsigned m_max_generation; //!< max. generation of an instance float m_max_cost; friend class quantifier_stat_gen; quantifier_stat(unsigned generation); public: unsigned get_size() const { return m_size; } unsigned get_depth() const { return m_depth; } unsigned get_generation() const { return m_generation; } unsigned get_case_split_factor() const { return m_case_split_factor; } unsigned get_num_nested_quantifiers() const { return m_num_nested_quantifiers; } unsigned get_num_instances() const { return m_num_instances; } unsigned get_num_instances_curr_search() const { return m_num_instances_curr_search; } unsigned & get_num_instances_curr_branch() { return m_num_instances_curr_branch; } void inc_num_instances() { m_num_instances++; m_num_instances_curr_search++; } void inc_num_instances_curr_branch() { m_num_instances_curr_branch++; } void reset_num_instances_curr_search() { m_num_instances_curr_search = 0; } void update_max_generation(unsigned g) { if (m_max_generation < g) m_max_generation = g; } unsigned get_max_generation() const { return m_max_generation; } void update_max_cost(float c) { if (m_max_cost < c) m_max_cost = c; } float get_max_cost() const { return m_max_cost; } }; /** \brief Functor used to generate quantifier statistics. */ class quantifier_stat_gen { struct entry { expr * m_expr; unsigned m_depth:31; bool m_depth_only:1; //!< track only the depth of this entry. entry():m_expr(0), m_depth(0), m_depth_only(false) {} entry(expr * n, unsigned depth = 0, bool depth_only = false):m_expr(n), m_depth(depth), m_depth_only(depth_only) {} }; ast_manager & m_manager; region & m_region; obj_map m_already_found; // expression to the max. depth it was reached. svector m_todo; approx_nat m_case_split_factor; void reset(); public: quantifier_stat_gen(ast_manager & m, region & r); quantifier_stat * operator()(quantifier * q, unsigned generation); }; }; #endif /* SMT_QUANTIFIER_STAT_H_ */ z3-z3-4.4.1/src/smt/smt_quick_checker.cpp000066400000000000000000000412741260446376700202160ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_quick_checker.cpp Abstract: Incomplete model checker. Author: Leonardo de Moura (leonardo) 2008-06-20. Revision History: --*/ #include"smt_context.h" #include"smt_quick_checker.h" #include"ast_pp.h" namespace smt { quick_checker::collector::collector(context & c): m_context(c), m_manager(c.get_manager()), m_conservative(true) { } void quick_checker::collector::init(quantifier * q) { m_num_vars = q->get_num_decls(); m_already_found.reserve(m_num_vars+1, false); m_candidates.reserve(m_num_vars+1); m_tmp_candidates.reserve(m_num_vars+1); for (unsigned i = 0; i < m_num_vars; i++) { m_already_found[i] = false; m_candidates[i].reset(); } m_cache.reset(); } /** \brief Returns true if there is a term (f ... n' ...) in the logical context such that n' is the i-th argument of f, and n and n' are in the same equivalence class. If f == 0 then true is returned (f == 0 means there is no parent to check) */ bool quick_checker::collector::check_arg(enode * n, func_decl * f, unsigned i) { if (!f || !m_conservative) return true; enode_vector::const_iterator it = m_context.begin_enodes_of(f); enode_vector::const_iterator end = m_context.end_enodes_of(f); for (; it != end; ++it) { enode * curr = *it; if (m_context.is_relevant(curr) && curr->is_cgr() && i < curr->get_num_args() && curr->get_arg(i)->get_root() == n->get_root()) return true; } return false; } void quick_checker::collector::collect_core(app * n, func_decl * p, unsigned i) { func_decl * f = n->get_decl(); unsigned num_args = n->get_num_args(); for (unsigned j = 0; j < num_args; j++) { expr * arg = n->get_arg(j); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (idx >= m_num_vars) return; if (m_already_found[idx] && m_conservative) { enode_set & s = m_candidates[idx]; enode_set & ns = m_tmp_candidates[idx]; if (s.empty()) continue; ns.reset(); enode_vector::const_iterator it = m_context.begin_enodes_of(f); enode_vector::const_iterator end = m_context.end_enodes_of(f); for (; it != end; ++it) { enode * curr = *it; if (m_context.is_relevant(curr) && curr->is_cgr() && check_arg(curr, p, i) && j < curr->get_num_args()) { enode * arg = curr->get_arg(j)->get_root(); // intersection if (s.contains(arg)) ns.insert(arg); } } SASSERT(m_conservative); s.swap(ns); } else { m_already_found[idx] = true; enode_set & s = m_candidates[idx]; enode_vector::const_iterator it = m_context.begin_enodes_of(f); enode_vector::const_iterator end = m_context.end_enodes_of(f); for (; it != end; ++it) { enode * curr = *it; if (m_context.is_relevant(curr) && curr->is_cgr() && check_arg(curr, p, i) && j < curr->get_num_args()) { enode * arg = curr->get_arg(j)->get_root(); s.insert(arg); } } } } else { if (n->get_family_id() != m_manager.get_basic_family_id()) collect(arg, n->get_decl(), j); else collect(arg, 0, 0); } } } void quick_checker::collector::collect(expr * n, func_decl * f, unsigned idx) { if (is_quantifier(n)) return; if (is_var(n)) return; if (is_ground(n)) return; entry e(n, f, idx); if (m_cache.contains(e)) return; m_cache.insert(e); collect_core(to_app(n), f, idx); } void quick_checker::collector::save_result(vector & candidates) { candidates.reserve(m_num_vars+1); for (unsigned i = 0; i < m_num_vars; i++) { enode_vector & v = candidates[i]; v.reset(); enode_set & s = m_candidates[i]; enode_set::iterator it = s.begin(); enode_set::iterator end = s.end(); for (; it != end; ++it) { enode * curr = *it; v.push_back(curr); } } TRACE("collector", tout << "candidates:\n"; for (unsigned i = 0; i < m_num_vars; i++) { tout << "var " << i << ":"; enode_vector & v = candidates[i]; enode_vector::iterator it = v.begin(); enode_vector::iterator end = v.end(); for (; it != end; ++it) tout << " #" << (*it)->get_owner_id(); tout << "\n"; }); } void quick_checker::collector::operator()(quantifier * q, bool conservative, vector & candidates) { flet l(m_conservative, conservative); init(q); TRACE("collector", tout << "model checking: #" << q->get_id() << "\n" << mk_pp(q, m_manager) << "\n";); collect(q->get_expr(), 0, 0); save_result(candidates); } quick_checker::quick_checker(context & c): m_context(c), m_manager(c.get_manager()), m_simplifier(c.get_simplifier()), m_collector(c), m_new_exprs(m_manager) { } /** \brief Instantiate instances unsatisfied by the current model. Return true if new instances were generated. */ bool quick_checker::instantiate_unsat(quantifier * q) { TRACE("quick_checker", tout << "instantiate instances unsatisfied by current model\n" << mk_pp(q, m_manager) << "\n";); m_candidate_vectors.reset(); m_collector(q, true, m_candidate_vectors); m_num_bindings = q->get_num_decls(); return process_candidates(q, true); } /** \brief Instantiate instances not satisfied by the current model. Return true if new instances were generated. */ bool quick_checker::instantiate_not_sat(quantifier * q) { TRACE("quick_checker", tout << "instantiate instances not satisfied by current model\n" << mk_pp(q, m_manager) << "\n";); m_candidate_vectors.reset(); m_collector(q, false, m_candidate_vectors); m_num_bindings = q->get_num_decls(); return process_candidates(q, false); } bool quick_checker::instantiate_not_sat(quantifier * q, unsigned num_candidates, expr * const * candidates) { // initialize m_candidates using the given set of candidates. m_candidate_vectors.reset(); m_num_bindings = q->get_num_decls(); m_candidate_vectors.reserve(m_num_bindings+1); for (unsigned i = 0; i < m_num_bindings; i++) { m_candidate_vectors[i].reset(); sort * s = q->get_decl_sort(i); for (unsigned j = 0; j < num_candidates; j++) { if (m_manager.get_sort(candidates[j]) == s) { expr * n = candidates[j]; m_context.internalize(n, false); enode * e = m_context.get_enode(n); m_candidate_vectors[i].push_back(e); } } } return process_candidates(q, false); } bool quick_checker::process_candidates(quantifier * q, bool unsat) { ptr_vector empty_used_enodes; buffer szs; buffer it; for (unsigned i = 0; i < m_num_bindings; i++) { unsigned sz = m_candidate_vectors[i].size(); if (sz == 0) return false; szs.push_back(sz); it.push_back(0); } TRACE("quick_checker_sizes", tout << mk_pp(q, m_manager) << "\n"; for (unsigned i = 0; i < szs.size(); i++) tout << szs[i] << " "; tout << "\n";); TRACE("quick_checker_candidates", tout << "candidates:\n"; for (unsigned i = 0; i < m_num_bindings; i++) { enode_vector & v = m_candidate_vectors[i]; enode_vector::iterator it = v.begin(); enode_vector::iterator end = v.end(); for (; it != end; ++it) tout << "#" << (*it)->get_owner_id() << " "; tout << "\n"; }); bool result = false; m_bindings.reserve(m_num_bindings+1, 0); do { for (unsigned i = 0; i < m_num_bindings; i++) m_bindings[m_num_bindings - i - 1] = m_candidate_vectors[i][it[i]]; if (!m_context.contains_instance(q, m_num_bindings, m_bindings.c_ptr())) { bool is_candidate = false; TRACE("quick_checker", tout << "processing bindings:"; for (unsigned i = 0; i < m_num_bindings; i++) tout << " #" << m_bindings[i]->get_owner_id(); tout << "\n";); if (unsat) is_candidate = check_quantifier(q, false); else is_candidate = !check_quantifier(q, true); if (is_candidate) { TRACE("quick_checker", tout << "found new candidate\n";); TRACE("quick_checker_sizes", tout << "found new candidate\n"; for (unsigned i = 0; i < m_num_bindings; i++) tout << "#" << m_bindings[i]->get_owner_id() << " "; tout << "\n";); unsigned max_generation = get_max_generation(m_num_bindings, m_bindings.c_ptr()); if (m_context.add_instance(q, 0 /* no pattern was used */, m_num_bindings, m_bindings.c_ptr(), max_generation, 0, // min_top_generation is only available for instances created by the MAM 0, // max_top_generation is only available for instances created by the MAM empty_used_enodes)) result = true; } } } while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); return result; } bool quick_checker::check_quantifier(quantifier * n, bool is_true) { bool r = check(n->get_expr(), is_true); m_new_exprs.reset(); m_check_cache.reset(); m_canonize_cache.reset(); return r; } bool quick_checker::all_args(app * a, bool is_true) { unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; i++) if (!check(a->get_arg(i), is_true)) return false; return true; } bool quick_checker::any_arg(app * a, bool is_true) { unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; i++) if (check(a->get_arg(i), is_true)) return true; return false; } bool quick_checker::check_core(expr * n, bool is_true) { SASSERT(m_manager.is_bool(n)); if (m_context.b_internalized(n) && m_context.is_relevant(n)) { lbool val = m_context.get_assignment(n); if (val != l_undef && is_true == (val == l_true)) return true; else return false; } if (!is_app(n)) return false; app * a = to_app(n); if (a->get_family_id() == m_manager.get_basic_family_id()) { switch (a->get_decl_kind()) { case OP_TRUE: return is_true; case OP_FALSE: return !is_true; case OP_NOT: return check(a->get_arg(0), !is_true); case OP_OR: return is_true ? any_arg(a, true) : all_args(a, false); case OP_AND: return is_true ? all_args(a, true) : any_arg(a, false); case OP_IFF: if (is_true) return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || (check(a->get_arg(0), false) && check(a->get_arg(1), false)); else return (check(a->get_arg(0), true) && check(a->get_arg(1), false)) || (check(a->get_arg(0), false) && check(a->get_arg(1), true)); case OP_ITE: if (check(a->get_arg(0), true)) return check(a->get_arg(1), is_true); else if (check(a->get_arg(0), false)) return check(a->get_arg(2), is_true); else return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true); case OP_EQ: if (is_true) { return canonize(a->get_arg(0)) == canonize(a->get_arg(1)); } else { expr * lhs = canonize(a->get_arg(0)); expr * rhs = canonize(a->get_arg(1)); if (m_context.e_internalized(lhs) && m_context.is_relevant(lhs) && m_context.e_internalized(rhs) && m_context.is_relevant(rhs) && m_context.get_enode(lhs)->get_root() != m_context.get_enode(rhs)->get_root()) return true; return m_manager.are_distinct(lhs, rhs); } default: break; } } expr * new_a = canonize(a); TRACE("quick_checker_canonizer", tout << "before:\n" << mk_pp(a, m_manager) << "\nafter:\n" << mk_pp(new_a, m_manager) << "\n";); if (m_context.lit_internalized(new_a) && m_context.is_relevant(new_a)) { lbool val = m_context.get_assignment(new_a); if (val != l_undef) return is_true == (val == l_true); } if (is_true && m_manager.is_true(new_a)) return true; if (!is_true && m_manager.is_false(new_a)) return true; return false; } bool quick_checker::check(expr * n, bool is_true) { expr_bool_pair p(n, is_true); bool r; if (m_check_cache.find(p, r)) return r; r = check_core(n, is_true); m_check_cache.insert(p, r); return r; } expr * quick_checker::canonize(expr * n) { if (is_var(n)) { unsigned idx = to_var(n)->get_idx(); if (idx >= m_num_bindings) return n; // VAR 0 is stored in the last position of m_bindings return m_bindings[m_num_bindings - idx - 1]->get_root()->get_owner(); } if (m_context.e_internalized(n)) return m_context.get_enode(n)->get_root()->get_owner(); if (!is_app(n) || to_app(n)->get_num_args() == 0) return n; expr * r; if (m_canonize_cache.find(n, r)) return r; bool has_arg_enodes = true; ptr_buffer new_args; ptr_buffer new_arg_enodes; unsigned num_args = to_app(n)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = canonize(to_app(n)->get_arg(i)); new_args.push_back(arg); if (m_context.e_internalized(arg)) new_arg_enodes.push_back(m_context.get_enode(arg)); else has_arg_enodes = false; } if (has_arg_enodes) { enode * e = m_context.get_enode_eq_to(to_app(n)->get_decl(), num_args, new_arg_enodes.c_ptr()); if (e) { m_canonize_cache.insert(n, e->get_root()->get_owner()); return e->get_root()->get_owner(); } } // substitute by values in the model for (unsigned i = 0; i < num_args; i++) { expr * arg = new_args[i]; if (m_context.e_internalized(arg)) { expr_ref new_value(m_manager); if (m_context.get_value(m_context.get_enode(arg), new_value)) { new_args[i] = new_value; m_new_exprs.push_back(new_value); } } } expr_ref new_expr(m_manager); m_simplifier.mk_app(to_app(n)->get_decl(), num_args, new_args.c_ptr(), new_expr); m_new_exprs.push_back(new_expr); m_canonize_cache.insert(n, new_expr); return new_expr; } }; z3-z3-4.4.1/src/smt/smt_quick_checker.h000066400000000000000000000073221260446376700176570ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_quick_cheker.h Abstract: Incomplete model checker. Author: Leonardo de Moura (leonardo) 2008-06-20. Revision History: --*/ #ifndef SMT_QUICK_CHECKER_H_ #define SMT_QUICK_CHECKER_H_ #include"ast.h" #include"simplifier.h" #include"obj_hashtable.h" namespace smt { class context; /** \brief Simple object for finding quantifier instantiations that are falsified by the current model. \remark The code for selecting candidates is very naive. */ class quick_checker { typedef obj_hashtable enode_set; /** \brief Functor for collecting candidates for quantifier instantiation. */ class collector { context & m_context; ast_manager & m_manager; bool m_conservative; unsigned m_num_vars; svector m_already_found; // mapping from var_idx -> bool vector m_candidates; // mapping from var_idx -> set of candidates vector m_tmp_candidates; // auxiliary mapping from var_idx -> set of candidates struct entry { expr * m_expr; func_decl * m_parent; unsigned m_parent_pos; entry(expr * n = 0, func_decl * d = 0, unsigned p = 0):m_expr(n), m_parent(d), m_parent_pos(p) {} unsigned hash() const { return m_parent ? mk_mix(m_expr->get_id(), m_parent->get_id(), m_parent_pos) : m_expr->get_id(); } bool operator==(entry const & e) const { return m_expr == e.m_expr && m_parent == e.m_parent && m_parent_pos == e.m_parent_pos; } }; typedef hashtable, default_eq > cache; cache m_cache; void init(quantifier * q); bool check_arg(enode * n, func_decl * f, unsigned i); void collect_core(app * n, func_decl * p, unsigned i); void collect(expr * n, func_decl * f, unsigned idx); void save_result(vector & candidates); public: collector(context & c); void operator()(quantifier * q, bool conservative, vector & candidates); }; typedef std::pair expr_bool_pair; typedef pair_hash, int_hash> expr_bool_pair_hash; typedef map > check_cache; typedef obj_map canonize_cache; context & m_context; ast_manager & m_manager; simplifier & m_simplifier; collector m_collector; expr_ref_vector m_new_exprs; vector m_candidate_vectors; check_cache m_check_cache; canonize_cache m_canonize_cache; unsigned m_num_bindings; ptr_vector m_bindings; bool all_args(app * a, bool is_true); bool any_arg(app * a, bool is_true); bool check_core(expr * n, bool is_true); bool check(expr * n, bool is_true); bool check_quantifier(quantifier * n, bool is_true); expr * canonize(expr * n); bool process_candidates(quantifier * q, bool unsat); public: quick_checker(context & c); bool instantiate_unsat(quantifier * q); bool instantiate_not_sat(quantifier * q); bool instantiate_not_sat(quantifier * q, unsigned num_candidates, expr * const * candidates); }; }; #endif /* SMT_QUICK_CHECKER_H_ */ z3-z3-4.4.1/src/smt/smt_relevancy.cpp000066400000000000000000000565331260446376700174120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_relevancy.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-04. Revision History: --*/ #include"smt_context.h" #include"smt_relevancy.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"ast_smt2_pp.h" namespace smt { void relevancy_eh::mark_as_relevant(relevancy_propagator & rp, expr * n) { rp.mark_as_relevant(n); } void relevancy_eh::mark_args_as_relevant(relevancy_propagator & rp, app * n) { unsigned j = n->get_num_args(); while (j > 0) { --j; rp.mark_as_relevant(n->get_arg(j)); } } void simple_relevancy_eh::operator()(relevancy_propagator & rp) { rp.mark_as_relevant(m_target); } void pair_relevancy_eh::operator()(relevancy_propagator & rp) { if (!rp.is_relevant(m_source1)) return; if (!rp.is_relevant(m_source2)) return; rp.mark_as_relevant(m_target); } class and_relevancy_eh : public relevancy_eh { app * m_parent; public: and_relevancy_eh(app * p):m_parent(p) {} virtual ~and_relevancy_eh() {} virtual void operator()(relevancy_propagator & rp); }; class or_relevancy_eh : public relevancy_eh { app * m_parent; public: or_relevancy_eh(app * p):m_parent(p) {} virtual ~or_relevancy_eh() {} virtual void operator()(relevancy_propagator & rp); }; class ite_relevancy_eh : public relevancy_eh { app * m_parent; public: ite_relevancy_eh(app * p):m_parent(p) {} virtual ~ite_relevancy_eh() {} virtual void operator()(relevancy_propagator & rp); }; class ite_term_relevancy_eh : public relevancy_eh { app * m_parent; app * m_then_eq; app * m_else_eq; public: ite_term_relevancy_eh(app * p, app * then_eq, app * else_eq):m_parent(p), m_then_eq(then_eq), m_else_eq(else_eq) {} virtual ~ite_term_relevancy_eh() {} virtual void operator()(relevancy_propagator & rp); }; relevancy_propagator::relevancy_propagator(context & ctx): m_context(ctx) { } bool relevancy_propagator::enabled() const { return m_context.relevancy(); } region & relevancy_propagator::get_region() const { return m_context.get_region(); } ast_manager & relevancy_propagator::get_manager() const { return m_context.get_manager(); } void relevancy_propagator::add_dependency(expr * src, expr * target) { if (!enabled()) return; if (is_relevant(src)) mark_as_relevant(target); else add_handler(src, mk_relevancy_eh(simple_relevancy_eh(target))); } relevancy_eh * relevancy_propagator::mk_or_relevancy_eh(app * n) { SASSERT(get_manager().is_or(n)); return mk_relevancy_eh(or_relevancy_eh(n)); } relevancy_eh * relevancy_propagator::mk_and_relevancy_eh(app * n) { SASSERT(get_manager().is_and(n)); return mk_relevancy_eh(and_relevancy_eh(n)); } relevancy_eh * relevancy_propagator::mk_ite_relevancy_eh(app * n) { SASSERT(get_manager().is_ite(n)); return mk_relevancy_eh(ite_relevancy_eh(n)); } relevancy_eh * relevancy_propagator::mk_term_ite_relevancy_eh(app * c, app * t, app * e) { return mk_relevancy_eh(ite_term_relevancy_eh(c, t, e)); } struct relevancy_propagator_imp : public relevancy_propagator { unsigned m_qhead; expr_ref_vector m_relevant_exprs; obj_hashtable m_is_relevant; typedef list relevancy_ehs; obj_map m_relevant_ehs; obj_map m_watches[2]; struct eh_trail { enum kind { POS_WATCH, NEG_WATCH, HANDLER }; kind m_kind; expr * m_node; eh_trail(expr * n):m_kind(HANDLER), m_node(n) {} eh_trail(expr * n, bool val):m_kind(val ? POS_WATCH : NEG_WATCH), m_node(n) {} kind get_kind() const { return m_kind; } expr * get_node() const { return m_node; } }; svector m_trail; struct scope { unsigned m_relevant_exprs_lim; unsigned m_trail_lim; }; svector m_scopes; bool m_propagating; relevancy_propagator_imp(context & ctx): relevancy_propagator(ctx), m_qhead(0), m_relevant_exprs(ctx.get_manager()), m_propagating(false) {} virtual ~relevancy_propagator_imp() { undo_trail(0); } relevancy_ehs * get_handlers(expr * n) { relevancy_ehs * r = 0; m_relevant_ehs.find(n, r); SASSERT(m_relevant_ehs.contains(n) || r == 0); return r; } void set_handlers(expr * n, relevancy_ehs * ehs) { if (ehs == 0) m_relevant_ehs.erase(n); else m_relevant_ehs.insert(n, ehs); } relevancy_ehs * get_watches(expr * n, bool val) { relevancy_ehs * r = 0; m_watches[val ? 1 : 0].find(n, r); SASSERT(m_watches[val ? 1 : 0].contains(n) || r == 0); return r; } void set_watches(expr * n, bool val, relevancy_ehs * ehs) { if (ehs == 0) m_watches[val ? 1 : 0].erase(n); else m_watches[val ? 1 : 0].insert(n, ehs); } void push_trail(eh_trail const & t) { get_manager().inc_ref(t.get_node()); m_trail.push_back(t); } virtual void add_handler(expr * source, relevancy_eh * eh) { if (!enabled()) return; if (is_relevant_core(source)) { eh->operator()(*this, source); } else { SASSERT(eh); push_trail(eh_trail(source)); set_handlers(source, new (get_region()) relevancy_ehs(eh, get_handlers(source))); } } virtual void add_watch(expr * n, bool val, relevancy_eh * eh) { if (!enabled()) return; lbool lval = m_context.find_assignment(n); if (!val) lval = ~lval; switch (lval) { case l_false: return; case l_undef: SASSERT(eh); set_watches(n, val, new (get_region()) relevancy_ehs(eh, get_watches(n, val))); push_trail(eh_trail(n, val)); break; case l_true: eh->operator()(*this, n, val); break; } } virtual void add_watch(expr * n, bool val, expr * target) { if (!enabled()) return; lbool lval = m_context.find_assignment(n); if (!val) lval = ~lval; switch (lval) { case l_false: return; case l_undef: add_watch(n, val, mk_relevancy_eh(simple_relevancy_eh(target))); break; case l_true: mark_as_relevant(target); propagate(); break; } } bool is_relevant_core(expr * n) const { return m_is_relevant.contains(n); } virtual bool is_relevant(expr * n) const { return !enabled() || is_relevant_core(n); } virtual void push() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_relevant_exprs_lim = m_relevant_exprs.size(); s.m_trail_lim = m_trail.size(); } virtual void pop(unsigned num_scopes) { SASSERT(m_context.get_scope_level() == m_scopes.size()); unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; unmark_relevant_exprs(s.m_relevant_exprs_lim); undo_trail(s.m_trail_lim); m_scopes.shrink(new_lvl); } /** \brief Unmark expressions marked as relevant. */ void unmark_relevant_exprs(unsigned old_lim) { SASSERT(old_lim <= m_relevant_exprs.size()); unsigned i = m_relevant_exprs.size(); while (i != old_lim) { --i; expr * n = m_relevant_exprs.get(i); m_is_relevant.erase(n); TRACE("propagate_relevancy", tout << "unmarking:\n" << mk_ismt2_pp(n, get_manager()) << "\n";); } m_relevant_exprs.shrink(old_lim); m_qhead = m_relevant_exprs.size(); } void undo_trail(unsigned old_lim) { SASSERT(old_lim <= m_trail.size()); ast_manager & m = get_manager(); unsigned i = m_trail.size(); while (i != old_lim) { --i; eh_trail & t = m_trail[i]; expr * n = t.get_node(); relevancy_ehs * ehs; switch (t.get_kind()) { case eh_trail::POS_WATCH: ehs = get_watches(n, true); SASSERT(ehs); set_watches(n, true, ehs->tail()); break; case eh_trail::NEG_WATCH: ehs = get_watches(n, false); SASSERT(ehs); set_watches(n, false, ehs->tail()); break; case eh_trail::HANDLER: ehs = get_handlers(n); SASSERT(ehs); set_handlers(n, ehs->tail()); break; default: UNREACHABLE(); break; } m.dec_ref(n); } m_trail.shrink(old_lim); } void set_relevant(expr * n) { m_is_relevant.insert(n); m_relevant_exprs.push_back(n); m_context.relevant_eh(n); } /** \brief Mark an expression as relevant and propagate the relevancy to its descendants. */ void mark_and_propagate(expr * n) { if (!enabled()) return; if (!is_relevant_core(n)) { mark_as_relevant(n); propagate(); } } /** \brief Mark the given expression as relevant if it is not already marked. */ virtual void mark_as_relevant(expr * n) { if (!enabled()) return; if (!is_relevant_core(n)) { enode * e = m_context.find_enode(n); if (e != 0) { enode * curr = e; do { set_relevant(curr->get_owner()); curr = curr->get_next(); } while (curr != e); } else { set_relevant(n); } } } /** \brief Marks the children of n as relevant. \pre n is marked as relevant. */ void propagate_relevant_app(app * n) { SASSERT(is_relevant_core(n)); unsigned j = n->get_num_args(); while (j > 0) { --j; mark_as_relevant(n->get_arg(j)); } } /** \brief Propagate relevancy for an or-application. */ void propagate_relevant_or(app * n) { SASSERT(get_manager().is_or(n)); lbool val = m_context.find_assignment(n); // If val is l_undef, then the expression // is a root, and no boolean variable was created for it. if (val == l_undef) val = l_true; switch (val) { case l_false: propagate_relevant_app(n); break; case l_undef: break; case l_true: { expr * true_arg = 0; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (m_context.find_assignment(arg) == l_true) { if (is_relevant_core(arg)) return; else if (!true_arg) true_arg = arg; } } if (true_arg) mark_as_relevant(true_arg); break; } } } /** \brief Propagate relevancy for an and-application. */ void propagate_relevant_and(app * n) { lbool val = m_context.find_assignment(n); switch (val) { case l_false: { expr * false_arg = 0; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (m_context.find_assignment(arg) == l_false) { if (is_relevant_core(arg)) return; else if (!false_arg) false_arg = arg; } } if (false_arg) mark_as_relevant(false_arg); break; } case l_undef: break; case l_true: propagate_relevant_app(n); break; } } /** \brief Propagate relevancy for an ite-expression. */ void propagate_relevant_ite(app * n) { TRACE("propagate_relevant_ite", tout << "propagating relevancy for #" << n->get_id() << "\n" << mk_pp(n, get_manager()) << "\n";); mark_as_relevant(n->get_arg(0)); switch (m_context.find_assignment(n->get_arg(0))) { case l_false: TRACE("propagate_relevant_ite", tout << "marking as relevant: " << mk_pp(n->get_arg(2), get_manager()) << "\n";); mark_as_relevant(n->get_arg(2)); break; case l_undef: TRACE("propagate_relevant_ite", tout << "ite c is unassigned\n";); break; case l_true: TRACE("propagate_relevant_ite", tout << "marking as relevant: " << mk_pp(n->get_arg(1), get_manager()) << "\n";); mark_as_relevant(n->get_arg(1)); break; } } /** \brief Propagate relevancy to the arguments of recently marked expressions. That is, expressions that are located at positions [m_qhead, m_relevant_exprs.size()) in the stack of relevant expressions. */ virtual void propagate() { if (m_propagating) { return; } flet l_prop(m_propagating, true); ast_manager & m = get_manager(); while (m_qhead < m_relevant_exprs.size()) { expr * n = m_relevant_exprs.get(m_qhead); TRACE("propagate_relevancy_to_args", tout << "propagating relevancy to args of #" << n->get_id() << "\n";); TRACE("propagate_relevancy", tout << "marking as relevant:\n" << mk_bounded_pp(n, m) << "\n";); SASSERT(is_relevant_core(n)); m_qhead++; if (is_app(n)) { family_id fid = to_app(n)->get_family_id(); if (fid == m.get_basic_family_id()) { switch (to_app(n)->get_decl_kind()) { case OP_OR: propagate_relevant_or(to_app(n)); break; case OP_AND: propagate_relevant_and(to_app(n)); break; case OP_ITE: propagate_relevant_ite(to_app(n)); break; default: propagate_relevant_app(to_app(n)); break; } } else { propagate_relevant_app(to_app(n)); } } relevancy_ehs * ehs = get_handlers(n); while (ehs != 0) { ehs->head()->operator()(*this, n); ehs = ehs->tail(); } } } virtual bool can_propagate() const { return m_qhead < m_relevant_exprs.size(); } virtual void assign_eh(expr * n, bool val) { if (!enabled()) return; ast_manager & m = get_manager(); SASSERT(enabled()); if (is_relevant_core(n)) { if (m.is_or(n)) propagate_relevant_or(to_app(n)); else if (m.is_and(n)) propagate_relevant_and(to_app(n)); } relevancy_ehs * ehs = get_watches(n, val); while (ehs != 0) { ehs->head()->operator()(*this, n, val); ehs = ehs->tail(); } } virtual void display(std::ostream & out) const { if (enabled() && !m_relevant_exprs.empty()) { out << "relevant exprs:\n"; for (unsigned i = 0; i < m_relevant_exprs.size(); i++) { out << "#" << m_relevant_exprs.get(i)->get_id() << " "; } out << "\n"; } } #ifdef Z3DEBUG bool check_relevancy_app(app * n) const { SASSERT(is_relevant(n)); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { CTRACE("relevancy_bug", !is_relevant(n->get_arg(i)), tout << "n: " << mk_ismt2_pp(n, get_manager()) << "\ni: " << i << "\n";); SASSERT(is_relevant(n->get_arg(i))); } return true; } virtual bool check_relevancy_or(app * n, bool root) const { lbool val = root ? l_true : m_context.find_assignment(n); if (val == l_false) return check_relevancy_app(n); if (val == l_true) { unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (m_context.find_assignment(arg) == l_true && is_relevant(arg)) return true; } TRACE("check_relevancy", tout << "failed:\n" << mk_ll_pp(n, get_manager()); display(tout);); UNREACHABLE(); } return true; } bool check_relevancy_and(app * n) const { lbool val = m_context.find_assignment(n); if (val == l_true) return check_relevancy_app(n); if (val == l_false) { unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (m_context.find_assignment(arg) == l_false && is_relevant(arg)) return true; } UNREACHABLE(); } return true; } bool check_relevancy_ite(app * n) const { SASSERT(is_relevant(n->get_arg(0))); switch (m_context.find_assignment(n->get_arg(0))) { case l_false: if (get_manager().is_bool(n)) { TRACE("ite_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";); SASSERT(is_relevant(n->get_arg(2))); } else { app_ref eq(get_manager()); eq = m_context.mk_eq_atom(n, n->get_arg(2)); SASSERT(is_relevant(eq.get())); } break; case l_undef: break; case l_true: if (get_manager().is_bool(n)) { SASSERT(is_relevant(n->get_arg(1))); } else { app_ref eq(get_manager()); eq = m_context.mk_eq_atom(n, n->get_arg(1)); SASSERT(is_relevant(eq.get())); } break; } return true; } bool check_relevancy(expr_ref_vector const & v) const { SASSERT(!can_propagate()); ast_manager & m = get_manager(); unsigned sz = v.size(); for (unsigned i = 0; i < sz; i++) { expr * n = v.get(i); if (is_relevant(n)) { TRACE("check_relevancy", tout << "checking:\n" << mk_ll_pp(n, get_manager()) << "internalized: " << m_context.find_enode(n) << "\n";); if (m.is_or(n)) { SASSERT(check_relevancy_or(to_app(n), false)); } else if (m.is_and(n)) { SASSERT(check_relevancy_and(to_app(n))); } else if (m.is_ite(n)) { SASSERT(check_relevancy_ite(to_app(n))); } else if (is_app(n)) { SASSERT(check_relevancy_app(to_app(n))); } else { enode * n0 = m_context.find_enode(n); if (n0 != 0) { enode * n = n0->get_next(); while (n0 != n) { SASSERT(is_relevant(n->get_owner())); n = n->get_next(); } } } } } return true; } #endif }; void and_relevancy_eh::operator()(relevancy_propagator & rp) { if (rp.is_relevant(m_parent)) static_cast(rp).propagate_relevant_and(m_parent); } void or_relevancy_eh::operator()(relevancy_propagator & rp) { if (rp.is_relevant(m_parent)) static_cast(rp).propagate_relevant_or(m_parent); } void ite_relevancy_eh::operator()(relevancy_propagator & rp) { if (rp.is_relevant(m_parent)) { static_cast(rp).propagate_relevant_ite(m_parent); } } void ite_term_relevancy_eh::operator()(relevancy_propagator & rp) { if (!rp.is_relevant(m_parent)) return; rp.mark_as_relevant(m_parent->get_arg(0)); switch (rp.get_context().get_assignment(m_parent->get_arg(0))) { case l_false: TRACE("ite_term_relevancy", tout << "marking else: #" << m_else_eq->get_id() << "\n";); rp.mark_as_relevant(m_else_eq); break; case l_undef: break; case l_true: TRACE("ite_term_relevancy", tout << "marking then: #" << m_then_eq->get_id() << "\n";); rp.mark_as_relevant(m_then_eq); break; } } relevancy_propagator * mk_relevancy_propagator(context & ctx) { return alloc(relevancy_propagator_imp, ctx); } }; z3-z3-4.4.1/src/smt/smt_relevancy.h000066400000000000000000000140331260446376700170440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_relevancy.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-04. Revision History: --*/ #ifndef SMT_RELEVANCY_H_ #define SMT_RELEVANCY_H_ #include"ast.h" namespace smt { class context; class relevancy_propagator; class relevancy_eh { protected: void mark_as_relevant(relevancy_propagator & rp, expr * n); void mark_args_as_relevant(relevancy_propagator & rp, app * n); public: relevancy_eh() {} virtual ~relevancy_eh() {} /** \brief This method is invoked when n is marked as relevant. */ virtual void operator()(relevancy_propagator & rp, expr * n) { operator()(rp); } /** \brief This method is invoked when atom is assigned to val. */ virtual void operator()(relevancy_propagator & rp, expr * atom, bool val) { operator()(rp); } /** \brief Fallback for the two previous methods. */ virtual void operator()(relevancy_propagator & rp) {} }; class simple_relevancy_eh : public relevancy_eh { expr * m_target; public: simple_relevancy_eh(expr * t):m_target(t) {} virtual ~simple_relevancy_eh() {} virtual void operator()(relevancy_propagator & rp); }; /** \brief Propagate relevancy to m_target if both m_source1 and m_source2 are relevant. */ class pair_relevancy_eh : public relevancy_eh { expr * m_source1; expr * m_source2; expr * m_target; public: pair_relevancy_eh(expr * s1, expr * s2, expr * t):m_source1(s1), m_source2(s2), m_target(t) {} virtual ~pair_relevancy_eh() {} virtual void operator()(relevancy_propagator & rp); }; /** \brief Relevancy propagator. The relevancy propagation constraints are specified to the relevancy propagator using the methods: - add_handler - add_watch This class also provides helper methods for specifying commonly used constraints. It uses the following API from smt::context - find_assignment(expr * n) - find_enode(expr * n) It notifies smt::context that an expression became relevant by invoking - relevant_eh(expr * n) smt::context notifies the relevancy_propagator that a literal was assigned by invoking assign_eh(n, bool val) */ class relevancy_propagator { protected: context & m_context; public: relevancy_propagator(context & ctx); virtual ~relevancy_propagator() {} context & get_context() { return m_context; } /** \brief Install an event handler that is invoked whenever n is marked as relevant. */ virtual void add_handler(expr * n, relevancy_eh * eh) = 0; /** \brief Install an event handler that is invoked whenever n is assigned to the given value. The relevancy propagator is notified of new assignments by the method assign_eh. */ virtual void add_watch(expr * n, bool val, relevancy_eh * eh) = 0; /** \brief Install an event handler that just marks target as relevant whenever n is assigned to the given value. The relevancy propagator is notified of new assignments by the method assign_eh. */ virtual void add_watch(expr * n, bool val, expr * target) = 0; /** \brief smt::context invokes this method whenever the expression is assigned to true/false */ virtual void assign_eh(expr * n, bool val) = 0; /** \brief Mark the given expression is relevant. */ virtual void mark_as_relevant(expr * n) = 0; /** \brief Return true if the given expression is marked as relevant. */ virtual bool is_relevant(expr * n) const = 0; /** \brief Propagate relevancy using the event handlers specified by add_handler and add_watch, and the structure of the expressions already marked as relevant. */ virtual void propagate() = 0; /** \brief Return true if it can propagate relevancy. */ virtual bool can_propagate() const = 0; /** \brief Create a backtracking point */ virtual void push() = 0; /** \brief Backtrack. */ virtual void pop(unsigned num_scopes) = 0; /** \brief Display relevant expressions. */ virtual void display(std::ostream & out) const = 0; #ifdef Z3DEBUG virtual bool check_relevancy(expr_ref_vector const & v) const = 0; virtual bool check_relevancy_or(app * n, bool root) const = 0; #endif // -------------------------- // // Helper method // // -------------------------- /** \brief Return true if relevancy propagation is enabled. */ bool enabled() const; /** \Brief Return the region allocator for the smt::context that owns this propagator. */ region & get_region() const; /** \Brief Return the ast_manager for the smt::context that owns this propagator. */ ast_manager & get_manager() const; template relevancy_eh * mk_relevancy_eh(Eh const & eh) { return new (get_region()) Eh(eh); } /** \brief Creates an event handler that marks target as relevant whenever src is marked as relevant. */ void add_dependency(expr * src, expr * target); relevancy_eh * mk_or_relevancy_eh(app * n); relevancy_eh * mk_and_relevancy_eh(app * n); relevancy_eh * mk_ite_relevancy_eh(app * n); relevancy_eh * mk_term_ite_relevancy_eh(app * c, app * t, app * e); }; relevancy_propagator * mk_relevancy_propagator(context & ctx); }; #endif /* SMT_RELEVANCY_H_ */ z3-z3-4.4.1/src/smt/smt_setup.cpp000066400000000000000000001102501260446376700165450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_setup.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-24. Revision History: --*/ #include"smt_context.h" #include"smt_setup.h" #include"static_features.h" #include"theory_arith.h" #include"theory_dense_diff_logic.h" #include"theory_diff_logic.h" #include"theory_utvpi.h" #include"theory_array.h" #include"theory_array_full.h" #include"theory_bv.h" #include"theory_datatype.h" #include"theory_dummy.h" #include"theory_dl.h" #include"theory_seq_empty.h" #include"theory_pb.h" #include"theory_fpa.h" namespace smt { setup::setup(context & c, smt_params & params): m_context(c), m_manager(c.get_manager()), m_params(params), m_already_configured(false) { } void setup::operator()(config_mode cm) { SASSERT(m_context.get_scope_level() == 0); SASSERT(!m_context.already_internalized()); SASSERT(!m_already_configured); // if (m_params.m_mbqi && m_params.m_model_compact) { // warning_msg("ignoring MODEL_COMPACT=true because it cannot be used with MBQI=true"); // m_params.m_model_compact = false; // } TRACE("setup", tout << "configuring logical context, logic: " << m_logic << "\n";); m_already_configured = true; switch (cm) { case CFG_BASIC: setup_unknown(); break; case CFG_LOGIC: setup_default(); break; case CFG_AUTO: setup_auto_config(); break; } } void setup::setup_default() { if (m_logic == "QF_UF") setup_QF_UF(); else if (m_logic == "QF_RDL") setup_QF_RDL(); else if (m_logic == "QF_IDL") setup_QF_IDL(); else if (m_logic == "QF_UFIDL") setup_QF_UFIDL(); else if (m_logic == "QF_LRA") setup_QF_LRA(); else if (m_logic == "QF_LIA") setup_QF_LIA(); else if (m_logic == "QF_UFLIA") setup_QF_UFLIA(); else if (m_logic == "QF_UFLRA") setup_QF_UFLRA(); else if (m_logic == "QF_AX") setup_QF_AX(); else if (m_logic == "QF_AUFLIA") setup_QF_AUFLIA(); else if (m_logic == "QF_BV") setup_QF_BV(); else if (m_logic == "QF_AUFBV") setup_QF_AUFBV(); else if (m_logic == "QF_ABV") setup_QF_AUFBV(); else if (m_logic == "QF_UFBV") setup_QF_AUFBV(); else if (m_logic == "QF_BVRE") setup_QF_BVRE(); else if (m_logic == "AUFLIA") setup_AUFLIA(); else if (m_logic == "AUFLIRA") setup_AUFLIRA(); else if (m_logic == "AUFNIRA") setup_AUFNIRA(); else if (m_logic == "AUFLIA+") setup_AUFLIA(); else if (m_logic == "AUFLIA-") setup_AUFLIA(); else if (m_logic == "AUFLIRA+") setup_AUFLIRA(); else if (m_logic == "AUFLIRA-") setup_AUFLIRA(); else if (m_logic == "AUFNIRA+") setup_AUFLIRA(); else if (m_logic == "AUFNIRA-") setup_AUFLIRA(); else if (m_logic == "UFNIA") setup_UFNIA(); else if (m_logic == "UFLRA") setup_UFLRA(); else if (m_logic == "LRA") setup_LRA(); else if (m_logic == "QF_FP") setup_QF_FP(); else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP") setup_QF_FPBV(); else setup_unknown(); } void setup::setup_auto_config() { static_features st(m_manager); IF_VERBOSE(100, verbose_stream() << "(smt.configuring)\n";); TRACE("setup", tout << "setup, logic: " << m_logic << "\n";); // HACK: do not collect features for QF_BV and QF_AUFBV... since they do not use them... if (m_logic == "QF_BV") { setup_QF_BV(); } else if (m_logic == "QF_AUFBV" || m_logic == "QF_ABV" || m_logic == "QF_UFBV") { setup_QF_AUFBV(); } else { IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";); st.collect(m_context.get_num_asserted_formulas(), m_context.get_asserted_formulas()); IF_VERBOSE(1000, st.display_primitive(verbose_stream());); if (m_logic == "QF_UF") setup_QF_UF(st); else if (m_logic == "QF_RDL") setup_QF_RDL(st); else if (m_logic == "QF_IDL") setup_QF_IDL(st); else if (m_logic == "QF_UFIDL") setup_QF_UFIDL(st); else if (m_logic == "QF_LRA") setup_QF_LRA(st); else if (m_logic == "QF_LIA") setup_QF_LIA(st); else if (m_logic == "QF_UFLIA") setup_QF_UFLIA(st); else if (m_logic == "QF_UFLRA") setup_QF_UFLRA(); else if (m_logic == "QF_AX") setup_QF_AX(st); else if (m_logic == "QF_BVRE") setup_QF_BVRE(); else if (m_logic == "QF_AUFLIA") setup_QF_AUFLIA(st); else if (m_logic == "AUFLIA") setup_AUFLIA(st); else if (m_logic == "AUFLIRA") setup_AUFLIRA(); else if (m_logic == "AUFNIRA") setup_AUFNIRA(); else if (m_logic == "AUFLIA+") setup_AUFLIA(); else if (m_logic == "AUFLIA-") setup_AUFLIA(); else if (m_logic == "AUFLIRA+") setup_AUFLIRA(); else if (m_logic == "AUFLIRA-") setup_AUFLIRA(); else if (m_logic == "AUFNIRA+") setup_AUFLIRA(); else if (m_logic == "AUFNIRA-") setup_AUFLIRA(); else if (m_logic == "UFNIA") setup_UFNIA(); else if (m_logic == "LRA") setup_LRA(); else setup_unknown(st); } } void check_no_arithmetic(static_features const & st, char const * logic) { if (st.m_num_arith_ineqs > 0 || st.m_num_arith_terms > 0 || st.m_num_arith_eqs > 0) throw default_exception("Benchmark constains arithmetic, but specified loging does not support it."); } void setup::setup_QF_UF() { m_params.m_relevancy_lvl = 0; m_params.m_nnf_cnf = false; } void setup::setup_QF_BVRE() { setup_QF_BV(); setup_QF_LIA(); m_context.register_plugin(alloc(smt::theory_seq_empty, m_manager)); } void setup::setup_QF_UF(static_features const & st) { check_no_arithmetic(st, "QF_UF"); m_params.m_relevancy_lvl = 0; m_params.m_nnf_cnf = false; m_params.m_restart_strategy = RS_LUBY; m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; m_params.m_random_initial_activity = IA_RANDOM; TRACE("setup", tout << "st.m_num_theories: " << st.m_num_theories << "\n"; tout << "st.m_num_uninterpreted_functions: " << st.m_num_uninterpreted_functions << "\n";); } void setup::setup_QF_RDL() { m_params.m_relevancy_lvl = 0; m_params.m_arith_expand_eqs = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_nnf_cnf = false; setup_mi_arith(); } static bool is_dense(static_features const & st) { return st.m_num_uninterpreted_constants < 1000 && (st.m_num_arith_eqs + st.m_num_arith_ineqs) > st.m_num_uninterpreted_constants * 9; } bool is_in_diff_logic(static_features const & st) { return st.m_num_arith_eqs == st.m_num_diff_eqs && st.m_num_arith_terms == st.m_num_diff_terms && st.m_num_arith_ineqs == st.m_num_diff_ineqs; } bool is_diff_logic(static_features const & st) { return is_in_diff_logic(st) && (st.m_num_diff_ineqs > 0 || st.m_num_diff_eqs > 0 || st.m_num_diff_terms > 0) ; } void check_no_uninterpreted_functions(static_features const & st, char const * logic) { if (st.m_num_uninterpreted_functions != 0) throw default_exception("Benchmark contains uninterpreted function symbols, but specified logic does not support them."); } void setup::setup_QF_RDL(static_features & st) { if (!is_in_diff_logic(st)) throw default_exception("Benchmark is not in QF_RDL (real difference logic)."); if (st.m_has_int) throw default_exception("Benchmark has integer variables but it is marked as QF_RDL (real difference logic)."); TRACE("setup", tout << "setup_QF_RDL(st)\n";); check_no_uninterpreted_functions(st, "QF_RDL"); m_params.m_relevancy_lvl = 0; m_params.m_arith_expand_eqs = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_nnf_cnf = false; if (is_dense(st)) { m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_adaptive = false; m_params.m_phase_selection = PS_CACHING; } // The smi theories use fixed size integers instead of rationals. // They have support for epsilons for modeling strict inequalities, but they // cannot handle rational numbers. // It is only safe to use them when the input does not contain rational numbers. // Moreover, if model construction is enabled, then rational numbers may be needed // to compute the actual value of epsilon even if the input does not have rational numbers. // Example: (x < 1) and (x > 0) if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } else if (!m_params.m_arith_auto_config_simplex && is_dense(st)) { if (!st.m_has_rational && !m_params.m_model && st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_dense_smi, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params)); } else { if (m_params.m_arith_auto_config_simplex || st.m_num_uninterpreted_constants > 4 * st.m_num_bool_constants || st.m_num_ite_terms > 0 /* theory_rdl and theory_frdl do not support ite-terms */) { // if (!st.m_has_rational && !m_params.m_model && st.arith_k_sum_is_small()) { // TRACE("rdl_bug", tout << "using theory_smi_arith\n";); // m_context.register_plugin(alloc(smt::theory_smi_arith, m_manager, m_params)); // } // else { TRACE("rdl_bug", tout << "using theory_mi_arith\n";); m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); // } } else { m_params.m_arith_bound_prop = BP_NONE; m_params.m_arith_propagation_strategy = ARITH_PROP_AGILITY; m_params.m_arith_add_binary_bounds = true; if (!st.m_has_rational && !m_params.m_model && st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_frdl, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_rdl, m_manager, m_params)); } } } void setup::setup_QF_IDL() { TRACE("setup", tout << "setup_QF_IDL()\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_expand_eqs = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_arith_small_lemma_size = 30; m_params.m_nnf_cnf = false; setup_i_arith(); } void setup::setup_QF_IDL(static_features & st) { if (!is_in_diff_logic(st)) throw default_exception("Benchmark is not in QF_IDL (integer difference logic)."); if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as QF_IDL (integer difference logic)."); TRACE("setup", tout << "setup QF_IDL, m_arith_k_sum: " << st.m_arith_k_sum << " m_num_diff_terms: " << st.m_num_arith_terms << "\n"; st.display_primitive(tout);); TRACE("setup", tout << "setup_QF_IDL(st)\n";); check_no_uninterpreted_functions(st, "QF_IDL"); m_params.m_relevancy_lvl = 0; m_params.m_arith_expand_eqs = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_arith_small_lemma_size = 30; m_params.m_nnf_cnf = false; if (st.m_num_uninterpreted_constants > 5000) m_params.m_relevancy_lvl = 2; else if (st.m_cnf && !is_dense(st)) m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; else m_params.m_phase_selection = PS_CACHING; if (is_dense(st) && st.m_num_bin_clauses + st.m_num_units == st.m_num_clauses) { m_params.m_restart_adaptive = false; m_params.m_restart_strategy = RS_GEOMETRIC; } if (st.m_cnf && st.m_num_units == st.m_num_clauses) { // the problem is just a big conjunction... using randomization to deal with crafted benchmarks m_params.m_random_initial_activity = IA_RANDOM; } TRACE("setup", tout << "RELEVANCY: " << m_params.m_relevancy_lvl << "\n"; tout << "ARITH_EQ_BOUNDS: " << m_params.m_arith_eq_bounds << "\n";); if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } else if (!m_params.m_arith_auto_config_simplex && is_dense(st)) { TRACE("setup", tout << "using dense diff logic...\n";); m_params.m_phase_selection = PS_CACHING_CONSERVATIVE; if (st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params)); } else { // if (st.arith_k_sum_is_small()) { // TRACE("setup", tout << "using small integer simplex...\n";); // m_context.register_plugin(alloc(smt::theory_si_arith, m_manager, m_params)); // } // else { TRACE("setup", tout << "using big integer simplex...\n";); m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); // } } } void setup::setup_QF_UFIDL() { TRACE("setup", tout << "setup_QF_UFIDL()\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; m_params.m_arith_eq_bounds = true; m_params.m_arith_expand_eqs = true; m_params.m_phase_selection = PS_ALWAYS_FALSE; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_restart_adaptive = false; setup_i_arith(); } void setup::setup_QF_UFIDL(static_features & st) { TRACE("setup", tout << "setup_QF_UFIDL(st)\n";); if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as QF_UFIDL (uninterpreted functions and difference logic)."); m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; if (st.m_num_uninterpreted_functions == 0) { m_params.m_arith_expand_eqs = true; m_params.m_arith_propagate_eqs = false; if (is_dense(st)) { m_params.m_arith_small_lemma_size = 128; m_params.m_lemma_gc_half = true; m_params.m_restart_strategy = RS_GEOMETRIC; if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } else if (st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params)); return; } } m_params.m_arith_eq_bounds = true; m_params.m_phase_selection = PS_ALWAYS_FALSE; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_restart_adaptive = false; if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } // else if (st.arith_k_sum_is_small()) // m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); } void setup::setup_QF_LRA() { TRACE("setup", tout << "setup_QF_LRA(st)\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_expand_eqs = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_eliminate_term_ite = true; m_params.m_nnf_cnf = false; setup_mi_arith(); } void setup::setup_QF_LRA(static_features const & st) { check_no_uninterpreted_functions(st, "QF_LRA"); m_params.m_relevancy_lvl = 0; m_params.m_arith_expand_eqs = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_eliminate_term_ite = true; m_params.m_nnf_cnf = false; if (numerator(st.m_arith_k_sum) > rational(2000000) && denominator(st.m_arith_k_sum) > rational(500)) { m_params.m_relevancy_lvl = 2; m_params.m_relevancy_lemma = false; } if (st.m_cnf) { m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; } else { m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_arith_stronger_lemmas = false; m_params.m_phase_selection = PS_ALWAYS_FALSE; m_params.m_restart_adaptive = false; } m_params.m_arith_small_lemma_size = 32; setup_mi_arith(); } void setup::setup_QF_LIA() { TRACE("setup", tout << "setup_QF_LIA(st)\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_expand_eqs = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_nnf_cnf = false; setup_i_arith(); } void setup::setup_QF_LIA(static_features const & st) { check_no_uninterpreted_functions(st, "QF_LIA"); TRACE("setup", tout << "QF_LIA setup\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_expand_eqs = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_nnf_cnf = false; if (st.m_max_ite_tree_depth > 50) { m_params.m_arith_expand_eqs = false; m_params.m_pull_cheap_ite_trees = true; m_params.m_arith_propagate_eqs = true; m_params.m_relevancy_lvl = 2; m_params.m_relevancy_lemma = false; } else if (st.m_num_clauses == st.m_num_units) { m_params.m_arith_gcd_test = false; m_params.m_arith_branch_cut_ratio = 4; m_params.m_relevancy_lvl = 2; m_params.m_arith_expand_eqs = true; m_params.m_eliminate_term_ite = true; // if (st.m_num_exprs < 5000 && st.m_num_ite_terms < 50) { // safeguard to avoid high memory consumption // TODO: implement analsysis function to decide where lift ite is too expensive. // m_params.m_lift_ite = LI_FULL; // } } else { m_params.m_eliminate_term_ite = true; m_params.m_phase_selection = PS_CACHING; m_params.m_restart_adaptive = false; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; } if (st.m_num_bin_clauses + st.m_num_units == st.m_num_clauses && st.m_cnf && st.m_arith_k_sum > rational(100000)) { m_params.m_arith_bound_prop = BP_NONE; m_params.m_arith_stronger_lemmas = false; } setup_i_arith(); } void setup::setup_QF_UFLIA() { m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; m_params.m_arith_propagation_threshold = 1000; setup_i_arith(); } void setup::setup_QF_UFLIA(static_features & st) { if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as QF_UFLIA (uninterpreted functions and linear integer arithmetic)."); setup_QF_UFLIA(); } void setup::setup_QF_UFLRA() { m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; setup_mi_arith(); } void setup::setup_QF_BV() { m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_bv_cc = false; m_params.m_bb_ext_gates = true; m_params.m_nnf_cnf = false; m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params)); } void setup::setup_QF_AUFBV() { m_params.m_array_mode = AR_SIMPLE; m_params.m_relevancy_lvl = 0; m_params.m_bv_cc = false; m_params.m_bb_ext_gates = true; m_params.m_nnf_cnf = false; m_params.m_propagate_booleans = true; m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params)); m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); } void setup::setup_QF_AX() { m_params.m_array_mode = AR_SIMPLE; m_params.m_nnf_cnf = false; m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); } void setup::setup_QF_AX(static_features const & st) { m_params.m_array_mode = AR_SIMPLE; m_params.m_nnf_cnf = false; if (st.m_num_clauses == st.m_num_units) { m_params.m_relevancy_lvl = 0; m_params.m_phase_selection = PS_ALWAYS_FALSE; } else { m_params.m_relevancy_lvl = 2; } m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); } void setup::setup_QF_AUFLIA() { TRACE("QF_AUFLIA", tout << "no static features\n";); m_params.m_array_mode = AR_SIMPLE; m_params.m_nnf_cnf = false; m_params.m_relevancy_lvl = 2; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; setup_i_arith(); m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); } void setup::setup_QF_AUFLIA(static_features const & st) { m_params.m_array_mode = AR_SIMPLE; if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as QF_AUFLIA (arrays, uninterpreted functions and linear integer arithmetic)."); m_params.m_nnf_cnf = false; if (st.m_num_clauses == st.m_num_units) { TRACE("QF_AUFLIA", tout << "using relevancy: 0\n";); m_params.m_relevancy_lvl = 0; m_params.m_phase_selection = PS_ALWAYS_FALSE; } else { m_params.m_relevancy_lvl = 0; // it was 2, for some reason 2 doesn't work anymore TODO: investigate m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; m_params.m_random_initial_activity = IA_ZERO; } // if (st.m_num_arith_ineqs == st.m_num_diff_ineqs && st.m_num_arith_eqs == st.m_num_diff_eqs && st.arith_k_sum_is_small()) // m_context.register_plugin(new smt::theory_si_arith(m_manager, m_params)); // else setup_i_arith(); m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); } void setup::setup_AUFLIA(bool simple_array) { TRACE("setup", tout << "AUFLIA\n";); m_params.m_array_mode = simple_array ? AR_SIMPLE : AR_FULL; m_params.m_pi_use_database = true; m_params.m_phase_selection = PS_ALWAYS_FALSE; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_eliminate_bounds = true; m_params.m_qi_quick_checker = MC_UNSAT; m_params.m_propagate_booleans = true; m_params.m_qi_lazy_threshold = 20; // m_params.m_qi_max_eager_multipatterns = 10; /// <<< HACK m_params.m_mbqi = true; // enabling MBQI and MACRO_FINDER by default :-) // MACRO_FINDER is a horrible for AUFLIA and UFNIA benchmarks (boogie benchmarks in general) // It destroys the existing patterns. // m_params.m_macro_finder = true; // m_params.m_ng_lift_ite = LI_FULL; TRACE("setup", tout << "max_eager_multipatterns: " << m_params.m_qi_max_eager_multipatterns << "\n";); setup_i_arith(); setup_arrays(); } void setup::setup_AUFLIA(static_features const & st) { if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as AUFLIA (arrays, uninterpreted functions and linear integer arithmetic)."); m_params.m_qi_eager_threshold = st.m_num_quantifiers_with_patterns == 0 ? 5 : 7; setup_AUFLIA(); } void setup::setup_AUFLIRA(bool simple_array) { m_params.m_array_mode = simple_array ? AR_SIMPLE : AR_FULL; m_params.m_phase_selection = PS_ALWAYS_FALSE; m_params.m_eliminate_bounds = true; m_params.m_qi_quick_checker = MC_UNSAT; m_params.m_propagate_booleans = true; m_params.m_qi_eager_threshold = 5; // Added for MBQI release m_params.m_qi_lazy_threshold = 20; // m_params.m_macro_finder = true; m_params.m_ng_lift_ite = LI_FULL; m_params.m_pi_max_multi_patterns = 10; //<< it was used for SMT-COMP m_params.m_array_lazy_ieq = true; m_params.m_array_lazy_ieq_delay = 4; // m_params.m_mbqi = true; // enabling MBQI by default :-) // setup_mi_arith(); setup_arrays(); } void setup::setup_UFNIA() { setup_AUFLIA(); } void setup::setup_UFLRA() { setup_AUFLIRA(); } void setup::setup_AUFLIAp() { setup_AUFLIA(); } void setup::setup_AUFNIRA() { setup_AUFLIRA(); } void setup::setup_LRA() { m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_eliminate_term_ite = true; setup_mi_arith(); } void setup::setup_QF_FP() { setup_QF_BV(); m_context.register_plugin(alloc(smt::theory_fpa, m_manager)); } void setup::setup_QF_FPBV() { setup_QF_BV(); m_context.register_plugin(alloc(smt::theory_fpa, m_manager)); } bool is_arith(static_features const & st) { return st.m_num_arith_ineqs > 0 || st.m_num_arith_terms > 0 || st.m_num_arith_eqs > 0; } void setup::setup_i_arith() { m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); } void setup::setup_mi_arith() { if (m_params.m_arith_mode == AS_OPTINF) { m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); } else { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } } void setup::setup_arith() { static_features st(m_manager); IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";); st.collect(m_context.get_num_asserted_formulas(), m_context.get_asserted_formulas()); IF_VERBOSE(1000, st.display_primitive(verbose_stream());); m_params.m_arith_fixnum = st.arith_k_sum_is_small(); m_params.m_arith_int_only = !st.m_has_rational && !st.m_has_real; switch(m_params.m_arith_mode) { case AS_NO_ARITH: m_context.register_plugin(alloc(smt::theory_dummy, m_manager.mk_family_id("arith"), "no arithmetic")); break; case AS_DIFF_LOGIC: m_params.m_arith_expand_eqs = true; if (m_params.m_arith_fixnum) { if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_fidl, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_frdl, m_manager, m_params)); } else { if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_idl, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_rdl, m_manager, m_params)); } break; case AS_DENSE_DIFF_LOGIC: m_params.m_arith_expand_eqs = true; if (m_params.m_arith_fixnum) { if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_smi, m_manager, m_params)); } else { if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params)); } break; case AS_UTVPI: m_params.m_arith_expand_eqs = true; if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_iutvpi, m_manager)); else m_context.register_plugin(alloc(smt::theory_rutvpi, m_manager)); break; case AS_OPTINF: m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); break; default: if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); break; } } void setup::setup_bv() { switch(m_params.m_bv_mode) { case BS_NO_BV: m_context.register_plugin(alloc(smt::theory_dummy, m_manager.mk_family_id("bv"), "no bit-vector")); break; case BS_BLASTER: m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params)); break; } } void setup::setup_arrays() { switch(m_params.m_array_mode) { case AR_NO_ARRAY: m_context.register_plugin(alloc(smt::theory_dummy, m_manager.mk_family_id("array"), "no array")); break; case AR_SIMPLE: m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); break; case AR_MODEL_BASED: throw default_exception("The model-based array theory solver is deprecated"); break; case AR_FULL: m_context.register_plugin(alloc(smt::theory_array_full, m_manager, m_params)); break; } } void setup::setup_datatypes() { TRACE("datatype", tout << "registering theory datatype...\n";); m_context.register_plugin(alloc(theory_datatype, m_manager, m_params)); } void setup::setup_dl() { m_context.register_plugin(mk_theory_dl(m_manager)); } void setup::setup_seq() { m_context.register_plugin(alloc(theory_seq_empty, m_manager)); } void setup::setup_card() { m_context.register_plugin(alloc(theory_pb, m_manager, m_params)); } void setup::setup_fpa() { setup_bv(); m_context.register_plugin(alloc(theory_fpa, m_manager)); } void setup::setup_unknown() { setup_arith(); setup_arrays(); setup_bv(); setup_datatypes(); setup_dl(); setup_seq(); setup_card(); setup_fpa(); } void setup::setup_unknown(static_features & st) { TRACE("setup", tout << "setup_unknown\n";); if (st.m_num_quantifiers > 0) { if (st.m_has_real) setup_AUFLIRA(false); else setup_AUFLIA(false); setup_datatypes(); setup_bv(); setup_fpa(); return; } TRACE("setup", tout << "num non UF theories: " << st.num_non_uf_theories() << "\n"; tout << "num theories: " << st.num_theories() << "\n"; tout << "is_diff_logic: " << is_diff_logic(st) << "\n"; tout << "is_arith: " << is_arith(st) << "\n"; tout << "has UF: " << st.has_uf() << "\n"; tout << "has real: " << st.m_has_real << "\n"; tout << "has int: " << st.m_has_int << "\n"; tout << "has bv: " << st.m_has_bv << "\n"; tout << "has fpa: " << st.m_has_fpa << "\n"; tout << "has arrays: " << st.m_has_arrays << "\n";); if (st.num_non_uf_theories() == 0) { setup_QF_UF(st); return; } if (st.num_theories() == 1 && is_diff_logic(st)) { if (st.m_has_real && !st.m_has_int) setup_QF_RDL(st); else if (!st.m_has_real && st.m_has_int) setup_QF_IDL(st); else setup_unknown(); return; } if (st.num_theories() == 2 && st.has_uf() && is_diff_logic(st)) { if (!st.m_has_real && st.m_has_int) setup_QF_UFIDL(st); else setup_unknown(); return; } if (st.num_theories() == 1 && is_arith(st)) { if (st.m_has_real) setup_QF_LRA(st); else setup_QF_LIA(st); return; } if (st.num_theories() == 2 && st.has_uf() && is_arith(st)) { if (!st.m_has_real) setup_QF_UFLIA(st); else if (!st.m_has_int) setup_QF_UFLRA(); else setup_unknown(); return; } if (st.num_theories() == 1 && st.m_has_bv) { setup_QF_BV(); return; } if (st.num_theories() == 1 && st.m_has_fpa) { setup_QF_FP(); return; } if (st.num_theories() == 2 && st.m_has_fpa && st.m_has_bv) { setup_QF_FPBV(); return; } if (st.num_theories() == 1 && st.m_has_arrays) { setup_QF_AX(); return; } if (st.num_theories() == 2 && st.has_uf() && st.m_has_arrays && st.m_has_bv) { setup_QF_AUFBV(); return; } if (st.num_theories() == 2 && st.has_uf() && st.m_has_arrays && st.m_has_int) { setup_QF_AUFLIA(); return; } setup_unknown(); } }; z3-z3-4.4.1/src/smt/smt_setup.h000066400000000000000000000070231260446376700162150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_setup.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-24. Revision History: --*/ #ifndef SMT_SETUP_H_ #define SMT_SETUP_H_ #include"ast.h" #include"smt_params.h" struct static_features; namespace smt { enum config_mode { CFG_BASIC, // install theories based on user options CFG_LOGIC, // install theories and configure Z3 based on the value of the parameter set-logic. CFG_AUTO, // install theories based on static features of the input formula }; class context; /** \brief Object used to setup a logical context. \warning In the current version, we can only setup a logical context at scope level 0, and before internalizing any formula. Auxiliary temporary contexts are used to avoid this limitation. */ class setup { context & m_context; ast_manager & m_manager; smt_params & m_params; symbol m_logic; bool m_already_configured; void setup_auto_config(); void setup_default(); // // setup_() methods do not depend on static features of the formula. So, they are safe to use // even in an incremental setting. // // setup_(static_features & st) can only be used if the logical context will perform a single // check. // void setup_QF_UF(); void setup_QF_UF(static_features const & st); void setup_QF_RDL(); void setup_QF_RDL(static_features & st); void setup_QF_IDL(); void setup_QF_IDL(static_features & st); void setup_QF_UFIDL(); void setup_QF_UFIDL(static_features & st); void setup_QF_LRA(); void setup_QF_LRA(static_features const & st); void setup_QF_LIA(); void setup_QF_LIA(static_features const & st); void setup_QF_UFLIA(); void setup_QF_UFLIA(static_features & st); void setup_QF_UFLRA(); void setup_QF_BV(); void setup_QF_AUFBV(); void setup_QF_AX(); void setup_QF_AX(static_features const & st); void setup_QF_AUFLIA(); void setup_QF_AUFLIA(static_features const & st); void setup_QF_FP(); void setup_QF_FPBV(); void setup_LRA(); void setup_AUFLIA(bool simple_array = true); void setup_AUFLIA(static_features const & st); void setup_AUFLIRA(bool simple_array = true); void setup_UFNIA(); void setup_UFLRA(); void setup_AUFLIAp(); void setup_AUFNIRA(); void setup_QF_BVRE(); void setup_unknown(); void setup_unknown(static_features & st); void setup_arrays(); void setup_datatypes(); void setup_bv(); void setup_arith(); void setup_dl(); void setup_seq(); void setup_card(); void setup_i_arith(); void setup_mi_arith(); void setup_fpa(); public: setup(context & c, smt_params & params); void mark_already_configured() { m_already_configured = true; } bool already_configured() const { return m_already_configured; } bool set_logic(symbol logic) { if (already_configured()) return false; m_logic = logic; return true; } symbol const & get_logic() const { return m_logic; } void operator()(config_mode cm); }; }; #endif /* SMT_SETUP_H_ */ z3-z3-4.4.1/src/smt/smt_solver.cpp000066400000000000000000000071461260446376700167300ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_solver.cpp Abstract: Wraps smt::kernel as a solver for the external API and cmd_context. Author: Leonardo (leonardo) 2012-10-21 Notes: --*/ #include"solver_na2as.h" #include"smt_kernel.h" #include"reg_decl_plugins.h" #include"smt_params.h" namespace smt { class solver : public solver_na2as { smt_params m_params; smt::kernel m_context; progress_callback * m_callback; symbol m_logic; public: solver(ast_manager & m, params_ref const & p, symbol const & l): solver_na2as(m), m_params(p), m_context(m, m_params) { m_logic = l; if (m_logic != symbol::null) m_context.set_logic(m_logic); } virtual ~solver() { } virtual void updt_params(params_ref const & p) { m_params.updt_params(p); m_context.updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { m_context.collect_param_descrs(r); } virtual void collect_statistics(statistics & st) const { m_context.collect_statistics(st); } virtual void assert_expr(expr * t) { m_context.assert_expr(t); } virtual void push_core() { m_context.push(); } virtual void pop_core(unsigned n) { m_context.pop(n); } virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { TRACE("solver_na2as", tout << "smt_solver::check_sat_core: " << num_assumptions << "\n";); return m_context.check(num_assumptions, assumptions); } virtual void 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)); } virtual void get_model(model_ref & m) { m_context.get_model(m); } virtual proof * get_proof() { return m_context.get_proof(); } virtual std::string reason_unknown() const { return m_context.last_failure_as_string(); } virtual void get_labels(svector & r) { buffer tmp; m_context.get_relevant_labels(0, tmp); r.append(tmp.size(), tmp.c_ptr()); } virtual void set_cancel(bool f) { m_context.set_cancel(f); } virtual void set_progress_callback(progress_callback * callback) { m_callback = callback; m_context.set_progress_callback(callback); } virtual unsigned get_num_assertions() const { return m_context.size(); } virtual expr * get_assertion(unsigned idx) const { SASSERT(idx < get_num_assertions()); return m_context.get_formulas()[idx]; } virtual void display(std::ostream & out) const { m_context.display(out); } }; }; solver * mk_smt_solver(ast_manager & m, params_ref const & p, symbol const & logic) { return alloc(smt::solver, m, p, logic); } class smt_solver_factory : public solver_factory { public: 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_smt_solver(m, p, logic); } }; solver_factory * mk_smt_solver_factory() { return alloc(smt_solver_factory); } z3-z3-4.4.1/src/smt/smt_solver.h000066400000000000000000000010341260446376700163630ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_solver.h Abstract: Wraps smt::kernel as a solver for the external API and cmd_context. Author: Leonardo (leonardo) 2012-10-21 Notes: This file was called default_solver.h. It was a bad name. --*/ #ifndef SMT_SOLVER_H_ #define SMT_SOLVER_H_ #include"ast.h" #include"params.h" class solver; class solver_factory; solver * mk_smt_solver(ast_manager & m, params_ref const & p, symbol const & logic); solver_factory * mk_smt_solver_factory(); #endif z3-z3-4.4.1/src/smt/smt_statistics.cpp000066400000000000000000000005261260446376700176030ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_statistics.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-21. Revision History: --*/ #include #include"smt_statistics.h" namespace smt { void statistics::reset() { memset(this, 0, sizeof(statistics)); } }; z3-z3-4.4.1/src/smt/smt_statistics.h000066400000000000000000000022701260446376700172460ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_statistics.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-21. Revision History: --*/ #ifndef SMT_STATISTICS_H_ #define SMT_STATISTICS_H_ #include namespace smt { struct statistics { unsigned m_num_propagations; unsigned m_num_bin_propagations; unsigned m_num_conflicts; unsigned m_num_sat_conflicts; unsigned m_num_decisions; unsigned m_num_add_eq; unsigned m_num_restarts; unsigned m_num_final_checks; unsigned m_num_mk_bool_var; unsigned m_num_del_bool_var; unsigned m_num_mk_enode; unsigned m_num_del_enode; unsigned m_num_mk_clause; unsigned m_num_del_clause; unsigned m_num_mk_bin_clause; unsigned m_num_mk_lits; unsigned m_num_dyn_ack; unsigned m_num_del_dyn_ack; unsigned m_num_interface_eqs; unsigned m_max_generation; unsigned m_num_minimized_lits; unsigned m_num_checks; statistics() { reset(); } void reset(); }; }; #endif /* SMT_STATISTICS_H_ */ z3-z3-4.4.1/src/smt/smt_theory.cpp000066400000000000000000000076571260446376700167370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_theory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #include"smt_context.h" #include"buffer.h" #include"ast_ll_pp.h" namespace smt { void theory::init(context * ctx) { SASSERT(m_context == 0); m_context = ctx; m_manager = &(ctx->get_manager()); } void theory::reset_eh() { m_var2enode.reset(); } void theory::push_scope_eh() { SASSERT(m_context); m_var2enode_lim.push_back(m_var2enode.size()); } void theory::pop_scope_eh(unsigned num_scopes) { SASSERT(m_context); unsigned scope_lvl = m_var2enode_lim.size(); SASSERT(num_scopes <= scope_lvl); unsigned new_lvl = scope_lvl - num_scopes; unsigned old_sz = m_var2enode_lim[new_lvl]; m_var2enode.shrink(old_sz); m_var2enode_lim.shrink(new_lvl); } void theory::display_var2enode(std::ostream & out) const { unsigned sz = m_var2enode.size(); for (unsigned v = 0; v < sz; v++) { out << "v" << v << " -> #" << m_var2enode[v]->get_owner_id() << "\n"; } } void theory::display_app(std::ostream & out, app * n) const { func_decl * d = n->get_decl(); if (n->get_num_args() == 0) { out << d->get_name(); display_parameters(out, d->get_num_parameters(), d->get_parameters()); } else if (n->get_family_id() == get_family_id()) { out << "(" << d->get_name(); display_parameters(out, d->get_num_parameters(), d->get_parameters()); unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { out << " "; display_app(out, to_app(n->get_arg(i))); } out << ")"; } else { out << "#" << n->get_id(); } } void theory::display_flat_app(std::ostream & out, app * n) const { func_decl * d = n->get_decl(); if (n->get_num_args() == 0) { out << d->get_name(); display_parameters(out, d->get_num_parameters(), d->get_parameters()); } else if (n->get_family_id() == get_family_id()) { out << "(" << d->get_name(); display_parameters(out, d->get_num_parameters(), d->get_parameters()); ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { n = todo.back(); todo.pop_back(); unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { app * arg = to_app(n->get_arg(i)); if (d->is_associative() && arg->get_decl() == d) { todo.push_back(arg); } else { out << " "; display_app(out, arg); } } } out << ")"; } else { out << "#" << n->get_id(); } } bool theory::is_relevant_and_shared(enode * n) const { context & ctx = get_context(); return ctx.is_relevant(n) && ctx.is_shared(n); } bool theory::assume_eq(enode * n1, enode * n2) { return get_context().assume_eq(n1, n2); } literal theory::mk_eq(expr * a, expr * b, bool gate_ctx) { context & ctx = get_context(); app * eq = ctx.mk_eq_atom(a, b); TRACE("mk_var_bug", tout << "mk_eq: " << eq->get_id() << " " << a->get_id() << " " << b->get_id() << "\n"; tout << mk_ll_pp(a, get_manager()) << "\n" << mk_ll_pp(b, get_manager());); ctx.internalize(eq, gate_ctx); return ctx.get_literal(eq); } theory::theory(family_id fid): m_id(fid), m_context(0), m_manager(0) { } theory::~theory() { } }; z3-z3-4.4.1/src/smt/smt_theory.h000066400000000000000000000342371260446376700163760ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_theory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #ifndef SMT_THEORY_H_ #define SMT_THEORY_H_ #include"smt_enode.h" #include"obj_hashtable.h" #include"statistics.h" namespace smt { class model_generator; class model_value_proc; class theory { theory_id m_id; context * m_context; ast_manager * m_manager; enode_vector m_var2enode; unsigned_vector m_var2enode_lim; friend class context; protected: virtual void init(context * ctx); /* --------------------------------------------------- In the logical context, expressions are 'internalized'. That is, the logical context creates auxiliary data-structures (e.g., enodes) and attach them to the expressions. The logical context does not know the internals of each theory. So, during the internalization process, it notifies the theory (plugin) whenever it finds an application with a theory function symbol. A theory variable created at scope level n must be deleted when scope level n is backtracked. The logical context uses the method is_attached_to_var to decide whether an enode is already associated with a theory variable or not. ------------------------------------------------------ */ virtual theory_var mk_var(enode * n) { SASSERT(!is_attached_to_var(n)); theory_var v = m_var2enode.size(); m_var2enode.push_back(n); return v; } public: /** \brief Return ture if the given enode is attached to a variable of the theory. \remark The result is not equivalent to n->get_th_var(get_id()) != null_theory_var A theory variable v may be in the list of variables of n, but it may be inherited from another enode n' during an equivalence class merge. That is, get_enode(v) != n. */ bool is_attached_to_var(enode const * n) const { theory_var v = n->get_th_var(get_id()); return v != null_theory_var && get_enode(v) == n; } protected: /** \brief Return true if the theory uses default internalization: "the internalization of an application internalizes all arguments". Theories like arithmetic do not use default internalization. For example, in the application (+ a (+ b c)), no enode is created for (+ b c). */ virtual bool default_internalizer() const { return true; } /** \brief This method is invoked by the logical context when atom is being internalized. The theory may return false if it does not want to implement the given predicate symbol. After the execution of this method the given atom must be associated with a new boolean variable. */ virtual bool internalize_atom(app * atom, bool gate_ctx) = 0; /** \brief This method is invoked by the logical context after the given equality atom is internalized. */ virtual void internalize_eq_eh(app * atom, bool_var v) { } /** \brief This method is invoked by the logical context when the term is being internalized. The theory may return false if it does not want to implement the given function symbol. After the execution of this method the given term must be associated with a new enode. */ virtual bool internalize_term(app * term) = 0; /** \brief Apply (interpreted) sort constraints on the given enode. */ virtual void apply_sort_cnstr(enode * n, sort * s) { } /** \brief This method is invoked when a truth value is assigned to the given boolean variable. */ virtual void assign_eh(bool_var v, bool is_true) { } /** \brief Equality propagation (v1 = v2): Core -> Theory */ virtual void new_eq_eh(theory_var v1, theory_var v2) = 0; /** \brief Return true if the theory does something with the disequalities implied by the core. */ virtual bool use_diseqs() const { return true; } /** \brief Disequality propagation (v1 /= v2): Core -> Theory */ virtual void new_diseq_eh(theory_var v1, theory_var v2) = 0; /** \brief This method is invoked when the theory application n is marked as relevant. */ virtual void relevant_eh(app * n) { } /** \brief This method is invoked when a new backtracking point is created. */ virtual void push_scope_eh(); /** \brief This method is invoked during backtracking. */ virtual void pop_scope_eh(unsigned num_scopes); /** \brief This method is invoked when the logical context is being restarted. */ virtual void restart_eh() { } /** \brief This method is invoked before the search starts. */ virtual void init_search_eh() { } /** \brief This method is invoked when the logical context assigned a truth value to all boolean variables and no inconsistency was detected. */ virtual final_check_status final_check_eh() { return FC_DONE; } /** \brief Parametric theories (e.g. Arrays) should implement this method. See example in context::is_shared */ virtual bool is_shared(theory_var v) const { return false; } /** \brief Return true if the theory has something to propagate */ virtual bool can_propagate() { return false; } /** \brief This method is invoked to give a theory a chance to perform theory propagation. */ virtual void propagate() { } /** \brief This method allows a theory to contribute to disequality propagation. */ virtual justification * why_is_diseq(theory_var v1, theory_var v2) { return 0; } /** \brief Just releases memory. */ virtual void flush_eh() { } /** \brief This method is invoked when the logical context is being reset. */ virtual void reset_eh(); // ---------------------------------------------------- // // Model validation (-vldt flag) // // ---------------------------------------------------- virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { return true; } // ---------------------------------------------------- // // Conflict resolution event handler // // ---------------------------------------------------- public: /** \brief This method is invoked when a theory atom is used during conflict resolution. This allows the theory to bump the activity of the enodes contained in the given atom. */ virtual void conflict_resolution_eh(app * atom, bool_var v) { } public: theory(family_id fid); virtual ~theory(); virtual void setup() { } theory_id get_id() const { return m_id; } family_id get_family_id() const { return m_id; } context & get_context() const { SASSERT(m_context); return *m_context; } ast_manager & get_manager() const { SASSERT(m_manager); return *m_manager; } enode * get_enode(theory_var v) const { SASSERT(v < static_cast(m_var2enode.size())); return m_var2enode[v]; } /** \brief Return the equivalence class representative of the given theory variable. */ theory_var get_representative(theory_var v) const { SASSERT(v != null_theory_var); theory_var r = get_enode(v)->get_root()->get_th_var(get_id()); SASSERT(r != null_theory_var); return r; } /** \brief Return true if the theory variable is the representative of its equivalence class. */ bool is_representative(theory_var v) const { return get_representative(v) == v; } unsigned get_num_vars() const { return m_var2enode.size(); } unsigned get_old_num_vars(unsigned num_scopes) const { return m_var2enode_lim[m_var2enode_lim.size() - num_scopes]; } virtual void display(std::ostream & out) const { out << "Theory " << static_cast(get_id()) << " does not have a display method\n"; display_var2enode(out); } virtual void display_var2enode(std::ostream & out) const; virtual void collect_statistics(::statistics & st) const { } void display_app(std::ostream & out, app * n) const; void display_flat_app(std::ostream & out, app * n) const; void display_var_def(std::ostream & out, theory_var v) const { return display_app(out, get_enode(v)->get_owner()); } void display_var_flat_def(std::ostream & out, theory_var v) const { return display_flat_app(out, get_enode(v)->get_owner()); } /** \brief Assume eqs between variable that are equal with respect to the given table. Table is a hashtable indexed by the variable value. table.contains(v) should be true if there is v' in table such that assignment of v is equal to v'. This method assumes that class VarValueTable contains the methods: - void reset() - theory_var insert_if_not_there(theory_var v) */ template bool assume_eqs(VarValueTable & table) { TRACE("assume_eqs", tout << "starting...\n";); table.reset(); bool result = false; int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { enode * n = get_enode(v); theory_var other = null_theory_var; TRACE("assume_eqs", tout << "#" << n->get_owner_id() << " is_relevant_and_shared: " << is_relevant_and_shared(n) << "\n";); if (n != 0 && is_relevant_and_shared(n)) { other = table.insert_if_not_there(v); if (other != v) { enode * n2 = get_enode(other); TRACE("assume_eqs", tout << "value(#" << n->get_owner_id() << ") = value(#" << n2->get_owner_id() << ")\n";); if (assume_eq(n, n2)) { TRACE("assume_eqs", tout << "new assumed eq\n";); result = true; } } } } return result; } /** \brief When an eq atom n is created during the search, the default behavior is to make sure that the n->get_arg(0)->get_id() < n->get_arg(1)->get_id(). This may create some redundant atoms, since some theories/families use different convetions in their simplifiers. For example, arithmetic always force a numeral to be in the right hand side. So, this method should be redefined if the default behavior conflicts with a convention used by the theory/family. */ virtual app * mk_eq_atom(expr * lhs, expr * rhs) { if (lhs->get_id() > rhs->get_id()) std::swap(lhs, rhs); return get_manager().mk_eq(lhs, rhs); } literal mk_eq(expr * a, expr * b, bool gate_ctx); // ----------------------------------- // // Model generation // // ----------------------------------- /** \brief Return true if theory support model construction */ virtual bool build_models() const { return true; } virtual void init_model(model_generator & m) { } virtual void finalize_model(model_generator & m) { } /** \brief Return a functor that can build the value (interpretation) for n. */ virtual model_value_proc * mk_value(enode * n, model_generator & mg) { return 0; } // ----------------------------------- // // Model checker // // ----------------------------------- virtual bool get_value(enode * n, expr_ref & r) { return false; } virtual char const * get_name() const { return "unknown"; } // ----------------------------------- // // Return a fresh new instance of the given theory. // This function is used to create a fresh context (new_ctx) containing the same theories of the context that owns this theory. // // We need the parameter new_ctx because of user_smt_theory :-( // // ----------------------------------- virtual theory * mk_fresh(context * new_ctx) = 0; protected: // ---------------------------------------------------- // // Auxiliary methods for assume_eqs // // smt::context is not defined at this point. // // ---------------------------------------------------- bool is_relevant_and_shared(enode * n) const; bool assume_eq(enode * n1, enode * n2); }; }; #endif /* SMT_THEORY_H_ */ z3-z3-4.4.1/src/smt/smt_theory_var_list.h000066400000000000000000000031771260446376700203000ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_theory_var_list.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef SMT_THEORY_VAR_LIST_H_ #define SMT_THEORY_VAR_LIST_H_ #include"smt_types.h" namespace smt { class theory_var_list { int m_th_id:8; int m_th_var:24; theory_var_list * m_next; public: theory_var_list(): m_th_id(null_theory_id), m_th_var(null_theory_var), m_next(0) { } theory_var_list(theory_id t, theory_var v, theory_var_list * n = 0): m_th_id(t), m_th_var(v), m_next(n) { } theory_id get_th_id() const { return m_th_id; } theory_var get_th_var() const { return m_th_var; } theory_var_list * get_next() const { return m_next; } void set_th_id(theory_id id) { m_th_id = id; } void set_th_var(theory_var v) { m_th_var = v; } void set_next(theory_var_list * next) { m_next = next; } }; // 32 bit machine COMPILE_TIME_ASSERT(sizeof(expr*) != 4 || sizeof(theory_var_list) == sizeof(theory_var_list *) + sizeof(int)); // 64 bit machine COMPILE_TIME_ASSERT(sizeof(expr*) != 8 || sizeof(theory_var_list) == sizeof(theory_var_list *) + sizeof(int) + /* a structure must be aligned */ sizeof(int)); }; #endif /* SMT_THEORY_VAR_LIST_H_ */ z3-z3-4.4.1/src/smt/smt_types.h000066400000000000000000000030101260446376700162110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_types.h Abstract: Basic types for the SMT engine Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef SMT_TYPES_H_ #define SMT_TYPES_H_ #include"list.h" #include"vector.h" #include"lbool.h" class model; namespace smt { /** \brief A boolean variable is just an integer. */ typedef int bool_var; const bool_var null_bool_var = -1; const bool_var true_bool_var = 0; const bool_var first_bool_var = 1; typedef svector bool_var_vector; typedef family_id theory_id; const theory_id null_theory_id = null_family_id; typedef int theory_var; const theory_var null_theory_var = -1; class enode; typedef ptr_vector enode_vector; typedef std::pair enode_pair; class context; class theory; class justification; class model_generator; enum final_check_status { FC_DONE, FC_CONTINUE, FC_GIVEUP }; inline std::ostream & operator<<(std::ostream & out, final_check_status st) { switch (st) { case FC_DONE: out << "done"; break; case FC_CONTINUE: out << "continue"; break; case FC_GIVEUP: out << "giveup"; break; } return out; } // if defined, then clauses have an extra mask field used to optimize backward subsumption, and backward/forward subsumption resolution. #define APPROX_LIT_SET }; #endif /* SMT_TYPES_H_ */ z3-z3-4.4.1/src/smt/smt_value_sort.cpp000066400000000000000000000032221260446376700175700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_value_sort.cpp Abstract: Determine if elements of a given sort can be values. Author: Nikolaj Bjorner (nbjorner) 2012-11-25 Revision History: --*/ #include "smt_value_sort.h" #include "bv_decl_plugin.h" #include "arith_decl_plugin.h" #include "datatype_decl_plugin.h" namespace smt { bool is_value_sort(ast_manager& m, sort* s) { arith_util arith(m); datatype_util data(m); bv_util bv(m); ptr_vector sorts; ast_mark mark; sorts.push_back(s); while (!sorts.empty()) { s = sorts.back(); sorts.pop_back(); if (mark.is_marked(s)) { continue; } mark.mark(s, true); if (arith.is_int_real(s)) { // simple } else if (m.is_bool(s)) { // simple } else if (bv.is_bv_sort(s)) { // simple } else if (data.is_datatype(s)) { ptr_vector const& cs = *data.get_datatype_constructors(s); for (unsigned i = 0; i < cs.size(); ++i) { func_decl* f = cs[i]; for (unsigned j = 0; j < f->get_arity(); ++j) { sorts.push_back(f->get_domain(j)); } } } else { return false; } } return true; } bool is_value_sort(ast_manager& m, expr* e) { return is_value_sort(m, m.get_sort(e)); } } z3-z3-4.4.1/src/smt/smt_value_sort.h000066400000000000000000000006741260446376700172450ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_value_sort.h Abstract: Determine if elements of a given sort can be values. Author: Nikolaj Bjorner (nbjorner) 2012-11-25 Revision History: --*/ #ifndef SMT_VALUE_SORT_H_ #define SMT_VALUE_SORT_H_ #include "ast.h" namespace smt { bool is_value_sort(ast_manager& m, sort* s); bool is_value_sort(ast_manager& m, expr* e); }; #endif z3-z3-4.4.1/src/smt/spanning_tree.h000066400000000000000000000045021260446376700170250ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: spanning_tree.h Abstract: Represent spanning trees with needed operations for Network Simplex Author: Anh-Dung Phan (t-anphan) 2013-11-06 Notes: --*/ #ifndef SPANNING_TREE_H_ #define SPANNING_TREE_H_ #include "diff_logic.h" #include "spanning_tree_base.h" namespace smt { template class thread_spanning_tree : public spanning_tree_base, protected Ext { protected: typedef dl_edge edge; typedef dl_graph graph; typedef typename Ext::numeral numeral; typedef typename Ext::fin_numeral fin_numeral; // Store the parent of a node i in the spanning tree svector m_pred; // Store the number of edge on the path from node i to the root svector m_depth; svector m_thread; // Store the pointer from node i to the next node in depth-first search order svector m_tree; // i |-> edge between (i, m_pred[i]) node_id m_root_t2; graph & m_graph; void swap_order(node_id q, node_id v); node_id find_rev_thread(node_id n) const; void fix_depth(node_id start, node_id after_end); node_id get_final(int start); bool is_preorder_traversal(node_id start, node_id end); node_id get_common_ancestor(node_id u, node_id v); bool is_forward_edge(edge_id e_id) const; bool is_ancestor_of(node_id ancestor, node_id child); public: thread_spanning_tree(graph & g); virtual void initialize(svector const & tree); void get_descendants(node_id start, svector & descendants); virtual void update(edge_id enter_id, edge_id leave_id); void get_path(node_id start, node_id end, svector & path, svector & against); bool in_subtree_t2(node_id child); bool check_well_formed(); }; template class basic_spanning_tree : public thread_spanning_tree { private: graph * m_tree_graph; public: basic_spanning_tree(graph & g); void initialize(svector const & tree); void update(edge_id enter_id, edge_id leave_id); }; } #endif z3-z3-4.4.1/src/smt/spanning_tree_base.h000066400000000000000000000023151260446376700200170ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: spanning_tree_base.h Abstract: Represent spanning trees with needed operations for Network Simplex Author: Anh-Dung Phan (t-anphan) 2013-11-06 Notes: --*/ #ifndef SPANNING_TREE_BASE_H_ #define SPANNING_TREE_BASE_H_ #include "util.h" #include "vector.h" namespace smt { template inline std::string pp_vector(std::string const & label, TV v) { std::ostringstream oss; oss << label << " "; for (unsigned i = 0; i < v.size(); ++i) { oss << v[i] << " "; } oss << std::endl; return oss.str(); } class spanning_tree_base { public: typedef int node_id; typedef int edge_id; virtual void initialize(svector const & tree) = 0; virtual void get_descendants(node_id start, svector & descendants) = 0; virtual void update(edge_id enter_id, edge_id leave_id) = 0; virtual void get_path(node_id start, node_id end, svector & path, svector & against) = 0; virtual bool in_subtree_t2(node_id child) = 0; virtual bool check_well_formed() = 0; }; } #endif z3-z3-4.4.1/src/smt/spanning_tree_def.h000066400000000000000000000372511260446376700176520ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: spanning_tree_def.h Abstract: Author: Anh-Dung Phan (t-anphan) 2013-11-06 Notes: --*/ #ifndef SPANNING_TREE_DEF_H_ #define SPANNING_TREE_DEF_H_ #include "spanning_tree.h" namespace smt { template thread_spanning_tree::thread_spanning_tree(graph & g) : m_graph(g) { } template void thread_spanning_tree::initialize(svector const & tree) { m_tree = tree; unsigned num_nodes = m_graph.get_num_nodes(); m_pred.resize(num_nodes); m_depth.resize(num_nodes); m_thread.resize(num_nodes); node_id root = num_nodes - 1; m_pred[root] = -1; m_depth[root] = 0; m_thread[root] = 0; // Create artificial edges from/to root node to/from other nodes and initialize the spanning tree for (int i = 0; i < root; ++i) { m_pred[i] = root; m_depth[i] = 1; m_thread[i] = i + 1; } TRACE("network_flow", { tout << pp_vector("Predecessors", m_pred) << pp_vector("Threads", m_thread); tout << pp_vector("Depths", m_depth) << pp_vector("Tree", m_tree); }); } template typename thread_spanning_tree::node_id thread_spanning_tree::get_common_ancestor(node_id u, node_id v) { while (u != v) { if (m_depth[u] > m_depth[v]) u = m_pred[u]; else v = m_pred[v]; } return u; } template void thread_spanning_tree::get_path(node_id start, node_id end, svector & path, svector & against) { node_id join = get_common_ancestor(start, end); path.reset(); while (start != join) { edge_id e_id = m_tree[start]; path.push_back(e_id); against.push_back(is_forward_edge(e_id)); start = m_pred[start]; } while (end != join) { edge_id e_id = m_tree[end]; path.push_back(e_id); against.push_back(!is_forward_edge(e_id)); end = m_pred[end]; } } template bool thread_spanning_tree::is_forward_edge(edge_id e_id) const { node_id start = m_graph.get_source(e_id); node_id end = m_graph.get_target(e_id); SASSERT(m_pred[start] == end || m_pred[end] == start); return m_pred[start] == end; } template void thread_spanning_tree::get_descendants(node_id start, svector & descendants) { descendants.reset(); descendants.push_back(start); node_id u = m_thread[start]; while (m_depth[u] > m_depth[start]) { descendants.push_back(u); u = m_thread[u]; } } template bool thread_spanning_tree::in_subtree_t2(node_id child) { if (m_depth[child] < m_depth[m_root_t2]) { return false; } return is_ancestor_of(m_root_t2, child); } template bool thread_spanning_tree::is_ancestor_of(node_id ancestor, node_id child) { for (node_id n = child; n != -1; n = m_pred[n]) { if (n == ancestor) { return true; } } return false; } /** \brief add entering_edge, remove leaving_edge from spanning tree. Old tree: New tree: root root / \ / \ x y x y / \ / \ / \ / \ u s u s | / / v w v w / \ \ / \ \ z p z p \ \ / q q */ template void thread_spanning_tree::update(edge_id enter_id, edge_id leave_id) { node_id p = m_graph.get_source(enter_id); node_id q = m_graph.get_target(enter_id); node_id u = m_graph.get_source(leave_id); node_id v = m_graph.get_target(leave_id); if (m_pred[u] == v) { std::swap(u, v); } SASSERT(m_pred[v] == u); if (is_ancestor_of(v, p)) { std::swap(p, q); } SASSERT(is_ancestor_of(v, q)); TRACE("network_flow", { tout << "update_spanning_tree: (" << p << ", " << q << ") enters, ("; tout << u << ", " << v << ") leaves\n"; }); // Old threads: alpha -> v -*-> f(v) -> beta | p -*-> f(p) -> gamma // New threads: alpha -> beta | p -*-> f(p) -> v -*-> f(v) -> gamma node_id f_p = get_final(p); node_id f_v = get_final(v); node_id alpha = find_rev_thread(v); node_id beta = m_thread[f_v]; node_id gamma = m_thread[f_p]; if (v != gamma) { m_thread[alpha] = beta; m_thread[f_p] = v; m_thread[f_v] = gamma; } node_id old_pred = m_pred[q]; // Update stem nodes from q to v if (q != v) { for (node_id n = q; n != v; ) { SASSERT(old_pred != u); // the last processed node_id is v SASSERT(-1 != m_pred[old_pred]); int next_old_pred = m_pred[old_pred]; swap_order(n, old_pred); m_tree[old_pred] = m_tree[n]; n = old_pred; old_pred = next_old_pred; } } m_pred[q] = p; m_tree[q] = enter_id; m_root_t2 = q; node_id after_final_q = (v == gamma) ? beta : gamma; fix_depth(q, after_final_q); SASSERT(!in_subtree_t2(p)); SASSERT(in_subtree_t2(q)); SASSERT(!in_subtree_t2(u)); SASSERT(in_subtree_t2(v)); TRACE("network_flow", { tout << pp_vector("Predecessors", m_pred) << pp_vector("Threads", m_thread); tout << pp_vector("Depths", m_depth) << pp_vector("Tree", m_tree); }); } /** swap v and q in tree. - fixup m_thread - fixup m_pred Case 1: final(q) == final(v) ------- Old thread: prev -> v -*-> alpha -> q -*-> final(q) -> next New thread: prev -> q -*-> final(q) -> v -*-> alpha -> next Case 2: final(q) != final(v) ------- Old thread: prev -> v -*-> alpha -> q -*-> final(q) -> beta -*-> final(v) -> next New thread: prev -> q -*-> final(q) -> v -*-> alpha -> beta -*-> final(v) -> next */ template void thread_spanning_tree::swap_order(node_id q, node_id v) { SASSERT(q != v); SASSERT(m_pred[q] == v); SASSERT(is_preorder_traversal(v, get_final(v))); node_id prev = find_rev_thread(v); node_id f_q = get_final(q); node_id f_v = get_final(v); node_id next = m_thread[f_v]; node_id alpha = find_rev_thread(q); if (f_q == f_v) { SASSERT(f_q != v && alpha != next); m_thread[f_q] = v; m_thread[alpha] = next; f_q = alpha; } else { node_id beta = m_thread[f_q]; SASSERT(f_q != v && alpha != beta); m_thread[f_q] = v; m_thread[alpha] = beta; f_q = f_v; } SASSERT(prev != q); m_thread[prev] = q; m_pred[v] = q; // Notes: f_q has to be used since m_depth hasn't been updated yet. SASSERT(is_preorder_traversal(q, f_q)); } /** \brief Check invariants of main data-structures. Spanning tree of m_graph + root is represented using: svector m_states; edge_id |-> edge_state svector m_pred; node_id |-> node svector m_depth; node_id |-> int svector m_thread; node_id |-> node Tree is determined by m_pred: - m_pred[root] == -1 - m_pred[n] = m != n for each node_id n, acyclic until reaching root. - m_depth[m_pred[n]] + 1 == m_depth[n] for each n != root m_thread is a linked list traversing all nodes. Furthermore, the nodes linked in m_thread follows a depth-first traversal order. */ template bool thread_spanning_tree::check_well_formed() { node_id root = m_pred.size()-1; // Check that m_thread traverses each node. // This gets checked using union-find as well. svector found(m_thread.size(), false); found[root] = true; for (node_id x = m_thread[root]; x != root; x = m_thread[x]) { SASSERT(x != m_thread[x]); found[x] = true; } for (unsigned i = 0; i < found.size(); ++i) { SASSERT(found[i]); } // m_pred is acyclic, and points to root. SASSERT(m_pred[root] == -1); SASSERT(m_depth[root] == 0); for (node_id i = 0; i < root; ++i) { SASSERT(m_depth[m_pred[i]] < m_depth[i]); } // m_depth[x] denotes distance from x to the root node for (node_id x = m_thread[root]; x != root; x = m_thread[x]) { SASSERT(m_depth[x] > 0); SASSERT(m_depth[x] == m_depth[m_pred[x]] + 1); } // m_thread forms a spanning tree over [0..root] // Union-find structure svector roots(m_pred.size(), -1); for (node_id x = m_thread[root]; x != root; x = m_thread[x]) { node_id y = m_pred[x]; // We are now going to check the edge between x and y SASSERT(find(roots, x) != find(roots, y)); merge(roots, x, y); } // All nodes belong to the same spanning tree for (unsigned i = 0; i < roots.size(); ++i) { SASSERT(roots[i] + roots.size() == 0 || roots[i] >= 0); } for (unsigned i = 0; i < m_tree.size(); ++i) { node_id src = m_graph.get_source(m_tree[i]); node_id tgt = m_graph.get_target(m_tree[i]); SASSERT(m_pred[src] == tgt || m_pred[tgt] == src); } return true; } static unsigned find(svector& roots, unsigned x) { unsigned old_x = x; while (roots[x] >= 0) { x = roots[x]; } SASSERT(roots[x] < 0); if (old_x != x) { roots[old_x] = x; } return x; } static void merge(svector& roots, unsigned x, unsigned y) { x = find(roots, x); y = find(roots, y); SASSERT(roots[x] < 0 && roots[y] < 0); if (x == y) { return; } if (roots[x] > roots[y]) { std::swap(x, y); } SASSERT(roots[x] <= roots[y]); roots[x] += roots[y]; roots[y] = x; } /** \brief find node_id that points to 'n' in m_thread */ template typename thread_spanning_tree::node_id thread_spanning_tree::find_rev_thread(node_id n) const { node_id ancestor = m_pred[n]; SASSERT(ancestor != -1); while (m_thread[ancestor] != n) { ancestor = m_thread[ancestor]; } return ancestor; } template void thread_spanning_tree::fix_depth(node_id start, node_id after_end) { while (start != after_end) { SASSERT(m_pred[start] != -1); m_depth[start] = m_depth[m_pred[start]]+1; start = m_thread[start]; } } template typename thread_spanning_tree::node_id thread_spanning_tree::get_final(int start) { int n = start; while (m_depth[m_thread[n]] > m_depth[start]) { n = m_thread[n]; } return n; } template bool thread_spanning_tree::is_preorder_traversal(node_id start, node_id end) { // get children of start uint_set children; children.insert(start); node_id root = m_pred.size()-1; for (int i = 0; i < root; ++i) { for (int j = 0; j < root; ++j) { if (children.contains(m_pred[j])) { children.insert(j); } } } // visit children using m_thread children.remove(start); do { start = m_thread[start]; SASSERT(children.contains(start)); children.remove(start); } while (start != end); SASSERT(children.empty()); return true; } // Basic spanning tree template basic_spanning_tree::basic_spanning_tree(graph & g) : thread_spanning_tree(g) { } template void basic_spanning_tree::initialize(svector const & tree) { m_tree_graph = alloc(graph); m_tree = tree; unsigned num_nodes = m_graph.get_num_nodes(); for (unsigned i = 0; i < num_nodes; ++i) { m_tree_graph->init_var(i); } vector const & es = m_graph.get_all_edges(); svector::const_iterator it = m_tree.begin(), end = m_tree.end(); for(; it != end; ++it) { edge const & e = es[*it]; m_tree_graph->add_edge(e.get_source(), e.get_target(), e.get_weight(), explanation()); } node_id root = num_nodes - 1; m_tree_graph->bfs_undirected(root, m_pred, m_depth); m_tree_graph->dfs_undirected(root, m_thread); } template void basic_spanning_tree::update(edge_id enter_id, edge_id leave_id) { if (m_tree_graph) dealloc(m_tree_graph); m_tree_graph = alloc(graph); unsigned num_nodes = m_graph.get_num_nodes(); for (unsigned i = 0; i < num_nodes; ++i) { m_tree_graph->init_var(i); } vector const & es = m_graph.get_all_edges(); svector::const_iterator it = m_tree.begin(), end = m_tree.end(); for(; it != end; ++it) { edge const & e = es[*it]; if (leave_id != *it) { m_tree_graph->add_edge(e.get_source(), e.get_target(), e.get_weight(), explanation()); } } edge const & e = es[enter_id]; m_tree_graph->add_edge(e.get_source(), e.get_target(), e.get_weight(), explanation()); node_id root = num_nodes - 1; m_tree_graph->bfs_undirected(root, m_pred, m_depth); m_tree_graph->dfs_undirected(root, m_thread); vector const & tree_edges = m_tree_graph->get_all_edges(); for (unsigned i = 0; i < tree_edges.size(); ++i) { edge const & e = tree_edges[i]; dl_var src = e.get_source(); dl_var tgt = e.get_target(); edge_id id; VERIFY(m_graph.get_edge_id(src, tgt, id)); SASSERT(tgt == m_pred[src] || src == m_pred[tgt]); if (tgt == m_pred[src]) { m_tree[src] = id; } else { m_tree[tgt] = id; } } node_id p = m_graph.get_source(enter_id); node_id q = m_graph.get_target(enter_id); m_root_t2 = p == m_pred[q] ? q : p; } } #endif z3-z3-4.4.1/src/smt/tactic/000077500000000000000000000000001260446376700152665ustar00rootroot00000000000000z3-z3-4.4.1/src/smt/tactic/ctx_solver_simplify_tactic.cpp000066400000000000000000000216531260446376700234340ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ctx_solver_simplify_tactic.cpp Abstract: Context simplifier for propagating solver assignments. Author: Nikolaj (nbjorner) 2012-3-6 Notes: --*/ #include"ctx_solver_simplify_tactic.h" #include"arith_decl_plugin.h" #include"smt_params.h" #include"smt_kernel.h" #include"ast_pp.h" #include"mk_simplified_app.h" #include"ast_util.h" class ctx_solver_simplify_tactic : public tactic { ast_manager& m; params_ref m_params; smt_params m_front_p; smt::kernel m_solver; arith_util m_arith; mk_simplified_app m_mk_app; func_decl_ref m_fn; obj_map m_fns; unsigned m_num_steps; volatile bool m_cancel; public: ctx_solver_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()): m(m), m_params(p), m_solver(m, m_front_p), m_arith(m), m_mk_app(m), m_fn(m), m_num_steps(0), m_cancel(false) { sort* i_sort = m_arith.mk_int(); m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } virtual tactic * translate(ast_manager & m) { return alloc(ctx_solver_simplify_tactic, m, m_params); } virtual ~ctx_solver_simplify_tactic() { obj_map::iterator it = m_fns.begin(), end = m_fns.end(); for (; it != end; ++it) { m.dec_ref(it->m_value); } m_fns.reset(); } virtual void updt_params(params_ref const & p) { m_solver.updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { m_solver.collect_param_descrs(r); } virtual void collect_statistics(statistics & st) const { st.update("solver-simplify-steps", m_num_steps); } virtual void reset_statistics() { m_num_steps = 0; } 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; reduce(*(in.get())); in->inc_depth(); result.push_back(in.get()); } virtual void cleanup() { reset_statistics(); m_solver.reset(); m_cancel = false; } protected: virtual void set_cancel(bool f) { m_solver.set_cancel(f); m_cancel = false; } void reduce(goal& g) { SASSERT(g.is_well_sorted()); expr_ref fml(m); tactic_report report("ctx-solver-simplify", g); if (g.inconsistent()) return; ptr_vector fmls; g.get_formulas(fmls); fml = mk_and(m, fmls.size(), fmls.c_ptr()); m_solver.push(); reduce(fml); m_solver.pop(1); SASSERT(m_solver.get_scope_level() == 0); TRACE("ctx_solver_simplify_tactic", for (unsigned i = 0; i < fmls.size(); ++i) { tout << mk_pp(fmls[i], m) << "\n"; } tout << "=>\n"; tout << mk_pp(fml, m) << "\n";); DEBUG_CODE( { m_solver.push(); expr_ref fml1(m); fml1 = mk_and(m, fmls.size(), fmls.c_ptr()); fml1 = m.mk_iff(fml, fml1); fml1 = m.mk_not(fml1); m_solver.assert_expr(fml1); lbool is_sat = m_solver.check(); TRACE("ctx_solver_simplify_tactic", tout << "is non-equivalence sat?: " << is_sat << "\n";); if (is_sat == l_true) { TRACE("ctx_solver_simplify_tactic", tout << "result is not equivalent to input\n"; tout << mk_pp(fml1, m) << "\n";); UNREACHABLE(); } m_solver.pop(1); }); g.reset(); g.assert_expr(fml, 0, 0); IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(ctx-solver-simplify :num-steps " << m_num_steps << ")\n";); SASSERT(g.is_well_sorted()); } struct expr_pos { unsigned m_parent; unsigned m_self; unsigned m_idx; expr* m_expr; expr_pos(unsigned p, unsigned s, unsigned i, expr* e): m_parent(p), m_self(s), m_idx(i), m_expr(e) {} expr_pos(): m_parent(0), m_self(0), m_idx(0), m_expr(0) {} }; void reduce(expr_ref& result){ SASSERT(m.is_bool(result)); ptr_vector names; svector todo; expr_ref_vector fresh_vars(m), trail(m); expr_ref res(m), tmp(m); obj_map cache; unsigned id = 1, child_id = 0; expr_ref n2(m), fml(m); unsigned parent_pos = 0, self_pos = 0, self_idx = 0; app * a; unsigned sz; expr_pos path_r; expr_ref_vector args(m); expr_ref n = mk_fresh(id, m.mk_bool_sort()); trail.push_back(n); fml = result.get(); tmp = m.mk_not(m.mk_iff(fml, n)); m_solver.assert_expr(tmp); todo.push_back(expr_pos(0,0,0,fml)); names.push_back(n); m_solver.push(); while (!todo.empty() && !m_cancel) { expr_ref res(m); args.reset(); expr* e = todo.back().m_expr; self_pos = todo.back().m_self; parent_pos = todo.back().m_parent; self_idx = todo.back().m_idx; n = names.back(); if (cache.contains(e)) { goto done; } if (m.is_bool(e) && simplify_bool(n, res)) { TRACE("ctx_solver_simplify_tactic", tout << "simplified: " << mk_pp(e, m) << " |-> " << mk_pp(res, m) << "\n";); goto done; } if (!is_app(e)) { res = e; goto done; } a = to_app(e); sz = a->get_num_args(); n2 = 0; for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); if (cache.find(arg, path_r)) { // // This is a single traversal version of the context // simplifier. It simplifies only the first occurrence of // a sub-term with respect to the context. // if (path_r.m_parent == self_pos && path_r.m_idx == i) { args.push_back(path_r.m_expr); } else { args.push_back(arg); } } else if (!n2) { n2 = mk_fresh(id, m.get_sort(arg)); trail.push_back(n2); todo.push_back(expr_pos(self_pos, child_id++, i, arg)); names.push_back(n2); args.push_back(n2); } else { args.push_back(arg); } } m_mk_app(a->get_decl(), args.size(), args.c_ptr(), res); trail.push_back(res); // child needs to be visited. if (n2) { m_solver.push(); tmp = m.mk_eq(res, n); m_solver.assert_expr(tmp); continue; } done: if (res) { cache.insert(e, expr_pos(parent_pos, self_pos, self_idx, res)); } todo.pop_back(); names.pop_back(); m_solver.pop(1); } if (!m_cancel) { VERIFY(cache.find(fml, path_r)); result = path_r.m_expr; } } bool simplify_bool(expr* n, expr_ref& res) { expr_ref tmp(m); m_solver.push(); m_solver.assert_expr(n); lbool is_sat = m_solver.check(); m_solver.pop(1); if (is_sat == l_false) { res = m.mk_true(); return true; } m_solver.push(); tmp = m.mk_not(n); m_solver.assert_expr(tmp); is_sat = m_solver.check(); m_solver.pop(1); if (is_sat == l_false) { res = m.mk_false(); return true; } return false; } expr_ref mk_fresh(unsigned& id, sort* s) { func_decl* fn; if (m.is_bool(s)) { fn = m_fn; } else if (!m_fns.find(s, fn)) { fn = m.mk_func_decl(symbol(0xbeef101 + id), m_arith.mk_int(), s); m.inc_ref(fn); m_fns.insert(s, fn); } return expr_ref(m.mk_app(fn, m_arith.mk_numeral(rational(id++), true)), m); } }; tactic * mk_ctx_solver_simplify_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(ctx_solver_simplify_tactic, m, p)); } z3-z3-4.4.1/src/smt/tactic/ctx_solver_simplify_tactic.h000066400000000000000000000010561260446376700230740ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ctx_solver_simplify_tactic.h Abstract: Context simplifier for propagating solver assignments. Author: Nikolaj (nbjorner) 2012-3-6 Notes: --*/ #ifndef CTX_SOLVER_SIMPLIFY_TACTIC_H_ #define CTX_SOLVER_SIMPLIFY_TACTIC_H_ #include"tactical.h" tactic * mk_ctx_solver_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("ctx-solver-simplify", "apply solver-based contextual simplification rules.", "mk_ctx_solver_simplify_tactic(m, p)") */ #endif z3-z3-4.4.1/src/smt/tactic/smt_tactic.cpp000066400000000000000000000276411260446376700201360ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt_tactic.h Abstract: smt::context as a tactic. Author: Leonardo (leonardo) 2011-10-18 Notes: --*/ #include"tactic.h" #include"tactical.h" #include"smt_kernel.h" #include"smt_params.h" #include"smt_params_helper.hpp" #include"rewriter_types.h" #include"filter_model_converter.h" #include"ast_util.h" typedef obj_map expr2expr_map; void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, expr2expr_map& bool2dep, ref& fmc) { expr2expr_map dep2bool; ptr_vector deps; ast_manager& m = g->m(); expr_ref_vector clause(m); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * f = g->form(i); expr_dependency * d = g->dep(i); if (d == 0 || !g->unsat_core_enabled()) { clauses.push_back(f); } else { // create clause (not d1 \/ ... \/ not dn \/ f) when the d's are the assumptions/dependencies of f. clause.reset(); clause.push_back(f); deps.reset(); m.linearize(d, deps); SASSERT(!deps.empty()); // d != 0, then deps must not be empty ptr_vector::iterator it = deps.begin(); ptr_vector::iterator end = deps.end(); for (; it != end; ++it) { expr * d = *it; if (is_uninterp_const(d) && m.is_bool(d)) { // no need to create a fresh boolean variable for d if (!bool2dep.contains(d)) { assumptions.push_back(d); bool2dep.insert(d, d); } clause.push_back(m.mk_not(d)); } else { // must normalize assumption expr * b = 0; if (!dep2bool.find(d, b)) { b = m.mk_fresh_const(0, m.mk_bool_sort()); dep2bool.insert(d, b); bool2dep.insert(b, d); assumptions.push_back(b); if (!fmc) { fmc = alloc(filter_model_converter, m); } fmc->insert(to_app(b)->get_decl()); } clause.push_back(m.mk_not(b)); } } SASSERT(clause.size() > 1); expr_ref cls(m); cls = mk_or(m, clause.size(), clause.c_ptr()); clauses.push_back(cls); } } } class smt_tactic : public tactic { smt_params m_params; params_ref m_params_ref; statistics m_stats; std::string m_failure; smt::kernel * m_ctx; symbol m_logic; progress_callback * m_callback; bool m_candidate_models; bool m_fail_if_inconclusive; public: smt_tactic(params_ref const & p): m_params_ref(p), m_ctx(0), m_callback(0) { updt_params_core(p); TRACE("smt_tactic", tout << this << "\np: " << p << "\n";); } virtual tactic * translate(ast_manager & m) { return alloc(smt_tactic, m_params_ref); } virtual ~smt_tactic() { SASSERT(m_ctx == 0); } smt_params & fparams() { return m_params; } void updt_params_core(params_ref const & p) { m_candidate_models = p.get_bool("candidate_models", false); m_fail_if_inconclusive = p.get_bool("fail_if_inconclusive", true); } virtual void updt_params(params_ref const & p) { TRACE("smt_tactic", tout << this << "\nupdt_params: " << p << "\n";); updt_params_core(p); fparams().updt_params(p); SASSERT(p.get_bool("auto_config", fparams().m_auto_config) == fparams().m_auto_config); } virtual void collect_param_descrs(param_descrs & r) { r.insert("candidate_models", CPK_BOOL, "(default: false) create candidate models even when quantifier or theory reasoning is incomplete."); r.insert("fail_if_inconclusive", CPK_BOOL, "(default: true) fail if found unsat (sat) for under (over) approximated goal."); smt_params_helper::collect_param_descrs(r); } virtual void set_cancel(bool f) { if (m_ctx) m_ctx->set_cancel(f); } virtual void collect_statistics(statistics & st) const { if (m_ctx) m_ctx->collect_statistics(st); // ctx is still running... else st.copy(m_stats); } virtual void cleanup() { } virtual void reset_statistics() { m_stats.reset(); } virtual void set_logic(symbol const & l) { m_logic = l; } virtual void set_progress_callback(progress_callback * callback) { m_callback = callback; } struct scoped_init_ctx { smt_tactic & m_owner; smt_params m_params; // smt-setup overwrites parameters depending on the current assertions. scoped_init_ctx(smt_tactic & o, ast_manager & m):m_owner(o) { m_params = o.fparams(); smt::kernel * new_ctx = alloc(smt::kernel, m, m_params); TRACE("smt_tactic", tout << "logic: " << o.m_logic << "\n";); new_ctx->set_logic(o.m_logic); if (o.m_callback) { new_ctx->set_progress_callback(o.m_callback); } #pragma omp critical (as_st_solver) { o.m_ctx = new_ctx; } } ~scoped_init_ctx() { smt::kernel * d = m_owner.m_ctx; #pragma omp critical (as_st_cancel) { m_owner.m_ctx = 0; } if (d) 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) { try { SASSERT(in->is_well_sorted()); ast_manager & m = in->m(); TRACE("smt_tactic", tout << this << "\nAUTO_CONFIG: " << fparams().m_auto_config << " HIDIV0: " << fparams().m_hi_div0 << " " << " PREPROCESS: " << fparams().m_preprocess << "\n"; tout << "RELEVANCY: " << fparams().m_relevancy_lvl << "\n"; tout << "fail-if-inconclusive: " << m_fail_if_inconclusive << "\n"; tout << "params_ref: " << m_params_ref << "\n"; tout << "nnf: " << fparams().m_nnf_cnf << "\n";); TRACE("smt_tactic_detail", in->display(tout);); TRACE("smt_tactic_memory", tout << "wasted_size: " << m.get_allocator().get_wasted_size() << "\n";); scoped_init_ctx init(*this, m); SASSERT(m_ctx != 0); expr_ref_vector clauses(m); expr2expr_map bool2dep; ptr_vector assumptions; ref fmc; if (in->unsat_core_enabled()) { extract_clauses_and_dependencies(in, clauses, assumptions, bool2dep, fmc); if (in->proofs_enabled() && !assumptions.empty()) throw tactic_exception("smt tactic does not support simultaneous generation of proofs and unsat cores"); for (unsigned i = 0; i < clauses.size(); ++i) { m_ctx->assert_expr(clauses[i].get()); } } else if (in->proofs_enabled()) { unsigned sz = in->size(); for (unsigned i = 0; i < sz; i++) { m_ctx->assert_expr(in->form(i), in->pr(i)); } } else { unsigned sz = in->size(); for (unsigned i = 0; i < sz; i++) { m_ctx->assert_expr(in->form(i)); } } if (m_ctx->canceled()) { throw tactic_exception("smt_tactic canceled"); } lbool r; if (assumptions.empty()) r = m_ctx->setup_and_check(); else r = m_ctx->check(assumptions.size(), assumptions.c_ptr()); m_ctx->collect_statistics(m_stats); switch (r) { case l_true: { if (m_fail_if_inconclusive && !in->sat_preserved()) throw tactic_exception("over-approximated goal found to be sat"); // the empty assertion set is trivially satifiable. in->reset(); result.push_back(in.get()); // store the model in a no-op model converter, and filter fresh Booleans if (in->models_enabled()) { model_ref md; m_ctx->get_model(md); mc = model2model_converter(md.get()); mc = concat(fmc.get(), mc.get()); } pc = 0; core = 0; return; } case l_false: { if (m_fail_if_inconclusive && !in->unsat_preserved()) { TRACE("smt_tactic", tout << "failed to show to be unsat...\n";); throw tactic_exception("under-approximated goal found to be unsat"); } // formula is unsat, reset the goal, and store false there. in->reset(); proof * pr = 0; expr_dependency * lcore = 0; if (in->proofs_enabled()) pr = m_ctx->get_proof(); if (in->unsat_core_enabled()) { unsigned sz = m_ctx->get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) { expr * b = m_ctx->get_unsat_core_expr(i); SASSERT(is_uninterp_const(b) && m.is_bool(b)); expr * d = bool2dep.find(b); lcore = m.mk_join(lcore, m.mk_leaf(d)); } } in->assert_expr(m.mk_false(), pr, lcore); result.push_back(in.get()); mc = 0; pc = 0; core = 0; return; } case l_undef: if (m_fail_if_inconclusive) throw tactic_exception("smt tactic failed to show goal to be sat/unsat"); result.push_back(in.get()); if (m_candidate_models) { switch (m_ctx->last_failure()) { case smt::NUM_CONFLICTS: case smt::THEORY: case smt::QUANTIFIERS: if (in->models_enabled()) { model_ref md; m_ctx->get_model(md); mc = model2model_converter(md.get()); } pc = 0; core = 0; return; default: break; } } m_failure = m_ctx->last_failure_as_string(); throw tactic_exception(m_failure.c_str()); } } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } }; tactic * mk_smt_tactic(params_ref const & p) { return alloc(smt_tactic, p); } tactic * mk_smt_tactic_using(bool auto_config, params_ref const & _p) { params_ref p = _p; p.set_bool("auto_config", auto_config); tactic * r = mk_smt_tactic(p); TRACE("smt_tactic", tout << "auto_config: " << auto_config << "\nr: " << r << "\np: " << p << "\n";); return using_params(r, p); } z3-z3-4.4.1/src/smt/tactic/smt_tactic.h000066400000000000000000000015371260446376700175770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt_tactic.h Abstract: smt::context as a tactic. Author: Leonardo (leonardo) 2011-10-18 Notes: --*/ #ifndef SMT_TACTIC_H_ #define SMT_TACTIC_H_ #include"params.h" #include"ast.h" #include"obj_hashtable.h" #include"goal.h" class tactic; class filter_model_converter; tactic * mk_smt_tactic(params_ref const & p = params_ref()); // syntax sugar for using_params(mk_smt_tactic(), p) where p = (:auto_config, auto_config) tactic * mk_smt_tactic_using(bool auto_config = true, params_ref const & p = params_ref()); void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, obj_map& bool2dep, ref& fmc); /* ADD_TACTIC("smt", "apply a SAT based SMT solver.", "mk_smt_tactic(p)") */ #endif z3-z3-4.4.1/src/smt/tactic/unit_subsumption_tactic.cpp000066400000000000000000000077451260446376700227650ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: unit_subsumption_tactic.cpp Abstract: Simplify goal using subsumption based on unit propagation. Author: Nikolaj Bjorner (nbjorner) 2012-9-6 --*/ #include "unit_subsumption_tactic.h" #include "smt_context.h" struct unit_subsumption_tactic : public tactic { ast_manager& m; params_ref m_params; smt_params m_fparams; volatile bool m_cancel; smt::context m_context; expr_ref_vector m_clauses; unsigned m_clause_count; bit_vector m_is_deleted; unsigned_vector m_deleted; unit_subsumption_tactic( ast_manager& m, params_ref const& p): m(m), m_params(p), m_cancel(false), m_context(m, m_fparams, p), m_clauses(m) { } void set_cancel(bool f) { m_cancel = f; m_context.set_cancel_flag(f); } virtual void cleanup() { set_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) { reduce_core(in, result); } virtual void updt_params(params_ref const& p) { m_params = p; // m_context.updt_params(p); does not exist. } virtual tactic* translate(ast_manager& m) { return alloc(unit_subsumption_tactic, m, m_params); } void checkpoint() { if (m_cancel) { throw tactic_exception(TACTIC_CANCELED_MSG); } } void reduce_core(goal_ref const& g, goal_ref_buffer& result) { init(g); m_context.push(); assert_clauses(g); m_context.push(); // internalize assertions. prune_clauses(); goal_ref r(g); insert_result(r); r->elim_true(); result.push_back(r.get()); m_context.pop(2); TRACE("unit_subsumption_tactic", g->display(tout); r->display(tout);); } void assert_clauses(goal_ref const& g) { for (unsigned i = 0; i < g->size(); ++i) { m_context.assert_expr(m.mk_iff(new_clause(), g->form(i))); } } void prune_clauses() { for (unsigned i = 0; i < m_clause_count; ++i) { prune_clause(i); } } void prune_clause(unsigned i) { m_context.push(); for (unsigned j = 0; j < m_clause_count; ++j) { if (i == j) { m_context.assert_expr(m.mk_not(m_clauses[j].get())); } else if (!m_is_deleted.get(j)) { m_context.assert_expr(m_clauses[j].get()); } } m_context.push(); // force propagation bool is_unsat = m_context.inconsistent(); m_context.pop(2); if (is_unsat) { TRACE("unit_subsumption_tactic", tout << "Removing clause " << i << "\n";); m_is_deleted.set(i, true); m_deleted.push_back(i); } } void insert_result(goal_ref& result) { for (unsigned i = 0; i < m_deleted.size(); ++i) { result->update(m_deleted[i], m.mk_true()); // TBD proof? } } void init(goal_ref const& g) { m_clause_count = 0; m_is_deleted.reset(); m_is_deleted.resize(g->size()); m_deleted.reset(); } expr* new_bool(unsigned& count, expr_ref_vector& v, char const* name) { SASSERT(count <= v.size()); if (count == v.size()) { v.push_back(m.mk_fresh_const(name, m.mk_bool_sort())); } return v[count++].get(); } expr* new_clause() { return new_bool(m_clause_count, m_clauses, "#clause"); } }; tactic * mk_unit_subsumption_tactic(ast_manager & m, params_ref const & p) { return alloc(unit_subsumption_tactic, m, p); } z3-z3-4.4.1/src/smt/tactic/unit_subsumption_tactic.h000066400000000000000000000014101260446376700224110ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: unit_subsumption_tactic.h Abstract: Simplify goal using subsumption based on unit propagation. Author: Nikolaj Bjorner (nbjorner) 2012-9-6 Notes: Background: PDR generates several clauses that subsume each-other. Simplify a goal assuming it is a conjunction of clauses. Subsumed clauses are simplified by using unit-propagation It uses the smt_context for the solver. --*/ #ifndef UNIT_SUBSUMPTION_TACTIC_H_ #define UNIT_SUBSUMPTION_TACTIC_H_ #include "tactic.h" tactic * mk_unit_subsumption_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("unit-subsume-simplify", "unit subsumption simplification.", "mk_unit_subsumption_tactic(m, p)") */ #endif z3-z3-4.4.1/src/smt/theory_arith.cpp000066400000000000000000000007161260446376700172300ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-04-22. Revision History: --*/ #include"theory_arith_def.h" namespace smt { template class theory_arith; template class theory_arith; // template class theory_arith; // template class theory_arith; template class smt::theory_arith; }; z3-z3-4.4.1/src/smt/theory_arith.h000066400000000000000000001546741260446376700167120ustar00rootroot00000000000000 /*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith.h Abstract: Author: Leonardo de Moura (leonardo) 2008-04-21. Revision History: --*/ #ifndef THEORY_ARITH_H_ #define THEORY_ARITH_H_ #include"smt_theory.h" #include"map.h" #include"heap.h" #include"nat_set.h" #include"inf_rational.h" #include"s_integer.h" #include"inf_s_integer.h" #include"arith_decl_plugin.h" #include"theory_arith_params.h" #include"arith_eq_adapter.h" #include"numeral_factory.h" #include"obj_pair_hashtable.h" #include"old_interval.h" #include"grobner.h" #include"arith_simplifier_plugin.h" #include"arith_eq_solver.h" #include"theory_opt.h" #include"uint_set.h" namespace smt { struct theory_arith_stats { unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests; unsigned m_assert_lower, m_assert_upper, m_assert_diseq, m_core2th_eqs, m_core2th_diseqs; unsigned m_th2core_eqs, m_th2core_diseqs, m_bound_props, m_offset_eqs, m_fixed_eqs, m_offline_eqs; unsigned m_max_min; unsigned m_gb_simplify, m_gb_superpose, m_gb_compute_basis, m_gb_num_processed; unsigned m_nl_branching, m_nl_linear, m_nl_bounds, m_nl_cross_nested; void reset() { memset(this, 0, sizeof(theory_arith_stats)); } theory_arith_stats() { reset(); } }; /** - There are 3 kinds of variables in the tableau: base, quasi-base, and non-base - Each base var and quasi-base var v owns a row R(v). - If v is a base var, then R(v) contains v and other non-base variables. - If v is a quasi-base var, then R(v) contains v and other base and non-base variables. - Each quasi-base var occurs only once in the tableau (i.e., it occurs in R(v)). - A quasi-base var does not have upper&lower bounds and distinct set. - A quasi-base var v can be transformed into a base var by eliminating the base vars v' in R(v). This can be accomplished by adding -c * R(v') where c is the coefficient of v' in R(v). - A column is used to store the occurrences of a non-base var v' in rows R(v), where v is a base variable. - An implied bound stores the linear equation that implied it. */ template class theory_arith : public theory, public theory_opt, private Ext { public: typedef typename Ext::numeral numeral; typedef typename Ext::inf_numeral inf_numeral; typedef vector numeral_vector; typedef map, default_eq > rational2var; static const int dead_row_id = -1; protected: bool proofs_enabled() const { return get_manager().proofs_enabled(); } bool coeffs_enabled() const { return proofs_enabled() || m_bound_watch != null_bool_var; } struct linear_monomial { numeral m_coeff; theory_var m_var; linear_monomial():m_var(null_theory_var) {} linear_monomial(numeral const & c, theory_var v):m_coeff(c), m_var(v) {} }; /** \brief A row_entry is: m_var*m_coeff m_col_idx points to the place in the column where the variable occurs. */ struct row_entry { numeral m_coeff; theory_var m_var; union { int m_col_idx; int m_next_free_row_entry_idx; }; row_entry():m_var(0), m_col_idx(0) {} row_entry(numeral const & c, theory_var v): m_coeff(c), m_var(v), m_col_idx(0) {} bool is_dead() const { return m_var == null_theory_var; } }; /** \brief A column entry points to the row and the row_entry within the row that has a non-zero coefficient on the variable associated with the column entry. */ struct col_entry { int m_row_id; union { int m_row_idx; int m_next_free_row_entry_idx; }; col_entry(int r, int i): m_row_id(r), m_row_idx(i) {} col_entry(): m_row_id(0), m_row_idx(0) {} bool is_dead() const { return m_row_id == dead_row_id; } }; struct column; /** \brief A row contains a base variable and set of row_entries. The base variable must occur in the set of row_entries with coefficient 1. */ struct row { vector m_entries; unsigned m_size; // the real size, m_entries contains dead row_entries. int m_base_var; int m_first_free_idx; // first available position. row(); unsigned size() const { return m_size; } unsigned num_entries() const { return m_entries.size(); } void reset(); row_entry & operator[](unsigned idx) { return m_entries[idx]; } row_entry const & operator[](unsigned idx) const { return m_entries[idx]; } typename vector::iterator begin_entries() { return m_entries.begin(); } const typename vector::const_iterator begin_entries() const { return m_entries.begin(); } typename vector::iterator end_entries() { return m_entries.end(); } const typename vector::const_iterator end_entries() const { return m_entries.end(); } row_entry & add_row_entry(int & pos_idx); void del_row_entry(unsigned idx); void compress(vector & cols); void compress_if_needed(vector & cols); void save_var_pos(svector & result_map) const; void reset_var_pos(svector & result_map) const; theory_var get_base_var() const { return m_base_var; } #ifdef Z3DEBUG bool is_coeff_of(theory_var v, numeral const & expected) const; #endif void display(std::ostream & out) const; numeral get_denominators_lcm() const; int get_idx_of(theory_var v) const; }; /** \brief A column stores in which rows a variable occurs. The column may have free/dead entries. The field m_first_free_idx is a reference to the first free/dead entry. */ struct column { svector m_entries; unsigned m_size; int m_first_free_idx; column():m_size(0), m_first_free_idx(-1) {} unsigned size() const { return m_size; } unsigned num_entries() const { return m_entries.size(); } void reset(); void compress(vector & rows); void compress_if_needed(vector & rows); void compress_singleton(vector & rows, unsigned singleton_pos); col_entry const * get_first_col_entry() const; col_entry & operator[](unsigned idx) { return m_entries[idx]; } col_entry const & operator[](unsigned idx) const { return m_entries[idx]; } typename svector::iterator begin_entries() { return m_entries.begin(); } const typename svector::const_iterator begin_entries() const { return m_entries.begin(); } typename svector::iterator end_entries() { return m_entries.end(); } const typename svector::const_iterator end_entries() const { return m_entries.end(); } col_entry & add_col_entry(int & pos_idx); void del_col_entry(unsigned idx); }; enum bound_kind { B_LOWER, B_UPPER }; friend std::ostream & operator<<(std::ostream & out, bound_kind k) { switch (k) { case B_LOWER: out << ">="; break; case B_UPPER: out << "<="; break; } return out; } typedef svector eq_vector; // keep track of coefficients used for bounds for proof generation. class antecedents { literal_vector m_lits; eq_vector m_eqs; vector m_lit_coeffs; vector m_eq_coeffs; vector m_params; bool m_init; bool empty() const { return m_eq_coeffs.empty() && m_lit_coeffs.empty(); } void init(); public: antecedents(): m_init(false) {} void reset(); literal_vector& lits() { return m_lits; } eq_vector& eqs() { return m_eqs; } void push_lit(literal l, numeral const& r, bool proofs_enabled); void push_eq(enode_pair const& p, numeral const& r, bool proofs_enabled); unsigned num_params() const { return empty()?0:m_eq_coeffs.size() + m_lit_coeffs.size() + 1; } numeral const* lit_coeffs() const { return m_lit_coeffs.c_ptr(); } numeral const* eq_coeffs() const { return m_eq_coeffs.c_ptr(); } parameter* params(char const* name); }; class gomory_cut_justification; class bound { protected: theory_var m_var; inf_numeral m_value; unsigned m_bound_kind:1; unsigned m_atom:1; public: bound(theory_var v, inf_numeral const & val, bound_kind k, bool a): m_var(v), m_value(val), m_bound_kind(k), m_atom(a) { } virtual ~bound() {} theory_var get_var() const { return m_var; } bound_kind get_bound_kind() const { return static_cast(m_bound_kind); } bool is_atom() const { return m_atom; } inf_numeral const & get_value() const { return m_value; } virtual bool has_justification() const { return false; } virtual void push_justification(antecedents& antecedents, numeral const& coeff, bool proofs_enabled) {} virtual void display(theory_arith const& th, std::ostream& out) const; }; enum atom_kind { A_LOWER, A_UPPER }; class atom : public bound { protected: bool_var m_bvar; inf_numeral m_k; unsigned m_atom_kind:2; // atom kind unsigned m_is_true:1; // cache: true if the atom was assigned to true. public: atom(bool_var bv, theory_var v, inf_numeral const & k, atom_kind kind); atom_kind get_atom_kind() const { return static_cast(m_atom_kind); } virtual ~atom() {} inline inf_numeral const & get_k() const { return m_k; } bool_var get_bool_var() const { return m_bvar; } bool is_true() const { return m_is_true; } void assign_eh(bool is_true, inf_numeral const & epsilon); virtual bool has_justification() const { return true; } virtual void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) { a.push_lit(literal(get_bool_var(), !m_is_true), coeff, proofs_enabled); } virtual void display(theory_arith const& th, std::ostream& out) const; }; class eq_bound : public bound { enode * m_lhs; enode * m_rhs; public: eq_bound(theory_var v, inf_numeral const & val, bound_kind k, enode * lhs, enode * rhs): bound(v, val, k, false), m_lhs(lhs), m_rhs(rhs) { SASSERT(m_lhs->get_root() == m_rhs->get_root()); } virtual ~eq_bound() {} virtual bool has_justification() const { return true; } virtual void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) { SASSERT(m_lhs->get_root() == m_rhs->get_root()); a.push_eq(enode_pair(m_lhs, m_rhs), coeff, proofs_enabled); } virtual void display(theory_arith const& th, std::ostream& out) const; }; class derived_bound : public bound { protected: literal_vector m_lits; eq_vector m_eqs; friend class theory_arith; public: derived_bound(theory_var v, inf_numeral const & val, bound_kind k):bound(v, val, k, false) {} virtual ~derived_bound() {} virtual bool has_justification() const { return true; } virtual void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled); virtual void push_lit(literal l, numeral const&) { m_lits.push_back(l); } virtual void push_eq(enode_pair const& p, numeral const&) { m_eqs.push_back(p); } virtual void display(theory_arith const& th, std::ostream& out) const; }; class justified_derived_bound : public derived_bound { vector m_lit_coeffs; vector m_eq_coeffs; friend class theory_arith; public: justified_derived_bound(theory_var v, inf_numeral const & val, bound_kind k):derived_bound(v, val, k) {} virtual ~justified_derived_bound() {} virtual bool has_justification() const { return true; } virtual void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled); virtual void push_lit(literal l, numeral const& coeff); virtual void push_eq(enode_pair const& p, numeral const& coeff); }; typedef int_hashtable > literal_idx_set; typedef obj_pair_hashtable eq_set; literal_vector m_tmp_acc_lits; eq_vector m_tmp_acc_eqs; literal_idx_set m_tmp_lit_set; eq_set m_tmp_eq_set; void accumulate_justification(bound & b, derived_bound & target, numeral const& coeff, literal_idx_set & lits, eq_set & eqs); inf_numeral normalize_bound(theory_var v, inf_numeral const & k, bound_kind kind); void mk_bound_from_row(theory_var v, inf_numeral const & coeff, bound_kind k, row const & r); typedef ptr_vector atoms; // #define SPARSE_MAP #ifdef SPARSE_MAP typedef u_map bool_var2atom; #else typedef ptr_vector bool_var2atom; #endif struct theory_var_lt { bool operator()(theory_var v1, theory_var v2) const { return v1 < v2; } }; typedef heap var_heap; enum var_kind { NON_BASE, BASE, QUASI_BASE }; struct var_data { unsigned m_row_id:28; // row owned by the variable, irrelevant if kind() == NON_BASE unsigned m_kind:2; unsigned m_is_int:1; unsigned m_nl_propagated:1; var_data(bool is_int = false):m_row_id(0), m_kind(NON_BASE), m_is_int(is_int), m_nl_propagated(false) {} var_kind kind() const { return static_cast(m_kind); } }; class bound_trail { theory_var m_var; bound * m_old_bound; public: bound_trail(theory_var v, bound * b, bool is_upper): m_var(v << 1 | static_cast(is_upper)), m_old_bound(b) { } bool is_upper() const { return (m_var & 1) == 1; } theory_var get_var() const { return m_var >> 1; } bound * get_old_bound() const { return m_old_bound; } }; theory_arith_stats m_stats; theory_arith_params & m_params; arith_util m_util; arith_eq_solver m_arith_eq_solver; bool m_found_unsupported_op; arith_eq_adapter m_arith_eq_adapter; vector m_rows; svector m_dead_rows; vector m_columns; // per var svector m_data; // per var vector m_value; // per var, the current assignment for the variable. vector m_old_value; // per var, the old assignment for the variable. ptr_vector m_bounds[2]; // per var, lower bound & upper_bound vector m_var_occs; // per var, atoms that contain a variable svector m_unassigned_atoms; // per var, the number of unassigned atoms that contain a variable. bool_var2atom m_bool_var2atom; // map bool_var -> atom svector m_var_pos; // temporary array used in add_rows atoms m_atoms; // set of theory atoms ptr_vector m_asserted_bounds; // set of asserted bounds unsigned m_asserted_qhead; ptr_vector m_new_atoms; // new bound atoms that have yet to be internalized. svector m_nl_monomials; // non linear monomials svector m_nl_propagated; // non linear monomials that became linear v_dependency_manager m_dep_manager; // for tracking bounds during non-linear reasoning var_heap m_to_patch; // heap containing all variables v s.t. m_value[v] does not satisfy bounds of v. nat_set m_left_basis; // temporary: set of variables that already left the basis in make_feasible bool m_blands_rule; svector m_update_trail_stack; // temporary trail stack used to restore the last feasible assignment. nat_set m_in_update_trail_stack; // set of variables in m_update_trail_stack svector m_to_check; // rows that should be checked for theory propagation nat_set m_in_to_check; // set of rows in m_to_check. inf_numeral m_tmp; random_gen m_random; unsigned m_num_conflicts; unsigned m_branch_cut_counter; bool m_eager_gcd; // true if gcd should be applied at every add_row unsigned m_final_check_idx; // backtracking svector m_bound_trail; svector m_unassigned_atoms_trail; ptr_vector m_bounds_to_delete; struct scope { unsigned m_atoms_lim; unsigned m_bound_trail_lim; unsigned m_unassigned_atoms_trail_lim; unsigned m_asserted_bounds_lim; unsigned m_asserted_qhead_old; unsigned m_bounds_to_delete_lim; unsigned m_nl_monomials_lim; unsigned m_nl_propagated_lim; }; svector m_scopes; literal_vector m_tmp_literal_vector2; antecedents m_tmp_antecedents; antecedents m_tmp_antecedents2; struct var_value_hash; friend struct var_value_hash; struct var_value_hash { theory_arith & m_th; var_value_hash(theory_arith & th):m_th(th) {} unsigned operator()(theory_var v) const { return m_th.get_value(v).hash(); } }; struct var_value_eq; friend struct var_value_eq; struct var_value_eq { theory_arith & m_th; var_value_eq(theory_arith & th):m_th(th) {} bool operator()(theory_var v1, theory_var v2) const { return m_th.get_value(v1) == m_th.get_value(v2) && m_th.is_int(v1) == m_th.is_int(v2); } }; typedef int_hashtable var_value_table; var_value_table m_var_value_table; virtual theory_var mk_var(enode * n); void found_unsupported_op(app * n); bool has_var(expr * v) const { return get_context().e_internalized(v) && get_context().get_enode(v)->get_th_var(get_id()) != null_theory_var; } theory_var expr2var(expr * v) const { SASSERT(get_context().e_internalized(v)); return get_context().get_enode(v)->get_th_var(get_id()); } expr * var2expr(theory_var v) const { return get_enode(v)->get_owner(); } bool reflection_enabled() const; bool reflect(app * n) const; unsigned lazy_pivoting_lvl() const { return m_params.m_arith_lazy_pivoting_lvl; } bool propagate_eqs() const { return m_params.m_arith_propagate_eqs && m_num_conflicts < m_params.m_arith_propagation_threshold; } bool propagate_diseqs() const { return false; } bool random_initial_value() const { return m_params.m_arith_random_initial_value; } int random_lower() const { return m_params.m_arith_random_lower; } int random_upper() const { return m_params.m_arith_random_upper; } unsigned blands_rule_threshold() const { return m_params.m_arith_blands_rule_threshold; } bound_prop_mode propagation_mode() const { return m_num_conflicts < m_params.m_arith_propagation_threshold ? m_params.m_arith_bound_prop : BP_NONE; } bool adaptive() const { return m_params.m_arith_adaptive; } double adaptive_assertion_threshold() const { return m_params.m_arith_adaptive_assertion_threshold; } unsigned max_lemma_size() const { return m_params.m_arith_max_lemma_size; } unsigned small_lemma_size() const { return m_params.m_arith_small_lemma_size; } bool relax_bounds() const { return m_params.m_arith_stronger_lemmas; } bool skip_big_coeffs() const { return m_params.m_arith_skip_rows_with_big_coeffs; } bool dump_lemmas() const { return m_params.m_arith_dump_lemmas; } bool process_atoms() const; unsigned get_num_conflicts() const { return m_num_conflicts; } var_kind get_var_kind(theory_var v) const { return m_data[v].kind(); } bool is_base(theory_var v) const { return get_var_kind(v) == BASE; } bool is_quasi_base(theory_var v) const { return get_var_kind(v) == QUASI_BASE; } bool is_non_base(theory_var v) const { return get_var_kind(v) == NON_BASE; } void set_var_kind(theory_var v, var_kind k) { m_data[v].m_kind = k; } unsigned get_var_row(theory_var v) const { SASSERT(!is_non_base(v)); return m_data[v].m_row_id; } void set_var_row(theory_var v, unsigned r_id) { m_data[v].m_row_id = r_id; } bool is_int(theory_var v) const { return m_data[v].m_is_int; } bool is_real(theory_var v) const { return !is_int(v); } bool get_implied_old_value(theory_var v, inf_numeral & r) const; inf_numeral const & get_implied_value(theory_var v) const; inf_numeral const & get_quasi_base_value(theory_var v) const { return get_implied_value(v); } inf_numeral const & get_value(theory_var v) const { return is_quasi_base(v) ? get_quasi_base_value(v) : m_value[v]; } bound * get_bound(theory_var v, bool upper) const { return m_bounds[static_cast(upper)][v]; } bound * lower(theory_var v) const { return m_bounds[0][v]; } bound * upper(theory_var v) const { return m_bounds[1][v]; } inf_numeral const & lower_bound(theory_var v) const { SASSERT(lower(v) != 0); return lower(v)->get_value(); } inf_numeral const & upper_bound(theory_var v) const { SASSERT(upper(v) != 0); return upper(v)->get_value(); } bool below_lower(theory_var v) const { bound * l = lower(v); return l != 0 && get_value(v) < l->get_value(); } bool above_upper(theory_var v) const { bound * u = upper(v); return u != 0 && get_value(v) > u->get_value(); } bool below_upper(theory_var v) const { bound * u = upper(v); return u == 0 || get_value(v) < u->get_value(); } bool above_lower(theory_var v) const { bound * l = lower(v); return l == 0 || get_value(v) > l->get_value(); } bool at_bound(theory_var v) const; bool at_lower(theory_var v) const { bound * l = lower(v); return l != 0 && get_value(v) == l->get_value(); } bool at_upper(theory_var v) const { bound * u = upper(v); return u != 0 && get_value(v) == u->get_value(); } bool is_free(theory_var v) const { return lower(v) == 0 && upper(v) == 0; } bool is_non_free(theory_var v) const { return lower(v) != 0 || upper(v) != 0; } bool is_bounded(theory_var v) const { return lower(v) != 0 && upper(v) != 0; } bool is_free(expr * n) const { SASSERT(get_context().e_internalized(n) && get_context().get_enode(n)->get_th_var(get_id()) != null_theory_var); return is_free(get_context().get_enode(n)->get_th_var(get_id())); } bool is_fixed(theory_var v) const; void set_bound_core(theory_var v, bound * new_bound, bool upper) { m_bounds[static_cast(upper)][v] = new_bound; } void restore_bound(theory_var v, bound * new_bound, bool upper) { set_bound_core(v, new_bound, upper); } void restore_nl_propagated_flag(unsigned old_trail_size); void set_bound(bound * new_bound, bool upper); inf_numeral const & get_epsilon(theory_var v) const { return is_real(v) ? this->m_real_epsilon : this->m_int_epsilon; } bool enable_cgc_for(app * n) const; enode * mk_enode(app * n); void mk_enode_if_reflect(app * n); template void add_row_entry(unsigned r_id, numeral const & coeff, theory_var v); void internalize_internal_monomial(app * m, unsigned r_id); theory_var internalize_add(app * n); theory_var internalize_mul_core(app * m); theory_var internalize_mul(app * m); theory_var internalize_div(app * n); theory_var mk_binary_op(app * n); theory_var internalize_idiv(app * n); theory_var internalize_mod(app * n); theory_var internalize_rem(app * n); theory_var internalize_to_real(app * n); theory_var internalize_to_int(app * n); void internalize_is_int(app * n); theory_var internalize_numeral(app * n); theory_var internalize_term_core(app * n); void mk_axiom(expr * n1, expr * n2); void mk_idiv_mod_axioms(expr * dividend, expr * divisor); void mk_div_axiom(expr * dividend, expr * divisor); void mk_rem_axiom(expr * dividend, expr * divisor); void mk_to_int_axiom(app* to_int); void mk_is_int_axiom(app* is_int); unsigned mk_row(); void init_row(unsigned r_id); void collect_vars(unsigned r_id, var_kind k, buffer & result); void normalize_quasi_base_row(unsigned r_id); void quasi_base_row2base_row(unsigned r_id); void normalize_base_row(unsigned r_id); void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params); void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params); void mk_bound_axioms(atom * a); void mk_bound_axiom(atom* a1, atom* a2); void flush_bound_axioms(); typename atoms::iterator next_sup(atom* a1, atom_kind kind, typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible); typename atoms::iterator next_inf(atom* a1, atom_kind kind, typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible); typename atoms::iterator first(atom_kind kind, typename atoms::iterator it, typename atoms::iterator end); struct compare_atoms { bool operator()(atom* a1, atom* a2) const { return a1->get_k() < a2->get_k(); } }; virtual bool default_internalizer() const { return false; } virtual bool internalize_atom(app * n, bool gate_ctx); virtual bool internalize_term(app * term); virtual void internalize_eq_eh(app * atom, bool_var v); virtual void apply_sort_cnstr(enode * n, sort * s); virtual void assign_eh(bool_var v, bool is_true); virtual void new_eq_eh(theory_var v1, theory_var v2); virtual bool use_diseqs() const; virtual void new_diseq_eh(theory_var v1, theory_var v2); virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual void relevant_eh(app * n); virtual void restart_eh(); virtual void init_search_eh(); /** \brief True if the assignment may be changed during final check. assume_eqs, check_int_feasibility, process_non_linear may change the current assignment to satisfy their respective constraints. However, when they do that the may create inconsistencies in the other modules. I use m_liberal_final_check to avoid infinite loops where the modules keep changing the assigment and no progress is made. If m_liberal_final_check is set to false, these modules will avoid mutating the assignment to satisfy constraints. See also m_changed_assignment flag. */ bool m_liberal_final_check; final_check_status final_check_core(); virtual final_check_status final_check_eh(); virtual bool can_propagate(); virtual void propagate(); bool propagate_core(); void failed(); virtual void flush_eh(); virtual void reset_eh(); virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const; // ----------------------------------- // // bool_var -> atom mapping // // ----------------------------------- void insert_bv2a(bool_var bv, atom * a) { #ifdef SPARSE_MAP m_bool_var2atom.insert(bv, a); #else m_bool_var2atom.setx(bv, a, 0); #endif } void erase_bv2a(bool_var bv) { #ifdef SPARSE_MAP m_bool_var2atom.erase(bv); #else m_bool_var2atom[bv] = 0; #endif } atom * get_bv2a(bool_var bv) { #ifdef SPARSE_MAP atom * a; m_bool_var2atom.find(bv, a); return a; #else return m_bool_var2atom.get(bv, 0); #endif } // ----------------------------------- // // Add Row // // ----------------------------------- void add_row(unsigned r1, const numeral & coeff, unsigned r2, bool apply_gcd_test); void add_rows(unsigned r1, unsigned sz, linear_monomial * a_xs); // ----------------------------------- // // Assignment management // // ----------------------------------- bool m_changed_assignment; //!< auxiliary variable set to true when the assignment is changed. void save_value(theory_var v); void discard_update_trail(); void restore_assignment(); void update_value_core(theory_var v, inf_numeral const & delta); void update_value(theory_var v, inf_numeral const & delta); void set_value(theory_var v, const inf_numeral & new_val) { update_value(v, new_val - m_value[v]); } // ----------------------------------- // // Pivoting // // ----------------------------------- template void pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, bool apply_gcd_test); template void eliminate(theory_var x_i, bool apply_gcd_test); void update_and_pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, inf_numeral const & x_i_new_val); int get_num_non_free_dep_vars(theory_var v, int best_so_far); theory_var select_blands_pivot_core(theory_var x_i, bool is_below, numeral & out_a_ij); template theory_var select_pivot_core(theory_var x_i, numeral & out_a_ij); theory_var select_pivot(theory_var x_i, bool is_below, numeral & out_a_ij); // ----------------------------------- // // Make feasible // // ----------------------------------- bool make_var_feasible(theory_var x_i); theory_var select_var_to_fix(); theory_var select_lg_error_var(bool least); theory_var select_greatest_error_var() { return select_lg_error_var(false); } theory_var select_least_error_var() { return select_lg_error_var(true); } theory_var select_smallest_var(); bool make_feasible(); void sign_row_conflict(theory_var x_i, bool is_below); // ----------------------------------- // // Assert bound // // ----------------------------------- bool assert_lower(bound * b); bool assert_upper(bound * b); bool assert_bound(bound * b); void sign_bound_conflict(bound * b1, bound * b2); // ----------------------------------- // // Bound propagation // // ----------------------------------- void mark_row_for_bound_prop(unsigned r1); void mark_rows_for_bound_prop(theory_var v); void is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const; void imply_bound_for_monomial(row const & r, int idx, bool lower); void imply_bound_for_all_monomials(row const & r, bool lower); void explain_bound(row const & r, int idx, bool lower, inf_numeral & delta, antecedents & antecedents); void mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k); void assign_bound_literal(literal l, row const & r, unsigned idx, bool lower, inf_numeral & delta, antecedents& antecedents); void propagate_bounds(); // ----------------------------------- // // Freedom intervals // // ----------------------------------- bool get_freedom_interval(theory_var x_j, bool & inf_l, inf_numeral & l, bool & inf_u, inf_numeral & u, numeral & m); // ----------------------------------- // // Implied eqs // // ----------------------------------- bool try_to_imply_eq(theory_var v1, theory_var v2); // ----------------------------------- // // Assume eqs with randomization // // ----------------------------------- typedef int_hashtable > var_set; var_set m_tmp_var_set; var_set m_tmp_var_set2; svector > m_assume_eq_candidates; unsigned m_assume_eq_head; bool random_update(theory_var v); void mutate_assignment(); bool assume_eqs_core(); bool delayed_assume_eqs(); // ----------------------------------- // // Integrality // // ----------------------------------- void move_non_base_vars_to_bounds(); bool has_infeasible_int_var(); theory_var find_infeasible_int_base_var(); theory_var find_bounded_infeasible_int_base_var(); void branch_infeasible_int_var(theory_var v); bool branch_infeasible_int_equality(); bool constrain_free_vars(row const & r); bool is_gomory_cut_target(row const & r); bool mk_gomory_cut(row const & r); bool gcd_test(row const & r); bool ext_gcd_test(row const & r, numeral const & least_coeff, numeral const & lcm_den, numeral const & consts); bool gcd_test(); void mk_polynomial_ge(unsigned num_args, row_entry const * args, rational const& k, expr_ref & result); bool max_min_infeasible_int_vars(); void patch_int_infeasible_vars(); void fix_non_base_vars(); unsynch_mpq_manager m_es_num_manager; // manager for euclidean solver. struct euclidean_solver_bridge; bool apply_euclidean_solver(); final_check_status check_int_feasibility(); // ----------------------------------- // // Eq propagation // // ----------------------------------- typedef std::pair value_sort_pair; typedef pair_hash, bool_hash> value_sort_pair_hash; typedef map > value2var; value2var m_fixed_var_table; typedef std::pair var_offset; typedef pair_hash > var_offset_hash; typedef map > var_offset2row_id; var_offset2row_id m_var_offset2row_id; bool is_equal(theory_var x, theory_var y) const { return get_enode(x)->get_root() == get_enode(y)->get_root(); } void fixed_var_eh(theory_var v); bool is_offset_row(row const & r, theory_var & x, theory_var & y, numeral & k) const; void propagate_cheap_eq(unsigned rid); void propagate_eq_to_core(theory_var x, theory_var y, antecedents& antecedents); virtual bool is_shared(theory_var v) const; // ----------------------------------- // // Justification // // ----------------------------------- void set_conflict(unsigned num_literals, literal const * lits, unsigned num_eqs, enode_pair const * eqs, antecedents& antecedents, bool is_lia, char const* proof_rule); void collect_fixed_var_justifications(row const & r, antecedents& antecedents) const; // ----------------------------------- // // Backtracking // // ----------------------------------- void push_bound_trail(theory_var v, bound * old_bound, bool is_upper) { m_bound_trail.push_back(bound_trail(v, old_bound, is_upper)); } void push_dec_unassigned_atoms_trail(theory_var v) { m_unassigned_atoms_trail.push_back(v); } void restore_bounds(unsigned old_trail_size); void restore_unassigned_atoms(unsigned old_trail_size); void del_atoms(unsigned old_size); void del_bounds(unsigned old_size); void del_vars(unsigned old_num_vars); void del_row(unsigned r_id); // ----------------------------------- // // Auxiliary methods // // ----------------------------------- col_entry const * get_a_base_row_that_contains(theory_var v); bool all_coeff_int(row const & r) const; col_entry const * get_row_for_eliminating(theory_var v) const; void move_unconstrained_to_base(); void elim_quasi_base_rows(); void remove_fixed_vars_from_base(); void try_to_minimize_rational_coeffs(); /** \brief See comment in theory::mk_eq_atom */ virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return m_util.mk_eq(lhs, rhs); } // ----------------------------------- // // Maximization/Minimization // // ----------------------------------- row m_tmp_row; void add_tmp_row(row & r1, numeral const & coeff, row const & r2); bool is_safe_to_leave(theory_var x, bool inc, bool& has_int, bool& is_shared); template void add_tmp_row_entry(row & r, numeral const & coeff, theory_var v); enum max_min_t { UNBOUNDED, AT_BOUND, OPTIMIZED, BEST_EFFORT}; max_min_t max_min(theory_var v, bool max, bool maintain_integrality, bool& has_shared); bool has_interface_equality(theory_var v); bool max_min(svector const & vars); max_min_t max_min(row& r, bool max, bool maintain_integrality, bool& has_shared); bool unbounded_gain(inf_numeral const & max_gain) const; bool safe_gain(inf_numeral const& min_gain, inf_numeral const & max_gain) const; void normalize_gain(numeral const& divisor, inf_numeral & max_gain) const; void init_gains(theory_var x, bool inc, inf_numeral& min_gain, inf_numeral& max_gain); bool update_gains(bool inc, theory_var x_i, numeral const& a_ij, inf_numeral& min_gain, inf_numeral& max_gain); bool move_to_bound(theory_var x_i, bool inc, unsigned& best_efforts, bool& has_shared); bool pick_var_to_leave( theory_var x_j, bool inc, numeral & a_ij, inf_numeral& min_gain, inf_numeral& max_gain, bool& shared, theory_var& x_i); // ----------------------------------- // // Non linear // // ----------------------------------- typedef int_hashtable > row_set; unsigned m_nl_rounds; bool m_nl_gb_exhausted; unsigned m_nl_strategy_idx; // for fairness expr_ref_vector m_nl_new_exprs; typedef obj_map var2num_occs; var2num_occs m_var2num_occs; typedef std::pair var_num_occs; struct var_num_occs_lt { bool operator()(var_num_occs const & vn1, var_num_occs const & vn2) const { return vn1.second > vn2.second; } }; /** \brief A monomial is 'pure' if does not have a numeric coefficient. */ bool is_pure_monomial(expr * m) const { return m_util.is_mul(m) && !m_util.is_numeral(to_app(m)->get_arg(0)); } bool is_pure_monomial(theory_var v) const { return is_pure_monomial(get_enode(v)->get_owner()); } void mark_var(theory_var v, svector & vars, var_set & already_found); void mark_dependents(theory_var v, svector & vars, var_set & already_found, row_set & already_visited_rows); void get_non_linear_cluster(svector & vars); std::pair analyze_monomial(expr * m) const; expr * get_monomial_body(expr * m) const; rational get_monomial_coeff(expr * m) const; unsigned get_num_vars_in_monomial(expr * m) const; typedef std::pair var_power_pair; var_power_pair get_var_and_degree(expr * m, unsigned i) const; void display_monomial(std::ostream & out, expr * m) const; bool propagate_nl_upward(expr * m); bool propagate_nl_downward(expr * m, unsigned i); interval mk_interval_for(theory_var v); interval mk_interval_for(expr * n); void mul_bound_of(expr * var, unsigned power, interval & target); interval evaluate_as_interval(expr * n); void dependency2new_bound(v_dependency * dep, derived_bound& new_bound); void mk_derived_nl_bound(theory_var v, inf_numeral const & coeff, bound_kind k, v_dependency * dep); bool update_bounds_using_interval(theory_var v, interval const & i); bool update_bounds_using_interval(expr * n, interval const & i); bool propagate_nl_bounds(expr * m); bool propagate_nl_bound(expr * m, int i); bool propagate_nl_bounds(); bool is_problematic_non_linear_row(row const & r); bool is_mixed_real_integer(row const & r) const; bool is_integer(row const & r) const; typedef std::pair coeff_expr; void get_polynomial_info(sbuffer const & p, sbuffer & vars); expr * p2expr(sbuffer & p); expr * power(expr * var, unsigned power); expr * mk_nary_mul(unsigned sz, expr * const * args, bool is_int); expr * mk_nary_add(unsigned sz, expr * const * args, bool is_int); expr * mk_nary_add(unsigned sz, expr * const * args); void display_nested_form(std::ostream & out, expr * p); unsigned get_degree_of(expr * m, expr * var); unsigned get_min_degree(sbuffer & p, expr * var); expr * factor(expr * m, expr * var, unsigned d); bool in_monovariate_monomials(sbuffer & p, expr * var, unsigned & i1, rational & c1, unsigned & n1, unsigned & i2, rational & c2, unsigned & n2); expr * horner(sbuffer & p, expr * var); expr * cross_nested(sbuffer & p, expr * var); bool is_cross_nested_consistent(sbuffer & p); bool is_cross_nested_consistent(row const & r); bool is_cross_nested_consistent(svector const & nl_cluster); rational get_value(theory_var v, bool & computed_epsilon); bool check_monomial_assignment(theory_var v, bool & computed_epsilon); bool check_monomial_assignments(); theory_var find_nl_var_for_branching(); bool branch_nl_int_var(theory_var v); bool is_monomial_linear(expr * m) const; numeral get_monomial_fixed_var_product(expr * m) const; expr * get_monomial_non_fixed_var(expr * m) const; bool propagate_linear_monomial(theory_var v); bool propagate_linear_monomials(); grobner::monomial * mk_gb_monomial(rational const & coeff, expr * m, grobner & gb, v_dependency * & dep, var_set & already_found); void add_monomial_def_to_gb(theory_var v, grobner & gb); void add_row_to_gb(row const & r, grobner & gb); void init_grobner_var_order(svector const & nl_cluster, grobner & gb); void init_grobner(svector const & nl_cluster, grobner & gb); interval mk_interval_for(grobner::monomial const * m); void set_conflict(v_dependency * d); bool is_inconsistent(interval const & I, unsigned num_monomials, grobner::monomial * const * monomials, v_dependency * dep); bool is_inconsistent(grobner::equation const * eq, grobner & gb); bool is_inconsistent2(grobner::equation const * eq, grobner & gb); expr * monomial2expr(grobner::monomial const * m, bool is_int); bool internalize_gb_eq(grobner::equation const * eq); enum gb_result { GB_PROGRESS, GB_NEW_EQ, GB_FAIL }; gb_result compute_grobner(svector const & nl_cluster); bool max_min_nl_vars(); final_check_status process_non_linear(); antecedents& get_antecedents(); // ----------------------------------- // // Constructor // // ----------------------------------- public: theory_arith(ast_manager & m, theory_arith_params & params); virtual ~theory_arith(); virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_arith, get_manager(), m_params); } virtual void setup(); virtual char const * get_name() const { return "arithmetic"; } // ----------------------------------- // // Model generation // // ----------------------------------- arith_factory * m_factory; numeral m_epsilon; void update_epsilon(const inf_numeral & l, const inf_numeral & u); void compute_epsilon(); void refine_epsilon(); virtual void init_model(model_generator & m); virtual model_value_proc * mk_value(enode * n, model_generator & mg); // ----------------------------------- // // Model checker // // ----------------------------------- virtual bool get_value(enode * n, expr_ref & r); // ----------------------------------- // // Optimization // // ----------------------------------- virtual inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared); virtual inf_eps_rational value(theory_var v); virtual theory_var add_objective(app* term); virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val); void enable_record_conflict(expr* bound); void record_conflict(unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, unsigned num_params, parameter* params); inf_eps_rational conflict_minimize(); private: virtual expr_ref mk_gt(theory_var v); bool_var m_bound_watch; inf_eps_rational m_upper_bound; bool get_theory_vars(expr * n, uint_set & vars); public: // ----------------------------------- // // Pretty Printing // // ----------------------------------- public: virtual void collect_statistics(::statistics & st) const; virtual void display(std::ostream & out) const; protected: void display_row(std::ostream & out, unsigned r_id, bool compact = true) const; void display_row(std::ostream & out, row const & r, bool compact = true) const; void display_rows(std::ostream & out, bool compact = true) const; void display_row_info(std::ostream & out, unsigned r_id) const; void display_row_info(std::ostream & out, row const & r) const; bool is_one_minus_one_row(row const & r) const; void display_row_shape(std::ostream & out, row const & r) const; void display_rows_shape(std::ostream & out) const; void display_rows_stats(std::ostream & out) const; void display_rows_bignums(std::ostream & out) const; void display_simplified_row(std::ostream & out, row const & r) const; void display_var(std::ostream & out, theory_var v) const; void display_vars(std::ostream & out) const; void display_bound(std::ostream & out, bound * b, unsigned indent = 0) const; void display_atoms(std::ostream & out) const; void display_asserted_atoms(std::ostream & out) const; void display_atom(std::ostream & out, atom * a, bool show_sign) const; void display_bounds_in_smtlib(std::ostream & out) const; void display_bounds_in_smtlib() const; void display_nl_monomials(std::ostream & out) const; void display_coeff_exprs(std::ostream & out, sbuffer const & p) const; void display_interval(std::ostream& out, interval const& i); void display_deps(std::ostream& out, v_dependency* dep); protected: // ----------------------------------- // // Debugging // // ----------------------------------- #ifdef Z3DEBUG bool check_vector_sizes() const; bool check_null_var_pos() const; bool has_var_kind(unsigned r_id, var_kind k) const; bool wf_row(unsigned r_id) const; bool wf_rows() const; bool wf_column(theory_var v) const; bool wf_columns() const; bool valid_assignment() const; bool valid_row_assignment() const; bool valid_row_assignment(row const & r) const; bool satisfy_bounds() const; bool satisfy_integrality() const; #endif }; class mi_ext { public: typedef rational numeral; typedef inf_rational inf_numeral; inf_numeral m_int_epsilon; inf_numeral m_real_epsilon; numeral fractional_part(inf_numeral const& n) { SASSERT(n.is_rational()); return n.get_rational() - floor(n); } static numeral fractional_part(numeral const & n) { return n - floor(n); } static inf_numeral mk_inf_numeral(numeral const & n, numeral const & r) { return inf_numeral(n, r); } static bool is_infinite(inf_numeral const& ) { return false; } mi_ext() : m_int_epsilon(rational(1)), m_real_epsilon(rational(0), true) {} }; class i_ext { public: typedef rational numeral; typedef rational inf_numeral; numeral m_int_epsilon; numeral m_real_epsilon; static numeral fractional_part(numeral const & n) { return n - floor(n); } static inf_numeral mk_inf_numeral(numeral const& n, numeral const& i) { UNREACHABLE(); return inf_numeral(n); } static bool is_infinite(inf_numeral const& ) { return false; } i_ext() : m_int_epsilon(1), m_real_epsilon(1) {} }; class si_ext { public: typedef s_integer numeral; typedef s_integer inf_numeral; numeral m_int_epsilon; numeral m_real_epsilon; static numeral fractional_part(inf_numeral const & n) { return n - floor(n); } static inf_numeral mk_inf_numeral(numeral const& n, numeral const& i) { UNREACHABLE(); return inf_numeral(n); } static bool is_infinite(inf_numeral const& ) { return false; } si_ext(): m_int_epsilon(s_integer(1)), m_real_epsilon(s_integer(1)) {} }; class smi_ext { public: typedef s_integer numeral; typedef inf_s_integer inf_numeral; inf_numeral m_int_epsilon; inf_numeral m_real_epsilon; static numeral fractional_part(const numeral & n) { UNREACHABLE(); return numeral(0); } static numeral fractional_part(const inf_numeral & n) { UNREACHABLE(); return numeral(0); } static inf_numeral mk_inf_numeral(numeral const& n, numeral const& i) { return inf_numeral(n, i); } static bool is_infinite(inf_numeral const& ) { return false; } smi_ext() : m_int_epsilon(s_integer(1)), m_real_epsilon(s_integer(0), true) {} }; class inf_ext { public: typedef rational numeral; typedef inf_eps_rational inf_numeral; inf_numeral m_int_epsilon; inf_numeral m_real_epsilon; numeral fractional_part(inf_numeral const& n) { SASSERT(n.is_rational()); return n.get_rational() - floor(n); } static numeral fractional_part(numeral const & n) { return n - floor(n); } static inf_numeral mk_inf_numeral(numeral const & n, numeral const & r) { return inf_numeral(inf_rational(n, r)); } static bool is_infinite(inf_numeral const& n) { return !n.get_infinity().is_zero(); } inf_ext() : m_int_epsilon(inf_rational(rational(1))), m_real_epsilon(inf_rational(rational(0), true)) {} }; typedef theory_arith theory_mi_arith; typedef theory_arith theory_i_arith; typedef smt::theory_arith theory_inf_arith; // typedef theory_arith theory_si_arith; // typedef theory_arith theory_smi_arith; }; #endif /* THEORY_ARITH_H_ */ z3-z3-4.4.1/src/smt/theory_arith_aux.h000066400000000000000000002470271260446376700175620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_aux.h Abstract: Author: Leonardo de Moura (leonardo) 2008-04-29. Revision History: --*/ #ifndef THEORY_ARITH_AUX_H_ #define THEORY_ARITH_AUX_H_ #include"inf_eps_rational.h" #include"theory_arith.h" #include"smt_farkas_util.h" #include"th_rewriter.h" #include"filter_model_converter.h" namespace smt { // ----------------------------------- // // Rows // // ----------------------------------- template theory_arith::row::row(): m_size(0), m_base_var(null_theory_var), m_first_free_idx(-1) { } template void theory_arith::row::reset() { m_entries.reset(); m_size = 0; m_base_var = -1; m_first_free_idx = -1; } /** \brief Add a new row_entry. The result is a reference to the new row_entry. The position of the new row_entry in the row is stored in pos_idx. */ template typename theory_arith::row_entry & theory_arith::row::add_row_entry(int & pos_idx) { m_size++; if (m_first_free_idx == -1) { pos_idx = m_entries.size(); m_entries.push_back(row_entry()); return m_entries.back(); } else { pos_idx = m_first_free_idx; row_entry & result = m_entries[pos_idx]; SASSERT(result.is_dead()); m_first_free_idx = result.m_next_free_row_entry_idx; return result; } } /** \brief Delete row_entry at position idx. */ template void theory_arith::row::del_row_entry(unsigned idx) { row_entry & t = m_entries[idx]; SASSERT(!t.is_dead()); t.m_next_free_row_entry_idx = m_first_free_idx; t.m_var = null_theory_var; m_size--; SASSERT(t.is_dead()); } /** \brief Remove holes (i.e., dead entries) from the row. */ template void theory_arith::row::compress(vector & cols) { unsigned i = 0; unsigned j = 0; unsigned sz = m_entries.size(); for (; i < sz; i++) { row_entry & t1 = m_entries[i]; if (!t1.is_dead()) { if (i != j) { row_entry & t2 = m_entries[j]; t2.m_coeff.swap(t1.m_coeff); t2.m_var = t1.m_var; t2.m_col_idx = t1.m_col_idx; SASSERT(!t2.is_dead()); column & col = cols[t2.m_var]; col[t2.m_col_idx].m_row_idx = j; } j++; } } SASSERT(j == m_size); m_entries.shrink(m_size); m_first_free_idx = -1; } /** \brief Invoke compress if the row contains too many holes (i.e., dead entries). */ template inline void theory_arith::row::compress_if_needed(vector & cols) { if (size() * 2 < num_entries()) { compress(cols); } } /** \brief Fill the map var -> pos/idx */ template inline void theory_arith::row::save_var_pos(svector & result_map) const { typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); unsigned idx = 0; for (; it != end; ++it, ++idx) { if (!it->is_dead()) { result_map[it->m_var] = idx; } } } /** \brief Reset the map var -> pos/idx. That is for all variables v in the row, set result[v] = -1 This method can be viewed as the "inverse" of save_var_pos. */ template inline void theory_arith::row::reset_var_pos(svector & result_map) const { typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead()) { result_map[it->m_var] = -1; } } }; #ifdef Z3DEBUG /** \brief Return true if the coefficient of v in the row is equals to 'expected'. */ template bool theory_arith::row::is_coeff_of(theory_var v, numeral const & expected) const { typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var == v) { return it->m_coeff == expected; } } return false; } #endif template void theory_arith::row::display(std::ostream & out) const { out << "v" << m_base_var << ", "; typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead()) { out << it->m_coeff << "*v" << it->m_var << " "; } } out << "\n"; } template typename theory_arith::numeral theory_arith::row::get_denominators_lcm() const { numeral r(1); TRACE("lcm_bug", tout << "starting get_denominators_lcm...\n";); typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead()) { r = lcm(r, denominator(it->m_coeff)); TRACE("lcm_bug", tout << "it->m_coeff: " << it->m_coeff << ", denominator(it->m_coeff): " << denominator(it->m_coeff) << ", r: " << r << "\n";); } } return r; } template int theory_arith::row::get_idx_of(theory_var v) const { typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); for (unsigned idx = 0; it != end; ++it, ++idx) { if (!it->is_dead() && it->m_var == v) return idx; } return -1; } // ----------------------------------- // // Columns // // ----------------------------------- template void theory_arith::column::reset() { m_entries.reset(); m_size = 0; m_first_free_idx = -1; } /** \brief Remove holes (i.e., dead entries) from the column. */ template void theory_arith::column::compress(vector & rows) { unsigned i = 0; unsigned j = 0; unsigned sz = m_entries.size(); for (; i < sz; i++) { col_entry & e1 = m_entries[i]; if (!e1.is_dead()) { if (i != j) { m_entries[j] = e1; row & r = rows[e1.m_row_id]; r[e1.m_row_idx].m_col_idx = j; } j++; } } SASSERT(j == m_size); m_entries.shrink(m_size); m_first_free_idx = -1; } /** \brief Invoke compress if the column contains too many holes (i.e., dead entries). */ template inline void theory_arith::column::compress_if_needed(vector & rows) { if (size() * 2 < num_entries()) { compress(rows); } } /** \brief Special version of compress, that is used when the column contain only one entry located at position singleton_pos. */ template void theory_arith::column::compress_singleton(vector & rows, unsigned singleton_pos) { SASSERT(m_size == 1); if (singleton_pos != 0) { col_entry & s = m_entries[singleton_pos]; m_entries[0] = s; row & r = rows[s.m_row_id]; r[s.m_row_idx].m_col_idx = 0; } m_first_free_idx = -1; m_entries.shrink(1); } template const typename theory_arith::col_entry * theory_arith::column::get_first_col_entry() const { typename svector::const_iterator it = m_entries.begin(); typename svector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead()) { return it; } } return 0; } template typename theory_arith::col_entry & theory_arith::column::add_col_entry(int & pos_idx) { m_size++; if (m_first_free_idx == -1) { pos_idx = m_entries.size(); m_entries.push_back(col_entry()); return m_entries.back(); } else { pos_idx = m_first_free_idx; col_entry & result = m_entries[pos_idx]; SASSERT(result.is_dead()); m_first_free_idx = result.m_next_free_row_entry_idx; return result; } } template void theory_arith::column::del_col_entry(unsigned idx) { col_entry & c = m_entries[idx]; SASSERT(!c.is_dead()); c.m_row_id = dead_row_id; c.m_next_free_row_entry_idx = m_first_free_idx; m_first_free_idx = idx; m_size--; } // ----------------------------------- // // Antecedents // // ----------------------------------- template void theory_arith::antecedents::init() { if (!m_init && !empty()) { m_params.push_back(parameter(symbol("unknown-arith"))); for (unsigned i = 0; i < m_lits.size(); i++) { m_params.push_back(parameter(m_lit_coeffs[i].to_rational())); } for (unsigned i = 0; i < m_eqs.size(); i++) { m_params.push_back(parameter(m_eq_coeffs[i].to_rational())); } m_init = true; } } template void theory_arith::antecedents::reset() { m_init = false; m_eq_coeffs.reset(); m_lit_coeffs.reset(); m_eqs.reset(); m_lits.reset(); m_params.reset(); } template void theory_arith::antecedents::push_lit(literal l, numeral const& r, bool proofs_enabled) { m_lits.push_back(l); if (proofs_enabled) { m_lit_coeffs.push_back(r); } } template void theory_arith::antecedents::push_eq(enode_pair const& p, numeral const& r, bool proofs_enabled) { m_eqs.push_back(p); if (proofs_enabled) { m_eq_coeffs.push_back(r); } } template parameter * theory_arith::antecedents::params(char const* name) { if (empty()) return 0; init(); m_params[0] = parameter(symbol(name)); return m_params.c_ptr(); } // ----------------------------------- // // Bounds // // ----------------------------------- template void theory_arith::bound::display(theory_arith const& th, std::ostream& out) const { out << "v" << get_var() << " " << get_bound_kind() << " " << get_value(); } // ----------------------------------- // // Atoms // // ----------------------------------- template theory_arith::atom::atom(bool_var bv, theory_var v, inf_numeral const & k, atom_kind kind): bound(v, inf_numeral::zero(), B_LOWER, true), m_bvar(bv), m_k(k), m_atom_kind(kind), m_is_true(false) { } template void theory_arith::atom::assign_eh(bool is_true, inf_numeral const & epsilon) { m_is_true = is_true; if (is_true) { this->m_value = m_k; this->m_bound_kind = static_cast(m_atom_kind); SASSERT((this->m_bound_kind == B_LOWER) == (m_atom_kind == A_LOWER)); } else { if (get_atom_kind() == A_LOWER) { this->m_value = m_k; this->m_value -= epsilon; this->m_bound_kind = B_UPPER; } else { SASSERT(get_atom_kind() == A_UPPER); this->m_value = m_k; this->m_value += epsilon; this->m_bound_kind = B_LOWER; } } } template void theory_arith::atom::display(theory_arith const& th, std::ostream& out) const { literal l(get_bool_var(), !m_is_true); out << "v" << bound::get_var() << " " << bound::get_bound_kind() << " " << get_k() << " "; out << l << ":"; th.get_context().display_detailed_literal(out, l); } // ----------------------------------- // // eq_bound // // ----------------------------------- template void theory_arith::eq_bound::display(theory_arith const& th, std::ostream& out) const { ast_manager& m = th.get_manager(); out << "#" << m_lhs->get_owner_id() << " " << mk_pp(m_lhs->get_owner(), m) << " = " << "#" << m_rhs->get_owner_id() << " " << mk_pp(m_rhs->get_owner(), m); } // ----------------------------------- // // Auxiliary methods // // ----------------------------------- template bool theory_arith::at_bound(theory_var v) const { bound * l = lower(v); if (l != 0 && get_value(v) == l->get_value()) return true; bound * u = upper(v); return u != 0 && get_value(v) == u->get_value(); } template bool theory_arith::is_fixed(theory_var v) const { bound * l = lower(v); if (l == 0) return false; bound * u = upper(v); if (u == 0) return false; return l->get_value() == u->get_value(); } template void theory_arith::set_bound(bound * new_bound, bool upper) { SASSERT(new_bound); SASSERT(!upper || new_bound->get_bound_kind() == B_UPPER); SASSERT(upper || new_bound->get_bound_kind() == B_LOWER); theory_var v = new_bound->get_var(); set_bound_core(v, new_bound, upper); if ((propagate_eqs() || propagate_diseqs()) && is_fixed(v)) fixed_var_eh(v); } /** \brief Return the col_entry that points to a base row that contains the given variable. Return 0 if no row contains v. */ template typename theory_arith::col_entry const * theory_arith::get_a_base_row_that_contains(theory_var v) { while (true) { column const & c = m_columns[v]; if (c.size() == 0) return 0; int quasi_base_rid = -1; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { unsigned rid = it->m_row_id; row & r = m_rows[rid]; if (is_base(r.get_base_var())) return it; else if (quasi_base_rid == -1) quasi_base_rid = rid; } } SASSERT(quasi_base_rid != -1); // since c.size() != 0 quasi_base_row2base_row(quasi_base_rid); // There is no guarantee that v is still a variable of row quasi_base_rid. // However, this loop will always terminate since I'm creating // a base row that contains v, or decreasing c.size(). } } /** \brief Return true if all coefficients of the given row are int. */ template bool theory_arith::all_coeff_int(row const & r) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && !it->m_coeff.is_int()) { TRACE("gomory_cut", display_row(tout, r, true);); return false; } } return true; } /** \brief Return the col_entry that points to row that contains the given variable. This row should not be owned by an unconstrained quasi-base variable. Return 0 if failed. This method is used by move_unconstrained_to_base */ template typename theory_arith::col_entry const * theory_arith::get_row_for_eliminating(theory_var v) const { column const & c = m_columns[v]; if (c.size() == 0) return 0; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { row const & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); if (is_quasi_base(s) && m_var_occs[s].size() == 0) continue; if (is_int(v)) { numeral const & c = r[it->m_row_idx].m_coeff; // If c == 1 or c == -1, and all other coefficients of r are integer, // then if we pivot v with the base var of r, we will produce a row // that will guarantee an integer assignment for v, when the // non-base vars have integer assignment. if (!c.is_one() && !c.is_minus_one()) continue; if (!all_coeff_int(r)) continue; } return it; } } return 0; } template void theory_arith::move_unconstrained_to_base() { if (lazy_pivoting_lvl() == 0) return; TRACE("move_unconstrained_to_base", tout << "before...\n"; display(tout);); int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (m_var_occs[v].size() == 0 && is_free(v)) { switch (get_var_kind(v)) { case QUASI_BASE: break; case BASE: if (is_int(v) && !all_coeff_int(m_rows[get_var_row(v)])) // If the row contains non integer coefficients, then v may be assigned // to a non-integer value even if all non-base variables are integer. // So, v should not be "eliminated" break; eliminate(v, m_eager_gcd); break; case NON_BASE: { col_entry const * entry = get_row_for_eliminating(v); if (entry) { TRACE("move_unconstrained_to_base", tout << "moving v" << v << " to the base\n";); row & r = m_rows[entry->m_row_id]; SASSERT(r[entry->m_row_idx].m_var == v); pivot(r.get_base_var(), v, r[entry->m_row_idx].m_coeff, m_eager_gcd); SASSERT(is_base(v)); set_var_kind(v, QUASI_BASE); SASSERT(is_quasi_base(v)); } break; } } } } TRACE("move_unconstrained_to_base", tout << "after...\n"; display(tout);); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); } /** \brief Force all quasi_base rows to become base rows. */ template void theory_arith::elim_quasi_base_rows() { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_quasi_base(v)) { quasi_base_row2base_row(get_var_row(v)); } } } /** \brief Remove fixed vars from the base. */ template void theory_arith::remove_fixed_vars_from_base() { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_base(v) && is_fixed(v)) { row const & r = m_rows[get_var_row(v)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != v && !is_fixed(it->m_var)) { break; } } if (it != end) { pivot(v, it->m_var, it->m_coeff, false); } } } } /** \brief Try to minimize the number of rational coefficients. The idea is to pivot x_i and x_j whenever there is a row x_i + 1/n * x_j + ... = 0 where - x_i is a base variable - x_j is a non-base variables - x_j is not a fixed variable - The denominator of any other coefficient a_ik divides n (I only consider the coefficient of non-fixed variables) remark if there are more than one variable with such properties, we give preference to free variables, then to variables where upper - lower is maximal. */ template void theory_arith::try_to_minimize_rational_coeffs() { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (!is_base(v) || !is_int(v)) continue; numeral max_den; row const & r = m_rows[get_var_row(v)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; if (it->m_var == v) continue; if (is_fixed(it->m_var)) continue; numeral num = numerator(it->m_coeff); if (!num.is_one() && !num.is_minus_one()) continue; numeral den = denominator(it->m_coeff); if (den > max_den) max_den = den; } if (max_den <= numeral(1)) continue; // check whether all a_ik denominators divide max_den it = r.begin_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; if (is_fixed(it->m_var)) continue; numeral den = denominator(it->m_coeff); if (!(max_den / den).is_int()) break; } if (it != end) continue; // pick best candidate theory_var x_j = null_theory_var; numeral a_ij; it = r.begin_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; if (it->m_var == v) continue; if (is_fixed(it->m_var)) continue; numeral num = numerator(it->m_coeff); if (!num.is_one() && !num.is_minus_one()) continue; numeral den = denominator(it->m_coeff); if (den != max_den) continue; if (x_j == null_theory_var || // TODO: add extra cases... is_free(it->m_var) || (is_bounded(x_j) && !is_bounded(it->m_var)) || (is_bounded(x_j) && is_bounded(it->m_var) && (upper_bound(x_j) - lower_bound(x_j) > upper_bound(it->m_var) - lower_bound(it->m_var)))) { x_j = it->m_var; a_ij = it->m_coeff; if (is_free(x_j)) break; } } if (x_j != null_theory_var) pivot(v, x_j, a_ij, false); } } // ----------------------------------- // // Derived bounds // // ----------------------------------- template void theory_arith::derived_bound::push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) { if (proofs_enabled) { for (unsigned i = 0; i < m_lits.size(); ++i) { a.push_lit(m_lits[i], coeff, proofs_enabled); } for (unsigned i = 0; i < m_eqs.size(); ++i) { a.push_eq(m_eqs[i], coeff, proofs_enabled); } } else { a.lits().append(m_lits.size(), m_lits.c_ptr()); a.eqs().append(m_eqs.size(), m_eqs.c_ptr()); } } template void theory_arith::derived_bound::display(theory_arith const& th, std::ostream& out) const { out << "v" << bound::get_var() << " " << bound::get_bound_kind() << " " << bound::get_value(); ast_manager& m = th.get_manager(); for (unsigned i = 0; i < m_eqs.size(); ++i) { enode* a = m_eqs[i].first; enode* b = m_eqs[i].second; out << " "; out << "#" << a->get_owner_id() << " " << mk_pp(a->get_owner(), m) << " = " << "#" << b->get_owner_id() << " " << mk_pp(b->get_owner(), m); } for (unsigned i = 0; i < m_lits.size(); ++i) { literal l = m_lits[i]; out << " " << l << ":"; th.get_context().display_detailed_literal(out, l); } } template void theory_arith::justified_derived_bound::push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) { for (unsigned i = 0; i < this->m_lits.size(); ++i) { a.push_lit(this->m_lits[i], coeff*m_lit_coeffs[i], proofs_enabled); } for (unsigned i = 0; i < this->m_eqs.size(); ++i) { a.push_eq(this->m_eqs[i], coeff*m_eq_coeffs[i], proofs_enabled); } } template void theory_arith::justified_derived_bound::push_lit(literal l, numeral const& coeff) { for (unsigned i = 0; i < this->m_lits.size(); ++i) { if (this->m_lits[i] == l) { m_lit_coeffs[i] += coeff; return; } } this->m_lits.push_back(l); m_lit_coeffs.push_back(coeff); } template void theory_arith::justified_derived_bound::push_eq(enode_pair const& p, numeral const& coeff) { for (unsigned i = 0; i < this->m_eqs.size(); ++i) { if (this->m_eqs[i] == p) { m_eq_coeffs[i] += coeff; return; } } this->m_eqs.push_back(p); m_eq_coeffs.push_back(coeff); } /** \brief Copy the justification of b to new_bound. Only literals and equalities not in lits and eqs are copied. The justification of b is also copied to lits and eqs. */ template void theory_arith::accumulate_justification(bound & b, derived_bound& new_bound, numeral const& coeff, literal_idx_set & lits, eq_set & eqs) { antecedents& ante = m_tmp_antecedents; ante.reset(); b.push_justification(ante, coeff, proofs_enabled()); unsigned num_lits = ante.lits().size(); for (unsigned i = 0; i < num_lits; ++i) { literal l = ante.lits()[i]; if (lits.contains(l.index())) continue; if (proofs_enabled()) { new_bound.push_lit(l, ante.lit_coeffs()[i]); } else { new_bound.push_lit(l, numeral::zero()); lits.insert(l.index()); } } unsigned num_eqs = ante.eqs().size(); for (unsigned i = 0; i < num_eqs; ++i) { enode_pair const & p = ante.eqs()[i]; if (eqs.contains(p)) continue; if (proofs_enabled()) { new_bound.push_eq(p, ante.eq_coeffs()[i]); } else { new_bound.push_eq(p, numeral::zero()); eqs.insert(p); } } } template typename theory_arith::inf_numeral theory_arith::normalize_bound(theory_var v, inf_numeral const & k, bound_kind kind) { if (is_real(v)) return k; if (kind == B_LOWER) return inf_numeral(ceil(k)); SASSERT(kind == B_UPPER); return inf_numeral(floor(k)); } /** \brief Create a derived bound for v using the given row as an explanation. */ template void theory_arith::mk_bound_from_row(theory_var v, inf_numeral const & k, bound_kind kind, row const & r) { inf_numeral k_norm = normalize_bound(v, k, kind); derived_bound * new_bound = proofs_enabled()?alloc(justified_derived_bound, v, k_norm, kind):alloc(derived_bound, v, k_norm, kind); m_bounds_to_delete.push_back(new_bound); m_asserted_bounds.push_back(new_bound); m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); #ifdef Z3DEBUG inf_numeral val; #endif typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; bool use_upper = (kind == B_UPPER); if (!it->m_coeff.is_pos()) use_upper = !use_upper; TRACE("derived_bound", tout << "using " << (use_upper ? "upper" : "lower") << " bound of v" << v << "\n";); bound * b = get_bound(v, use_upper); SASSERT(b); DEBUG_CODE({ inf_numeral tmp = b->get_value(); tmp *= it->m_coeff; val += tmp; }); accumulate_justification(*b, *new_bound, it->m_coeff, m_tmp_lit_set, m_tmp_eq_set); } } TRACE("derived_bound", tout << "explanation:\n"; literal_vector::const_iterator it1 = new_bound->m_lits.begin(); literal_vector::const_iterator end1 = new_bound->m_lits.end(); for (; it1 != end1; ++it1) tout << *it1 << " "; tout << " "; eq_vector::const_iterator it2 = new_bound->m_eqs.begin(); eq_vector::const_iterator end2 = new_bound->m_eqs.end(); for (; it2 != end2; ++it2) tout << "#" << it2->first->get_owner_id() << "=#" << it2->second->get_owner_id() << " "; tout << "\n";); DEBUG_CODE(CTRACE("derived_bound", k != val, tout << "k: " << k << ", k_norm: " << k_norm << ", val: " << val << "\n";);); SASSERT(k == val); } // ----------------------------------- // // Maximization/Minimization // // ----------------------------------- /** \brief Set: row1 <- row1 + coeff * row2, where row1 is a temporary row. \remark Columns do not need to be updated when updating a temporary row. */ template void theory_arith::add_tmp_row(row & r1, numeral const & coeff, row const & r2) { r1.save_var_pos(m_var_pos); // // loop over variables in row2, // add terms in row2 to row1. // #define ADD_TMP_ROW(_SET_COEFF_, _ADD_COEFF_) \ typename vector::const_iterator it = r2.begin_entries(); \ typename vector::const_iterator end = r2.end_entries(); \ for (; it != end; ++it) { \ if (!it->is_dead()) { \ theory_var v = it->m_var; \ int pos = m_var_pos[v]; \ if (pos == -1) { \ /* variable v is not in row1 */ \ int row_idx; \ row_entry & r_entry = r1.add_row_entry(row_idx); \ r_entry.m_var = v; \ _SET_COEFF_; \ } \ else { \ /* variable v is in row1 */ \ row_entry & r_entry = r1[pos]; \ SASSERT(r_entry.m_var == v); \ _ADD_COEFF_; \ if (r_entry.m_coeff.is_zero()) { \ r1.del_row_entry(pos); \ } \ m_var_pos[v] = -1; \ } \ } \ } ((void) 0) if (coeff.is_one()) { ADD_TMP_ROW(r_entry.m_coeff = it->m_coeff, r_entry.m_coeff += it->m_coeff); } else if (coeff.is_minus_one()) { ADD_TMP_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff.neg(), r_entry.m_coeff -= it->m_coeff); } else { ADD_TMP_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff *= coeff, r_entry.m_coeff += it->m_coeff * coeff); } r1.reset_var_pos(m_var_pos); } template bool theory_arith::is_safe_to_leave(theory_var x, bool inc, bool& has_int, bool& shared) { context& ctx = get_context(); shared |= ctx.is_shared(get_enode(x)); column & c = m_columns[x]; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); has_int = false; bool unbounded = (inc && !upper(x)) || (!inc && !lower(x)); bool was_unsafe = false; for (; it != end; ++it) { if (it->is_dead()) continue; row const & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); numeral const & coeff = r[it->m_row_idx].m_coeff; if (s != null_theory_var && is_int(s)) has_int = true; bool is_unsafe = (s != null_theory_var && is_int(s) && !coeff.is_int()); shared |= (s != null_theory_var && ctx.is_shared(get_enode(s))); was_unsafe |= is_unsafe; bool inc_s = coeff.is_neg() ? inc : !inc; unbounded &= !get_bound(s, inc_s); TRACE("opt", tout << "is v" << x << " safe to leave for v" << s << "? " << (is_unsafe?"no":"yes") << " " << (has_int?"int":"real") << " " << (unbounded?"unbounded":"bounded") << "\n"; display_row(tout, r, true);); if (was_unsafe && !unbounded) return false; } return !was_unsafe || unbounded; } template bool theory_arith::get_theory_vars(expr * n, uint_set & vars) { rational r; expr* x, *y; if (m_util.is_numeral(n, r)) { return true; } else if (m_util.is_add(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { if (!get_theory_vars(to_app(n)->get_arg(i), vars)) { return false; } } } else if (m_util.is_to_real(n, x) || m_util.is_to_int(n, x)) { return get_theory_vars(x, vars); } else if (m_util.is_mul(n, x, y) && m_util.is_numeral(x, r)) { return get_theory_vars(y, vars); } else if (m_util.is_mul(n, y, x) && m_util.is_numeral(x, r)) { return get_theory_vars(y, vars); } else if (!is_app(n)) { return false; } else if (to_app(n)->get_family_id() == m_util.get_family_id()) { return false; } else { context & ctx = get_context(); SASSERT(ctx.e_internalized(n)); enode * e = ctx.get_enode(n); if (is_attached_to_var(e)) { vars.insert(e->get_th_var(get_id())); } return true; } return true; } // // add_objective(expr* term) internalizes the arithmetic term and creates // a row for it if it is not already internalized. // Then return the variable corresponding to the term. // template theory_var theory_arith::add_objective(app* term) { theory_var v = internalize_term_core(term); TRACE("opt", tout << mk_pp(term, get_manager()) << " |-> v" << v << "\n";); SASSERT(!is_quasi_base(v)); if (!is_linear(get_manager(), term)) { v = null_theory_var; } return v; } template inf_eps_rational theory_arith::value(theory_var v) { return inf_eps_rational(get_value(v)); } template inf_eps_rational theory_arith::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { TRACE("bound_bug", display_var(tout, v); display(tout);); has_shared = false; max_min_t r = max_min(v, true, true, has_shared); if (r == UNBOUNDED) { has_shared = false; blocker = get_manager().mk_false(); return inf_eps_rational::infinity(); } else { blocker = mk_gt(v); return inf_eps_rational(get_value(v)); } } /** \brief: Create an atom that enforces the inequality v > val The arithmetical expression encoding the inequality suffices for the theory of aritmetic. */ template expr_ref theory_arith::mk_gt(theory_var v) { ast_manager& m = get_manager(); inf_numeral const& val = get_value(v); expr* obj = get_enode(v)->get_owner(); expr_ref e(m); rational r = val.get_rational(); if (m_util.is_int(m.get_sort(obj))) { if (r.is_int()) { r += rational::one(); } else { r = ceil(r); } e = m_util.mk_numeral(r, m.get_sort(obj)); e = m_util.mk_ge(obj, e); } else { // obj is over the reals. e = m_util.mk_numeral(r, m.get_sort(obj)); if (val.get_infinitesimal().is_neg()) { e = m_util.mk_ge(obj, e); } else { e = m_util.mk_gt(obj, e); } } return e; } /** \brief create atom that enforces: val <= v The atom that enforces the inequality is created directly as opposed to using arithmetical terms. This allows to handle inequalities with non-standard numbers. */ template expr_ref theory_arith::mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val) { ast_manager& m = get_manager(); context& ctx = get_context(); std::ostringstream strm; strm << val << " <= " << mk_pp(get_enode(v)->get_owner(), get_manager()); app* b = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); if (!ctx.b_internalized(b)) { fm.insert(b->get_decl()); bool_var bv = ctx.mk_bool_var(b); ctx.set_var_theory(bv, get_id()); // ctx.set_enode_flag(bv, true); atom* a = alloc(atom, bv, v, val, A_LOWER); mk_bound_axioms(a); m_unassigned_atoms[v]++; m_var_occs[v].push_back(a); m_atoms.push_back(a); insert_bv2a(bv, a); TRACE("arith", tout << mk_pp(b, m) << "\n"; display_atom(tout, a, false);); } return expr_ref(b, m); } /** \brief enable watching bound atom. */ template void theory_arith::enable_record_conflict(expr* bound) { m_params.m_arith_bound_prop = BP_NONE; SASSERT(propagation_mode() == BP_NONE); // bound propagtion rules are not (yet) handled. if (bound) { context& ctx = get_context(); m_bound_watch = ctx.get_bool_var(bound); } else { m_bound_watch = null_bool_var; } m_upper_bound = -inf_eps_rational::infinity(); } /** \brief pos < 0 == r(Ax <= b) + q(v <= val) == val' <= q*v & q*v <= q*val q*v - val' >= 0 => (q*v - val' - q*v)/q >= -v == val/q <= v */ template void theory_arith::record_conflict( unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, unsigned num_params, parameter* params) { ast_manager& m = get_manager(); context& ctx = get_context(); expr_ref tmp(m), vq(m); expr* x, *y, *e; if (null_bool_var == m_bound_watch) { return; } unsigned idx = num_lits; for (unsigned i = 0; i < num_lits; ++i) { if (m_bound_watch == lits[i].var()) { //SASSERT(!lits[i].sign()); idx = i; break; } } if (idx == num_lits) { return; } for (unsigned i = 0; i < num_lits; ++i) { ctx.literal2expr(lits[i], tmp); } for (unsigned i = 0; i < num_eqs; ++i) { enode_pair const& p = eqs[i]; x = p.first->get_owner(); y = p.second->get_owner(); tmp = m.mk_eq(x,y); } SASSERT(num_params == 1 + num_lits + num_eqs); SASSERT(params[0].is_symbol()); SASSERT(params[0].get_symbol() == symbol("farkas")); // for now, just handle this rule. farkas_util farkas(m); rational q; for (unsigned i = 0; i < num_lits; ++i) { parameter const& pa = params[i+1]; SASSERT(pa.is_rational()); if (idx == i) { q = abs(pa.get_rational()); continue; } ctx.literal2expr(lits[i], tmp); farkas.add(abs(pa.get_rational()), to_app(tmp)); } for (unsigned i = 0; i < num_eqs; ++i) { enode_pair const& p = eqs[i]; x = p.first->get_owner(); y = p.second->get_owner(); tmp = m.mk_eq(x,y); parameter const& pa = params[1 + num_lits + i]; SASSERT(pa.is_rational()); farkas.add(abs(pa.get_rational()), to_app(tmp)); } tmp = farkas.get(); // IF_VERBOSE(1, verbose_stream() << "Farkas result: " << tmp << "\n";); atom* a = get_bv2a(m_bound_watch); SASSERT(a); expr_ref_vector terms(m); vector mults; bool strict = false; if (m_util.is_le(tmp, x, y) || m_util.is_ge(tmp, y, x)) { } else if (m.is_not(tmp, e) && (m_util.is_le(e, y, x) || m_util.is_ge(e, x, y))) { strict = true; } else if (m.is_eq(tmp, x, y)) { } else { UNREACHABLE(); } e = var2expr(a->get_var()); q *= farkas.get_normalize_factor(); SASSERT(!m_util.is_int(e) || q.is_int()); // TBD: not fully handled. if (q.is_one()) { vq = e; } else { vq = m_util.mk_mul(m_util.mk_numeral(q, q.is_int()), e); } vq = m_util.mk_add(m_util.mk_sub(x, y), vq); if (!q.is_one()) { vq = m_util.mk_div(vq, m_util.mk_numeral(q, q.is_int())); } th_rewriter rw(m); rw(vq, tmp); VERIFY(m_util.is_numeral(tmp, q)); if (m_upper_bound < q) { m_upper_bound = q; if (strict) { m_upper_bound -= get_epsilon(a->get_var()); } IF_VERBOSE(1, verbose_stream() << "new upper bound: " << m_upper_bound << "\n";); } } /** \brief find the minimal upper bound on the variable that was last enabled for conflict recording. */ template inf_eps_rational theory_arith::conflict_minimize() { return m_upper_bound; } /** \brief Select tightest variable x_i to pivot with x_j. The goal is to select a x_i such that the value of x_j is increased (decreased) if inc = true (inc = false), and the tableau remains feasible. Store the gain in x_j of the pivoting operation in 'gain'. Note the gain can be too much. That is, it may make x_i infeasible. In this case, instead of pivoting we move x_j to its upper bound (lower bound) when inc = true (inc = false). If no x_i imposes a restriction on x_j, then return null_theory_var. That is, x_j is free to move to its upper bound (lower bound). Get the equations for x_j: x_i1 = coeff_1 * x_j + rest_1 ... x_in = coeff_n * x_j + rest_n gain_k := (upper_bound(x_ik) - value(x_ik))/coeff_k */ template bool theory_arith::pick_var_to_leave( theory_var x_j, // non-base variable to increment/decrement bool inc, numeral & a_ij, // coefficient of x_i inf_numeral& min_gain, // minimal required gain on x_j (integral value on integers) inf_numeral& max_gain, // maximal possible gain on x_j bool& has_shared, // determine if pivot involves shared variable theory_var& x_i) { // base variable to pivot with x_j context& ctx = get_context(); x_i = null_theory_var; init_gains(x_j, inc, min_gain, max_gain); has_shared |= ctx.is_shared(get_enode(x_j)); if (is_int(x_j) && !get_value(x_j).is_int()) { return false; } column & c = m_columns[x_j]; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); bool empty_column = true; for (; it != end; ++it) { if (it->is_dead()) continue; empty_column = false; row const & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); numeral const & coeff_ij = r[it->m_row_idx].m_coeff; if (update_gains(inc, s, coeff_ij, min_gain, max_gain) || (x_i == null_theory_var && !unbounded_gain(max_gain))) { x_i = s; a_ij = coeff_ij; } has_shared |= ctx.is_shared(get_enode(s)); } TRACE("opt", tout << (safe_gain(min_gain, max_gain)?"safe":"unsafe") << "\n"; tout << "min gain: " << min_gain; tout << " max gain: " << max_gain << "\n"; tout << "v" << x_i << " "; tout << (has_shared?"shared":"not shared") << "\n";); SASSERT(!safe_gain(min_gain, max_gain) || empty_column || (unbounded_gain(max_gain) == (x_i == null_theory_var))); return !empty_column && safe_gain(min_gain, max_gain); } template bool theory_arith::unbounded_gain(inf_numeral const & max_gain) const { return max_gain.is_minus_one(); } /* A gain is 'safe' with respect to the tableau if: - the selected variable is unbounded and every base variable where it occurs is unbounded in the direction of the gain. max_gain == -1 is used to indicate unbounded variables. - the selected variable is a rational (min_gain == -1, max_gain >= 0). - */ template bool theory_arith::safe_gain(inf_numeral const& min_gain, inf_numeral const & max_gain) const { return unbounded_gain(max_gain) || min_gain <= max_gain; } /** \brief ensure that maximal gain is divisible by divisor. */ template void theory_arith::normalize_gain(numeral const& divisor, inf_numeral & max_gain) const { SASSERT(divisor.is_int()); if (!divisor.is_minus_one() && !max_gain.is_minus_one()) { max_gain = floor(max_gain/divisor)*divisor; } } /** \brief initialize gains for x_j based on the bounds for x_j. */ template void theory_arith::init_gains( theory_var x, // non-base variable to increment/decrement bool inc, inf_numeral& min_gain, // min value to increment, -1 if rational inf_numeral& max_gain) { // max value to decrement, -1 if unbounded min_gain = -inf_numeral::one(); max_gain = -inf_numeral::one(); if (inc && upper(x)) { max_gain = upper_bound(x) - get_value(x); } else if (!inc && lower(x)) { max_gain = get_value(x) - lower_bound(x); } if (is_int(x)) { min_gain = inf_numeral::one(); } TRACE("opt", tout << "v" << x << " := " << get_value(x) << " " << "min gain: " << min_gain << " " << "max gain: " << max_gain << "\n";); SASSERT(max_gain.is_minus_one() || !max_gain.is_neg()); SASSERT(min_gain.is_minus_one() || min_gain.is_one()); SASSERT(is_int(x) == min_gain.is_one()); } template bool theory_arith::update_gains( bool inc, // increment/decrement x_j theory_var x_i, // potential base variable to pivot numeral const& a_ij, // coefficient of x_j in row where x_i is base. inf_numeral& min_gain, // min value to increment, -1 if rational inf_numeral& max_gain) { // max value to decrement, -1 if unbounded // x_i = row + a_ij*x_j // a_ij > 0, inc -> decrement x_i // a_ij < 0, !inc -> decrement x_i // a_ij denominator SASSERT(!a_ij.is_zero()); if (!safe_gain(min_gain, max_gain)) return false; inf_numeral max_inc = inf_numeral::minus_one(); bool decrement_x_i = (inc && a_ij.is_pos()) || (!inc && a_ij.is_neg()); if (decrement_x_i && lower(x_i)) { max_inc = abs((get_value(x_i) - lower_bound(x_i))/a_ij); } else if (!decrement_x_i && upper(x_i)) { max_inc = abs((upper_bound(x_i) - get_value(x_i))/a_ij); } numeral den_aij(1); bool is_tighter = false; if (is_int(x_i)) den_aij = denominator(a_ij); SASSERT(den_aij.is_pos() && den_aij.is_int()); if (is_int(x_i) && !den_aij.is_one()) { if (min_gain.is_neg()) { min_gain = inf_numeral(den_aij); } else { min_gain = inf_numeral(lcm(min_gain.get_rational(), den_aij)); } normalize_gain(min_gain.get_rational(), max_gain); } if (!max_inc.is_minus_one()) { if (is_int(x_i)) { TRACE("opt", tout << "v" << x_i << " a_ij " << a_ij << " " << "min gain: " << min_gain << " " << "max gain: " << max_gain << "\n";); max_inc = floor(max_inc); normalize_gain(min_gain.get_rational(), max_inc); } if (unbounded_gain(max_gain)) { max_gain = max_inc; is_tighter = true; } else if (max_gain > max_inc) { max_gain = max_inc; is_tighter = true; } } TRACE("opt", tout << "v" << x_i << " a_ij " << a_ij << " " << "min gain: " << min_gain << " " << "max gain: " << max_gain << " tighter: " << (is_tighter?"true":"false") << "\n";); SASSERT(max_gain.is_minus_one() || !max_gain.is_neg()); SASSERT(min_gain.is_minus_one() || !min_gain.is_neg()); //SASSERT(!is_int(x_i) || min_gain.is_pos()); //SASSERT(!is_int(x_i) || min_gain.is_int()); //SASSERT(!is_int(x_i) || max_gain.is_int()); return is_tighter; } /** \brief Check if bound change affects interface equality. */ template bool theory_arith::has_interface_equality(theory_var x) { theory_var num = get_num_vars(); context& ctx = get_context(); enode* r = get_enode(x)->get_root(); for (theory_var v = 0; v < num; v++) { if (v == x) continue; enode* n = get_enode(v); if (ctx.is_shared(n) && n->get_root() == r) { return true; } } return false; } /** \brief Maximize (Minimize) the given temporary row. Return true if succeeded. */ template typename theory_arith::max_min_t theory_arith::max_min( row & r, bool max, bool maintain_integrality, bool& has_shared) { m_stats.m_max_min++; unsigned best_efforts = 0; bool inc = false; context& ctx = get_context(); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); numeral a_ij, curr_a_ij, coeff, curr_coeff; inf_numeral min_gain, max_gain, curr_min_gain, curr_max_gain; unsigned round = 0; max_min_t result = OPTIMIZED; has_shared = false; unsigned max_efforts = 10 + (ctx.get_random_value() % 20); while (best_efforts < max_efforts && !ctx.get_cancel_flag()) { theory_var x_j = null_theory_var; theory_var x_i = null_theory_var; max_gain.reset(); min_gain.reset(); ++round; TRACE("opt", tout << "round: " << round << ", max: " << max << "\n"; display_row(tout, r, true); tout << "state:\n"; display(tout);); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; theory_var curr_x_j = it->m_var; theory_var curr_x_i = null_theory_var; SASSERT(is_non_base(curr_x_j)); curr_coeff = it->m_coeff; bool curr_inc = curr_coeff.is_pos() ? max : !max; if ((curr_inc && at_upper(curr_x_j)) || (!curr_inc && at_lower(curr_x_j))) { // variable cannot be used for max/min. continue; } bool picked_var = pick_var_to_leave(curr_x_j, curr_inc, curr_a_ij, curr_min_gain, curr_max_gain, has_shared, curr_x_i); SASSERT(!picked_var || safe_gain(curr_min_gain, curr_max_gain)); if (!picked_var) { // && (r.size() > 1 || !safe_gain(curr_min_gain, curr_max_gain)) TRACE("opt", tout << "no variable picked\n";); best_efforts++; } else if (curr_x_i == null_theory_var) { TRACE("opt", tout << "v" << curr_x_j << " is unrestricted by other variables\n";); // we can increase/decrease curr_x_j as much as we want. x_i = null_theory_var; // unbounded x_j = curr_x_j; inc = curr_inc; min_gain = curr_min_gain; max_gain = curr_max_gain; break; } else if (curr_max_gain > max_gain) { x_i = curr_x_i; x_j = curr_x_j; a_ij = curr_a_ij; coeff = curr_coeff; max_gain = curr_max_gain; min_gain = curr_min_gain; inc = curr_inc; } else if (curr_max_gain.is_zero() && (x_i == null_theory_var || curr_x_i < x_i)) { x_i = curr_x_i; x_j = curr_x_j; a_ij = curr_a_ij; coeff = curr_coeff; max_gain = curr_max_gain; min_gain = curr_min_gain; inc = curr_inc; // continue } } TRACE("opt", tout << "after traversing row:\nx_i: v" << x_i << ", x_j: v" << x_j << ", gain: " << max_gain << "\n"; tout << "best efforts: " << best_efforts << " has shared: " << has_shared << "\n";); if (x_j == null_theory_var) { TRACE("opt", tout << "row is " << (max ? "maximized" : "minimized") << "\n"; display_row(tout, r, true);); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); result = OPTIMIZED; break; } if (min_gain.is_pos() && !min_gain.is_one()) { ++best_efforts; } if (x_i == null_theory_var) { // can increase/decrease x_j as much as we want. if (inc && upper(x_j)) { if (max_gain.is_zero()) return BEST_EFFORT; SASSERT(!unbounded_gain(max_gain)); update_value(x_j, max_gain); TRACE("opt", tout << "moved v" << x_j << " to upper bound\n";); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); continue; } if (!inc && lower(x_j)) { if (max_gain.is_zero()) return BEST_EFFORT; SASSERT(!unbounded_gain(max_gain)); SASSERT(max_gain.is_pos()); max_gain.neg(); update_value(x_j, max_gain); TRACE("opt", tout << "moved v" << x_j << " to lower bound\n";); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); continue; } #if 0 if (ctx.is_shared(get_enode(x_j)) && has_interface_equality(x_j)) { ++best_efforts; } else { SASSERT(unbounded_gain(max_gain)); has_shared = false; best_efforts = 0; } #endif // // NB. As it stands this is a possibly unsound conclusion for shared theories. // the tradeoff is non-termination for unbounded objectives in the // presence of sharing. // has_shared = false; best_efforts = 0; result = UNBOUNDED; break; } if (!is_fixed(x_j) && is_bounded(x_j) && (upper_bound(x_j) - lower_bound(x_j) == max_gain)) { // can increase/decrease x_j up to upper/lower bound. if (inc) { TRACE("opt", tout << "moved v" << x_j << " to upper bound\n";); } else { max_gain.neg(); TRACE("opt", tout << "moved v" << x_j << " to lower bound\n";); } update_value(x_j, max_gain); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); continue; } TRACE("opt", tout << "max: " << max << ", x_i: v" << x_i << ", x_j: v" << x_j << ", a_ij: " << a_ij << ", coeff: " << coeff << "\n"; if (upper(x_i)) tout << "upper x_i: " << upper_bound(x_i) << " "; if (lower(x_i)) tout << "lower x_i: " << lower_bound(x_i) << " "; tout << "value x_i: " << get_value(x_i) << "\n"; if (upper(x_j)) tout << "upper x_j: " << upper_bound(x_j) << " "; if (lower(x_j)) tout << "lower x_j: " << lower_bound(x_j) << " "; tout << "value x_j: " << get_value(x_j) << "\n"; ); pivot(x_i, x_j, a_ij, false); SASSERT(is_non_base(x_i)); SASSERT(is_base(x_j)); bool inc_xi = inc?a_ij.is_neg():a_ij.is_pos(); if (!move_to_bound(x_i, inc_xi, best_efforts, has_shared)) { TRACE("opt", tout << "can't move bound fully\n";); // break; // break; } row & r2 = m_rows[get_var_row(x_j)]; coeff.neg(); add_tmp_row(r, coeff, r2); SASSERT(r.get_idx_of(x_j) == -1); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); } TRACE("opt", display(tout);); return (best_efforts>0 || ctx.get_cancel_flag())?BEST_EFFORT:result; } /** Move the variable x_i maximally towards its bound as long as bounds of other variables are not violated. Returns false if an integer bound was truncated and no progress was made. */ template bool theory_arith::move_to_bound( theory_var x_i, // variable to move bool inc, // increment variable or decrement unsigned& best_efforts, // is bound move a best effort? bool& has_shared) { // does move include shared variables? inf_numeral min_gain, max_gain; if (is_int(x_i) && !get_value(x_i).is_int()) { ++best_efforts; return false; } init_gains(x_i, inc, min_gain, max_gain); column & c = m_columns[x_i]; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; row const & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); numeral const & coeff = r[it->m_row_idx].m_coeff; update_gains(inc, s, coeff, min_gain, max_gain); has_shared |= get_context().is_shared(get_enode(s)); } bool result = false; if (safe_gain(min_gain, max_gain)) { TRACE("opt", tout << "Safe delta: " << max_gain << "\n";); SASSERT(!unbounded_gain(max_gain)); if (!inc) { max_gain.neg(); } update_value(x_i, max_gain); if (!min_gain.is_pos() || min_gain.is_one()) { ++best_efforts; } result = !max_gain.is_zero(); } if (!result) { ++best_efforts; } return result; } /** \brief Add an entry to a temporary row. \remark Columns do not need to be updated when updating a temporary row. */ template template void theory_arith::add_tmp_row_entry(row & r, numeral const & coeff, theory_var v) { int r_idx; row_entry & r_entry = r.add_row_entry(r_idx); r_entry.m_var = v; r_entry.m_coeff = coeff; if (invert) r_entry.m_coeff .neg(); } /** \brief Maximize/Minimize the given variable. The bounds of v are update if procedure succeeds. */ template typename theory_arith::max_min_t theory_arith::max_min(theory_var v, bool max, bool maintain_integrality, bool& has_shared) { expr* e = get_enode(v)->get_owner(); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); SASSERT(!is_quasi_base(v)); if ((max && at_upper(v)) || (!max && at_lower(v))) { TRACE("opt", display_var(tout << "At " << (max?"max: ":"min: ") << mk_pp(e, get_manager()) << " \n", v);); return AT_BOUND; // nothing to be done... } m_tmp_row.reset(); if (is_non_base(v)) { add_tmp_row_entry(m_tmp_row, numeral(1), v); } else { row & r = m_rows[get_var_row(v)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != v) add_tmp_row_entry(m_tmp_row, it->m_coeff, it->m_var); } } max_min_t r = max_min(m_tmp_row, max, maintain_integrality, has_shared); if (r == OPTIMIZED) { TRACE("opt", tout << mk_pp(e, get_manager()) << " " << (max ? "max" : "min") << " value is: " << get_value(v) << "\n"; display_row(tout, m_tmp_row, true); display_row_info(tout, m_tmp_row);); mk_bound_from_row(v, get_value(v), max ? B_UPPER : B_LOWER, m_tmp_row); } else if (r == UNBOUNDED) { TRACE("opt", display_var(tout << "unbounded: " << mk_pp(e, get_manager()) << "\n", v);); } else { TRACE("opt", display_var(tout << "not optimized: " << mk_pp(e, get_manager()) << "\n", v);); } return r; } /** \brief Maximize & Minimize variables in vars. Return false if an inconsistency was detected. */ template bool theory_arith::max_min(svector const & vars) { bool succ = false; bool has_shared = false; svector::const_iterator it = vars.begin(); svector::const_iterator end = vars.end(); for (; it != end; ++it) { if (max_min(*it, true, false, has_shared) == OPTIMIZED && !has_shared) succ = true; if (max_min(*it, false, false, has_shared) == OPTIMIZED && !has_shared) succ = true; } if (succ) { // process new bounds bool r = propagate_core(); TRACE("opt", tout << "after max/min round:\n"; display(tout);); return r; } return true; } // ----------------------------------- // // Freedom intervals // // ----------------------------------- /** \brief See Model-based theory combination paper. Return false if failed to build the freedom interval. \remark If x_j is an integer variable, then m will contain the lcm of the denominators of a_ij. We only consider the a_ij coefficients for x_i */ template bool theory_arith::get_freedom_interval(theory_var x_j, bool & inf_l, inf_numeral & l, bool & inf_u, inf_numeral & u, numeral & m) { if (is_base(x_j)) return false; inf_numeral const & x_j_val = get_value(x_j); column & c = m_columns[x_j]; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); inf_l = true; inf_u = true; l.reset(); u.reset(); m = numeral(1); #define IS_FIXED() { if (!inf_l && !inf_u && l == u) goto fi_succeeded; } #define SET_LOWER(VAL) { inf_numeral const & _VAL = VAL; if (inf_l || _VAL > l) { l = _VAL; inf_l = false; } IS_FIXED(); } #define SET_UPPER(VAL) { inf_numeral const & _VAL = VAL; if (inf_u || _VAL < u) { u = _VAL; inf_u = false; } IS_FIXED(); } if (lower(x_j)) { SET_LOWER(lower_bound(x_j)); } if (upper(x_j)) { SET_UPPER(upper_bound(x_j)); } for (; it != end; ++it) { if (!it->is_dead()) { row & r = m_rows[it->m_row_id]; theory_var x_i = r.get_base_var(); if (x_i != null_theory_var && !is_quasi_base(x_i)) { numeral const & a_ij = r[it->m_row_idx].m_coeff; inf_numeral const & x_i_val = get_value(x_i); if (is_int(x_i) && is_int(x_j) && !a_ij.is_int()) m = lcm(m, denominator(a_ij)); bound * x_i_lower = lower(x_i); bound * x_i_upper = upper(x_i); if (a_ij.is_neg()) { if (x_i_lower) { inf_numeral new_l = x_j_val + ((x_i_val - x_i_lower->get_value()) / a_ij); SET_LOWER(new_l); } if (x_i_upper) { inf_numeral new_u = x_j_val + ((x_i_val - x_i_upper->get_value()) / a_ij); SET_UPPER(new_u); } } else { if (x_i_upper) { inf_numeral new_l = x_j_val + ((x_i_val - x_i_upper->get_value()) / a_ij); SET_LOWER(new_l); } if (x_i_lower) { inf_numeral new_u = x_j_val + ((x_i_val - x_i_lower->get_value()) / a_ij); SET_UPPER(new_u); } } } } } fi_succeeded: TRACE("freedom_interval", tout << "freedom variable for:\n"; display_var(tout, x_j); tout << "["; if (inf_l) tout << "-oo"; else tout << l; tout << "; "; if (inf_u) tout << "oo"; else tout << u; tout << "]\n";); return true; } // ----------------------------------- // // Implied eqs // // ----------------------------------- /** \brief Try to check whether v1 == v2 is implied by the current state. If it is return true. */ template bool theory_arith::try_to_imply_eq(theory_var v1, theory_var v2) { SASSERT(v1 != v2); SASSERT(get_value(v1) == get_value(v2)); SASSERT(valid_assignment()); if (is_quasi_base(v1) || is_quasi_base(v2)) return false; m_tmp_row.reset(); if (is_non_base(v1)) { add_tmp_row_entry(m_tmp_row, numeral(1), v1); } else { row & r = m_rows[get_var_row(v1)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != v1) add_tmp_row_entry(m_tmp_row, it->m_coeff, it->m_var); } } m_tmp_row.save_var_pos(m_var_pos); #define ADD_ENTRY(COEFF, VAR) { \ int pos = m_var_pos[VAR]; \ if (pos == -1) { \ add_tmp_row_entry(m_tmp_row, COEFF, VAR); \ } \ else { \ row_entry & r_entry = m_tmp_row[pos]; \ SASSERT(r_entry.m_var == VAR); \ r_entry.m_coeff += COEFF; \ if (r_entry.m_coeff.is_zero()) \ m_tmp_row.del_row_entry(pos); \ m_var_pos[VAR] = -1; \ } \ } if (is_non_base(v2)) { ADD_ENTRY(numeral(-1), v2); } else { row & r = m_rows[get_var_row(v2)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != v2) { numeral c = it->m_coeff; c.neg(); ADD_ENTRY(c, it->m_var); } } } m_tmp_row.reset_var_pos(m_var_pos); SASSERT(m_tmp_row.size() > 0); #if 0 TRACE("imply_eq", display_row_info(tout, m_tmp_row);); m_tmp_acc_lits.reset(); m_tmp_acc_eqs.reset(); m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); if ((OPTIMIZED == max_min(m_tmp_row, true)) && is_zero_row(m_tmp_row, true, m_tmp_acc_lits, m_tmp_acc_eqs, m_tmp_lit_set, m_tmp_eq_set) && (OPTIMIZED == max_min(m_tmp_row, false)) && is_zero_row(m_tmp_row, false, m_tmp_acc_lits, m_tmp_acc_eqs, m_tmp_lit_set, m_tmp_eq_set)) { // v1 == v2 TRACE("imply_eq", tout << "found new implied equality:\n"; display_var(tout, v1); display_var(tout, v2);); // TODO: assert implied equality // return true; } #endif return false; } // ----------------------------------- // // Assume eqs // // The revamped assume eqs try to perturbate the // current assignment using pivoting operations. // // ----------------------------------- #define RANGE 10000 /** \brief Performs a random update on v using its freedom interval. Return true if it was possible to change. */ template bool theory_arith::random_update(theory_var v) { if (is_fixed(v) || !is_non_base(v)) return false; bool inf_l, inf_u; inf_numeral l, u; numeral m; get_freedom_interval(v, inf_l, l, inf_u, u, m); if (inf_l && inf_u) { inf_numeral new_val = inf_numeral(m_random() % (RANGE + 1)); set_value(v, new_val); return true; } if (is_int(v)) { if (!inf_l) { l = ceil(l); if (!m.is_one()) l = m*ceil(l/m); } if (!inf_u) { u = floor(u); if (!m.is_one()) u = m*floor(u/m); } } if (!inf_l && !inf_u && l >= u) return false; if (inf_u) { SASSERT(!inf_l); inf_numeral delta = inf_numeral(m_random() % (RANGE + 1)); inf_numeral new_val = l + m*delta; set_value(v, new_val); return true; } if (inf_l) { SASSERT(!inf_u); inf_numeral delta = inf_numeral(m_random() % (RANGE + 1)); inf_numeral new_val = u - m*delta; set_value(v, new_val); return true; } if (!is_int(v)) { SASSERT(!inf_l && !inf_u); numeral delta = numeral(m_random() % (RANGE + 1)); inf_numeral new_val = l + ((delta * (u - l)) / numeral(RANGE)); set_value(v, new_val); return true; } else { unsigned range = RANGE; numeral r = (u.get_rational() - l.get_rational()) / m; if (r < numeral(RANGE)) range = static_cast(r.get_uint64()); inf_numeral new_val = l + m * (inf_numeral(m_random() % (range + 1))); set_value(v, new_val); return true; } } template void theory_arith::mutate_assignment() { remove_fixed_vars_from_base(); int num_vars = get_num_vars(); m_var_value_table.reset(); m_tmp_var_set.reset(); sbuffer candidates; for (theory_var v = 0; v < num_vars; v++) { enode * n1 = get_enode(v); if (!is_relevant_and_shared(n1)) continue; theory_var other = m_var_value_table.insert_if_not_there(v); if (other == v) continue; // first node with the given value... enode * n2 = get_enode(other); if (n1->get_root() == n2->get_root()) continue; if (!is_fixed(v)) { candidates.push_back(v); } else if (!is_fixed(other) && !m_tmp_var_set.contains(other)) { m_tmp_var_set.insert(other); candidates.push_back(other); } } if (candidates.empty()) return; typename sbuffer::iterator it = candidates.begin(); typename sbuffer::iterator end = candidates.end(); m_tmp_var_set.reset(); m_tmp_var_set2.reset(); for (; it != end; ++it) { theory_var v = *it; SASSERT(!is_fixed(v)); if (is_base(v)) { row & r = m_rows[get_var_row(v)]; typename vector::const_iterator it2 = r.begin_entries(); typename vector::const_iterator end2 = r.end_entries(); for (; it2 != end2; ++it2) { if (!it2->is_dead() && it2->m_var != v && !is_fixed(it2->m_var) && random_update(it2->m_var)) break; } } else { random_update(v); } } SASSERT(m_to_patch.empty()); } /** \brief We must redefine this method, because theory of arithmetic contains underspecified operators such as division by 0. (/ a b) is essentially an uninterpreted function when b = 0. Thus, 'a' must be considered a shared var if it is the child of an underspecified operator. */ template bool theory_arith::is_shared(theory_var v) const { enode * n = get_enode(v); enode * r = n->get_root(); enode_vector::const_iterator it = r->begin_parents(); enode_vector::const_iterator end = r->end_parents(); for (; it != end; ++it) { enode * parent = *it; app * o = parent->get_owner(); if (o->get_family_id() == get_id()) { switch (o->get_decl_kind()) { case OP_DIV: case OP_IDIV: case OP_REM: case OP_MOD: return true; default: break; } } } return false; } template bool theory_arith::assume_eqs_core() { // See comment in m_liberal_final_check declaration if (m_liberal_final_check) mutate_assignment(); TRACE("assume_eq_int", display(tout);); unsigned old_sz = m_assume_eq_candidates.size(); TRACE("func_interp_bug", display(tout);); m_var_value_table.reset(); bool result = false; int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { enode * n = get_enode(v); TRACE("func_interp_bug", tout << "#" << n->get_owner_id() << " -> " << m_value[v] << "\n";); if (!is_relevant_and_shared(n)) continue; theory_var other = null_theory_var; other = m_var_value_table.insert_if_not_there(v); if (other == v) continue; enode * n2 = get_enode(other); if (n->get_root() == n2->get_root()) continue; TRACE("func_interp_bug", tout << "adding to assume_eq queue #" << n->get_owner_id() << " #" << n2->get_owner_id() << "\n";); m_assume_eq_candidates.push_back(std::make_pair(other, v)); result = true; } if (result) get_context().push_trail(restore_size_trail, false>(m_assume_eq_candidates, old_sz)); return delayed_assume_eqs(); // return this->assume_eqs(m_var_value_table); } template bool theory_arith::delayed_assume_eqs() { if (m_assume_eq_head == m_assume_eq_candidates.size()) return false; get_context().push_trail(value_trail(m_assume_eq_head)); while (m_assume_eq_head < m_assume_eq_candidates.size()) { std::pair const & p = m_assume_eq_candidates[m_assume_eq_head]; theory_var v1 = p.first; theory_var v2 = p.second; m_assume_eq_head++; CTRACE("func_interp_bug", get_value(v1) == get_value(v2) && get_enode(v1)->get_root() != get_enode(v2)->get_root(), tout << "assuming eq: #" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";); if (get_value(v1) == get_value(v2) && get_enode(v1)->get_root() != get_enode(v2)->get_root() && assume_eq(get_enode(v1), get_enode(v2))) { return true; } } return false; } #if 0 /** \brief Check if the given row is implying a zero upper/lower bound. Accumulate the justification in the given vectors. Return true if it is. */ template bool theory_arith::is_zero_row(row const & r, bool upper, literal_vector & lit_vect, eq_vector & eq_vect, literal_idx_set & lits, eq_set & eqs) { inf_numeral val; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; bool use_upper = upper; if (!it->m_coeff.is_pos()) use_upper = !use_upper; bound * b = get_bound(v, use_upper); inf_numeral tmp = b->get_value(); tmp *= it->m_coeff; val += tmp; SASSERT(b); acc_umulate_justification(*b, lit_vect, eq_vect, lits, eqs); } } return val.is_zero(); } #endif }; #endif /* THEORY_ARITH_AUX_H_ */ z3-z3-4.4.1/src/smt/theory_arith_core.h000066400000000000000000004056751260446376700177220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_core.h Abstract: Author: Leonardo de Moura (leonardo) 2008-04-22. Revision History: --*/ #ifndef THEORY_ARITH_CORE_H_ #define THEORY_ARITH_CORE_H_ #include"smt_context.h" #include"theory_arith.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"smt_model_generator.h" #include"ast_smt2_pp.h" namespace smt { template void theory_arith::found_unsupported_op(app * n) { if (!m_found_unsupported_op) { TRACE("arith", tout << "found non supported expression:\n" << mk_pp(n, get_manager()) << "\n";); get_context().push_trail(value_trail(m_found_unsupported_op)); m_found_unsupported_op = true; } } template bool theory_arith::process_atoms() const { if (!adaptive()) return true; unsigned total_conflicts = get_context().get_num_conflicts(); if (total_conflicts < 10) return true; double f = static_cast(get_num_conflicts())/static_cast(total_conflicts); TRACE_CODE({ static unsigned counter = 0; counter++; if (counter % 1000 == 0) { TRACE("arith_adaptive", tout << "arith_conflicts: " << get_num_conflicts() << " total_conflicts: " << total_conflicts << " factor: " << f << "\n";); } }); return f >= adaptive_assertion_threshold(); } template theory_var theory_arith::mk_var(enode * n) { theory_var r = theory::mk_var(n); SASSERT(r == static_cast(m_columns.size())); SASSERT(check_vector_sizes()); bool is_int = m_util.is_int(n->get_owner()); TRACE("mk_arith_var", tout << mk_pp(n->get_owner(), get_manager()) << " is_int: " << is_int << "\n";); m_columns .push_back(column()); m_data .push_back(var_data(is_int)); if (random_initial_value()) { unsigned val = (m_random()%(random_upper() - random_lower())) + random_lower(); m_value .push_back(inf_numeral(val)); } else { m_value .push_back(inf_numeral()); } m_old_value .push_back(inf_numeral()); SASSERT(m_var_occs.size() == static_cast(r)); m_var_occs .push_back(atoms()); SASSERT(m_var_occs.back().empty()); m_unassigned_atoms .push_back(0); m_var_pos .push_back(-1); m_bounds[0] .push_back(0); m_bounds[1] .push_back(0); if (r >= static_cast(m_to_patch.get_bounds())) m_to_patch.set_bounds(r + 1); m_in_update_trail_stack.assure_domain(r); m_left_basis.assure_domain(r); m_in_to_check.assure_domain(r); if (is_pure_monomial(n->get_owner())) m_nl_monomials.push_back(r); SASSERT(check_vector_sizes()); SASSERT(m_var_occs[r].empty()); TRACE("mk_arith_var", tout << "#" << n->get_owner_id() << " :=\n" << mk_ll_pp(n->get_owner(), get_manager()) << "\n"; tout << "is_attached_to_var: " << is_attached_to_var(n) << ", var: " << n->get_th_var(get_id()) << "\n";); get_context().attach_th_var(n, this, r); SASSERT(m_var_occs.back().empty()); return r; } template inline bool theory_arith::reflection_enabled() const { return m_params.m_arith_reflect; } template inline bool theory_arith::reflect(app * n) const { if (reflection_enabled()) return true; // reflect everything // Every underspecified operator must be reflected in the egraph. // Although it is underspecified, it is still a function. // For example, a/b != a/0 then we must have b != 0 // I use the Egraph to enforce that. if (n->get_family_id() == get_id()) { switch (n->get_decl_kind()) { case OP_DIV: case OP_IDIV: case OP_REM: case OP_MOD: return true; default: break; } } return false; } template inline bool theory_arith::enable_cgc_for(app * n) const { // Congruence closure is not enabled for (+ ...) and (* ...) applications. return !(n->get_family_id() == get_id() && (n->get_decl_kind() == OP_ADD || n->get_decl_kind() == OP_MUL)); } /** \brief Create an enode for n. */ template enode * theory_arith::mk_enode(app * n) { context & ctx = get_context(); if (ctx.e_internalized(n)) return ctx.get_enode(n); else return ctx.mk_enode(n, !reflect(n), false, enable_cgc_for(n)); } /** \brief Create an enode for n if reflection is enabled. */ template void theory_arith::mk_enode_if_reflect(app * n) { if (reflection_enabled()) { // make sure that n is in the e-graph mk_enode(n); } } /** \brief Add coeff * v to the row r. The column is also updated. */ template template void theory_arith::add_row_entry(unsigned r_id, numeral const & coeff, theory_var v) { row & r = m_rows[r_id]; column & c = m_columns[v]; int r_idx; row_entry & r_entry = r.add_row_entry(r_idx); int c_idx; col_entry & c_entry = c.add_col_entry(c_idx); r_entry.m_var = v; r_entry.m_coeff = coeff; if (invert) r_entry.m_coeff .neg(); r_entry.m_col_idx = c_idx; c_entry.m_row_id = r_id; c_entry.m_row_idx = r_idx; } /** \brief Internalize the monomial of a polynomial. Store the monomial in the given row. The monomial is negated before being inserted into the row. */ template void theory_arith::internalize_internal_monomial(app * m, unsigned r_id) { context & ctx = get_context(); if (ctx.e_internalized(m)) { enode * e = ctx.get_enode(m); if (is_attached_to_var(e)) { // there is already a theory variable (i.e., name) for m. theory_var v = e->get_th_var(get_id()); add_row_entry(r_id, numeral::minus_one(), v); return; } } rational _val; if (m_util.is_mul(m) && m_util.is_numeral(m->get_arg(0), _val)) { SASSERT(m->get_num_args() == 2); numeral val(_val); theory_var v = internalize_term_core(to_app(m->get_arg(1))); if (reflection_enabled()) { internalize_term_core(to_app(m->get_arg(0))); mk_enode(m); } add_row_entry(r_id, val, v); } else { theory_var v = internalize_term_core(m); add_row_entry(r_id, numeral::minus_one(), v); } } /** \brief Internalize a polynomial (+ h t). Return an alias for the monomial, that is, a variable v such that v = (+ h t) is a new row in the tableau. */ template theory_var theory_arith::internalize_add(app * n) { TRACE("add_bug", tout << "n: " << mk_pp(n, get_manager()) << "\n";); CTRACE("internalize_add_bug", n->get_num_args() == 2 && n->get_arg(0) == n->get_arg(1), tout << "n: " << mk_pp(n, get_manager()) << "\n";); SASSERT(m_util.is_add(n)); unsigned r_id = mk_row(); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { internalize_internal_monomial(to_app(n->get_arg(i)), r_id); } enode * e = mk_enode(n); theory_var v = e->get_th_var(get_id()); if (v == null_theory_var) { v = mk_var(e); add_row_entry(r_id, numeral::one(), v); init_row(r_id); } else { // HACK: n was already internalized by the internalize_internal_monomial or internalize_internal_add call above. // This can happen when one of calls invoke (indirectly) mk_axiom. // For example, they contain a nested to_int(t) term. // TODO: reimplement mk_axiom. The current implementation is flaky. // I should cache the axioms that need to be created. They should only be internalized after we finished internalizing the // current atom. Several other theories have similar problems. del_row(r_id); } return v; } /** \brief Internalize a term (* x y z) that does not contain a coefficient (numeral). */ template theory_var theory_arith::internalize_mul_core(app * m) { TRACE("internalize_mul_core", tout << "internalizing...\n" << mk_pp(m,get_manager()) << "\n";); if (!m_util.is_mul(m)) return internalize_term_core(m); for (unsigned i = 0; i < m->get_num_args(); i++) { app * arg = to_app(m->get_arg(i)); SASSERT(!m_util.is_numeral(arg)); theory_var v = internalize_term_core(arg); if (v == null_theory_var) { mk_var(mk_enode(arg)); } } enode * e = mk_enode(m); theory_var v = e->get_th_var(get_id()); if (v == null_theory_var) { v = mk_var(e); } return v; } /** \brief Internalize the terms of the form (* c (* t1 ... tn)) and (* t1 ... tn). Return an alias for the term. */ template theory_var theory_arith::internalize_mul(app * m) { rational _val; SASSERT(m_util.is_mul(m)); if (m_util.is_numeral(m->get_arg(0), _val)) { SASSERT(m->get_num_args() == 2); numeral val(_val); SASSERT(!val.is_one()); unsigned r_id = mk_row(); if (reflection_enabled()) internalize_term_core(to_app(m->get_arg(0))); theory_var v = internalize_mul_core(to_app(m->get_arg(1))); add_row_entry(r_id, val, v); enode * e = mk_enode(m); theory_var s = mk_var(e); add_row_entry(r_id, numeral::one(), s); init_row(r_id); return s; } else { return internalize_mul_core(m); } } template theory_var theory_arith::mk_binary_op(app * n) { SASSERT(n->get_num_args() == 2); context & ctx = get_context(); if (ctx.e_internalized(n)) return expr2var(n); ctx.internalize(n->get_arg(0), false); ctx.internalize(n->get_arg(1), false); enode * e = mk_enode(n); return mk_var(e); } template theory_var theory_arith::internalize_div(app * n) { theory_var s = mk_binary_op(n); context & ctx = get_context(); if (!ctx.relevancy()) mk_div_axiom(n->get_arg(0), n->get_arg(1)); return s; } template theory_var theory_arith::internalize_idiv(app * n) { theory_var s = mk_binary_op(n); context & ctx = get_context(); app * mod = m_util.mk_mod(n->get_arg(0), n->get_arg(1)); ctx.internalize(mod, false); if (ctx.relevancy()) ctx.add_relevancy_dependency(n, mod); return s; } template theory_var theory_arith::internalize_mod(app * n) { TRACE("arith_mod", tout << "internalizing...\n" << mk_pp(n, get_manager()) << "\n";); theory_var s = mk_binary_op(n); context & ctx = get_context(); if (!ctx.relevancy()) mk_idiv_mod_axioms(n->get_arg(0), n->get_arg(1)); return s; } template theory_var theory_arith::internalize_rem(app * n) { theory_var s = mk_binary_op(n); context & ctx = get_context(); if (!ctx.relevancy()) { mk_rem_axiom(n->get_arg(0), n->get_arg(1)); } return s; } template void theory_arith::mk_axiom(expr * ante, expr * conseq) { ast_manager & m = get_manager(); TRACE("arith_axiom", tout << mk_pp(ante, m) << "\n" << mk_pp(conseq, m) << "\n";); context & ctx = get_context(); simplifier & s = ctx.get_simplifier(); expr_ref s_ante(m), s_conseq(m); expr* s_conseq_n, * s_ante_n; bool negated; proof_ref pr(m); s(ante, s_ante, pr); negated = m.is_not(s_ante, s_ante_n); if (negated) s_ante = s_ante_n; ctx.internalize(s_ante, false); literal l_ante = ctx.get_literal(s_ante); if (negated) l_ante.neg(); s(conseq, s_conseq, pr); negated = m.is_not(s_conseq, s_conseq_n); if (negated) s_conseq = s_conseq_n; ctx.internalize(s_conseq, false); literal l_conseq = ctx.get_literal(s_conseq); if (negated) l_conseq.neg(); literal lits[2] = {l_ante, l_conseq}; ctx.mk_th_axiom(get_id(), 2, lits); if (ctx.relevancy()) { if (l_ante == false_literal) { ctx.mark_as_relevant(l_conseq); } else { // We must mark the antecedent as relevant, otherwise the // core will not propagate it to the theory of arithmetic. // In a previous version, we were not doing that. // The core was assigning it to true, this assignment was inconsistent with // the state of the theory of arithmetic, but the conflict was not detected // because it was not propagated to this theory. ctx.mark_as_relevant(l_ante); ctx.add_rel_watch(~l_ante, s_conseq); // mark consequent as relevant if antecedent is false. } } } template void theory_arith::mk_div_axiom(expr * p, expr * q) { if (!m_util.is_zero(q)) { ast_manager & m = get_manager(); TRACE("div_axiom_bug", tout << "expanding div_axiom for: " << mk_pp(p, m) << " / " << mk_pp(q, m) << "\n";); expr * div = m_util.mk_div(p, q); expr * zero = m_util.mk_numeral(rational(0), false); expr_ref eqz(m), eq(m); eqz = m.mk_eq(q, zero); eq = m.mk_eq(m_util.mk_mul(q, div), p); TRACE("div_axiom_bug", tout << "eqz: " << mk_pp(eqz, m) << "\neq: " << mk_pp(eq, m) << "\n";); mk_axiom(eqz, eq); } } template void theory_arith::mk_idiv_mod_axioms(expr * dividend, expr * divisor) { if (!m_util.is_zero(divisor)) { // if divisor is zero, then idiv and mod are uninterpreted functions. ast_manager & m = get_manager(); expr * div = m_util.mk_idiv(dividend, divisor); expr * mod = m_util.mk_mod(dividend, divisor); expr * zero = m_util.mk_numeral(rational(0), true); expr * abs_divisor = m.mk_ite(m_util.mk_lt(divisor, zero), m_util.mk_sub(zero, divisor), divisor); expr_ref eqz(m), eq(m), lower(m), upper(m); eqz = m.mk_eq(divisor, zero); eq = m.mk_eq(m_util.mk_add(m_util.mk_mul(divisor, div), mod), dividend); lower = m_util.mk_le(zero, mod); upper = m_util.mk_lt(mod, abs_divisor); mk_axiom(eqz, eq); mk_axiom(eqz, lower); mk_axiom(eqz, upper); rational k; if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) && k.is_pos() && k < rational(8)) { rational j(0); #if 1 literal_buffer lits; expr_ref mod_j(m); context& ctx = get_context(); while(j < k) { mod_j = m.mk_eq(mod, m_util.mk_numeral(j, true)); ctx.internalize(mod_j, false); literal lit(ctx.get_literal(mod_j)); lits.push_back(lit); ctx.mark_as_relevant(lit); j += rational(1); } ctx.mk_th_axiom(get_id(), lits.size(), lits.begin()); #else // performs slightly worse. literal_buffer lits; expr_ref mod_j(m), div_j(m), num_j(m), n_mod_j(m), n_div_j(m); context& ctx = get_context(); while(j < k) { num_j = m_util.mk_numeral(j, true); mod_j = m.mk_eq(mod, num_j); div_j = m.mk_eq(dividend, m_util.mk_add(m_util.mk_mul(div, divisor), num_j)); n_mod_j = m.mk_not(mod_j); n_div_j = m.mk_not(div_j); mk_axiom(n_mod_j, div_j); mk_axiom(n_div_j, mod_j); j += rational(1); } #endif } } } template void theory_arith::mk_rem_axiom(expr * dividend, expr * divisor) { // if divisor is zero, then rem is an uninterpreted function. ast_manager & m = get_manager(); expr * zero = m_util.mk_numeral(rational(0), true); expr * rem = m_util.mk_rem(dividend, divisor); expr * mod = m_util.mk_mod(dividend, divisor); expr_ref dltz(m), eq1(m), eq2(m); dltz = m_util.mk_lt(divisor, zero); eq1 = m.mk_eq(rem, mod); eq2 = m.mk_eq(rem, m_util.mk_sub(zero, mod)); // n < 0 || rem(a,n) = mod(a, n) mk_axiom(dltz, eq1); dltz = m.mk_not(dltz); // !n < 0 || rem(a,n) = -mod(a, n) mk_axiom(dltz, eq2); } // // create the term: s := to_real(to_int(x)) - x // add the bounds 0 <= s < 1 // template void theory_arith::mk_to_int_axiom(app * n) { SASSERT(m_util.is_to_int(n)); ast_manager & m = get_manager(); expr* x = n->get_arg(0); // to_int (to_real x) = x if (m_util.is_to_real(x)) { mk_axiom(m.mk_false(), m.mk_eq(to_app(x)->get_arg(0), n)); return; } expr* to_r = m_util.mk_to_real(n); expr_ref lo(m_util.mk_le(to_r, x), m); expr_ref hi(m_util.mk_lt(x, m_util.mk_add(to_r, m_util.mk_numeral(rational(1), false))), m); mk_axiom(m.mk_false(), lo); mk_axiom(m.mk_false(), hi); } template theory_var theory_arith::internalize_to_int(app * n) { SASSERT(n->get_num_args() == 1); context & ctx = get_context(); if (ctx.e_internalized(n)) return expr2var(n); /* theory_var arg = */ internalize_term_core(to_app(n->get_arg(0))); enode * e = mk_enode(n); theory_var r = mk_var(e); if (!ctx.relevancy()) mk_to_int_axiom(n); return r; } // // Create the axiom (iff (is_int x) (= x (to_real (to_int x)))) // template void theory_arith::mk_is_int_axiom(app * n) { SASSERT(m_util.is_is_int(n)); ast_manager & m = get_manager(); expr* x = n->get_arg(0); expr* eq = m.mk_eq(m_util.mk_to_real(m_util.mk_to_int(x)), x); mk_axiom(m.mk_not(n), eq); mk_axiom(m.mk_not(eq), n); } template void theory_arith::internalize_is_int(app * n) { SASSERT(n->get_num_args() == 1); context & ctx = get_context(); if (ctx.b_internalized(n)) return; /* theory_var arg = */ internalize_term_core(to_app(n->get_arg(0))); enode * e = mk_enode(n); /* theory_var r = */ mk_var(e); if (!ctx.relevancy()) mk_is_int_axiom(n); } // create the row: r - arg = 0 template theory_var theory_arith::internalize_to_real(app * n) { SASSERT(n->get_num_args() == 1); context & ctx = get_context(); if (ctx.e_internalized(n)) return expr2var(n); TRACE("to_real_bug", tout << "to-real\n" << mk_ismt2_pp(n, get_manager()) << "\n";); theory_var arg = internalize_term_core(to_app(n->get_arg(0))); // n may be internalized by the call above if n is of the form (to_real (to_int t)) // The internalizer for (to_int t) will create (to_real (to_int t)) and internalize it. // This is a recurrent bug in Z3. TODO: I should create a queue of axioms that need to be asserted. // This queue is processes only after we finish the internalization of the current assertion. if (ctx.e_internalized(n)) return expr2var(n); enode * e = mk_enode(n); theory_var r = mk_var(e); unsigned r_id = mk_row(); add_row_entry(r_id, numeral(1), arg); add_row_entry(r_id, numeral(1), r); init_row(r_id); return r; } template theory_var theory_arith::internalize_numeral(app * n) { rational _val; VERIFY(m_util.is_numeral(n, _val)); numeral val(_val); SASSERT(!get_context().e_internalized(n)); enode * e = mk_enode(n); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. // e->mark_as_interpreted(); theory_var v = mk_var(e); inf_numeral ival(val); bound * l = alloc(bound, v, ival, B_LOWER, false); bound * u = alloc(bound, v, ival, B_UPPER, false); set_bound(l, false); set_bound(u, true); m_bounds_to_delete.push_back(l); m_bounds_to_delete.push_back(u); m_value[v] = ival; return v; } /** \brief Internalize the given term and return an alias for it. Return null_theory_var if the term was not implemented by the theory yet. */ template theory_var theory_arith::internalize_term_core(app * n) { TRACE("arith_internalize_detail", tout << "internalize_term_core:\n" << mk_pp(n, get_manager()) << "\n";); context & ctx = get_context(); if (ctx.e_internalized(n)) { enode * e = ctx.get_enode(n); if (is_attached_to_var(e)) return e->get_th_var(get_id()); } SASSERT(!m_util.is_sub(n)); SASSERT(!m_util.is_uminus(n)); if (m_util.is_add(n)) return internalize_add(n); else if (m_util.is_mul(n)) return internalize_mul(n); else if (m_util.is_div(n)) return internalize_div(n); else if (m_util.is_idiv(n)) return internalize_idiv(n); else if (m_util.is_mod(n)) return internalize_mod(n); else if (m_util.is_rem(n)) return internalize_rem(n); else if (m_util.is_to_real(n)) return internalize_to_real(n); else if (m_util.is_to_int(n)) return internalize_to_int(n); else if (m_util.is_numeral(n)) return internalize_numeral(n); if (m_util.is_power(n)) { // unsupported found_unsupported_op(n); return mk_binary_op(n); } if (m_util.is_irrational_algebraic_numeral(n)) { // unsupported found_unsupported_op(n); enode * e = mk_enode(n); return mk_var(e); } else { TRACE("arith_internalize_detail", tout << "before:\n" << mk_pp(n, get_manager()) << "\n";); if (!ctx.e_internalized(n)) ctx.internalize(n, false); TRACE("arith_internalize_detail", tout << "after:\n" << mk_pp(n, get_manager()) << "\n";); enode * e = ctx.get_enode(n); if (!is_attached_to_var(e)) return mk_var(e); else return e->get_th_var(get_id()); } } /** \brief Create a new empty row. Return the new row id. */ template unsigned theory_arith::mk_row() { unsigned r; if (m_dead_rows.empty()) { r = m_rows.size(); m_rows.push_back(row()); } else { r = m_dead_rows.back(); m_dead_rows.pop_back(); } m_in_to_check.assure_domain(r); SASSERT(m_rows[r].size() == 0); SASSERT(m_rows[r].num_entries() == 0); return r; } /** \brief Initialize a new row, the last monomial is going to be the owner of the row. The last monomial must have coeff 1. */ template void theory_arith::init_row(unsigned r_id) { row & r = m_rows[r_id]; SASSERT(r.m_first_free_idx == -1); SASSERT(r.size() != 0); SASSERT(r.size() == r.num_entries()); SASSERT(r[r.size() - 1].m_coeff.is_one()); theory_var s = r[r.size() - 1].m_var; r.m_base_var = s; set_var_row(s, r_id); TRACE("init_row_bug", tout << "before:\n"; display_row_info(tout, r);); if (lazy_pivoting_lvl() > 2) { set_var_kind(s, QUASI_BASE); normalize_quasi_base_row(r_id); SASSERT(!has_var_kind(get_var_row(s), QUASI_BASE)); } else { normalize_base_row(r_id); SASSERT(get_var_kind(s) == BASE); SASSERT(!has_var_kind(get_var_row(s), BASE)); } TRACE("init_row_bug", tout << "after:\n"; display_row_info(tout, r);); if (propagation_mode() != BP_NONE) mark_row_for_bound_prop(r_id); SASSERT(r.is_coeff_of(s, numeral::one())); SASSERT(wf_row(r_id)); } /** \brief Collect variables in the given row that have the given kind, but a different from the row main var (i.e., var that owns the row). The inv of the coefficients is also stored in result */ template void theory_arith::collect_vars(unsigned r_id, var_kind k, buffer & result) { row & r = m_rows[r_id]; theory_var base = r.m_base_var; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); unsigned idx = 0; for (; it != end; ++it, ++idx) { if (!it->is_dead() && get_var_kind(it->m_var) == k && it->m_var != base) { numeral c = it->m_coeff; c.neg(); result.push_back(linear_monomial(c, it->m_var)); } } } /** \brief Normalize row as a quasi base row, it does not contain quasi-base variables different from r.m_base_var. */ template void theory_arith::normalize_quasi_base_row(unsigned r_id) { buffer to_add; collect_vars(r_id, QUASI_BASE, to_add); add_rows(r_id, to_add.size(), to_add.c_ptr()); SASSERT(!has_var_kind(r_id, QUASI_BASE)); } /** \brief Convert a quasi-base row into a base row. */ template void theory_arith::quasi_base_row2base_row(unsigned r_id) { TRACE("quasi_base_row2base_row", tout << "quasi_base_row2base_row...\n";); buffer to_add; collect_vars(r_id, BASE, to_add); TRACE("quasi_base_bug_detail", display_row_info(tout, r_id); for (unsigned i = 0; i < to_add.size(); i++) { theory_var v = to_add[i].m_var; SASSERT(is_base(v)); SASSERT(!has_var_kind(get_var_row(v), BASE)); tout << "coeff: " << to_add[i].m_coeff << ", var: #" << get_enode(v)->get_owner_id() << "\n"; display_row_info(tout, get_var_row(v)); tout << "\n"; }); add_rows(r_id, to_add.size(), to_add.c_ptr()); theory_var s = m_rows[r_id].get_base_var(); set_var_kind(s, BASE); inf_numeral tmp; if (get_implied_old_value(s, tmp)) { // This code is necessary because of the method // restore_assignment. That is, the invariant // valid_row_assignment() could be invalidated when // restore_assignment is executed. // // Remark: The method restore_assignment will restore the // old value of variables, and the value of s should be // compatible with them. // // For example, consider the following scenario: // // 1) s is a quasi-base var, s depends on x, and value of x is v0 // // 2) x is updated to v1, but the update does not affect s (s is a quasi-base var). // // 3) quasi_base_row2base_row is executed, and we compute the value of s using // the current value of x (v1). // // 4) a conflict is detected, and the value of x is restored to v0. // // 5) if this branch is deleted, the row owned by s will not satisfy // valid_row_assignment. // m_value[s] = tmp; SASSERT(!m_in_update_trail_stack.contains(s)); save_value(s); } m_value[s] = get_implied_value(s); TRACE("valid_row_assignment_bug", display_row_info(tout, r_id);); SASSERT(!has_var_kind(r_id, BASE)); SASSERT(!has_var_kind(r_id, QUASI_BASE)); SASSERT(valid_row_assignment(m_rows[r_id])); } /** \brief Normalize row as a base row. A base row does not contain quasi-base and base variables different from r.m_base_var. */ template void theory_arith::normalize_base_row(unsigned r_id) { if (lazy_pivoting_lvl() > 0) normalize_quasi_base_row(r_id); quasi_base_row2base_row(r_id); } template void theory_arith::mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) { get_context().mk_th_axiom(get_id(), l1, l2, num_params, params); } template void theory_arith::mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { get_context().mk_th_axiom(get_id(), l1, l2, l3, num_params, params); } template void theory_arith::mk_bound_axioms(atom * a1) { theory_var v = a1->get_var(); atoms & occs = m_var_occs[v]; TRACE("mk_bound_axioms", tout << "add bound axioms for v" << v << " " << a1 << "\n";); if (!get_context().is_searching()) { // // NB. We make an assumption that user push calls propagation // before internal scopes are pushed. This flushes all newly // asserted atoms into the right context. // m_new_atoms.push_back(a1); return; } inf_numeral const & k1(a1->get_k()); atom_kind kind1 = a1->get_atom_kind(); TRACE("mk_bound_axioms", display_atom(tout << "making bound axioms for " << a1 << " ", a1, true); tout << "\n";); typename atoms::iterator it = occs.begin(); typename atoms::iterator end = occs.end(); typename atoms::iterator lo_inf = end, lo_sup = end; typename atoms::iterator hi_inf = end, hi_sup = end; for (; it != end; ++it) { atom * a2 = *it; inf_numeral const & k2(a2->get_k()); atom_kind kind2 = a2->get_atom_kind(); TRACE("mk_bound_axioms", display_atom(tout << "compare " << a2 << " ", a2, true); tout << "\n";); if (k1 == k2 && kind1 == kind2) { continue; } SASSERT(k1 != k2 || kind1 != kind2); if (kind2 == A_LOWER) { if (k2 < k1) { if (lo_inf == end || k2 > (*lo_inf)->get_k()) { lo_inf = it; } } else if (lo_sup == end || k2 < (*lo_sup)->get_k()) { lo_sup = it; } } else if (k2 < k1) { if (hi_inf == end || k2 > (*hi_inf)->get_k()) { hi_inf = it; } } else if (hi_sup == end || k2 < (*hi_sup)->get_k()) { hi_sup = it; } } if (lo_inf != end) mk_bound_axiom(a1, *lo_inf); if (lo_sup != end) mk_bound_axiom(a1, *lo_sup); if (hi_inf != end) mk_bound_axiom(a1, *hi_inf); if (hi_sup != end) mk_bound_axiom(a1, *hi_sup); } template void theory_arith::mk_bound_axiom(atom* a1, atom* a2) { TRACE("mk_bound_axioms", tout << a1 << " " << a2 << "\n";); theory_var v = a1->get_var(); literal l1(a1->get_bool_var()); literal l2(a2->get_bool_var()); inf_numeral const & k1(a1->get_k()); inf_numeral const & k2(a2->get_k()); atom_kind kind1 = a1->get_atom_kind(); atom_kind kind2 = a2->get_atom_kind(); bool v_is_int = is_int(v); SASSERT(v == a2->get_var()); if (k1 == k2 && kind1 == kind2) return; SASSERT(k1 != k2 || kind1 != kind2); parameter coeffs[3] = { parameter(symbol("farkas")), parameter(rational(1)), parameter(rational(1)) }; if (kind1 == A_LOWER) { if (kind2 == A_LOWER) { if (k2 <= k1) { mk_clause(~l1, l2, 3, coeffs); } else { mk_clause(l1, ~l2, 3, coeffs); } } else if (k1 <= k2) { // k1 <= k2, k1 <= x or x <= k2 mk_clause(l1, l2, 3, coeffs); } else { // k1 > hi_inf, k1 <= x => ~(x <= hi_inf) mk_clause(~l1, ~l2, 3, coeffs); if (v_is_int && k1 == k2 + inf_numeral(1)) { // k1 <= x or x <= k1-1 mk_clause(l1, l2, 3, coeffs); } } } else if (kind2 == A_LOWER) { if (k1 >= k2) { // k1 >= lo_inf, k1 >= x or lo_inf <= x mk_clause(l1, l2, 3, coeffs); } else { // k1 < k2, k2 <= x => ~(x <= k1) mk_clause(~l1, ~l2, 3, coeffs); if (v_is_int && k1 == k2 - inf_numeral(1)) { // x <= k1 or k1+l <= x mk_clause(l1, l2, 3, coeffs); } } } else { // kind1 == A_UPPER, kind2 == A_UPPER if (k1 >= k2) { // k1 >= k2, x <= k2 => x <= k1 mk_clause(l1, ~l2, 3, coeffs); } else { // k1 <= hi_sup , x <= k1 => x <= hi_sup mk_clause(~l1, l2, 3, coeffs); } } } template void theory_arith::flush_bound_axioms() { while (!m_new_atoms.empty()) { ptr_vector atoms; atoms.push_back(m_new_atoms.back()); m_new_atoms.pop_back(); theory_var v = atoms.back()->get_var(); for (unsigned i = 0; i < m_new_atoms.size(); ++i) { if (m_new_atoms[i]->get_var() == v) { atoms.push_back(m_new_atoms[i]); m_new_atoms[i] = m_new_atoms.back(); m_new_atoms.pop_back(); --i; } } ptr_vector occs(m_var_occs[v]); std::sort(atoms.begin(), atoms.end(), compare_atoms()); std::sort(occs.begin(), occs.end(), compare_atoms()); typename atoms::iterator begin1 = occs.begin(); typename atoms::iterator begin2 = occs.begin(); typename atoms::iterator end = occs.end(); begin1 = first(A_LOWER, begin1, end); begin2 = first(A_UPPER, begin2, end); typename atoms::iterator lo_inf = begin1, lo_sup = begin1; typename atoms::iterator hi_inf = begin2, hi_sup = begin2; typename atoms::iterator lo_inf1 = begin1, lo_sup1 = begin1; typename atoms::iterator hi_inf1 = begin2, hi_sup1 = begin2; bool flo_inf, fhi_inf, flo_sup, fhi_sup; ptr_addr_hashtable visited; for (unsigned i = 0; i < atoms.size(); ++i) { atom* a1 = atoms[i]; lo_inf1 = next_inf(a1, A_LOWER, lo_inf, end, flo_inf); hi_inf1 = next_inf(a1, A_UPPER, hi_inf, end, fhi_inf); lo_sup1 = next_sup(a1, A_LOWER, lo_sup, end, flo_sup); hi_sup1 = next_sup(a1, A_UPPER, hi_sup, end, fhi_sup); if (lo_inf1 != end) lo_inf = lo_inf1; if (lo_sup1 != end) lo_sup = lo_sup1; if (hi_inf1 != end) hi_inf = hi_inf1; if (hi_sup1 != end) hi_sup = hi_sup1; if (!flo_inf) lo_inf = end; if (!fhi_inf) hi_inf = end; if (!flo_sup) lo_sup = end; if (!fhi_sup) hi_sup = end; visited.insert(a1); if (lo_inf1 != end && lo_inf != end && !visited.contains(*lo_inf)) mk_bound_axiom(a1, *lo_inf); if (lo_sup1 != end && lo_sup != end && !visited.contains(*lo_sup)) mk_bound_axiom(a1, *lo_sup); if (hi_inf1 != end && hi_inf != end && !visited.contains(*hi_inf)) mk_bound_axiom(a1, *hi_inf); if (hi_sup1 != end && hi_sup != end && !visited.contains(*hi_sup)) mk_bound_axiom(a1, *hi_sup); } } } template typename theory_arith::atoms::iterator theory_arith::first( atom_kind kind, typename atoms::iterator it, typename atoms::iterator end) { for (; it != end; ++it) { atom* a = *it; if (a->get_atom_kind() == kind) return it; } return end; } template typename theory_arith::atoms::iterator theory_arith::next_inf( atom* a1, atom_kind kind, typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible) { inf_numeral const & k1(a1->get_k()); typename atoms::iterator result = end; found_compatible = false; for (; it != end; ++it) { atom * a2 = *it; if (a1 == a2) continue; if (a2->get_atom_kind() != kind) continue; inf_numeral const & k2(a2->get_k()); found_compatible = true; if (k2 <= k1) { result = it; } else { break; } } return result; } template typename theory_arith::atoms::iterator theory_arith::next_sup( atom* a1, atom_kind kind, typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible) { inf_numeral const & k1(a1->get_k()); found_compatible = false; for (; it != end; ++it) { atom * a2 = *it; if (a1 == a2) continue; if (a2->get_atom_kind() != kind) continue; inf_numeral const & k2(a2->get_k()); found_compatible = true; if (k1 < k2) { return it; } } return end; } template bool theory_arith::internalize_atom(app * n, bool gate_ctx) { TRACE("arith_internalize", tout << "internalising atom:\n" << mk_pp(n, this->get_manager()) << "\n";); context & ctx = get_context(); SASSERT(m_util.is_le(n) || m_util.is_ge(n) || m_util.is_is_int(n)); SASSERT(!ctx.b_internalized(n)); atom_kind kind; if (m_util.is_is_int(n)) { internalize_is_int(n); if (ctx.b_internalized(n)) { TRACE("arith_internalize", tout << "term was re-internalized: #" << n->get_id() << "\n";); return true; } bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); return true; } if (m_util.is_le(n)) kind = A_UPPER; else kind = A_LOWER; app * lhs = to_app(n->get_arg(0)); app * rhs = to_app(n->get_arg(1)); SASSERT(m_util.is_numeral(rhs)); theory_var v = internalize_term_core(lhs); if (v == null_theory_var) { TRACE("arith_internalize", tout << "failed to internalize: #" << n->get_id() << "\n";); return false; } if (ctx.b_internalized(n)) { TRACE("arith_internalize", tout << "term was re-internalized: #" << n->get_id() << "\n";); return true; } bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); rational _k; VERIFY(m_util.is_numeral(rhs, _k)); inf_numeral k(_k); atom * a = alloc(atom, bv, v, k, kind); mk_bound_axioms(a); m_unassigned_atoms[v]++; atoms & occs = m_var_occs[v]; occs.push_back(a); m_atoms.push_back(a); insert_bv2a(bv, a); TRACE("arith_internalize", tout << "succeeded... v" << v << " " << kind << " " << k << "\n"; for (unsigned i = 0; i + 1 < occs.size(); ++i) tout << occs[i] << "\n";); return true; } template bool theory_arith::internalize_term(app * term) { TRACE("arith_internalize", tout << "internalising term:\n" << mk_pp(term, this->get_manager()) << "\n";); theory_var v = internalize_term_core(term); TRACE("arith_internalize", tout << "theory_var: " << v << "\n";); return v != null_theory_var; } template void theory_arith::internalize_eq_eh(app * atom, bool_var v) { if (m_params.m_arith_eager_eq_axioms) { context & ctx = get_context(); app * lhs = to_app(atom->get_arg(0)); app * rhs = to_app(atom->get_arg(1)); enode * n1 = ctx.get_enode(lhs); enode * n2 = ctx.get_enode(rhs); // The expression atom may be a theory axiom. In this case, it may not be in simplified form. // So, an atom such as (= a a) may occur. The procedure mk_axioms, expects n1 != n2. // So, we should check it. It doesn't make sense to create an axiom for (= a a) in the arith_eq_adapter. if (n1->get_th_var(get_id()) != null_theory_var && n2->get_th_var(get_id()) != null_theory_var && n1 != n2) { TRACE("mk_axioms_bug", tout << mk_bounded_pp(atom, get_manager(), 5) << "\n";); m_arith_eq_adapter.mk_axioms(n1, n2); } } } template void theory_arith::apply_sort_cnstr(enode * n, sort * s) { // do nothing... } template void theory_arith::assign_eh(bool_var v, bool is_true) { TRACE("arith", tout << "v" << v << " " << is_true << "\n";); atom * a = get_bv2a(v); if (!a) return; SASSERT(get_context().get_assignment(a->get_bool_var()) != l_undef); SASSERT((get_context().get_assignment(a->get_bool_var()) == l_true) == is_true); a->assign_eh(is_true, get_epsilon(a->get_var())); m_asserted_bounds.push_back(a); } template void theory_arith::relevant_eh(app * n) { TRACE("arith_relevant_eh", tout << "relevant_eh: " << mk_pp(n, get_manager()) << "\n";); if (m_util.is_mod(n)) mk_idiv_mod_axioms(n->get_arg(0), n->get_arg(1)); else if (m_util.is_rem(n)) mk_rem_axiom(n->get_arg(0), n->get_arg(1)); else if (m_util.is_div(n)) mk_div_axiom(n->get_arg(0), n->get_arg(1)); else if (m_util.is_to_int(n)) mk_to_int_axiom(n); else if (m_util.is_is_int(n)) mk_is_int_axiom(n); } template void theory_arith::new_eq_eh(theory_var v1, theory_var v2) { TRACE("arith_new_eq_eh", tout << "#" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";); TRACE("arith_new_eq_eh_detail", tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); enode * n1 = get_enode(v1); if (!m_util.is_int(n1->get_owner()) && !m_util.is_real(n1->get_owner())) { return; } if (m_params.m_arith_eq_bounds) { enode * n2 = get_enode(v2); SASSERT(n1->get_root() == n2->get_root()); if (m_util.is_numeral(n1->get_owner())) { std::swap(v1, v2); std::swap(n1, n2); } rational k; bound * b1 = 0; bound * b2 = 0; if (m_util.is_numeral(n2->get_owner(), k)) { inf_numeral val(k); b1 = alloc(eq_bound, v1, val, B_LOWER, n1, n2); b2 = alloc(eq_bound, v1, val, B_UPPER, n1, n2); } else { if (n1->get_owner_id() > n2->get_owner_id()) std::swap(n1, n2); sort * st = get_manager().get_sort(n1->get_owner()); app * minus_one = m_util.mk_numeral(rational::minus_one(), st); app * s = m_util.mk_add(n1->get_owner(), m_util.mk_mul(minus_one, n2->get_owner())); context & ctx = get_context(); ctx.internalize(s, false); enode * e_s = ctx.get_enode(s); ctx.mark_as_relevant(e_s); SASSERT(is_attached_to_var(e_s)); theory_var v_s = e_s->get_th_var(get_id()); b1 = alloc(eq_bound, v_s, inf_numeral::zero(), B_LOWER, n1, n2); b2 = alloc(eq_bound, v_s, inf_numeral::zero(), B_UPPER, n1, n2); } m_bounds_to_delete.push_back(b1); m_bounds_to_delete.push_back(b2); m_asserted_bounds.push_back(b1); m_asserted_bounds.push_back(b2); } else { m_arith_eq_adapter.new_eq_eh(v1, v2); } } template bool theory_arith::use_diseqs() const { return true; } template void theory_arith::new_diseq_eh(theory_var v1, theory_var v2) { TRACE("arith_new_diseq_eh", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); m_stats.m_assert_diseq++; m_arith_eq_adapter.new_diseq_eh(v1, v2); } template void theory_arith::restart_eh() { m_arith_eq_adapter.restart_eh(); } template void theory_arith::init_search_eh() { TRACE("arith_init_search", display(tout);); m_num_conflicts = 0; m_branch_cut_counter = 0; m_eager_gcd = m_params.m_arith_eager_gcd; if (lazy_pivoting_lvl() == 1) elim_quasi_base_rows(); move_unconstrained_to_base(); m_arith_eq_adapter.init_search_eh(); m_final_check_idx = 0; m_nl_gb_exhausted = false; m_nl_strategy_idx = 0; } template final_check_status theory_arith::final_check_core() { unsigned old_idx = m_final_check_idx; final_check_status result = FC_DONE; final_check_status ok; do { TRACE("final_check_arith", tout << "m_final_check_idx: " << m_final_check_idx << ", result: " << result << "\n";); switch (m_final_check_idx) { case 0: ok = check_int_feasibility(); TRACE("final_check_arith", tout << "check_int_feasibility(), ok: " << ok << "\n";); break; case 1: if (assume_eqs_core()) ok = FC_CONTINUE; else ok = FC_DONE; TRACE("final_check_arith", tout << "assume_eqs(), ok: " << ok << "\n";); break; default: ok = process_non_linear(); TRACE("final_check_arith", tout << "non_linear(), ok: " << ok << "\n";); break; } m_final_check_idx = (m_final_check_idx + 1) % 3; switch (ok) { case FC_DONE: break; case FC_GIVEUP: result = FC_GIVEUP; break; case FC_CONTINUE: TRACE("final_check_arith", tout << "continue arith..." << (get_context().inconsistent()?"inconsistent\n":"\n");); return FC_CONTINUE; } } while (m_final_check_idx != old_idx); if (result == FC_DONE && m_found_unsupported_op) { TRACE("arith", tout << "Found unsupported operation\n";); result = FC_GIVEUP; } return result; } template final_check_status theory_arith::final_check_eh() { TRACE("arith_eq_adapter_info", m_arith_eq_adapter.display_already_processed(tout);); TRACE("arith_final_check", display(tout);); #if 0 if (true /*m_params.m_smtlib_dump_lemmas*/) { literal_buffer tmp; for (unsigned i = 0; i < m_asserted_qhead; i++) { bound * b = m_asserted_bounds[i]; if (b->is_atom()) { atom* a = static_cast(b); bool_var bv = a->get_bool_var(); lbool is_true = get_context().get_assignment(bv); if (is_true == l_true) { tmp.push_back(literal(bv)); } else if (is_true == l_false) { tmp.push_back(~literal(bv)); } } } get_context().display_lemma_as_smt_problem(tmp.size(), tmp.c_ptr(), false_literal, "QF_LIA"); } #endif if (!propagate_core()) return FC_CONTINUE; if (delayed_assume_eqs()) return FC_CONTINUE; get_context().push_trail(value_trail(m_final_check_idx)); m_liberal_final_check = true; m_changed_assignment = false; final_check_status result = final_check_core(); if (result != FC_DONE) return result; if (!m_changed_assignment) return FC_DONE; m_liberal_final_check = false; m_changed_assignment = false; result = final_check_core(); TRACE("final_check_arith", tout << "result: " << result << "\n";); return result; } template bool theory_arith::can_propagate() { return process_atoms() && m_asserted_qhead < m_asserted_bounds.size(); } template void theory_arith::propagate() { TRACE("arith_propagate", tout << "propagate\n"; display(tout);); if (!process_atoms()) return; propagate_core(); } template bool theory_arith::propagate_core() { CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); flush_bound_axioms(); propagate_linear_monomials(); while (m_asserted_qhead < m_asserted_bounds.size()) { bound * b = m_asserted_bounds[m_asserted_qhead]; m_asserted_qhead++; if (!assert_bound(b)) { failed(); return false; } } if (!make_feasible()) { failed(); return false; } if (get_context().get_cancel_flag()) { return true; } CASSERT("arith", satisfy_bounds()); discard_update_trail(); SASSERT(m_update_trail_stack.empty()); propagate_bounds(); SASSERT(m_asserted_qhead == m_asserted_bounds.size()); SASSERT(m_update_trail_stack.empty()); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); CASSERT("arith", satisfy_bounds()); return true; } template void theory_arith::failed() { restore_assignment(); m_to_patch.reset(); m_to_check.reset(); m_in_to_check.reset(); } template void theory_arith::flush_eh() { std::for_each(m_atoms.begin(), m_atoms.end(), delete_proc()); m_atoms.reset(); std::for_each(m_bounds_to_delete.begin(), m_bounds_to_delete.end(), delete_proc()); m_bounds_to_delete.reset(); } template void theory_arith::reset_eh() { m_stats.reset(); m_rows .reset(); m_arith_eq_adapter .reset_eh(); m_dead_rows .reset(); m_columns .reset(); m_data .reset(); m_value .reset(); m_old_value .reset(); m_bounds[0] .reset(); m_bounds[1] .reset(); m_var_occs .reset(); m_unassigned_atoms .reset(); m_bool_var2atom .reset(); m_var_pos .reset(); std::for_each(m_atoms.begin(), m_atoms.end(), delete_proc()); m_atoms .reset(); std::for_each(m_bounds_to_delete.begin(), m_bounds_to_delete.end(), delete_proc()); m_bounds_to_delete.reset(); m_asserted_bounds .reset(); m_asserted_qhead = 0; m_to_patch .reset(); m_left_basis .reset(); m_blands_rule = false; m_update_trail_stack .reset(); m_in_update_trail_stack .reset(); m_to_check .reset(); m_in_to_check .reset(); m_num_conflicts = 0; m_bound_trail .reset(); m_unassigned_atoms_trail .reset(); m_scopes .reset(); m_nl_monomials .reset(); m_nl_propagated .reset(); m_nl_rounds = 0; m_nl_gb_exhausted = false; m_nl_strategy_idx = 0; theory::reset_eh(); } template bool theory_arith::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { return true; } /** \brief Compute the value of a base or quasi-base variable using the value of the dependent variables. */ template typename theory_arith::inf_numeral const & theory_arith::get_implied_value(theory_var v) const { SASSERT(is_quasi_base(v) || is_base(v)); inf_numeral & sum = const_cast *>(this)->m_tmp; sum.reset(); unsigned r_id = get_var_row(v); row const & r = m_rows[r_id]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != v) { SASSERT(!is_quasi_base(it->m_var)); SASSERT(get_value(it->m_var) == m_value[it->m_var]); sum += it->m_coeff * get_value(it->m_var); } } sum.neg(); return sum; } /** \brief Compute the value of a base or quasi-base variable using the old value of the dependent variables. By old, we mean the value of the variable in the beginning of propagate(). Store the result in 'result'. Return true if the old value is different from the current value. */ template bool theory_arith::get_implied_old_value(theory_var v, inf_numeral & result) const { SASSERT(is_quasi_base(v) || is_base(v)); bool is_diff = false; result.reset(); unsigned r_id = get_var_row(v); row const & r = m_rows[r_id]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != v) { theory_var v2 = it->m_var; SASSERT(!is_quasi_base(v2)); SASSERT(get_value(v2) == m_value[v2]); if (m_in_update_trail_stack.contains(v2)) { result += it->m_coeff * m_old_value[v2]; is_diff = true; } else { result += it->m_coeff * m_value[v2]; } } } result.neg(); return is_diff; } template theory_arith::theory_arith(ast_manager & m, theory_arith_params & params): theory(m.mk_family_id("arith")), m_params(params), m_util(m), m_arith_eq_solver(m), m_found_unsupported_op(false), m_arith_eq_adapter(*this, params, m_util), m_asserted_qhead(0), m_to_patch(1024), m_blands_rule(false), m_random(params.m_arith_random_seed), m_num_conflicts(0), m_branch_cut_counter(0), m_eager_gcd(m_params.m_arith_eager_gcd), m_final_check_idx(0), m_var_value_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), m_liberal_final_check(true), m_changed_assignment(false), m_assume_eq_head(0), m_nl_rounds(0), m_nl_gb_exhausted(false), m_nl_new_exprs(m), m_bound_watch(null_bool_var) { } template theory_arith::~theory_arith() { } template void theory_arith::setup() { m_random.set_seed(m_params.m_arith_random_seed); theory::setup(); } // ----------------------------------- // // Add Row // // ----------------------------------- /** \brief Set: row1 <- row1 + coeff * row2 */ template void theory_arith::add_row(unsigned rid1, const numeral & coeff, unsigned rid2, bool apply_gcd_test) { m_stats.m_add_rows++; if (propagation_mode() != BP_NONE) mark_row_for_bound_prop(rid1); row & r1 = m_rows[rid1]; row & r2 = m_rows[rid2]; CASSERT("row_assignment_bug", valid_row_assignment(r1)); CASSERT("row_assignment_bug", valid_row_assignment(r2)); r1.compress_if_needed(m_columns); r2.compress_if_needed(m_columns); CASSERT("arith", check_null_var_pos()); r1.save_var_pos(m_var_pos); // // loop over variables in row2, // add terms in row2 to row1. // #define ADD_ROW(_SET_COEFF_, _ADD_COEFF_) \ typename vector::const_iterator it = r2.begin_entries(); \ typename vector::const_iterator end = r2.end_entries(); \ for (; it != end; ++it) { \ if (!it->is_dead()) { \ theory_var v = it->m_var; \ int pos = m_var_pos[v]; \ if (pos == -1) { \ /* variable v is not in row1 */ \ int row_idx; \ row_entry & r_entry = r1.add_row_entry(row_idx); \ r_entry.m_var = v; \ _SET_COEFF_; \ column & c = m_columns[v]; \ int col_idx; \ col_entry & c_entry = c.add_col_entry(col_idx); \ r_entry.m_col_idx = col_idx; \ c_entry.m_row_id = rid1; \ c_entry.m_row_idx = row_idx; \ } \ else { \ /* variable v is in row1 */ \ row_entry & r_entry = r1[pos]; \ SASSERT(r_entry.m_var == v); \ _ADD_COEFF_; \ if (r_entry.m_coeff.is_zero()) { \ int col_idx = r_entry.m_col_idx; \ r1.del_row_entry(pos); \ column & c = m_columns[v]; \ c.del_col_entry(col_idx); \ } \ m_var_pos[v] = -1; \ } \ } \ } ((void) 0) if (coeff.is_one()) { ADD_ROW(r_entry.m_coeff = it->m_coeff, r_entry.m_coeff += it->m_coeff); } else if (coeff.is_minus_one()) { ADD_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff.neg(), r_entry.m_coeff -= it->m_coeff); } else { ADD_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff *= coeff, r_entry.m_coeff += it->m_coeff * coeff); } r1.reset_var_pos(m_var_pos); CASSERT("arith", check_null_var_pos()); CASSERT("row_assignment_bug", valid_row_assignment(r1)); CASSERT("row_assignment_bug", valid_row_assignment(r2)); if (apply_gcd_test) { theory_var v = r1.get_base_var(); if (is_int(v) && !get_value(v).is_int()) gcd_test(r1); } } /** \brief Set r1 <- r1 + a_xs[0].m_coeff * get_var_row(a_xs[0].m_var) + ... + a_xs[0].m_coeff * get_var_row(a_xs[sz-1].m_var) \pre For all i in [0..sz-1]. not is_non_base(a_xs[i]) */ template void theory_arith::add_rows(unsigned r1, unsigned sz, linear_monomial * a_xs) { if (sz == 0) return; for (unsigned i = 0; i < sz; i++) { linear_monomial & m = a_xs[i]; numeral c = m.m_coeff; theory_var v = m.m_var; SASSERT(!is_non_base(v)); add_row(r1, c, get_var_row(v), false); } } // ----------------------------------- // // Assignment management // // ----------------------------------- template void theory_arith::save_value(theory_var v) { SASSERT(!is_quasi_base(v)); if (!m_in_update_trail_stack.contains(v)) { m_in_update_trail_stack.insert(v); SASSERT(m_value[v] == get_value(v)); m_old_value[v] = m_value[v]; m_update_trail_stack.push_back(v); TRACE("save_value", tout << "v" << v << " = " << get_value(v) << "\n";); } m_changed_assignment = true; } template void theory_arith::discard_update_trail() { m_in_update_trail_stack.reset(); m_update_trail_stack.reset(); } template void theory_arith::restore_assignment() { CASSERT("arith", valid_row_assignment()); TRACE("restore_assignment_bug", tout << "START restore_assignment...\n";); typename svector::iterator it = m_update_trail_stack.begin(); typename svector::iterator end = m_update_trail_stack.end(); for(; it != end; ++it) { theory_var v = *it; TRACE("restore_assignment_bug", tout << "restoring v" << v << " <- " << m_old_value[v] << "\n";); SASSERT(!is_quasi_base(v)); SASSERT(m_in_update_trail_stack.contains(v)); m_value[v] = m_old_value[v]; } m_update_trail_stack.reset(); m_in_update_trail_stack.reset(); CASSERT("arith", valid_row_assignment()); } /** \brief m_value[v] += delta */ template void theory_arith::update_value_core(theory_var v, inf_numeral const & delta) { save_value(v); m_value[v] += delta; if (is_base(v) && !m_to_patch.contains(v) && (below_lower(v) || above_upper(v))) { m_to_patch.insert(v); } } /** \brief m_value[v] += delta, and update dependent (non-base) variables. */ template void theory_arith::update_value(theory_var v, inf_numeral const & delta) { update_value_core(v, delta); column & c = m_columns[v]; c.compress_if_needed(m_rows); inf_numeral delta2; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { row & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); if (s != null_theory_var && !is_quasi_base(s)) { delta2 = delta; delta2 *= r[it->m_row_idx].m_coeff; delta2.neg(); update_value_core(s, delta2); } } } } // ----------------------------------- // // Pivoting // // ----------------------------------- /** \brief Make x_j the new base variable for row of x_i x_j is assumed to have coefficient a_ij. Let row_id = m_base_var[x_i] rows[row_id] <- rows[row_id] / a_ij rows[other] <- row[other] - rows[row_id] * a_kj Where a_kj is the coefficient of x_j in row[other] */ template template void theory_arith::pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, bool apply_gcd_test) { TRACE("arith_pivot", tout << "pivoting: v" << x_i << ", v" << x_j << "\n";); m_stats.m_pivots++; SASSERT(is_base(x_i) || is_quasi_base(x_i)); SASSERT(x_i != x_j); int r_id = get_var_row(x_i); row & r = m_rows[r_id]; SASSERT(r.is_coeff_of(x_j, a_ij)); #define DIVIDE_ROW(_ADJUST_COEFF_) \ typename vector::iterator it = r.begin_entries(); \ typename vector::iterator end = r.end_entries(); \ for (; it != end; ++it) { \ if (!it->is_dead()) { \ _ADJUST_COEFF_; \ } \ } ((void) 0) if (a_ij.is_minus_one()) { DIVIDE_ROW(it->m_coeff.neg()); } else if (!a_ij.is_one()) { numeral tmp = a_ij; DIVIDE_ROW(it->m_coeff /= tmp); } set_var_row(x_i, -1); set_var_row(x_j, r_id); SASSERT(r.m_base_var == x_i); r.m_base_var = x_j; set_var_kind(x_i, NON_BASE); set_var_kind(x_j, BASE); eliminate(x_j, apply_gcd_test); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); TRACE("arith_pivot", tout << "after pivoting:\n"; display(tout);); TRACE("pivot_shape", display_rows_shape(tout);); TRACE("pivot_stats", display_rows_stats(tout);); TRACE_CODE({ static unsigned val = 0; val ++; if (val % 100 == 0) { TRACE("pivot_bignums", display_rows_bignums(tout);); }}); } /** \brief Eliminate x_i from the rows different from get_var_row(x_i) If Lazy = true, then x_i is only eliminated from base rows. */ template template void theory_arith::eliminate(theory_var x_i, bool apply_gcd_test) { SASSERT(is_base(x_i) || is_quasi_base(x_i)); unsigned r_id = get_var_row(x_i); column & c = m_columns[x_i]; numeral a_kj; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); int i = 0; int s_pos = -1; for (; it != end; ++it, ++i) { if (!it->is_dead()) { if (it->m_row_id != static_cast(r_id)) { row & r2 = m_rows[it->m_row_id]; theory_var s2 = r2.m_base_var; if (s2 != null_theory_var && (!Lazy || is_base(s2))) { a_kj = r2[it->m_row_idx].m_coeff; a_kj.neg(); add_row(it->m_row_id, a_kj, r_id, apply_gcd_test); } } else { s_pos = i; } } } CTRACE("eliminate", !Lazy && c.size() != 1, tout << "eliminating v" << x_i << ", Lazy: " << Lazy << ", c.size: " << c.size() << "\n"; display(tout);); SASSERT(Lazy || c.size() == 1); if (c.size() == 1) { // When lazy pivoting is used, then after pivoting c may // not be a singleton c.compress_singleton(m_rows, s_pos); } } /** \brief Set x_j to be the new base variable of row owned by x_i a_ij - coefficient of x_j in the row owned by x_i x_i_new_val - new value of x_i */ template void theory_arith::update_and_pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, inf_numeral const & x_i_new_val) { CASSERT("arith", valid_row_assignment()); TRACE("update_and_pivot_bug_detail", display(tout);); SASSERT(is_base(x_i)); inf_numeral theta = m_value[x_i]; TRACE("update_and_pivot_bug", tout << "theta 1) " << theta << " " << x_i_new_val << "\n";); theta -= x_i_new_val; TRACE("update_and_pivot_bug", tout << "theta 2) " << theta << " " << a_ij << "\n";); theta /= a_ij; TRACE("update_and_pivot_bug", tout << "theta 3) " << theta << "\n";); update_value(x_j, theta); CTRACE("arith", get_value(x_i) != x_i_new_val, tout << "x_i: " << x_i << ", x_j: " << x_j << ", a_ij: " << a_ij << ", x_i_new_val: " << x_i_new_val << "\n"; tout << "new val: " << get_value(x_i) << ", theta: " << theta << "\n"; display(tout);); SASSERT(get_value(x_i) == x_i_new_val); if (!m_to_patch.contains(x_j) && (below_lower(x_j) || above_upper(x_j))) m_to_patch.insert(x_j); pivot(x_i, x_j, a_ij, m_eager_gcd); CASSERT("arith", valid_row_assignment()); } /** \brief Return the number of base variables that are non free and are v dependent. The function adds 1 to the result if v is non free. The function returns with a partial result r if r > best_so_far. This function is used to select the pivot variable. */ template int theory_arith::get_num_non_free_dep_vars(theory_var v, int best_so_far) { int result = is_non_free(v); column & c = m_columns[v]; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { row & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); if (s != null_theory_var && is_base(s)) { result += is_non_free(s); if (result > best_so_far) return result; } } } return result; } /** \brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. */ template theory_var theory_arith::select_blands_pivot_core(theory_var x_i, bool is_below, numeral & out_a_ij) { SASSERT(is_base(x_i)); theory_var max = get_num_vars(); theory_var result = max; row const & r = m_rows[get_var_row(x_i)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var x_j = it->m_var; numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? a_ij.is_neg() : a_ij.is_pos(); bool is_pos = !is_neg; if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { SASSERT(is_non_base(x_j)); if (x_j < result) { result = x_j; out_a_ij = a_ij; } } } } return result < max ? result : null_theory_var; } /** \brief Select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. The argument is_below is true (false) if x_i is below its lower bound (above its upper bound). */ template template theory_var theory_arith::select_pivot_core(theory_var x_i, numeral & out_a_ij) { SASSERT(is_base(x_i)); theory_var max = get_num_vars(); theory_var result = max; row const & r = m_rows[get_var_row(x_i)]; int n; int best_col_sz = INT_MAX; int best_so_far = INT_MAX; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var x_j = it->m_var; numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? a_ij.is_neg() : a_ij.is_pos(); bool is_pos = !is_neg; if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { int num = get_num_non_free_dep_vars(x_j, best_so_far); int col_sz = m_columns[x_j].size(); if (num < best_so_far || (num == best_so_far && col_sz < best_col_sz)) { result = x_j; out_a_ij = a_ij; best_so_far = num; best_col_sz = col_sz; n = 1; } else if (num == best_so_far && col_sz == best_col_sz) { n++; if (m_random()%n == 0) { result = x_j; out_a_ij = a_ij; } } } } } return result < max ? result : null_theory_var; } /** \brief Wrapper for select_blands_pivot_core and select_pivot_core */ template theory_var theory_arith::select_pivot(theory_var x_i, bool is_below, numeral & out_a_ij) { TRACE("select_pivot", tout << "m_blands_rule: " << m_blands_rule << " v" << x_i << "\n";); CTRACE("select_pivot_info", x_i > 500, get_context().display(tout);); if (m_blands_rule) return select_blands_pivot_core(x_i, is_below, out_a_ij); else if (is_below) return select_pivot_core(x_i, out_a_ij); else return select_pivot_core(x_i, out_a_ij); } // ----------------------------------- // // Make feasible // // ----------------------------------- /** \brief Make the given variable feasible. This method assumes that x_i is a base var. Return false if it was not possible to make x_i feasible. */ template bool theory_arith::make_var_feasible(theory_var x_i) { CTRACE("arith_bug", !is_base(x_i), tout << "x_i: " << x_i << ", below_lower(x_i): " << below_lower(x_i) << ", above_upper(x_i): " << above_upper(x_i) << "\n"; display(tout);); SASSERT(is_base(x_i)); bool is_below; if (below_lower(x_i)) { is_below = true; } else if (above_upper(x_i)) { is_below = false; } else { // x_i is already feasible return true; } TRACE("make_var_feasible", display_row(tout, get_var_row(x_i), false);); numeral a_ij; theory_var x_j = select_pivot(x_i, is_below, a_ij); if (x_j != null_theory_var) { SASSERT(is_base(x_i)); TRACE("pivot_bug", display_row_info(tout, get_var_row(x_i));); update_and_pivot(x_i, x_j, a_ij, get_bound(x_i, !is_below)->get_value()); return true; } else { // conflict detected sign_row_conflict(x_i, is_below); return false; } } template theory_var theory_arith::select_lg_error_var(bool least) { TRACE("select_pivot", tout << "starting...\n";); theory_var best = null_theory_var; inf_numeral best_error; inf_numeral curr_error; typename var_heap::iterator it = m_to_patch.begin(); typename var_heap::iterator end = m_to_patch.end(); for (; it != end; ++it) { theory_var v = *it; if (below_lower(v)) curr_error = lower(v)->get_value() - get_value(v); else if (above_upper(v)) curr_error = get_value(v) - upper(v)->get_value(); else continue; SASSERT(curr_error > inf_numeral(0)); if (best == null_theory_var || (!least && curr_error > best_error) || (least && curr_error < best_error)) { TRACE("select_pivot", tout << "best: " << best << " v" << v << ", best_error: " << best_error << ", curr_error: " << curr_error << "\n";); best = v; best_error = curr_error; } } if (best == null_theory_var) m_to_patch.clear(); // all variables are satisfied else m_to_patch.erase(best); return best; } template theory_var theory_arith::select_smallest_var() { return m_to_patch.erase_min(); } template theory_var theory_arith::select_var_to_fix() { if (m_blands_rule) return select_smallest_var(); switch (m_params.m_arith_pivot_strategy) { case ARITH_PIVOT_GREATEST_ERROR: return select_greatest_error_var(); case ARITH_PIVOT_LEAST_ERROR: return select_least_error_var(); default: return select_smallest_var(); } } /** \brief Return true if it was possible to patch all variables in m_to_patch. */ template bool theory_arith::make_feasible() { TRACE("arith_make_feasible", tout << "make_feasible\n"; display(tout);); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); m_left_basis.reset(); m_blands_rule = false; unsigned num_repeated = 0; while (!m_to_patch.empty()) { theory_var v = select_var_to_fix(); if (v == null_theory_var) { // all variables were satisfied... SASSERT(m_to_patch.empty()); break; } if (!m_blands_rule) { if (m_left_basis.contains(v)) { num_repeated++; if (num_repeated > blands_rule_threshold()) { TRACE("blands_rule", tout << "using blands rule, " << num_repeated << "\n";); // std::cerr << "BLANDS RULE...\n"; m_blands_rule = true; } } else { m_left_basis.insert(v); } } if (!make_var_feasible(v)) { TRACE("arith_make_feasible", tout << "make_feasible: unsat\n"; display(tout);); return false; } TRACE("arith_make_feasible_detail", display(tout);); if (get_context().get_cancel_flag()) { return true; } } TRACE("arith_make_feasible", tout << "make_feasible: sat\n"; display(tout);); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); CASSERT("arith", satisfy_bounds()); return true; } /** \brief A row is in a sign inconsistency when it is implying a lower (upper) bound on x_i, which is above (below) its known upper (lower) bound. */ template void theory_arith::sign_row_conflict(theory_var x_i, bool is_below) { inf_numeral delta; row const & r = m_rows[get_var_row(x_i)]; int idx = r.get_idx_of(x_i); SASSERT(idx >= 0); bound * b = 0; // Remark: // if x_i is an integer variable, then delta can be negative: // // Example: x_i <= 0 get_value(x_i) = 1/4 // // The value is above the upper bound. // Since x_i is an integer, get_epsilon(x_i) = 1, and delta = -3/4 if (is_below) { SASSERT(below_lower(x_i)); b = lower(x_i); if (relax_bounds()) { delta = b->get_value(); delta -= get_value(x_i); delta -= get_epsilon(x_i); if (delta.is_neg()) delta.reset(); } } else { SASSERT(above_upper(x_i)); b = upper(x_i); if (relax_bounds()) { delta = get_value(x_i); delta -= b->get_value(); delta -= get_epsilon(x_i); if (delta.is_neg()) delta.reset(); } } TRACE("sign_row_conflict", tout << "v" << x_i << " is_below: " << is_below << " delta: " << delta << "\n"; display_var(tout, x_i); tout << "is_below_lower: " << below_lower(x_i) << ", is_above_upper: " << above_upper(x_i) << "\n";); antecedents& ante = get_antecedents(); explain_bound(r, idx, !is_below, delta, ante); b->push_justification(ante, numeral(1), coeffs_enabled()); set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante, is_int(x_i), "farkas"); // display_bounds_in_smtlib(); } // ----------------------------------- // // Assert bound // // ----------------------------------- /** \brief Assert x >= k, return false if a conflict is detected. */ template bool theory_arith::assert_lower(bound * b) { SASSERT(b->get_bound_kind() == B_LOWER); theory_var v = b->get_var(); inf_numeral const & k = b->get_value(); bound * u = upper(v); bound * l = lower(v); if (u && k > u->get_value()) { sign_bound_conflict(u, b); return false; } if (l && k <= l->get_value()) { // redundant return true; } switch (get_var_kind(v)) { case QUASI_BASE: quasi_base_row2base_row(get_var_row(v)); SASSERT(get_var_kind(v) == BASE); case BASE: if (!m_to_patch.contains(v) && get_value(v) < k) { TRACE("to_patch_bug", tout << "need to be patched (assert_lower): "; display_var(tout, v);); m_to_patch.insert(v); } break; case NON_BASE: if (get_value(v) < k) set_value(v, k); break; } push_bound_trail(v, l, false); set_bound(b, false); if (propagation_mode() != BP_NONE) mark_rows_for_bound_prop(v); return true; } /** \brief Assert x <= k, return false if a conflict is detected. */ template bool theory_arith::assert_upper(bound * b) { SASSERT(b->get_bound_kind() == B_UPPER); theory_var v = b->get_var(); inf_numeral const & k = b->get_value(); bound * u = upper(v); bound * l = lower(v); if (l && k < l->get_value()) { sign_bound_conflict(l, b); return false; } if (u && k >= u->get_value()) { // redundant return true; } switch (get_var_kind(v)) { case QUASI_BASE: quasi_base_row2base_row(get_var_row(v)); SASSERT(get_var_kind(v) == BASE); case BASE: if (!m_to_patch.contains(v) && get_value(v) > k) { TRACE("to_patch_bug", tout << "need to be patched (assert upper): "; display_var(tout, v);); m_to_patch.insert(v); } break; case NON_BASE: if (get_value(v) > k) set_value(v, k); break; } push_bound_trail(v, u, true); set_bound(b, true); if (propagation_mode() != BP_NONE) mark_rows_for_bound_prop(v); return true; } template bool theory_arith::assert_bound(bound * b) { TRACE("assert_bound", display_bound(tout, b);); theory_var v = b->get_var(); if (b->is_atom()) { CTRACE("unassigned_atoms", m_unassigned_atoms[v] <= 0, display_var(tout, v);); SASSERT(m_unassigned_atoms[v] > 0); push_dec_unassigned_atoms_trail(v); m_unassigned_atoms[v]--; } bool result = true; switch (b->get_bound_kind()) { case B_LOWER: m_stats.m_assert_lower++; result = assert_lower(b); break; case B_UPPER: m_stats.m_assert_upper++; result = assert_upper(b); break; } TRACE("arith_assert", tout << "result: " << result << "\n"; display(tout);); return result; } /** \brief Sign a conflict because the bounds b1 and b2 are contradictory. */ template void theory_arith::sign_bound_conflict(bound * b1, bound * b2) { SASSERT(b1->get_var() == b2->get_var()); antecedents& ante = get_antecedents(); b1->push_justification(ante, numeral(1), coeffs_enabled()); b2->push_justification(ante, numeral(1), coeffs_enabled()); set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante, is_int(b1->get_var()), "farkas"); TRACE("arith_conflict", tout << "bound conflict v" << b1->get_var() << "\n"; tout << "bounds: " << b1 << " " << b2 << "\n";); } // ----------------------------------- // // Bound propagation // // ----------------------------------- /** \brief Mark the row r1 for bound propagation. */ template void theory_arith::mark_row_for_bound_prop(unsigned r1) { if (!m_in_to_check.contains(r1) && m_rows[r1].m_base_var != null_theory_var) { m_in_to_check.insert(r1); m_to_check.push_back(r1); } } /** \brief Mark all rows that contain v for bound propagation. */ template void theory_arith::mark_rows_for_bound_prop(theory_var v) { column const & c = m_columns[v]; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) mark_row_for_bound_prop(it->m_row_id); } } /** \brief Given a row: a_1 * x_1 + ... + a_n * x_n = 0 Claim: If forall i in [1..n]. (a_i > 0 => upper(x_i) != oo) and (a_i < 0 => lower(x_i) != -oo) Then row implies a lower bound for every monomial in the row. Proof: Without loss of generality, we consider the monomial a_1 * x_1 a_1 * x_1 = - a_2 * x_2 - ... - a_n * x_n = (Sum_{a_i < 0} -a_i * x_i) + (Sum_{a_j > 0} -a_j * x_j) >= (Sum_{a_i < 0} -a_i * lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j)) If one the condition fails for the monomial a_i * x_i, then the row can still be used to imply a lower bound for this monomial. -4*x + 2*y - 3*z = 0 y <= 1 z >= 1 -x = -2*y + 3*z >= -2 + 3 >= 1 -4*x >= 1 Remark: the lower bound is not for the variable, but for the monomial. Claim: If forall i in [1..n]. (a_i > 0 => lower(x_i) != oo) and (a_i < 0 => upper(x_i) != -oo) Then row implies a upper bound for every monomial in the row. Proof: similar to the previous claim. The result is stored in lower_idx and upper_idx - lower_idx >= 0 : row can imply a lower bound for the monomial at 'lower_idx' - lower_idx == -1 : row can imply a lower bound for every monomial in the row. - lower_idx == -2 : row cannot be used to imply a lower bound. - upper_idx >= 0 : row can imply a upper bound for the monomial at 'upper_idx' - upper_idx == -1 : row can imply a upper bound for every monomial in the row. - upper_idx == -2 : row cannot be used to imply a upper bound. */ template void theory_arith::is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const { lower_idx = -1; upper_idx = -1; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (int i = 0; it != end; ++it, ++i) { if (!it->is_dead()) { #define UPDATE_IDX(IDX) IDX = IDX == -1 ? i : -2 if (skip_big_coeffs() && it->m_coeff.is_big()) { TRACE("is_row_useful", tout << "skipping row that contains big number...\n"; display_row_info(tout, r);); lower_idx = -2; upper_idx = -2; return; } bool is_pos = it->m_coeff.is_pos(); if (lower(it->m_var) == 0) { if (is_pos) { UPDATE_IDX(upper_idx); } else { UPDATE_IDX(lower_idx); } } if (upper(it->m_var) == 0) { if (is_pos) { UPDATE_IDX(lower_idx); } else { UPDATE_IDX(upper_idx); } } if (lower_idx == -2 && upper_idx == -2) return; } } } /** \brief Imply a lower/upper bound for the monomial stored at position idx. Then this bound is used to produce a bound for the monomial variable. */ template void theory_arith::imply_bound_for_monomial(row const & r, int idx, bool is_lower) { row_entry const & entry = r[idx]; if (m_unassigned_atoms[entry.m_var] > 0) { inf_numeral implied_k; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (int idx2 = 0; it != end; ++it, ++idx2) { if (!it->is_dead() && idx != idx2) { bound * b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg()); SASSERT(b); // implied_k -= it->m_coeff * b->get_value(); implied_k.submul(it->m_coeff, b->get_value()); } } implied_k /= entry.m_coeff; if (entry.m_coeff.is_pos() == is_lower) { // implied_k is a lower bound for entry.m_var bound * curr = lower(entry.m_var); if (curr == 0 || implied_k > curr->get_value()) { TRACE("arith_imply_bound", tout << "implying lower bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); mk_implied_bound(r, idx, is_lower, entry.m_var, B_LOWER, implied_k); } } else { // implied_k is an upper bound for it->m_var bound * curr = upper(entry.m_var); if (curr == 0 || implied_k < curr->get_value()) { TRACE("arith_imply_bound", tout << "implying upper bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k); } } } } /** \brief Auxiliary method. See is_row_useful_for_bound_prop If is_lower = true (false), then imply a lower (upper) bound for all monomials in the row. The monomial bounds are used to compute bounds for the monomial variables. */ template void theory_arith::imply_bound_for_all_monomials(row const & r, bool is_lower) { // Traverse the row once and compute // bb = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j)) If is_lower = true // bb = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_j < 0} -a_j * upper(x_j)) If is_lower = false inf_numeral bb; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { inf_numeral const & b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg())->get_value(); // bb -= it->m_coeff * b; bb.submul(it->m_coeff, b); } } inf_numeral implied_k; it = r.begin_entries(); for (int idx = 0; it != end; ++it, ++idx) { if (!it->is_dead() && m_unassigned_atoms[it->m_var] > 0) { inf_numeral const & b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg())->get_value(); implied_k = bb; // implied_k += it->m_coeff * b; implied_k.addmul(it->m_coeff, b); // implied_k is a bound for the monomial in position it implied_k /= it->m_coeff; TRACE("arith_imply_bound", display_var(tout, it->m_var); tout << "implied bound: " << (it->m_coeff.is_pos() ? ">=" : "<=") << implied_k << "\n";); if (it->m_coeff.is_pos() == is_lower) { // implied_k is a lower bound for it->m_var bound * curr = lower(it->m_var); if (curr == 0 || implied_k > curr->get_value()) { // improved lower bound TRACE("arith_imply_bound", tout << "implying lower bound for v" << it->m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, it->m_var);); mk_implied_bound(r, idx, is_lower, it->m_var, B_LOWER, implied_k); } } else { // implied_k is an upper bound for it->m_var bound * curr = upper(it->m_var); if (curr == 0 || implied_k < curr->get_value()) { // improved upper bound TRACE("arith_imply_bound", tout << "implying upper bound for v" << it->m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, it->m_var);); mk_implied_bound(r, idx, is_lower, it->m_var, B_UPPER, implied_k); } } } } } /** \brief Create an explanation for the lower/upper bound of the variable at position idx. \remark delta is used for relaxing the explanation. That is, the implied bound can be delta weaker the the computed value. \remark the is_lower parameter is a little bit counterintuitive. It says if the other monomials imply a lower (upper) bound for the monomial at position idx. Store the result in 'antecedent' */ template void theory_arith::explain_bound(row const & r, int idx, bool is_lower, inf_numeral & delta, antecedents& ante) { SASSERT(delta >= inf_numeral::zero()); if (!relax_bounds() && (!ante.lits().empty() || !ante.eqs().empty())) return; context & ctx = get_context(); ante.reset(); // !!!!TBD: should equality ante also be reset here!!!! row_entry const & entry = r[idx]; numeral coeff = entry.m_coeff; if (relax_bounds()) { // if the variable v at position idx can have a delta increase (decrease) of 'delta', then // the monomial (coeff * v) at position idx can have a delta increase (decrease) of '|coeff| * delta' if (coeff.is_neg()) coeff.neg(); delta *= coeff; // adjust delta } typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); TRACE("arith_proof", display_row(tout, r, false); ); for (int idx2 = 0; it != end; ++it, ++idx2) { if (!it->is_dead() && idx != idx2) { bound * b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg()); SASSERT(b); if (!b->has_justification()) continue; if (!relax_bounds() || delta.is_zero()) { b->push_justification(ante, it->m_coeff, coeffs_enabled()); continue; } numeral coeff = it->m_coeff; bool is_b_lower = b->get_bound_kind() == B_LOWER; if (coeff.is_neg()) coeff.neg(); numeral inv_coeff(1); inv_coeff /= coeff; inf_numeral k_1 = b->get_value(); inf_numeral limit_k1; // if the max decrease (increase) of the curr monomial (coeff * v2) is delta, then // the maximal decrease (increase) of v2 is (1/|coeff| * delta) if (is_b_lower) { limit_k1 = k_1; // limit_k1 -= delta * coeff; limit_k1.submul(inv_coeff, delta); } else { limit_k1 = k_1; // limit_k1 += delta * coeff; limit_k1.addmul(inv_coeff, delta); } TRACE("propagate_bounds_bug", tout << "is_b_lower: " << is_b_lower << " k1: " << k_1 << " limit_k1: " << limit_k1 << " delta: " << delta << " coeff: " << coeff << "\n";); inf_numeral k_2 = k_1; atom * new_atom = 0; atoms const & as = m_var_occs[it->m_var]; typename atoms::const_iterator it = as.begin(); typename atoms::const_iterator end = as.end(); for (; it != end; ++it) { atom * a = *it; if (a == b) continue; bool_var bv = a->get_bool_var(); lbool val = ctx.get_assignment(bv); if (val == l_undef) continue; // TODO: check if the following line is a bottleneck TRACE("arith", tout << "v" << a->get_bool_var() << " " << (val == l_true) << "\n";); a->assign_eh(val == l_true, get_epsilon(a->get_var())); if (val != l_undef && a->get_bound_kind() == b->get_bound_kind()) { SASSERT((ctx.get_assignment(bv) == l_true) == a->is_true()); inf_numeral a_val = a->get_value(); if (is_b_lower) { if (a_val >= limit_k1 && a_val < k_2) { k_2 = a_val; new_atom = a; } } else { if (a_val <= limit_k1 && a_val > k_2) { k_2 = a_val; new_atom = a; } } } } SASSERT(!is_b_lower || k_2 <= k_1); SASSERT(is_b_lower || k_2 >= k_1); if (new_atom == 0) { b->push_justification(ante, coeff, coeffs_enabled()); continue; } SASSERT(!is_b_lower || k_2 < k_1); SASSERT(is_b_lower || k_2 > k_1); if (is_b_lower) { TRACE("propagate_bounds", tout << "coeff: " << coeff << ", k_1 - k_2: " << k_1 - k_2 << ", delta: " << delta << "\n";); delta -= coeff*(k_1 - k_2); } else { TRACE("propagate_bounds", tout << "coeff: " << coeff << ", k_2 - k_1: " << k_2 - k_1 << ", delta: " << delta << "\n";); delta -= coeff*(k_2 - k_1); } TRACE("propagate_bounds", tout << "delta (after replace): " << delta << "\n";); new_atom->push_justification(ante, coeff, coeffs_enabled()); SASSERT(delta >= inf_numeral::zero()); } } } template void theory_arith::mk_implied_bound(row const & r, unsigned idx, bool is_lower, theory_var v, bound_kind kind, inf_numeral const & k) { atoms const & as = m_var_occs[v]; antecedents& ante = get_antecedents(); inf_numeral const & epsilon = get_epsilon(v); inf_numeral delta; typename atoms::const_iterator it = as.begin(); typename atoms::const_iterator end = as.end(); for (; it != end; ++it) { atom * a = *it; bool_var bv = a->get_bool_var(); literal l(bv); if (get_context().get_assignment(bv) == l_undef) { inf_numeral const & k2 = a->get_k(); delta.reset(); if (a->get_atom_kind() == A_LOWER) { // v >= k k >= k2 |- v >= k2 if (kind == B_LOWER && k >= k2) { if (relax_bounds()) { delta = k; delta -= k2; } TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta, ante); } // v <= k k < k2 |- v < k2 |- not v >= k2 if (kind == B_UPPER && k < k2) { // it is not sufficient to check whether k < k2. // example: // k = -1/5*epsilon // k2 = 0 // Thus, v <= -1/5*epsilon // (not v >= 0) which is equivalent to v <= -epsilon. delta = k2; delta -= k; delta -= epsilon; if (delta.is_nonneg()) { TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", not v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta, ante); } } } else { // v >= k k > k2 |- v > k2 |- not v <= k2 if (kind == B_LOWER && k > k2) { // it is not sufficient to check whether k > k2. // see example above. delta = k; delta -= k2; delta -= epsilon; if (delta.is_nonneg()) { TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", not v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta, ante); } } // v <= k k <= k2 |- v <= k2 if (kind == B_UPPER && k <= k2) { if (relax_bounds()) { delta = k2; delta -= k; } TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta, ante); } } } } } template void theory_arith::assign_bound_literal(literal l, row const & r, unsigned idx, bool is_lower, inf_numeral & delta, antecedents& ante) { m_stats.m_bound_props++; context & ctx = get_context(); explain_bound(r, idx, is_lower, delta, ante); if (dump_lemmas()) { char const * logic = is_int(r.get_base_var()) ? "QF_LIA" : "QF_LRA"; ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), l, logic); } TRACE("propagate_bounds", literal_vector::const_iterator it = ante.lits().begin(); literal_vector::const_iterator end = ante.lits().end(); for (; it != end; ++it) { ctx.display_detailed_literal(tout, *it); tout << " "; } eq_vector::const_iterator it2 = ante.eqs().begin(); eq_vector::const_iterator end2 = ante.eqs().end(); for (; it2 != end2; ++it2) { tout << "#" << it2->first->get_owner_id() << "=#" << it2->second->get_owner_id() << " "; } tout << " --> "; ctx.display_detailed_literal(tout, l); tout << "\n";); if (ante.lits().size() < small_lemma_size() && ante.eqs().empty()) { literal_vector & lits = m_tmp_literal_vector2; lits.reset(); lits.push_back(l); literal_vector::const_iterator it = ante.lits().begin(); literal_vector::const_iterator end = ante.lits().end(); for (; it != end; ++it) lits.push_back(~(*it)); justification * js = 0; if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr(), ante.num_params(), ante.params("assign-bounds")); } ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); } else { region & r = ctx.get_region(); ctx.assign(l, ctx.mk_justification( ext_theory_propagation_justification( get_id(), r, ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), l, ante.num_params(), ante.params("assign-bounds")))); } } /** \brief Traverse rows in m_to_check and try do derive improved bounds for the variables occurring in them. */ template void theory_arith::propagate_bounds() { TRACE("propagate_bounds_detail", display(tout);); typename svector::iterator it = m_to_check.begin(); typename svector::iterator end = m_to_check.end(); for (; it != end; ++it) { row & r = m_rows[*it]; if (r.get_base_var() != null_theory_var) { if (r.size() < max_lemma_size()) { // Ignore big rows. int lower_idx; int upper_idx; is_row_useful_for_bound_prop(r, lower_idx, upper_idx); if (lower_idx >= 0) { imply_bound_for_monomial(r, lower_idx, true); } else if (lower_idx == -1) { imply_bound_for_all_monomials(r, true); } if (upper_idx >= 0) { imply_bound_for_monomial(r, upper_idx, false); } else if (upper_idx == -1) { imply_bound_for_all_monomials(r, false); } // sneaking cheap eq detection in this loop propagate_cheap_eq(*it); } #if 0 theory_var v = r.get_base_var(); if (!is_int(v) || get_value(v).is_int()) { // If an integer value is not assigned to an integer value, then // bound propagation can diverge. m_in_to_check.remove(v); } #endif } } m_to_check.reset(); m_in_to_check.reset(); } // ----------------------------------- // // Justification // // ----------------------------------- template void theory_arith::set_conflict(unsigned num_literals, literal const * lits, unsigned num_eqs, enode_pair const * eqs, antecedents& bounds, bool is_lia, char const* proof_rule) { SASSERT(num_literals != 0 || num_eqs != 0); context & ctx = get_context(); m_stats.m_conflicts++; m_num_conflicts++; if (dump_lemmas()) { char const * logic = is_lia ? "QF_LIA" : "QF_LRA"; ctx.display_lemma_as_smt_problem(num_literals, lits, num_eqs, eqs, false_literal, logic); } region & r = ctx.get_region(); TRACE("arith_conflict", for (unsigned i = 0; i < num_literals; i++) { ctx.display_detailed_literal(tout, lits[i]); tout << " "; if (coeffs_enabled()) { tout << "bound: " << bounds.lit_coeffs()[i] << "\n"; } } for (unsigned i = 0; i < num_eqs; i++) { tout << "#" << eqs[i].first->get_owner_id() << "=#" << eqs[i].second->get_owner_id() << " "; if (coeffs_enabled()) { tout << "bound: " << bounds.eq_coeffs()[i] << "\n"; } } for (unsigned i = 0; i < bounds.num_params(); ++i) { tout << bounds.params(proof_rule)[i] << "\n"; } tout << "\n";); record_conflict(num_literals, lits, num_eqs, eqs, bounds.num_params(), bounds.params(proof_rule)); ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification(get_id(), r, num_literals, lits, num_eqs, eqs, bounds.num_params(), bounds.params(proof_rule)))); } /** \brief Collect the proofs for the fixed variables in the given row. Store the proofs in result. */ template void theory_arith::collect_fixed_var_justifications(row const & r, antecedents& antecedents) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && is_fixed(it->m_var)) { lower(it->m_var)->push_justification(antecedents, it->m_coeff, coeffs_enabled()); upper(it->m_var)->push_justification(antecedents, it->m_coeff, coeffs_enabled()); } } } // ----------------------------------- // // Model generation // // ----------------------------------- // // The arithmetic module uses infinitesimals. So, // an inf_numeral (n,k) represents n + k*epsilon // where epsilon is a very small number. // In order to generate a model, we need to compute // a value for epsilon in a way all bounds remain // satisfied. // // 1) Handling inequalities: (n1, k1) <= (n2, k2) // // The only intersting case is n1 < n2 and k1 > k2. // Using the definition of infinitesimal numbers // we have: // n1 + k1 * epsilon <= n2 + k2 - epsilon // Therefore: // epsilon <= (n2 - n1) / (k1 - k2) // // Starting at Z3 V2.0, we split disequalities. // So, we do not need to handle them. If we decide // to support them again in the future: // // 2) Handling disequalities: (n1, k1) /= n2 // // case a) k1 is positive and n1 < n2 // Thus, epsilon < (n2 - n1) / k1 // => epsilon <= (n2 - n1) / 2*k1 // // case b) k1 is negative and n1 > n2 // Similarly, epsilon <= (n2 - n1) / 2*k1 // /** \brief Update the value of epsilon using the inequality l <= u */ template void theory_arith::update_epsilon(const inf_numeral & l, const inf_numeral & u) { if (l.get_rational() < u.get_rational() && l.get_infinitesimal() > u.get_infinitesimal()) { numeral new_epsilon = (u.get_rational() - l.get_rational()) / (l.get_infinitesimal() - u.get_infinitesimal()); if (new_epsilon < m_epsilon) { m_epsilon = new_epsilon; } } SASSERT(m_epsilon.is_pos()); } template void theory_arith::compute_epsilon() { m_epsilon = numeral(1); theory_var num = get_num_vars(); for (theory_var v = 0; v < num; v++) { bound * l = lower(v); bound * u = upper(v); if (l != 0) update_epsilon(l->get_value(), get_value(v)); if (u != 0) update_epsilon(get_value(v), u->get_value()); } TRACE("epsilon_bug", tout << "epsilon: " << m_epsilon << "\n";); } /** The epsilon computed by compute_epsilon may accidentally make two shared variables to have the same assignment. This method keeps dividing epsilon by 2 until this "clash" does not occur. Here is an example of the problem Assignment: x -> 9.5 y -> 10 - epsilon x and y have different assignments. However, if compute_epsilon sets epsilon to 0.5, then x and y become 9.5. However, an equality is not propagated to the core since in the assignment above they are assigned to distinct values. This bug was reported by Marcello Bersani. Remark: this is not really a soundness bug. The result sat/unsat produced by Z3 was still correct. However, the model construction was incorrect. Perhaps, this explains why this bug was not detected before. */ template void theory_arith::refine_epsilon() { while (true) { rational2var mapping; theory_var num = get_num_vars(); bool refine = false; for (theory_var v = 0; v < num; v++) { if (is_int(v)) continue; if (!get_context().is_shared(get_enode(v))) continue; inf_numeral const & val = get_value(v); if (Ext::is_infinite(val)) { continue; } rational value = val.get_rational().to_rational() + m_epsilon.to_rational() * val.get_infinitesimal().to_rational(); theory_var v2; if (mapping.find(value, v2)) { SASSERT(!is_int(v2)); if (get_value(v) != get_value(v2)) { // v and v2 are not known to be equal. // The choice of m_epsilon is making them equal. TRACE("refine_epsilon", tout << "v" << v << " v" << v2 << " " << get_value(v) << " " << get_value(v2) << " " << value << std::endl; ); refine = true; break; } } else { mapping.insert(value, v); } } if (!refine) return; numeral two(2); m_epsilon = m_epsilon / two; TRACE("refine_epsilon", tout << "new epsilon..." << m_epsilon << std::endl;); } } template void theory_arith::init_model(model_generator & m) { TRACE("theory_arith", tout << "init model invoked...\n";); m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); compute_epsilon(); refine_epsilon(); } template model_value_proc * theory_arith::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); inf_numeral const & val = get_value(v); rational num = val.get_rational().to_rational() + m_epsilon.to_rational() * val.get_infinitesimal().to_rational(); if (is_int(v) && !num.is_int()) { TRACE("arith", tout << "Truncating non-integer value. This is possible for non-linear constraints v" << v << " " << num << "\n";); num = floor(num); } return alloc(expr_wrapper_proc, m_factory->mk_value(num, is_int(v))); } // ----------------------------------- // // Model checker support // // ----------------------------------- template bool theory_arith::get_value(enode * n, expr_ref & r) { theory_var v = n->get_th_var(get_id()); if (v == null_theory_var) { // TODO: generate fresh value different from other get_value(v) for all v. return false; } inf_numeral const & val = get_value(v); if (!val.get_infinitesimal().is_zero()) { // TODO: add support for infinitesimals return false; } numeral _val = val.get_rational(); r = m_util.mk_numeral(_val.to_rational(), is_int(v)); return true; } // ----------------------------------- // // Backtracking // // ----------------------------------- template void theory_arith::push_scope_eh() { theory::push_scope_eh(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_atoms_lim = m_atoms.size(); s.m_bound_trail_lim = m_bound_trail.size(); s.m_unassigned_atoms_trail_lim = m_unassigned_atoms_trail.size(); s.m_asserted_bounds_lim = m_asserted_bounds.size(); s.m_asserted_qhead_old = m_asserted_qhead; s.m_bounds_to_delete_lim = m_bounds_to_delete.size(); s.m_nl_monomials_lim = m_nl_monomials.size(); s.m_nl_propagated_lim = m_nl_propagated.size(); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); CASSERT("arith", satisfy_bounds()); } template void theory_arith::pop_scope_eh(unsigned num_scopes) { CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); TRACE("arith_pop_scope_bug", display(tout);); // The m_update_trail_stack may not be empty. // In an old version, only propagate_core and methods invoked by propagate_core were // inserting elements in the m_update_trail_stack stack. // This is not true anymore. The method quasi_base_row2base_row also does that. // So, restore_assignment must be invoked. In most cases, it is a noop since the stack is empty. restore_assignment(); m_to_patch.reset(); unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; restore_bounds(s.m_bound_trail_lim); restore_unassigned_atoms(s.m_unassigned_atoms_trail_lim); m_asserted_bounds.shrink(s.m_asserted_bounds_lim); m_asserted_qhead = s.m_asserted_qhead_old; TRACE("arith_pop_scope_bug", tout << "num_vars: " << get_num_vars() << ", num_old_vars: " << get_old_num_vars(num_scopes) << "\n";); restore_nl_propagated_flag(s.m_nl_propagated_lim); m_nl_monomials.shrink(s.m_nl_monomials_lim); del_atoms(s.m_atoms_lim); del_bounds(s.m_bounds_to_delete_lim); del_vars(get_old_num_vars(num_scopes)); m_scopes.shrink(new_lvl); theory::pop_scope_eh(num_scopes); VERIFY(make_feasible()); SASSERT(m_to_patch.empty()); m_to_check.reset(); m_in_to_check.reset(); m_new_atoms.reset(); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); CASSERT("arith", satisfy_bounds()); } template void theory_arith::restore_nl_propagated_flag(unsigned old_trail_size) { typename svector::iterator begin = m_nl_propagated.begin() + old_trail_size; typename svector::iterator it = m_nl_propagated.end(); while (it != begin) { --it; SASSERT(m_data[*it].m_nl_propagated); m_data[*it].m_nl_propagated = false; } m_nl_propagated.shrink(old_trail_size); } template void theory_arith::restore_bounds(unsigned old_trail_size) { CASSERT("arith", wf_rows()); typename svector::iterator begin = m_bound_trail.begin() + old_trail_size; typename svector::iterator it = m_bound_trail.end(); while (it != begin) { --it; theory_var v = it->get_var(); bound * b = it->get_old_bound(); SASSERT(is_base(v) || is_non_base(v)); restore_bound(v, b, it->is_upper()); if (lazy_pivoting_lvl() > 2 && b == 0 && is_base(v) && is_free(v)) { SASSERT(!has_var_kind(get_var_row(v), BASE)); SASSERT(!has_var_kind(get_var_row(v), QUASI_BASE)); eliminate(v, false); SASSERT(m_columns[v].size() == 1); SASSERT(!has_var_kind(get_var_row(v), BASE)); SASSERT(!has_var_kind(get_var_row(v), QUASI_BASE)); set_var_kind(v, QUASI_BASE); } } m_bound_trail.shrink(old_trail_size); CASSERT("arith", wf_rows()); } template void theory_arith::restore_unassigned_atoms(unsigned old_trail_size) { svector::iterator begin = m_unassigned_atoms_trail.begin() + old_trail_size; svector::iterator it = m_unassigned_atoms_trail.end(); while (it != begin) { --it; m_unassigned_atoms[*it]++; } m_unassigned_atoms_trail.shrink(old_trail_size); } template void theory_arith::del_atoms(unsigned old_size) { typename atoms::iterator begin = m_atoms.begin() + old_size; typename atoms::iterator it = m_atoms.end(); while (it != begin) { --it; atom * a = *it; theory_var v = a->get_var(); bool_var bv = a->get_bool_var(); erase_bv2a(bv); SASSERT(m_var_occs[v].back() == a); m_var_occs[v].pop_back(); dealloc(a); } m_atoms.shrink(old_size); } template void theory_arith::del_bounds(unsigned old_size) { typename ptr_vector::iterator begin = m_bounds_to_delete.begin() + old_size; typename ptr_vector::iterator it = m_bounds_to_delete.end(); while (it != begin) { --it; bound * b = *it; dealloc(b); } m_bounds_to_delete.shrink(old_size); } template void theory_arith::del_vars(unsigned old_num_vars) { int num_vars = get_num_vars(); SASSERT(num_vars >= static_cast(old_num_vars)); if (num_vars != static_cast(old_num_vars)) { theory_var v = num_vars; while (v > static_cast(old_num_vars)) { --v; switch (get_var_kind(v)) { case QUASI_BASE: SASSERT(m_columns[v].size() == 1); del_row(get_var_row(v)); TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); break; case BASE: SASSERT(lazy_pivoting_lvl() != 0 || m_columns[v].size() == 1); if (lazy_pivoting_lvl() > 0) eliminate(v, false); TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); del_row(get_var_row(v)); break; case NON_BASE: { col_entry const * entry = get_a_base_row_that_contains(v); if (entry) { row & r = m_rows[entry->m_row_id]; SASSERT(is_base(r.get_base_var())); SASSERT(r[entry->m_row_idx].m_var == v); pivot(r.get_base_var(), v, r[entry->m_row_idx].m_coeff, false); SASSERT(is_base(v)); del_row(get_var_row(v)); TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); } else { TRACE("arith_make_feasible", tout << "no row v" << v << "\n";); } break; } } m_in_update_trail_stack.remove(v); m_left_basis.remove(v); m_in_to_check.remove(v); } m_columns .shrink(old_num_vars); m_data .shrink(old_num_vars); m_value .shrink(old_num_vars); m_old_value .shrink(old_num_vars); m_var_occs .shrink(old_num_vars); m_unassigned_atoms.shrink(old_num_vars); m_var_pos .shrink(old_num_vars); m_bounds[0] .shrink(old_num_vars); m_bounds[1] .shrink(old_num_vars); SASSERT(check_vector_sizes()); } SASSERT(m_var_occs.size() == old_num_vars); } template void theory_arith::del_row(unsigned r_id) { row & r = m_rows[r_id]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; column & c = m_columns[v]; c.del_col_entry(it->m_col_idx); } } r.m_base_var = null_theory_var; r.reset(); m_dead_rows.push_back(r_id); } /** \brief reset and retrieve built-in explanation hints for arithmetic lemmmas. */ template typename theory_arith::antecedents& theory_arith::get_antecedents() { m_tmp_antecedents.reset(); return m_tmp_antecedents; } }; #endif /* THEORY_ARITH_CORE_H_ */ z3-z3-4.4.1/src/smt/theory_arith_def.h000066400000000000000000000007751260446376700175200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_def.h Abstract: Author: Leonardo de Moura (leonardo) 2008-04-22. Revision History: --*/ #ifndef THEORY_ARITH_DEF_H_ #define THEORY_ARITH_DEF_H_ #include"theory_arith.h" #include"theory_arith_core.h" #include"theory_arith_aux.h" #include"theory_arith_inv.h" #include"theory_arith_pp.h" #include"theory_arith_int.h" #include"theory_arith_eq.h" #include"theory_arith_nl.h" #endif /* THEORY_ARITH_DEF_H_ */ z3-z3-4.4.1/src/smt/theory_arith_eq.h000066400000000000000000000311501260446376700173560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_eq.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-22. Revision History: --*/ #ifndef THEORY_ARITH_EQ_H_ #define THEORY_ARITH_EQ_H_ // #define PROFILE_OFFSET_ROW #ifdef PROFILE_OFFSET_ROW #include"stopwatch.h" #undef max #undef min #endif namespace smt { /** \brief This method is invoked when a variable was non fixed and become fixed. */ template void theory_arith::fixed_var_eh(theory_var v) { if (!propagate_eqs()) return; SASSERT(is_fixed(v)); // WARNINING: it is not safe to use get_value(v) here, since // get_value(v) may not satisfy v bounds at this point. CTRACE("arith_bug", !lower_bound(v).is_rational(), display_var(tout, v);); SASSERT(lower_bound(v).is_rational()); numeral const & val = lower_bound(v).get_rational(); value_sort_pair key(val, is_int(v)); theory_var v2; if (m_fixed_var_table.find(key, v2)) { if (v2 < static_cast(get_num_vars()) && is_fixed(v2) && lower_bound(v2).get_rational() == val) { // It only makes sense to propagate equality to the core when v and v2 have the same sort. // The table m_fixed_var_table is not restored during backtrack. So, it may // contain invalid (key -> value) pairs. So, we must check whether v2 is really equal to val (previous test) AND it has // the same sort of v. The following test was missing in a previous version of Z3. if (!is_equal(v, v2) && is_int(v) == is_int(v2)) { antecedents& ante = get_antecedents(); // // v <= k <= v2 => v <= v2 // v >= k >= v2 => v >= v2 // lower(v)->push_justification(ante, numeral::zero(), proofs_enabled()); upper(v2)->push_justification(ante, numeral::zero(), proofs_enabled()); lower(v2)->push_justification(ante, numeral::zero(), proofs_enabled()); upper(v)->push_justification(ante, numeral::zero(), proofs_enabled()); TRACE("arith_fixed_propagate_eq", tout << "propagate eq: v" << v << " = v" << v2 << "\n"; display_var(tout, v); display_var(tout, v2);); m_stats.m_fixed_eqs++; propagate_eq_to_core(v, v2, ante); } } else { // the original fixed variable v2 was deleted or its bounds were removed // during backtracking. m_fixed_var_table.erase(key); m_fixed_var_table.insert(key, v); } } else { m_fixed_var_table.insert(key, v); } } /** \brief Returns true if r is a offset row. A offset row is a row that can be written as: x = y + M where x and y are non fixed variables, and M is linear polynomials where all variables are fixed, and M evaluates to k. When true is returned, x, y and k are stored in the given arguments. \remark The following rule is used to select x and y. - if the base variable is not fixed, then x is the base var. - otherwise x is the smallest var. */ template bool theory_arith::is_offset_row(row const & r, theory_var & x, theory_var & y, numeral & k) const { #ifdef PROFILE_OFFSET_ROW static stopwatch timer; static unsigned total = 0; static unsigned ok = 0; timer.start(); total ++; #endif // Quick check without using big numbers... // Check if there are more than 2 unbounded vars. unsigned bad = 0; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; if (lower(v) != 0 && upper(v) != 0) continue; bad++; if (bad > 2) { #ifdef PROFILE_OFFSET_ROW timer.stop(); #endif return false; } } } // Full check using == for big numbers... x = null_theory_var; y = null_theory_var; it = r.begin_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; if (is_fixed(v)) continue; if (it->m_coeff.is_one() && x == null_theory_var) { x = v; continue; } if (it->m_coeff.is_minus_one() && y == null_theory_var) { y = v; continue; } #ifdef PROFILE_OFFSET_ROW timer.stop(); #endif return false; } } if (x == null_theory_var && y == null_theory_var) { #ifdef PROFILE_OFFSET_ROW timer.stop(); #endif return false; } k.reset(); it = r.begin_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; if (v == x || v == y) continue; SASSERT(is_fixed(v)); k -= it->m_coeff * lower_bound(v).get_rational(); } } #ifdef PROFILE_OFFSET_ROW timer.stop(); ok++; if (ok % 100000 == 0) { TRACE("propagate_cheap_eq", tout << total << " " << ok << " " << static_cast(ok)/static_cast(total) << " " << timer.get_seconds() << "\n"; tout.flush();); } #endif if (y == null_theory_var) return true; if (x == null_theory_var) { std::swap(x, y); k.neg(); SASSERT(x != null_theory_var); return true; } if (r.get_base_var() != x && x > y) { std::swap(x, y); k.neg(); } return true; } /** \brief Cheap propagation of equalities x_i = x_j, when x_i = y + k x_j = y + k This equalities are detected by maintaining a map: (y, k) -> row_id when a row is of the form x = y + k This methods checks whether the given row is an offset row (See is_offset_row), and uses the map to find new equalities if that is the case. */ template void theory_arith::propagate_cheap_eq(unsigned rid) { if (!propagate_eqs()) return; TRACE("propagate_cheap_eq", tout << "checking if row " << rid << " can propagate equality.\n"; display_row_info(tout, rid);); row const & r = m_rows[rid]; theory_var x; theory_var y; numeral k; if (is_offset_row(r, x, y, k)) { if (y == null_theory_var) { // x is an implied fixed var at k. value_sort_pair key(k, is_int(x)); theory_var x2; if (m_fixed_var_table.find(key, x2) && x2 < static_cast(get_num_vars()) && is_fixed(x2) && lower_bound(x2).get_rational() == k && // We must check whether x2 is an integer. // The table m_fixed_var_table is not restored during backtrack. So, it may // contain invalid (key -> value) pairs. // So, we must check whether x2 is really equal to k (previous test) // AND has the same sort of x. // The following test was missing in a previous version of Z3. is_int(x) == is_int(x2) && !is_equal(x, x2)) { antecedents& ante = get_antecedents(); collect_fixed_var_justifications(r, ante); // // x1 <= k1 x1 >= k1, x2 <= x1 + k2 x2 >= x1 + k2 // lower(x2)->push_justification(ante, numeral::zero(), proofs_enabled()); upper(x2)->push_justification(ante, numeral::zero(), proofs_enabled()); m_stats.m_fixed_eqs++; propagate_eq_to_core(x, x2, ante); } } if (k.is_zero() && y != null_theory_var && !is_equal(x, y) && is_int(x) == is_int(y)) { // found equality x = y antecedents& ante = get_antecedents(); collect_fixed_var_justifications(r, ante); TRACE("propagate_cheap_eq", tout << "propagate eq using x-y=0 row:\n"; display_row_info(tout, r);); m_stats.m_offset_eqs++; propagate_eq_to_core(x, y, ante); } int row_id; var_offset key(y, k); if (m_var_offset2row_id.find(key, row_id)) { row & r2 = m_rows[row_id]; if (r.get_base_var() == r2.get_base_var()) { // it is the same row. return; } theory_var x2; theory_var y2; numeral k2; if (r2.get_base_var() != null_theory_var && is_offset_row(r2, x2, y2, k2)) { bool new_eq = false; #ifdef _TRACE bool swapped = false; #endif if (y == y2 && k == k2) { new_eq = true; } else if (y2 != null_theory_var) { #ifdef _TRACE swapped = true; #endif std::swap(x2, y2); k2.neg(); if (y == y2 && k == k2) { new_eq = true; } } if (new_eq) { if (!is_equal(x, x2) && is_int(x) == is_int(x2)) { SASSERT(y == y2 && k == k2); antecedents& ante = get_antecedents(); collect_fixed_var_justifications(r, ante); collect_fixed_var_justifications(r2, ante); TRACE("propagate_cheap_eq", tout << "propagate eq two rows:\n"; tout << "swapped: " << swapped << "\n"; tout << "x : v" << x << "\n"; tout << "x2 : v" << x2 << "\n"; display_row_info(tout, r); display_row_info(tout, r2);); m_stats.m_offset_eqs++; propagate_eq_to_core(x, x2, ante); } return; } } // the original row was delete or it is not offset row anymore ===> remove it from table m_var_offset2row_id.erase(key); } // add new entry m_var_offset2row_id.insert(key, rid); } } template void theory_arith::propagate_eq_to_core(theory_var x, theory_var y, antecedents& antecedents) { // I doesn't make sense to propagate an equality (to the core) of variables of different sort. SASSERT(is_int(x) == is_int(y)); // Ignore equality if variables are already known to be equal. if (is_equal(x, y)) return; context & ctx = get_context(); region & r = ctx.get_region(); enode * _x = get_enode(x); enode * _y = get_enode(y); justification * js = ctx.mk_justification( ext_theory_eq_propagation_justification( get_id(), r, antecedents.lits().size(), antecedents.lits().c_ptr(), antecedents.eqs().size(), antecedents.eqs().c_ptr(), _x, _y, antecedents.num_params(), antecedents.params("eq-propagate"))); TRACE("propagate_eq_to_core", tout << "detected equality: #" << _x->get_owner_id() << " = #" << _y->get_owner_id() << "\n"; display_var(tout, x); display_var(tout, y);); ctx.assign_eq(_x, _y, eq_justification(js)); } }; #endif /* THEORY_ARITH_EQ_H_ */ z3-z3-4.4.1/src/smt/theory_arith_int.h000066400000000000000000001602021260446376700175440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_int.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-17. Revision History: --*/ #ifndef THEORY_ARITH_INT_H_ #define THEORY_ARITH_INT_H_ #include"ast_ll_pp.h" #include"arith_simplifier_plugin.h" #include"well_sorted.h" #include"euclidean_solver.h" #include"numeral_buffer.h" #include"ast_smt2_pp.h" namespace smt { // ----------------------------------- // // Integrality // // ----------------------------------- /** \brief Move non base variables to one of its bounds. If the variable does not have bounds, it is integer, but it is not assigned to an integer value, then the variable is set to an integer value. In mixed integer/real problems moving a real variable to a bound could cause an integer value to have an infinitesimal. Such an assignment would disable mk_gomory_cut, and Z3 would loop. */ template void theory_arith::move_non_base_vars_to_bounds() { theory_var num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_non_base(v)) { bound * l = lower(v); bound * u = upper(v); const inf_numeral & val = get_value(v); if (l != 0 && u != 0) { if (val != l->get_value() && val != u->get_value()) set_value(v, l->get_value()); } else if (l != 0) { if (val != l->get_value()) set_value(v, l->get_value()); } else if (u != 0) { if (val != u->get_value()) set_value(v, u->get_value()); } else { if (is_int(v) && !val.is_int()) { inf_numeral new_val(floor(val)); set_value(v, new_val); } } } } } /** \brief Returns true if the there is an integer variable that is not assigned to an integer value. */ template bool theory_arith::has_infeasible_int_var() { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) return true; } return false; } /** \brief Find an integer base var that is not assigned to an integer value, but is bounded (i.e., it has lower and upper bounds). Return null_var_id if all integer base variables are assigned to integer values. If there are multiple variables satisfying the condition above, then select the one with the tightest bound. */ template theory_var theory_arith::find_bounded_infeasible_int_base_var() { theory_var result = null_theory_var; numeral range; numeral new_range; numeral small_range_thresold(1024); unsigned n = 0; typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v == null_theory_var) continue; if (!is_base(v)) continue; if (!is_int(v)) continue; if (get_value(v).is_int()) continue; if (!is_bounded(v)) continue; numeral const & l = lower_bound(v).get_rational(); numeral const & u = upper_bound(v).get_rational(); new_range = u; new_range -= l; if (new_range > small_range_thresold) continue; if (result == null_theory_var) { result = v; range = new_range; n = 1; continue; } if (new_range < range) { n = 1; result = v; range = new_range; continue; } if (new_range == range) { n++; if (m_random() % n == 0) { result = v; range = new_range; continue; } } } return result; } /** \brief Find an integer base var that is not assigned to an integer value. Return null_var_id if all integer base variables are assigned to integer values. \remark This method gives preference to bounded integer variables. If all variables are unbounded, then it selects a random one. */ template theory_var theory_arith::find_infeasible_int_base_var() { theory_var v = find_bounded_infeasible_int_base_var(); if (v != null_theory_var) { TRACE("find_infeasible_int_base_var", display_var(tout, v);); return v; } unsigned n = 0; theory_var r = null_theory_var; #define SELECT_VAR(VAR) if (r == null_theory_var) { n = 1; r = VAR; } else { n++; SASSERT(n >= 2); if (m_random() % n == 0) r = VAR; } typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v != null_theory_var && is_base(v) && is_int(v) && !get_value(v).is_int()) { SELECT_VAR(v); } } if (r == null_theory_var) { it = m_rows.begin(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v != null_theory_var && is_quasi_base(v) && is_int(v) && !get_value(v).is_int()) { quasi_base_row2base_row(get_var_row(v)); SELECT_VAR(v); } } if (r == null_theory_var) return null_theory_var; } CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); return r; } /** \brief Create "branch and bound" case-split. */ template void theory_arith::branch_infeasible_int_var(theory_var v) { SASSERT(is_int(v)); SASSERT(!get_value(v).is_int()); m_stats.m_branches++; TRACE("arith_branching", tout << "branching v" << v << " = " << get_value(v) << "\n"; display_var(tout, v);); numeral k = ceil(get_value(v)); rational _k = k.to_rational(); expr * bound = m_util.mk_ge(get_enode(v)->get_owner(), m_util.mk_numeral(_k, true)); TRACE("arith_branching", tout << mk_bounded_pp(bound, get_manager()) << "\n";); context & ctx = get_context(); ctx.internalize(bound, true); ctx.mark_as_relevant(bound); } /** \brief Create a "cut from proof" lemma. The set of rows where the base variable is tight are extracted. These row equalities are checked for integer feasiability. If they are not integer feasible, then an integer infeasible equation, that is implied from the extracted equalities is extracted. The extracted equality a*x = 0 is blocked by asserting the disjunction (a*x > 0 \/ a*x < 0) */ template bool theory_arith::branch_infeasible_int_equality() { typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); vector > rows; unsigned max_row = 1; // all rows should contain a constant in the last position. u_map var2index; // map theory variables to positions in 'rows'. u_map index2var; // map back positions in 'rows' to theory variables. for (; it != end; ++it) { theory_var b = it->get_base_var(); if (b == null_theory_var) { TRACE("theory_arith_int", display_row(tout << "null: ", *it, true); ); continue; } bool is_tight = false; numeral const_coeff(0); bound* l = lower(b), *u = upper(b); if (l != 0 && get_value(b) - inf_numeral(1) < l->get_value()) { SASSERT(l->get_value() <= get_value(b)); is_tight = true; const_coeff = l->get_value().get_rational(); } else if (u != 0 && get_value(b) + inf_numeral(1) > u->get_value()) { SASSERT(get_value(b) <= u->get_value()); is_tight = true; const_coeff = u->get_value().get_rational(); } if (!is_tight) { TRACE("theory_arith_int", display_row(tout << "!tight: ", *it, true); display_var(tout, b); ); continue; } rows.push_back(vector(max_row)); vector& row = rows.back(); numeral denom(1); unsigned index = 0; typename vector::const_iterator it_r = it->begin_entries(); typename vector::const_iterator end_r = it->end_entries(); for (; it_r != end_r && is_tight; ++it_r) { if (it_r->is_dead()) continue; theory_var x = it_r->m_var; if (x == b) continue; numeral coeff = it_r->m_coeff; if (is_fixed(x)) { const_coeff += coeff*lower(x)->get_value().get_rational(); continue; } if (!is_int(x)) { TRACE("theory_arith_int", display_row(tout << "!int: ", *it, true); ); is_tight = false; continue; } if (var2index.find(x, index)) { row[index] = coeff.to_rational(); } else { row.push_back(coeff.to_rational()); var2index.insert(x, max_row); index2var.insert(max_row, x); ++max_row; } numeral tmp_coeff = denominator(coeff); denom = lcm(tmp_coeff, denom); } if (!is_tight) { rows.pop_back(); continue; } row[0] = const_coeff.to_rational(); numeral tmp_const_coeff = denominator(const_coeff); denom = lcm(tmp_const_coeff, denom); if (!denom.is_one()) { for (unsigned i = 0; i < max_row; ++i) { row[i] *= denom.to_rational(); } } TRACE("theory_arith_int", tout << "extracted row:\n"; for (unsigned i = 0; i < max_row; ++i) { tout << row[i] << " "; } tout << " = 0\n"; tout << "base value: " << get_value(b) << "\n"; display_row(tout, *it, true); ); } // // Align the sizes of rows. // The sizes are monotonically increasing. // for (unsigned i = 0; i < rows.size(); ++i) { unsigned sz = rows[i].size(); SASSERT(sz <= max_row); if (sz == max_row) { break; } rows[i].resize(max_row); } vector unsat_row; if (m_arith_eq_solver.solve_integer_equations(rows, unsat_row)) { // The equalities were integer feasiable. return false; } buffer pol; for (unsigned i = 1; i < unsat_row.size(); ++i) { numeral c(unsat_row[i]); if (!c.is_zero()) { theory_var var; if (!index2var.find(i, var)) { UNREACHABLE(); } pol.push_back(row_entry(c, var)); } } if (pol.empty()) { TRACE("theory_arith_int", tout << "The witness is trivial\n";); return false; } expr_ref p1(get_manager()), p2(get_manager()); mk_polynomial_ge(pol.size(), pol.c_ptr(), -unsat_row[0]+rational(1), p1); for (unsigned i = 0; i < pol.size(); ++i) { pol[i].m_coeff.neg(); } mk_polynomial_ge(pol.size(), pol.c_ptr(), unsat_row[0]+rational(1), p2); context& ctx = get_context(); ctx.internalize(p1, false); ctx.internalize(p2, false); literal l1(ctx.get_literal(p1)), l2(ctx.get_literal(p2)); ctx.mark_as_relevant(p1.get()); ctx.mark_as_relevant(p2.get()); ctx.mk_th_axiom(get_id(), l1, l2); TRACE("theory_arith_int", tout << "cut: (or " << mk_pp(p1, get_manager()) << " " << mk_pp(p2, get_manager()) << ")\n"; ); return true; } /** \brief Create bounds for (non base) free vars in the given row. Return true if at least one variable was constrained. This method is used to enable the application of gomory cuts. */ template bool theory_arith::constrain_free_vars(row const & r) { bool result = false; theory_var b = r.get_base_var(); SASSERT(b != null_theory_var); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != b && is_free(it->m_var)) { theory_var v = it->m_var; expr * bound = m_util.mk_ge(get_enode(v)->get_owner(), m_util.mk_numeral(rational::zero(), is_int(v))); context & ctx = get_context(); ctx.internalize(bound, true); ctx.mark_as_relevant(bound); result = true; } } return result; } /** \brief Return true if it is possible to apply a gomory cut on the given row. \sa constrain_free_vars */ template bool theory_arith::is_gomory_cut_target(row const & r) { TRACE("gomory_cut", r.display(tout);); theory_var b = r.get_base_var(); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { // All non base variables must be at their bounds and assigned to rationals (that is, infinitesimals are not allowed). if (!it->is_dead() && it->m_var != b && (!at_bound(it->m_var) || !get_value(it->m_var).is_rational())) { TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; display_var(tout, it->m_var); tout << "at_bound: " << at_bound(it->m_var) << "\n"; tout << "infinitesimal: " << !get_value(it->m_var).is_rational() << "\n";); return false; } } return true; } template void theory_arith::mk_polynomial_ge(unsigned num_args, row_entry const * args, rational const& k, expr_ref & result) { // Remark: the polynomials internalized by theory_arith may not satisfy poly_simplifier_plugin->wf_polynomial assertion. bool all_int = true; for (unsigned i = 0; i < num_args && all_int; ++i) { all_int = is_int(args[i].m_var); } ast_manager & m = get_manager(); expr_ref_vector _args(m); for (unsigned i = 0; i < num_args; i++) { rational _k = args[i].m_coeff.to_rational(); expr * x = get_enode(args[i].m_var)->get_owner(); if (m_util.is_int(x) && !all_int) x = m_util.mk_to_real(x); if (_k.is_one()) _args.push_back(x); else _args.push_back(m_util.mk_mul(m_util.mk_numeral(_k, m_util.is_int(x)), x)); } expr_ref pol(m); pol = m_util.mk_add(_args.size(), _args.c_ptr()); result = m_util.mk_ge(pol, m_util.mk_numeral(k, all_int)); TRACE("arith_mk_polynomial", tout << "before simplification:\n" << mk_pp(pol, m) << "\n";); simplifier & s = get_context().get_simplifier(); proof_ref pr(m); s(result, result, pr); TRACE("arith_mk_polynomial", tout << "after simplification:\n" << mk_pp(pol, m) << "\n";); SASSERT(is_well_sorted(get_manager(), result)); } template class theory_arith::gomory_cut_justification : public ext_theory_propagation_justification { public: gomory_cut_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, antecedents& bounds, literal consequent): ext_theory_propagation_justification(fid, r, num_lits, lits, num_eqs, eqs, consequent, bounds.num_params(), bounds.params("gomory-cut")) { } // Remark: the assignment must be propagated back to arith virtual theory_id get_from_theory() const { return null_theory_id; } }; /** \brief Create a gomory cut for the given row. */ template bool theory_arith::mk_gomory_cut(row const & r) { // The following assertion is wrong. It may be violated in mixed-integer problems. // SASSERT(!all_coeff_int(r)); theory_var x_i = r.get_base_var(); SASSERT(is_int(x_i)); // The following assertion is wrong. It may be violated in mixed-real-interger problems. // The check is_gomory_cut_target will discard rows where any variable contains infinitesimals. // SASSERT(m_value[x_i].is_rational()); // infinitesimals are not used for integer variables SASSERT(!m_value[x_i].is_int()); // the base variable is not assigned to an integer value. if (constrain_free_vars(r) || !is_gomory_cut_target(r)) { TRACE("gomory_cut", tout << "failed to apply gomory cut:\n"; tout << "constrain_free_vars(r): " << constrain_free_vars(r) << "\n";); return false; } TRACE("gomory_cut", tout << "applying cut at:\n"; display_row_info(tout, r);); antecedents& ante = get_antecedents(); m_stats.m_gomory_cuts++; // gomory will be pol >= k numeral k(1); buffer pol; numeral f_0 = Ext::fractional_part(m_value[x_i]); numeral one_minus_f_0 = numeral(1) - f_0; numeral lcm_den(1); unsigned num_ints = 0; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != x_i) { theory_var x_j = it->m_var; numeral a_ij = it->m_coeff; a_ij.neg(); // make the used format compatible with the format used in: Integrating Simplex with DPLL(T) if (is_real(x_j)) { numeral new_a_ij; TRACE("gomory_cut_detail", tout << a_ij << "*v" << x_j << "\n";); if (at_lower(x_j)) { if (a_ij.is_pos()) { new_a_ij = a_ij / one_minus_f_0; } else { TRUSTME(!f_0.is_zero()); new_a_ij = a_ij / f_0; new_a_ij.neg(); } // k += new_a_ij * lower_bound(x_j).get_rational(); k.addmul(new_a_ij, lower_bound(x_j).get_rational()); lower(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } else { SASSERT(at_upper(x_j)); if (a_ij.is_pos()) { TRUSTME(!f_0.is_zero()); new_a_ij = a_ij / f_0; new_a_ij.neg(); // the upper terms are inverted. } else { // new_a_ij = - a_ij / one_minus_f_0 // new_a_ij.neg() // the upper terms are inverted new_a_ij = a_ij / one_minus_f_0; } // k += new_a_ij * upper_bound(x_j).get_rational(); k.addmul(new_a_ij, upper_bound(x_j).get_rational()); upper(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } pol.push_back(row_entry(new_a_ij, x_j)); } else { ++num_ints; SASSERT(is_int(x_j)); numeral f_j = Ext::fractional_part(a_ij); TRACE("gomory_cut_detail", tout << a_ij << "*v" << x_j << "\n"; tout << "fractional_part: " << Ext::fractional_part(a_ij) << "\n"; tout << "f_j: " << f_j << "\n"; tout << "f_0: " << f_0 << "\n"; tout << "one_minus_f_0: " << one_minus_f_0 << "\n";); if (!f_j.is_zero()) { numeral new_a_ij; if (at_lower(x_j)) { if (f_j <= one_minus_f_0) { new_a_ij = f_j / one_minus_f_0; } else { new_a_ij = (numeral(1) - f_j) / f_0; } // k += new_a_ij * lower_bound(x_j).get_rational(); k.addmul(new_a_ij, lower_bound(x_j).get_rational()); lower(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } else { SASSERT(at_upper(x_j)); if (f_j <= f_0) { new_a_ij = f_j / f_0; } else { new_a_ij = (numeral(1) - f_j) / one_minus_f_0; } new_a_ij.neg(); // the upper terms are inverted // k += new_a_ij * upper_bound(x_j).get_rational(); k.addmul(new_a_ij, upper_bound(x_j).get_rational()); upper(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } TRACE("gomory_cut_detail", tout << "new_a_ij: " << new_a_ij << "\n";); pol.push_back(row_entry(new_a_ij, x_j)); lcm_den = lcm(lcm_den, denominator(new_a_ij)); } } } } CTRACE("empty_pol", pol.empty(), display_row_info(tout, r);); expr_ref bound(get_manager()); if (pol.empty()) { SASSERT(k.is_pos()); // conflict 0 >= k where k is positive set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante, true, "gomory-cut"); return true; } else if (pol.size() == 1) { theory_var v = pol[0].m_var; k /= pol[0].m_coeff; bool is_lower = pol[0].m_coeff.is_pos(); if (is_int(v) && !k.is_int()) { k = is_lower?ceil(k):floor(k); } rational _k = k.to_rational(); if (is_lower) bound = m_util.mk_ge(get_enode(v)->get_owner(), m_util.mk_numeral(_k, is_int(v))); else bound = m_util.mk_le(get_enode(v)->get_owner(), m_util.mk_numeral(_k, is_int(v))); } else { if (num_ints > 0) { lcm_den = lcm(lcm_den, denominator(k)); TRACE("gomory_cut_detail", tout << "k: " << k << " lcm_den: " << lcm_den << "\n"; for (unsigned i = 0; i < pol.size(); i++) { tout << pol[i].m_coeff << " " << pol[i].m_var << "\n"; } tout << "k: " << k << "\n";); SASSERT(lcm_den.is_pos()); if (!lcm_den.is_one()) { // normalize coefficients of integer parameters to be integers. unsigned n = pol.size(); for (unsigned i = 0; i < n; i++) { pol[i].m_coeff *= lcm_den; SASSERT(!is_int(pol[i].m_var) || pol[i].m_coeff.is_int()); } k *= lcm_den; } TRACE("gomory_cut_detail", tout << "after *lcm\n"; for (unsigned i = 0; i < pol.size(); i++) { tout << pol[i].m_coeff << " " << pol[i].m_var << "\n"; } tout << "k: " << k << "\n";); } mk_polynomial_ge(pol.size(), pol.c_ptr(), k.to_rational(), bound); } TRACE("gomory_cut", tout << "new cut:\n" << mk_pp(bound, get_manager()) << "\n";); literal l = null_literal; context & ctx = get_context(); ctx.internalize(bound, true); l = ctx.get_literal(bound); ctx.mark_as_relevant(l); ctx.assign(l, ctx.mk_justification( gomory_cut_justification( get_id(), ctx.get_region(), ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante, l))); return true; } /** \brief Return false if the row failed the GCD test, that is, a conflict was detected. \remark if the variables with the least coefficient are bounded, then the ext_gcd_test is invoked. */ template bool theory_arith::gcd_test(row const & r) { if (!m_params.m_arith_gcd_test) return true; m_stats.m_gcd_tests++; numeral lcm_den = r.get_denominators_lcm(); TRACE("gcd_test_bug", r.display(tout); tout << "lcm: " << lcm_den << "\n";); numeral consts(0); numeral gcds(0); numeral least_coeff(0); bool least_coeff_is_bounded = false; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { if (is_fixed(it->m_var)) { // WARNINING: it is not safe to use get_value(it->m_var) here, since // get_value(it->m_var) may not satisfy it->m_var bounds at this point. numeral aux = lcm_den * it->m_coeff; consts += aux * lower_bound(it->m_var).get_rational(); } else if (is_real(it->m_var)) { return true; } else if (gcds.is_zero()) { gcds = abs(lcm_den * it->m_coeff); least_coeff = gcds; least_coeff_is_bounded = is_bounded(it->m_var); } else { numeral aux = abs(lcm_den * it->m_coeff); gcds = gcd(gcds, aux); if (aux < least_coeff) { least_coeff = aux; least_coeff_is_bounded = is_bounded(it->m_var); } else if (least_coeff_is_bounded && aux == least_coeff) { least_coeff_is_bounded = is_bounded(it->m_var); } } SASSERT(gcds.is_int()); SASSERT(least_coeff.is_int()); TRACE("gcd_test_bug", tout << "coeff: " << it->m_coeff << ", gcds: " << gcds << " least_coeff: " << least_coeff << " consts: " << consts << "\n";); } } if (gcds.is_zero()) { // All variables are fixed. // This theory guarantees that the assignment satisfies each row, and // fixed integer variables are assigned to integer values. return true; } if (!(consts / gcds).is_int()) { TRACE("gcd_test", tout << "row failed the GCD test:\n"; display_row_info(tout, r);); antecedents& ante = get_antecedents(); collect_fixed_var_justifications(r, ante); context & ctx = get_context(); ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante.num_params(), ante.params("gcd-test")))); return false; } if (least_coeff.is_one() && !least_coeff_is_bounded) { SASSERT(gcds.is_one()); return true; } if (least_coeff_is_bounded) { return ext_gcd_test(r, least_coeff, lcm_den, consts); } return true; } /** \brief Auxiliary method for gcd_test. */ template bool theory_arith::ext_gcd_test(row const & r, numeral const & least_coeff, numeral const & lcm_den, numeral const & consts) { numeral gcds(0); numeral l(consts); numeral u(consts); antecedents& ante = get_antecedents(); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && !is_fixed(it->m_var)) { theory_var v = it->m_var; SASSERT(!is_real(v)); numeral ncoeff = lcm_den * it->m_coeff; SASSERT(ncoeff.is_int()); numeral abs_ncoeff = abs(ncoeff); if (abs_ncoeff == least_coeff) { SASSERT(is_bounded(v)); if (ncoeff.is_pos()) { // l += ncoeff * lower_bound(v).get_rational(); l.addmul(ncoeff, lower_bound(v).get_rational()); // u += ncoeff * upper_bound(v).get_rational(); u.addmul(ncoeff, upper_bound(v).get_rational()); } else { // l += ncoeff * upper_bound(v).get_rational(); l.addmul(ncoeff, upper_bound(v).get_rational()); // u += ncoeff * lower_bound(v).get_rational(); u.addmul(ncoeff, lower_bound(v).get_rational()); } lower(v)->push_justification(ante, it->m_coeff, coeffs_enabled()); upper(v)->push_justification(ante, it->m_coeff, coeffs_enabled()); } else if (gcds.is_zero()) { gcds = abs_ncoeff; } else { gcds = gcd(gcds, abs_ncoeff); } SASSERT(gcds.is_int()); } } if (gcds.is_zero()) { return true; } numeral l1 = ceil(l/gcds); numeral u1 = floor(u/gcds); if (u1 < l1) { TRACE("gcd_test", tout << "row failed the extended GCD test:\n"; display_row_info(tout, r);); collect_fixed_var_justifications(r, ante); context & ctx = get_context(); ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante.num_params(), ante.params("gcd-test")))); return false; } return true; } /** \brief Return true if all rows pass the GCD test. */ template bool theory_arith::gcd_test() { if (!m_params.m_arith_gcd_test) return true; if (m_eager_gcd) return true; typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v != null_theory_var && is_int(v) && !get_value(v).is_int() && !gcd_test(*it)) { if (m_params.m_arith_adaptive_gcd) m_eager_gcd = true; return false; } } return true; } /** \brief Try to create bounds for unbounded infeasible integer variables. Return false if an inconsistency is detected. */ template bool theory_arith::max_min_infeasible_int_vars() { var_set & already_processed = m_tmp_var_set; already_processed.reset(); svector vars; for (;;) { vars.reset(); // Collect infeasible integer variables. typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v != null_theory_var && is_int(v) && !get_value(v).is_int() && !is_bounded(v) && !already_processed.contains(v)) { vars.push_back(v); already_processed.insert(v); } } if (vars.empty()) return true; if (max_min(vars)) return false; } } /** \brief Try to patch int infeasible vars using freedom intervals. */ template void theory_arith::patch_int_infeasible_vars() { SASSERT(m_to_patch.empty()); int num = get_num_vars(); bool inf_l, inf_u; inf_numeral l, u; numeral m; for (theory_var v = 0; v < num; v++) { if (!is_non_base(v)) continue; get_freedom_interval(v, inf_l, l, inf_u, u, m); if (m.is_one() && get_value(v).is_int()) continue; // check whether value of v is already a multiple of m. if ((get_value(v).get_rational() / m).is_int()) continue; TRACE("patch_int", tout << "TARGET v" << v << " -> ["; if (inf_l) tout << "-oo"; else tout << ceil(l); tout << ", "; if (inf_u) tout << "oo"; else tout << floor(u); tout << "]"; tout << ", m: " << m << ", val: " << get_value(v) << ", is_int: " << is_int(v) << "\n";); if (!inf_l) l = ceil(l); if (!inf_u) u = floor(u); if (!m.is_one()) { if (!inf_l) l = m*ceil(l/m); if (!inf_u) u = m*floor(u/m); } if (!inf_l && !inf_u && l > u) continue; // cannot patch if (!inf_l) set_value(v, l); else if (!inf_u) set_value(v, u); else set_value(v, inf_numeral(0)); } SASSERT(m_to_patch.empty()); } /** \brief Force all non basic variables to be assigned to integer values. */ template void theory_arith::fix_non_base_vars() { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (!is_non_base(v)) continue; if (!is_int(v)) continue; if (get_value(v).is_int()) continue; inf_numeral new_val(floor(get_value(v))); set_value(v, new_val); } if (!make_feasible()) failed(); } template struct theory_arith::euclidean_solver_bridge { typedef numeral_buffer mpz_buffer; theory_arith & t; euclidean_solver m_solver; unsigned_vector m_tv2v; // theory var to euclidean solver var svector m_j2v; // justification to theory var // aux fields unsigned_vector m_xs; mpz_buffer m_as; unsigned_vector m_js; typedef euclidean_solver::var evar; typedef euclidean_solver::justification ejustification; euclidean_solver_bridge(theory_arith & _t):t(_t), m_solver(&t.m_es_num_manager), m_as(m_solver.m()) {} evar mk_var(theory_var v) { m_tv2v.reserve(v+1, UINT_MAX); if (m_tv2v[v] == UINT_MAX) m_tv2v[v] = m_solver.mk_var(); return m_tv2v[v]; } /** \brief Given a monomial, retrieve its coefficient and the power product That is, mon = a * pp */ void get_monomial(expr * mon, rational & a, expr * & pp) { expr * a_expr; if (t.m_util.is_mul(mon, a_expr, pp) && t.m_util.is_numeral(a_expr, a)) return; a = rational(1); pp = mon; } /** \brief Return the theory var associated with the given power product. */ theory_var get_theory_var(expr * pp) { context & ctx = t.get_context(); if (ctx.e_internalized(pp)) { enode * e = ctx.get_enode(pp); if (t.is_attached_to_var(e)) return e->get_th_var(t.get_id()); } return null_theory_var; } /** \brief Create an euclidean_solver variable for the given power product, if it has a theory variable associated with it. */ evar mk_var(expr * pp) { theory_var v = get_theory_var(pp); if (v == null_theory_var) return UINT_MAX; return mk_var(v); } /** \brief Return the euclidean_solver variable associated with the given power product. Return UINT_MAX, if it doesn't have one. */ evar get_var(expr * pp) { theory_var v = get_theory_var(pp); if (v == null_theory_var || v >= static_cast(m_tv2v.size())) return UINT_MAX; return m_tv2v[v]; } void assert_eqs() { // traverse definitions looking for equalities mpz c, a; mpz one; euclidean_solver::numeral_manager & m = m_solver.m(); m.set(one, 1); mpz_buffer & as = m_as; unsigned_vector & xs = m_xs; int num = t.get_num_vars(); for (theory_var v = 0; v < num; v++) { if (!t.is_fixed(v)) continue; if (!t.is_int(v)) continue; // only integer variables expr * n = t.get_enode(v)->get_owner(); if (t.m_util.is_numeral(n)) continue; // skip stupid equality c - c = 0 inf_numeral const & val = t.get_value(v); rational num = val.get_rational().to_rational(); SASSERT(num.is_int()); num.neg(); m.set(c, num.to_mpq().numerator()); ejustification j = m_solver.mk_justification(); m_j2v.reserve(j+1, null_theory_var); m_j2v[j] = v; as.reset(); xs.reset(); bool failed = false; unsigned num_args; expr * const * args; if (t.m_util.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]; expr * pp; rational a_val; get_monomial(arg, a_val, pp); if (!a_val.is_int()) { failed = true; break; } evar x = mk_var(pp); if (x == UINT_MAX) { failed = true; break; } m.set(a, a_val.to_mpq().numerator()); as.push_back(a); xs.push_back(x); } if (!failed) { m_solver.assert_eq(as.size(), as.c_ptr(), xs.c_ptr(), c, j); TRACE("euclidean_solver", tout << "add definition: v" << v << " := " << mk_ismt2_pp(n, t.get_manager()) << "\n";); } else { TRACE("euclidean_solver", tout << "failed for:\n" << mk_ismt2_pp(n, t.get_manager()) << "\n";); } } m.del(a); m.del(c); m.del(one); } void mk_bound(theory_var v, rational k, bool lower, bound * old_bound, unsigned_vector const & js) { derived_bound * new_bound = alloc(derived_bound, v, inf_numeral(k), lower ? B_LOWER : B_UPPER); t.m_tmp_lit_set.reset(); t.m_tmp_eq_set.reset(); if (old_bound != 0) { t.accumulate_justification(*old_bound, *new_bound, numeral(0) /* refine for proof gen */, t.m_tmp_lit_set, t.m_tmp_eq_set); } unsigned_vector::const_iterator it = js.begin(); unsigned_vector::const_iterator end = js.end(); for (; it != end; ++it) { ejustification j = *it; theory_var fixed_v = m_j2v[j]; SASSERT(fixed_v != null_theory_var); t.accumulate_justification(*(t.lower(fixed_v)), *new_bound, numeral(0) /* refine for proof gen */, t.m_tmp_lit_set, t.m_tmp_eq_set); t.accumulate_justification(*(t.upper(fixed_v)), *new_bound, numeral(0) /* refine for proof gen */, t.m_tmp_lit_set, t.m_tmp_eq_set); } t.m_bounds_to_delete.push_back(new_bound); t.m_asserted_bounds.push_back(new_bound); } void mk_lower(theory_var v, rational k, bound * old_bound, unsigned_vector const & js) { mk_bound(v, k, true, old_bound, js); } void mk_upper(theory_var v, rational k, bound * old_bound, unsigned_vector const & js) { mk_bound(v, k, false, old_bound, js); } bool tight_bounds(theory_var v) { SASSERT(!t.is_fixed(v)); euclidean_solver::numeral_manager & m = m_solver.m(); expr * n = t.get_enode(v)->get_owner(); SASSERT(!t.m_util.is_numeral(n)); // should not be a numeral since v is not fixed. bool propagated = false; mpz a; mpz c; rational g; // gcd of the coefficients of the variables that are not in m_solver rational c2; bool init_g = false; mpz_buffer & as = m_as; unsigned_vector & xs = m_xs; as.reset(); xs.reset(); unsigned num_args; expr * const * args; if (t.m_util.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 j = 0; j < num_args; j++) { expr * arg = args[j]; expr * pp; rational a_val; get_monomial(arg, a_val, pp); if (!a_val.is_int()) goto cleanup; evar x = get_var(pp); if (x == UINT_MAX) { a_val = abs(a_val); if (init_g) g = gcd(g, a_val); else g = a_val; init_g = true; if (g.is_one()) goto cleanup; // gcd of the coeffs is one. } else { m.set(a, a_val.to_mpq().numerator()); as.push_back(a); xs.push_back(x); } } m_js.reset(); m_solver.normalize(as.size(), as.c_ptr(), xs.c_ptr(), c, a, c, m_js); if (init_g) { if (!m.is_zero(a)) g = gcd(g, rational(a)); } else { g = rational(a); } TRACE("euclidean_solver", tout << "tightening " << mk_ismt2_pp(t.get_enode(v)->get_owner(), t.get_manager()) << "\n"; tout << "g: " << g << ", a: " << m.to_string(a) << ", c: " << m.to_string(c) << "\n"; t.display_var(tout, v);); if (g.is_one()) goto cleanup; CTRACE("euclidean_solver_zero", g.is_zero(), tout << "tightening " << mk_ismt2_pp(t.get_enode(v)->get_owner(), t.get_manager()) << "\n"; tout << "g: " << g << ", a: " << m.to_string(a) << ", c: " << m.to_string(c) << "\n"; t.display(tout); tout << "------------\nSolver state:\n"; m_solver.display(tout);); if (g.is_zero()) { // The definition of v is equal to (0 + c). // That is, v is fixed at c. // The justification is just m_js, the existing bounds of v are not needed for justifying the new bounds for v. c2 = rational(c); TRACE("euclidean_solver_new", tout << "new fixed: " << c2 << "\n";); propagated = true; mk_lower(v, c2, 0, m_js); mk_upper(v, c2, 0, m_js); } else { TRACE("euclidean_solver", tout << "inequality can be tightned, since all coefficients are multiple of: " << g << "\n";); // Let l and u be the current lower and upper bounds. // Then, the following new bounds can be generated: // // l' := g*ceil((l - c)/g) + c // u' := g*floor((l - c)/g) + c bound * l = t.lower(v); bound * u = t.upper(v); c2 = rational(c); if (l != 0) { rational l_old = l->get_value().get_rational().to_rational(); rational l_new = g*ceil((l_old - c2)/g) + c2; TRACE("euclidean_solver_new", tout << "new lower: " << l_new << " old: " << l_old << "\n"; tout << "c: " << c2 << " ceil((l_old - c2)/g): " << (ceil((l_old - c2)/g)) << "\n";); if (l_new > l_old) { propagated = true; mk_lower(v, l_new, l, m_js); } } if (u != 0) { rational u_old = u->get_value().get_rational().to_rational(); rational u_new = g*floor((u_old - c2)/g) + c2; TRACE("euclidean_solver_new", tout << "new upper: " << u_new << " old: " << u_old << "\n";); if (u_new < u_old) { propagated = true; mk_upper(v, u_new, u, m_js); } } } cleanup: m.del(a); m.del(c); return propagated; } bool tight_bounds() { bool propagated = false; context & ctx = t.get_context(); // try to apply solution set to every definition int num = t.get_num_vars(); for (theory_var v = 0; v < num; v++) { if (t.is_fixed(v)) continue; // skip equations... if (!t.is_int(v)) continue; // skip non integer definitions... if (t.lower(v) == 0 && t.upper(v) == 0) continue; // there is nothing to be tightned if (tight_bounds(v)) propagated = true; if (ctx.inconsistent()) break; } return propagated; } bool operator()() { TRACE("euclidean_solver", t.display(tout);); assert_eqs(); m_solver.solve(); if (m_solver.inconsistent()) { // TODO: set conflict TRACE("euclidean_solver_conflict", tout << "conflict detected...\n"; m_solver.display(tout);); return false; } return tight_bounds(); } }; template bool theory_arith::apply_euclidean_solver() { TRACE("euclidean_solver", tout << "executing euclidean solver...\n";); euclidean_solver_bridge esb(*this); if (esb()) { propagate_core(); return true; } return false; } /** \brief Return FC_DONE if the assignment is int feasible. Otherwise, apply GCD test, branch and bound and Gomory Cuts. */ template final_check_status theory_arith::check_int_feasibility() { TRACE("arith_int_detail", get_context().display(tout);); if (!has_infeasible_int_var()) { TRACE("arith_int_incomp", tout << "FC_DONE 1...\n"; display(tout);); return FC_DONE; } TRACE("arith_int_fracs", int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) { numeral f1 = get_value(v).get_rational() - floor(get_value(v).get_rational()); numeral f2 = ceil(get_value(v).get_rational()) - get_value(v).get_rational(); if (f2 < f1) f1 = f2; tout << "v" << v << " -> " << f1 << " "; display_var(tout, v); } }); TRACE("arith_int_fracs_min_max", numeral max(0); numeral min(1); int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) { numeral f1 = get_value(v).get_rational() - floor(get_value(v).get_rational()); numeral f2 = ceil(get_value(v).get_rational()) - get_value(v).get_rational(); if (f1 < min) min = f1; if (f2 < min) min = f2; if (f1 > max) max = f1; if (f2 > max) max = f2; } } tout << "max: " << max << ", min: " << min << "\n";); if (m_params.m_arith_ignore_int) { TRACE("arith", tout << "Ignore int: give up\n";); return FC_GIVEUP; } if (!gcd_test()) return FC_CONTINUE; if (m_params.m_arith_euclidean_solver) apply_euclidean_solver(); if (get_context().inconsistent()) return FC_CONTINUE; remove_fixed_vars_from_base(); TRACE("arith_int_freedom", int num = get_num_vars(); bool inf_l; bool inf_u; inf_numeral l; inf_numeral u; numeral m; for (theory_var v = 0; v < num; v++) { if (is_non_base(v)) { get_freedom_interval(v, inf_l, l, inf_u, u, m); if ((!m.is_one() /* && !l.is_zero() */) || !get_value(v).is_int()) { tout << "TARGET v" << v << " -> ["; if (inf_l) tout << "-oo"; else tout << ceil(l); tout << ", "; if (inf_u) tout << "oo"; else tout << floor(u); tout << "]"; tout << ", m: " << m << ", val: " << get_value(v) << ", is_int: " << is_int(v) << "\n"; } } }); patch_int_infeasible_vars(); fix_non_base_vars(); if (get_context().inconsistent()) return FC_CONTINUE; TRACE("arith_int_inf", int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) { display_var(tout, v); } }); TRACE("arith_int_rows", unsigned num = 0; typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v == null_theory_var) continue; if (is_int(v) && !get_value(v).is_int()) { num++; display_simplified_row(tout, *it); tout << "\n"; } } tout << "num infeasible: " << num << "\n";); theory_var int_var = find_infeasible_int_base_var(); if (int_var == null_theory_var) { TRACE("arith_int_incomp", tout << "FC_DONE 2...\n"; display(tout);); return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; } #if 0 if (find_bounded_infeasible_int_base_var() == null_theory_var) { // TODO: this is too expensive... I should replace it by a procedure // that refine bounds using the current state of the tableau. if (!max_min_infeasible_int_vars()) return FC_CONTINUE; if (!gcd_test()) return FC_CONTINUE; } #endif m_branch_cut_counter++; // TODO: add giveup code if (m_branch_cut_counter % m_params.m_arith_branch_cut_ratio == 0) { TRACE("opt", display(tout);); move_non_base_vars_to_bounds(); if (!make_feasible()) { TRACE("arith_int", tout << "failed to move variables to bounds.\n";); failed(); return FC_CONTINUE; } theory_var int_var = find_infeasible_int_base_var(); if (int_var != null_theory_var) { TRACE("arith_int", tout << "v" << int_var << " does not have an integer assignment: " << get_value(int_var) << "\n";); SASSERT(is_base(int_var)); row const & r = m_rows[get_var_row(int_var)]; if (!mk_gomory_cut(r)) { // silent failure } return FC_CONTINUE; } } else { if (m_params.m_arith_int_eq_branching && branch_infeasible_int_equality()) { return FC_CONTINUE; } theory_var int_var = find_infeasible_int_base_var(); if (int_var != null_theory_var) { TRACE("arith_int", tout << "v" << int_var << " does not have an integer assignment: " << get_value(int_var) << "\n";); // apply branching branch_infeasible_int_var(int_var); return FC_CONTINUE; } } return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; } }; #endif /* THEORY_ARITH_INT_H_ */ z3-z3-4.4.1/src/smt/theory_arith_inv.h000066400000000000000000000174301260446376700175520ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_inv.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-02. Revision History: --*/ #ifndef THEORY_ARITH_INV_H_ #define THEORY_ARITH_INV_H_ #include"theory_arith.h" #include"ast_pp.h" namespace smt { #ifdef Z3DEBUG template bool theory_arith::check_vector_sizes() const { SASSERT(m_columns.size() == m_data.size()); SASSERT(m_value.size() == m_data.size()); SASSERT(m_old_value.size() == m_data.size()); SASSERT(m_unassigned_atoms.size() == m_data.size()); SASSERT(m_var_pos.size() == m_data.size()); SASSERT(m_bounds[0].size() == m_data.size()); SASSERT(m_bounds[1].size() == m_data.size()); return true; } /** \brief Check whether all entries of m_var_pos are -1 */ template bool theory_arith::check_null_var_pos() const { svector::const_iterator it = m_var_pos.begin(); svector::const_iterator end = m_var_pos.end(); for (; it != end; ++it) { SASSERT(*it == -1); } return true; } /** \brief Return true if the given row has a variable different from r.m_base_var that has the given kind. */ template bool theory_arith::has_var_kind(unsigned r_id, var_kind k) const { row const & r = m_rows[r_id]; theory_var base = r.m_base_var; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && get_var_kind(it->m_var) == k && it->m_var != base) return true; } return false; } /** \brief Return true if the given row is well formed. */ template bool theory_arith::wf_row(unsigned r_id) const { buffer already_found; already_found.resize(get_num_vars(), false); row const & r = m_rows[r_id]; if (r.m_base_var != null_theory_var) { int i = 0; theory_var s = r.m_base_var; SASSERT(is_base(s) || is_quasi_base(s)); CTRACE("arith_bug", !(!is_base(s) || (!has_var_kind(r_id, BASE) && !has_var_kind(r_id, QUASI_BASE))), display_row_info(tout, r_id);); SASSERT(!is_base(s) || (!has_var_kind(r_id, BASE) && !has_var_kind(r_id, QUASI_BASE))); CTRACE("arith_bug", is_quasi_base(s) && has_var_kind(r_id, QUASI_BASE), display_row_info(tout, r_id);); SASSERT(!is_quasi_base(s) || !has_var_kind(r_id, QUASI_BASE)); SASSERT(r.is_coeff_of(s, numeral::one())); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it, ++i) { if (!it->is_dead()) { CTRACE("row_bug", already_found[it->m_var], display_row_info(tout, r_id);); SASSERT(!already_found[it->m_var]); already_found[it->m_var] = true; column const & c = m_columns[it->m_var]; CTRACE("row_bug", it->m_coeff.is_zero(), display_row_info(tout, r_id);); SASSERT(!it->m_coeff.is_zero()); SASSERT(c[it->m_col_idx].m_row_id == static_cast(r_id)); SASSERT(c[it->m_col_idx].m_row_idx == i); } } } return true; } /** \brief Return true if all rows are well formed. */ template bool theory_arith::wf_rows() const { unsigned num = m_rows.size(); for (unsigned r_id = 0; r_id < num; r_id++) { SASSERT(wf_row(r_id)); if (m_rows[r_id].m_base_var == null_theory_var) { SASSERT(std::find(m_dead_rows.begin(), m_dead_rows.end(), r_id) != m_dead_rows.end()); } } return true; } /** \brief Return true if the column associated with v is well formed. */ template bool theory_arith::wf_column(theory_var v) const { column const & c = m_columns[v]; int i = 0; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it, ++i) { if (!it->is_dead()) { row const & r = m_rows[it->m_row_id]; CTRACE("wf_column", r.size() == 0, tout << "v" << v << ", it->m_row_id: " << it->m_row_id << "\n"; display_row_info(tout, r); display(tout);); SASSERT(r.size() != 0); SASSERT(r[it->m_row_idx].m_var == v); SASSERT(r[it->m_row_idx].m_col_idx == i); } } SASSERT(!is_quasi_base(v) || c.size() == 1); return true; } /** \brief Return true if all columns are well formed. */ template bool theory_arith::wf_columns() const { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { SASSERT(wf_column(v)); } return true; } /** \brief Return true if all rows evaluate to zero. \remark Quasi-base rows don't need to be checked. */ template bool theory_arith::valid_row_assignment() const { TRACE("valid_row_assignment", display(tout);); typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { if (it->get_base_var() != null_theory_var) { SASSERT(valid_row_assignment(*it)); } } return true; } template bool theory_arith::valid_row_assignment(row const & r) const { theory_var s = r.get_base_var(); SASSERT(is_base(s) || is_quasi_base(s)); if (s != null_theory_var && is_base(s)) { inf_numeral sum; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { sum += it->m_coeff * m_value[it->m_var]; } } CTRACE("valid_row_assignment_bug", !sum.is_zero(), tout << "checking: "; display_row_info(tout, r);); SASSERT(sum.is_zero()); } return true; } template bool theory_arith::satisfy_bounds() const { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { CTRACE("bound_bug", below_lower(v) || above_upper(v), display_var(tout, v); display(tout);); SASSERT(!below_lower(v)); SASSERT(!above_upper(v)); if (below_lower(v) || above_upper(v)) return false; } return true; } template bool theory_arith::satisfy_integrality() const { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) { TRACE("bound_bug", display_var(tout, v); display(tout);); return false; } } return true; } template bool theory_arith::valid_assignment() const { if (valid_row_assignment() && satisfy_bounds() && satisfy_integrality()) { return true; } TRACE("arith", display(tout);); return false; } #endif }; #endif /* THEORY_ARITH_INV_H_ */ z3-z3-4.4.1/src/smt/theory_arith_nl.h000066400000000000000000003116161260446376700173720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_nl.h Abstract: Author: Leonardo de Moura (leonardo) 2008-12-08. Revision History: --*/ #ifndef THEORY_ARITH_NL_H_ #define THEORY_ARITH_NL_H_ #include"ast_smt2_pp.h" namespace smt { template expr * theory_arith::mk_nary_mul(unsigned sz, expr * const * args, bool is_int) { if (sz == 0) return m_util.mk_numeral(rational(1), is_int); if (sz == 1) return args[0]; if (sz == 2) return m_util.mk_mul(args[0], args[1]); if (m_util.is_numeral(args[0])) return m_util.mk_mul(args[0], m_util.mk_mul(sz - 1, args + 1)); return m_util.mk_mul(sz, args); } template expr * theory_arith::mk_nary_add(unsigned sz, expr * const * args, bool is_int) { if (sz == 0) return m_util.mk_numeral(rational(0), is_int); if (sz == 1) return args[0]; return m_util.mk_add(sz, args); } template expr * theory_arith::mk_nary_add(unsigned sz, expr * const * args) { SASSERT(sz != 0); return mk_nary_add(sz, args, false); } /** \brief Insert v into vars and already_found if v is not already in already_found. */ template void theory_arith::mark_var(theory_var v, svector & vars, var_set & already_found) { if (already_found.contains(v)) return; TRACE("non_linear", tout << "marking: v" << v << "\n";); already_found.insert(v); vars.push_back(v); } /** \brief Invoke mark_var for all variables in rows that contain v. */ template void theory_arith::mark_dependents(theory_var v, svector & vars, var_set & already_found, row_set & already_visited_rows) { if (is_pure_monomial(v)) { expr * n = var2expr(v); SASSERT(m_util.is_mul(n)); for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) { expr * curr = to_app(n)->get_arg(i); theory_var v = expr2var(curr); SASSERT(v != null_theory_var); mark_var(v, vars, already_found); } } if (is_fixed(v)) return; column & c = m_columns[v]; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); for (; it != end; ++it) { if (it->is_dead() || already_visited_rows.contains(it->m_row_id)) continue; TRACE("non_linear_bug", tout << "visiting row: " << it->m_row_id << "\n";); already_visited_rows.insert(it->m_row_id); row & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); // ignore quasi base vars... actually they should not be used if the problem is non linear... if (is_quasi_base(s)) continue; // If s is a base variable different from v and it is free, then this row can be ignored. // It doesn't need to be part of the non linear cluster. For all purposes, this variable // was eliminated by substitution. if (is_free(s) && s != v) continue; typename vector::const_iterator it2 = r.begin_entries(); typename vector::const_iterator end2 = r.end_entries(); for (; it2 != end2; ++it2) { if (!it2->is_dead() && !is_fixed(it2->m_var)) mark_var(it2->m_var, vars, already_found); } } } /** \brief Store in vars the variables that are in the non linear cluster of constraints, and are not satisfied by the current assignment. */ template void theory_arith::get_non_linear_cluster(svector & vars) { if (m_nl_monomials.empty()) return; var_set already_found; row_set already_visited_rows; context & ctx = get_context(); svector::const_iterator it = m_nl_monomials.begin(); svector::const_iterator end = m_nl_monomials.end(); for (; it != end; ++it) { theory_var v = *it; expr * n = var2expr(v); if (ctx.is_relevant(n)) mark_var(v, vars, already_found); } for (unsigned idx = 0; idx < vars.size(); idx++) { TRACE("non_linear", tout << "marking dependents of: v" << vars[idx] << "\n";); mark_dependents(vars[idx], vars, already_found, already_visited_rows); } TRACE("non_linear", tout << "variables in non linear cluster:\n"; svector::const_iterator it = vars.begin(); svector::const_iterator end = vars.end(); for (; it != end; ++it) tout << "v" << *it << " "; tout << "\n";); } /** \brief Return the number of variables that do not have bounds associated with it. The result is 0, 1, or 2. The value 2 means "2 or more". The second value is the idx of the a variable that does not have bounds associated with it. It is only usefull when the first value is 1. The second value is -1 if such variable does not exist, that is, the first value is 0. \remark if a variables has an even number of occurrences, then I consider that it has a bound associated with it. Examples: 1) Assume x1, x4 have bounds: analyze_monomial(x1 * x2 * x2 * x3 * x3 * x3 * x4) --> (1,2) Explanation: x2 doesn't have bounds, but x2 has an even power. So x2*x2 has bound [0, oo). So, there is one variable without bounds x3. It is the third variable in the monomial, then its idx is 2. */ template std::pair theory_arith::analyze_monomial(expr * m) const { SASSERT(is_pure_monomial(m)); expr * var = 0; unsigned power = 0; unsigned c = 0; int free_var_idx = -1; int idx = 0; for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); if (var == 0) { var = arg; power = 1; } else if (arg == var) { power++; } else { if (power % 2 == 1 && is_free(var)) { c++; free_var_idx = idx; if (c > 1) return std::make_pair(2, free_var_idx); } var = arg; power = 1; idx++; } } if (power % 2 == 1 && is_free(var)) { c++; free_var_idx = idx; } return std::make_pair(c, free_var_idx); } /** \brief Given a monomial c*M, return M */ template expr * theory_arith::get_monomial_body(expr * m) const { SASSERT(m_util.is_mul(m)); if (m_util.is_numeral(to_app(m)->get_arg(0))) return to_app(m)->get_arg(1); return m; } /** \brief Given a monomial c*M, return c */ template rational theory_arith::get_monomial_coeff(expr * m) const { SASSERT(m_util.is_mul(m)); rational r; if (m_util.is_numeral(to_app(m)->get_arg(0), r)) return r; return rational(1); } /** \brief Return the number of distinct variables in the given monomial. */ template unsigned theory_arith::get_num_vars_in_monomial(expr * m) const { SASSERT(m_util.is_mul(m)); m = get_monomial_body(m); SASSERT(!m_util.is_numeral(m)); if (m_util.is_mul(m)) { unsigned num_vars = 0; expr * var = 0; for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * curr = to_app(m)->get_arg(i); if (var != curr) { num_vars++; var = curr; } } return num_vars; } else { return 1; } } /** \brief Return the i-th var of m and its power. */ template typename theory_arith::var_power_pair theory_arith::get_var_and_degree(expr * m, unsigned i) const { SASSERT(m_util.is_mul(m)); SASSERT(i < get_num_vars_in_monomial(m)); m = get_monomial_body(m); if (m_util.is_mul(m)) { unsigned curr_idx = 0; expr * var = 0; unsigned power = 0; unsigned j; for (j = 0; j < to_app(m)->get_num_args(); j++) { expr * arg = to_app(m)->get_arg(j); if (var == 0) { var = arg; power = 1; } else if (var == arg) { power++; } else { if (curr_idx == i) return var_power_pair(var, power); curr_idx++; var = arg; power = 1; } } SASSERT(curr_idx == i); return var_power_pair(var, power); } else { SASSERT(i == 0); return var_power_pair(m, 1); } } /** \brief Return an interval using the bounds for v. */ template interval theory_arith::mk_interval_for(theory_var v) { bound * l = lower(v); bound * u = upper(v); if (l && u) { return interval(m_dep_manager, l->get_value().get_rational().to_rational(), !l->get_value().get_infinitesimal().to_rational().is_zero(), m_dep_manager.mk_leaf(l), u->get_value().get_rational().to_rational(), !u->get_value().get_infinitesimal().to_rational().is_zero(), m_dep_manager.mk_leaf(u)); } else if (l) { return interval(m_dep_manager, l->get_value().get_rational().to_rational(), !l->get_value().get_infinitesimal().to_rational().is_zero(), true, m_dep_manager.mk_leaf(l)); } else if (u) { return interval(m_dep_manager, u->get_value().get_rational().to_rational(), !u->get_value().get_infinitesimal().to_rational().is_zero(), false, m_dep_manager.mk_leaf(u)); } else { return interval(m_dep_manager); } } /** \brief Return an interval for the given expression using its bounds. */ template interval theory_arith::mk_interval_for(expr * n) { if (!has_var(n)) return interval(m_dep_manager); return mk_interval_for(expr2var(n)); } /** \brief target *= [lower(var), upper(var)]^power */ template void theory_arith::mul_bound_of(expr * var, unsigned power, interval & target) { theory_var v = expr2var(var); interval i = mk_interval_for(v); TRACE("non_linear", display_interval(tout << "bound: ",i); tout << i << "\n"; tout << mk_pp(var, get_manager()) << "\n"; tout << "power " << power << ": " << expt(i, power) << "\n"; display_interval(tout << "target before: ", target); tout << "\n";); i.expt(power); target *= i; TRACE("non_linear", display_interval(tout << "target after: ", target); tout << "\n";); } /** \brief Evaluate the given expression using interval arithmetic. - If a subexpression is internalized, then mk_interval_for is used to compute its interval. - Only +, *, and numerals are handled. */ template interval theory_arith::evaluate_as_interval(expr * n) { TRACE("nl_evaluate", tout << "evaluating: " << mk_bounded_pp(n, get_manager(), 10) << "\n";); if (has_var(n)) { TRACE("nl_evaluate", tout << "n has a variable associated with it\n";); TRACE("cross_nested_eval_bug", display_nested_form(tout, n); tout << "\ninterval: " << mk_interval_for(n) << "\n"; display_var(tout, expr2var(n));); return mk_interval_for(n); } else if (m_util.is_add(n)) { TRACE("nl_evaluate", tout << "is add\n";); interval r(m_dep_manager, rational(0)); for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) { r += evaluate_as_interval(to_app(n)->get_arg(i)); } TRACE("cross_nested_eval_bug", display_nested_form(tout, n); tout << "\ninterval: " << r << "\n";); return r; } else if (m_util.is_mul(n)) { TRACE("nl_evaluate", tout << "is mul\n";); interval r(m_dep_manager, get_monomial_coeff(n)); unsigned num_vars = get_num_vars_in_monomial(n); for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(n, i); expr * var = p.first; unsigned power = p.second; interval it = evaluate_as_interval(var); it.expt(power); r *= it; } TRACE("cross_nested_eval_bug", display_nested_form(tout, n); tout << "\ninterval: " << r << "\n";); return r; } else { rational val; if (m_util.is_numeral(n, val)) { TRACE("nl_evaluate", tout << "is numeral\n";); return interval(m_dep_manager, val); } else { TRACE("nl_evaluate", tout << "is unknown\n";); return interval(m_dep_manager); } } } template void theory_arith::display_monomial(std::ostream & out, expr * m) const { bool first = true; unsigned num_vars = get_num_vars_in_monomial(m); for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(m, i); SASSERT(p.first != 0); if (first) first = false; else out << " * "; out << mk_bounded_pp(p.first, get_manager()) << "^" << p.second; } } template void theory_arith::dependency2new_bound(v_dependency * dep, derived_bound& new_bound) { ptr_vector bounds; m_dep_manager.linearize(dep, bounds); m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); ptr_vector::const_iterator it = bounds.begin(); ptr_vector::const_iterator end = bounds.end(); for (; it != end; ++it) { bound * b = static_cast(*it); accumulate_justification(*b, new_bound, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); } } /** \brief Create a new derived bound. The justification is stored in the object dep. */ template void theory_arith::mk_derived_nl_bound(theory_var v, inf_numeral const & coeff, bound_kind k, v_dependency * dep) { inf_numeral coeff_norm = normalize_bound(v, coeff, k); derived_bound * new_bound = alloc(derived_bound, v, coeff_norm, k); m_bounds_to_delete.push_back(new_bound); m_asserted_bounds.push_back(new_bound); // copy justification to new bound dependency2new_bound(dep, *new_bound); TRACE("buggy_bound", new_bound->display(*this, tout); tout << "\n";); } /** \brief Update the bounds of v, using the interval i. Return true if i improves the bounds of v. */ template bool theory_arith::update_bounds_using_interval(theory_var v, interval const & i) { SASSERT(v != null_theory_var); bool r = false; if (!i.minus_infinity()) { inf_numeral new_lower(i.get_lower_value()); if (i.is_lower_open()) new_lower += get_epsilon(v); bound * old_lower = lower(v); if (old_lower == 0 || new_lower > old_lower->get_value()) { TRACE("non_linear", tout << "NEW lower bound for v" << v << " " << new_lower << "\n"; display_interval(tout, i); tout << "\n";); mk_derived_nl_bound(v, new_lower, B_LOWER, i.get_lower_dependencies()); r = true; } } if (!i.plus_infinity()) { inf_numeral new_upper(i.get_upper_value()); if (i.is_upper_open()) new_upper -= get_epsilon(v); bound * old_upper = upper(v); if (old_upper == 0 || new_upper < old_upper->get_value()) { TRACE("non_linear", tout << "NEW upper bound for v" << v << " " << new_upper << "\n"; display_interval(tout, i); tout << "\n";); mk_derived_nl_bound(v, new_upper, B_UPPER, i.get_upper_dependencies()); r = true; } } return r; } template bool theory_arith::update_bounds_using_interval(expr * n, interval const & i) { SASSERT(expr2var(n) != null_theory_var); TRACE("non_linear", tout << "NL bounds for m: " << i << "\n" << mk_pp(n, get_manager()) << "\n";); return update_bounds_using_interval(expr2var(n), i); } /** \brief Use the bounds of the variables to build a bound for m. */ template bool theory_arith::propagate_nl_upward(expr * m) { SASSERT(is_pure_monomial(m)); unsigned num_vars = get_num_vars_in_monomial(m); interval new_bounds(m_dep_manager, rational(1)); // TODO: the following code can be improved it is quadratic on the degree of the monomial. TRACE("nl_arith_bug", tout << "processing upward:\n" << mk_pp(m, get_manager()) << "\n";); for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(m, i); expr * var = p.first; unsigned power = p.second; TRACE("nl_arith_bug", tout << "interval before: " << new_bounds << "\n"; theory_var v = expr2var(var); interval i = mk_interval_for(v); display_var(tout, v); tout << "interval for var: " << i << "\n" << mk_pp(var, get_manager()) << "\npower: " << power << " " << expt(i, power) << "\n";); mul_bound_of(var, power, new_bounds); TRACE("nl_arith_bug", tout << "interval after: " << new_bounds << "\n";); } return update_bounds_using_interval(m, new_bounds); } /** \brief Propagate a bound to the i-th variable of the given monomial using the bounds of m and other variables in m. \remark We do not support roots in interval... so, if the i-th var has power != 1 the method returns without doing anything. */ template bool theory_arith::propagate_nl_downward(expr * m, unsigned i) { SASSERT(is_pure_monomial(m)); SASSERT(i < get_num_vars_in_monomial(m)); var_power_pair p = get_var_and_degree(m, i); expr * v = p.first; unsigned power = p.second; TRACE("propagate_nl_downward", tout << "m: " << mk_ismt2_pp(m, get_manager()) << "\nv: " << mk_ismt2_pp(v, get_manager()) << "\npower: " << power << "\n";); if (power != 1) return false; // TODO: remove, when the n-th root is implemented in interval. unsigned num_vars = get_num_vars_in_monomial(m); interval other_bounds(m_dep_manager, rational(1)); // TODO: the following code can be improved it is quadratic on the degree of the monomial. for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(m, i); if (p.first == v) continue; expr * var = p.first; unsigned power = p.second; mul_bound_of(var, power, other_bounds); } if (other_bounds.contains_zero()) return false; // interval division requires that divisor doesn't contain 0. interval r = mk_interval_for(m); r /= other_bounds; return update_bounds_using_interval(v, r); } /** \brief Try to propagate a bound using the given non linear monomial. Return true if some bound was propagated. If i == -1, then use the bound of the variables to propagate a bound for the monomial m. If i != -1, then it is the index of the variable that I will compute bounds for. */ template bool theory_arith::propagate_nl_bound(expr * m, int i) { TRACE("propagate_nl_bound", tout << "propagate using i: " << i << "\n"; display_monomial(tout, m); tout << "\n";); if (i == -1) return propagate_nl_upward(m); else return propagate_nl_downward(m, i); } /** \brief The given monomial and its elements have bounds. Propagate bounds to all of them. Return true if some bound was propagated. */ template bool theory_arith::propagate_nl_bounds(expr * m) { TRACE("non_linear", tout << "propagate several bounds using:\n"; display_monomial(tout, m); tout << "\n";); bool result = propagate_nl_upward(m); unsigned num_vars = get_num_vars_in_monomial(m); for (unsigned i = 0; i < num_vars; i++) if (propagate_nl_downward(m, i)) { m_stats.m_nl_bounds++; result = true; } return result; } /** \brief Try to propagate bounds using non linear monomials. Return true if some bound was propagated. */ template bool theory_arith::propagate_nl_bounds() { m_dep_manager.reset(); bool propagated = false; context & ctx = get_context(); svector::const_iterator it = m_nl_monomials.begin(); svector::const_iterator end = m_nl_monomials.end(); for (; it != end; ++it) { theory_var v = *it; expr * m = var2expr(v); if (!ctx.is_relevant(m)) continue; std::pair p = analyze_monomial(m); TRACE("propagate_nl_bound", tout << "m: " << mk_ismt2_pp(m, get_manager()) << "\n" << "p: " << p.first << " " << p.second << "\n";); unsigned num_bad_vars = p.first; int free_var_idx = p.second; SASSERT(num_bad_vars != 1 || free_var_idx != -1); if (num_bad_vars >= 2) continue; bool is_free_m = is_free(m); TRACE("propagate_nl_bound", tout << "is_free_m: " << is_free_m << "\n";); if (num_bad_vars == 1 && is_free_m) continue; if (num_bad_vars == 0) { if (!is_free_m) { if (propagate_nl_bounds(m)) propagated = true; } else { if (propagate_nl_bound(m, -1)) { m_stats.m_nl_bounds++; propagated = true; } } } else { SASSERT (!is_free_m); if (propagate_nl_bound(m, free_var_idx)) { m_stats.m_nl_bounds++; propagated = true; } } } return propagated; } /** \brief Return the value of v as a rational. If computed_epsilon = false and v has an infinitesimal, then compute_epsilon() is invoked. */ template rational theory_arith::get_value(theory_var v, bool & computed_epsilon) { inf_numeral const & val = get_value(v); if (!val.get_infinitesimal().is_zero() && !computed_epsilon) { compute_epsilon(); computed_epsilon = true; } return val.get_rational().to_rational() + m_epsilon.to_rational() * val.get_infinitesimal().to_rational(); } /** \brief Return true if for the monomial x_1 * ... * x_n associated with v, the following holds: get_value(x_1) * ... * get_value(x_n) = get_value(v) */ template bool theory_arith::check_monomial_assignment(theory_var v, bool & computed_epsilon) { SASSERT(is_pure_monomial(var2expr(v))); expr * m = var2expr(v); rational val(1); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); theory_var curr = expr2var(arg); SASSERT(curr != null_theory_var); val *= get_value(curr, computed_epsilon); } return get_value(v, computed_epsilon) == val; } /** \brief Return true if for every monomial x_1 * ... * x_n, get_value(x_1) * ... * get_value(x_n) = get_value(x_1 * ... * x_n) */ template bool theory_arith::check_monomial_assignments() { bool computed_epsilon = false; context & ctx = get_context(); svector::const_iterator it = m_nl_monomials.begin(); svector::const_iterator end = m_nl_monomials.end(); for (; it != end; ++it) { TRACE("non_linear", tout << "v" << *it << " is relevant: " << ctx.is_relevant(get_enode(*it)) << "\n"; tout << "check_monomial_assignments result: " << check_monomial_assignment(*it, computed_epsilon) << "\n"; tout << "computed_epsilon: " << computed_epsilon << "\n";); if (ctx.is_relevant(get_enode(*it)) && !check_monomial_assignment(*it, computed_epsilon)) { TRACE("non_linear_failed", tout << "check_monomial_assignment failed for:\n" << mk_ismt2_pp(var2expr(*it), get_manager()) << "\n"; display_var(tout, *it);); return false; } } return true; } /** \brief Try to find an integer variable for performing branching in the non linear cluster. The idea is select a variable in a monomial with an invalid assignment. I give preference to variables with small ranges. If no variable is bounded, then select a random one. Free variables are not considered. */ template theory_var theory_arith::find_nl_var_for_branching() { TRACE("nl_branching", tout << "looking for variable to branch...\n"; display(tout);); context & ctx = get_context(); theory_var target = null_theory_var; bool bounded = false; unsigned n = 0; numeral range; svector::const_iterator it = m_nl_monomials.begin(); svector::const_iterator end = m_nl_monomials.end(); for (; it != end; ++it) { theory_var v = *it; if (is_real(v)) continue; bool computed_epsilon = false; bool r = check_monomial_assignment(v, computed_epsilon); SASSERT(!computed_epsilon); // integer variables do not use epsilon if (!r) { expr * m = get_enode(v)->get_owner(); SASSERT(is_pure_monomial(m)); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); theory_var curr = ctx.get_enode(arg)->get_th_var(get_id()); TRACE("nl_branching", tout << "target: v" << target << ", curr: v" << curr << "\n";); if (!is_fixed(curr) && is_int(curr)) { if (is_bounded(curr)) { numeral new_range; new_range = upper_bound(curr).get_rational(); new_range -= lower_bound(curr).get_rational(); if (!bounded || new_range < range) { target = curr; range = new_range; bounded = true; } } else if (!bounded) { n++; TRACE("nl_branching", tout << "n: " << n << "\n";); if (m_random()%n == 0) target = curr; SASSERT(target != null_theory_var); } SASSERT(target != null_theory_var); } TRACE("nl_branching", tout << "after target: v" << target << "\n";); } } } return target; } /** \brief Branch on an integer variable. This method is invoked when v is part of a non linear monomial that is not satisfied by the current assignment. if v >= l, then create the case split v >= l+1 else v <= u, then create the case split v <= u-1 else do nothing and return false. */ template bool theory_arith::branch_nl_int_var(theory_var v) { TRACE("non_linear", tout << "BRANCHING on v" << v << "\n";); m_stats.m_nl_branching++; SASSERT(is_int(v)); expr * bound = 0; if (lower(v)) bound = m_util.mk_le(var2expr(v), m_util.mk_numeral(lower_bound(v).get_rational().to_rational(), true)); else if (upper(v)) bound = m_util.mk_ge(var2expr(v), m_util.mk_numeral(upper_bound(v).get_rational().to_rational(), true)); else bound = m_util.mk_eq(var2expr(v), m_util.mk_numeral(rational(0), true)); TRACE("non_linear", tout << "new bound:\n" << mk_pp(bound, get_manager()) << "\n";); context & ctx = get_context(); ctx.internalize(bound, true); ctx.mark_as_relevant(bound); literal l = ctx.get_literal(bound); SASSERT(!l.sign()); ctx.set_true_first_flag(l.var()); // force the context to case split to true first, independently of the phase selection strategy. return true; } /** \brief Return true if the given monomial is linear. */ template bool theory_arith::is_monomial_linear(expr * m) const { SASSERT(is_pure_monomial(m)); unsigned num_nl_vars = 0; for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); theory_var _var = expr2var(arg); if (!is_fixed(_var)) { num_nl_vars++; } else { if (lower_bound(_var).is_zero()) return true; } } return num_nl_vars <= 1; } /** \brief Return the product of the value of the fixed variables in the monomial m. */ template typename theory_arith::numeral theory_arith::get_monomial_fixed_var_product(expr * m) const { SASSERT(is_pure_monomial(m)); numeral r(1); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); theory_var _var = expr2var(arg); if (is_fixed(_var)) r *= lower_bound(_var).get_rational(); } return r; } /** \brief Return the first non fixed variable in the given monomial. Return 0, if the monomial does not have a non fixed variable. */ template expr * theory_arith::get_monomial_non_fixed_var(expr * m) const { SASSERT(is_pure_monomial(m)); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); theory_var _var = expr2var(arg); if (!is_fixed(_var)) return arg; } return 0; } /** \brief Propagate linear monomial. Check whether the give monomial became linear and propagate. */ template bool theory_arith::propagate_linear_monomial(theory_var v) { TRACE("non_linear", tout << "checking whether v" << v << " became linear...\n";); if (m_data[v].m_nl_propagated) return false; // already propagated this monomial. expr * m = var2expr(v); if (!is_monomial_linear(m)) return false; // monomial is not linear. m_stats.m_nl_linear++; m_data[v].m_nl_propagated = true; m_nl_propagated.push_back(v); TRACE("non_linear", tout << "v" << v << " is linear " << mk_pp(m, get_manager()) << "\n";); numeral k = get_monomial_fixed_var_product(m); TRACE("non_linear", tout << "new linear monomial... k: " << k << "\n";); expr * x_n = k.is_zero() ? 0 : get_monomial_non_fixed_var(m); TRACE("non_linear_bug", if (x_n != 0) { tout << "x_n: " << mk_bounded_pp(x_n, get_manager()) << "\nx_n: #" << x_n->get_id() << "\n"; }); context & ctx = get_context(); derived_bound * new_lower = 0; derived_bound * new_upper = 0; if (x_n != 0) { // All but one of the x_i variables are assigned. // Let x_n be the unassigned variable. // Then, we know that x_1*...*x_n = k*x_n, where k is the product of beta(x_1)*...*beta(x_{n-1}) // beta(x_i) == lower(x_i) // Let m be (* x_1 ... x_n), then assert equality // (= (+ (* x_1 ... x_n) (* -k x_n)) 0) when x_1 ... x_{n-1} are fixed variables. // where k = lower(x_1)*...*lower(x_{n-1}) TRACE("non_linear", tout << "x_n: " << mk_pp(x_n, get_manager()) << "\n";); k.neg(); expr * k_x_n = k.is_one() ? x_n : m_util.mk_mul(m_util.mk_numeral(k.to_rational(), is_int(v)), x_n); expr * rhs = m_util.mk_add(m, k_x_n); TRACE("non_linear_bug", tout << "rhs: " << mk_bounded_pp(rhs, get_manager(),5) << "\ninternalized: " << ctx.e_internalized(rhs) << "\n";); if (!has_var(rhs)) { ctx.internalize(rhs, false); ctx.mark_as_relevant(rhs); } TRACE("non_linear_bug", tout << "enode: " << get_context().get_enode(rhs) << " enode_id: " << get_context().get_enode(rhs)->get_owner_id() << "\n";); theory_var new_v = expr2var(rhs); TRACE("non_linear_bug", ctx.display(tout);); SASSERT(new_v != null_theory_var); new_lower = alloc(derived_bound, new_v, inf_numeral(0), B_LOWER); new_upper = alloc(derived_bound, new_v, inf_numeral(0), B_UPPER); } else { // One of the x_i variables is zero, // or all of them are assigned. // Assert the equality // (= (* x_1 ... x_n) k) TRACE("non_linear", tout << "all variables are fixed.\n";); new_lower = alloc(derived_bound, v, inf_numeral(k), B_LOWER); new_upper = alloc(derived_bound, v, inf_numeral(k), B_UPPER); } SASSERT(new_lower != 0); SASSERT(new_upper != 0); m_bounds_to_delete.push_back(new_lower); m_asserted_bounds.push_back(new_lower); m_bounds_to_delete.push_back(new_upper); m_asserted_bounds.push_back(new_upper); // Add the justification for new_lower and new_upper. // The justification is the lower and upper bounds of all fixed variables. m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); SASSERT(is_pure_monomial(m)); bool found_zero = false; for (unsigned i = 0; !found_zero && i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); theory_var _var = expr2var(arg); if (is_fixed(_var)) { bound * l = lower(_var); bound * u = upper(_var); if (l->get_value().is_zero()) { /* if zero was found, then it is the explanation */ SASSERT(k.is_zero()); found_zero = true; m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); new_lower->m_lits.reset(); new_lower->m_eqs.reset(); } accumulate_justification(*l, *new_lower, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); TRACE("non_linear", for (unsigned j = 0; j < new_lower->m_lits.size(); ++j) { ctx.display_detailed_literal(tout, new_lower->m_lits[j]); tout << " "; } tout << "\n";); accumulate_justification(*u, *new_lower, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); TRACE("non_linear", for (unsigned j = 0; j < new_lower->m_lits.size(); ++j) { ctx.display_detailed_literal(tout, new_lower->m_lits[j]); tout << " "; } tout << "\n";); } } new_upper->m_lits.append(new_lower->m_lits); new_upper->m_eqs.append(new_lower->m_eqs); TRACE("non_linear", tout << "lower: " << new_lower << " upper: " << new_upper << "\n"; for (unsigned j = 0; j < new_upper->m_lits.size(); ++j) { ctx.display_detailed_literal(tout, new_upper->m_lits[j]); tout << " "; } tout << "\n";); return true; } /** \brief Traverse all non linear monomials, and check the ones that became linear and propagate. Return true if propagated. */ template bool theory_arith::propagate_linear_monomials() { TRACE("non_linear", tout << "propagating linear monomials...\n";); bool p = false; svector::const_iterator it = m_nl_monomials.begin(); svector::const_iterator end = m_nl_monomials.end(); for (; it != end; ++it) { theory_var v = *it; if (propagate_linear_monomial(v)) p = true; } CTRACE("non_linear", p, display(tout);); return p; } /* Interval arithmetic does not satisfy distributivity. Actually, it satisfies the sub-distributivity property: x*(y + z) \subseteq x*y + x*z The sub-distributivity property only holds if condensation is not used. For example: x * (x^3 + 1) \subseteq x*x^3 + x, but it is not the case that x * (x^3 + 1) \subseteq x^4 + x for example, for x = [-2,1] x*(x^3+1) = [-7, 14] x^4 + x = [-2, 17] This weakness of AI is known as the "dependency problem", which comes from the decorrelation of the multiple occurrences of one variable during interval evaluation. Given a polynomial: p(x) = a_0 + a_1 * x + ... + a_n * x^n The horner extension is: h_p(x) = a_0 + x*(a_1 + ... + x*(a_{n-1} + a_n * x) + ...) The horner extension of p(x) = x^4 + x^3 + 2*x is: h_p(x) = x(2 + x^3(1 + x)) The horner extension evaluates tighter intervals when condensation is not used. Remark: there is no guarantee that horner extension will provide a tighter interval than a sum of monomials when condensation is used. For multivariate polynomials nested (or cross nested) forms are used. The idea is to select one variable, and pretend the other are parameters. The horner form is computed for the selected variable, and the computation continues for the polynomials on the parameters. As described above, the horner form is not optimal with respect to to condensation. I use the following two properties to deal with monovariate polynomials with two monomials: p(x) = a*x^n + b*x^{n+m} for n >= m is equivalent to b*x^{n-m}*[(x^{m} + a/(2b))^2 - (a/2b)^2] This polynomial provides tight bound when n and m have the same parity and: 1) a*b > 0 and (lower(x) >= 0 or upper(x)^m <= -a/b) 2) a*b < 0 and (upper(x) <= 0 or lower(x)^m >= a/b) This polynomial also provides tight bounds when n = m, and the polynomial is simplified to, and n and m may have arbitrary parities: b*[(x^{n} + a/(2b))^2 - (a/2b)^2] Example: x > 1 x^2 - x <= 0 is unsatisfiable If we compute the bounds for x^2 - x we obtain (-oo, oo). On the other hand, if we compute the bounds for (x - 1/2)^2 - 1/4 we obtain the bounds (0, oo), and the inconsistency is detected. Remark: In Z3, I condensate multiple occurrences of a variable when evaluating monomials. So, the interval for a monomial is always tight. Remark: M1*(M2 + M3) is more precise than M1 * M2 + M1 * M3, if intersection(Vars(M1), union(Vars(M2), Vars(M3))) = empty-set, Remark: A trivial consequence of Moore's theorem for interval arithmetic. If two monomials M1 and M2 do not share variables, then the interval for M1 + M2 is tight. */ /** \brief Check whether the same variable occurs in two different monomials. \remark Fixed variables are ignored. \remark A trivial consequence of Moore's theorem for interval arithmetic. If two monomials M1 and M2 do not share variables, then the interval for M1 + M2 is tight. */ template bool theory_arith::is_problematic_non_linear_row(row const & r) { m_tmp_var_set.reset(); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; if (is_fixed(v)) continue; if (is_pure_monomial(v)) { expr * m = var2expr(v); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { theory_var curr = expr2var(to_app(m)->get_arg(i)); if (m_tmp_var_set.contains(curr)) return true; } SASSERT(m == var2expr(v)); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { theory_var curr = expr2var(to_app(m)->get_arg(i)); if (!is_fixed(curr)) m_tmp_var_set.insert(curr); } } else { if (m_tmp_var_set.contains(v)) return true; SASSERT(!is_fixed(v)); m_tmp_var_set.insert(v); } } } return false; } /** \brief Return true if the row mixes real and integer variables. This kind of row cannot be converted back to an expression, since expressions in Z3 cannot have mixed sorts. */ template bool theory_arith::is_mixed_real_integer(row const & r) const { bool found_int = false; bool found_real = false; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; theory_var v = it->m_var; // TODO: possible improvement... ignore fixed variables. // If we implement this improvement, we are actually changing the contract of this function // and we will also have to fix the affected functions. if (is_int(v)) found_int = true; if (is_real(v)) found_real = true; if (found_int && found_real) return true; } return false; } /** \brief Return true if the row contains only integer variables. */ template bool theory_arith::is_integer(row const & r) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; theory_var v = it->m_var; // TODO: possible improvement... ignore fixed variables. if (!is_int(v)) return false; } return true; } template void theory_arith::display_coeff_exprs(std::ostream & out, sbuffer const & p) const { typename sbuffer::const_iterator it = p.begin(); typename sbuffer::const_iterator end = p.end(); for (bool first = true; it != end; ++it) { if (first) first = false; else out << "+\n"; out << it->first << " * " << mk_pp(it->second, get_manager()) << "\n"; } } /** \brief Traverse p and store in vars the (non-fixed) variables that occur in more than one monomial. The number of occurrences is also stored. */ template void theory_arith::get_polynomial_info(sbuffer const & p, sbuffer & varinfo) { context & ctx = get_context(); varinfo.reset(); m_var2num_occs.reset(); #define ADD_OCC(VAR) if (has_var(VAR) && !is_fixed(expr2var(VAR))) { \ TRACE("nl_info", tout << "adding occ: " << mk_bounded_pp(VAR, get_manager()) << "\n";); \ unsigned occs = 0; \ m_var2num_occs.find(VAR, occs); \ occs++; \ m_var2num_occs.insert(VAR, occs); \ } typename sbuffer::const_iterator it = p.begin(); typename sbuffer::const_iterator end = p.end(); for (; it != end; ++it) { expr * m = it->second; if (is_pure_monomial(m)) { unsigned num_vars = get_num_vars_in_monomial(m); for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(m, i); ADD_OCC(p.first); } } else if (m_util.is_numeral(m)) { continue; } else if (ctx.e_internalized(m)) { ADD_OCC(m); } else { TRACE("non_linear", tout << mk_pp(m, get_manager()) << "\n";); UNREACHABLE(); } } // Update the number of occurrences in the result vector. typename var2num_occs::iterator it2 = m_var2num_occs.begin(); typename var2num_occs::iterator end2 = m_var2num_occs.end(); for (; it2 != end2; ++it2) { if ((*it2).m_value > 1) varinfo.push_back(var_num_occs((*it2).m_key, (*it2).m_value)); } } /** \brief Convert p into an expression. */ template expr * theory_arith::p2expr(sbuffer & p) { SASSERT(!p.empty()); TRACE("p2expr_bug", display_coeff_exprs(tout, p);); ptr_buffer args; sbuffer::const_iterator it = p.begin(); sbuffer::const_iterator end = p.end(); for (; it != end; ++it) { rational const & c = it->first; expr * var = it->second; if (!c.is_one()) { rational c2; expr * m = 0; if (m_util.is_numeral(var, c2)) m = m_util.mk_numeral(c*c2, m_util.is_int(var)); else m = m_util.mk_mul(m_util.mk_numeral(c, m_util.is_int(var)), var); m_nl_new_exprs.push_back(m); args.push_back(m); } else { args.push_back(var); } } SASSERT(!args.empty()); expr * r = mk_nary_add(args.size(), args.c_ptr()); m_nl_new_exprs.push_back(r); return r; } /** \brief Return expression representing: var^power */ template expr * theory_arith::power(expr * var, unsigned power) { SASSERT(power > 0); expr * r = var; for (unsigned i = 1; i < power; i++) r = m_util.mk_mul(var, r); m_nl_new_exprs.push_back(r); return r; } /** \brief Return true if var only occurs in two monovariate monomials, and return its power and coefficients and these monomials. The arguments i1 and i2 contain the position in p of the two monomials. */ template bool theory_arith::in_monovariate_monomials(sbuffer & p, expr * var, unsigned & i1, rational & c1, unsigned & n1, unsigned & i2, rational & c2, unsigned & n2) { int idx = 0; #define SET_RESULT(POWER) { \ if (idx == 0) { \ c1 = it->first; \ n1 = POWER; \ idx = 1; \ i1 = i; \ } \ else if (idx == 1) { \ c2 = it->first; \ n2 = POWER; \ idx = 2; \ i2 = i; \ } \ else \ return false; \ } typename sbuffer::const_iterator it = p.begin(); typename sbuffer::const_iterator end = p.end(); for (unsigned i = 0; it != end; ++it, ++i) { expr * m = it->second; if (is_pure_monomial(m)) { unsigned num_vars = get_num_vars_in_monomial(m); for (unsigned j = 0; j < num_vars; j++) { var_power_pair p = get_var_and_degree(m, j); if (p.first == var) { if (num_vars > 1) return false; SET_RESULT(p.second); } } } else if (m == var) { SET_RESULT(1); } } if (idx != 2) return false; return true; } /** \brief Diplay a nested form expression */ template void theory_arith::display_nested_form(std::ostream & out, expr * p) { if (has_var(p)) { out << "#" << p->get_id(); } else if (m_util.is_add(p)) { SASSERT(!has_var(p)); out << "("; for (unsigned i = 0; i < to_app(p)->get_num_args(); i++) { if (i > 0) out << " + "; display_nested_form(out, to_app(p)->get_arg(i)); } out << ")"; } else if (m_util.is_mul(p)) { rational c = get_monomial_coeff(p); bool first = true; if (!c.is_one()) { out << c; first = false; } unsigned num_vars = get_num_vars_in_monomial(p); for (unsigned i = 0; i < num_vars; i++) { if (first) first = false; else out << "*"; var_power_pair pair = get_var_and_degree(p, i); expr * var = pair.first; unsigned power = pair.second; display_nested_form(out, var); if (power != 1) out << "^" << power; } } else { rational val; if (m_util.is_numeral(p, val)) out << val; else out << "[unknown #" << p->get_id() << "]"; } } /** \brief Return the degree of var in m. */ template unsigned theory_arith::get_degree_of(expr * m, expr * var) { if (m == var) return 1; if (is_pure_monomial(m)) { unsigned num_vars = get_num_vars_in_monomial(m); for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(m, i); if (p.first == var) return p.second; } } return 0; } /** \brief Return the minimal degree of var in the polynomial p. */ template unsigned theory_arith::get_min_degree(sbuffer & p, expr * var) { SASSERT(!p.empty()); SASSERT(var != 0); // get monomial where the degree of var is min. unsigned d = UINT_MAX; // min. degree of var sbuffer::const_iterator it = p.begin(); sbuffer::const_iterator end = p.end(); for (; it != end; ++it) { expr * m = it->second; d = std::min(d, get_degree_of(m, var)); if (d == 0) return d; } SASSERT(d != UINT_MAX); return d; } /** \brief Divide m by var^d. */ template expr * theory_arith::factor(expr * m, expr * var, unsigned d) { TRACE("factor", tout << "m: " << mk_pp(m, get_manager()) << "\nvar: " << mk_pp(var, get_manager()) << "\nd: " << d << "\n";); if (d == 0) return m; if (m == var) { SASSERT(d == 1); expr * result = m_util.mk_numeral(rational(1), m_util.is_int(var)); m_nl_new_exprs.push_back(result); return result; } SASSERT(is_pure_monomial(m)); unsigned idx = 0; ptr_buffer new_args; for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); if (arg == var) { if (idx < d) idx++; else new_args.push_back(arg); } else { new_args.push_back(arg); } } SASSERT(idx == d); TRACE("factor_bug", tout << "new_args:\n"; for(unsigned i = 0; i < new_args.size(); i++) tout << mk_pp(new_args[i], get_manager()) << "\n";); expr * result = mk_nary_mul(new_args.size(), new_args.c_ptr(), m_util.is_int(var)); m_nl_new_exprs.push_back(result); TRACE("factor", tout << "result: " << mk_pp(result, get_manager()) << "\n";); return result; } /** \brief Return the horner extension of p with respect to var. */ template expr * theory_arith::horner(sbuffer & p, expr * var) { SASSERT(!p.empty()); SASSERT(var != 0); unsigned d = get_min_degree(p, var); TRACE("horner_bug", tout << "poly:\n"; for (unsigned i = 0; i < p.size(); i++) { if (i > 0) tout << " + "; tout << p[i].first << "*" << mk_pp(p[i].second, get_manager()); } tout << "\n"; tout << "var: " << mk_pp(var, get_manager()) << "\n"; tout << "min_degree: " << d << "\n";); sbuffer e; // monomials/x^d where var occurs with degree d sbuffer r; // rest sbuffer::const_iterator it = p.begin(); sbuffer::const_iterator end = p.end(); for (; it != end; ++it) { expr * m = it->second; expr * f = factor(m, var, d); if (get_degree_of(m, var) == d) { e.push_back(coeff_expr(it->first, f)); } else { SASSERT(get_degree_of(m, var) > d); r.push_back(coeff_expr(it->first, f)); } } expr * s = cross_nested(e, 0); if (!r.empty()) { expr * q = horner(r, var); // TODO: improve here s = m_util.mk_add(q, s); } expr * result = s; if (d != 0) { expr * xd = power(var, d); result = m_util.mk_mul(xd, s); } m_nl_new_exprs.push_back(result); return result; } /** \brief Convert the polynomial p into an equivalent cross nested expression. The idea is to obtain an expression e where evaluate_as_interval(e) is more precise than evaluate_as_interval(p). If var != 0, then it is used for performing the horner extension */ template expr * theory_arith::cross_nested(sbuffer & p, expr * var) { TRACE("non_linear", tout << "p.size: " << p.size() << "\n";); if (var == 0) { sbuffer varinfo; get_polynomial_info(p, varinfo); if (varinfo.empty()) return p2expr(p); sbuffer::const_iterator it = varinfo.begin(); sbuffer::const_iterator end = varinfo.end(); var = it->first; unsigned max = it->second; ++it; for (; it != end; ++it) { if (it->second > max) { var = it->first; max = it->second; } } } SASSERT(var != 0); unsigned i1 = UINT_MAX; unsigned i2 = UINT_MAX; rational a, b; unsigned n = UINT_MAX; unsigned nm = UINT_MAX; if (in_monovariate_monomials(p, var, i1, a, n, i2, b, nm)) { CTRACE("in_monovariate_monomials", n == nm, for (unsigned i = 0; i < p.size(); i++) { if (i > 0) tout << " + "; tout << p[i].first << "*" << mk_pp(p[i].second, get_manager()); } tout << "\n"; tout << "var: " << mk_pp(var, get_manager()) << "\n"; tout << "i1: " << i1 << "\n"; tout << "a: " << a << "\n"; tout << "n: " << n << "\n"; tout << "i2: " << i2 << "\n"; tout << "b: " << b << "\n"; tout << "nm: " << nm << "\n";); SASSERT(n != nm); expr * new_expr = 0; if (nm < n) { std::swap(n, nm); std::swap(a, b); } SASSERT(nm > n); unsigned m = nm - n; if (n % 2 == m % 2 && n >= m) { // b*x^{n-m}*[(x^{m} + a/(2b))^2 - (a/2b)^2] // b*[(x^{m} + a/(2b))^2 - (a/2b)^2] for n == m rational a2b = a; expr * xm = power(var, m); a2b /= (rational(2) * b); // we cannot create a numeral that has sort int, but it is a rational. if (!m_util.is_int(var) || a2b.is_int()) { rational ma2b2 = a2b * a2b; ma2b2.neg(); expr * xm_a2b = m_util.mk_add(m_util.mk_numeral(a2b, m_util.is_int(var)), xm); expr * xm_a2b2 = m_util.mk_mul(xm_a2b, xm_a2b); expr * rhs = m_util.mk_add(xm_a2b2, m_util.mk_numeral(ma2b2, m_util.is_int(var))); expr * rhs2 = 0; if (n > m) rhs2 = m_util.mk_mul(power(var, n - m), rhs); else rhs2 = rhs; new_expr = b.is_one() ? rhs2 : m_util.mk_mul(m_util.mk_numeral(b, m_util.is_int(var)), rhs2); m_nl_new_exprs.push_back(new_expr); TRACE("non_linear", tout << "new_expr:\n"; display_nested_form(tout, new_expr); tout << "\n";); sbuffer rest; unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { if (i != i1 && i != i2) rest.push_back(p[i]); } if (rest.empty()) return new_expr; TRACE("non_linear", tout << "rest size: " << rest.size() << ", i1: " << i1 << ", i2: " << i2 << "\n";); expr * h = cross_nested(rest, 0); expr * r = m_util.mk_add(new_expr, h); m_nl_new_exprs.push_back(r); return r; } } } return horner(p, var); } /** \brief Check whether the given polynomial is consistent with respect to the known bounds. The polynomial is converted into an equivalent cross nested form. */ template bool theory_arith::is_cross_nested_consistent(sbuffer & p) { sbuffer varinfo; get_polynomial_info(p, varinfo); if (varinfo.empty()) return true; std::stable_sort(varinfo.begin(), varinfo.end(), var_num_occs_lt()); TRACE("cross_nested", tout << "var num occs:\n"; sbuffer::const_iterator it = varinfo.begin(); sbuffer::const_iterator end = varinfo.end(); for (; it != end ; ++it) { tout << mk_bounded_pp(it->first, get_manager()) << " -> " << it->second << "\n"; }); sbuffer::const_iterator it = varinfo.begin(); sbuffer::const_iterator end = varinfo.end(); for (; it != end; ++it) { m_nl_new_exprs.reset(); expr * var = it->first; expr * cn = cross_nested(p, var); // Remark: cn may not be well-sorted because, since a row may contain mixed integer/real monomials. // This is not really a problem, since evaluate_as_interval will work even if cn is not well-sorted. if (!cn) continue; TRACE("cross_nested", tout << "nested form for var:\n" << mk_ismt2_pp(var, get_manager()) << "\n"; display_nested_form(tout, cn); tout << "\n"; tout << "c:\n" << mk_ismt2_pp(cn, get_manager()) << "\n";); interval i = evaluate_as_interval(cn); TRACE("cross_nested", tout << "interval: " << i << "\n";); v_dependency * d = 0; if (!i.minus_infinity() && (i.get_lower_value().is_pos() || (i.get_lower_value().is_zero() && i.is_lower_open()))) d = i.get_lower_dependencies(); else if (!i.plus_infinity() && (i.get_upper_value().is_neg() || (i.get_upper_value().is_zero() && i.is_upper_open()))) d = i.get_upper_dependencies(); if (d) { TRACE("cross_nested", tout << "nested form conflict: " << i << "\n";); set_conflict(d); return false; } } return true; } /** \brief Check whether the polynomial represented by the current row is consistent with respect to the known bound when converted into a equivalent cross nested form. */ template bool theory_arith::is_cross_nested_consistent(row const & r) { TRACE("cross_nested", tout << "is_cross_nested_consistent:\n"; display_row(tout, r, false);); if (!is_problematic_non_linear_row(r)) return true; TRACE("cross_nested", tout << "problematic...\n";); /* The method is_cross_nested converts rows back to expressions. The conversion back to expressions may create sort incorrect expressions. This is in some sense ok, since these expressions are temporary, but the sort incorrect expressions may generate assertion violations. Sort incorrect expressions may be created in the following cases: 1) mixed real int rows. 2) int rows that contain non integer coefficients. 3) int rows that when converted to cross nested form use non integer coefficients. There are several ways to deal with this problem: a) Ignore the assertion violations. Disadvantage: it will prevent us from running Z3 in debug mode on some benchmarks. b) Remove the assertions. Disadvantage: these assertions helped us to find many important bugs in Z3 c) Disable the assertions temporally. This sounds like a big HACK. d) Use a different data-structure to represent polynomials in cross-nested form. Disadvantage: code duplication, the data-structure is essentially identical to the ASTs we are using right now. e) Disable the test when we cannot create a well-sorted expression. I'm temporally using this solution. I implemented the following logic: 1) (mixed real int) Disable the test. Most benchmarks do not contain mixed int real variables. 2) (int coeffs) I multiply the row by a constant to force it to have only integer coefficients. 3) (new non-int coeffs) This only happens in an optional step in the conversion. Now, for int rows, I only apply this optional step only if non-int coeffs are not created. */ if (is_mixed_real_integer(r)) return true; // giving up... see comment above TRACE("cross_nested", tout << "cheking problematic row...\n";); rational c = rational::one(); if (is_integer(r)) c = r.get_denominators_lcm().to_rational(); TRACE("non_linear", tout << "check problematic row:\n"; display_row(tout, r); display_row(tout, r, false);); sbuffer p; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) p.push_back(coeff_expr(it->m_coeff.to_rational() * c, var2expr(it->m_var))); } SASSERT(!p.empty()); CTRACE("cross_nested_bug", !c.is_one(), tout << "c: " << c << "\n"; display_row(tout, r); tout << "---> p (coeffs, exprs):\n"; display_coeff_exprs(tout, p);); return is_cross_nested_consistent(p); } /** \brief Check whether an inconsistency can be found using cross nested form in the non linear cluster. */ template bool theory_arith::is_cross_nested_consistent(svector const & nl_cluster) { svector::const_iterator it = nl_cluster.begin(); svector::const_iterator end = nl_cluster.end(); for (; it != end; ++it) { theory_var v = *it; if (!is_base(v)) continue; m_stats.m_nl_cross_nested++; row const & r = m_rows[get_var_row(v)]; if (!is_cross_nested_consistent(r)) return false; } return true; } #define FIXED 0 #define QUOTED_FIXED 1 #define BOUNDED 2 #define QUOTED_BOUNDED 3 #define NOT_FREE 4 #define QUOTED_NOT_FREE 5 #define FREE 6 #define QUOTED_FREE 7 #define MAX_DEFAULT_WEIGHT 7 /** \brief Initialize variable order for grobner basis computation. Make: "quoted free vars" > "free vars" > "quoted variables with lower or upper bounds" > "variables with lower or upper bounds" > "quoted bounded variables" > "bounded variables" > "quoted fixed variables" > "fixed variables" */ template void theory_arith::init_grobner_var_order(svector const & nl_cluster, grobner & gb) { // Initialize variable order svector::const_iterator it = nl_cluster.begin(); svector::const_iterator end = nl_cluster.end(); for (; it != end; ++it) { theory_var v = *it; expr * var = var2expr(v); if (is_fixed(v)) { gb.set_weight(var, is_pure_monomial(var) ? QUOTED_FIXED : FIXED); } else if (is_bounded(v)) { gb.set_weight(var, is_pure_monomial(var) ? QUOTED_BOUNDED : BOUNDED); } else if (lower(v) || upper(v)) { gb.set_weight(var, is_pure_monomial(var) ? QUOTED_NOT_FREE : NOT_FREE); } else { SASSERT(is_free(v)); gb.set_weight(var, is_pure_monomial(var) ? QUOTED_FREE : FREE); } } } /** \brief Create a new monomial using the given coeff and m. Fixed variables in m are substituted by their values. The arg dep is updated to store these dependencies. The set already_found is updated with the fixed variables in m. A variable is only added to dep if it is not already in already_found. Return null if the monomial was simplied to 0. */ template grobner::monomial * theory_arith::mk_gb_monomial(rational const & _coeff, expr * m, grobner & gb, v_dependency * & dep, var_set & already_found) { ptr_buffer vars; rational coeff = _coeff; rational r; #undef PROC_VAR #define PROC_VAR(VAR) { \ if (m_util.is_numeral(VAR, r)) { \ coeff *= r; \ } \ else { \ theory_var _var = expr2var(VAR); \ if (is_fixed(_var)) { \ if (!already_found.contains(_var)) { \ already_found.insert(_var); \ dep = m_dep_manager.mk_join(dep, m_dep_manager.mk_join(m_dep_manager.mk_leaf(lower(_var)), m_dep_manager.mk_leaf(upper(_var)))); \ } \ coeff *= lower_bound(_var).get_rational().to_rational(); \ } \ else { \ vars.push_back(VAR); \ } \ } \ } if (m_util.is_mul(m)) { coeff *= get_monomial_coeff(m); m = get_monomial_body(m); if (m_util.is_mul(m)) { SASSERT(is_pure_monomial(m)); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); PROC_VAR(arg); } } else { PROC_VAR(m); } } else { PROC_VAR(m); } if (!coeff.is_zero()) return gb.mk_monomial(coeff, vars.size(), vars.c_ptr()); else return 0; } /** \brief Send the given row to the grobner basis object. All fixed variables are substituted before sending the row to gb. */ template void theory_arith::add_row_to_gb(row const & r, grobner & gb) { TRACE("non_linear", tout << "adding row to gb\n"; display_row(tout, r);); ptr_buffer monomials; v_dependency * dep = 0; m_tmp_var_set.reset(); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { rational coeff = it->m_coeff.to_rational(); expr * m = var2expr(it->m_var); TRACE("non_linear", tout << "monomial: " << mk_pp(m, get_manager()) << "\n";); grobner::monomial * new_m = mk_gb_monomial(coeff, m, gb, dep, m_tmp_var_set); TRACE("non_linear", tout << "new monomial:\n"; if (new_m) gb.display_monomial(tout, *new_m); else tout << "null"; tout << "\n";); if (new_m) monomials.push_back(new_m); } } gb.assert_eq_0(monomials.size(), monomials.c_ptr(), dep); } /** \brief v must be a pure monomial. That is, v = (quote (* x_1 ... x_n)) Add the monomial (quote (* x_1 ... x_n)) = x_1 * ... * x_n. Fixed variables are substituted. */ template void theory_arith::add_monomial_def_to_gb(theory_var v, grobner & gb) { ptr_buffer monomials; v_dependency * dep = 0; m_tmp_var_set.reset(); expr * m = var2expr(v); SASSERT(is_pure_monomial(m)); grobner::monomial * new_m = mk_gb_monomial(rational(1), m, gb, dep, m_tmp_var_set); if (new_m) monomials.push_back(new_m); rational coeff(-1); if (is_fixed(v)) { dep = m_dep_manager.mk_join(dep, m_dep_manager.mk_join(m_dep_manager.mk_leaf(lower(v)), m_dep_manager.mk_leaf(upper(v)))); coeff *= lower_bound(v).get_rational().to_rational(); if (!coeff.is_zero()) monomials.push_back(gb.mk_monomial(coeff, 0, 0)); } else { monomials.push_back(gb.mk_monomial(coeff, 1, &m)); } gb.assert_eq_0(monomials.size(), monomials.c_ptr(), dep); } /** Initialize grobner basis data structure using the non linear cluster. The GB is initialized using rows and non linear monomials. */ template void theory_arith::init_grobner(svector const & nl_cluster, grobner & gb) { init_grobner_var_order(nl_cluster, gb); svector::const_iterator it = nl_cluster.begin(); svector::const_iterator end = nl_cluster.end(); for (; it != end; ++it) { theory_var v = *it; if (is_base(v)) { row const & r = m_rows[get_var_row(v)]; add_row_to_gb(r, gb); } if (is_pure_monomial(v) && !m_data[v].m_nl_propagated && is_fixed(v)) { add_monomial_def_to_gb(v, gb); } } } /** \brief Return the interval for the given monomial */ template interval theory_arith::mk_interval_for(grobner::monomial const * m) { interval r(m_dep_manager, rational(m->get_coeff())); expr * var = 0; unsigned power = 0; unsigned num_vars = m->get_degree(); for (unsigned i = 0; i < num_vars; i++) { expr * curr = m->get_var(i); if (var == 0) { var = curr; power = 1; } else if (curr == var) { power++; } else { mul_bound_of(var, power, r); var = curr; power = 1; } } if (var != 0) mul_bound_of(var, power, r); return r; } /** \brief Set a conflict using a dependency object. */ template void theory_arith::set_conflict(v_dependency * d) { bool is_lia = false; // TODO: fix it, but this is only used for debugging. antecedents& ante = get_antecedents(); derived_bound b(null_theory_var, inf_numeral(0), B_LOWER); dependency2new_bound(d, b); set_conflict(b.m_lits.size(), b.m_lits.c_ptr(), b.m_eqs.size(), b.m_eqs.c_ptr(), ante, is_lia, "arith_nl"); TRACE("non_linear", for (unsigned i = 0; i < b.m_lits.size(); ++i) { tout << b.m_lits[i] << " "; }); } /** \brief Return true if I.get_lower() <= - M_1 - ... - M_n <= I.get_upper() is inconsistent. Where M_i is monomials[i] and n = num_monomials. A conflict will also be set using the bounds of the variables occurring in the monomials M_i's. */ template bool theory_arith::is_inconsistent(interval const & I, unsigned num_monomials, grobner::monomial * const * monomials, v_dependency * dep) { interval r(I); for (unsigned i = 0; i < num_monomials; i++) { grobner::monomial const * m = monomials[i]; r += mk_interval_for(m); if (r.minus_infinity() && r.plus_infinity()) return false; } TRACE("non_linear_bug", tout << "is_inconsistent, r: " << r << "\n";); v_dependency * interval_deps = 0; bool conflict = false; if (!r.minus_infinity() && (r.get_lower_value().is_pos() || (r.get_lower_value().is_zero() && r.is_lower_open()))) { interval_deps = r.get_lower_dependencies(); conflict = true; TRACE("non_linear_bug", tout << "is inconsistent, interval_deps: " << interval_deps << "\n";); } else if (!r.plus_infinity() && (r.get_upper_value().is_neg() || (r.get_upper_value().is_zero() && r.is_upper_open()))) { interval_deps = r.get_upper_dependencies(); conflict = true; TRACE("non_linear_bug", tout << "is inconsistent, interval_deps: " << interval_deps << "\n";); } // interval_deps cannot be used to check if a conflict was detected, since interval_deps may be 0 even when r does not contain 0 if (conflict) { TRACE("non_linear", tout << "conflicting interval for = 0 equation: " << r << "\n";); set_conflict(m_dep_manager.mk_join(interval_deps, dep)); return true; } return false; } /** \brief Return true if the equation is inconsistent, and sign a conflict. */ template bool theory_arith::is_inconsistent(grobner::equation const * eq, grobner & gb) { interval zero(m_dep_manager, rational(0)); if (is_inconsistent(zero, eq->get_num_monomials(), eq->get_monomials(), eq->get_dependency())) { TRACE("non_linear", tout << "found conflict\n"; gb.display_equation(tout, *eq);); return true; } return false; } /** \brief Return true if the given monomial c*M is squared. The square root of the c is stored in r. */ bool is_perfect_square(grobner::monomial const * m, rational & r) { unsigned num_vars = m->get_degree(); if (num_vars % 2 == 1) return false; if (!m->get_coeff().is_perfect_square(r)) return false; expr * var = 0; unsigned power = 0; for (unsigned i = 0; i < num_vars; i++) { expr * curr = m->get_var(i); if (var == 0) { var = curr; power = 1; } else if (var == curr) { power++; } else { if (power % 2 == 1) return false; var = curr; power = 1; } } return power % 2 == 0; } /** \brief Return m1m2 is of the form (-2ab)*M1*M2 assuming that m1_sq = a^2*M1*M1 m2_sq = b^2*M2*M2 */ bool is_perfect_square(grobner::monomial const * m1_sq, rational const & a, grobner::monomial const * m2_sq, rational const & b, grobner::monomial const * m1m2) { DEBUG_CODE({ rational a1; rational b1; SASSERT(is_perfect_square(m1_sq, a1) && a == a1 && is_perfect_square(m2_sq, b1) && b == b1); }); if (m1m2->get_coeff().is_nonneg()) return false; rational c(-2); c *= a; c *= b; if (m1m2->get_coeff() != c) return false; unsigned num1 = m1_sq->get_degree(); unsigned num2 = m2_sq->get_degree(); unsigned num12 = m1m2->get_degree(); if (num1 + num2 != num12 * 2) return false; unsigned i1, i2, i12; i1 = i2 = i12 = 0; while (true) { expr * v1 = 0; expr * v2 = 0; expr * v12 = 0; if (i1 < num1) v1 = m1_sq->get_var(i1); if (i2 < num2) v2 = m2_sq->get_var(i2); if (i12 < num12) v12 = m1m2->get_var(i12); if (v1 == 0 && v2 == 0 && v12 == 0) return true; if (v12 == 0) return false; if (v1 == v12) { SASSERT(m1_sq->get_var(i1+1) == v1); i1 += 2; i12 ++; } else if (v2 == v12) { SASSERT(m2_sq->get_var(i2+1) == v2); i2 += 2; i12 ++; } else { return false; } } } /** \brief Return true if the equation is inconsistent. In this version, perfect squares are eliminated, and replaced with the interval [0, oo), if the interval associated with them is less precise than [0, oo). \remark I track only simple perfect squares of the form (M1 - M2)^2, where M1 and M2 are arbitrary monomials. */ template bool theory_arith::is_inconsistent2(grobner::equation const * eq, grobner & gb) { // TODO: a possible improvement: create a quotation for (M1 - M2)^2 // instead of trying to find it in a specific equation. // This approach is more precise, but more expensive // since a new row must be created. buffer intervals; unsigned num = eq->get_num_monomials(); for (unsigned i = 0; i < num; i++) { grobner::monomial const * m = eq->get_monomial(i); intervals.push_back(mk_interval_for(m)); } sbuffer deleted; deleted.resize(num, false); ptr_buffer monomials; // try to eliminate monomials that form perfect squares of the form (M1 - M2)^2 for (unsigned i = 0; i < num; i++) { grobner::monomial const * m1 = eq->get_monomial(i); rational a; if (deleted[i]) continue; if (!is_perfect_square(m1, a)) { monomials.push_back(const_cast(m1)); continue; } TRACE("non_linear", tout << "found perfect square monomial m1: "; gb.display_monomial(tout, *m1); tout << "\n";); // try to find another perfect square unsigned j = i + 1; for (; j < num; j++) { if (deleted[j]) continue; grobner::monomial const * m2 = eq->get_monomial(j); rational b; if (!is_perfect_square(m2, b)) continue; TRACE("non_linear", tout << "found perfect square monomial m2: "; gb.display_monomial(tout, *m2); tout << "\n";); // try to find -2*root(m1)*root(m2) // This monomial must be smaller than m1, since m2 is smaller than m1. unsigned k = i + 1; for (; k < num; k++) { if (deleted[k]) continue; grobner::monomial const * m1m2 = eq->get_monomial(k); if (!is_perfect_square(m1, a, m2, b, m1m2)) continue; // m1, m2, and m1m2 form a perfect square. // check if [0, oo) provides a better lowerbound than adding the intervals of m1, m2 and m1m2; TRACE("non_linear", tout << "found perfect square (M1-M2)^2:\n"; gb.display_monomial(tout, *m1); tout << "\n"; gb.display_monomial(tout, *m2); tout << "\n"; gb.display_monomial(tout, *m1m2); tout << "\n";); interval I = intervals[i]; I += intervals[j]; I += intervals[k]; if (I.minus_infinity() || I.get_lower_value().is_neg()) { TRACE("non_linear", tout << "the lower bound improved when perfect square is eliminated.\n";); // Found improvement... // mark these monomials as deleted deleted[i] = true; deleted[j] = true; deleted[k] = true; break; } } if (k < num) break; // found perfect square } if (j == num) { // didn't find perfect square of the form (M1-M2)^2 monomials.push_back(const_cast(m1)); } } if (monomials.size() == num) return false; // didn't find any perfect square. interval ge_zero(m_dep_manager, rational(0), false, true, 0); if (is_inconsistent(ge_zero, monomials.size(), monomials.c_ptr(), eq->get_dependency())) { TRACE("non_linear", tout << "found conflict\n"; gb.display_equation(tout, *eq);); return true; } return false; } template expr * theory_arith::monomial2expr(grobner::monomial const * m, bool is_int) { unsigned num_vars = m->get_degree(); ptr_buffer args; if (!m->get_coeff().is_one()) args.push_back(m_util.mk_numeral(m->get_coeff(), is_int)); for (unsigned j = 0; j < num_vars; j++) args.push_back(m->get_var(j)); return mk_nary_mul(args.size(), args.c_ptr(), is_int); } /** \brief Assert the new equation in the simplex tableau. */ template bool theory_arith::internalize_gb_eq(grobner::equation const * eq) { bool is_int = false; unsigned num_monomials = eq->get_num_monomials(); for (unsigned i = 0; i < num_monomials; i++) { grobner::monomial const * m = eq->get_monomial(i); unsigned degree = m->get_degree(); if (degree > m_params.m_nl_arith_max_degree) return false; if (degree > 0) is_int = m_util.is_int(m->get_var(0)); } rational k; ptr_buffer args; for (unsigned i = 0; i < num_monomials; i++) { grobner::monomial const * m = eq->get_monomial(i); if (m->get_degree() == 0) k -= m->get_coeff(); else args.push_back(monomial2expr(eq->get_monomial(i), is_int)); } context & ctx = get_context(); simplifier & s = ctx.get_simplifier(); expr_ref pol(get_manager()); SASSERT(!args.empty()); pol = mk_nary_add(args.size(), args.c_ptr()); expr_ref s_pol(get_manager()); proof_ref pr(get_manager()); TRACE("gb_bug", tout << mk_ll_pp(pol, get_manager()) << "\n";); s(pol, s_pol, pr); if (!has_var(s_pol)) { TRACE("spol_bug", tout << "internalizing...\n" << mk_ll_pp(s_pol, get_manager()) << "\n";); ctx.internalize(s_pol, false); ctx.mark_as_relevant(s_pol.get()); } SASSERT(has_var(s_pol.get())); // s_pol = k theory_var v = expr2var(s_pol); // v = k CTRACE("spol_bug", v == null_theory_var, tout << mk_ll_pp(s_pol, get_manager()) << "\n"; display(tout);); SASSERT(v != null_theory_var); // assert bounds for s_pol mk_derived_nl_bound(v, inf_numeral(k), B_LOWER, eq->get_dependency()); mk_derived_nl_bound(v, inf_numeral(k), B_UPPER, eq->get_dependency()); TRACE("non_linear", tout << "inserted new equation into the tableau\n"; display_var(tout, v);); return true; } /** \brief Compute Grobner basis, return true if a conflict or new fixed variables were detected. */ template typename theory_arith::gb_result theory_arith::compute_grobner(svector const & nl_cluster) { if (m_nl_gb_exhausted) return GB_FAIL; grobner gb(get_manager(), m_dep_manager); init_grobner(nl_cluster, gb); TRACE("non_linear", display(tout);); bool warn = false; unsigned next_weight = MAX_DEFAULT_WEIGHT + 1; // next weight using during perturbation phase. ptr_vector eqs; while (true) { TRACE("non_linear_gb", tout << "before:\n"; gb.display(tout);); bool r = gb.compute_basis(m_params.m_nl_arith_gb_threshold); m_stats.m_gb_simplify += gb.m_stats.m_simplify; m_stats.m_gb_superpose += gb.m_stats.m_superpose; m_stats.m_gb_num_processed += gb.m_stats.m_num_processed; m_stats.m_gb_compute_basis++; if (!r && !warn) { IF_VERBOSE(3, verbose_stream() << "Grobner basis computation interrupted. Increase threshold using NL_ARITH_GB_THRESHOLD=\n";); get_context().push_trail(value_trail(m_nl_gb_exhausted)); m_nl_gb_exhausted = true; warn = true; } if (get_context().get_cancel_flag()) { return GB_FAIL; } TRACE("non_linear_gb", tout << "after:\n"; gb.display(tout);); // Scan the grobner basis eqs, and look for inconsistencies. eqs.reset(); gb.get_equations(eqs); TRACE("grobner_bug", tout << "after gb\n";); ptr_vector::const_iterator it = eqs.begin(); ptr_vector::const_iterator end = eqs.end(); for (; it != end; ++it) { grobner::equation * eq = *it; TRACE("grobner_bug", gb.display_equation(tout, *eq);); if (is_inconsistent(eq, gb)) return GB_PROGRESS; if (is_inconsistent2(eq, gb)) return GB_PROGRESS; } // Scan the grobner basis eqs for equations of the form x - k = 0 or x = 0 is found, and x is not fixed, // then assert bounds for x, and continue gb_result result = GB_FAIL; if (m_params.m_nl_arith_gb_eqs) { it = eqs.begin(); for (; it != end; ++it) { grobner::equation * eq = *it; if (!eq->is_linear_combination()) { TRACE("non_linear", tout << "processing new equality:\n"; gb.display_equation(tout, *eq);); TRACE("non_linear_bug", tout << "processing new equality:\n"; gb.display_equation(tout, *eq);); if (internalize_gb_eq(eq)) result = GB_NEW_EQ; } } } if (result != GB_FAIL) return result; if (!m_params.m_nl_arith_gb_perturbate) return result; if (m_nl_gb_exhausted) return result; // Try to change the variable order... in such a way the leading term is modified. // I only consider linear equations... (HACK) // Moreover, I do not change the weight of a variable more than once in this loop. bool modified = false; it = eqs.begin(); for (; it != end; ++it) { grobner::equation const * eq = *it; unsigned num_monomials = eq->get_num_monomials(); CTRACE("grobner_bug", num_monomials <= 0, gb.display_equation(tout, *eq);); if (num_monomials == 0) continue; // HACK: the equation 0 = 0, should have been discarded by the GB module. if (eq->get_monomial(0)->get_degree() != 1) continue; for (unsigned j = 1; j < num_monomials; j++) { grobner::monomial const * m = eq->get_monomial(j); if (m->get_degree() != 1) continue; expr * var = m->get_var(0); if (gb.get_weight(var) > MAX_DEFAULT_WEIGHT) continue; // variable was already updated TRACE("non_linear", tout << "increased weight of: " << mk_bounded_pp(var, get_manager()) << "\n";); gb.set_weight(var, next_weight); next_weight++; gb.update_order(); TRACE("non_linear", tout << "after updating order\n"; gb.display(tout);); modified = true; break; } if (modified) break; } if (!modified) return result; } } /** \brief Maximize/Minimize variables in non linear monomials. */ template bool theory_arith::max_min_nl_vars() { var_set already_found; svector vars; svector::const_iterator it = m_nl_monomials.begin(); svector::const_iterator end = m_nl_monomials.end(); for (; it != end; ++it) { theory_var v = *it; mark_var(v, vars, already_found); expr * n = var2expr(v); SASSERT(is_pure_monomial(n)); for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) { expr * curr = to_app(n)->get_arg(i); theory_var v = expr2var(curr); SASSERT(v != null_theory_var); mark_var(v, vars, already_found); } } return max_min(vars); } /** \brief Process non linear constraints. */ template final_check_status theory_arith::process_non_linear() { if (m_nl_monomials.empty()) return FC_DONE; if (check_monomial_assignments()) { return FC_DONE; } if (!m_params.m_nl_arith) { TRACE("non_linear", tout << "Non-linear is not enabled\n";); return FC_GIVEUP; } TRACE("process_non_linear", display(tout);); if (m_nl_rounds > m_params.m_nl_arith_rounds) { TRACE("non_linear", tout << "GIVEUP non linear problem...\n";); IF_VERBOSE(3, verbose_stream() << "Max. non linear arithmetic rounds. Increase threshold using NL_ARITH_ROUNDS=\n";); return FC_GIVEUP; } get_context().push_trail(value_trail(m_nl_rounds)); m_nl_rounds++; elim_quasi_base_rows(); move_non_base_vars_to_bounds(); TRACE("non_linear", tout << "processing non linear constraints...\n"; get_context().display(tout);); if (!make_feasible()) { TRACE("non_linear", tout << "failed to move variables to bounds.\n";); failed(); return FC_CONTINUE; } if (!max_min_nl_vars()) return FC_CONTINUE; if (check_monomial_assignments()) { return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; } svector vars; get_non_linear_cluster(vars); bool progress; unsigned old_idx = m_nl_strategy_idx; get_context().push_trail(value_trail(m_nl_strategy_idx)); do { progress = false; switch (m_nl_strategy_idx) { case 0: if (propagate_nl_bounds()) { propagate_core(); progress = true; } break; case 1: if (!is_cross_nested_consistent(vars)) progress = true; break; case 2: if (m_params.m_nl_arith_gb) { switch(compute_grobner(vars)) { case GB_PROGRESS: progress = true; break; case GB_NEW_EQ: progress = true; propagate_core(); break; case GB_FAIL: break; } } break; case 3: if (m_params.m_nl_arith_branching) { theory_var target = find_nl_var_for_branching(); if (target != null_theory_var && branch_nl_int_var(target)) progress = true; } break; } m_nl_strategy_idx = (m_nl_strategy_idx + 1) % 4; if (progress) return FC_CONTINUE; } while (m_nl_strategy_idx != old_idx); if (check_monomial_assignments()) { return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; } TRACE("non_linear", display(tout);); return FC_GIVEUP; } }; #endif /* THEORY_ARITH_NL_H_ */ z3-z3-4.4.1/src/smt/theory_arith_pp.h000066400000000000000000000437431260446376700174030ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_pp.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-05. Revision History: --*/ #ifndef THEORY_ARITH_PP_H_ #define THEORY_ARITH_PP_H_ #include"theory_arith.h" #include"ast_smt_pp.h" #include"stats.h" namespace smt { template void theory_arith::collect_statistics(::statistics & st) const { st.update("arith conflicts", m_stats.m_conflicts); st.update("add rows", m_stats.m_add_rows); st.update("pivots", m_stats.m_pivots); st.update("assert lower", m_stats.m_assert_lower); st.update("assert upper", m_stats.m_assert_upper); st.update("assert diseq", m_stats.m_assert_diseq); st.update("bound prop", m_stats.m_bound_props); st.update("fixed eqs", m_stats.m_fixed_eqs); st.update("offset eqs", m_stats.m_offset_eqs); st.update("gcd tests", m_stats.m_gcd_tests); st.update("ineq splits", m_stats.m_branches); st.update("gomory cuts", m_stats.m_gomory_cuts); st.update("max-min", m_stats.m_max_min); st.update("grobner", m_stats.m_gb_compute_basis); st.update("pseudo nonlinear", m_stats.m_nl_linear); st.update("nonlinear bounds", m_stats.m_nl_bounds); st.update("nonlinear horner", m_stats.m_nl_cross_nested); m_arith_eq_adapter.collect_statistics(st); } template void theory_arith::display(std::ostream & out) const { out << "Theory arithmetic:\n"; display_vars(out); display_nl_monomials(out); display_rows(out, true); display_rows(out, false); display_atoms(out); display_asserted_atoms(out); } template void theory_arith::display_nl_monomials(std::ostream & out) const { if (m_nl_monomials.empty()) return; out << "non linear monomials:\n"; svector::const_iterator it = m_nl_monomials.begin(); svector::const_iterator end = m_nl_monomials.end(); for (; it != end; ++it) display_var(out, *it); } template void theory_arith::display_row(std::ostream & out, unsigned r_id, bool compact) const { out << r_id << " "; display_row(out, m_rows[r_id], compact); } template void theory_arith::display_row(std::ostream & out, row const & r, bool compact) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); out << "(v" << r.get_base_var() << ") : "; bool first = true; for (; it != end; ++it) { if (!it->is_dead()) { if (first) first = false; else out << " + "; theory_var s = it->m_var; numeral const & c = it->m_coeff; if (!c.is_one()) out << c << "*"; if (compact) { out << "v" << s; if (is_fixed(s)) { out << ":" << lower(s)->get_value(); } } else display_var_flat_def(out, s); } } out << "\n"; } template void theory_arith::display_rows(std::ostream & out, bool compact) const { if (compact) out << "rows (compact view):\n"; else out << "rows (expanded view):\n"; unsigned num = m_rows.size(); for (unsigned r_id = 0; r_id < num; r_id++) { if (m_rows[r_id].m_base_var != null_theory_var) { display_row(out, r_id, compact); } } } template void theory_arith::display_row_shape(std::ostream & out, row const & r) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { numeral const & c = it->m_coeff; if (c.is_one()) out << "1"; else if (c.is_minus_one()) out << "-"; else if (c.is_int() && c.to_rational().is_small()) out << "i"; else if (c.is_int() && !c.to_rational().is_small()) out << "I"; else if (c.to_rational().is_small()) out << "r"; else out << "R"; } } out << "\n"; } template bool theory_arith::is_one_minus_one_row(row const & r) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { numeral const & c = it->m_coeff; if (!c.is_one() && !c.is_minus_one()) return false; } } return true; } template void theory_arith::display_rows_shape(std::ostream & out) const { unsigned num = m_rows.size(); unsigned num_trivial = 0; for (unsigned r_id = 0; r_id < num; r_id++) { row const & r = m_rows[r_id]; if (r.m_base_var != null_theory_var) { if (is_one_minus_one_row(r)) num_trivial++; else display_row_shape(out, r); } } out << "num. trivial: " << num_trivial << "\n"; } template void theory_arith::display_rows_bignums(std::ostream & out) const { unsigned num = m_rows.size(); for (unsigned r_id = 0; r_id < num; r_id++) { row const & r = m_rows[r_id]; if (r.m_base_var != null_theory_var) { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { numeral const & c = it->m_coeff; if (c.to_rational().is_big()) { std::string str = c.to_rational().to_string(); if (str.length() > 48) out << str << "\n"; } } } } } } template void theory_arith::display_rows_stats(std::ostream & out) const { unsigned num_vars = get_num_vars(); unsigned num_rows = 0; unsigned num_non_zeros = 0; unsigned num_ones = 0; unsigned num_minus_ones = 0; unsigned num_small_ints = 0; unsigned num_big_ints = 0; unsigned num_small_rats = 0; unsigned num_big_rats = 0; for (unsigned r_id = 0; r_id < m_rows.size(); r_id++) { row const & r = m_rows[r_id]; if (r.m_base_var != null_theory_var) { num_rows++; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { numeral const & c = it->m_coeff; num_non_zeros++; if (c.is_one()) num_ones++; else if (c.is_minus_one()) num_minus_ones++; else if (c.is_int() && c.to_rational().is_small()) num_small_ints++; else if (c.is_int() && !c.to_rational().is_small()) num_big_ints++; else if (c.to_rational().is_small()) num_small_rats++; else num_big_rats++; } } } } out << "A: " << num_rows << " X " << num_vars << "\n"; out << "avg. row: " << num_non_zeros / num_rows << ", num. non zeros: " << num_non_zeros << "\n"; unsigned spc = 6; out.width(spc); out << 1 << "|"; out.width(spc); out << -1 << "|"; out.width(spc); out << "i"; out << "|"; out.width(spc); out << "I"; out << "|"; out.width(spc); out << "r"; out << "|"; out.width(spc); out << "R"; out << "\n"; out.width(spc); out << num_ones << "|"; out.width(spc); out << num_minus_ones << "|"; out.width(spc); out << num_small_ints; out << "|"; out.width(spc); out << num_big_ints; out << "|"; out.width(spc); out << num_small_rats; out << "|"; out.width(spc); out << num_big_rats; out << "\n"; } template void theory_arith::display_row_info(std::ostream & out, unsigned r_id) const { out << r_id << " "; display_row_info(out, m_rows[r_id]); } template void theory_arith::display_row_info(std::ostream & out, row const & r) const { display_row(out, r, true); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) if (!it->is_dead()) display_var(out, it->m_var); } /** \brief Display row after substituting fixed variables. */ template void theory_arith::display_simplified_row(std::ostream & out, row const & r) const { bool has_rat_coeff = false; numeral k; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); out << "(v" << r.get_base_var() << ") : "; bool first = true; for (; it != end; ++it) { if (it->is_dead()) continue; theory_var v = it->m_var; numeral const & c = it->m_coeff; if (is_fixed(v)) { k += c * lower_bound(v).get_rational(); continue; } if (!c.is_int()) has_rat_coeff = true; if (first) first = false; else out << " + "; if (!c.is_one()) out << c << "*"; out << "v" << v; } if (!k.is_zero()) { if (!first) out << " + "; out << k; } out << "\n"; if (has_rat_coeff) { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) if (!it->is_dead() && (is_base(it->m_var) || (!is_fixed(it->m_var) && (lower(it->m_var) || upper(it->m_var))))) display_var(out, it->m_var); } } template void theory_arith::display_var(std::ostream & out, theory_var v) const { out << "v"; out.width(4); out << std::left << v; out << " #"; out.width(4); out << get_enode(v)->get_owner_id(); out << std::right; out << " lo:"; out.width(10); if (lower(v)) { out << lower(v)->get_value(); } else { out << "-oo"; } out << ", up:"; out.width(10); if (upper(v)) { out << upper(v)->get_value(); } else { out << "oo"; } out << ", value: "; out.width(10); out << get_value(v); out << ", occs: "; out.width(4); out << m_columns[v].size(); out << ", atoms: "; out.width(4); out << m_var_occs[v].size(); out << (is_int(v) ? ", int " : ", real"); switch (get_var_kind(v)) { case NON_BASE: out << ", non-base "; break; case QUASI_BASE: out << ", quasi-base"; break; case BASE: out << ", base "; break; } out << ", shared: " << get_context().is_shared(get_enode(v)); out << ", unassigned: " << m_unassigned_atoms[v]; out << ", rel: " << get_context().is_relevant(get_enode(v)); out << ", def: "; display_var_flat_def(out, v); out << "\n"; } template void theory_arith::display_vars(std::ostream & out) const { out << "vars:\n"; int n = get_num_vars(); for (theory_var v = 0; v < n; v++) display_var(out, v); } template void theory_arith::display_bound(std::ostream & out, bound * b, unsigned indent) const { for (unsigned i = 0; i < indent; i++) out << " "; b->display(*this, out); out << "\n"; } template void theory_arith::display_deps(std::ostream & out, v_dependency* dep) { ptr_vector bounds; m_dep_manager.linearize(dep, bounds); m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); ptr_vector::const_iterator it = bounds.begin(); ptr_vector::const_iterator end = bounds.end(); for (; it != end; ++it) { bound * b = static_cast(*it); out << " "; b->display(*this, out); } } template void theory_arith::display_interval(std::ostream & out, interval const& i) { i.display(out); display_deps(out << " lo:", i.get_lower_dependencies()); display_deps(out << " hi:", i.get_upper_dependencies()); } template void theory_arith::display_atoms(std::ostream & out) const { out << "atoms:\n"; for (unsigned i = 0; i < m_atoms.size(); i++) display_atom(out, m_atoms[i], false); } template void theory_arith::display_asserted_atoms(std::ostream & out) const { out << "asserted atoms:\n"; for (unsigned i = 0; i < m_asserted_qhead; i++) { bound * b = m_asserted_bounds[i]; if (b->is_atom()) display_atom(out, static_cast(b), true); } if (m_asserted_qhead < m_asserted_bounds.size()) { out << "delayed atoms:\n"; for (unsigned i = m_asserted_qhead; i < m_asserted_bounds.size(); i++) { bound * b = m_asserted_bounds[i]; if (b->is_atom()) display_atom(out, static_cast(b), true); } } } template void theory_arith::display_atom(std::ostream & out, atom * a, bool show_sign) const { theory_var v = a->get_var(); inf_numeral const & k = a->get_k(); enode * e = get_enode(v); if (show_sign) { if (!a->is_true()) out << "not "; else out << " "; } out << "v"; out.width(3); out << std::left << v << " #"; out.width(3); out << e->get_owner_id(); out << std::right; out << " "; if (a->get_atom_kind() == A_LOWER) out << ">="; else out << "<="; out << " "; out.width(6); out << k << " "; display_var_flat_def(out, v); out << "\n"; } template void theory_arith::display_bounds_in_smtlib(std::ostream & out) const { ast_manager & m = get_manager(); ast_smt_pp pp(m); pp.set_benchmark_name("lemma"); int n = get_num_vars(); for (theory_var v = 0; v < n; v++) { expr * n = get_enode(v)->get_owner(); if (is_fixed(v)) { inf_numeral k_inf = lower_bound(v); rational k = k_inf.get_rational().to_rational(); expr_ref eq(m); eq = m.mk_eq(n, m_util.mk_numeral(k, is_int(v))); pp.add_assumption(eq); } else { if (lower(v) != 0) { inf_numeral k_inf = lower_bound(v); rational k = k_inf.get_rational().to_rational(); expr_ref ineq(m); if (k_inf.get_infinitesimal().is_zero()) ineq = m_util.mk_le(m_util.mk_numeral(k, is_int(v)), n); else ineq = m_util.mk_lt(m_util.mk_numeral(k, is_int(v)), n); pp.add_assumption(ineq); } if (upper(v) != 0) { inf_numeral k_inf = upper_bound(v); rational k = k_inf.get_rational().to_rational(); expr_ref ineq(m); if (k_inf.get_infinitesimal().is_zero()) ineq = m_util.mk_le(n, m_util.mk_numeral(k, is_int(v))); else ineq = m_util.mk_lt(n, m_util.mk_numeral(k, is_int(v))); pp.add_assumption(ineq); } } } pp.display(out, m.mk_true()); } template void theory_arith::display_bounds_in_smtlib() const { char buffer[128]; static int id = 0; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "arith_%d.smt", id); #else sprintf(buffer, "arith_%d.smt", id); #endif std::ofstream out(buffer); display_bounds_in_smtlib(out); out.close(); id++; } }; #endif /* THEORY_ARITH_PP_H_ */ z3-z3-4.4.1/src/smt/theory_array.cpp000066400000000000000000000416751260446376700172500ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-01. Revision History: --*/ #include"smt_context.h" #include"theory_array.h" #include"ast_ll_pp.h" #include"stats.h" namespace smt { theory_array::theory_array(ast_manager & m, theory_array_params & params): theory_array_base(m), m_params(params), m_find(*this), m_trail_stack(*this), m_final_check_idx(0) { } theory_array::~theory_array() { std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc()); m_var_data.reset(); } void theory_array::init(context * ctx) { theory_array_base::init(ctx); if (!ctx->relevancy()) m_params.m_array_laziness = 0; } void theory_array::merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) { // v1 is the new root TRACE("array", tout << "merging v" << v1 << " v" << v2 << "\n"; display_var(tout, v1);); SASSERT(v1 == find(v1)); var_data * d1 = m_var_data[v1]; var_data * d2 = m_var_data[v2]; if (!d1->m_prop_upward && d2->m_prop_upward) set_prop_upward(v1); ptr_vector::iterator it = d2->m_stores.begin(); ptr_vector::iterator end = d2->m_stores.end(); for (; it != end; ++it) add_store(v1, *it); it = d2->m_parent_stores.begin(); end = d2->m_parent_stores.end(); for (; it != end; ++it) add_parent_store(v1, *it); it = d2->m_parent_selects.begin(); end = d2->m_parent_selects.end(); for (; it != end; ++it) add_parent_select(v1, *it); TRACE("array", tout << "after merge\n"; display_var(tout, v1);); } void theory_array::unmerge_eh(theory_var v1, theory_var v2) { // do nothing } theory_var theory_array::mk_var(enode * n) { theory_var r = theory_array_base::mk_var(n); theory_var r2 = m_find.mk_var(); SASSERT(r == r2); SASSERT(r == static_cast(m_var_data.size())); m_var_data.push_back(alloc(var_data)); var_data * d = m_var_data[r]; TRACE("array", tout << mk_bounded_pp(n->get_owner(), get_manager()) << "\nis_array: " << is_array_sort(n) << ", is_select: " << is_select(n) << ", is_store: " << is_store(n) << "\n";); d->m_is_array = is_array_sort(n); if (d->m_is_array) register_sort(get_manager().get_sort(n->get_owner())); d->m_is_select = is_select(n); if (is_store(n)) d->m_stores.push_back(n); get_context().attach_th_var(n, this, r); if (m_params.m_array_laziness <= 1 && is_store(n)) instantiate_axiom1(n); return r; } void theory_array::add_parent_select(theory_var v, enode * s) { if (m_params.m_array_cg && !s->is_cgr()) return; SASSERT(is_select(s)); v = find(v); var_data * d = m_var_data[v]; d->m_parent_selects.push_back(s); m_trail_stack.push(push_back_trail(d->m_parent_selects)); ptr_vector::iterator it = d->m_stores.begin(); ptr_vector::iterator end = d->m_stores.end(); for (; it != end; ++it) { instantiate_axiom2a(s, *it); } if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { it = d->m_parent_stores.begin(); end = d->m_parent_stores.end(); for (; it != end; ++it) { enode * store = *it; SASSERT(is_store(store)); if (!m_params.m_array_cg || store->is_cgr()) instantiate_axiom2b(s, store); } } } void theory_array::add_parent_store(theory_var v, enode * s) { if (m_params.m_array_cg && !s->is_cgr()) return; SASSERT(is_store(s)); v = find(v); var_data * d = m_var_data[v]; d->m_parent_stores.push_back(s); m_trail_stack.push(push_back_trail(d->m_parent_stores)); if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { ptr_vector::iterator it = d->m_parent_selects.begin(); ptr_vector::iterator end = d->m_parent_selects.end(); for (; it != end; ++it) if (!m_params.m_array_cg || (*it)->is_cgr()) instantiate_axiom2b(*it, s); } } bool theory_array::instantiate_axiom2b_for(theory_var v) { bool result = false; var_data * d = m_var_data[v]; ptr_vector::iterator it = d->m_parent_stores.begin(); ptr_vector::iterator end = d->m_parent_stores.end(); for (; it != end; ++it) { ptr_vector::iterator it2 = d->m_parent_selects.begin(); ptr_vector::iterator end2 = d->m_parent_selects.end(); for (; it2 != end2; ++it2) if (instantiate_axiom2b(*it2, *it)) result = true; } return result; } /** \brief Mark v for upward propagation. That is, enables the propagation of select(v, i) to store(v,j,k). */ void theory_array::set_prop_upward(theory_var v) { if (m_params.m_array_weak) return; v = find(v); var_data * d = m_var_data[v]; if (!d->m_prop_upward) { TRACE("array", tout << "#" << v << "\n";); m_trail_stack.push(reset_flag_trail(d->m_prop_upward)); d->m_prop_upward = true; if (!m_params.m_array_delay_exp_axiom) instantiate_axiom2b_for(v); ptr_vector::iterator it = d->m_stores.begin(); ptr_vector::iterator end = d->m_stores.end(); for (; it != end; ++it) set_prop_upward(*it); } } void theory_array::set_prop_upward(enode * store) { if (is_store(store)) { theory_var st_v = store->get_arg(0)->get_th_var(get_id()); set_prop_upward(st_v); } } void theory_array::set_prop_upward(theory_var v, var_data* d) { unsigned sz = d->m_stores.size(); for (unsigned i = 0; i < sz; ++i) { set_prop_upward(d->m_stores[i]); } } /** \brief Return the size of the equivalence class for array terms that can be expressed as \lambda i : Index . [.. (select a i) ..] */ unsigned theory_array::get_lambda_equiv_size(theory_var v, var_data* d) { return d->m_stores.size(); } void theory_array::add_store(theory_var v, enode * s) { if (m_params.m_array_cg && !s->is_cgr()) return; SASSERT(is_store(s)); v = find(v); var_data * d = m_var_data[v]; unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d); if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) { set_prop_upward(v, d); } d->m_stores.push_back(s); m_trail_stack.push(push_back_trail(d->m_stores)); ptr_vector::iterator it = d->m_parent_selects.begin(); ptr_vector::iterator end = d->m_parent_selects.end(); for (; it != end; ++it) { SASSERT(is_select(*it)); instantiate_axiom2a(*it, s); } if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) set_prop_upward(s); } void theory_array::instantiate_axiom1(enode * store) { TRACE("array", tout << "axiom 1:\n" << mk_bounded_pp(store->get_owner(), get_manager()) << "\n";); SASSERT(is_store(store)); m_stats.m_num_axiom1++; assert_store_axiom1(store); } void theory_array::instantiate_axiom2a(enode * select, enode * store) { TRACE("array", tout << "axiom 2a: #" << select->get_owner_id() << " #" << store->get_owner_id() << "\n";); SASSERT(is_select(select)); SASSERT(is_store(store)); if (assert_store_axiom2(store, select)) m_stats.m_num_axiom2a++; } bool theory_array::instantiate_axiom2b(enode * select, enode * store) { TRACE("array_axiom2b", tout << "axiom 2b: #" << select->get_owner_id() << " #" << store->get_owner_id() << "\n";); SASSERT(is_select(select)); SASSERT(is_store(store)); if (assert_store_axiom2(store, select)) { m_stats.m_num_axiom2b++; return true; } return false; } void theory_array::instantiate_extensionality(enode * a1, enode * a2) { TRACE("array", tout << "extensionality: #" << a1->get_owner_id() << " #" << a2->get_owner_id() << "\n";); SASSERT(is_array_sort(a1)); SASSERT(is_array_sort(a2)); if (m_params.m_array_extensional && assert_extensionality(a1, a2)) m_stats.m_num_extensionality++; } bool theory_array::internalize_atom(app * atom, bool) { return internalize_term(atom); } // // Internalize the term. If it has already been internalized, return false. // bool theory_array::internalize_term_core(app * n) { TRACE("array_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";); context & ctx = get_context(); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(n->get_arg(i), false); if (ctx.e_internalized(n)) { return false; } enode * e = ctx.mk_enode(n, false, false, true); if (!is_attached_to_var(e)) mk_var(e); if (get_manager().is_bool(n)) { bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); ctx.set_enode_flag(bv, true); } return true; } bool theory_array::internalize_term(app * n) { if (!is_store(n) && !is_select(n)) { if (!is_array_ext(n)) found_unsupported_op(n); return false; } TRACE("array_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";); if (!internalize_term_core(n)) { return true; } context & ctx = get_context(); enode * arg0 = ctx.get_enode(n->get_arg(0)); if (!is_attached_to_var(arg0)) mk_var(arg0); if (m_params.m_array_laziness == 0) { theory_var v_arg = arg0->get_th_var(get_id()); SASSERT(v_arg != null_theory_var); if (is_select(n)) { add_parent_select(v_arg, ctx.get_enode(n)); } else if (is_store(n)) { add_parent_store(v_arg, ctx.get_enode(n)); } } return true; } void theory_array::apply_sort_cnstr(enode * n, sort * s) { SASSERT(is_array_sort(s)); if (!is_attached_to_var(n)) mk_var(n); } void theory_array::new_eq_eh(theory_var v1, theory_var v2) { m_find.merge(v1, v2); } void theory_array::new_diseq_eh(theory_var v1, theory_var v2) { v1 = find(v1); v2 = find(v2); var_data * d1 = m_var_data[v1]; if (d1->m_is_array) { SASSERT(m_var_data[v2]->m_is_array); TRACE("ext", tout << "extensionality:\n" << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager(), 5) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager(), 5) << "\n";); instantiate_extensionality(get_enode(v1), get_enode(v2)); } } void theory_array::relevant_eh(app * n) { if (m_params.m_array_laziness == 0) return; if (!is_store(n) && !is_select(n)) return; context & ctx = get_context(); enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v_arg = arg->get_th_var(get_id()); SASSERT(v_arg != null_theory_var); if (is_select(n)) { add_parent_select(v_arg, ctx.get_enode(n)); } else { SASSERT(is_store(n)); if (m_params.m_array_laziness > 1) instantiate_axiom1(ctx.get_enode(n)); add_parent_store(v_arg, ctx.get_enode(n)); } } void theory_array::push_scope_eh() { theory_array_base::push_scope_eh(); m_trail_stack.push_scope(); } void theory_array::pop_scope_eh(unsigned num_scopes) { m_trail_stack.pop_scope(num_scopes); unsigned num_old_vars = get_old_num_vars(num_scopes); std::for_each(m_var_data.begin() + num_old_vars, m_var_data.end(), delete_proc()); m_var_data.shrink(num_old_vars); theory_array_base::pop_scope_eh(num_scopes); SASSERT(m_find.get_num_vars() == m_var_data.size()); SASSERT(m_find.get_num_vars() == get_num_vars()); } final_check_status theory_array::final_check_eh() { m_final_check_idx++; final_check_status r; if (m_params.m_array_lazy_ieq) { // Delay the creation of interface equalities... The // motivation is too give other theories and quantifier // instantiation to do something useful during final // check. if (m_final_check_idx % m_params.m_array_lazy_ieq_delay != 0) { assert_delayed_axioms(); r = FC_CONTINUE; } else { if (mk_interface_eqs_at_final_check() == FC_CONTINUE) r = FC_CONTINUE; else r = assert_delayed_axioms(); } } else { if (m_final_check_idx % 2 == 1) { if (assert_delayed_axioms() == FC_CONTINUE) r = FC_CONTINUE; else r = mk_interface_eqs_at_final_check(); } else { if (mk_interface_eqs_at_final_check() == FC_CONTINUE) r = FC_CONTINUE; else r = assert_delayed_axioms(); } } TRACE("as_array", tout << "m_found_unsupported_op: " << m_found_unsupported_op << " " << r << "\n";); if (r == FC_DONE && m_found_unsupported_op) r = FC_GIVEUP; return r; } final_check_status theory_array::assert_delayed_axioms() { if (!m_params.m_array_delay_exp_axiom) return FC_DONE; final_check_status r = FC_DONE; unsigned num_vars = get_num_vars(); for (unsigned v = 0; v < num_vars; v++) { var_data * d = m_var_data[v]; if (d->m_prop_upward && instantiate_axiom2b_for(v)) r = FC_CONTINUE; } return r; } final_check_status theory_array::mk_interface_eqs_at_final_check() { unsigned n = mk_interface_eqs(); m_stats.m_num_eq_splits += n; if (n > 0) return FC_CONTINUE; return FC_DONE; } void theory_array::reset_eh() { m_trail_stack.reset(); std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc()); m_var_data.reset(); theory_array_base::reset_eh(); } void theory_array::display(std::ostream & out) const { out << "Theory array:\n"; unsigned num_vars = get_num_vars(); for (unsigned v = 0; v < num_vars; v++) { display_var(out, v); } } // TODO: move to another file void theory_array::display_ids(std::ostream & out, unsigned n, enode * const * v) { for (unsigned i = 0; i < n; i++) { if (i > 0) out << " "; out << "#" << v[i]->get_owner_id(); } } void theory_array::display_var(std::ostream & out, theory_var v) const { var_data const * d = m_var_data[v]; out << "v"; out.width(4); out << std::left << v; out << " #"; out.width(4); out << get_enode(v)->get_owner_id() << " -> #"; out.width(4); out << get_enode(find(v))->get_owner_id(); out << std::right; out << " is_array: " << d->m_is_array << " is_select: " << d->m_is_select << " upward: " << d->m_prop_upward; out << " stores: {"; display_ids(out, d->m_stores.size(), d->m_stores.c_ptr()); out << "} p_stores: {"; display_ids(out, d->m_parent_stores.size(), d->m_parent_stores.c_ptr()); out << "} p_selects: {"; display_ids(out, d->m_parent_selects.size(), d->m_parent_selects.c_ptr()); out << "}"; out << "\n"; } void theory_array::collect_statistics(::statistics & st) const { st.update("array ax1", m_stats.m_num_axiom1); st.update("array ax2", m_stats.m_num_axiom2a); st.update("array exp ax2", m_stats.m_num_axiom2b); st.update("array ext ax", m_stats.m_num_extensionality); st.update("array splits", m_stats.m_num_eq_splits); } }; z3-z3-4.4.1/src/smt/theory_array.h000066400000000000000000000104051260446376700167000ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-01. Revision History: --*/ #ifndef THEORY_ARRAY_H_ #define THEORY_ARRAY_H_ #include"theory_array_base.h" #include"theory_array_params.h" #include"union_find.h" namespace smt { struct theory_array_stats { unsigned m_num_axiom1, m_num_axiom2a, m_num_axiom2b, m_num_extensionality, m_num_eq_splits; unsigned m_num_map_axiom, m_num_default_map_axiom; unsigned m_num_select_const_axiom, m_num_default_store_axiom, m_num_default_const_axiom, m_num_default_as_array_axiom; unsigned m_num_select_as_array_axiom; void reset() { memset(this, 0, sizeof(theory_array_stats)); } theory_array_stats() { reset(); } }; class theory_array : public theory_array_base { protected: typedef trail_stack th_trail_stack; typedef union_find th_union_find; struct var_data { ptr_vector m_stores; ptr_vector m_parent_selects; ptr_vector m_parent_stores; bool m_prop_upward; bool m_is_array; bool m_is_select; var_data():m_prop_upward(false), m_is_array(false), m_is_select(false) {} }; ptr_vector m_var_data; theory_array_params & m_params; theory_array_stats m_stats; th_union_find m_find; th_trail_stack m_trail_stack; unsigned m_final_check_idx; virtual void init(context * ctx); virtual theory_var mk_var(enode * n); virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); virtual void apply_sort_cnstr(enode * n, sort * s); virtual void new_eq_eh(theory_var v1, theory_var v2); virtual void new_diseq_eh(theory_var v1, theory_var v2); virtual void relevant_eh(app * n); virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual final_check_status final_check_eh(); virtual void reset_eh(); virtual void init_search_eh() { m_final_check_idx = 0; } virtual void set_prop_upward(theory_var v); virtual void set_prop_upward(enode* n); virtual void set_prop_upward(theory_var v, var_data* d); virtual unsigned get_lambda_equiv_size(theory_var v, var_data* d); theory_var find(theory_var v) const { return m_find.find(v); } bool is_root(theory_var v) const { return m_find.is_root(v); } virtual void add_parent_select(theory_var v, enode * s); void add_parent_store(theory_var v, enode * s); void add_store(theory_var v, enode * s); bool internalize_term_core(app * term); void instantiate_axiom2a(enode * select, enode * store); bool instantiate_axiom2b(enode * select, enode * store); void instantiate_axiom1(enode * store); void instantiate_extensionality(enode * a1, enode * a2); bool instantiate_axiom2b_for(theory_var v); virtual final_check_status assert_delayed_axioms(); final_check_status mk_interface_eqs_at_final_check(); static void display_ids(std::ostream & out, unsigned n, enode * const * v); public: theory_array(ast_manager & m, theory_array_params & params); virtual ~theory_array(); virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_array, get_manager(), m_params); } virtual char const * get_name() const { return "array"; } virtual void display_var(std::ostream & out, theory_var v) const; virtual void display(std::ostream & out) const; virtual void collect_statistics(::statistics & st) const; th_trail_stack & get_trail_stack() { return m_trail_stack; } virtual void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var); static void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {} void unmerge_eh(theory_var v1, theory_var v2); }; }; #endif /* THEORY_ARRAY_H_ */ z3-z3-4.4.1/src/smt/theory_array_base.cpp000066400000000000000000001062251260446376700202330ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_base.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-02. Revision History: --*/ #include"smt_context.h" #include"theory_array_base.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"smt_model_generator.h" #include"func_interp.h" #include"ast_smt2_pp.h" namespace smt { theory_array_base::theory_array_base(ast_manager & m): theory(m.mk_family_id("array")), m_found_unsupported_op(false) { } void theory_array_base::found_unsupported_op(expr * n) { TRACE("theory_array_unsup", tout << mk_ll_pp(n, get_manager()) << "\n";); if (!m_found_unsupported_op) { get_context().push_trail(value_trail(m_found_unsupported_op)); m_found_unsupported_op = true; } } app * theory_array_base::mk_select(unsigned num_args, expr * const * args) { app * r = get_manager().mk_app(get_family_id(), OP_SELECT, 0, 0, num_args, args); TRACE("mk_var_bug", tout << "mk_select: " << r->get_id() << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_id(); tout << "\n";); return r; } app * theory_array_base::mk_store(unsigned num_args, expr * const * args) { return get_manager().mk_app(get_family_id(), OP_STORE, 0, 0, num_args, args); } app * theory_array_base::mk_default(expr * a) { sort * s = get_manager().get_sort(a); unsigned num_params = get_dimension(s); parameter const* params = s->get_info()->get_parameters(); return get_manager().mk_app(get_family_id(), OP_ARRAY_DEFAULT, num_params, params, 1, & a); } unsigned theory_array_base::get_dimension(sort * s) const { SASSERT(s->is_sort_of(get_family_id(), ARRAY_SORT)); SASSERT(s->get_info()->get_num_parameters() >= 2); return s->get_info()->get_num_parameters()-1; } void theory_array_base::assert_axiom(unsigned num_lits, literal * lits) { context & ctx = get_context(); TRACE("array_axiom", tout << "literals:\n"; for (unsigned i = 0; i < num_lits; ++i) { expr * e = ctx.bool_var2expr(lits[i].var()); if (lits[i].sign()) tout << "not "; tout << mk_pp(e, get_manager()) << " "; tout << "\n"; }); ctx.mk_th_axiom(get_id(), num_lits, lits); } void theory_array_base::assert_axiom(literal l1, literal l2) { literal ls[2] = { l1, l2 }; assert_axiom(2, ls); } void theory_array_base::assert_axiom(literal l) { assert_axiom(1, &l); } void theory_array_base::assert_store_axiom1_core(enode * e) { app * n = e->get_owner(); SASSERT(is_store(n)); context & ctx = get_context(); ast_manager & m = get_manager(); ptr_buffer sel_args; unsigned num_args = n->get_num_args(); SASSERT(num_args >= 3); sel_args.push_back(n); for (unsigned i = 1; i < num_args - 1; ++i) { sel_args.push_back(to_app(n->get_arg(i))); } expr_ref sel(m); sel = mk_select(sel_args.size(), sel_args.c_ptr()); expr * val = n->get_arg(num_args - 1); TRACE("array", tout << mk_bounded_pp(sel, m) << " = " << mk_bounded_pp(val, m) << "\n";); if (m.proofs_enabled()) { literal l(mk_eq(sel, val, true)); ctx.mark_as_relevant(l); assert_axiom(l); } else { TRACE("mk_var_bug", tout << "mk_sel: " << sel->get_id() << "\n";); ctx.internalize(sel, false); ctx.assign_eq(ctx.get_enode(sel), ctx.get_enode(val), eq_justification::mk_axiom()); ctx.mark_as_relevant(sel.get()); } } /** \brief Assert axiom 2: FORALL a, i_i, ..., i_n, j_1, ..., j_n i_1 /= j_1 => select(store(a, i_1, ..., i_n, v), j_1, ..., j_n) = select(a, j_1, ..., j_n) and ... and i_n /= j_n => select(store(a, i_1, ..., i_n, v), j_1, ..., j_n) = select(a, j_1, ..., j_n) */ void theory_array_base::assert_store_axiom2_core(enode * store, enode * select) { TRACE("array", tout << "generating axiom2: #" << store->get_owner_id() << " #" << select->get_owner_id() << "\n"; tout << mk_bounded_pp(store->get_owner(), get_manager()) << "\n" << mk_bounded_pp(select->get_owner(), get_manager()) << "\n";); SASSERT(is_store(store)); SASSERT(is_select(select)); SASSERT(store->get_num_args() == 1 + select->get_num_args()); ptr_buffer sel1_args, sel2_args; context & ctx = get_context(); ast_manager & m = get_manager(); enode * a = store->get_arg(0); enode * const * is = select->get_args() + 1; enode * const * js = store->get_args() + 1; unsigned num_args = select->get_num_args() - 1; sel1_args.push_back(store->get_owner()); sel2_args.push_back(a->get_owner()); for (unsigned i = 0; i < num_args; i++) { sel1_args.push_back(is[i]->get_owner()); sel2_args.push_back(is[i]->get_owner()); } expr_ref sel1(m), sel2(m); bool init = false; literal conseq = null_literal; expr * conseq_expr = 0; for (unsigned i = 0; i < num_args; i++) { enode * idx1 = js[i]; enode * idx2 = is[i]; if (idx1->get_root() == idx2->get_root()) { TRACE("array_bug", tout << "indexes are equal... skipping...\n";); continue; } if (!init) { sel1 = mk_select(sel1_args.size(), sel1_args.c_ptr()); sel2 = mk_select(sel2_args.size(), sel2_args.c_ptr()); if (sel1 == sel2) { TRACE("array_bug", tout << "sel1 and sel2 are equal:\n";); break; } init = true; TRACE("array", tout << mk_bounded_pp(sel1, m) << " " << mk_bounded_pp(sel2, m) << "\n";); conseq = mk_eq(sel1, sel2, true); conseq_expr = ctx.bool_var2expr(conseq.var()); } literal ante = mk_eq(idx1->get_owner(), idx2->get_owner(), true); ctx.mark_as_relevant(ante); // ctx.force_phase(ante); ctx.add_rel_watch(~ante, conseq_expr); // ctx.mark_as_relevant(conseq_expr); TRACE("array", tout << "asserting axiom2: " << ante << "\n";); TRACE("array_map_bug", tout << "axiom2:\n"; tout << mk_ismt2_pp(idx1->get_owner(), m) << "\n=\n" << mk_ismt2_pp(idx2->get_owner(), m); tout << "\nimplies\n" << mk_ismt2_pp(conseq_expr, m) << "\n";); assert_axiom(ante, conseq); } } bool theory_array_base::assert_store_axiom2(enode * store, enode * select) { unsigned num_args = select->get_num_args(); unsigned i = 1; for (; i < num_args; i++) if (store->get_arg(i)->get_root() != select->get_arg(i)->get_root()) break; if (i == num_args) return false; if (get_context().add_fingerprint(store, store->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) { TRACE("array", tout << "adding axiom2 to todo queue\n";); m_axiom2_todo.push_back(std::make_pair(store, select)); return true; } TRACE("array", tout << "axiom already instantiated: #" << store->get_owner_id() << " #" << select->get_owner_id() << "\n";); return false; } func_decl_ref_vector * theory_array_base::register_sort(sort * s_array) { unsigned dimension = get_dimension(s_array); func_decl_ref_vector * ext_skolems = 0; if (!m_sort2skolem.find(s_array, ext_skolems)) { ast_manager & m = get_manager(); ext_skolems = alloc(func_decl_ref_vector, m); for (unsigned i = 0; i < dimension; ++i) { sort * ext_sk_domain[2] = { s_array, s_array }; parameter p(i); func_decl * ext_sk_decl = m.mk_func_decl(get_id(), OP_ARRAY_EXT_SKOLEM, 1, &p, 2, ext_sk_domain); ext_skolems->push_back(ext_sk_decl); } m_sort2skolem.insert(s_array, ext_skolems); m_sorts_trail.push_back(s_array); } return ext_skolems; } bool theory_array_base::value_eq_proc::operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == n2->get_num_args()); unsigned n = n1->get_num_args(); // skipping first argument of the select. for(unsigned i = 1; i < n; i++) { if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) { return false; } } return true; } /** \brief Return true if there is a select(v1', i1) and a select(v2', i2) such that: v1' = v1, v2' = v2, i1 = i2, select(v1', i1) /= select(v2', i2) in the logical context. */ bool theory_array_base::already_diseq(enode * v1, enode * v2) { context & ctx = get_context(); enode * r1 = v1->get_root(); enode * r2 = v2->get_root(); if (r1->get_class_size() > r2->get_class_size()) { std::swap(r1, r2); } m_array_value.reset(); // populate m_array_value if the select(a, i) parent terms of r1 enode_vector::const_iterator it = r1->begin_parents(); enode_vector::const_iterator end = r1->end_parents(); for (; it != end; ++it) { enode* parent = *it; if (parent->is_cgr() && ctx.is_relevant(parent) && is_select(parent->get_owner()) && parent->get_arg(0)->get_root() == r1) { m_array_value.insert(parent); } } // traverse select(a, i) parent terms of r2 trying to find a match. it = r2->begin_parents(); end = r2->end_parents(); for (; it != end; ++it) { enode * parent = *it; enode * other; if (parent->is_cgr() && ctx.is_relevant(parent) && is_select(parent->get_owner()) && parent->get_arg(0)->get_root() == r2 && m_array_value.find(parent, other)) { if (ctx.is_diseq(parent, other)) { TRACE("array_ext", tout << "selects are disequal\n";); return true; } } } return false; } bool theory_array_base::assert_extensionality(enode * n1, enode * n2) { context & ctx = get_context(); if (n1->get_owner_id() > n2->get_owner_id()) std::swap(n1, n2); enode * nodes[2] = { n1, n2 }; if (!ctx.add_fingerprint(this, 0, 2, nodes)) return false; // axiom was already instantiated if (already_diseq(n1, n2)) return false; m_extensionality_todo.push_back(std::make_pair(n1, n2)); return true; } void theory_array_base::assert_extensionality_core(enode * n1, enode * n2) { app * e1 = n1->get_owner(); app * e2 = n2->get_owner(); context & ctx = get_context(); ast_manager & m = get_manager(); func_decl_ref_vector * funcs = 0; sort * s = m.get_sort(e1); if (!m_sort2skolem.find(s, funcs)) { UNREACHABLE(); return; } unsigned dimension = funcs->size(); expr_ref_vector args1(m), args2(m); args1.push_back(e1); args2.push_back(e2); for (unsigned i = 0; i < dimension; i++) { expr * k = m.mk_app((*funcs)[i].get(), e1, e2); args1.push_back(k); args2.push_back(k); } expr * sel1 = mk_select(dimension+1, args1.c_ptr()); expr * sel2 = mk_select(dimension+1, args2.c_ptr()); TRACE("ext", tout << mk_bounded_pp(sel1, m) << "\n" << mk_bounded_pp(sel2, m) << "\n";); literal n1_eq_n2 = mk_eq(e1, e2, true); literal sel1_eq_sel2 = mk_eq(sel1, sel2, true); ctx.mark_as_relevant(n1_eq_n2); ctx.mark_as_relevant(sel1_eq_sel2); assert_axiom(n1_eq_n2, ~sel1_eq_sel2); } bool theory_array_base::can_propagate() { return !m_axiom1_todo.empty() || !m_axiom2_todo.empty() || !m_extensionality_todo.empty(); } void theory_array_base::propagate() { while (can_propagate()) { for (unsigned i = 0; i < m_axiom1_todo.size(); i++) assert_store_axiom1_core(m_axiom1_todo[i]); m_axiom1_todo.reset(); for (unsigned i = 0; i < m_axiom2_todo.size(); i++) assert_store_axiom2_core(m_axiom2_todo[i].first, m_axiom2_todo[i].second); m_axiom2_todo.reset(); for (unsigned i = 0; i < m_extensionality_todo.size(); i++) assert_extensionality_core(m_extensionality_todo[i].first, m_extensionality_todo[i].second); m_extensionality_todo.reset(); } } /** \brief Return true if v is shared between two different "instances" of the array theory. It is shared if it is used in more than one role. The possible roles are: array, index, and value. Example: (store v i j) <--- v is used as an array (select A v) <--- v is used as an index (store A i v) <--- v is used as an value */ bool theory_array_base::is_shared(theory_var v) const { enode * n = get_enode(v); enode * r = n->get_root(); bool is_array = false; bool is_index = false; bool is_value = false; int num_roles = 0; #define SET_ARRAY(arg) if (arg->get_root() == r && !is_array) { is_array = true; num_roles++; } if (num_roles > 1) return true #define SET_INDEX(arg) if (arg->get_root() == r && !is_index) { is_index = true; num_roles++; } if (num_roles > 1) return true #define SET_VALUE(arg) if (arg->get_root() == r && !is_value) { is_value = true; num_roles++; } if (num_roles > 1) return true enode_vector::const_iterator it = r->begin_parents(); enode_vector::const_iterator end = r->end_parents(); for (; it != end; ++it) { enode * parent = *it; #if 0 if (!ctx.is_relevant(parent)) continue; #endif unsigned num_args = parent->get_num_args(); if (is_store(parent)) { SET_ARRAY(parent->get_arg(0)); for (unsigned i = 1; i < num_args - 1; i++) { SET_INDEX(parent->get_arg(i)); } SET_VALUE(parent->get_arg(num_args - 1)); } else if (is_select(parent)) { SET_ARRAY(parent->get_arg(0)); for (unsigned i = 1; i < num_args; i++) { SET_INDEX(parent->get_arg(i)); } } else if (is_const(parent)) { SET_VALUE(parent->get_arg(0)); } } return false; } #if 0 void theory_array_base::collect_shared_vars(sbuffer & result) { TRACE("array_shared", tout << "collecting shared vars...\n";); context & ctx = get_context(); ptr_buffer to_unmark; unsigned num_vars = get_num_vars(); for (unsigned i = 0; i < num_vars; i++) { enode * n = get_enode(i); if (ctx.is_relevant(n) && ctx.is_shared(n)) { enode * r = n->get_root(); if (!r->is_marked() && is_array_sort(r)) { TRACE("array_shared", tout << "new shared var: #" << r->get_owner_id() << "\n";); r->set_mark(); to_unmark.push_back(r); theory_var r_th_var = r->get_th_var(get_id()); SASSERT(r_th_var != null_theory_var); result.push_back(r_th_var); } } } unmark_enodes(to_unmark.size(), to_unmark.c_ptr()); } #else void theory_array_base::collect_shared_vars(sbuffer & result) { TRACE("array_shared", tout << "collecting shared vars...\n";); context & ctx = get_context(); ptr_buffer to_unmark; unsigned num_vars = get_num_vars(); for (unsigned i = 0; i < num_vars; i++) { enode * n = get_enode(i); if (ctx.is_relevant(n)) { enode * r = n->get_root(); if (!r->is_marked()){ if(is_array_sort(r) && ctx.is_shared(r)) { TRACE("array_shared", tout << "new shared var: #" << r->get_owner_id() << "\n";); theory_var r_th_var = r->get_th_var(get_id()); SASSERT(r_th_var != null_theory_var); result.push_back(r_th_var); } r->set_mark(); to_unmark.push_back(r); } } } unmark_enodes(to_unmark.size(), to_unmark.c_ptr()); } #endif /** \brief Create interface variables for shared array variables. Return the number of new interface equalities. */ unsigned theory_array_base::mk_interface_eqs() { context & ctx = get_context(); ast_manager & m = get_manager(); sbuffer roots; collect_shared_vars(roots); unsigned result = 0; sbuffer::iterator it1 = roots.begin(); sbuffer::iterator end1 = roots.end(); for (; it1 != end1; ++it1) { TRACE("array_bug", tout << "mk_interface_eqs: processing: v" << *it1 << "\n";); theory_var v1 = *it1; enode * n1 = get_enode(v1); sort * s1 = m.get_sort(n1->get_owner()); sbuffer::iterator it2 = it1; ++it2; for (; it2 != end1; ++it2) { theory_var v2 = *it2; enode * n2 = get_enode(v2); sort * s2 = m.get_sort(n2->get_owner()); if (s1 == s2 && !ctx.is_diseq(n1, n2)) { app * eq = mk_eq_atom(n1->get_owner(), n2->get_owner()); if (!ctx.b_internalized(eq) || !ctx.is_relevant(eq)) { result++; ctx.internalize(eq, true); ctx.mark_as_relevant(eq); } } } } return result; } void theory_array_base::push_scope_eh() { m_scopes.push_back(scope(m_sorts_trail.size())); theory::push_scope_eh(); } void theory_array_base::pop_scope_eh(unsigned num_scopes) { reset_queues(); scope const & s = m_scopes[m_scopes.size() - num_scopes]; restore_sorts(s.m_sorts_trail_lim); m_scopes.shrink(m_scopes.size()-num_scopes); theory::pop_scope_eh(num_scopes); } void theory_array_base::restore_sorts(unsigned old_size) { while (m_sorts_trail.size() > old_size) { sort * s = m_sorts_trail.back(); func_decl_ref_vector * funcs = 0; if (m_sort2skolem.find(s, funcs)) { m_sort2skolem.remove(s); dealloc(funcs); } m_sorts_trail.pop_back(); } } void theory_array_base::reset_eh() { reset_queues(); pop_scope_eh(0); theory::reset_eh(); } void theory_array_base::reset_queues() { m_axiom1_todo.reset(); m_axiom2_todo.reset(); m_extensionality_todo.reset(); } void theory_array_base::set_default(theory_var v, enode* n) { TRACE("array", tout << "set default: " << v << " " << mk_pp(n->get_owner(), get_manager()) << "\n";); v = mg_find(v); if (m_defaults[v] == 0) { m_defaults[v] = n; } } enode* theory_array_base::get_default(theory_var v) { return m_defaults[mg_find(v)]; } theory_var theory_array_base::mg_find(theory_var n) { if (m_parents[n] < 0) { return n; } theory_var n0 = n; n = m_parents[n0]; if (m_parents[n] < -1) { return n; } while (m_parents[n] >= 0) { n = m_parents[n]; } // compress path. while (m_parents[n0] >= 0) { theory_var n1 = m_parents[n0]; m_parents[n0] = n; n0 = n1; } return n; } void theory_array_base::mg_merge(theory_var n, theory_var m) { n = mg_find(n); m = mg_find(m); if (n != m) { SASSERT(m_parents[n] < 0); SASSERT(m_parents[m] < 0); if (m_parents[n] > m_parents[m]) { std::swap(n, m); } m_parents[n] += m_parents[m]; m_parents[m] = n; if (m_defaults[n] == 0) { m_defaults[n] = m_defaults[m]; } CTRACE("array", m_defaults[m], tout << mk_pp(m_defaults[m]->get_root()->get_owner(), get_manager()) << "\n"; tout << mk_pp(m_defaults[n]->get_root()->get_owner(), get_manager()) << "\n"; ); // NB. it may be the case that m_defaults[m] != m_defaults[n] // when m and n are finite arrays. } } void theory_array_base::init_model(model_generator & m) { m_factory = alloc(array_factory, get_manager(), m.get_model()); m.register_factory(m_factory); m_use_unspecified_default = is_unspecified_default_ok(); collect_defaults(); collect_selects(); propagate_selects(); } /** \brief It is ok to use an unspecified default value for arrays, when the logical context does not contain store, default and const terms. That is, other modules (such as smt_model_finder) may set the default value to an arbitrary value. */ bool theory_array_base::is_unspecified_default_ok() const { context & ctx = get_context(); int num_vars = get_num_vars(); for (theory_var v = 0; v < num_vars; ++v) { enode * n = get_enode(v); // If n is not relevant, then it should not be used to set defaults. if (!ctx.is_relevant(n)) continue; if (is_store(n) || is_const(n) || is_default(n)) return false; } return true; } void theory_array_base::collect_defaults() { int num_vars = get_num_vars(); m_defaults.reset(); m_else_values.reset(); m_parents.reset(); m_parents.resize(num_vars, -1); m_defaults.resize(num_vars, 0); m_else_values.resize(num_vars, 0); if (m_use_unspecified_default) return; context & ctx = get_context(); // // Create equivalence classes for defaults. // for (theory_var v = 0; v < num_vars; ++v) { enode * n = get_enode(v); // If n is not relevant, then it should not be used to set defaults. if (!ctx.is_relevant(n)) continue; theory_var r = get_representative(v); mg_merge(v, r); if (is_store(n)) { theory_var w = n->get_arg(0)->get_th_var(get_id()); SASSERT(w != null_theory_var); mg_merge(v, get_representative(w)); TRACE("array", tout << "merge: " << mk_pp(n->get_owner(), get_manager()) << " " << v << " " << w << "\n";); } else if (is_const(n)) { set_default(v, n->get_arg(0)); } else if (is_default(n)) { theory_var w = n->get_arg(0)->get_th_var(get_id()); SASSERT(w != null_theory_var); set_default(w, n); } } } unsigned theory_array_base::sel_hash::operator()(enode * n) const { return get_composite_hash(n, n->get_num_args() - 1, sel_khasher(), sel_chasher()); } bool theory_array_base::sel_eq::operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == n2->get_num_args()); unsigned num_args = n1->get_num_args(); for (unsigned i = 1; i < num_args; i++) { if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) return false; } return true; } theory_array_base::select_set * theory_array_base::get_select_set(enode * n) { enode * r = n->get_root(); select_set * set = 0; m_selects.find(r, set); if (set == 0) { set = alloc(select_set); m_selects.insert(r, set); m_selects_domain.push_back(r); m_selects_range.push_back(set); } return set; } void theory_array_base::collect_selects() { int num_vars = get_num_vars(); m_selects.reset(); m_selects_domain.reset(); m_selects_range.reset(); for (theory_var v = 0; v < num_vars; ++v) { enode * r = get_enode(v)->get_root(); if (is_representative(v) && get_context().is_relevant(r)) { enode_vector::iterator it = r->begin_parents(); enode_vector::iterator end = r->end_parents(); for (; it != end; ++it) { enode * parent = *it; if (parent->get_cg() == parent && get_context().is_relevant(parent) && is_select(parent) && parent->get_arg(0)->get_root() == r) { select_set * s = get_select_set(r); SASSERT(!s->contains(parent) || (*(s->find(parent)))->get_root() == parent->get_root()); s->insert(parent); } } } } } void theory_array_base::propagate_select_to_store_parents(enode * r, enode * sel, svector & todo) { SASSERT(r->get_root() == r); SASSERT(is_select(sel)); if (!get_context().is_relevant(r)) { return; } ptr_vector::const_iterator it = r->begin_parents(); ptr_vector::const_iterator end = r->end_parents(); for (; it != end; ++it) { enode * parent = *it; if (get_context().is_relevant(parent) && is_store(parent) && parent->get_arg(0)->get_root() == r) { // propagate upward select_set * parent_sel_set = get_select_set(parent); enode * parent_root = parent->get_root(); if (parent_sel_set->contains(sel)) continue; SASSERT(sel->get_num_args() + 1 == parent->get_num_args()); // check whether the sel idx was overwritten by the store unsigned num_args = sel->get_num_args(); unsigned i = 1; for (; i < num_args; i++) { if (sel->get_arg(i)->get_root() != parent->get_arg(i)->get_root()) break; } if (i < num_args) { SASSERT(!parent_sel_set->contains(sel) || (*(parent_sel_set->find(sel)))->get_root() == sel->get_root()); parent_sel_set->insert(sel); todo.push_back(std::make_pair(parent_root, sel)); } } } } void theory_array_base::propagate_selects_to_store_parents(enode * r, svector & todo) { select_set * sel_set = get_select_set(r); select_set::iterator it2 = sel_set->begin(); select_set::iterator end2 = sel_set->end(); for (; it2 != end2; ++it2) { enode * sel = *it2; SASSERT(is_select(sel)); propagate_select_to_store_parents(r, sel, todo); } } void theory_array_base::propagate_selects() { svector todo; ptr_vector::const_iterator it = m_selects_domain.begin(); ptr_vector::const_iterator end = m_selects_domain.end(); for (; it != end; ++it) { enode * r = *it; propagate_selects_to_store_parents(r, todo); } for (unsigned qhead = 0; qhead < todo.size(); qhead++) { enode_pair & pair = todo[qhead]; enode * r = pair.first; enode * sel = pair.second; propagate_select_to_store_parents(r, sel, todo); } } void theory_array_base::finalize_model(model_generator & m) { std::for_each(m_selects_range.begin(), m_selects_range.end(), delete_proc()); } class array_value_proc : public model_value_proc { family_id m_fid; sort * m_sort; unsigned m_num_entries; unsigned m_dim; //!< number of dimensions; app * m_else; bool m_unspecified_else; svector m_dependencies; public: array_value_proc(family_id fid, sort * s, extra_fresh_value * v): m_fid(fid), m_sort(s), m_num_entries(0), m_dim(0), m_else(0), m_unspecified_else(false) { m_dependencies.push_back(model_value_dependency(v)); } array_value_proc(family_id fid, sort * s, app * else_value): m_fid(fid), m_sort(s), m_num_entries(0), m_dim(0), m_else(else_value), m_unspecified_else(false) { } array_value_proc(family_id fid, sort * s, enode * else_value): m_fid(fid), m_sort(s), m_num_entries(0), m_dim(0), m_else(0), m_unspecified_else(false) { m_dependencies.push_back(model_value_dependency(else_value)); } array_value_proc(family_id fid, sort * s): m_fid(fid), m_sort(s), m_num_entries(0), m_dim(0), m_else(0), m_unspecified_else(true) { } virtual ~array_value_proc() {} void add_entry(unsigned num_args, enode * const * args, enode * value) { SASSERT(num_args > 0); SASSERT(m_dim == 0 || m_dim == num_args); m_dim = num_args; m_num_entries ++; for (unsigned i = 0; i < num_args; i++) m_dependencies.push_back(model_value_dependency(args[i])); m_dependencies.push_back(model_value_dependency(value)); } virtual void get_dependencies(buffer & result) { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } virtual app * mk_value(model_generator & mg, ptr_vector & values) { // values must have size = m_num_entries * (m_dim + 1) + ((m_else || m_unspecified_else) ? 0 : 1) // an array value is a lookup table + else_value // each entry has m_dim indexes that map to a value. ast_manager & m = mg.get_manager(); SASSERT(values.size() == m_dependencies.size()); SASSERT(values.size() == m_num_entries * (m_dim + 1) + ((m_else || m_unspecified_else) ? 0 : 1)); unsigned arity = get_array_arity(m_sort); func_decl * f = mk_aux_decl_for_array_sort(m, m_sort); func_interp * fi = alloc(func_interp, m, arity); mg.get_model().register_decl(f, fi); unsigned idx = 0; if (m_else || m_unspecified_else) { fi->set_else(m_else); } else { fi->set_else(to_app(values[0])); idx = 1; } ptr_buffer args; for (unsigned i = 0; i < m_num_entries; i++) { args.reset(); // copy indices for (unsigned j = 0; j < m_dim; j++, idx++) args.push_back(values[idx]); expr * result = values[idx]; idx++; fi->insert_entry(args.c_ptr(), result); } parameter p[1] = { parameter(f) }; return m.mk_app(m_fid, OP_AS_ARRAY, 1, p); } }; model_value_proc * theory_array_base::mk_value(enode * n, model_generator & m) { SASSERT(get_context().is_relevant(n)); theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); sort * s = get_manager().get_sort(n->get_owner()); enode * else_val_n = get_default(v); array_value_proc * result = 0; if (m_use_unspecified_default) { SASSERT(else_val_n == 0); result = alloc(array_value_proc, get_id(), s); } else { if (else_val_n != 0) { SASSERT(get_context().is_relevant(else_val_n)); result = alloc(array_value_proc, get_id(), s, else_val_n); } else { theory_var r = mg_find(v); void * else_val = m_else_values[r]; // DISABLED. It seems wrong, since different nodes can share the same // else_val according to the mg class. // SASSERT(else_val == 0 || get_context().is_relevant(UNTAG(app*, else_val))); if (else_val == 0) { sort * range = to_sort(s->get_parameter(s->get_num_parameters() - 1).get_ast()); // IMPORTANT: // The implementation should not assume a fresh value is created for // the else_val if the range is finite if (range->is_infinite()) else_val = TAG(void*, m.mk_extra_fresh_value(range), 1); else else_val = TAG(void*, m.get_some_value(range), 0); m_else_values[r] = else_val; } if (GET_TAG(else_val) == 0) { result = alloc(array_value_proc, get_id(), s, UNTAG(app*, else_val)); } else { result = alloc(array_value_proc, get_id(), s, UNTAG(extra_fresh_value*, else_val)); } } } SASSERT(result != 0); select_set * sel_set = 0; m_selects.find(n->get_root(), sel_set); if (sel_set != 0) { ptr_buffer args; select_set::iterator it = sel_set->begin(); select_set::iterator end = sel_set->end(); for (; it != end; ++it) { enode * select = *it; args.reset(); unsigned num = select->get_num_args(); for (unsigned j = 1; j < num; ++j) args.push_back(select->get_arg(j)); SASSERT(get_context().is_relevant(select)); result->add_entry(args.size(), args.c_ptr(), select); } } return result; } }; z3-z3-4.4.1/src/smt/theory_array_base.h000066400000000000000000000170731260446376700177020ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_base.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-02. Revision History: --*/ #ifndef THEORY_ARRAY_BASE_H_ #define THEORY_ARRAY_BASE_H_ #include"smt_theory.h" #include"array_decl_plugin.h" #include"array_factory.h" namespace smt { class theory_array_base : public theory { protected: bool m_found_unsupported_op; void found_unsupported_op(expr * n); bool is_store(app const* n) const { return n->is_app_of(get_id(), OP_STORE); } bool is_map(app const* n) const { return n->is_app_of(get_id(), OP_ARRAY_MAP); } bool is_select(app const* n) const { return n->is_app_of(get_id(), OP_SELECT); } bool is_default(app const* n) const { return n->is_app_of(get_id(), OP_ARRAY_DEFAULT); } bool is_const(app const* n) const { return n->is_app_of(get_id(), OP_CONST_ARRAY); } bool is_array_ext(app const * n) const { return n->is_app_of(get_id(), OP_ARRAY_EXT_SKOLEM); } bool is_as_array(app const * n) const { return n->is_app_of(get_id(), OP_AS_ARRAY); } bool is_array_sort(sort const* s) const { return s->is_sort_of(get_id(), ARRAY_SORT); } bool is_array_sort(app const* n) const { return is_array_sort(get_manager().get_sort(n)); } bool is_store(enode const * n) const { return is_store(n->get_owner()); } bool is_map(enode const* n) const { return is_map(n->get_owner()); } bool is_select(enode const* n) const { return is_select(n->get_owner()); } bool is_const(enode const* n) const { return is_const(n->get_owner()); } bool is_as_array(enode const * n) const { return is_as_array(n->get_owner()); } bool is_default(enode const* n) const { return is_default(n->get_owner()); } bool is_array_sort(enode const* n) const { return is_array_sort(n->get_owner()); } app * mk_select(unsigned num_args, expr * const * args); app * mk_store(unsigned num_args, expr * const * args); app * mk_default(expr* a); unsigned get_dimension(sort* s) const; ptr_vector m_axiom1_todo; svector > m_axiom2_todo; svector > m_extensionality_todo; void assert_axiom(unsigned num_lits, literal * lits); void assert_axiom(literal l1, literal l2); void assert_axiom(literal l); void assert_store_axiom1_core(enode * n); void assert_store_axiom2_core(enode * store, enode * select); void assert_store_axiom1(enode * n) { m_axiom1_todo.push_back(n); } bool assert_store_axiom2(enode * store, enode * select); void assert_extensionality_core(enode * a1, enode * a2); bool assert_extensionality(enode * a1, enode * a2); // -------------------------------------------------- // Array sort -> extensionality skolems // // -------------------------------------------------- ptr_vector m_sorts_trail; obj_map m_sort2skolem; func_decl_ref_vector * register_sort(sort * s_array); // -------------------------------------------------- // array_value table // // Use select(A, i) nodes to represent an assignment for A. // This structure is used to minimize the number of times the // extensionality axiom is applied. // // -------------------------------------------------- struct value_chasher { unsigned operator()(enode const * n, unsigned idx) const { return n->get_arg(idx+1)->get_root()->hash(); } }; struct value_khasher { unsigned operator()(enode * n) const { return 17; } }; struct value_hash_proc { unsigned operator()(enode * n) const { return get_composite_hash(n, n->get_num_args() - 1); } }; struct value_eq_proc { bool operator()(enode * n1, enode * n2) const; }; typedef ptr_hashtable array_value; array_value m_array_value; bool already_diseq(enode * v1, enode * v2); // -------------------------------------------------- // Backtracking // // // -------------------------------------------------- struct scope { unsigned m_sorts_trail_lim; scope(unsigned l):m_sorts_trail_lim(l) {} }; svector m_scopes; void restore_sorts(unsigned old_size); // -------------------------------------------------- // Interface // // // -------------------------------------------------- virtual bool is_shared(theory_var v) const; void collect_shared_vars(sbuffer & result); unsigned mk_interface_eqs(); virtual bool can_propagate(); virtual void propagate(); virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual void reset_eh(); void reset_queues(); // ----------------------------------- // // Model generation // // ----------------------------------- // I need a set of select enodes where select(A,i) = select(B,j) if i->get_root() == j->get_root() struct sel_khasher { unsigned operator()(enode const * n) const { return 0; } }; struct sel_chasher { unsigned operator()(enode const * n, unsigned idx) const { return n->get_arg(idx+1)->get_root()->hash(); } }; struct sel_hash { unsigned operator()(enode * n) const; }; struct sel_eq { bool operator()(enode * n1, enode * n2) const; }; typedef ptr_hashtable select_set; array_factory * m_factory; ptr_vector m_defaults; // temporary field for model construction ptr_vector m_else_values; // tagged pointer: expr or extra_fresh_value svector m_parents; // temporary field for model construction obj_map m_selects; // mapping from array -> relevant selects ptr_vector m_selects_domain; ptr_vector m_selects_range; bool m_use_unspecified_default; // temporary field for model construction theory_var mg_find(theory_var v); void mg_merge(theory_var n, theory_var m); void set_default(theory_var v, enode* n); enode* get_default(theory_var v); virtual void init_model(model_generator & m); bool is_unspecified_default_ok() const; void collect_defaults(); void collect_selects(); void propagate_select_to_store_parents(enode * r, enode * sel, svector & todo); void propagate_selects_to_store_parents(enode * r, svector & todo); void propagate_selects(); select_set * get_select_set(enode * n); virtual void finalize_model(model_generator & m); virtual model_value_proc * mk_value(enode * n, model_generator & m); public: theory_array_base(ast_manager & m); virtual ~theory_array_base() { restore_sorts(0); } }; }; #endif /* THEORY_ARRAY_BASE_H_ */ z3-z3-4.4.1/src/smt/theory_array_full.cpp000066400000000000000000000722221260446376700202620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_full.cpp Abstract: Author: Nikolaj Bjorner 2008-22-10 Revision History: --*/ #include "smt_context.h" #include "theory_array_full.h" #include "ast_ll_pp.h" #include "ast_pp.h" #include "ast_smt2_pp.h" #include "stats.h" namespace smt { theory_array_full::theory_array_full(ast_manager & m, theory_array_params & params) : theory_array(m, params), m_sort2epsilon(m) {} theory_array_full::~theory_array_full() { std::for_each(m_var_data_full.begin(), m_var_data_full.end(), delete_proc()); m_var_data_full.reset(); } void theory_array_full::add_map(theory_var v, enode* s) { if (m_params.m_array_cg && !s->is_cgr()) { return; } SASSERT(is_map(s)); v = find(v); var_data_full * d_full = m_var_data_full[v]; var_data * d = m_var_data[v]; // // TODO: defaulting to exhaustive up-propagation. // instead apply stratified filter. set_prop_upward(v,d); d_full->m_maps.push_back(s); m_trail_stack.push(push_back_trail(d_full->m_maps)); ptr_vector::iterator it = d->m_parent_selects.begin(); ptr_vector::iterator end = d->m_parent_selects.end(); for (; it != end; ++it) { SASSERT(is_select(*it)); instantiate_select_map_axiom(*it, s); } set_prop_upward(s); } bool theory_array_full::instantiate_axiom_map_for(theory_var v) { bool result = false; var_data * d = m_var_data[v]; var_data_full * d_full = m_var_data_full[v]; unsigned num_maps = d_full->m_parent_maps.size(); unsigned num_selects = d->m_parent_selects.size(); for (unsigned i = 0; i < num_maps; ++i) { for (unsigned j = 0; j < num_selects; ++j) { if (instantiate_select_map_axiom(d->m_parent_selects[j], d_full->m_parent_maps[i])) { result = true; } } } return result; } void theory_array_full::add_parent_map(theory_var v, enode* s) { if (m_params.m_array_cg && !s->is_cgr()) { return; } SASSERT(v != null_theory_var); SASSERT(is_map(s)); v = find(v); var_data * d = m_var_data[v]; var_data_full * d_full = m_var_data_full[v]; d_full->m_parent_maps.push_back(s); m_trail_stack.push(push_back_trail(d_full->m_parent_maps)); if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { ptr_vector::iterator it = d->m_parent_selects.begin(); ptr_vector::iterator end = d->m_parent_selects.end(); for (; it != end; ++it) { if (!m_params.m_array_cg || (*it)->is_cgr()) { instantiate_select_map_axiom(*it, s); } } } } // // set set_prop_upward on root and recursively on children if necessary. // void theory_array_full::set_prop_upward(theory_var v) { if (m_params.m_array_weak) return; v = find(v); var_data * d = m_var_data[v]; if (!d->m_prop_upward) { m_trail_stack.push(reset_flag_trail(d->m_prop_upward)); d->m_prop_upward = true; TRACE("array", tout << "#" << v << "\n";); if (!m_params.m_array_delay_exp_axiom) { instantiate_axiom2b_for(v); instantiate_axiom_map_for(v); } var_data_full * d2 = m_var_data_full[v]; ptr_vector::iterator it = d->m_stores.begin(); ptr_vector::iterator end = d->m_stores.end(); for (; it != end; ++it) { set_prop_upward(*it); } it = d2->m_maps.begin(); end = d2->m_maps.end(); for (; it != end; ++it) { set_prop_upward(*it); } it = d2->m_consts.begin(); end = d2->m_consts.end(); for (; it != end; ++it) { set_prop_upward(*it); } } } // // call set_prop_upward on array arguments. // void theory_array_full::set_prop_upward(enode * n) { TRACE("array", tout << mk_pp(n->get_owner(), get_manager()) << "\n";); if (is_store(n)) { set_prop_upward(n->get_arg(0)->get_th_var(get_id())); } else if (is_map(n)) { for (unsigned i = 0; i < n->get_num_args(); ++i) { set_prop_upward(n->get_arg(i)->get_th_var(get_id())); } } } void theory_array_full::set_prop_upward(theory_var v, var_data* d) { if (m_params.m_array_always_prop_upward || d->m_stores.size() >= 1) { theory_array::set_prop_upward(v, d); } else { var_data_full * d2 = m_var_data_full[v]; unsigned sz = d2->m_maps.size(); for(unsigned i = 0; i < sz; ++i) { set_prop_upward(d2->m_maps[i]); } } } unsigned theory_array_full::get_lambda_equiv_size(theory_var v, var_data* d) { var_data_full * d2 = m_var_data_full[v]; return d->m_stores.size() + 2*d2->m_consts.size() + 2*d2->m_maps.size(); } void theory_array_full::add_const(theory_var v, enode* cnst) { var_data * d = m_var_data[v]; unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d); if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) { set_prop_upward(v, d); } ptr_vector & consts = m_var_data_full[v]->m_consts; m_trail_stack.push(push_back_trail(consts)); consts.push_back(cnst); instantiate_default_const_axiom(cnst); ptr_vector::iterator it = d->m_parent_selects.begin(); ptr_vector::iterator end = d->m_parent_selects.end(); for (; it != end; ++it) { SASSERT(is_select(*it)); instantiate_select_const_axiom(*it, cnst); } } void theory_array_full::add_as_array(theory_var v, enode* arr) { var_data * d = m_var_data[v]; unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d); if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) { set_prop_upward(v, d); } ptr_vector & as_arrays = m_var_data_full[v]->m_as_arrays; m_trail_stack.push(push_back_trail(as_arrays)); as_arrays.push_back(arr); instantiate_default_as_array_axiom(arr); ptr_vector::iterator it = d->m_parent_selects.begin(); ptr_vector::iterator end = d->m_parent_selects.end(); for (; it != end; ++it) { SASSERT(is_select(*it)); instantiate_select_as_array_axiom(*it, arr); } } void theory_array_full::reset_eh() { theory_array::reset_eh(); std::for_each(m_var_data_full.begin(), m_var_data_full.end(), delete_proc()); m_var_data_full.reset(); m_eqs.reset(); m_eqsv.reset(); } void theory_array_full::display_var(std::ostream & out, theory_var v) const { theory_array::display_var(out, v); var_data_full const * d = m_var_data_full[v]; out << " maps: {"; display_ids(out, d->m_maps.size(), d->m_maps.c_ptr()); out << "} p_parent_maps: {"; display_ids(out, d->m_parent_maps.size(), d->m_parent_maps.c_ptr()); out << "} p_const: {"; display_ids(out, d->m_consts.size(), d->m_consts.c_ptr()); out << "}\n"; } theory_var theory_array_full::mk_var(enode * n) { theory_var r = theory_array::mk_var(n); SASSERT(r == static_cast(m_var_data_full.size())); m_var_data_full.push_back(alloc(var_data_full)); var_data_full * d = m_var_data_full.back(); if (is_map(n)) { instantiate_default_map_axiom(n); d->m_maps.push_back(n); } else if (is_const(n)) { instantiate_default_const_axiom(n); d->m_consts.push_back(n); } else if (is_default(n)) { // no-op } else if (is_as_array(n)) { instantiate_default_as_array_axiom(n); d->m_as_arrays.push_back(n); } return r; } bool theory_array_full::internalize_atom(app * atom, bool) { return internalize_term(atom); } bool theory_array_full::internalize_term(app * n) { TRACE("array", tout << mk_pp(n, get_manager()) << "\n";); if (is_store(n) || is_select(n)) { return theory_array::internalize_term(n); } if (!is_const(n) && !is_default(n) && !is_map(n) && !is_as_array(n)) { if (!is_array_ext(n)) found_unsupported_op(n); return false; } if (!internalize_term_core(n)) { return true; } context & ctx = get_context(); if (is_map(n)) { for (unsigned i = 0; i < n->get_num_args(); ++i) { enode* arg = ctx.get_enode(n->get_arg(i)); if (!is_attached_to_var(arg)) { mk_var(arg); } } } else if (is_default(n)) { enode* arg0 = ctx.get_enode(n->get_arg(0)); if (!is_attached_to_var(arg0)) { mk_var(arg0); } } enode* node = ctx.get_enode(n); if (!is_attached_to_var(node)) { mk_var(node); } if (is_default(n)) { enode* arg0 = ctx.get_enode(n->get_arg(0)); theory_var v_arg = arg0->get_th_var(get_id()); add_parent_default(v_arg); } else if (is_map(n)) { for (unsigned i = 0; i < n->get_num_args(); ++i) { enode* arg = ctx.get_enode(n->get_arg(i)); theory_var v_arg = arg->get_th_var(get_id()); add_parent_map(v_arg, node); } instantiate_default_map_axiom(node); } else if (is_const(n)) { instantiate_default_const_axiom(node); } else if (is_as_array(n)) { // The array theory is not a decision procedure // for as-array. // Ex: (as-array f) = (as-array g) & f(0) = 0 & g(0) = 1 // There is nothing to propagate the disequality. // Even if there was, as-array on interpreted // functions will be incomplete. // The instantiation operations are still sound to include. found_unsupported_op(n); instantiate_default_as_array_axiom(node); } return true; } void theory_array_full::merge_eh(theory_var v1, theory_var v2, theory_var u, theory_var w) { theory_array::merge_eh(v1, v2, u, w); // v1 is the new root SASSERT(v1 == find(v1)); var_data_full * d2 = m_var_data_full[v2]; ptr_vector::iterator it, end; it = d2->m_maps.begin(); end = d2->m_maps.end(); for (; it != end; ++it) { add_map(v1, *it); } it = d2->m_parent_maps.begin(); end = d2->m_parent_maps.end(); for (; it != end; ++it) { add_parent_map(v1, *it); } it = d2->m_consts.begin(); end = d2->m_consts.end(); for (; it != end; ++it) { add_const(v1, *it); } it = d2->m_as_arrays.begin(); end = d2->m_as_arrays.end(); for (; it != end; ++it) { add_as_array(v1, *it); } TRACE("array", tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n"; tout << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n"; tout << "merge in\n"; display_var(tout, v2); tout << "after merge\n"; display_var(tout, v1);); } void theory_array_full::add_parent_default(theory_var v) { SASSERT(v != null_theory_var); v = find(v); var_data* d = m_var_data[v]; ptr_vector::iterator it, end; it = d->m_stores.begin(); end = d->m_stores.end(); for(; it != end; ++it) { enode * store = *it; SASSERT(is_store(store)); instantiate_default_store_axiom(store); } if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { it = d->m_parent_stores.begin(); end = d->m_parent_stores.end(); for (; it != end; ++it) { enode* store = *it; SASSERT(is_store(store)); if (!m_params.m_array_cg || store->is_cgr()) { instantiate_default_store_axiom(store); } } } } void theory_array_full::add_parent_select(theory_var v, enode * s) { TRACE("array", tout << v << " select parent: " << mk_pp(s->get_owner(), get_manager()) << "\n"; display_var(tout, v); ); theory_array::add_parent_select(v,s); v = find(v); var_data_full* d_full = m_var_data_full[v]; var_data* d = m_var_data[v]; ptr_vector::iterator it = d_full->m_consts.begin(); ptr_vector::iterator end = d_full->m_consts.end(); for (; it != end; ++it) { instantiate_select_const_axiom(s, *it); } it = d_full->m_maps.begin(); end = d_full->m_maps.end(); for (; it != end; ++it) { enode* map = *it; SASSERT(is_map(map)); instantiate_select_map_axiom(s, map); } if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { it = d_full->m_parent_maps.begin(); end = d_full->m_parent_maps.end(); for (; it != end; ++it) { enode* map = *it; SASSERT(is_map(map)); if (!m_params.m_array_cg || map->is_cgr()) { instantiate_select_map_axiom(s, map); } } } } void theory_array_full::relevant_eh(app* n) { TRACE("array", tout << mk_pp(n, get_manager()) << "\n";); theory_array::relevant_eh(n); if (!is_default(n) && !is_select(n) && !is_map(n) && !is_const(n) && !is_as_array(n)) { return; } context & ctx = get_context(); enode* node = ctx.get_enode(n); if (is_select(n)) { enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v = arg->get_th_var(get_id()); SASSERT(v != null_theory_var); add_parent_select(find(v), node); } else if (is_default(n)) { enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v = arg->get_th_var(get_id()); SASSERT(v != null_theory_var); add_parent_default(find(v)); } else if (is_const(n)) { instantiate_default_const_axiom(node); } else if (is_map(n)) { for (unsigned i = 0; i < n->get_num_args(); ++i) { enode* arg = ctx.get_enode(n->get_arg(i)); theory_var v_arg = find(arg->get_th_var(get_id())); add_parent_map(v_arg, node); set_prop_upward(v_arg); } instantiate_default_map_axiom(node); } else if (is_as_array(n)) { instantiate_default_as_array_axiom(node); } } // // Assert axiom: // select(map[f](a, ... d), i) = f(select(a,i),...,select(d,i)) // bool theory_array_full::instantiate_select_map_axiom(enode* sl, enode* mp) { app* map = mp->get_owner(); app* select = sl->get_owner(); SASSERT(is_map(map)); SASSERT(is_select(select)); SASSERT(map->get_num_args() > 0); func_decl* f = to_func_decl(map->get_decl()->get_parameter(0).get_ast()); context& ctx = get_context(); ast_manager& m = get_manager(); TRACE("array_map_bug", tout << "invoked instantiate_select_map_axiom\n"; tout << sl->get_owner_id() << " " << mp->get_owner_id() << "\n"; tout << mk_ismt2_pp(sl->get_owner(), m) << "\n" << mk_ismt2_pp(mp->get_owner(), m) << "\n";); if (!ctx.add_fingerprint(mp, mp->get_owner_id(), sl->get_num_args() - 1, sl->get_args() + 1)) { return false; } TRACE("array_map_bug", tout << "new axiom\n";); m_stats.m_num_map_axiom++; TRACE("array", tout << mk_bounded_pp(mp->get_owner(), get_manager()) << "\n"; tout << mk_bounded_pp(sl->get_owner(), get_manager()) << "\n";); unsigned num_args = select->get_num_args(); unsigned num_arrays = map->get_num_args(); ptr_buffer args1, args2; vector > args2l; args1.push_back(map); for (unsigned j = 0; j < num_arrays; ++j) { ptr_vector arg; arg.push_back(map->get_arg(j)); args2l.push_back(arg); } for (unsigned i = 1; i < num_args; ++i) { expr* arg = select->get_arg(i); for (unsigned j = 0; j < num_arrays; ++j) { args2l[j].push_back(arg); } args1.push_back(arg); } for (unsigned j = 0; j < num_arrays; ++j) { expr* sel = mk_select(args2l[j].size(), args2l[j].c_ptr()); args2.push_back(sel); } expr_ref sel1(m), sel2(m); sel1 = mk_select(args1.size(), args1.c_ptr()); m_simp->mk_app(f, args2.size(), args2.c_ptr(), sel2); ctx.internalize(sel1, false); ctx.internalize(sel2, false); TRACE("array_map_bug", tout << "select-map axiom\n" << mk_ismt2_pp(sel1, m) << "\n=\n" << mk_ismt2_pp(sel2,m) << "\n";); return try_assign_eq(sel1, sel2); } // // // Assert axiom: // default(map[f](a,..,d)) = f(default(a),..,default(d)) // bool theory_array_full::instantiate_default_map_axiom(enode* mp) { SASSERT(is_map(mp)); app* map = mp->get_owner(); context& ctx = get_context(); if (!ctx.add_fingerprint(this, 0, 1, &mp)) { return false; } TRACE("array", tout << mk_bounded_pp(map, get_manager()) << "\n";); m_stats.m_num_default_map_axiom++; func_decl* f = to_func_decl(map->get_decl()->get_parameter(0).get_ast()); SASSERT(map->get_num_args() == f->get_arity()); ptr_buffer args2; for (unsigned i = 0; i < map->get_num_args(); ++i) { args2.push_back(mk_default(map->get_arg(i))); } expr* def1 = mk_default(map); expr_ref def2(get_manager()); m_simp->mk_app(f, args2.size(), args2.c_ptr(), def2); ctx.internalize(def1, false); ctx.internalize(def2, false); return try_assign_eq(def1, def2); } bool theory_array_full::instantiate_default_const_axiom(enode* cnst) { context& ctx = get_context(); if (!ctx.add_fingerprint(this, 0, 1, &cnst)) { return false; } m_stats.m_num_default_const_axiom++; SASSERT(is_const(cnst)); TRACE("array", tout << mk_bounded_pp(cnst->get_owner(), get_manager()) << "\n";); expr* val = cnst->get_arg(0)->get_owner(); expr* def = mk_default(cnst->get_owner()); ctx.internalize(def, false); return try_assign_eq(val, def); } bool theory_array_full::instantiate_default_as_array_axiom(enode* arr) { context& ctx = get_context(); if (!ctx.add_fingerprint(this, 0, 1, &arr)) { return false; } m_stats.m_num_default_as_array_axiom++; SASSERT(is_as_array(arr)); TRACE("array", tout << mk_bounded_pp(arr->get_owner(), get_manager()) << "\n";); expr* def = mk_default(arr->get_owner()); func_decl * f = array_util(get_manager()).get_as_array_func_decl(arr->get_owner()); ptr_vector args; for (unsigned i = 0; i < f->get_arity(); ++i) { args.push_back(mk_epsilon(f->get_domain(i))); } expr_ref val(get_manager().mk_app(f, args.size(), args.c_ptr()), get_manager()); ctx.internalize(def, false); ctx.internalize(val.get(), false); return try_assign_eq(val.get(), def); } bool theory_array_full::has_large_domain(app* array_term) { SASSERT(is_array_sort(array_term)); sort* s = get_manager().get_sort(array_term); unsigned dim = get_dimension(s); parameter const * params = s->get_info()->get_parameters(); rational sz(1); for (unsigned i = 0; i < dim; ++i) { SASSERT(params[i].is_ast()); sort* d = to_sort(params[i].get_ast()); if (d->is_infinite() || d->is_very_big()) { return true; } sz *= rational(d->get_num_elements().size(),rational::ui64()); if (sz >= rational(1 << 20)) { return true; } } return false; } // // Assert axiom: // select(const v, i_1, ..., i_n) = v // bool theory_array_full::instantiate_select_const_axiom(enode* select, enode* cnst) { SASSERT(is_const(cnst)); SASSERT(is_select(select)); SASSERT(cnst->get_num_args() == 1); context& ctx = get_context(); unsigned num_args = select->get_num_args(); if (!ctx.add_fingerprint(cnst, cnst->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) { return false; } m_stats.m_num_select_const_axiom++; ptr_buffer sel_args; sel_args.push_back(cnst->get_owner()); for (unsigned short i = 1; i < num_args; ++i) { sel_args.push_back(select->get_owner()->get_arg(i)); } expr * sel = mk_select(sel_args.size(), sel_args.c_ptr()); expr * val = cnst->get_owner()->get_arg(0); TRACE("array", tout << "new select-const axiom...\n"; tout << "const: " << mk_bounded_pp(cnst->get_owner(), get_manager()) << "\n"; tout << "select: " << mk_bounded_pp(select->get_owner(), get_manager()) << "\n"; tout << " sel/const: " << mk_bounded_pp(sel, get_manager()) << "\n"; tout << "value: " << mk_bounded_pp(val, get_manager()) << "\n"; tout << "#" << sel->get_id() << " = #" << val->get_id() << "\n"; ); ctx.internalize(sel, false); return try_assign_eq(sel,val); } // // Assert axiom: // select(as-array f, i_1, ..., i_n) = (f i_1 ... i_n) // bool theory_array_full::instantiate_select_as_array_axiom(enode* select, enode* arr) { SASSERT(is_as_array(arr->get_owner())); SASSERT(is_select(select)); SASSERT(arr->get_num_args() == 0); context& ctx = get_context(); unsigned num_args = select->get_num_args(); if (!ctx.add_fingerprint(arr, arr->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) { return false; } m_stats.m_num_select_as_array_axiom++; ptr_buffer sel_args; sel_args.push_back(arr->get_owner()); for (unsigned short i = 1; i < num_args; ++i) { sel_args.push_back(select->get_owner()->get_arg(i)); } expr * sel = mk_select(sel_args.size(), sel_args.c_ptr()); func_decl * f = array_util(get_manager()).get_as_array_func_decl(arr->get_owner()); expr_ref val(get_manager().mk_app(f, sel_args.size()-1, sel_args.c_ptr()+1), get_manager()); TRACE("array", tout << "new select-as-array axiom...\n"; tout << "as-array: " << mk_bounded_pp(arr->get_owner(), get_manager()) << "\n"; tout << "select: " << mk_bounded_pp(select->get_owner(), get_manager()) << "\n"; tout << " sel/as-array: " << mk_bounded_pp(sel, get_manager()) << "\n"; tout << "value: " << mk_bounded_pp(val.get(), get_manager()) << "\n"; tout << "#" << sel->get_id() << " = #" << val->get_id() << "\n"; ); ctx.internalize(sel, false); ctx.internalize(val.get(), false); return try_assign_eq(sel,val); } bool theory_array_full::instantiate_default_store_axiom(enode* store) { SASSERT(is_store(store)); SASSERT(store->get_num_args() >= 3); app* store_app = store->get_owner(); context& ctx = get_context(); ast_manager& m = get_manager(); if (!ctx.add_fingerprint(this, 0, 1, &store)) { return false; } m_stats.m_num_default_store_axiom++; app* def1; app* def2; TRACE("array", tout << mk_bounded_pp(store_app, m) << "\n";); if (has_large_domain(store_app)) { def2 = mk_default(store_app->get_arg(0)); } else { // // let A = store(B, i, v) // // Add: // default(A) = ite(epsilon = i, v, default(B)) // expr_ref_vector eqs(m); unsigned num_args = store_app->get_num_args(); for (unsigned i = 1; i + 1 < num_args; ++i) { sort* srt = m.get_sort(store_app->get_arg(i)); app* ep = mk_epsilon(srt); eqs.push_back(m.mk_eq(ep, store_app->get_arg(i))); } expr_ref eq(m); simplifier_plugin* p = m_simp->get_plugin(m.get_basic_family_id()); basic_simplifier_plugin* bp = static_cast(p); bp->mk_and(eqs.size(), eqs.c_ptr(), eq); expr* defA = mk_default(store_app->get_arg(0)); def2 = m.mk_ite(eq, store_app->get_arg(num_args-1), defA); #if 0 // // add soft constraints to guide model construction so that // epsilon agrees with the else case in the model construction. // for (unsigned i = 0; i < eqs.size(); ++i) { // assume_diseq(eqs[i]); } #endif } def1 = mk_default(store_app); ctx.internalize(def1, false); ctx.internalize(def2, false); return try_assign_eq(def1, def2); } app* theory_array_full::mk_epsilon(sort* s) { app* eps = 0; if (m_sort2epsilon.find(s, eps)) { return eps; } eps = get_manager().mk_fresh_const("epsilon", s); m_trail_stack.push( ast2ast_trail(m_sort2epsilon, s, eps)); return eps; } final_check_status theory_array_full::assert_delayed_axioms() { final_check_status r = FC_DONE; if (!m_params.m_array_delay_exp_axiom) { r = FC_DONE; } else { r = theory_array::assert_delayed_axioms(); unsigned num_vars = get_num_vars(); for (unsigned v = 0; v < num_vars; v++) { var_data * d = m_var_data[v]; if (d->m_prop_upward && instantiate_axiom_map_for(v)) r = FC_CONTINUE; } } while (!m_eqsv.empty()) { literal eq = m_eqsv.back(); m_eqsv.pop_back(); get_context().mark_as_relevant(eq); assert_axiom(eq); r = FC_CONTINUE; } if (r == FC_DONE && m_found_unsupported_op) r = FC_GIVEUP; return r; } bool theory_array_full::try_assign_eq(expr* v1, expr* v2) { TRACE("array", tout << mk_bounded_pp(v1, get_manager()) << "\n==\n" << mk_bounded_pp(v2, get_manager()) << "\n";); if (m_eqs.contains(v1, v2)) { return false; } else { m_eqs.insert(v1, v2, true); literal eq(mk_eq(v1, v2, true)); get_context().mark_as_relevant(eq); assert_axiom(eq); // m_eqsv.push_back(eq); return true; } } void theory_array_full::pop_scope_eh(unsigned num_scopes) { unsigned num_old_vars = get_old_num_vars(num_scopes); theory_array::pop_scope_eh(num_scopes); std::for_each(m_var_data_full.begin() + num_old_vars, m_var_data_full.end(), delete_proc()); m_var_data_full.shrink(num_old_vars); m_eqs.reset(); m_eqsv.reset(); } void theory_array_full::collect_statistics(::statistics & st) const { theory_array::collect_statistics(st); st.update("array map ax", m_stats.m_num_map_axiom); st.update("array def const", m_stats.m_num_default_const_axiom); st.update("array sel const", m_stats.m_num_select_const_axiom); st.update("array def store", m_stats.m_num_default_store_axiom); st.update("array def as-array", m_stats.m_num_default_as_array_axiom); st.update("array sel as-array", m_stats.m_num_select_as_array_axiom); } } z3-z3-4.4.1/src/smt/theory_array_full.h000066400000000000000000000063071260446376700177300ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_full.h Abstract: Author: Nikolaj Bjorner 2008-22-10 Revision History: --*/ #ifndef THEORY_ARRAY_FULL_H_ #define THEORY_ARRAY_FULL_H_ #include "theory_array.h" #include "simplifier.h" #include "ast_trail.h" namespace smt { class theory_array_full : public theory_array { struct var_data_full { ptr_vector m_maps; ptr_vector m_consts; ptr_vector m_as_arrays; ptr_vector m_parent_maps; var_data_full() {} }; ptr_vector m_var_data_full; ast2ast_trailmap m_sort2epsilon; simplifier* m_simp; obj_pair_map m_eqs; svector m_eqsv; protected: //virtual final_check_status final_check_eh(); virtual void reset_eh(); virtual void set_prop_upward(theory_var v); virtual void set_prop_upward(enode* n); virtual void set_prop_upward(theory_var v, var_data* d); virtual unsigned get_lambda_equiv_size(theory_var v, var_data* d); virtual bool internalize_term(app * term); virtual bool internalize_atom(app * atom, bool gate_ctx); virtual void pop_scope_eh(unsigned num_scopes); virtual theory_var mk_var(enode * n); virtual void relevant_eh(app * n); void add_const(theory_var v, enode* c); void add_map(theory_var v, enode* s); void add_parent_map(theory_var v, enode* s); void add_as_array(theory_var v, enode* arr); virtual void add_parent_select(theory_var v, enode * s); void add_parent_default(theory_var v); virtual final_check_status assert_delayed_axioms(); bool instantiate_default_const_axiom(enode* cnst); bool instantiate_default_store_axiom(enode* store); bool instantiate_default_map_axiom(enode* map); bool instantiate_default_as_array_axiom(enode* arr); bool has_large_domain(app* array_term); app* mk_epsilon(sort* s); bool instantiate_select_const_axiom(enode* select, enode* cnst); bool instantiate_select_as_array_axiom(enode* select, enode* arr); bool instantiate_select_map_axiom(enode* select, enode* map); bool instantiate_axiom_map_for(theory_var v); bool try_assign_eq(expr* n1, expr* n2); void assign_eqs(); public: theory_array_full(ast_manager & m, theory_array_params & params); virtual ~theory_array_full(); virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_array_full, get_manager(), m_params); } virtual void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var); virtual void display_var(std::ostream & out, theory_var v) const; virtual void collect_statistics(::statistics & st) const; virtual void init(context* ctx) { // the parent class is theory_array. // theory::init(ctx); theory_array::init(ctx); m_simp = &ctx->get_simplifier(); } }; }; #endif /* THEORY_ARRAY_H_ */ z3-z3-4.4.1/src/smt/theory_bv.cpp000066400000000000000000002160461260446376700165350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_bv.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-06. Revision History: --*/ #include"smt_context.h" #include"theory_bv.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"smt_model_generator.h" #include"stats.h" namespace smt { void theory_bv::init(context * ctx) { theory::init(ctx); m_simplifier = &(ctx->get_simplifier()); } theory_var theory_bv::mk_var(enode * n) { theory_var r = theory::mk_var(n); m_find.mk_var(); m_bits.push_back(literal_vector()); m_wpos.push_back(0); m_zero_one_bits.push_back(zero_one_bits()); get_context().attach_th_var(n, this, r); return r; } app * theory_bv::mk_bit2bool(app * bv, unsigned idx) { parameter p(idx); expr * args[1] = {bv}; return get_manager().mk_app(get_id(), OP_BIT2BOOL, 1, &p, 1, args); } void theory_bv::mk_bits(theory_var v) { enode * n = get_enode(v); app * owner = n->get_owner(); unsigned bv_size = get_bv_size(n); context & ctx = get_context(); literal_vector & bits = m_bits[v]; bits.reset(); for (unsigned i = 0; i < bv_size; i++) { app * bit = mk_bit2bool(owner, i); ctx.internalize(bit, true); bool_var b = ctx.get_bool_var(bit); bits.push_back(literal(b)); } } class mk_atom_trail : public trail { bool_var m_var; public: mk_atom_trail(bool_var v):m_var(v) {} virtual void undo(theory_bv & th) { theory_bv::atom * a = th.get_bv2a(m_var); a->~atom(); th.erase_bv2a(m_var); } }; void theory_bv::mk_bit2bool(app * n) { context & ctx = get_context(); SASSERT(!ctx.b_internalized(n)); expr* first_arg = n->get_arg(0); if (!ctx.e_internalized(first_arg)) { // This may happen if bit2bool(x) is in a conflict // clause that is being reinitialized, and x was not reinitialized // yet. // So, we internalize x (i.e., arg) ctx.internalize(first_arg, false); SASSERT(ctx.e_internalized(first_arg)); // In most cases, when x is internalized, its bits are created. // They are created because x is a bit-vector operation or apply_sort_cnstr is invoked. // However, there is an exception. The method apply_sort_cnstr is not invoked for ite-terms. // So, I execute get_var on the enode attached to first_arg. // This will force a theory variable to be created if it does not already exist. // This will also force the creation of all bits for x. enode * first_arg_enode = ctx.get_enode(first_arg); get_var(first_arg_enode); // numerals are not blasted into bit2bool, so we do this directly. if (!ctx.b_internalized(n)) { rational val; unsigned sz; VERIFY(m_util.is_numeral(first_arg, val, sz)); theory_var v = first_arg_enode->get_th_var(get_id()); app* owner = first_arg_enode->get_owner(); for (unsigned i = 0; i < sz; ++i) { ctx.internalize(mk_bit2bool(owner, i), true); } m_bits[v].reset(); rational bit; for (unsigned i = 0; i < sz; ++i) { div(val, rational::power_of_two(i), bit); mod(bit, rational(2), bit); m_bits[v].push_back(bit.is_zero()?false_literal:true_literal); } } } else { enode * arg = ctx.get_enode(first_arg); // The argument was already internalized, but it may not have a theory variable associated with it. // For example, for ite-terms the method apply_sort_cnstr is not invoked. // See comment in the then-branch. theory_var v_arg = arg->get_th_var(get_id()); if (v_arg == null_theory_var) { // The method get_var will create a theory variable for arg. // As a side-effect the bits for arg will also be created. get_var(arg); SASSERT(ctx.b_internalized(n)); } else { SASSERT(v_arg != null_theory_var); bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); bit_atom * a = new (get_region()) bit_atom(); insert_bv2a(bv, a); m_trail_stack.push(mk_atom_trail(bv)); unsigned idx = n->get_decl()->get_parameter(0).get_int(); SASSERT(a->m_occs == 0); a->m_occs = new (get_region()) var_pos_occ(v_arg, idx); } } } void theory_bv::process_args(app * n) { context & ctx = get_context(); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(n->get_arg(i), false); } enode * theory_bv::mk_enode(app * n) { context & ctx = get_context(); enode * e; if (ctx.e_internalized(n)) { e = ctx.get_enode(n); } else { e = ctx.mk_enode(n, !m_params.m_bv_reflect, false, m_params.m_bv_cc); mk_var(e); } SASSERT(e->get_th_var(get_id()) != null_theory_var); return e; } theory_var theory_bv::get_var(enode * n) { theory_var v = n->get_th_var(get_id()); if (v == null_theory_var) { v = mk_var(n); mk_bits(v); } return v; } enode * theory_bv::get_arg(enode * n, unsigned idx) { if (m_params.m_bv_reflect) { return n->get_arg(idx); } else { context & ctx = get_context(); app * arg = to_app(n->get_owner()->get_arg(idx)); SASSERT(ctx.e_internalized(arg)); return ctx.get_enode(arg); } } inline theory_var theory_bv::get_arg_var(enode * n, unsigned idx) { return get_var(get_arg(n, idx)); } void theory_bv::get_bits(theory_var v, expr_ref_vector & r) { context & ctx = get_context(); literal_vector & bits = m_bits[v]; literal_vector::const_iterator it = bits.begin(); literal_vector::const_iterator end = bits.end(); for (; it != end; ++it) { expr_ref l(get_manager()); ctx.literal2expr(*it, l); r.push_back(l); } } inline void theory_bv::get_bits(enode * n, expr_ref_vector & r) { get_bits(get_var(n), r); } inline void theory_bv::get_arg_bits(enode * n, unsigned idx, expr_ref_vector & r) { get_bits(get_arg_var(n, idx), r); } inline void theory_bv::get_arg_bits(app * n, unsigned idx, expr_ref_vector & r) { context & ctx = get_context(); app * arg = to_app(n->get_arg(idx)); SASSERT(ctx.e_internalized(arg)); get_bits(ctx.get_enode(arg), r); } class add_var_pos_trail : public trail { theory_bv::bit_atom * m_atom; public: add_var_pos_trail(theory_bv::bit_atom * a):m_atom(a) {} virtual void undo(theory_bv & th) { SASSERT(m_atom->m_occs); m_atom->m_occs = m_atom->m_occs->m_next; } }; /** \brief v1[idx] = ~v2[idx], then v1 /= v2 is a theory axiom. */ void theory_bv::mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx) { SASSERT(m_bits[v1][idx] == ~m_bits[v2][idx]); TRACE("bv_diseq_axiom", tout << "found new diseq axiom\n"; display_var(tout, v1); display_var(tout, v2);); // found new disequality m_stats.m_num_diseq_static++; enode * e1 = get_enode(v1); enode * e2 = get_enode(v2); literal l = ~(mk_eq(e1->get_owner(), e2->get_owner(), true)); context & ctx = get_context(); ctx.mk_th_axiom(get_id(), 1, &l); if (ctx.relevancy()) { expr * eq = ctx.bool_var2expr(l.var()); relevancy_eh * eh = ctx.mk_relevancy_eh(pair_relevancy_eh(e1->get_owner(), e2->get_owner(), eq)); ctx.add_relevancy_eh(e1->get_owner(), eh); ctx.add_relevancy_eh(e2->get_owner(), eh); } } void theory_bv::register_true_false_bit(theory_var v, unsigned idx) { SASSERT(m_bits[v][idx] == true_literal || m_bits[v][idx] == false_literal); bool is_true = (m_bits[v][idx] == true_literal); zero_one_bits & bits = m_zero_one_bits[v]; bits.push_back(zero_one_bit(v, idx, is_true)); } /** \brief v[idx] = ~v'[idx], then v /= v' is a theory axiom. */ void theory_bv::find_new_diseq_axioms(var_pos_occ * occs, theory_var v, unsigned idx) { literal l = m_bits[v][idx]; l.neg(); while (occs) { theory_var v2 = occs->m_var; unsigned idx2 = occs->m_idx; if (idx == idx2 && m_bits[v2][idx2] == l && get_bv_size(v2) == get_bv_size(v)) mk_new_diseq_axiom(v, v2, idx); occs = occs->m_next; } } /** \brief Add bit l to the given variable. */ void theory_bv::add_bit(theory_var v, literal l) { context & ctx = get_context(); literal_vector & bits = m_bits[v]; unsigned idx = bits.size(); bits.push_back(l); if (l.var() == true_bool_var) { register_true_false_bit(v, idx); } else { theory_id th_id = ctx.get_var_theory(l.var()); if (th_id == get_id()) { atom * a = get_bv2a(l.var()); SASSERT(a && a->is_bit()); bit_atom * b = static_cast(a); find_new_diseq_axioms(b->m_occs, v, idx); m_trail_stack.push(add_var_pos_trail(b)); b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs); } else { SASSERT(th_id == null_theory_id); ctx.set_var_theory(l.var(), get_id()); SASSERT(ctx.get_var_theory(l.var()) == get_id()); bit_atom * b = new (get_region()) bit_atom(); insert_bv2a(l.var(), b); m_trail_stack.push(mk_atom_trail(l.var())); SASSERT(b->m_occs == 0); b->m_occs = new (get_region()) var_pos_occ(v, idx); } } } void theory_bv::simplify_bit(expr * s, expr_ref & r) { // proof_ref p(get_manager()); // if (get_context().at_base_level()) // m_simplifier->operator()(s, r, p); // else r = s; } void theory_bv::init_bits(enode * n, expr_ref_vector const & bits) { context & ctx = get_context(); ast_manager & m = get_manager(); theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); unsigned sz = bits.size(); SASSERT(get_bv_size(n) == sz); for (unsigned i = 0; i < sz; i++) { expr * bit = bits.get(i); expr_ref s_bit(m); simplify_bit(bit, s_bit); ctx.internalize(s_bit, true); literal l = ctx.get_literal(s_bit.get()); TRACE("init_bits", tout << "bit " << i << " of #" << n->get_owner_id() << "\n" << mk_ll_pp(s_bit, m) << "\n";); add_bit(v, l); } find_wpos(v); } /** \brief Find an unassigned bit for m_wpos[v], if such bit cannot be found invoke fixed_var_eh */ void theory_bv::find_wpos(theory_var v) { context & ctx = get_context(); literal_vector const & bits = m_bits[v]; unsigned sz = bits.size(); unsigned & wpos = m_wpos[v]; unsigned init = wpos; for (; wpos < sz; wpos++) { TRACE("find_wpos", tout << "curr bit: " << bits[wpos] << "\n";); if (ctx.get_assignment(bits[wpos]) == l_undef) { TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";); return; } } wpos = 0; for (; wpos < init; wpos++) { if (ctx.get_assignment(bits[wpos]) == l_undef) { TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";); return; } } TRACE("find_wpos", tout << "v" << v << " is a fixed variable.\n";); fixed_var_eh(v); } class fixed_eq_justification : public justification { theory_bv & m_th; theory_var m_var1; theory_var m_var2; void mark_bits(conflict_resolution & cr, literal_vector const & bits) { context & ctx = cr.get_context(); literal_vector::const_iterator it = bits.begin(); literal_vector::const_iterator end = bits.end(); for (; it != end; ++it) { if (it->var() != true_bool_var) { if (ctx.get_assignment(*it) == l_true) cr.mark_literal(*it); else cr.mark_literal(~(*it)); } } } void get_proof(conflict_resolution & cr, literal l, ptr_buffer & prs, bool & visited) { if (l.var() == true_bool_var) return; proof * pr = 0; if (cr.get_context().get_assignment(l) == l_true) pr = cr.get_proof(l); else pr = cr.get_proof(~l); if (pr) prs.push_back(pr); else visited = false; } public: fixed_eq_justification(theory_bv & th, theory_var v1, theory_var v2): m_th(th), m_var1(v1), m_var2(v2) { } virtual void get_antecedents(conflict_resolution & cr) { mark_bits(cr, m_th.m_bits[m_var1]); mark_bits(cr, m_th.m_bits[m_var2]); } virtual proof * mk_proof(conflict_resolution & cr) { ptr_buffer prs; context & ctx = cr.get_context(); bool visited = true; literal_vector const & bits1 = m_th.m_bits[m_var1]; literal_vector const & bits2 = m_th.m_bits[m_var2]; literal_vector::const_iterator it1 = bits1.begin(); literal_vector::const_iterator it2 = bits2.begin(); literal_vector::const_iterator end1 = bits1.end(); for (; it1 != end1; ++it1, ++it2) { get_proof(cr, *it1, prs, visited); get_proof(cr, *it2, prs, visited); } if (!visited) return 0; expr * fact = ctx.mk_eq_atom(m_th.get_enode(m_var1)->get_owner(), m_th.get_enode(m_var2)->get_owner()); ast_manager & m = ctx.get_manager(); return m.mk_th_lemma(get_from_theory(), fact, prs.size(), prs.c_ptr()); } virtual theory_id get_from_theory() const { return m_th.get_id(); } virtual char const * get_name() const { return "bv-fixed-eq"; } }; void theory_bv::fixed_var_eh(theory_var v) { numeral val; bool r = get_fixed_value(v, val); SASSERT(r); unsigned sz = get_bv_size(v); value_sort_pair key(val, sz); theory_var v2; if (m_fixed_var_table.find(key, v2)) { numeral val2; if (v2 < static_cast(get_num_vars()) && is_bv(v2) && get_bv_size(v2) == sz && get_fixed_value(v2, val2) && val == val2) { if (get_enode(v)->get_root() != get_enode(v2)->get_root()) { SASSERT(get_bv_size(v) == get_bv_size(v2)); context & ctx = get_context(); justification * js = ctx.mk_justification(fixed_eq_justification(*this, v, v2)); TRACE("fixed_var_eh", tout << "detected equality: v" << v << " = v" << v2 << "\n"; display_var(tout, v); display_var(tout, v2);); m_stats.m_num_th2core_eq++; ctx.assign_eq(get_enode(v), get_enode(v2), eq_justification(js)); } } else { // the original fixed variable v2 was deleted or it is not fixed anymore. m_fixed_var_table.erase(key); m_fixed_var_table.insert(key, v); } } else { m_fixed_var_table.insert(key, v); } } bool theory_bv::get_fixed_value(theory_var v, numeral & result) const { context & ctx = get_context(); result.reset(); literal_vector const & bits = m_bits[v]; literal_vector::const_iterator it = bits.begin(); literal_vector::const_iterator end = bits.end(); for (unsigned i = 0; it != end; ++it, ++i) { switch (ctx.get_assignment(*it)) { case l_false: break; case l_undef: return false; case l_true: result += m_bb.power(i); break; } } return true; } bool theory_bv::get_fixed_value(app* x, numeral & result) const { context& ctx = get_context(); if (!ctx.e_internalized(x)) return false; enode * e = ctx.get_enode(x); theory_var v = e->get_th_var(get_id()); return get_fixed_value(v, result); } void theory_bv::internalize_num(app * n) { SASSERT(!get_context().e_internalized(n)); ast_manager & m = get_manager(); numeral val; unsigned sz; m_util.is_numeral(n, val, sz); enode * e = mk_enode(n); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. // e->mark_as_interpreted(); theory_var v = e->get_th_var(get_id()); expr_ref_vector bits(m); m_bb.num2bits(val, sz, bits); SASSERT(bits.size() == sz); literal_vector & c_bits = m_bits[v]; for (unsigned i = 0; i < sz; i++) { expr * l = bits.get(i); if (m.is_true(l)) { c_bits.push_back(true_literal); } else { SASSERT(m.is_false(l)); c_bits.push_back(false_literal); } register_true_false_bit(v, i); } fixed_var_eh(v); } void theory_bv::internalize_mkbv(app* n) { ast_manager& m = get_manager(); expr_ref_vector bits(m); process_args(n); enode * e = mk_enode(n); bits.append(n->get_num_args(), n->get_args()); init_bits(e, bits); } void theory_bv::internalize_bv2int(app* n) { SASSERT(!get_context().e_internalized(n)); ast_manager & m = get_manager(); context& ctx = get_context(); TRACE("bv", tout << mk_bounded_pp(n, m) << "\n";); process_args(n); mk_enode(n); if (!ctx.relevancy()) { assert_bv2int_axiom(n); } } void theory_bv::assert_bv2int_axiom(app * n) { // // create the axiom: // n = bv2int(k) = ite(bit2bool(k[sz-1],2^{sz-1},0) + ... + ite(bit2bool(k[0],1,0)) // SASSERT(get_context().e_internalized(n)); SASSERT(m_util.is_bv2int(n)); ast_manager & m = get_manager(); TRACE("bv2int_bug", tout << "bv2int:\n" << mk_pp(n, m) << "\n";); context & ctx = get_context(); sort * int_sort = m.get_sort(n); app * k = to_app(n->get_arg(0)); SASSERT(m_util.is_bv_sort(m.get_sort(k))); expr_ref_vector k_bits(m); enode * k_enode = mk_enode(k); get_bits(k_enode, k_bits); unsigned sz = m_util.get_bv_size(k); expr_ref_vector args(m); expr_ref zero(m_autil.mk_numeral(numeral(0), int_sort), m); numeral num(1); for (unsigned i = 0; i < sz; ++i) { // Remark: A previous version of this method was using // // expr* b = mk_bit2bool(k,i); // // This is not correct. The predicate bit2bool is an // internal construct, and it was not meant for building // axioms directly. It is used to represent the bits of a // constant, and in some cases the bits of a complicated // bit-vector expression. In most cases, the bits of a // composite bit-vector expression T are just boolean // combinations of bit2bool atoms of the bit-vector // constants contained in T. So, instead of using // mk_bit2bool to access a particular bit of T, we should // use the method get_bits. // expr * b = k_bits.get(i); expr_ref n(m_autil.mk_numeral(num, int_sort), m); args.push_back(m.mk_ite(b, n, zero)); num *= numeral(2); } expr_ref sum(m); arith_simp().mk_add(sz, args.c_ptr(), sum); TRACE("bv", tout << mk_pp(n, m) << "\n"; tout << mk_pp(sum, m) << "\n"; ); literal l(mk_eq(n, sum, false)); ctx.mark_as_relevant(l); ctx.mk_th_axiom(get_id(), 1, &l); } void theory_bv::internalize_int2bv(app* n) { SASSERT(!get_context().e_internalized(n)); SASSERT(n->get_num_args() == 1); context& ctx = get_context(); process_args(n); mk_enode(n); mk_bits(ctx.get_enode(n)->get_th_var(get_id())); if (!ctx.relevancy()) { assert_int2bv_axiom(n); } } void theory_bv::assert_int2bv_axiom(app* n) { // // create the axiom: // bv2int(n) = e mod 2^bit_width // where n = int2bv(e) // // Create the axioms: // bit2bool(i,n) == ((e div 2^i) mod 2 != 0) // for i = 0,.., sz-1 // SASSERT(get_context().e_internalized(n)); SASSERT(m_util.is_int2bv(n)); ast_manager & m = get_manager(); context& ctx = get_context(); parameter param(m_autil.mk_int()); expr* n_expr = n; expr* e = n->get_arg(0); expr_ref lhs(m), rhs(m); lhs = m.mk_app(get_id(), OP_BV2INT, 1, ¶m, 1, &n_expr); unsigned sz = m_util.get_bv_size(n); numeral mod = power(numeral(2), sz); rhs = m_autil.mk_mod(e, m_autil.mk_numeral(mod, true)); literal l(mk_eq(lhs, rhs, false)); ctx.mark_as_relevant(l); ctx.mk_th_axiom(get_id(), 1, &l); TRACE("bv", tout << mk_pp(lhs, m) << " == \n"; tout << mk_pp(rhs, m) << "\n"; ); expr_ref_vector n_bits(m); enode * n_enode = mk_enode(n); get_bits(n_enode, n_bits); for (unsigned i = 0; i < sz; ++i) { numeral div = power(numeral(2), i); mod = numeral(2); rhs = m_autil.mk_idiv(e, m_autil.mk_numeral(div,true)); rhs = m_autil.mk_mod(rhs, m_autil.mk_numeral(mod, true)); rhs = m.mk_eq(rhs, m_autil.mk_numeral(rational(1), true)); lhs = n_bits.get(i); TRACE("bv", tout << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n";); l = literal(mk_eq(lhs, rhs, false)); ctx.mark_as_relevant(l); ctx.mk_th_axiom(get_id(), 1, &l); } } #define MK_UNARY(NAME, BLAST_OP) \ void theory_bv::NAME(app * n) { \ SASSERT(!get_context().e_internalized(n)); \ SASSERT(n->get_num_args() == 1); \ process_args(n); \ ast_manager & m = get_manager(); \ enode * e = mk_enode(n); \ expr_ref_vector arg1_bits(m), bits(m); \ get_arg_bits(e, 0, arg1_bits); \ m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), bits); \ init_bits(e, bits); \ } #define MK_BINARY(NAME, BLAST_OP) \ void theory_bv::NAME(app * n) { \ SASSERT(!get_context().e_internalized(n)); \ SASSERT(n->get_num_args() == 2); \ process_args(n); \ ast_manager & m = get_manager(); \ enode * e = mk_enode(n); \ expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m); \ get_arg_bits(e, 0, arg1_bits); \ get_arg_bits(e, 1, arg2_bits); \ SASSERT(arg1_bits.size() == arg2_bits.size()); \ m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits); \ init_bits(e, bits); \ } #define MK_AC_BINARY(NAME, BLAST_OP) \ void theory_bv::NAME(app * n) { \ SASSERT(!get_context().e_internalized(n)); \ SASSERT(n->get_num_args() >= 2); \ process_args(n); \ ast_manager & m = get_manager(); \ enode * e = mk_enode(n); \ expr_ref_vector arg_bits(m); \ expr_ref_vector bits(m); \ expr_ref_vector new_bits(m); \ unsigned i = n->get_num_args(); \ --i; \ get_arg_bits(e, i, bits); \ while (i > 0) { \ --i; \ arg_bits.reset(); \ get_arg_bits(e, i, arg_bits); \ SASSERT(arg_bits.size() == bits.size()); \ new_bits.reset(); \ m_bb.BLAST_OP(arg_bits.size(), arg_bits.c_ptr(), bits.c_ptr(), new_bits); \ bits.swap(new_bits); \ } \ init_bits(e, bits); \ } #define MK_BINARY_COND(NAME, BLAST_OP) \ void theory_bv::NAME(app * n) { \ SASSERT(!get_context().e_internalized(n)); \ SASSERT(n->get_num_args() == 2); \ process_args(n); \ ast_manager & m = get_manager(); \ context& ctx = get_context(); \ enode * e = mk_enode(n); \ expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m); \ expr_ref cond(m), s_cond(m); \ get_arg_bits(e, 0, arg1_bits); \ get_arg_bits(e, 1, arg2_bits); \ SASSERT(arg1_bits.size() == arg2_bits.size()); \ m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits, cond); \ init_bits(e, bits); \ simplify_bit(cond, s_cond); \ ctx.internalize(s_cond, true); \ literal l(ctx.get_literal(s_cond)); \ ctx.mark_as_relevant(l); \ ctx.mk_th_axiom(get_id(), 1, &l); \ TRACE("bv", tout << mk_pp(cond, get_manager()) << "\n"; tout << l << "\n";); \ } MK_UNARY(internalize_not, mk_not); MK_UNARY(internalize_redand, mk_redand); MK_UNARY(internalize_redor, mk_redor); MK_AC_BINARY(internalize_add, mk_adder); MK_AC_BINARY(internalize_mul, mk_multiplier); MK_BINARY(internalize_udiv, mk_udiv); MK_BINARY(internalize_sdiv, mk_sdiv); MK_BINARY(internalize_urem, mk_urem); MK_BINARY(internalize_srem, mk_srem); MK_BINARY(internalize_smod, mk_smod); MK_BINARY(internalize_shl, mk_shl); MK_BINARY(internalize_lshr, mk_lshr); MK_BINARY(internalize_ashr, mk_ashr); MK_BINARY(internalize_ext_rotate_left, mk_ext_rotate_left); MK_BINARY(internalize_ext_rotate_right, mk_ext_rotate_right); MK_AC_BINARY(internalize_and, mk_and); MK_AC_BINARY(internalize_or, mk_or); MK_AC_BINARY(internalize_xor, mk_xor); MK_AC_BINARY(internalize_nand, mk_nand); MK_AC_BINARY(internalize_nor, mk_nor); MK_AC_BINARY(internalize_xnor, mk_xnor); MK_BINARY(internalize_comp, mk_comp); #define MK_PARAMETRIC_UNARY(NAME, BLAST_OP) \ void theory_bv::NAME(app * n) { \ SASSERT(!get_context().e_internalized(n)); \ SASSERT(n->get_num_args() == 1); \ process_args(n); \ ast_manager & m = get_manager(); \ enode * e = mk_enode(n); \ expr_ref_vector arg1_bits(m), bits(m); \ get_arg_bits(e, 0, arg1_bits); \ unsigned param = n->get_decl()->get_parameter(0).get_int(); \ m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), param, bits); \ init_bits(e, bits); \ } MK_PARAMETRIC_UNARY(internalize_sign_extend, mk_sign_extend); MK_PARAMETRIC_UNARY(internalize_zero_extend, mk_zero_extend); MK_PARAMETRIC_UNARY(internalize_rotate_left, mk_rotate_left); MK_PARAMETRIC_UNARY(internalize_rotate_right, mk_rotate_right); void theory_bv::internalize_concat(app * n) { process_args(n); enode * e = mk_enode(n); theory_var v = e->get_th_var(get_id()); unsigned num_args = n->get_num_args(); unsigned i = num_args; while (i > 0) { i--; theory_var arg = get_arg_var(e, i); literal_vector::const_iterator it = m_bits[arg].begin(); literal_vector::const_iterator end = m_bits[arg].end(); for (; it != end; ++it) add_bit(v, *it); } find_wpos(v); } void theory_bv::internalize_extract(app * n) { SASSERT(n->get_num_args() == 1); process_args(n); enode * e = mk_enode(n); theory_var v = e->get_th_var(get_id()); theory_var arg = get_arg_var(e, 0); unsigned start = n->get_decl()->get_parameter(1).get_int(); unsigned end = n->get_decl()->get_parameter(0).get_int(); SASSERT(start <= end); literal_vector & arg_bits = m_bits[arg]; for (unsigned i = start; i <= end; ++i) add_bit(v, arg_bits[i]); find_wpos(v); } bool theory_bv::internalize_term(app * term) { SASSERT(term->get_family_id() == get_family_id()); TRACE("bv", tout << "internalizing term: " << mk_bounded_pp(term, get_manager()) << "\n";); if (approximate_term(term)) { return false; } switch (term->get_decl_kind()) { case OP_BV_NUM: internalize_num(term); return true; case OP_BADD: internalize_add(term); return true; case OP_BMUL: internalize_mul(term); return true; case OP_BSDIV_I: internalize_sdiv(term); return true; case OP_BUDIV_I: internalize_udiv(term); return true; case OP_BSREM_I: internalize_srem(term); return true; case OP_BUREM_I: internalize_urem(term); return true; case OP_BSMOD_I: internalize_smod(term); return true; case OP_BAND: internalize_and(term); return true; case OP_BOR: internalize_or(term); return true; case OP_BNOT: internalize_not(term); return true; case OP_BXOR: internalize_xor(term); return true; case OP_BNAND: internalize_nand(term); return true; case OP_BNOR: internalize_nor(term); return true; case OP_BXNOR: internalize_xnor(term); return true; case OP_CONCAT: internalize_concat(term); return true; case OP_SIGN_EXT: internalize_sign_extend(term); return true; case OP_ZERO_EXT: internalize_zero_extend(term); return true; case OP_EXTRACT: internalize_extract(term); return true; case OP_BREDOR: internalize_redor(term); return true; case OP_BREDAND: internalize_redand(term); return true; case OP_BCOMP: internalize_comp(term); return true; case OP_BSHL: internalize_shl(term); return true; case OP_BLSHR: internalize_lshr(term); return true; case OP_BASHR: internalize_ashr(term); return true; case OP_ROTATE_LEFT: internalize_rotate_left(term); return true; case OP_ROTATE_RIGHT: internalize_rotate_right(term); return true; case OP_EXT_ROTATE_LEFT: internalize_ext_rotate_left(term); return true; case OP_EXT_ROTATE_RIGHT: internalize_ext_rotate_right(term); return true; case OP_BSDIV0: return false; case OP_BUDIV0: return false; case OP_BSREM0: return false; case OP_BUREM0: return false; case OP_BSMOD0: return false; case OP_MKBV: internalize_mkbv(term); return true; case OP_INT2BV: if (m_params.m_bv_enable_int2bv2int) { internalize_int2bv(term); } return m_params.m_bv_enable_int2bv2int; case OP_BV2INT: if (m_params.m_bv_enable_int2bv2int) { internalize_bv2int(term); } return m_params.m_bv_enable_int2bv2int; default: TRACE("bv_op", tout << "unsupported operator: " << mk_ll_pp(term, get_manager()) << "\n";); UNREACHABLE(); return false; } } #define MK_NO_OVFL(NAME, OP) \ void theory_bv::NAME(app *n) { \ SASSERT(n->get_num_args() == 2); \ process_args(n); \ ast_manager & m = get_manager(); \ context & ctx = get_context(); \ expr_ref_vector arg1_bits(m), arg2_bits(m); \ get_arg_bits(n, 0, arg1_bits); \ get_arg_bits(n, 1, arg2_bits); \ expr_ref out(m); \ m_bb.OP(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), out); \ expr_ref s_out(m); \ simplify_bit(out, s_out); \ ctx.internalize(s_out, true); \ literal def = ctx.get_literal(s_out); \ literal l(ctx.mk_bool_var(n)); \ ctx.set_var_theory(l.var(), get_id()); \ le_atom * a = new (get_region()) le_atom(l, def); /* abuse le_atom */ \ insert_bv2a(l.var(), a); \ m_trail_stack.push(mk_atom_trail(l.var())); \ /* smul_no_overflow and umul_no_overflow are using the le_atom (THIS IS A BIG HACK)... */ \ /* the connection between the l and def was never realized when */ \ /* relevancy() is true and m_bv_lazy_le is false (the default configuration). */ \ /* So, we need to check also the m_bv_lazy_le flag here. */ \ /* Maybe, we should rename the le_atom to bridge_atom, and m_bv_lazy_le option to m_bv_lazy_bridge. */ \ if (!ctx.relevancy() || !m_params.m_bv_lazy_le) { \ ctx.mk_th_axiom(get_id(), l, ~def); \ ctx.mk_th_axiom(get_id(), ~l, def); \ } \ } MK_NO_OVFL(internalize_umul_no_overflow, mk_umul_no_overflow); MK_NO_OVFL(internalize_smul_no_overflow, mk_smul_no_overflow); MK_NO_OVFL(internalize_smul_no_underflow, mk_smul_no_underflow); template void theory_bv::internalize_le(app * n) { SASSERT(n->get_num_args() == 2); process_args(n); ast_manager & m = get_manager(); context & ctx = get_context(); expr_ref_vector arg1_bits(m), arg2_bits(m); get_arg_bits(n, 0, arg1_bits); get_arg_bits(n, 1, arg2_bits); expr_ref le(m); if (Signed) m_bb.mk_sle(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le); else m_bb.mk_ule(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le); expr_ref s_le(m); simplify_bit(le, s_le); ctx.internalize(s_le, true); literal def = ctx.get_literal(s_le); literal l(ctx.mk_bool_var(n)); ctx.set_var_theory(l.var(), get_id()); le_atom * a = new (get_region()) le_atom(l, def); insert_bv2a(l.var(), a); m_trail_stack.push(mk_atom_trail(l.var())); if (!ctx.relevancy() || !m_params.m_bv_lazy_le) { ctx.mk_th_axiom(get_id(), l, ~def); ctx.mk_th_axiom(get_id(), ~l, def); } } bool theory_bv::internalize_carry(app * n, bool gate_ctx) { context & ctx = get_context(); ctx.internalize(n->get_arg(0), true); ctx.internalize(n->get_arg(1), true); ctx.internalize(n->get_arg(2), true); bool is_new_var = false; bool_var v; if (!ctx.b_internalized(n)) { is_new_var = true; v = ctx.mk_bool_var(n); literal r(v); literal l1 = ctx.get_literal(n->get_arg(0)); literal l2 = ctx.get_literal(n->get_arg(1)); literal l3 = ctx.get_literal(n->get_arg(2)); ctx.mk_gate_clause(~r, l1, l2); ctx.mk_gate_clause(~r, l1, l3); ctx.mk_gate_clause(~r, l2, l3); ctx.mk_gate_clause( r, ~l1, ~l2); ctx.mk_gate_clause( r, ~l1, ~l3); ctx.mk_gate_clause( r, ~l2, ~l3); } else { v = ctx.get_bool_var(n); } if (!ctx.e_internalized(n) && !gate_ctx) { bool suppress_args = true; bool merge_tf = !gate_ctx; ctx.mk_enode(n, suppress_args, merge_tf, true); ctx.set_enode_flag(v, is_new_var); } return true; } bool theory_bv::internalize_xor3(app * n, bool gate_ctx) { context & ctx = get_context(); ctx.internalize(n->get_arg(0), true); ctx.internalize(n->get_arg(1), true); ctx.internalize(n->get_arg(2), true); bool is_new_var = false; bool_var v; if (!ctx.b_internalized(n)) { is_new_var = true; v = ctx.mk_bool_var(n); literal r(v); literal l1 = ctx.get_literal(n->get_arg(0)); literal l2 = ctx.get_literal(n->get_arg(1)); literal l3 = ctx.get_literal(n->get_arg(2)); ctx.mk_gate_clause(~r, l1, l2, l3); ctx.mk_gate_clause(~r, ~l1, ~l2, l3); ctx.mk_gate_clause(~r, ~l1, l2, ~l3); ctx.mk_gate_clause(~r, l1, ~l2, ~l3); ctx.mk_gate_clause( r, ~l1, l2, l3); ctx.mk_gate_clause( r, l1, ~l2, l3); ctx.mk_gate_clause( r, l1, l2, ~l3); ctx.mk_gate_clause( r, ~l1, ~l2, ~l3); } else { v = ctx.get_bool_var(n); } if (!ctx.e_internalized(n) && !gate_ctx) { bool suppress_args = true; bool merge_tf = !gate_ctx; ctx.mk_enode(n, suppress_args, merge_tf, true); ctx.set_enode_flag(v, is_new_var); } return true; } bool theory_bv::internalize_atom(app * atom, bool gate_ctx) { TRACE("bv", tout << "internalizing atom: " << mk_bounded_pp(atom, get_manager()) << "\n";); SASSERT(atom->get_family_id() == get_family_id()); if (approximate_term(atom)) { return false; } switch (atom->get_decl_kind()) { case OP_BIT2BOOL: mk_bit2bool(atom); return true; case OP_ULEQ: internalize_le(atom); return true; case OP_SLEQ: internalize_le(atom); return true; case OP_XOR3: return internalize_xor3(atom, gate_ctx); case OP_CARRY: return internalize_carry(atom, gate_ctx); case OP_BUMUL_NO_OVFL: internalize_umul_no_overflow(atom); return true; case OP_BSMUL_NO_OVFL: internalize_smul_no_overflow(atom); return true; case OP_BSMUL_NO_UDFL: internalize_smul_no_underflow(atom); return true; default: UNREACHABLE(); return false; } } // // Determine whether bit-vector expression should be approximated // based on the number of bits used by the arguments. // bool theory_bv::approximate_term(app* n) { if (m_params.m_bv_blast_max_size == INT_MAX) { return false; } unsigned num_args = n->get_num_args(); for (unsigned i = 0; i <= num_args; i++) { expr* arg = (i == num_args)?n:n->get_arg(i); sort* s = get_manager().get_sort(arg); s = get_manager().get_sort(arg); if (m_util.is_bv_sort(s) && m_util.get_bv_size(arg) > m_params.m_bv_blast_max_size) { if (!m_approximates_large_bvs) { TRACE("bv", tout << "found large size bit-vector:\n" << mk_pp(n, get_manager()) << "\n";); get_context().push_trail(value_trail(m_approximates_large_bvs)); m_approximates_large_bvs = true; } return true; } } return false; } void theory_bv::apply_sort_cnstr(enode * n, sort * s) { if (!is_attached_to_var(n) && !approximate_term(n->get_owner())) { theory_var v = mk_var(n); mk_bits(v); } } void theory_bv::new_eq_eh(theory_var v1, theory_var v2) { TRACE("bv_eq", tout << "new_eq: " << mk_pp(get_enode(v1)->get_owner(), get_manager()) << " = " << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); TRACE("bv", tout << "new_eq_eh v" << v1 << " = v" << v2 << " relevant1: " << get_context().is_relevant(get_enode(v1)) << " relevant2: " << get_context().is_relevant(get_enode(v2)) << "\n";); m_find.merge(v1, v2); } void theory_bv::new_diseq_eh(theory_var v1, theory_var v2) { if (is_bv(v1)) { SASSERT(m_bits[v1].size() == m_bits[v2].size()); expand_diseq(v1, v2); } } void theory_bv::expand_diseq(theory_var v1, theory_var v2) { SASSERT(get_bv_size(v1) == get_bv_size(v2)); context & ctx = get_context(); ast_manager & m = get_manager(); #ifdef _TRACE unsigned num_bool_vars = ctx.get_num_bool_vars(); #endif literal_vector & lits = m_tmp_literals; lits.reset(); lits.push_back(mk_eq(get_enode(v1)->get_owner(), get_enode(v2)->get_owner(), true)); literal_vector const & bits1 = m_bits[v1]; literal_vector::const_iterator it1 = bits1.begin(); literal_vector::const_iterator end1 = bits1.end(); literal_vector const & bits2 = m_bits[v2]; literal_vector::const_iterator it2 = bits2.begin(); for (; it1 != end1; ++it1, ++it2) { if (*it1 == ~(*it2)) return; // static diseq } it1 = bits1.begin(); it2 = bits2.begin(); for (; it1 != end1; ++it1, ++it2) { expr_ref l1(m), l2(m), diff(m); ctx.literal2expr(*it1, l1); ctx.literal2expr(*it2, l2); m_bb.mk_xor(l1, l2, diff); ctx.internalize(diff, true); literal arg = ctx.get_literal(diff); lits.push_back(arg); } m_stats.m_num_diseq_dynamic++; ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); TRACE_CODE({ static unsigned num = 0; static unsigned new_bool_vars = 0; new_bool_vars += (ctx.get_num_bool_vars() - num_bool_vars); if (num % 1000 == 0) TRACE("expand_diseq", tout << "num: " << num << " " << new_bool_vars << "\n";); num++; }); } void theory_bv::assign_eh(bool_var v, bool is_true) { context & ctx = get_context(); atom * a = get_bv2a(v); TRACE("bv", tout << "assert: v" << v << " #" << ctx.bool_var2expr(v)->get_id() << " is_true: " << is_true << "\n";); if (a->is_bit()) { // The following optimization is not correct. // Boolean variables created for performing bit-blasting are reused. // See regression\trevor6.smt for example. // // if (ctx.has_th_justification(v, get_id())) { // TRACE("bv", tout << "has th_justification\n";); // return; // } m_prop_queue.reset(); bit_atom * b = static_cast(a); var_pos_occ * curr = b->m_occs; while (curr) { m_prop_queue.push_back(var_pos(curr->m_var, curr->m_idx)); curr = curr->m_next; } TRACE("bv", tout << m_prop_queue.size() << "\n";); propagate_bits(); } } void theory_bv::propagate_bits() { context & ctx = get_context(); for (unsigned i = 0; i < m_prop_queue.size(); i++) { var_pos const & entry = m_prop_queue[i]; theory_var v = entry.first; unsigned idx = entry.second; if (m_wpos[v] == idx) find_wpos(v); literal_vector & bits = m_bits[v]; literal bit = bits[idx]; lbool val = ctx.get_assignment(bit); theory_var v2 = next(v); TRACE("bv_bit_prop", tout << "propagating #" << get_enode(v)->get_owner_id() << "[" << idx << "] = " << val << "\n";); while (v2 != v) { literal_vector & bits2 = m_bits[v2]; literal bit2 = bits2[idx]; SASSERT(bit != ~bit2); lbool val2 = ctx.get_assignment(bit2); TRACE("bv_bit_prop", tout << "propagating #" << get_enode(v2)->get_owner_id() << "[" << idx << "] = " << val2 << "\n";); if (val != val2) { literal antecedent = bit; literal consequent = bit2; if (val == l_false) { antecedent.neg(); consequent.neg(); } SASSERT(ctx.get_assignment(antecedent) == l_true); assign_bit(consequent, v, v2, idx, antecedent, false); if (ctx.inconsistent()) { TRACE("bv_bit_prop", tout << "inconsistent " << bit << " " << bit2 << "\n";); return; } } v2 = next(v2); } } m_prop_queue.reset(); TRACE("bv_bit_prop", tout << "done propagating\n";); } void theory_bv::assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc) { m_stats.m_num_bit2core++; context & ctx = get_context(); SASSERT(ctx.get_assignment(antecedent) == l_true); SASSERT(m_bits[v2][idx].var() == consequent.var()); SASSERT(consequent.var() != antecedent.var()); TRACE("bv_bit_prop", tout << "assigning: "; ctx.display_literal(tout, consequent); tout << " using "; ctx.display_literal(tout, antecedent); tout << " #" << get_enode(v1)->get_owner_id() << " #" << get_enode(v2)->get_owner_id() << " idx: " << idx << "\n"; tout << "propagate_eqc: " << propagate_eqc << "\n";); if (consequent == false_literal) { m_stats.m_num_conflicts++; ctx.set_conflict(mk_bit_eq_justification(v1, v2, consequent, antecedent)); } else { ctx.assign(consequent, mk_bit_eq_justification(v1, v2, consequent, antecedent)); if (m_wpos[v2] == idx) find_wpos(v2); // REMARK: bit_eq_justification is marked as a theory_bv justification. // Thus, the assignment to consequent will not be notified back to the theory. // So, we need to propagate the assignment to other bits. bool_var bv = consequent.var(); atom * a = get_bv2a(bv); SASSERT(a->is_bit()); bit_atom * b = static_cast(a); var_pos_occ * curr = b->m_occs; while (curr) { TRACE("assign_bit_bug", tout << "curr->m_var: v" << curr->m_var << ", curr->m_idx: " << curr->m_idx << ", v2: v" << v2 << ", idx: " << idx << "\n"; tout << "find(curr->m_var): v" << find(curr->m_var) << ", find(v2): v" << find(v2) << "\n"; tout << "is bit of #" << get_enode(curr->m_var)->get_owner_id() << "\n"; ); // If find(curr->m_var) == find(v2) && curr->m_idx == idx and propagate_eqc == false, then // this bit will be propagated to the equivalence class of v2 by assign_bit caller. if (propagate_eqc || find(curr->m_var) != find(v2) || curr->m_idx != idx) m_prop_queue.push_back(var_pos(curr->m_var, curr->m_idx)); curr = curr->m_next; } } } void theory_bv::relevant_eh(app * n) { ast_manager & m = get_manager(); context & ctx = get_context(); TRACE("bv", tout << "relevant: " << mk_pp(n, m) << "\n";); if (m.is_bool(n)) { bool_var v = ctx.get_bool_var(n); atom * a = get_bv2a(v); if (a && !a->is_bit()) { le_atom * le = static_cast(a); ctx.mark_as_relevant(le->m_def); if (m_params.m_bv_lazy_le) { ctx.mk_th_axiom(get_id(), le->m_var, ~le->m_def); ctx.mk_th_axiom(get_id(), ~le->m_var, le->m_def); } } } else if (m_params.m_bv_enable_int2bv2int && m_util.is_bv2int(n)) { ctx.mark_as_relevant(n->get_arg(0)); assert_bv2int_axiom(n); } else if (m_params.m_bv_enable_int2bv2int && m_util.is_int2bv(n)) { ctx.mark_as_relevant(n->get_arg(0)); assert_int2bv_axiom(n); } else if (ctx.e_internalized(n)) { enode * e = ctx.get_enode(n); theory_var v = e->get_th_var(get_id()); if (v != null_theory_var) { literal_vector & bits = m_bits[v]; literal_vector::iterator it = bits.begin(); literal_vector::iterator end = bits.end(); for (; it != end; ++it) ctx.mark_as_relevant(*it); } } } void theory_bv::push_scope_eh() { theory::push_scope_eh(); m_trail_stack.push_scope(); } void theory_bv::pop_scope_eh(unsigned num_scopes) { TRACE("bv",tout << num_scopes << "\n";); m_trail_stack.pop_scope(num_scopes); unsigned num_old_vars = get_old_num_vars(num_scopes); m_bits.shrink(num_old_vars); m_wpos.shrink(num_old_vars); m_zero_one_bits.shrink(num_old_vars); theory::pop_scope_eh(num_scopes); } final_check_status theory_bv::final_check_eh() { SASSERT(check_invariant()); if (m_approximates_large_bvs) { return FC_GIVEUP; } return FC_DONE; } void theory_bv::reset_eh() { pop_scope_eh(m_trail_stack.get_num_scopes()); m_bool_var2atom.reset(); m_fixed_var_table.reset(); theory::reset_eh(); } theory_bv::theory_bv(ast_manager & m, theory_bv_params const & params, bit_blaster_params const & bb_params): theory(m.mk_family_id("bv")), m_params(params), m_util(m), m_autil(m), m_simplifier(0), m_bb(m, bb_params), m_trail_stack(*this), m_find(*this), m_approximates_large_bvs(false) { } theory_bv::~theory_bv() { } void theory_bv::merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { TRACE("bv", tout << "merging: #" << get_enode(v1)->get_owner_id() << " #" << get_enode(v2)->get_owner_id() << "\n";); TRACE("bv_bit_prop", tout << "merging: #" << get_enode(v1)->get_owner_id() << " #" << get_enode(v2)->get_owner_id() << "\n";); if (!merge_zero_one_bits(r1, r2)) { TRACE("bv", tout << "conflict detected\n";); return; // conflict was detected } m_prop_queue.reset(); context & ctx = get_context(); literal_vector & bits1 = m_bits[v1]; literal_vector & bits2 = m_bits[v2]; SASSERT(bits1.size() == bits2.size()); unsigned sz = bits1.size(); bool changed; TRACE("bv", tout << "bits size: " << sz << "\n";); do { // This outerloop is necessary to avoid missing propagation steps. // For example, let's assume that bits1 and bits2 contains the following // sequence of bits: // b4 b3 b2 b1 // b5 b4 b3 b2 // Let's also assume that b1 is assigned, and b2, b3, b4, and b5 are not. // Only the propagation from b1 to b2 is performed by the first iteration of this // loop. // // In the worst case, we need to execute this loop bits1.size() times. // // Remark: the assignment to b2 is marked as a bv theory propagation, // then it is not notified to the bv theory. changed = false; for (unsigned idx = 0; idx < sz; idx++) { literal bit1 = bits1[idx]; literal bit2 = bits2[idx]; CTRACE("bv_bug", bit1 == ~bit2, display_var(tout, v1); display_var(tout, v2); tout << "idx: " << idx << "\n";); SASSERT(bit1 != ~bit2); lbool val1 = ctx.get_assignment(bit1); lbool val2 = ctx.get_assignment(bit2); if (val1 == val2) continue; changed = true; if (val1 != l_undef && val2 != l_undef) { TRACE("bv", tout << "inconsistent "; display_var(tout, v1); display_var(tout, v2); tout << "idx: " << idx << "\n";); } if (val1 != l_undef) { literal antecedent = bit1; literal consequent = bit2; if (val1 == l_false) { consequent.neg(); antecedent.neg(); } assign_bit(consequent, v1, v2, idx, antecedent, true); } else if (val2 != l_undef) { literal antecedent = bit2; literal consequent = bit1; if (val2 == l_false) { consequent.neg(); antecedent.neg(); } assign_bit(consequent, v2, v1, idx, antecedent, true); } if (ctx.inconsistent()) return; if (val1 != l_undef && val2 != l_undef && val1 != val2) { UNREACHABLE(); } } } while(changed); propagate_bits(); } bool theory_bv::merge_zero_one_bits(theory_var r1, theory_var r2) { zero_one_bits & bits2 = m_zero_one_bits[r2]; if (bits2.empty()) return true; zero_one_bits & bits1 = m_zero_one_bits[r1]; unsigned bv_size = get_bv_size(r1); SASSERT(bv_size == get_bv_size(r2)); m_merge_aux[0].reserve(bv_size+1, null_theory_var); m_merge_aux[1].reserve(bv_size+1, null_theory_var); #define RESET_MERGET_AUX() { \ zero_one_bits::iterator it = bits1.begin(); \ zero_one_bits::iterator end = bits1.end(); \ for (; it != end; ++it) \ m_merge_aux[it->m_is_true][it->m_idx] = null_theory_var; \ } DEBUG_CODE(for (unsigned i = 0; i < bv_size; i++) { SASSERT(m_merge_aux[0][i] == null_theory_var || m_merge_aux[1][i] == null_theory_var); }); // save info about bits1 zero_one_bits::iterator it = bits1.begin(); zero_one_bits::iterator end = bits1.end(); for (; it != end; ++it) m_merge_aux[it->m_is_true][it->m_idx] = it->m_owner; // check if bits2 is consistent with bits1, and copy new bits to bits1 it = bits2.begin(); end = bits2.end(); for (; it != end; ++it) { theory_var v2 = it->m_owner; theory_var v1 = m_merge_aux[!it->m_is_true][it->m_idx]; if (v1 != null_theory_var) { // conflict was detected ... v1 and v2 have complementary bits SASSERT(m_bits[v1][it->m_idx] == ~(m_bits[v2][it->m_idx])); SASSERT(m_bits[v1].size() == m_bits[v2].size()); mk_new_diseq_axiom(v1, v2, it->m_idx); RESET_MERGET_AUX(); return false; } if (m_merge_aux[it->m_is_true][it->m_idx] == null_theory_var) { // copy missing variable to bits1 bits1.push_back(*it); } } // reset m_merge_aux vector RESET_MERGET_AUX(); DEBUG_CODE(for (unsigned i = 0; i < bv_size; i++) { SASSERT(m_merge_aux[0][i] == null_theory_var || m_merge_aux[1][i] == null_theory_var); }); return true; } class bit_eq_justification : public justification { enode * m_v1; enode * m_v2; theory_id m_th_id; // TODO: steal 4 bits from each one of the following literas and use them to represent the th_id. literal m_consequent; literal m_antecedent; public: bit_eq_justification(theory_id th_id, enode * v1, enode * v2, literal c, literal a): m_v1(v1), m_v2(v2), m_th_id(th_id), m_consequent(c), m_antecedent(a) {} virtual void get_antecedents(conflict_resolution & cr) { cr.mark_eq(m_v1, m_v2); if (m_antecedent.var() != true_bool_var) cr.mark_literal(m_antecedent); } virtual proof * mk_proof(conflict_resolution & cr) { bool visited = true; ptr_buffer prs; proof * pr = cr.get_proof(m_v1, m_v2); if (pr) prs.push_back(pr); else visited = false; if (m_antecedent.var() != true_bool_var) { proof * pr = cr.get_proof(m_antecedent); if (pr) prs.push_back(pr); else visited = false; } if (!visited) return 0; context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref fact(m); ctx.literal2expr(m_consequent, fact); return m.mk_th_lemma(get_from_theory(), fact, prs.size(), prs.c_ptr()); } virtual theory_id get_from_theory() const { return m_th_id; } virtual char const * get_name() const { return "bv-bit-eq"; } }; inline justification * theory_bv::mk_bit_eq_justification(theory_var v1, theory_var v2, literal consequent, literal antecedent) { return get_context().mk_justification(bit_eq_justification(get_id(), get_enode(v1), get_enode(v2), consequent, antecedent)); } void theory_bv::unmerge_eh(theory_var v1, theory_var v2) { // v1 was the root of the equivalence class // I must remove the zero_one_bits that are from v2. // REMARK: it is unsafe to invoke check_zero_one_bits, since // the enode associated with v1 and v2 may have already been // deleted. // // The logical context trail_stack is popped before // the theories pop_scope_eh is invoked. zero_one_bits & bits = m_zero_one_bits[v1]; if (bits.empty()) { // SASSERT(check_zero_one_bits(v1)); // SASSERT(check_zero_one_bits(v2)); return; } unsigned j = bits.size(); while (j > 0) { --j; zero_one_bit & bit = bits[j]; if (find(bit.m_owner) == v1) { bits.shrink(j+1); // SASSERT(check_zero_one_bits(v1)); // SASSERT(check_zero_one_bits(v2)); return; } } bits.shrink(0); // SASSERT(check_zero_one_bits(v1)); // SASSERT(check_zero_one_bits(v2)); } void theory_bv::init_model(model_generator & m) { m_factory = alloc(bv_factory, get_manager()); m.register_factory(m_factory); } model_value_proc * theory_bv::mk_value(enode * n, model_generator & mg) { numeral val; theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); #ifdef Z3DEBUG bool r = #endif get_fixed_value(v, val); SASSERT(r); return alloc(expr_wrapper_proc, m_factory->mk_value(val, get_bv_size(v))); } void theory_bv::display_var(std::ostream & out, theory_var v) const { out << "v"; out.width(4); out << std::left << v; out << " #"; out.width(4); out << get_enode(v)->get_owner_id() << " -> #"; out.width(4); out << get_enode(find(v))->get_owner_id(); out << std::right << ", bits:"; context & ctx = get_context(); literal_vector const & bits = m_bits[v]; literal_vector::const_iterator it = bits.begin(); literal_vector::const_iterator end = bits.end(); for (; it != end; ++it) { out << " "; ctx.display_literal(out, *it); } numeral val; if (get_fixed_value(v, val)) out << ", value: " << val; out << "\n"; } void theory_bv::display_bit_atom(std::ostream & out, bool_var v, bit_atom const * a) const { context & ctx = get_context(); out << "#" << ctx.bool_var2expr(v)->get_id() << " ->"; var_pos_occ * curr = a->m_occs; while (curr) { out << " #" << get_enode(curr->m_var)->get_owner_id() << "[" << curr->m_idx << "]"; curr = curr->m_next; } out << "\n"; } void theory_bv::display_atoms(std::ostream & out) const { out << "atoms:\n"; context & ctx = get_context(); unsigned num = ctx.get_num_bool_vars(); for (unsigned v = 0; v < num; v++) { atom * a = get_bv2a(v); if (a && a->is_bit()) display_bit_atom(out, v, static_cast(a)); } } void theory_bv::display(std::ostream & out) const { out << "Theory bv:\n"; unsigned num_vars = get_num_vars(); for (unsigned v = 0; v < num_vars; v++) { display_var(out, v); } display_atoms(out); } void theory_bv::collect_statistics(::statistics & st) const { st.update("bv conflicts", m_stats.m_num_conflicts); st.update("bv diseqs", m_stats.m_num_diseq_static); st.update("bv dynamic diseqs", m_stats.m_num_diseq_dynamic); st.update("bv bit2core", m_stats.m_num_bit2core); st.update("bv->core eq", m_stats.m_num_th2core_eq); } #ifdef Z3DEBUG bool theory_bv::check_assignment(theory_var v) const { context & ctx = get_context(); if (!is_root(v)) return true; if (!ctx.is_relevant(get_enode(v))) { return true; } theory_var v2 = v; literal_vector const & bits2 = m_bits[v2]; theory_var v1 = v2; do { literal_vector const & bits1 = m_bits[v1]; SASSERT(bits1.size() == bits2.size()); unsigned sz = bits1.size(); for (unsigned i = 0; i < sz; i++) { literal bit1 = bits1[i]; literal bit2 = bits2[i]; lbool val1 = ctx.get_assignment(bit1); lbool val2 = ctx.get_assignment(bit2); CTRACE("bv_bug", val1 != val2, tout << "equivalence class is inconsistent, i: " << i << "\n"; display_var(tout, v1); display_var(tout, v2); tout << "val1: " << val1 << " lvl: " << ctx.get_assign_level(bit1.var()) << " bit " << bit1 << "\n"; tout << "val2: " << val2 << " lvl: " << ctx.get_assign_level(bit2.var()) << " bit " << bit2 << "\n";); SASSERT(val1 == val2); } SASSERT(ctx.is_relevant(get_enode(v1))); v1 = next(v1); } while (v1 != v); return true; } /** \brief Check whether m_zero_one_bits is an accurate summary of the bits in the equivalence class rooted by v. \remark The method does nothing if v is not the root of the equivalence class. */ bool theory_bv::check_zero_one_bits(theory_var v) const { if (get_context().inconsistent()) return true; // property is only valid if the context is not in a conflict. if (is_root(v) && is_bv(v)) { svector bits[2]; unsigned num_bits = 0; unsigned bv_sz = get_bv_size(v); bits[0].resize(bv_sz, false); bits[1].resize(bv_sz, false); theory_var curr = v; do { literal_vector const & lits = m_bits[curr]; for (unsigned i = 0; i < lits.size(); i++) { literal l = lits[i]; if (l.var() == true_bool_var) { unsigned is_true = (l == true_literal); SASSERT(!bits[!is_true][i]); // no complementary bits if (!bits[is_true][i]) { bits[is_true][i] = true; num_bits++; } } } curr = next(curr); } while (curr != v); zero_one_bits const & _bits = m_zero_one_bits[v]; SASSERT(_bits.size() == num_bits); svector already_found; already_found.resize(bv_sz, false); zero_one_bits::const_iterator it = _bits.begin(); zero_one_bits::const_iterator end = _bits.end(); for (; it != end; ++it) { SASSERT(find(it->m_owner) == v); SASSERT(bits[it->m_is_true][it->m_idx]); SASSERT(!already_found[it->m_idx]); already_found[it->m_idx] = true; } } return true; } bool theory_bv::check_invariant() const { unsigned num = get_num_vars(); for (unsigned v = 0; v < num; v++) { check_assignment(v); check_zero_one_bits(v); } return true; } #endif }; z3-z3-4.4.1/src/smt/theory_bv.h000066400000000000000000000273431260446376700162020ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_bv.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-03. Revision History: --*/ #ifndef THEORY_BV_H_ #define THEORY_BV_H_ #include"smt_theory.h" #include"theory_bv_params.h" #include"bit_blaster.h" #include"trail.h" #include"union_find.h" #include"simplifier.h" #include"bv_simplifier_plugin.h" #include"arith_decl_plugin.h" #include"arith_simplifier_plugin.h" #include"numeral_factory.h" namespace smt { struct theory_bv_stats { unsigned m_num_diseq_static, m_num_diseq_dynamic, m_num_bit2core, m_num_th2core_eq, m_num_conflicts; void reset() { memset(this, 0, sizeof(theory_bv_stats)); } theory_bv_stats() { reset(); } }; class theory_bv : public theory { typedef rational numeral; typedef trail_stack th_trail_stack; typedef union_find th_union_find; typedef std::pair var_pos; class atom { public: virtual ~atom() {} virtual bool is_bit() const = 0; }; struct var_pos_occ { theory_var m_var; unsigned m_idx; var_pos_occ * m_next; var_pos_occ(theory_var v = null_theory_var, unsigned idx = 0, var_pos_occ * next = 0):m_var(v), m_idx(idx), m_next(next) {} }; struct bit_atom : public atom { var_pos_occ * m_occs; bit_atom():m_occs(0) {} virtual ~bit_atom() {} virtual bool is_bit() const { return true; } }; struct le_atom : public atom { literal m_var; literal m_def; le_atom(literal v, literal d):m_var(v), m_def(d) {} virtual ~le_atom() {} virtual bool is_bit() const { return false; } }; /** \brief Structure used to store the position of a bitvector variable that contains the true_literal/false_literal. Remark: the implementation assumes that bitvector variables containing complementary bits are never merged. I assert a disequality (not (= x y)) whenever x and y contain complementary bits. However, this is too expensive when the bit is the true_literal or false_literal. The number of disequalities is too big. To avoid this problem, each equivalence class has a set of its true_literal and false_literal bits in the form of svector. Before merging two classes we just check if the merge is valid by traversing these vectors. */ struct zero_one_bit { theory_var m_owner; //!< variable that owns the bit: useful for backtracking unsigned m_idx:31; unsigned m_is_true:1; zero_one_bit(theory_var v = null_theory_var, unsigned idx = UINT_MAX, bool is_true = false): m_owner(v), m_idx(idx), m_is_true(is_true) {} }; typedef svector zero_one_bits; #ifdef SPARSE_MAP typedef u_map bool_var2atom; void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.insert(bv, a); } void erase_bv2a(bool_var bv) { m_bool_var2atom.erase(bv); } atom * get_bv2a(bool_var bv) const { atom * a; m_bool_var2atom.find(bv, a); return a; } #else typedef ptr_vector bool_var2atom; void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); } void erase_bv2a(bool_var bv) { m_bool_var2atom[bv] = 0; } atom * get_bv2a(bool_var bv) const { return m_bool_var2atom.get(bv, 0); } #endif theory_bv_stats m_stats; theory_bv_params const & m_params; bv_util m_util; arith_util m_autil; simplifier * m_simplifier; bit_blaster m_bb; th_trail_stack m_trail_stack; th_union_find m_find; vector m_bits; // per var, the bits of a given variable. svector m_wpos; // per var, watch position for fixed variable detection. vector m_zero_one_bits; // per var, see comment in the struct zero_one_bit bool_var2atom m_bool_var2atom; typedef svector vars; typedef std::pair value_sort_pair; typedef pair_hash, unsigned_hash> value_sort_pair_hash; typedef map > value2var; value2var m_fixed_var_table; literal_vector m_tmp_literals; svector m_prop_queue; bool m_approximates_large_bvs; theory_var find(theory_var v) const { return m_find.find(v); } theory_var next(theory_var v) const { return m_find.next(v); } bool is_root(theory_var v) const { return m_find.is_root(v); } unsigned get_bv_size(app const * n) const { return m_util.get_bv_size(n); } unsigned get_bv_size(enode const * n) const { return m_util.get_bv_size(n->get_owner()); } unsigned get_bv_size(theory_var v) const { return get_bv_size(get_enode(v)); } bool is_bv(app const* n) const { return m_util.is_bv_sort(get_manager().get_sort(n)); } bool is_bv(enode const* n) const { return is_bv(n->get_owner()); } bool is_bv(theory_var v) const { return is_bv(get_enode(v)); } region & get_region() { return m_trail_stack.get_region(); } bool is_numeral(theory_var v) const { return m_util.is_numeral(get_enode(v)->get_owner()); } app * mk_bit2bool(app * bv, unsigned idx); void mk_bits(theory_var v); friend class mk_atom_trail; void mk_bit2bool(app * n); void process_args(app * n); enode * mk_enode(app * n); theory_var get_var(enode * n); enode * get_arg(enode * n, unsigned idx); theory_var get_arg_var(enode * n, unsigned idx); void get_bits(theory_var v, expr_ref_vector & r); void get_bits(enode * n, expr_ref_vector & r); void get_arg_bits(enode * n, unsigned idx, expr_ref_vector & r); void get_arg_bits(app * n, unsigned idx, expr_ref_vector & r); friend class add_var_pos_trail; void simplify_bit(expr * s, expr_ref & r); void mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx); friend class register_true_false_bit_trail; void register_true_false_bit(theory_var v, unsigned idx); void find_new_diseq_axioms(var_pos_occ * occs, theory_var v, unsigned idx); void add_bit(theory_var v, literal l); void init_bits(enode * n, expr_ref_vector const & bits); void find_wpos(theory_var v); friend class fixed_eq_justification; void fixed_var_eh(theory_var v); bool get_fixed_value(theory_var v, numeral & result) const; void internalize_num(app * n); void internalize_add(app * n); void internalize_mul(app * n); void internalize_udiv(app * n); void internalize_sdiv(app * n); void internalize_urem(app * n); void internalize_srem(app * n); void internalize_smod(app * n); void internalize_shl(app * n); void internalize_lshr(app * n); void internalize_ashr(app * n); void internalize_ext_rotate_left(app * n); void internalize_ext_rotate_right(app * n); void internalize_and(app * n); void internalize_or(app * n); void internalize_not(app * n); void internalize_nand(app * n); void internalize_nor(app * n); void internalize_xor(app * n); void internalize_xnor(app * n); void internalize_concat(app * n); void internalize_sign_extend(app * n); void internalize_zero_extend(app * n); void internalize_extract(app * n); void internalize_redand(app * n); void internalize_redor(app * n); void internalize_comp(app * n); void internalize_rotate_left(app * n); void internalize_rotate_right(app * n); void internalize_bv2int(app* n); void internalize_int2bv(app* n); void internalize_mkbv(app* n); void internalize_umul_no_overflow(app *n); void internalize_smul_no_overflow(app *n); void internalize_smul_no_underflow(app *n); bool approximate_term(app* n); template void internalize_le(app * atom); bool internalize_xor3(app * n, bool gate_ctx); bool internalize_carry(app * n, bool gate_ctx); justification * mk_bit_eq_justification(theory_var v1, theory_var v2, literal consequent, literal antecedent); void propagate_bits(); void assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc); void assert_int2bv_axiom(app* n); void assert_bv2int_axiom(app* n); arith_simplifier_plugin & arith_simp() const { SASSERT(m_simplifier != 0); arith_simplifier_plugin * as = static_cast(m_simplifier->get_plugin(m_autil.get_family_id())); SASSERT(as != 0); return *as; } protected: virtual void init(context * ctx); virtual theory_var mk_var(enode * n); virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); virtual void apply_sort_cnstr(enode * n, sort * s); virtual void new_eq_eh(theory_var v1, theory_var v2); virtual void new_diseq_eh(theory_var v1, theory_var v2); virtual void expand_diseq(theory_var v1, theory_var v2); virtual void assign_eh(bool_var v, bool is_true); virtual void relevant_eh(app * n); virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual final_check_status final_check_eh(); virtual void reset_eh(); svector m_merge_aux[2]; //!< auxiliary vector used in merge_zero_one_bits bool merge_zero_one_bits(theory_var r1, theory_var r2); // ----------------------------------- // // Model generation // // ----------------------------------- bv_factory * m_factory; virtual void init_model(model_generator & m); virtual model_value_proc * mk_value(enode * n, model_generator & mg); public: theory_bv(ast_manager & m, theory_bv_params const & params, bit_blaster_params const & bb_params); virtual ~theory_bv(); virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_bv, get_manager(), m_params, m_bb.get_params()); } virtual char const * get_name() const { return "bit-vector"; } th_trail_stack & get_trail_stack() { return m_trail_stack; } void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2); void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { SASSERT(check_zero_one_bits(r1)); } void unmerge_eh(theory_var v1, theory_var v2); void display_var(std::ostream & out, theory_var v) const; void display_bit_atom(std::ostream & out, bool_var v, bit_atom const * a) const; void display_atoms(std::ostream & out) const; virtual void display(std::ostream & out) const; virtual void collect_statistics(::statistics & st) const; bool get_fixed_value(app* x, numeral & result) const; #ifdef Z3DEBUG bool check_assignment(theory_var v) const; bool check_invariant() const; bool check_zero_one_bits(theory_var v) const; #endif }; }; #endif /* THEORY_BV_H_ */ z3-z3-4.4.1/src/smt/theory_datatype.cpp000066400000000000000000001044311260446376700177330ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_datatype.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-31. Revision History: --*/ #include"smt_context.h" #include"theory_datatype.h" #include"smt_model_generator.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"stats.h" #include"ast_smt2_pp.h" namespace smt { class dt_eq_justification : public ext_theory_eq_propagation_justification { public: dt_eq_justification(family_id fid, region & r, literal antecedent, enode * lhs, enode * rhs): ext_theory_eq_propagation_justification(fid, r, 1, &antecedent, 0, 0, lhs, rhs) { } // Remark: the assignment must be propagated back to the datatype theory. virtual theory_id get_from_theory() const { return null_theory_id; } }; /** \brief Assert the axiom (antecedent => lhs = rhs) antecedent may be null_literal */ void theory_datatype::assert_eq_axiom(enode * lhs, expr * rhs, literal antecedent) { ast_manager & m = get_manager(); context & ctx = get_context(); if (m.proofs_enabled()) { literal l(mk_eq(lhs->get_owner(), rhs, true)); ctx.mark_as_relevant(l); if (antecedent != null_literal) { literal lits[2] = {l, ~antecedent}; ctx.mk_th_axiom(get_id(), 2, lits); } else { literal lits[1] = {l}; ctx.mk_th_axiom(get_id(), 1, lits); } } else { ctx.internalize(rhs, false); TRACE("datatype", tout << "adding axiom:\n" << mk_pp(lhs->get_owner(), m) << "\n=\n" << mk_pp(rhs, m) << "\n";); if (antecedent == null_literal) { ctx.assign_eq(lhs, ctx.get_enode(rhs), eq_justification::mk_axiom()); } else if (ctx.get_assignment(antecedent) != l_true) { literal l(mk_eq(lhs->get_owner(), rhs, true)); ctx.mark_as_relevant(l); ctx.mark_as_relevant(antecedent); literal lits[2] = {l, ~antecedent}; ctx.mk_th_axiom(get_id(), 2, lits); } else { SASSERT(ctx.get_assignment(antecedent) == l_true); region & r = ctx.get_region(); enode * _rhs = ctx.get_enode(rhs); justification * js = ctx.mk_justification(dt_eq_justification(get_id(), r, antecedent, lhs, _rhs)); TRACE("datatype", tout << "assigning... #" << lhs->get_owner_id() << " #" << _rhs->get_owner_id() << "\n"; tout << "v" << lhs->get_th_var(get_id()) << " v" << _rhs->get_th_var(get_id()) << "\n";); TRACE("datatype_detail", display(tout);); ctx.assign_eq(lhs, _rhs, eq_justification(js)); } } } /** \brief Assert the equality (= n (c (acc_1 n) ... (acc_m n))) where where acc_i are the accessors of constructor c. */ void theory_datatype::assert_is_constructor_axiom(enode * n, func_decl * c, literal antecedent) { TRACE("datatype_bug", tout << "creating axiom (= n (c (acc_1 n) ... (acc_m n))) for\n" << mk_pp(n->get_owner(), get_manager()) << "\n";); m_stats.m_assert_cnstr++; SASSERT(m_util.is_constructor(c)); SASSERT(m_util.is_datatype(get_manager().get_sort(n->get_owner()))); ast_manager & m = get_manager(); ptr_vector args; ptr_vector const * accessors = m_util.get_constructor_accessors(c); SASSERT(c->get_arity() == accessors->size()); ptr_vector::const_iterator it = accessors->begin(); ptr_vector::const_iterator end = accessors->end(); for (; it != end; ++it) { func_decl * d = *it; SASSERT(d->get_arity() == 1); expr * acc = m.mk_app(d, n->get_owner()); args.push_back(acc); } expr * mk = m.mk_app(c, args.size(), args.c_ptr()); assert_eq_axiom(n, mk, antecedent); } /** \brief Given a constructor n := (c a_1 ... a_m) assert the axioms (= (acc_1 n) a_1) ... (= (acc_m n) a_m) */ void theory_datatype::assert_accessor_axioms(enode * n) { m_stats.m_assert_accessor++; SASSERT(is_constructor(n)); ast_manager & m = get_manager(); func_decl * d = n->get_decl(); ptr_vector const * accessors = m_util.get_constructor_accessors(d); SASSERT(n->get_num_args() == accessors->size()); ptr_vector::const_iterator it = accessors->begin(); ptr_vector::const_iterator end = accessors->end(); for (unsigned i = 0; it != end; ++it, ++i) { func_decl * acc = *it; app * acc_app = m.mk_app(acc, n->get_owner()); enode * arg = n->get_arg(i); assert_eq_axiom(arg, acc_app, null_literal); } } /** \brief Sign a conflict for r := is_mk(a), c := mk(...), not(r), and c == a. */ void theory_datatype::sign_recognizer_conflict(enode * c, enode * r) { SASSERT(is_constructor(c)); SASSERT(is_recognizer(r)); SASSERT(m_util.get_recognizer_constructor(r->get_decl()) == c->get_decl()); SASSERT(c->get_root() == r->get_arg(0)->get_root()); TRACE("recognizer_conflict", tout << mk_ismt2_pp(c->get_owner(), get_manager()) << "\n" << mk_ismt2_pp(r->get_owner(), get_manager()) << "\n";); context & ctx = get_context(); literal l(ctx.enode2bool_var(r)); SASSERT(ctx.get_assignment(l) == l_false); l.neg(); SASSERT(ctx.get_assignment(l) == l_true); enode_pair p(c, r->get_arg(0)); region & reg = ctx.get_region(); ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), reg, 1, &l, 1, &p))); } /** \brief Given a field update n := { r with field := v } for constructor C, assert the axioms: (=> (is-C r) (= (acc_j n) (acc_j r))) for acc_j != field (=> (is-C r) (= (field n) v)) for acc_j != field (=> (not (is-C r)) (= n r)) */ void theory_datatype::assert_update_field_axioms(enode * n) { m_stats.m_assert_update_field++; SASSERT(is_update_field(n)); context & ctx = get_context(); ast_manager & m = get_manager(); app* own = n->get_owner(); expr* arg1 = own->get_arg(0); func_decl * upd = n->get_decl(); func_decl * acc = to_func_decl(upd->get_parameter(0).get_ast()); func_decl * con = m_util.get_accessor_constructor(acc); func_decl * rec = m_util.get_constructor_recognizer(con); ptr_vector const * accessors = m_util.get_constructor_accessors(con); ptr_vector::const_iterator it = accessors->begin(); ptr_vector::const_iterator end = accessors->end(); app_ref rec_app(m.mk_app(rec, arg1), m); ctx.internalize(rec_app, false); literal is_con(ctx.get_bool_var(rec_app)); for (; it != end; ++it) { enode* arg; func_decl * acc1 = *it; if (acc1 == acc) { arg = n->get_arg(1); } else { app* acc_app = m.mk_app(acc1, arg1); ctx.internalize(acc_app, false); arg = ctx.get_enode(acc_app); } app * acc_own = m.mk_app(acc1, own); assert_eq_axiom(arg, acc_own, is_con); } // update_field is identity if 'n' is not created by a matching constructor. assert_eq_axiom(n, arg1, ~is_con); } theory_var theory_datatype::mk_var(enode * n) { theory_var r = theory::mk_var(n); theory_var r2 = m_find.mk_var(); SASSERT(r == r2); SASSERT(r == static_cast(m_var_data.size())); m_var_data.push_back(alloc(var_data)); var_data * d = m_var_data[r]; context & ctx = get_context(); ctx.attach_th_var(n, this, r); if (is_constructor(n)) { d->m_constructor = n; assert_accessor_axioms(n); } else if (is_update_field(n)) { assert_update_field_axioms(n); } else { ast_manager & m = get_manager(); sort * s = m.get_sort(n->get_owner()); if (m_util.get_datatype_num_constructors(s) == 1) { func_decl * c = m_util.get_datatype_constructors(s)->get(0); assert_is_constructor_axiom(n, c, null_literal); } else { if (m_params.m_dt_lazy_splits == 0 || (m_params.m_dt_lazy_splits == 1 && !s->is_infinite())) mk_split(r); } } return r; } bool theory_datatype::internalize_atom(app * atom, bool gate_ctx) { TRACE("datatype", tout << "internalizing atom:\n" << mk_pp(atom, get_manager()) << "\n";); return internalize_term(atom); } bool theory_datatype::internalize_term(app * term) { TRACE("datatype", tout << "internalizing term:\n" << mk_pp(term, get_manager()) << "\n";); context & ctx = get_context(); unsigned num_args = term->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(term->get_arg(i), false); // the internalization of the arguments may trigger the internalization of term. if (ctx.e_internalized(term)) return true; enode * e = ctx.mk_enode(term, false, get_manager().is_bool(term), true); // possible optimization, the third argument may be set to false, if the term (actually, atom) is not in the context of a gate. if (get_manager().is_bool(term)) { bool_var bv = ctx.mk_bool_var(term); ctx.set_var_theory(bv, get_id()); ctx.set_enode_flag(bv, true); } if (is_constructor(term) || is_update_field(term)) { SASSERT(!is_attached_to_var(e)); // *** We must create a theory variable for each argument that has sort datatype *** // // The apply_sort_cnstr method will not create a theory // variable for an expression N when sort of N has an // infinite number of elements. // // This may create problems during model construction. // For example, suppose we have // x1 = cons(v1, x2) // and x1 and x2 are lists of integers. // This sort has an infinite number of elements. So, in principle, // we do not need a theory variable for x2. // Recall that if an expression is not associated with a // theory variable, then a fresh value is associated with // it. // Moreover, fresh variables of sort S can only be created after the // interpretation for each (relevant) expression of sort S in the // logical context is created. Returning to the example, // to create the interpretation of x1 we need the // interpretation for x2. So, x2 cannot be a fresh value, // since it would have to be created after x1. // for (unsigned i = 0; i < num_args; i++) { enode * arg = e->get_arg(i); sort * s = get_manager().get_sort(arg->get_owner()); if (!m_util.is_datatype(s)) continue; if (is_attached_to_var(arg)) continue; mk_var(arg); } mk_var(e); } else { SASSERT(is_accessor(term) || is_recognizer(term)); SASSERT(term->get_num_args() == 1); enode * arg = e->get_arg(0); if (!is_attached_to_var(arg)) mk_var(arg); SASSERT(is_attached_to_var(arg)); } if (is_recognizer(term)) { enode * arg = e->get_arg(0); theory_var v = arg->get_th_var(get_id()); SASSERT(v != null_theory_var); // When relevancy propagation is enabled, the recognizer is only added when it is marked as relevant. if (!ctx.relevancy()) add_recognizer(v, e); } return true; } void theory_datatype::apply_sort_cnstr(enode * n, sort * s) { // Remark: If s is an infinite sort, then it is not necessary to create // a theory variable. // // Actually, when the logical context has quantifiers, it is better to // disable this optimization. // Example: // // (forall (l list) (a Int) (= (len (cons a l)) (+ (len l) 1))) // (assert (> (len a) 1) // // If the theory variable is not created for 'a', then a wrong model will be generated. TRACE("datatype", tout << "apply_sort_cnstr: #" << n->get_owner_id() << "\n";); TRACE("datatype_bug", tout << "apply_sort_cnstr:\n" << mk_pp(n->get_owner(), get_manager()) << "\n";); if ((get_context().has_quantifiers() || (m_util.is_datatype(s) && !s->is_infinite())) && !is_attached_to_var(n)) { mk_var(n); } } void theory_datatype::new_eq_eh(theory_var v1, theory_var v2) { m_find.merge(v1, v2); } bool theory_datatype::use_diseqs() const { return false; } void theory_datatype::new_diseq_eh(theory_var v1, theory_var v2) { UNREACHABLE(); } void theory_datatype::assign_eh(bool_var v, bool is_true) { context & ctx = get_context(); enode * n = ctx.bool_var2enode(v); if (!is_recognizer(n)) return; TRACE("datatype", tout << "assigning recognizer: #" << n->get_owner_id() << " is_true: " << is_true << "\n" << mk_bounded_pp(n->get_owner(), get_manager()) << "\n";); SASSERT(n->get_num_args() == 1); enode * arg = n->get_arg(0); theory_var tv = arg->get_th_var(get_id()); tv = m_find.find(tv); var_data * d = m_var_data[tv]; func_decl * r = n->get_decl(); func_decl * c = m_util.get_recognizer_constructor(r); if (is_true) { SASSERT(tv != null_theory_var); if (d->m_constructor != 0 && d->m_constructor->get_decl() == c) return; // do nothing assert_is_constructor_axiom(arg, c, literal(v)); } else { if (d->m_constructor != 0) { if (d->m_constructor->get_decl() == c) { // conflict sign_recognizer_conflict(d->m_constructor, n); } } else { propagate_recognizer(tv, n); } } } void theory_datatype::relevant_eh(app * n) { TRACE("datatype", tout << "relevant_eh: " << mk_bounded_pp(n, get_manager()) << "\n";); context & ctx = get_context(); SASSERT(ctx.relevancy()); if (is_recognizer(n)) { TRACE("datatype", tout << "relevant_eh: #" << n->get_id() << "\n" << mk_bounded_pp(n, get_manager()) << "\n";); SASSERT(ctx.e_internalized(n)); enode * e = ctx.get_enode(n); theory_var v = e->get_arg(0)->get_th_var(get_id()); SASSERT(v != null_theory_var); add_recognizer(v, e); } } void theory_datatype::push_scope_eh() { theory::push_scope_eh(); m_trail_stack.push_scope(); } void theory_datatype::pop_scope_eh(unsigned num_scopes) { m_trail_stack.pop_scope(num_scopes); unsigned num_old_vars = get_old_num_vars(num_scopes); std::for_each(m_var_data.begin() + num_old_vars, m_var_data.end(), delete_proc()); m_var_data.shrink(num_old_vars); theory::pop_scope_eh(num_scopes); SASSERT(m_find.get_num_vars() == m_var_data.size()); SASSERT(m_find.get_num_vars() == get_num_vars()); } final_check_status theory_datatype::final_check_eh() { int num_vars = get_num_vars(); final_check_status r = FC_DONE; for (int v = 0; v < num_vars; v++) { if (v == static_cast(m_find.find(v))) { enode * node = get_enode(v); if (occurs_check(node)) { // conflict was detected... // return... return FC_CONTINUE; } if (m_params.m_dt_lazy_splits > 0) { // using lazy case splits... var_data * d = m_var_data[v]; if (d->m_constructor == 0) { mk_split(v); r = FC_CONTINUE; } } } } return r; } /** \brief Check if n can be reached starting from n and following equalities and constructors. For example, occur_check(a1) returns true in the following set of equalities: a1 = cons(v1, a2) a2 = cons(v2, a3) a3 = cons(v3, a1) */ bool theory_datatype::occurs_check(enode * n) { TRACE("datatype", tout << "occurs check: #" << n->get_owner_id() << "\n";); m_to_unmark.reset(); m_used_eqs.reset(); m_main = n; bool res = occurs_check_core(m_main); unmark_enodes(m_to_unmark.size(), m_to_unmark.c_ptr()); if (res) { context & ctx = get_context(); region & r = ctx.get_region(); ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, 0, m_used_eqs.size(), m_used_eqs.c_ptr()))); TRACE("occurs_check", tout << "occurs_check: true\n"; svector::const_iterator it = m_used_eqs.begin(); svector::const_iterator end = m_used_eqs.end(); for(; it != end; ++it) { enode_pair const & p = *it; tout << "eq: #" << p.first->get_owner_id() << " #" << p.second->get_owner_id() << "\n"; tout << mk_bounded_pp(p.first->get_owner(), get_manager()) << " " << mk_bounded_pp(p.second->get_owner(), get_manager()) << "\n"; }); } return res; } /** \brief Auxiliary method for occurs_check. TODO: improve performance. */ bool theory_datatype::occurs_check_core(enode * app) { if (app->is_marked()) return false; m_stats.m_occurs_check++; app->set_mark(); m_to_unmark.push_back(app); TRACE("datatype", tout << "occurs check_core: #" << app->get_owner_id() << " #" << m_main->get_owner_id() << "\n";); theory_var v = app->get_root()->get_th_var(get_id()); if (v != null_theory_var) { v = m_find.find(v); var_data * d = m_var_data[v]; if (d->m_constructor) { if (app != d->m_constructor) m_used_eqs.push_back(enode_pair(app, d->m_constructor)); unsigned num_args = d->m_constructor->get_num_args(); for (unsigned i = 0; i < num_args; i++) { enode * arg = d->m_constructor->get_arg(i); if (arg->get_root() == m_main->get_root()) { if (arg != m_main) m_used_eqs.push_back(enode_pair(arg, m_main)); return true; } if (m_util.is_datatype(get_manager().get_sort(arg->get_owner())) && occurs_check_core(arg)) return true; } if (app != d->m_constructor) { SASSERT(m_used_eqs.back().first == app); SASSERT(m_used_eqs.back().second == d->m_constructor); m_used_eqs.pop_back(); } } } return false; } void theory_datatype::reset_eh() { m_trail_stack.reset(); std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc()); m_var_data.reset(); theory::reset_eh(); m_util.reset(); m_stats.reset(); } bool theory_datatype::is_shared(theory_var v) const { // In principle, parametric theories such as Array Theory and // Datatype Theory need to implement this method. However, the datatype theory // propagates all implied equalities. And, the is_shared method is essentially used // to create interface equalities. So, it is safe to return false. return false; } theory_datatype::theory_datatype(ast_manager & m, theory_datatype_params & p): theory(m.mk_family_id("datatype")), m_params(p), m_util(m), m_find(*this), m_trail_stack(*this) { } theory_datatype::~theory_datatype() { std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc()); m_var_data.reset(); } void theory_datatype::display(std::ostream & out) const { out << "Theory datatype:\n"; unsigned num_vars = get_num_vars(); for (unsigned v = 0; v < num_vars; v++) display_var(out, v); } void theory_datatype::collect_statistics(::statistics & st) const { st.update("datatype occurs check", m_stats.m_occurs_check); st.update("datatype splits", m_stats.m_splits); st.update("datatype constructor ax", m_stats.m_assert_cnstr); st.update("datatype accessor ax", m_stats.m_assert_accessor); st.update("datatype update ax", m_stats.m_assert_update_field); } void theory_datatype::display_var(std::ostream & out, theory_var v) const { var_data * d = m_var_data[v]; out << "v" << v << " #" << get_enode(v)->get_owner_id() << " -> v" << m_find.find(v) << " "; if (d->m_constructor) out << mk_bounded_pp(d->m_constructor->get_owner(), get_manager()); else out << "(null)"; out << "\n"; } void theory_datatype::init_model(model_generator & m) { m_factory = alloc(datatype_factory, get_manager(), m.get_model()); m.register_factory(m_factory); } class datatype_value_proc : public model_value_proc { func_decl * m_constructor; svector m_dependencies; public: datatype_value_proc(func_decl * d):m_constructor(d) {} void add_dependency(enode * n) { m_dependencies.push_back(model_value_dependency(n)); } virtual ~datatype_value_proc() {} virtual void get_dependencies(buffer & result) { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } virtual app * mk_value(model_generator & mg, ptr_vector & values) { SASSERT(values.size() == m_dependencies.size()); return mg.get_manager().mk_app(m_constructor, values.size(), values.c_ptr()); } }; model_value_proc * theory_datatype::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); v = m_find.find(v); SASSERT(v != null_theory_var); var_data * d = m_var_data[v]; SASSERT(d->m_constructor); func_decl * c_decl = d->m_constructor->get_decl(); datatype_value_proc * result = alloc(datatype_value_proc, c_decl); unsigned num = d->m_constructor->get_num_args(); for (unsigned i = 0; i < num; i++) result->add_dependency(d->m_constructor->get_arg(i)); return result; } void theory_datatype::merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) { // v1 is the new root TRACE("datatype", tout << "merging v" << v1 << " v" << v2 << "\n";); SASSERT(v1 == static_cast(m_find.find(v1))); var_data * d1 = m_var_data[v1]; var_data * d2 = m_var_data[v2]; if (d2->m_constructor != 0) { context & ctx = get_context(); if (d1->m_constructor != 0 && d1->m_constructor->get_decl() != d2->m_constructor->get_decl()) { region & r = ctx.get_region(); enode_pair p(d1->m_constructor, d2->m_constructor); SASSERT(d1->m_constructor->get_root() == d2->m_constructor->get_root()); ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, 0, 1, &p))); } if (d1->m_constructor == 0) { m_trail_stack.push(set_ptr_trail(d1->m_constructor)); // check whether there is a recognizer in d1 that conflicts with d2->m_constructor; if (!d1->m_recognizers.empty()) { unsigned c_idx = m_util.get_constructor_idx(d2->m_constructor->get_decl()); enode * recognizer = d1->m_recognizers[c_idx]; if (recognizer != 0 && ctx.get_assignment(recognizer) == l_false) { sign_recognizer_conflict(d2->m_constructor, recognizer); return; } } d1->m_constructor = d2->m_constructor; } } ptr_vector::iterator it = d2->m_recognizers.begin(); ptr_vector::iterator end = d2->m_recognizers.end(); for (; it != end; ++it) if (*it) add_recognizer(v1, *it); } void theory_datatype::unmerge_eh(theory_var v1, theory_var v2) { // do nothing } void theory_datatype::add_recognizer(theory_var v, enode * recognizer) { SASSERT(is_recognizer(recognizer)); context & ctx = get_context(); v = m_find.find(v); var_data * d = m_var_data[v]; sort * s = recognizer->get_decl()->get_domain(0); if (d->m_recognizers.empty()) { SASSERT(m_util.is_datatype(s)); d->m_recognizers.resize(m_util.get_datatype_num_constructors(s), 0); } SASSERT(d->m_recognizers.size() == m_util.get_datatype_num_constructors(s)); unsigned c_idx = m_util.get_recognizer_constructor_idx(recognizer->get_decl()); if (d->m_recognizers[c_idx] == 0) { lbool val = ctx.get_assignment(recognizer); TRACE("datatype", tout << "adding recognizer to v" << v << " rec: #" << recognizer->get_owner_id() << " val: " << val << "\n";); if (val == l_true) { // do nothing... // If recognizer assignment was already processed, then // d->m_constructor is already set. // Otherwise, it will be set when assign_eh is invoked. return; } if (val == l_false && d->m_constructor != 0) { func_decl * c_decl = m_util.get_recognizer_constructor(recognizer->get_decl()); if (d->m_constructor->get_decl() == c_decl) { // conflict sign_recognizer_conflict(d->m_constructor, recognizer); } return; } SASSERT(val == l_undef || (val == l_false && d->m_constructor == 0)); d->m_recognizers[c_idx] = recognizer; m_trail_stack.push(set_vector_idx_trail(d->m_recognizers, c_idx)); if (val == l_false) { propagate_recognizer(v, recognizer); } } } /** \brief Propagate a recognizer assigned to false. */ void theory_datatype::propagate_recognizer(theory_var v, enode * recognizer) { SASSERT(is_recognizer(recognizer)); SASSERT(static_cast(m_find.find(v)) == v); context & ctx = get_context(); SASSERT(ctx.get_assignment(recognizer) == l_false); unsigned num_unassigned = 0; unsigned unassigned_idx = UINT_MAX; enode * n = get_enode(v); sort * dt = get_manager().get_sort(n->get_owner()); var_data * d = m_var_data[v]; CTRACE("datatype", d->m_recognizers.empty(), ctx.display(tout);); SASSERT(!d->m_recognizers.empty()); literal_vector lits; svector eqs; ptr_vector::const_iterator it = d->m_recognizers.begin(); ptr_vector::const_iterator end = d->m_recognizers.end(); for (unsigned idx = 0; it != end; ++it, ++idx) { enode * r = *it; if (r && ctx.get_assignment(r) == l_true) return; // nothing to be propagated if (r && ctx.get_assignment(r) == l_false) { SASSERT(r->get_num_args() == 1); lits.push_back(literal(ctx.enode2bool_var(r), true)); if (n != r->get_arg(0)) { // Argument of the current recognizer is not necessarily equal to n. // This can happen when n and r->get_arg(0) are in the same equivalence class. // We must add equality as an assumption to the conflict or propagation SASSERT(n->get_root() == r->get_arg(0)->get_root()); eqs.push_back(enode_pair(n, r->get_arg(0))); } continue; } if (num_unassigned == 0) unassigned_idx = idx; num_unassigned++; } if (num_unassigned == 0) { // conflict SASSERT(!lits.empty()); region & reg = ctx.get_region(); TRACE("datatype_conflict", tout << mk_ismt2_pp(recognizer->get_owner(), get_manager()) << "\n"; for (unsigned i = 0; i < lits.size(); i++) { ctx.display_detailed_literal(tout, lits[i]); tout << "\n"; } for (unsigned i = 0; i < eqs.size(); i++) { tout << mk_ismt2_pp(eqs[i].first->get_owner(), get_manager()) << " = " << mk_ismt2_pp(eqs[i].second->get_owner(), get_manager()) << "\n"; }); ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), reg, lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr()))); } else if (num_unassigned == 1) { // propagate remaining recognizer SASSERT(!lits.empty()); enode * r = d->m_recognizers[unassigned_idx]; literal consequent; if (!r) { ptr_vector const * constructors = m_util.get_datatype_constructors(dt); func_decl * rec = m_util.get_constructor_recognizer(constructors->get(unassigned_idx)); app * rec_app = get_manager().mk_app(rec, n->get_owner()); ctx.internalize(rec_app, false); consequent = literal(ctx.get_bool_var(rec_app)); } else { consequent = literal(ctx.enode2bool_var(r)); } ctx.mark_as_relevant(consequent); region & reg = ctx.get_region(); ctx.assign(consequent, ctx.mk_justification(ext_theory_propagation_justification(get_id(), reg, lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), consequent))); } else { // there are more than 2 unassigned recognizers... // if eager splits are enabled... create new case split if (m_params.m_dt_lazy_splits == 0 || (!dt->is_infinite() && m_params.m_dt_lazy_splits == 1)) mk_split(v); } } /** \brief Create a new case split for v. That is, create the atom (is_mk v) and mark it as relevant. If first is true, it means that v does not have recognizer yet. */ void theory_datatype::mk_split(theory_var v) { context & ctx = get_context(); ast_manager & m = get_manager(); v = m_find.find(v); enode * n = get_enode(v); sort * s = m.get_sort(n->get_owner()); func_decl * non_rec_c = m_util.get_non_rec_constructor(s); TRACE("datatype_bug", tout << "non_rec_c: " << non_rec_c->get_name() << "\n";); unsigned non_rec_idx = m_util.get_constructor_idx(non_rec_c); var_data * d = m_var_data[v]; SASSERT(d->m_constructor == 0); func_decl * r = 0; m_stats.m_splits++; if (d->m_recognizers.empty()) { r = m_util.get_constructor_recognizer(non_rec_c); } else { enode * recognizer = d->m_recognizers[non_rec_idx]; if (recognizer == 0) { r = m_util.get_constructor_recognizer(non_rec_c); } else if (!ctx.is_relevant(recognizer)) { ctx.mark_as_relevant(recognizer); return; } else if (ctx.get_assignment(recognizer) != l_false) { // if is l_true, then we are done // otherwise wait recognizer to be assigned. return; } else { // look for a slot of d->m_recognizers that is 0, or it is not marked as relevant and is unassigned. ptr_vector::const_iterator it = d->m_recognizers.begin(); ptr_vector::const_iterator end = d->m_recognizers.end(); for (unsigned idx = 0; it != end; ++it, ++idx) { enode * curr = *it; if (curr == 0) { ptr_vector const * constructors = m_util.get_datatype_constructors(s); // found empty slot... r = m_util.get_constructor_recognizer(constructors->get(idx)); break; } else if (!ctx.is_relevant(curr)) { ctx.mark_as_relevant(curr); return; } else if (ctx.get_assignment(curr) != l_false) { return; } } if (r == 0) return; // all recognizers are asserted to false... conflict will be detected... } } SASSERT(r != 0); app * r_app = m.mk_app(r, n->get_owner()); TRACE("datatype", tout << "creating split: " << mk_bounded_pp(r_app, m) << "\n";); ctx.internalize(r_app, false); bool_var bv = ctx.get_bool_var(r_app); ctx.set_true_first_flag(bv); ctx.mark_as_relevant(bv); } }; z3-z3-4.4.1/src/smt/theory_datatype.h000066400000000000000000000107511260446376700174010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_datatype.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-31. Revision History: --*/ #ifndef THEORY_DATATYPE_H_ #define THEORY_DATATYPE_H_ #include"smt_theory.h" #include"union_find.h" #include"theory_datatype_params.h" #include"datatype_decl_plugin.h" #include"datatype_factory.h" namespace smt { class theory_datatype : public theory { typedef trail_stack th_trail_stack; typedef union_find th_union_find; struct var_data { ptr_vector m_recognizers; //!< recognizers of this equivalence class that are being watched. enode * m_constructor; //!< constructor of this equivalence class, 0 if there is no constructor in the eqc. var_data(): m_constructor(0) { } }; struct stats { unsigned m_occurs_check, m_splits; unsigned m_assert_cnstr, m_assert_accessor, m_assert_update_field; void reset() { memset(this, 0, sizeof(stats)); } stats() { reset(); } }; theory_datatype_params & m_params; datatype_util m_util; ptr_vector m_var_data; th_union_find m_find; th_trail_stack m_trail_stack; datatype_factory * m_factory; stats m_stats; bool is_constructor(app * f) const { return m_util.is_constructor(f); } bool is_recognizer(app * f) const { return m_util.is_recognizer(f); } bool is_accessor(app * f) const { return m_util.is_accessor(f); } bool is_update_field(app * f) const { return m_util.is_update_field(f); } bool is_constructor(enode * n) const { return is_constructor(n->get_owner()); } bool is_recognizer(enode * n) const { return is_recognizer(n->get_owner()); } bool is_accessor(enode * n) const { return is_accessor(n->get_owner()); } bool is_update_field(enode * n) const { return m_util.is_update_field(n->get_owner()); } void assert_eq_axiom(enode * lhs, expr * rhs, literal antecedent); void assert_is_constructor_axiom(enode * n, func_decl * c, literal antecedent); void assert_accessor_axioms(enode * n); void assert_update_field_axioms(enode * n); void add_recognizer(theory_var v, enode * recognizer); void propagate_recognizer(theory_var v, enode * r); void sign_recognizer_conflict(enode * c, enode * r); ptr_vector m_to_unmark; svector m_used_eqs; enode * m_main; bool occurs_check(enode * n); bool occurs_check_core(enode * n); void mk_split(theory_var v); void display_var(std::ostream & out, theory_var v) const; protected: virtual theory_var mk_var(enode * n); virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); virtual void apply_sort_cnstr(enode * n, sort * s); virtual void new_eq_eh(theory_var v1, theory_var v2); virtual bool use_diseqs() const; virtual void new_diseq_eh(theory_var v1, theory_var v2); virtual void assign_eh(bool_var v, bool is_true); virtual void relevant_eh(app * n); virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual final_check_status final_check_eh(); virtual void reset_eh(); virtual bool is_shared(theory_var v) const; public: theory_datatype(ast_manager & m, theory_datatype_params & p); virtual ~theory_datatype(); virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_datatype, get_manager(), m_params); } virtual void display(std::ostream & out) const; virtual void collect_statistics(::statistics & st) const; virtual void init_model(model_generator & m); virtual model_value_proc * mk_value(enode * n, model_generator & m); th_trail_stack & get_trail_stack() { return m_trail_stack; } virtual void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var); static void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {} void unmerge_eh(theory_var v1, theory_var v2); virtual char const * get_name() const { return "datatype"; } }; }; #endif /* THEORY_DATATYPE_H_ */ z3-z3-4.4.1/src/smt/theory_dense_diff_logic.cpp000066400000000000000000000007241260446376700213630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_dense_diff_logic.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-16. Revision History: --*/ #include"theory_dense_diff_logic_def.h" namespace smt { template class theory_dense_diff_logic; template class theory_dense_diff_logic; template class theory_dense_diff_logic; template class theory_dense_diff_logic; }; z3-z3-4.4.1/src/smt/theory_dense_diff_logic.h000066400000000000000000000253361260446376700210360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_dense_diff_logic.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-16. Revision History: TODO: eager equality propagation --*/ #ifndef THEORY_DENSE_DIFF_LOGIC_H_ #define THEORY_DENSE_DIFF_LOGIC_H_ #include"theory_arith.h" #include"theory_arith_params.h" #include"arith_decl_plugin.h" #include"arith_eq_adapter.h" #include"theory_opt.h" namespace smt { struct theory_dense_diff_logic_statistics { unsigned m_num_assertions; unsigned m_num_propagations; void reset() { m_num_assertions = 0; m_num_propagations = 0; } theory_dense_diff_logic_statistics() { reset(); } }; template class theory_dense_diff_logic : public theory, public theory_opt, private Ext { public: theory_dense_diff_logic_statistics m_stats; private: typedef typename Ext::inf_numeral numeral; class atom { typedef typename Ext::inf_numeral numeral; bool_var m_bvar; theory_var m_source; theory_var m_target; numeral m_offset; public: atom(bool_var bv, theory_var source, theory_var target, numeral const & offset): m_bvar(bv), m_source(source), m_target(target), m_offset(offset) { } bool_var get_bool_var() const { return m_bvar; } theory_var get_source() const { return m_source; } theory_var get_target() const { return m_target; } numeral const & get_offset() const { return m_offset; } }; typedef ptr_vector atoms; typedef ptr_vector bool_var2atom; struct edge { theory_var m_source; theory_var m_target; numeral m_offset; literal m_justification; edge():m_source(null_theory_var), m_target(null_theory_var), m_justification(null_literal) {} edge(theory_var s, theory_var t, numeral const & offset, literal js): m_source(s), m_target(t), m_offset(offset), m_justification(js) { } }; typedef int edge_id; typedef vector edges; static const edge_id null_edge_id = -1; static const edge_id self_edge_id = 0; struct cell { edge_id m_edge_id; numeral m_distance; atoms m_occs; cell(): m_edge_id(null_edge_id) { } }; struct cell_trail { unsigned short m_source; unsigned short m_target; edge_id m_old_edge_id; numeral m_old_distance; cell_trail(unsigned short s, unsigned short t, edge_id old_edge_id, numeral const & old_distance): m_source(s), m_target(t), m_old_edge_id(old_edge_id), m_old_distance(old_distance) {} }; typedef vector row; typedef vector matrix; struct scope { unsigned m_atoms_lim; unsigned m_edges_lim; unsigned m_cell_trail_lim; }; theory_arith_params & m_params; arith_util m_autil; arith_eq_adapter m_arith_eq_adapter; atoms m_atoms; atoms m_bv2atoms; edges m_edges; // list of asserted edges matrix m_matrix; svector m_is_int; vector m_cell_trail; svector m_scopes; bool m_non_diff_logic_exprs; // For optimization purpose typedef vector > objective_term; vector m_objectives; vector m_objective_consts; vector m_objective_assignments; struct f_target { theory_var m_target; numeral m_new_distance; }; typedef std::pair var_pair; typedef vector f_targets; literal_vector m_tmp_literals; svector m_tmp_pairs; f_targets m_f_targets; vector m_assignment; struct var_value_hash; friend struct var_value_hash; struct var_value_hash { theory_dense_diff_logic & m_th; var_value_hash(theory_dense_diff_logic & th):m_th(th) {} unsigned operator()(theory_var v) const { return m_th.m_assignment[v].hash(); } }; struct var_value_eq; friend struct var_value_eq; struct var_value_eq { theory_dense_diff_logic & m_th; var_value_eq(theory_dense_diff_logic & th):m_th(th) {} bool operator()(theory_var v1, theory_var v2) const { return m_th.m_assignment[v1] == m_th.m_assignment[v2] && m_th.is_int(v1) == m_th.is_int(v2); } }; typedef int_hashtable var_value_table; var_value_table m_var_value_table; // ----------------------------------- // // Auxiliary // // ----------------------------------- bool is_int(theory_var v) const { return m_is_int[v]; } bool is_real(theory_var v) const { return !is_int(v); } numeral const & get_epsilon(theory_var v) const { return is_real(v) ? this->m_real_epsilon : this->m_int_epsilon; } bool is_times_minus_one(expr * n, app * & r) const { expr * _r; if (m_autil.is_times_minus_one(n, _r)) { r = to_app(_r); return true; } return false; } app * mk_zero_for(expr * n); theory_var mk_var(enode * n); theory_var internalize_term_core(app * n); void found_non_diff_logic_expr(expr * n); bool is_connected(theory_var source, theory_var target) const { return m_matrix[source][target].m_edge_id != null_edge_id; } void mk_clause(literal l1, literal l2); void mk_clause(literal l1, literal l2, literal l3); void add_edge(theory_var source, theory_var target, numeral const & offset, literal l); void update_cells(); void propagate_using_cell(theory_var source, theory_var target); void get_antecedents(theory_var source, theory_var target, literal_vector & result); void assign_literal(literal l, theory_var source, theory_var target); void restore_cells(unsigned old_size); void del_atoms(unsigned old_size); void del_vars(unsigned old_num_vars); void init_model(); bool internalize_objective(expr * n, rational const& m, rational& r, objective_term & objective); expr_ref mk_ineq(theory_var v, inf_rational const& val, bool is_strict); #ifdef Z3DEBUG bool check_vector_sizes() const; bool check_matrix() const; #endif public: numeral const & get_distance(theory_var source, theory_var target) const { SASSERT(is_connected(source, target)); return m_matrix[source][target].m_distance; } // ----------------------------------- // // Internalization // // ----------------------------------- virtual bool internalize_atom(app * n, bool gate_ctx); virtual bool internalize_term(app * term); virtual void internalize_eq_eh(app * atom, bool_var v); virtual void apply_sort_cnstr(enode * n, sort * s); virtual void assign_eh(bool_var v, bool is_true); virtual void new_eq_eh(theory_var v1, theory_var v2); virtual bool use_diseqs() const; virtual void new_diseq_eh(theory_var v1, theory_var v2); virtual void conflict_resolution_eh(app * atom, bool_var v); virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual void restart_eh(); virtual void init_search_eh(); virtual final_check_status final_check_eh(); virtual bool can_propagate(); virtual void propagate(); virtual void flush_eh(); virtual void reset_eh(); bool dump_lemmas() const { return m_params.m_arith_dump_lemmas; } virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const; virtual void display(std::ostream & out) const; virtual void display_atom(std::ostream & out, atom * a) const; virtual void collect_statistics(::statistics & st) const; // ----------------------------------- // // Model generation // // ----------------------------------- arith_factory * m_factory; rational m_epsilon; // void update_epsilon(const inf_numeral & l, const inf_numeral & u); void compute_epsilon(); void fix_zero(); virtual void init_model(model_generator & m); virtual model_value_proc * mk_value(enode * n, model_generator & mg); // ----------------------------------- // // Optimization // // ----------------------------------- virtual inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared); virtual inf_eps_rational value(theory_var v); virtual theory_var add_objective(app* term); virtual expr_ref mk_gt(theory_var v, inf_rational const& val); virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val); // ----------------------------------- // // Main // // ----------------------------------- public: theory_dense_diff_logic(ast_manager & m, theory_arith_params & p); virtual ~theory_dense_diff_logic() { reset_eh(); } virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_dense_diff_logic, get_manager(), m_params); } virtual char const * get_name() const { return "difference-logic"; } /** \brief See comment in theory::mk_eq_atom */ virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return m_autil.mk_eq(lhs, rhs); } }; typedef theory_dense_diff_logic theory_dense_mi; typedef theory_dense_diff_logic theory_dense_i; typedef theory_dense_diff_logic theory_dense_smi; typedef theory_dense_diff_logic theory_dense_si; }; #endif /* THEORY_DENSE_DIFF_LOGIC_H_ */ z3-z3-4.4.1/src/smt/theory_dense_diff_logic_def.h000066400000000000000000001245771260446376700216630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_dense_diff_logic_def.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-16. Revision History: --*/ #ifndef THEORY_DENSE_DIFF_LOGIC_DEF_H_ #define THEORY_DENSE_DIFF_LOGIC_DEF_H_ #include"smt_context.h" #include"theory_dense_diff_logic.h" #include"ast_pp.h" #include"smt_model_generator.h" #include"simplex.h" #include"simplex_def.h" namespace smt { template theory_dense_diff_logic::theory_dense_diff_logic(ast_manager & m, theory_arith_params & p): theory(m.mk_family_id("arith")), m_params(p), m_autil(m), m_arith_eq_adapter(*this, p, m_autil), m_non_diff_logic_exprs(false), m_var_value_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)) { m_edges.push_back(edge()); } template inline app * theory_dense_diff_logic::mk_zero_for(expr * n) { return m_autil.mk_numeral(rational(0), get_manager().get_sort(n)); } template theory_var theory_dense_diff_logic::mk_var(enode * n) { theory_var v = theory::mk_var(n); bool is_int = m_autil.is_int(n->get_owner()); m_is_int.push_back(is_int); m_f_targets.push_back(f_target()); typename matrix::iterator it = m_matrix.begin(); typename matrix::iterator end = m_matrix.end(); for (; it != end; ++it) { it->push_back(cell()); } m_matrix.push_back(row()); row & r = m_matrix.back(); SASSERT(r.empty()); r.resize(v+1); cell & c = m_matrix[v][v]; c.m_edge_id = self_edge_id; c.m_distance.reset(); SASSERT(check_vector_sizes()); get_context().attach_th_var(n, this, v); return v; } template theory_var theory_dense_diff_logic::internalize_term_core(app * n) { context & ctx = get_context(); if (ctx.e_internalized(n)) { enode * e = ctx.get_enode(n); if (is_attached_to_var(e)) return e->get_th_var(get_id()); } rational _k; if (m_autil.is_add(n) && to_app(n)->get_num_args() == 2 && m_autil.is_numeral(to_app(n)->get_arg(0), _k)) { numeral k(_k); if (m_params.m_arith_reflect) internalize_term_core(to_app(to_app(n)->get_arg(0))); theory_var s = internalize_term_core(to_app(to_app(n)->get_arg(1))); enode * e = ctx.mk_enode(n, !m_params.m_arith_reflect, false, true); theory_var v = mk_var(e); add_edge(s, v, k, null_literal); k.neg(); add_edge(v, s, k, null_literal); return v; } else if (m_autil.is_numeral(n, _k)) { enode * e = ctx.mk_enode(n, false, false, true); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. // e->mark_as_interpreted(); theory_var v = mk_var(e); if (_k.is_zero()) return v; theory_var z = internalize_term_core(mk_zero_for(n)); numeral k(_k); add_edge(z, v, k, null_literal); k.neg(); add_edge(v, z, k, null_literal); return v; } else if (!m_autil.is_arith_expr(n)) { if (!ctx.e_internalized(n)) ctx.internalize(n, false); enode * e = ctx.get_enode(n); if (!is_attached_to_var(e)) return mk_var(e); else return e->get_th_var(get_id()); } else { return null_theory_var; } } template void theory_dense_diff_logic::found_non_diff_logic_expr(expr * n) { if (!m_non_diff_logic_exprs) { TRACE("non_diff_logic", tout << "found non diff logic expression:\n" << mk_pp(n, get_manager()) << "\n";); get_context().push_trail(value_trail(m_non_diff_logic_exprs)); IF_VERBOSE(0, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, get_manager()) << ")\n";); m_non_diff_logic_exprs = true; } } template bool theory_dense_diff_logic::internalize_atom(app * n, bool gate_ctx) { if (memory::above_high_watermark()) { found_non_diff_logic_expr(n); // little hack... TODO: change to no_memory and return l_undef if SAT return false; } TRACE("ddl", tout << "internalizing atom:\n" << mk_pp(n, get_manager()) << "\n";); context & ctx = get_context(); SASSERT(!ctx.b_internalized(n)); SASSERT(m_autil.is_le(n) || m_autil.is_ge(n)); theory_var source, target; SASSERT(m_autil.is_le(n) || m_autil.is_ge(n)); app * lhs = to_app(n->get_arg(0)); app * rhs = to_app(n->get_arg(1)); SASSERT(m_autil.is_numeral(rhs)); rational _k; m_autil.is_numeral(rhs, _k); numeral offset(_k); app * s, * t; if (m_autil.is_add(lhs) && to_app(lhs)->get_num_args() == 2 && is_times_minus_one(to_app(lhs)->get_arg(1), s)) { t = to_app(to_app(lhs)->get_arg(0)); } else if (m_autil.is_mul(lhs) && to_app(lhs)->get_num_args() == 2 && m_autil.is_minus_one(to_app(lhs)->get_arg(0))) { s = to_app(to_app(lhs)->get_arg(1)); t = mk_zero_for(s); } else if (!m_autil.is_arith_expr(lhs)) { t = to_app(lhs); s = mk_zero_for(t); } else { TRACE("ddl", tout << "failed to internalize:\n" << mk_pp(n, get_manager()) << "\n";); found_non_diff_logic_expr(n); return false; } source = internalize_term_core(s); target = internalize_term_core(t); if (source == null_theory_var || target == null_theory_var) { TRACE("ddl", tout << "failed to internalize:\n" << mk_pp(n, get_manager()) << "\n";); found_non_diff_logic_expr(n); return false; } SASSERT(source != null_theory_var && target != null_theory_var); if (m_autil.is_ge(n)) { std::swap(source, target); offset.neg(); } bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); atom * a = alloc(atom, bv, source, target, offset); m_atoms.push_back(a); m_bv2atoms.setx(bv, a, 0); m_matrix[source][target].m_occs.push_back(a); m_matrix[target][source].m_occs.push_back(a); TRACE("ddl", tout << "succeeded internalizing:\n" << mk_pp(n, get_manager()) << "\n";); return true; } template void theory_dense_diff_logic::mk_clause(literal l1, literal l2) { get_context().mk_th_axiom(get_id(), l1, l2); } template void theory_dense_diff_logic::mk_clause(literal l1, literal l2, literal l3) { get_context().mk_th_axiom(get_id(), l1, l2, l3); } template bool theory_dense_diff_logic::internalize_term(app * term) { if (memory::above_high_watermark()) { found_non_diff_logic_expr(term); // little hack... TODO: change to no_memory and return l_undef if SAT return false; } TRACE("ddl", tout << "internalizing term: " << mk_pp(term, get_manager()) << "\n";); theory_var v = internalize_term_core(term); TRACE("ddl", tout << mk_pp(term, get_manager()) << "\ninternalization result: " << (v != null_theory_var) << "\n";); if (v == null_theory_var) found_non_diff_logic_expr(term); return v != null_theory_var; } template void theory_dense_diff_logic::internalize_eq_eh(app * atom, bool_var v) { if (memory::above_high_watermark()) return; context & ctx = get_context(); app * lhs = to_app(atom->get_arg(0)); app * rhs = to_app(atom->get_arg(1)); app * s; if (m_autil.is_add(lhs) && to_app(lhs)->get_num_args() == 2 && is_times_minus_one(to_app(lhs)->get_arg(1), s) && m_autil.is_numeral(rhs)) { // force axioms for (= (+ x (* -1 y)) k) // this is necessary because (+ x (* -1 y)) is not a diff logic term. m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs)); return; } if (m_params.m_arith_eager_eq_axioms) { enode * n1 = ctx.get_enode(lhs); enode * n2 = ctx.get_enode(rhs); if (n1->get_th_var(get_id()) != null_theory_var && n2->get_th_var(get_id()) != null_theory_var) m_arith_eq_adapter.mk_axioms(n1, n2); } } template void theory_dense_diff_logic::apply_sort_cnstr(enode * n, sort * s) { // do nothing... } template void theory_dense_diff_logic::assign_eh(bool_var v, bool is_true) { if (get_context().has_th_justification(v, get_id())) { TRACE("ddl", tout << "ignoring atom propagated by the theory.\n";); return; } atom * a = m_bv2atoms.get(v, 0); if (!a) { SASSERT(get_manager().is_eq(get_context().bool_var2expr(v))); return; } m_stats.m_num_assertions++; literal l = literal(v, !is_true); theory_var s = a->get_source(); theory_var t = a->get_target(); numeral k = a->get_offset(); TRACE("assign_profile", tout << "#" << get_enode(s)->get_owner_id() << " #" << get_enode(t)->get_owner_id() << " " << k << "\n";); if (l.sign()) { k.neg(); k -= get_epsilon(s); add_edge(t, s, k, l); } else { add_edge(s, t, k, l); } TRACE("ddl_detail", display(tout);); } template void theory_dense_diff_logic::new_eq_eh(theory_var v1, theory_var v2) { m_arith_eq_adapter.new_eq_eh(v1, v2); } template bool theory_dense_diff_logic::use_diseqs() const { return true; } template void theory_dense_diff_logic::new_diseq_eh(theory_var v1, theory_var v2) { m_arith_eq_adapter.new_diseq_eh(v1, v2); } template void theory_dense_diff_logic::conflict_resolution_eh(app * atom, bool_var v) { // do nothing } template void theory_dense_diff_logic::push_scope_eh() { theory::push_scope_eh(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_atoms_lim = m_atoms.size(); s.m_edges_lim = m_edges.size(); s.m_cell_trail_lim = m_cell_trail.size(); } template void theory_dense_diff_logic::pop_scope_eh(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; restore_cells(s.m_cell_trail_lim); m_edges.shrink(s.m_edges_lim); del_atoms(s.m_atoms_lim); del_vars(get_old_num_vars(num_scopes)); m_scopes.shrink(new_lvl); theory::pop_scope_eh(num_scopes); } template void theory_dense_diff_logic::restore_cells(unsigned old_size) { unsigned sz = m_cell_trail.size(); unsigned i = sz; while (i > old_size) { i--; cell_trail & t = m_cell_trail[i]; cell & c = m_matrix[t.m_source][t.m_target]; c.m_edge_id = t.m_old_edge_id; c.m_distance = t.m_old_distance; } m_cell_trail.shrink(old_size); } template void theory_dense_diff_logic::del_atoms(unsigned old_size) { typename atoms::iterator begin = m_atoms.begin() + old_size; typename atoms::iterator it = m_atoms.end(); while (it != begin) { --it; atom * a = *it; TRACE("del_atoms", tout << "deleting: p" << a->get_bool_var() << "\n";); m_bv2atoms[a->get_bool_var()] = 0; theory_var s = a->get_source(); theory_var t = a->get_target(); TRACE("del_atoms", tout << "m_matrix.size() " << m_matrix.size() << ", m_matrix[s].size() " << m_matrix[s].size() << ", m_matrix[t].size(): " << m_matrix[t].size() << ", t: " << t << ", s: " << s << "\n";); SASSERT(m_matrix[s][t].m_occs.back() == a); SASSERT(m_matrix[t][s].m_occs.back() == a); m_matrix[s][t].m_occs.pop_back(); m_matrix[t][s].m_occs.pop_back(); dealloc(a); } m_atoms.shrink(old_size); } template void theory_dense_diff_logic::del_vars(unsigned old_num_vars) { int num_vars = get_num_vars(); SASSERT(num_vars >= static_cast(old_num_vars)); if (num_vars != static_cast(old_num_vars)) { m_is_int.shrink(old_num_vars); m_f_targets.shrink(old_num_vars); m_matrix.shrink(old_num_vars); typename matrix::iterator it = m_matrix.begin(); typename matrix::iterator end = m_matrix.end(); for (; it != end; ++it) { it->shrink(old_num_vars); } } } template void theory_dense_diff_logic::restart_eh() { m_arith_eq_adapter.restart_eh(); } template void theory_dense_diff_logic::init_search_eh() { m_arith_eq_adapter.init_search_eh(); } template final_check_status theory_dense_diff_logic::final_check_eh() { init_model(); if (assume_eqs(m_var_value_table)) return FC_CONTINUE; // logical context contains arithmetic expressions that are not // in the difference logic fragment. if (m_non_diff_logic_exprs) return FC_GIVEUP; return FC_DONE; } template bool theory_dense_diff_logic::can_propagate() { // do nothing return false; } template void theory_dense_diff_logic::propagate() { // do nothing } template void theory_dense_diff_logic::flush_eh() { // do nothing } template void theory_dense_diff_logic::reset_eh() { del_atoms(0); m_atoms .reset(); m_bv2atoms .reset(); m_edges .reset(); m_matrix .reset(); m_is_int .reset(); m_f_targets .reset(); m_cell_trail .reset(); m_scopes .reset(); m_non_diff_logic_exprs = false; m_edges.push_back(edge()); theory::reset_eh(); } template bool theory_dense_diff_logic::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { return is_true ? m_assignment[v1] == m_assignment[v2] : m_assignment[v1] != m_assignment[v2]; } /** \brief Store in results the antecedents that justify that the distance between source and target. */ template void theory_dense_diff_logic::get_antecedents(theory_var source, theory_var target, literal_vector & result) { TRACE("ddl", tout << "get_antecedents, source: #" << get_enode(source)->get_owner_id() << ", target: #" << get_enode(target)->get_owner_id() << "\n";); CTRACE("ddl", !is_connected(source, target), display(tout);); SASSERT(is_connected(source, target)); svector & todo = m_tmp_pairs; todo.reset(); if (source != target) todo.push_back(var_pair(source, target)); while (!todo.empty()) { var_pair & curr = todo.back(); theory_var s = curr.first; theory_var t = curr.second; todo.pop_back(); SASSERT(is_connected(s, t)); cell & c = m_matrix[s][t]; SASSERT(c.m_edge_id != self_edge_id); edge & e = m_edges[c.m_edge_id]; if (e.m_justification != null_literal) result.push_back(e.m_justification); if (s != e.m_source) todo.push_back(var_pair(s, e.m_source)); if (e.m_target != t) todo.push_back(var_pair(e.m_target, t)); } } template void theory_dense_diff_logic::update_cells() { edge_id new_edge_id = m_edges.size() - 1; edge & last = m_edges.back(); theory_var s = last.m_source; theory_var t = last.m_target; numeral const & k = last.m_offset; // Compute set F of nodes such that: // x in F iff // k + d(t, x) < d(s, x) numeral new_dist; row & t_row = m_matrix[t]; typename row::iterator it = t_row.begin(); typename row::iterator end = t_row.end(); typename f_targets::iterator fbegin = m_f_targets.begin(); typename f_targets::iterator target = fbegin; for (theory_var x = 0; it != end; ++it, ++x) { if (it->m_edge_id != null_edge_id && x != s) { new_dist = k; new_dist += it->m_distance; cell & s_x = m_matrix[s][x]; TRACE("ddl", tout << "s: #" << get_enode(s)->get_owner_id() << " x: #" << get_enode(x)->get_owner_id() << " new_dist: " << new_dist << "\n"; tout << "already has edge: " << s_x.m_edge_id << " old dist: " << s_x.m_distance << "\n";); if (s_x.m_edge_id == null_edge_id || new_dist < s_x.m_distance) { target->m_target = x; target->m_new_distance = new_dist; ++target; } } } typename f_targets::iterator fend = target; // For each node y such that y --> s, and for each node x in F, // check whether d(y, s) + new_dist(x) < d(y, x). typename matrix::iterator it2 = m_matrix.begin(); typename matrix::iterator end2 = m_matrix.end(); for (theory_var y = 0; it2 != end2; ++it2, ++y) { if (y != t) { row & r = *it2; cell & c = r[s]; if (c.m_edge_id != null_edge_id) { numeral const & d_y_s = c.m_distance; target = fbegin; for (; target != fend; ++target) { theory_var x = target->m_target; if (x != y) { new_dist = d_y_s; new_dist += target->m_new_distance; cell & y_x = m_matrix[y][x]; if (y_x.m_edge_id == null_edge_id || new_dist < y_x.m_distance) { m_cell_trail.push_back(cell_trail(y, x, y_x.m_edge_id, y_x.m_distance)); y_x.m_edge_id = new_edge_id; y_x.m_distance = new_dist; if (!y_x.m_occs.empty()) { propagate_using_cell(y, x); } } } } } } } CASSERT("ddl", check_matrix()); } template void theory_dense_diff_logic::assign_literal(literal l, theory_var source, theory_var target) { context & ctx = get_context(); literal_vector & antecedents = m_tmp_literals; antecedents.reset(); get_antecedents(source, target, antecedents); ctx.assign(l, ctx.mk_justification(theory_propagation_justification(get_id(), ctx.get_region(), antecedents.size(), antecedents.c_ptr(), l))); } template void theory_dense_diff_logic::propagate_using_cell(theory_var source, theory_var target) { cell & c = m_matrix[source][target]; SASSERT(c.m_edge_id != null_edge_id); numeral neg_dist = c.m_distance; neg_dist.neg(); context & ctx = get_context(); typename atoms::const_iterator it = c.m_occs.begin(); typename atoms::const_iterator end = c.m_occs.end(); for (; it != end; ++it) { atom * a = *it; if (ctx.get_assignment(a->get_bool_var()) == l_undef) { if (a->get_source() == source) { SASSERT(a->get_target() == target); if (c.m_distance <= a->get_offset()) { m_stats.m_num_propagations++; TRACE("ddl", tout << "asserting atom to true: "; display_atom(tout, a); tout << "distance(#" << get_enode(source)->get_owner_id() << ", #" << get_enode(target)->get_owner_id() << "): " << c.m_distance << "\n";); assign_literal(literal(a->get_bool_var(), false), source, target); } } else { SASSERT(a->get_source() == target); SASSERT(a->get_target() == source); if (neg_dist > a->get_offset()) { m_stats.m_num_propagations++; TRACE("ddl", tout << "asserting atom to true: "; display_atom(tout, a); tout << "distance(#" << get_enode(source)->get_owner_id() << ", #" << get_enode(target)->get_owner_id() << "): " << c.m_distance << "\n";); assign_literal(literal(a->get_bool_var(), true), source, target); } } } } } template inline void theory_dense_diff_logic::add_edge(theory_var source, theory_var target, numeral const & offset, literal l) { TRACE("ddl", tout << "trying adding edge: #" << get_enode(source)->get_owner_id() << " -- " << offset << " --> #" << get_enode(target)->get_owner_id() << "\n";); cell & c_inv = m_matrix[target][source]; if (c_inv.m_edge_id != null_edge_id && - c_inv.m_distance > offset) { // conflict detected. TRACE("ddl", tout << "conflict detected: #" << get_enode(source)->get_owner_id() << " #" << get_enode(target)->get_owner_id() << " offset: " << offset << ", c_inv.m_edge_id: " << c_inv.m_edge_id << ", c_inv.m_distance: " << c_inv.m_distance << "\n";); literal_vector & antecedents = m_tmp_literals; antecedents.reset(); get_antecedents(target, source, antecedents); if (l != null_literal) antecedents.push_back(l); context & ctx = get_context(); region & r = ctx.get_region(); ctx.set_conflict(ctx.mk_justification(theory_conflict_justification(get_id(), r, antecedents.size(), antecedents.c_ptr()))); if (dump_lemmas()) { ctx.display_lemma_as_smt_problem(antecedents.size(), antecedents.c_ptr(), false_literal, ""); } return; } cell & c = m_matrix[source][target]; if (c.m_edge_id == null_edge_id || offset < c.m_distance) { TRACE("ddl", tout << "adding edge: #" << get_enode(source)->get_owner_id() << " -- " << offset << " --> #" << get_enode(target)->get_owner_id() << "\n";); m_edges.push_back(edge(source, target, offset, l)); update_cells(); } } #ifdef Z3DEBUG template bool theory_dense_diff_logic::check_vector_sizes() const { SASSERT(m_matrix.size() == m_f_targets.size()); SASSERT(m_is_int.size() == m_matrix.size()); typename matrix::const_iterator it = m_matrix.begin(); typename matrix::const_iterator end = m_matrix.end(); for (; it != end; ++it) { SASSERT(it->size() == m_matrix.size()); } return true; } template bool theory_dense_diff_logic::check_matrix() const { int sz = m_matrix.size(); for (theory_var i = 0; i < sz; i++) { for (theory_var j = 0; j < sz; j++) { cell const & c = m_matrix[i][j]; if (c.m_edge_id == self_edge_id) { SASSERT(i == j); SASSERT(c.m_distance.is_zero()); } else if (c.m_edge_id != null_edge_id) { edge const & e = m_edges[c.m_edge_id]; theory_var s = e.m_source; theory_var t = e.m_target; numeral k = get_distance(i, s); k += e.m_offset; k += get_distance(t, j); if (c.m_distance != k) { CTRACE("ddl", c.m_distance != k, tout << "i: " << i << " j: " << j << " k: " << k << " c.m_distance: " << c.m_distance << "\n"; display(tout);); SASSERT(c.m_distance == k); } } } } return true; } #endif template void theory_dense_diff_logic::display(std::ostream & out) const { out << "Theory dense difference logic:\n"; display_var2enode(out); typename matrix::const_iterator it1 = m_matrix.begin(); typename matrix::const_iterator end1 = m_matrix.end(); for (int v1 = 0; it1 != end1; ++it1, ++v1) { typename row::const_iterator it2 = it1->begin(); typename row::const_iterator end2 = it1->end(); for (int v2 = 0; it2 != end2; ++it2, ++v2) { if (it2->m_edge_id != null_edge_id && it2->m_edge_id != self_edge_id) { out << "#"; out.width(5); out << std::left << get_enode(v1)->get_owner_id() << " -- "; out.width(10); out << std::left << it2->m_distance << " : id"; out.width(5); out << std::left << it2->m_edge_id << " --> #"; out << get_enode(v2)->get_owner_id() << "\n"; } } } out << "atoms:\n"; typename atoms::const_iterator it2 = m_atoms.begin(); typename atoms::const_iterator end2 = m_atoms.end(); for (;it2 != end2; ++it2) { atom * a = *it2; display_atom(out, a); } } template void theory_dense_diff_logic::display_atom(std::ostream & out, atom * a) const { out << "#"; out.width(5); out << std::left << get_enode(a->get_target())->get_owner_id() << " - #"; out.width(5); out << std::left << get_enode(a->get_source())->get_owner_id() << " <= "; out.width(10); out << std::left << a->get_offset() << " assignment: " << get_context().get_assignment(a->get_bool_var()) << "\n"; } template void theory_dense_diff_logic::collect_statistics(::statistics & st) const { st.update("dd assertions", m_stats.m_num_assertions); st.update("dd propagations", m_stats.m_num_propagations); m_arith_eq_adapter.collect_statistics(st); } /** \brief Build a model for doing model-based theory combination. */ template void theory_dense_diff_logic::init_model() { int num_vars = get_num_vars(); m_assignment.reset(); m_assignment.resize(num_vars); for (int i = 0; i < num_vars; i++) { row & r = m_matrix[i]; numeral & d = m_assignment[i]; for (int j = 0; j < num_vars; j++) { if (i != j) { cell & c = r[j]; if (c.m_edge_id != null_edge_id && c.m_distance < d) { d = c.m_distance; } } } } for (int i = 0; i < num_vars; i++) m_assignment[i].neg(); TRACE("ddl_model", tout << "ddl model\n"; for (theory_var v = 0; v < num_vars; v++) { tout << "#" << mk_pp(get_enode(v)->get_owner(), get_manager()) << " = " << m_assignment[v] << "\n"; }); } /** The arithmetic module uses infinitesimals. So, an inf_numeral (n,k) represents n + k*epsilon where epsilon is a very small number. In order to generate a model, we need to compute a value for epsilon in a way all inequalities remain satisfied. assume we have the inequality x - y <= (n_c, k_c) where the interpretation for x and y is: (n_x, k_x) and (n_y, k_y). So, (n_x, k_x) <= (n_y + n_c, k_y + k_c) The only intersting case is n_x < n_y + n_c and k_x > k_y + k_c. Using the definition of infinitesimal numbers we have: n_x + k_x * epsilon <= n_y + n_c + (k_y + k_c) * epsilon Therefore: epsilon <= (n_y + n_c - n_x) / (k_x - k_y - k_c) */ template void theory_dense_diff_logic::compute_epsilon() { m_epsilon = rational(1); typename edges::const_iterator it = m_edges.begin(); typename edges::const_iterator end = m_edges.end(); // first edge is null SASSERT(it->m_target == null_theory_var); SASSERT(it->m_source == null_theory_var); ++it; for (; it != end; ++it) { edge const & e = *it; rational n_x = m_assignment[e.m_target].get_rational().to_rational(); rational k_x = m_assignment[e.m_target].get_infinitesimal().to_rational(); rational n_y = m_assignment[e.m_source].get_rational().to_rational(); rational k_y = m_assignment[e.m_source].get_infinitesimal().to_rational(); rational n_c = e.m_offset.get_rational().to_rational(); rational k_c = e.m_offset.get_infinitesimal().to_rational(); TRACE("epsilon", tout << "(n_x,k_x): " << n_x << ", " << k_x << ", (n_y,k_y): " << n_y << ", " << k_y << ", (n_c,k_c): " << n_c << ", " << k_c << "\n";); if (n_x < n_y + n_c && k_x > k_y + k_c) { rational new_epsilon = (n_y + n_c - n_x) / (k_x - k_y - k_c); if (new_epsilon < m_epsilon) { TRACE("epsilon", tout << "new epsilon: " << new_epsilon << "\n";); m_epsilon = new_epsilon; } } } } template void theory_dense_diff_logic::fix_zero() { int num_vars = get_num_vars(); for (int v = 0; v < num_vars; ++v) { enode * n = get_enode(v); if (m_autil.is_zero(n->get_owner()) && !m_assignment[v].is_zero()) { numeral val = m_assignment[v]; sort * s = get_manager().get_sort(n->get_owner()); // adjust the value of all variables that have the same sort. for (int v2 = 0; v2 < num_vars; ++v2) { enode * n2 = get_enode(v2); if (get_manager().get_sort(n2->get_owner()) == s) { m_assignment[v2] -= val; } } SASSERT(m_assignment[v].is_zero()); } } TRACE("ddl_model", tout << "ddl model\n"; for (theory_var v = 0; v < num_vars; v++) { tout << "#" << mk_pp(get_enode(v)->get_owner(), get_manager()) << " = " << m_assignment[v] << "\n"; }); } template void theory_dense_diff_logic::init_model(model_generator & m) { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); fix_zero(); compute_epsilon(); } template model_value_proc * theory_dense_diff_logic::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); numeral const & val = m_assignment[v]; rational num = val.get_rational().to_rational() + m_epsilon * val.get_infinitesimal().to_rational(); return alloc(expr_wrapper_proc, m_factory->mk_value(num, is_int(v))); } // TBD: code is common to both sparse and dense difference logic solvers. template bool theory_dense_diff_logic::internalize_objective(expr * n, rational const& m, rational& q, objective_term & objective) { // Compile term into objective_term format rational r; expr* x, *y; if (m_autil.is_numeral(n, r)) { q += r; } else if (m_autil.is_add(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { if (!internalize_objective(to_app(n)->get_arg(i), m, q, objective)) { return false; } } } else if (m_autil.is_mul(n, x, y) && m_autil.is_numeral(x, r)) { return internalize_objective(y, m*r, q, objective); } else if (m_autil.is_mul(n, y, x) && m_autil.is_numeral(x, r)) { return internalize_objective(y, m*r, q, objective); } else if (!is_app(n)) { return false; } else if (to_app(n)->get_family_id() == m_autil.get_family_id()) { return false; } else { context& ctx = get_context(); enode * e = 0; theory_var v = 0; if (ctx.e_internalized(n)) { e = ctx.get_enode(to_app(n)); } else { e = ctx.mk_enode(to_app(n), false, false, true); } v = e->get_th_var(get_id()); if (v == null_theory_var) { v = mk_var(e); } objective.push_back(std::make_pair(v, m)); } return true; } template inf_eps_rational theory_dense_diff_logic::value(theory_var v) { objective_term const& objective = m_objectives[v]; inf_eps r = inf_eps(m_objective_consts[v]); for (unsigned i = 0; i < objective.size(); ++i) { numeral n = m_assignment[v]; rational r1 = n.get_rational().to_rational(); rational r2 = n.get_infinitesimal().to_rational(); r += objective[i].second * inf_eps(rational(0), inf_rational(r1, r2)); } return r; } template inf_eps_rational theory_dense_diff_logic::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { typedef simplex::simplex Simplex; Simplex S; ast_manager& m = get_manager(); objective_term const& objective = m_objectives[v]; has_shared = false; IF_VERBOSE(1, for (unsigned i = 0; i < objective.size(); ++i) { verbose_stream() << objective[i].second << " * v" << objective[i].first << " "; } verbose_stream() << " + " << m_objective_consts[v] << "\n";); unsigned num_nodes = get_num_vars(); unsigned num_edges = m_edges.size(); S.ensure_var(num_nodes + num_edges + m_objectives.size()); for (unsigned i = 0; i < num_nodes; ++i) { numeral const& a = m_assignment[i]; rational fin = a.get_rational().to_rational(); rational inf = a.get_infinitesimal().to_rational(); mpq_inf q(fin.to_mpq(), inf.to_mpq()); S.set_value(i, q); } for (unsigned i = 0; i < num_nodes; ++i) { enode * n = get_enode(i); if (m_autil.is_zero(n->get_owner())) { S.set_lower(v, mpq_inf(mpq(0), mpq(0))); S.set_upper(v, mpq_inf(mpq(0), mpq(0))); break; } } svector vars; unsynch_mpq_manager mgr; scoped_mpq_vector coeffs(mgr); coeffs.push_back(mpq(1)); coeffs.push_back(mpq(-1)); coeffs.push_back(mpq(-1)); vars.resize(3); for (unsigned i = 0; i < num_edges; ++i) { edge const& e = m_edges[i]; if (e.m_source == null_theory_var || e.m_target == null_theory_var) { continue; } unsigned base_var = num_nodes + i; vars[0] = e.m_target; vars[1] = e.m_source; vars[2] = base_var; S.add_row(base_var, 3, vars.c_ptr(), coeffs.c_ptr()); // t - s <= w // t - s - b = 0, b >= w numeral const& w = e.m_offset; rational fin = w.get_rational().to_rational(); rational inf = w.get_infinitesimal().to_rational(); mpq_inf q(fin.to_mpq(),inf.to_mpq()); S.set_upper(base_var, q); } unsigned w = num_nodes + num_edges + v; // add objective function as row. coeffs.reset(); vars.reset(); for (unsigned i = 0; i < objective.size(); ++i) { coeffs.push_back(objective[i].second.to_mpq()); vars.push_back(objective[i].first); } coeffs.push_back(mpq(1)); vars.push_back(w); Simplex::row row = S.add_row(w, vars.size(), vars.c_ptr(), coeffs.c_ptr()); TRACE("opt", S.display(tout); display(tout);); // optimize lbool is_sat = S.make_feasible(); if (is_sat == l_undef) { blocker = m.mk_false(); return inf_eps::infinity(); } TRACE("opt", S.display(tout); ); SASSERT(is_sat != l_false); lbool is_fin = S.minimize(w); switch (is_fin) { case l_true: { simplex::mpq_ext::eps_numeral const& val = S.get_value(w); inf_rational r(-rational(val.first), -rational(val.second)); TRACE("opt", tout << r << " " << "\n"; S.display_row(tout, row, true);); Simplex::row_iterator it = S.row_begin(row), end = S.row_end(row); expr_ref_vector& core = m_objective_assignments[v]; expr_ref tmp(m); core.reset(); for (; it != end; ++it) { unsigned v = it->m_var; if (num_nodes <= v && v < num_nodes + num_edges) { unsigned edge_id = v - num_nodes; literal lit = m_edges[edge_id].m_justification; get_context().literal2expr(lit, tmp); core.push_back(tmp); } } for (unsigned i = 0; i < num_nodes; ++i) { mpq_inf const& val = S.get_value(i); rational q(val.first), eps(val.second); numeral a(q); m_assignment[i] = a; // TBD: if epsilon is != 0, then adjust a by some small fraction. } blocker = mk_gt(v, r); IF_VERBOSE(10, verbose_stream() << blocker << "\n";); return inf_eps(rational(0), r); } default: TRACE("opt", tout << "unbounded\n"; ); blocker = m.mk_false(); return inf_eps::infinity(); } } template theory_var theory_dense_diff_logic::add_objective(app* term) { objective_term objective; theory_var result = m_objectives.size(); rational q(1), r(0); expr_ref_vector vr(get_manager()); if (!is_linear(get_manager(), term)) { result = null_theory_var; } else if (internalize_objective(term, q, r, objective)) { m_objectives.push_back(objective); m_objective_consts.push_back(r); m_objective_assignments.push_back(vr); } else { result = null_theory_var; } return result; } template expr_ref theory_dense_diff_logic::mk_gt(theory_var v, inf_rational const& val) { return mk_ineq(v, val, true); } template expr_ref theory_dense_diff_logic::mk_ge( filter_model_converter& fm, theory_var v, inf_rational const& val) { return mk_ineq(v, val, false); } template expr_ref theory_dense_diff_logic::mk_ineq(theory_var v, inf_rational const& val, bool is_strict) { ast_manager& m = get_manager(); objective_term const& t = m_objectives[v]; expr_ref e(m), f(m), f2(m); if (t.size() == 1 && t[0].second.is_one()) { f = get_enode(t[0].first)->get_owner(); } else if (t.size() == 1 && t[0].second.is_minus_one()) { f = m_autil.mk_uminus(get_enode(t[0].first)->get_owner()); } else if (t.size() == 2 && t[0].second.is_one() && t[1].second.is_minus_one()) { f = get_enode(t[0].first)->get_owner(); f2 = get_enode(t[1].first)->get_owner(); f = m_autil.mk_sub(f, f2); } else if (t.size() == 2 && t[1].second.is_one() && t[0].second.is_minus_one()) { f = get_enode(t[1].first)->get_owner(); f2 = get_enode(t[0].first)->get_owner(); f = m_autil.mk_sub(f, f2); } else { // expr_ref_vector const& core = m_objective_assignments[v]; f = m.mk_and(core.size(), core.c_ptr()); if (is_strict) { f = m.mk_not(f); } TRACE("arith", tout << "block: " << f << "\n";); return f; } e = m_autil.mk_numeral(val.get_rational(), m.get_sort(f)); if (val.get_infinitesimal().is_neg()) { if (is_strict) { f = m_autil.mk_ge(f, e); } else { expr_ref_vector const& core = m_objective_assignments[v]; f = m.mk_and(core.size(), core.c_ptr()); } } else { if (is_strict) { f = m_autil.mk_gt(f, e); } else { f = m_autil.mk_ge(f, e); } } return f; } }; #endif /* THEORY_DENSE_DIFF_LOGIC_DEF_H_ */ z3-z3-4.4.1/src/smt/theory_diff_logic.cpp000066400000000000000000000012061260446376700202010ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: theory_diff_logic.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-04-21. Nikolaj Bjorner (nbjorner) 2008-05-05 Revision History: --*/ #include "theory_diff_logic.h" #include"rational.h" #include"theory_diff_logic_def.h" #include"sparse_matrix_def.h" namespace smt { template class theory_diff_logic; template class theory_diff_logic; template class theory_diff_logic; template class theory_diff_logic; }; namespace simplex { template class simplex; template class sparse_matrix; }; z3-z3-4.4.1/src/smt/theory_diff_logic.h000066400000000000000000000332361260446376700176560ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: theory_diff_logic.h Abstract: Author: Leonardo de Moura (leonardo) 2008-04-21. Nikolaj Bjorner (nbjorner) 2008-05-05 Revision History: --*/ #ifndef THEORY_DIFF_LOGIC_H_ #define THEORY_DIFF_LOGIC_H_ #include"rational.h" #include"inf_rational.h" #include"inf_int_rational.h" #include"s_integer.h" #include"inf_s_integer.h" #include"smt_theory.h" #include"diff_logic.h" #include"arith_decl_plugin.h" #include"smt_justification.h" #include"map.h" #include"smt_params.h" #include"arith_eq_adapter.h" #include"smt_model_generator.h" #include"numeral_factory.h" #include"smt_clause.h" #include"theory_opt.h" #include"simplex.h" #include"simplex_def.h" // The DL theory can represent term such as n + k, where n is an enode and k is a numeral. namespace smt { struct theory_diff_logic_statistics { unsigned m_num_conflicts; unsigned m_num_assertions; unsigned m_num_th2core_eqs; unsigned m_num_core2th_eqs; unsigned m_num_core2th_diseqs; unsigned m_num_core2th_new_diseqs; void reset() { memset(this, 0, sizeof(*this)); } theory_diff_logic_statistics() { reset(); } }; template class theory_diff_logic : public theory, public theory_opt, private Ext { typedef typename Ext::numeral numeral; typedef simplex::simplex Simplex; typedef inf_eps_rational inf_eps; class atom { bool_var m_bvar; bool m_true; int m_pos; int m_neg; public: atom(bool_var bv, int pos, int neg): m_bvar(bv), m_true(false), m_pos(pos), m_neg(neg) { } ~atom() {} bool_var get_bool_var() const { return m_bvar; } bool is_true() const { return m_true; } void assign_eh(bool is_true) { m_true = is_true; } int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } int get_pos() const { return m_pos; } int get_neg() const { return m_neg; } std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; }; typedef ptr_vector atoms; typedef u_map bool_var2atom; // Auxiliary info for propagating cheap equalities class eq_prop_info { int m_scc_id; numeral m_delta; theory_var m_root; public: eq_prop_info(int scc_id, const numeral & d, theory_var r = null_theory_var): m_scc_id(scc_id), m_delta(d), m_root(r) { } theory_var get_root() const { return m_root; } unsigned hash() const { return mk_mix(static_cast(m_scc_id), m_delta.hash(), 0x9e3779b9); } bool operator==(const eq_prop_info & info) const { return m_scc_id == info.m_scc_id && m_delta == info.m_delta; } }; struct eq_prop_info_hash_proc { unsigned operator()(eq_prop_info * info) const { return info->hash(); } }; struct eq_prop_info_eq_proc { bool operator()(eq_prop_info * info1, eq_prop_info * info2) const { return *info1 == *info2; } }; typedef ptr_hashtable eq_prop_info_set; // Extension for diff_logic core. struct GExt : public Ext { typedef literal explanation; }; // Functor used to collect the proofs for a conflict due to // a negative cycle. class nc_functor { literal_vector m_antecedents; theory_diff_logic& m_super; public: nc_functor(theory_diff_logic& s) : m_super(s) {} void reset(); literal_vector const& get_lits() const { return m_antecedents; } void operator()(literal const & ex) { if (ex != null_literal) { m_antecedents.push_back(ex); } } void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { m_super.new_edge(src, dst, num_edges, edges); } }; struct scope { unsigned m_atoms_lim; unsigned m_asserted_atoms_lim; unsigned m_asserted_qhead_old; }; typedef dl_graph Graph; smt_params & m_params; arith_util m_util; arith_eq_adapter m_arith_eq_adapter; theory_diff_logic_statistics m_stats; Graph m_graph; theory_var m_zero; // cache the variable representing the zero variable. int_vector m_scc_id; // Cheap equality propagation eq_prop_info_set m_eq_prop_info_set; // set of existing equality prop infos ptr_vector m_eq_prop_infos; app_ref_vector m_terms; svector m_signs; ptr_vector m_atoms; ptr_vector m_asserted_atoms; // set of asserted atoms unsigned m_asserted_qhead; bool_var2atom m_bool_var2atom; svector m_scopes; unsigned m_num_core_conflicts; unsigned m_num_propagation_calls; double m_agility; bool m_is_lia; bool m_non_diff_logic_exprs; arith_factory * m_factory; rational m_delta; nc_functor m_nc_functor; // For optimization purpose typedef vector > objective_term; vector m_objectives; vector m_objective_consts; vector m_objective_assignments; vector m_objective_rows; Simplex m_S; unsigned m_num_simplex_edges; // Set a conflict due to a negative cycle. void set_neg_cycle_conflict(); void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges); // Create a new theory variable. virtual theory_var mk_var(enode* n); virtual theory_var mk_var(app* n); void compute_delta(); void found_non_diff_logic_expr(expr * n); bool is_interpreted(app* n) const { return get_family_id() == n->get_family_id(); } public: theory_diff_logic(ast_manager& m, smt_params & params): theory(m.mk_family_id("arith")), m_params(params), m_util(m), m_arith_eq_adapter(*this, params, m_util), m_zero(null_theory_var), m_terms(m), m_asserted_qhead(0), m_num_core_conflicts(0), m_num_propagation_calls(0), m_agility(0.5), m_is_lia(true), m_non_diff_logic_exprs(false), m_factory(0), m_nc_functor(*this), m_num_simplex_edges(0) { } virtual ~theory_diff_logic() { reset_eh(); } virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_diff_logic, get_manager(), m_params); } virtual char const * get_name() const { return "difference-logic"; } /** \brief See comment in theory::mk_eq_atom */ virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return m_util.mk_eq(lhs, rhs); } virtual void init(context * ctx); virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); virtual void internalize_eq_eh(app * atom, bool_var v); virtual void assign_eh(bool_var v, bool is_true); virtual void new_eq_eh(theory_var v1, theory_var v2); virtual bool use_diseqs() const { return true; } virtual void new_diseq_eh(theory_var v1, theory_var v2); virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual void restart_eh() { m_arith_eq_adapter.restart_eh(); } virtual void relevant_eh(app* e) {} virtual void init_search_eh() { m_arith_eq_adapter.init_search_eh(); } virtual final_check_status final_check_eh(); virtual bool is_shared(theory_var v) const { return false; } virtual bool can_propagate() { return m_asserted_qhead != m_asserted_atoms.size(); } virtual void propagate(); virtual justification * why_is_diseq(theory_var v1, theory_var v2) { NOT_IMPLEMENTED_YET(); return 0; } // virtual void flush_eh(); virtual void reset_eh(); virtual void init_model(model_generator & m); virtual model_value_proc * mk_value(enode * n, model_generator & mg); virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const; virtual void display(std::ostream & out) const; virtual void collect_statistics(::statistics & st) const; // ----------------------------------- // // Optimization // // ----------------------------------- virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared); virtual inf_eps value(theory_var v); virtual theory_var add_objective(app* term); virtual expr_ref mk_gt(theory_var v, inf_rational const& val); virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val); bool internalize_objective(expr * n, rational const& m, rational& r, objective_term & objective); private: expr_ref mk_ineq(theory_var v, inf_rational const& val, bool is_strict); virtual void new_eq_eh(theory_var v1, theory_var v2, justification& j); virtual void new_diseq_eh(theory_var v1, theory_var v2, justification& j); bool decompose_linear(app_ref_vector& args, svector& signs); bool is_sign(expr* n, bool& sign); bool is_negative(app* n, app*& m); void del_atoms(unsigned old_size); void propagate_core(); bool propagate_atom(atom* a); theory_var mk_term(app* n); theory_var mk_num(app* n, rational const& r); bool is_offset(app* n, app*& v, app*& offset, rational& r); bool is_consistent() const; bool reflect() const { return m_params.m_arith_reflect; } bool theory_resolve() const { return m_params.m_theory_resolve; } unsigned lazy_pivoting_lvl() const { return m_params.m_arith_lazy_pivoting_lvl; } bool propagate_eqs() const { return m_params.m_arith_propagate_eqs; } bool dump_lemmas() const { return m_params.m_arith_dump_lemmas; } theory_var expand(bool pos, theory_var v, rational & k); void new_eq_or_diseq(bool is_eq, theory_var v1, theory_var v2, justification& eq_just); void get_eq_antecedents(theory_var v1, theory_var v2, unsigned timestamp, conflict_resolution & cr); void get_implied_bound_antecedents(edge_id bridge_edge, edge_id subsumed_edge, conflict_resolution & cr); theory_var get_zero() const { return m_zero; } void inc_conflicts(); // Optimization: // convert variables, edges and objectives to simplex. unsigned node2simplex(unsigned v); unsigned edge2simplex(unsigned e); unsigned obj2simplex(unsigned v); unsigned num_simplex_vars(); bool is_simplex_edge(unsigned e); unsigned simplex2edge(unsigned e); void update_simplex(Simplex& S); }; struct idl_ext { // TODO: It doesn't need to be a rational, but a bignum integer. static const bool m_int_theory = true; typedef rational numeral; typedef rational fin_numeral; numeral m_epsilon; idl_ext() : m_epsilon(1) {} }; struct sidl_ext { // TODO: It doesn't need to be a rational, but a bignum integer. static const bool m_int_theory = true; typedef s_integer numeral; typedef s_integer fin_numeral; numeral m_epsilon; sidl_ext() : m_epsilon(1) {} }; struct rdl_ext { static const bool m_int_theory = false; typedef inf_int_rational numeral; typedef rational fin_numeral; numeral m_epsilon; rdl_ext() : m_epsilon(rational(), true) {} }; struct srdl_ext { static const bool m_int_theory = false; typedef inf_s_integer numeral; typedef s_integer fin_numeral; numeral m_epsilon; srdl_ext() : m_epsilon(s_integer(0),true) {} }; typedef theory_diff_logic theory_idl; typedef theory_diff_logic theory_fidl; typedef theory_diff_logic theory_rdl; typedef theory_diff_logic theory_frdl; }; #endif /* THEORY_DIFF_LOGIC_H_ */ z3-z3-4.4.1/src/smt/theory_diff_logic_def.h000066400000000000000000001227301260446376700204720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_diff_logic_def.h Abstract: Difference Logic Author: Leonardo de Moura (leonardo) 2006-11-29. Nikolaj Bjorner (nbjorner) 2008-05-11 Revision History: 2008-05-11 ported from v1.2. Add theory propagation. --*/ #ifndef THEORY_DIFF_LOGIC_DEF_H_ #define THEORY_DIFF_LOGIC_DEF_H_ #include"theory_diff_logic.h" #include"smt_context.h" #include"map.h" #include"ast_pp.h" #include"warning.h" #include"smt_model_generator.h" #include"model_implicant.h" using namespace smt; template std::ostream& theory_diff_logic::atom::display(theory_diff_logic const& th, std::ostream& out) const { context& ctx = th.get_context(); lbool asgn = ctx.get_assignment(m_bvar); //SASSERT(asgn == l_undef || ((asgn == l_true) == m_true)); bool sign = (l_undef == asgn) || m_true; return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; if (l_undef == asgn) { out << "unassigned\n"; } else { th.m_graph.display_edge(out, get_asserted_edge()); } return out; } // ----------------------------------------- // theory_diff_logic::nc_functor template void theory_diff_logic::nc_functor::reset() { m_antecedents.reset(); } // ----------------------------------------- // theory_diff_logic template void theory_diff_logic::init(context * ctx) { theory::init(ctx); app* zero; enode* e; zero = m_util.mk_numeral(rational(0), true); e = ctx->mk_enode(zero, false, false, true); SASSERT(!is_attached_to_var(e)); m_zero = mk_var(e); } template bool theory_diff_logic::internalize_term(app * term) { bool result = null_theory_var != mk_term(term); CTRACE("arith", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); if (!result) { TRACE("non_diff_logic", tout << "Terms may not be internalized\n";); found_non_diff_logic_expr(term); } return result; } template class diff_logic_bounds { bool m_inf_is_set; bool m_sup_is_set; bool m_eq_found; literal m_inf_l; literal m_sup_l; literal m_eq_l; numeral m_inf_w; numeral m_sup_w; numeral m_w; public: diff_logic_bounds() { reset(numeral(0)); } void reset(numeral const& w) { m_inf_is_set = false; m_sup_is_set = false; m_eq_found = false; m_inf_l = null_literal; m_sup_l = null_literal; m_eq_l = null_literal; m_w = w; } void operator()(numeral const& w, literal l) { if (l != null_literal) { if ((w < m_w) && (!m_inf_is_set || w > m_inf_w)) { m_inf_w = w; m_inf_l = l; m_inf_is_set = true; } else if ((w > m_w) && (!m_sup_is_set || w < m_sup_w)) { m_sup_w = w; m_sup_l = l; m_sup_is_set = true; } else if (w == m_w) { m_eq_found = true; m_eq_l = l; } } } bool get_inf(numeral& w, literal& l) const { w = m_inf_w; l = m_inf_l; return m_inf_is_set; } bool get_sup(numeral& w, literal& l) const { w = m_sup_w; l = m_sup_l; return m_sup_is_set; } bool get_eq(literal& l) const { l = m_eq_l; return m_eq_found; } }; // // Atoms are of the form x + -1*y <= k, or x + -1*y = k // template void theory_diff_logic::found_non_diff_logic_expr(expr * n) { if (!m_non_diff_logic_exprs) { TRACE("non_diff_logic", tout << "found non diff logic expression:\n" << mk_pp(n, get_manager()) << "\n";); IF_VERBOSE(0, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, get_manager()) << ")\n";); get_context().push_trail(value_trail(m_non_diff_logic_exprs)); m_non_diff_logic_exprs = true; } } template bool theory_diff_logic::internalize_atom(app * n, bool gate_ctx) { context & ctx = get_context(); if (!m_util.is_le(n) && !m_util.is_ge(n)) { found_non_diff_logic_expr(n); return false; } SASSERT(m_util.is_le(n) || m_util.is_ge(n)); SASSERT(!ctx.b_internalized(n)); bool is_ge = m_util.is_ge(n); bool_var bv; rational kr; theory_var source, target; // target - source <= k app * lhs = to_app(n->get_arg(0)); app * rhs = to_app(n->get_arg(1)); if (!m_util.is_numeral(rhs)) { std::swap(rhs, lhs); is_ge = !is_ge; } if (!m_util.is_numeral(rhs, kr)) { found_non_diff_logic_expr(n); return false; } numeral k(kr); m_terms.reset(); m_signs.reset(); m_terms.push_back(lhs); m_signs.push_back(true); if (!decompose_linear(m_terms, m_signs)) { found_non_diff_logic_expr(n); return false; } if (m_terms.size() == 2 && m_signs[0] != m_signs[1]) { target = mk_var(m_terms[0].get()); source = mk_var(m_terms[1].get()); if (!m_signs[0]) { std::swap(target, source); } } else { target = mk_var(lhs); source = get_zero(); } if (is_ge) { std::swap(target, source); k.neg(); } bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); literal l(bv); // // Create axioms for situations as: // x - y <= 5 => x - y <= 7 // if (m_params.m_arith_add_binary_bounds) { literal l0; numeral k0; diff_logic_bounds bounds; bounds.reset(k); m_graph.enumerate_edges(source, target, bounds); if (bounds.get_eq(l0)) { ctx.mk_th_axiom(get_id(),~l0,l); ctx.mk_th_axiom(get_id(),~l,l0); } else { if (bounds.get_inf(k0, l0)) { SASSERT(k0 <= k); ctx.mk_th_axiom(get_id(),~l0,l); } if (bounds.get_sup(k0, l0)) { SASSERT(k <= k0); ctx.mk_th_axiom(get_id(),~l,l0); } } } edge_id pos = m_graph.add_edge(source, target, k, l); k.neg(); if (m_util.is_int(lhs)) { SASSERT(k.is_int()); k -= numeral(1); } else { m_is_lia = false; k -= this->m_epsilon; } edge_id neg = m_graph.add_edge(target, source, k, ~l); atom * a = alloc(atom, bv, pos, neg); m_atoms.push_back(a); m_bool_var2atom.insert(bv, a); TRACE("arith", tout << mk_pp(n, get_manager()) << "\n"; m_graph.display_edge(tout << "pos: ", pos); m_graph.display_edge(tout << "neg: ", neg); ); return true; } template void theory_diff_logic::internalize_eq_eh(app * atom, bool_var v) { context & ctx = get_context(); ast_manager& m = get_manager(); TRACE("arith", tout << mk_pp(atom, m) << "\n";); app * lhs = to_app(atom->get_arg(0)); app * rhs = to_app(atom->get_arg(1)); app * s; if (m_util.is_add(lhs) && to_app(lhs)->get_num_args() == 2 && is_negative(to_app(to_app(lhs)->get_arg(1)), s) && m_util.is_numeral(rhs)) { // force axioms for (= (+ x (* -1 y)) k) // this is necessary because (+ x (* -1 y)) is not a diff logic term. m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs)); return; } if (m_params.m_arith_eager_eq_axioms) { enode * n1 = ctx.get_enode(lhs); enode * n2 = ctx.get_enode(rhs); if (n1->get_th_var(get_id()) != null_theory_var && n2->get_th_var(get_id()) != null_theory_var) m_arith_eq_adapter.mk_axioms(n1, n2); } } template void theory_diff_logic::assign_eh(bool_var v, bool is_true) { m_stats.m_num_assertions++; atom * a = 0; VERIFY (m_bool_var2atom.find(v, a)); SASSERT(a); SASSERT(get_context().get_assignment(v) != l_undef); SASSERT((get_context().get_assignment(v) == l_true) == is_true); a->assign_eh(is_true); m_asserted_atoms.push_back(a); } template void theory_diff_logic::collect_statistics(::statistics & st) const { st.update("dl conflicts", m_stats.m_num_conflicts); st.update("dl asserts", m_stats.m_num_assertions); st.update("core->dl eqs", m_stats.m_num_core2th_eqs); st.update("core->dl diseqs", m_stats.m_num_core2th_diseqs); m_arith_eq_adapter.collect_statistics(st); m_graph.collect_statistics(st); } template void theory_diff_logic::push_scope_eh() { theory::push_scope_eh(); m_graph.push(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_atoms_lim = m_atoms.size(); s.m_asserted_atoms_lim = m_asserted_atoms.size(); s.m_asserted_qhead_old = m_asserted_qhead; } template void theory_diff_logic::pop_scope_eh(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; del_atoms(s.m_atoms_lim); m_asserted_atoms.shrink(s.m_asserted_atoms_lim); m_asserted_qhead = s.m_asserted_qhead_old; m_scopes.shrink(new_lvl); unsigned num_edges = m_graph.get_num_edges(); m_graph.pop(num_scopes); if (num_edges != m_graph.get_num_edges() && m_num_simplex_edges > 0) { m_S.reset(); m_num_simplex_edges = 0; m_objective_rows.reset(); } theory::pop_scope_eh(num_scopes); } template final_check_status theory_diff_logic::final_check_eh() { if (can_propagate()) { propagate_core(); return FC_CONTINUE; } TRACE("arith_final", display(tout); ); // either will already be zero (as we don't do mixed constraints). m_graph.set_to_zero(m_zero); SASSERT(is_consistent()); if (m_non_diff_logic_exprs) { return FC_GIVEUP; } return FC_DONE; } template void theory_diff_logic::del_atoms(unsigned old_size) { typename atoms::iterator begin = m_atoms.begin() + old_size; typename atoms::iterator it = m_atoms.end(); while (it != begin) { --it; atom * a = *it; bool_var bv = a->get_bool_var(); m_bool_var2atom.erase(bv); dealloc(a); } m_atoms.shrink(old_size); } template bool theory_diff_logic::decompose_linear(app_ref_vector& terms, svector& signs) { for (unsigned i = 0; i < terms.size(); ++i) { app* n = terms[i].get(); if (m_util.is_add(n)) { expr* arg = n->get_arg(0); if (!is_app(arg)) return false; terms[i] = to_app(arg); for (unsigned j = 1; j < n->get_num_args(); ++j) { arg = n->get_arg(j); if (!is_app(arg)) return false; terms.push_back(to_app(arg)); signs.push_back(signs[i]); } --i; continue; } expr* x, *y; bool sign; if (m_util.is_mul(n, x, y)) { if (is_sign(x, sign) && is_app(y)) { terms[i] = to_app(y); signs[i] = (signs[i] == sign); --i; } else if (is_sign(y, sign) && is_app(x)) { terms[i] = to_app(x); signs[i] = (signs[i] == sign); --i; } continue; } if (m_util.is_uminus(n, x) && is_app(x)) { terms[i] = to_app(x); signs[i] = !signs[i]; --i; continue; } } return true; } template bool theory_diff_logic::is_sign(expr* n, bool& sign) { rational r; expr* x; if (m_util.is_numeral(n, r)) { if (r.is_one()) { sign = true; return true; } if (r.is_minus_one()) { sign = false; return true; } } else if (m_util.is_uminus(n, x)) { if (is_sign(x, sign)) { sign = !sign; return true; } } return false; } template bool theory_diff_logic::is_negative(app* n, app*& m) { expr* a0, *a1, *a2; rational r; if (!m_util.is_mul(n, a0, a1)) { return false; } if (m_util.is_numeral(a1)) { std::swap(a0, a1); } if (m_util.is_numeral(a0, r) && r.is_minus_one() && is_app(a1)) { m = to_app(a1); return true; } if (m_util.is_uminus(a1)) { std::swap(a0, a1); } if (m_util.is_uminus(a0, a2) && m_util.is_numeral(a2, r) && r.is_one() && is_app(a1)) { m = to_app(a1); return true; } return false; } template void theory_diff_logic::propagate() { if (m_params.m_arith_adaptive) { switch(m_params.m_arith_propagation_strategy) { case ARITH_PROP_PROPORTIONAL: { ++m_num_propagation_calls; if (m_num_propagation_calls * (m_stats.m_num_conflicts + 1) > m_params.m_arith_adaptive_propagation_threshold * get_context().m_stats.m_num_conflicts) { m_num_propagation_calls = 1; TRACE("arith_prop", tout << "propagating: " << m_num_propagation_calls << "\n";); propagate_core(); } else { TRACE("arith_prop", tout << "skipping propagation " << m_num_propagation_calls << "\n";); } break; } case ARITH_PROP_AGILITY: { // update agility with factor generated by other conflicts. double g = m_params.m_arith_adaptive_propagation_threshold; while (m_num_core_conflicts < get_context().m_stats.m_num_conflicts) { m_agility = m_agility*g; ++m_num_core_conflicts; } ++m_num_propagation_calls; bool do_propagate = (m_num_propagation_calls * m_agility > m_params.m_arith_adaptive_propagation_threshold); TRACE("arith_prop", tout << (do_propagate?"propagating: ":"skipping ") << " " << m_num_propagation_calls << " agility: " << m_agility << "\n";); if (do_propagate) { m_num_propagation_calls = 0; propagate_core(); } break; } default: UNREACHABLE(); propagate_core(); } } else { propagate_core(); } } template void theory_diff_logic::inc_conflicts() { m_stats.m_num_conflicts++; if (m_params.m_arith_adaptive) { double g = m_params.m_arith_adaptive_propagation_threshold; m_agility = m_agility*g + 1 - g; } } template void theory_diff_logic::propagate_core() { bool consistent = true; while (consistent && can_propagate()) { atom * a = m_asserted_atoms[m_asserted_qhead]; m_asserted_qhead++; consistent = propagate_atom(a); } } template bool theory_diff_logic::propagate_atom(atom* a) { context& ctx = get_context(); TRACE("arith", a->display(*this, tout); tout << "\n";); if (ctx.inconsistent()) { return false; } int edge_id = a->get_asserted_edge(); if (!m_graph.enable_edge(edge_id)) { set_neg_cycle_conflict(); return false; } return true; } template void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { if (!theory_resolve()) { return; } TRACE("dl_activity", tout << "\n";); context& ctx = get_context(); numeral w(0); for (unsigned i = 0; i < num_edges; ++i) { w += m_graph.get_weight(edges[i]); } enode* e1 = get_enode(src); enode* e2 = get_enode(dst); expr* n1 = e1->get_owner(); expr* n2 = e2->get_owner(); bool is_int = m_util.is_int(n1); rational num = w.get_rational().to_rational(); expr_ref le(get_manager()); if (w.is_rational()) { // x - y <= w expr* n3 = m_util.mk_numeral(num, is_int); n2 = m_util.mk_mul(m_util.mk_numeral(rational(-1), is_int), n2); le = m_util.mk_le(m_util.mk_add(n1,n2), n3); } else { // x - y < w // <=> // not (x - y >= w) // <=> // not (y - x <= -w) // SASSERT(w.get_infinitesimal().is_neg()); expr* n3 = m_util.mk_numeral(-num, is_int); n1 = m_util.mk_mul(m_util.mk_numeral(rational(-1), is_int), n1); le = m_util.mk_le(m_util.mk_add(n2,n1), n3); le = get_manager().mk_not(le); } ctx.internalize(le, false); ctx.mark_as_relevant(le.get()); literal lit(ctx.get_literal(le)); bool_var bv = lit.var(); atom* a = 0; m_bool_var2atom.find(bv, a); SASSERT(a); edge_id e_id = a->get_pos(); literal_vector lits; for (unsigned i = 0; i < num_edges; ++i) { lits.push_back(~m_graph.get_explanation(edges[i])); } lits.push_back(lit); TRACE("dl_activity", tout << mk_pp(le, get_manager()) << "\n"; tout << "edge: " << e_id << "\n"; ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n"; ); justification * js = 0; if (get_manager().proofs_enabled()) { vector params; params.push_back(parameter(symbol("farkas"))); params.resize(lits.size()+1, parameter(rational(1))); js = new (ctx.get_region()) theory_lemma_justification(get_id(), ctx, lits.size(), lits.c_ptr(), params.size(), params.c_ptr()); } ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); if (dump_lemmas()) { char const * logic = m_is_lia ? "QF_LIA" : "QF_LRA"; ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); } #if 0 TRACE("arith", tout << "shortcut:\n"; for (unsigned i = 0; i < num_edges; ++i) { edge_id e = edges[i]; // tgt <= src + w numeral w = m_graph.get_weight(e); dl_var tgt = m_graph.get_target(e); dl_var src = m_graph.get_source(e); if (i + 1 < num_edges) { dl_var tgt2 = m_graph.get_target(edges[i+1]); SASSERT(src == tgt2); } tout << "$" << tgt << " <= $" << src << " + " << w << "\n"; } { numeral w = m_graph.get_weight(e_id); dl_var tgt = m_graph.get_target(e_id); dl_var src = m_graph.get_source(e_id); tout << "$" << tgt << " <= $" << src << " + " << w << "\n"; } ); #endif } template void theory_diff_logic::set_neg_cycle_conflict() { m_nc_functor.reset(); m_graph.traverse_neg_cycle2(m_params.m_arith_stronger_lemmas, m_nc_functor); inc_conflicts(); literal_vector const& lits = m_nc_functor.get_lits(); context & ctx = get_context(); TRACE("arith_conflict", tout << "conflict: "; for (unsigned i = 0; i < lits.size(); ++i) { ctx.display_literal_info(tout, lits[i]); } tout << "\n"; ); if (dump_lemmas()) { char const * logic = m_is_lia ? "QF_LIA" : "QF_LRA"; ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); } vector params; if (get_manager().proofs_enabled()) { params.push_back(parameter(symbol("farkas"))); params.resize(lits.size()+1, parameter(rational(1))); } ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); } template bool theory_diff_logic::is_offset(app* n, app*& v, app*& offset, rational& r) { if (!m_util.is_add(n)) { return false; } if (n->get_num_args() == 2 && m_util.is_numeral(n->get_arg(0), r)) { v = to_app(n->get_arg(1)); offset = to_app(n->get_arg(0)); return true; } if (n->get_num_args() == 2 && m_util.is_numeral(n->get_arg(1), r)) { v = to_app(n->get_arg(0)); offset = to_app(n->get_arg(1)); return true; } return false; } template theory_var theory_diff_logic::mk_term(app* n) { SASSERT(!m_util.is_sub(n)); SASSERT(!m_util.is_uminus(n)); app* a, *offset; theory_var source, target; enode* e; context& ctx = get_context(); TRACE("arith", tout << mk_pp(n, get_manager()) << "\n";); rational r; if (m_util.is_numeral(n, r)) { return mk_num(n, r); } else if (is_offset(n, a, offset, r)) { // n = a + k source = mk_var(a); for (unsigned i = 0; i < n->get_num_args(); ++i) { expr* arg = n->get_arg(i); std::cout << "internalize: " << mk_pp(arg, get_manager()) << " " << ctx.e_internalized(arg) << "\n"; if (!ctx.e_internalized(arg)) { ctx.internalize(arg, false); } } e = get_context().mk_enode(n, false, false, true); target = mk_var(e); numeral k(r); // target - source <= k, source - target <= -k m_graph.enable_edge(m_graph.add_edge(source, target, k, null_literal)); m_graph.enable_edge(m_graph.add_edge(target, source, -k, null_literal)); return target; } else if (m_util.is_add(n)) { return null_theory_var; } else if (m_util.is_mul(n)) { return null_theory_var; } else if (m_util.is_div(n)) { return null_theory_var; } else if (m_util.is_idiv(n)) { return null_theory_var; } else if (m_util.is_mod(n)) { return null_theory_var; } else if (m_util.is_rem(n)) { return null_theory_var; } else { return mk_var(n); } } template theory_var theory_diff_logic::mk_num(app* n, rational const& r) { theory_var v = null_theory_var; enode* e = 0; context& ctx = get_context(); if (r.is_zero()) { v = get_zero(); } else if (ctx.e_internalized(n)) { e = ctx.get_enode(n); v = e->get_th_var(get_id()); SASSERT(v != null_theory_var); } else { theory_var zero = get_zero(); SASSERT(n->get_num_args() == 0); e = ctx.mk_enode(n, false, false, true); v = mk_var(e); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. // e->mark_as_interpreted(); numeral k(r); // v = k: v - zero <= k, zero - v <= - k m_graph.enable_edge(m_graph.add_edge(zero, v, k, null_literal)); m_graph.enable_edge(m_graph.add_edge(v, zero, -k, null_literal)); } return v; } template theory_var theory_diff_logic::mk_var(enode* n) { theory_var v = theory::mk_var(n); TRACE("diff_logic_vars", tout << "mk_var: " << v << "\n";); m_graph.init_var(v); get_context().attach_th_var(n, this, v); return v; } template theory_var theory_diff_logic::mk_var(app* n) { context & ctx = get_context(); enode* e = 0; theory_var v = null_theory_var; if (ctx.e_internalized(n)) { e = ctx.get_enode(n); v = e->get_th_var(get_id()); } else { ctx.internalize(n, false); e = ctx.get_enode(n); } if (v == null_theory_var) { v = mk_var(e); } if (is_interpreted(n)) { TRACE("non_diff_logic", tout << "Variable should not be interpreted\n";); found_non_diff_logic_expr(n); } TRACE("arith", tout << mk_pp(n, get_manager()) << " |-> " << v << "\n";); return v; } template void theory_diff_logic::reset_eh() { for (unsigned i = 0; i < m_atoms.size(); ++i) { dealloc(m_atoms[i]); } m_graph .reset(); m_zero = null_theory_var; m_atoms .reset(); m_asserted_atoms .reset(); m_stats .reset(); m_scopes .reset(); m_asserted_qhead = 0; m_num_core_conflicts = 0; m_num_propagation_calls = 0; m_agility = 0.5; m_is_lia = true; m_non_diff_logic_exprs = false; m_objectives .reset(); m_objective_consts.reset(); m_objective_assignments.reset(); theory::reset_eh(); } template void theory_diff_logic::compute_delta() { m_delta = rational(1); unsigned num_edges = m_graph.get_num_edges(); for (unsigned i = 0; i < num_edges; ++i) { if (!m_graph.is_enabled(i)) { continue; } numeral w = m_graph.get_weight(i); dl_var tgt = m_graph.get_target(i); dl_var src = m_graph.get_source(i); rational n_x = m_graph.get_assignment(tgt).get_rational().to_rational(); rational k_x = m_graph.get_assignment(tgt).get_infinitesimal().to_rational(); rational n_y = m_graph.get_assignment(src).get_rational().to_rational(); rational k_y = m_graph.get_assignment(src).get_infinitesimal().to_rational(); rational n_c = w.get_rational().to_rational(); rational k_c = w.get_infinitesimal().to_rational(); TRACE("epsilon", tout << "(n_x,k_x): " << n_x << ", " << k_x << ", (n_y,k_y): " << n_y << ", " << k_y << ", (n_c,k_c): " << n_c << ", " << k_c << "\n";); if (n_x < n_y + n_c && k_x > k_y + k_c) { rational new_delta = (n_y + n_c - n_x) / (k_x - k_y - k_c); if (new_delta < m_delta) { TRACE("epsilon", tout << "new delta: " << new_delta << "\n";); m_delta = new_delta; } } } } template void theory_diff_logic::init_model(smt::model_generator & m) { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); compute_delta(); } template model_value_proc * theory_diff_logic::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); numeral val = m_graph.get_assignment(v); rational num = val.get_rational().to_rational() + m_delta * val.get_infinitesimal().to_rational(); TRACE("arith", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); return alloc(expr_wrapper_proc, m_factory->mk_value(num, m_util.is_int(n->get_owner()))); } template bool theory_diff_logic::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { NOT_IMPLEMENTED_YET(); return true; } template void theory_diff_logic::display(std::ostream & out) const { for (unsigned i = 0; i < m_atoms.size(); ++i) { m_atoms[i]->display(*this, out); } m_graph.display(out); } template bool theory_diff_logic::is_consistent() const { context& ctx = get_context(); for (unsigned i = 0; i < m_atoms.size(); ++i) { atom* a = m_atoms[i]; bool_var bv = a->get_bool_var(); lbool asgn = ctx.get_assignment(bv); if (ctx.is_relevant(ctx.bool_var2expr(bv)) && asgn != l_undef) { SASSERT((asgn == l_true) == a->is_true()); int edge_id = a->get_asserted_edge(); SASSERT(m_graph.is_enabled(edge_id)); SASSERT(m_graph.is_feasible(edge_id)); } } return m_graph.is_feasible(); } template theory_var theory_diff_logic::expand(bool pos, theory_var v, rational & k) { context& ctx = get_context(); enode* e = get_enode(v); rational r; for (;;) { app* n = e->get_owner(); if (m_util.is_add(n) && n->get_num_args() == 2) { app* x = to_app(n->get_arg(0)); app* y = to_app(n->get_arg(1)); if (m_util.is_numeral(x, r)) { e = ctx.get_enode(y); } else if (m_util.is_numeral(y, r)) { e = ctx.get_enode(x); } v = e->get_th_var(get_id()); SASSERT(v != null_theory_var); if (v == null_theory_var) { break; } if (pos) { k += r; } else { k -= r; } } else { break; } } return v; } template void theory_diff_logic::new_eq_or_diseq(bool is_eq, theory_var v1, theory_var v2, justification& eq_just) { rational k; theory_var s = expand(true, v1, k); theory_var t = expand(false, v2, k); context& ctx = get_context(); ast_manager& m = get_manager(); if (s == t) { if (is_eq != k.is_zero()) { // conflict 0 /= k; inc_conflicts(); ctx.set_conflict(&eq_just); } } else { // // Create equality ast, internalize_atom // assign the corresponding equality literal. // app_ref eq(m), s2(m), t2(m); app* s1 = get_enode(s)->get_owner(); app* t1 = get_enode(t)->get_owner(); s2 = m_util.mk_sub(t1, s1); t2 = m_util.mk_numeral(k, m.get_sort(s2.get())); // t1 - s1 = k eq = m.mk_eq(s2.get(), t2.get()); TRACE("diff_logic", tout << v1 << " .. " << v2 << "\n"; tout << mk_pp(eq.get(), m) <<"\n";); if (!internalize_atom(eq.get(), false)) { UNREACHABLE(); } literal l(ctx.get_literal(eq.get())); if (!is_eq) { l = ~l; } ctx.assign(l, b_justification(&eq_just), false); } } template void theory_diff_logic::new_eq_eh( theory_var v1, theory_var v2, justification& j) { m_stats.m_num_core2th_eqs++; new_eq_or_diseq(true, v1, v2, j); } template void theory_diff_logic::new_diseq_eh( theory_var v1, theory_var v2, justification& j) { m_stats.m_num_core2th_diseqs++; new_eq_or_diseq(false, v1, v2, j); } template void theory_diff_logic::new_eq_eh(theory_var v1, theory_var v2) { m_arith_eq_adapter.new_eq_eh(v1, v2); } template void theory_diff_logic::new_diseq_eh(theory_var v1, theory_var v2) { m_arith_eq_adapter.new_diseq_eh(v1, v2); } struct imp_functor { conflict_resolution & m_cr; imp_functor(conflict_resolution& cr) : m_cr(cr) {} void operator()(literal l) { m_cr.mark_literal(l); } }; template void theory_diff_logic::get_eq_antecedents( theory_var v1, theory_var v2, unsigned timestamp, conflict_resolution & cr) { imp_functor functor(cr); VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, timestamp, functor)); VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, timestamp, functor)); } template void theory_diff_logic::get_implied_bound_antecedents(edge_id bridge_edge, edge_id subsumed_edge, conflict_resolution & cr) { imp_functor f(cr); m_graph.explain_subsumed_lazy(bridge_edge, subsumed_edge, f); } template unsigned theory_diff_logic::node2simplex(unsigned v) { return m_objectives.size() + 2*v + 1; } template unsigned theory_diff_logic::edge2simplex(unsigned e) { return m_objectives.size() + 2*e; } template unsigned theory_diff_logic::obj2simplex(unsigned e) { return e; } template unsigned theory_diff_logic::num_simplex_vars() { return m_objectives.size() + std::max(2*m_graph.get_num_edges(),2*m_graph.get_num_nodes()+1); } template bool theory_diff_logic::is_simplex_edge(unsigned e) { if (e < m_objectives.size()) return false; e -= m_objectives.size(); return (0 == (e & 0x1)); } template unsigned theory_diff_logic::simplex2edge(unsigned e) { SASSERT(is_simplex_edge(e)); return (e - m_objectives.size())/2; } template void theory_diff_logic::update_simplex(Simplex& S) { unsigned num_nodes = m_graph.get_num_nodes(); vector > const& es = m_graph.get_all_edges(); S.ensure_var(num_simplex_vars()); for (unsigned i = 0; i < num_nodes; ++i) { numeral const& a = m_graph.get_assignment(i); rational fin = a.get_rational().to_rational(); rational inf = a.get_infinitesimal().to_rational(); mpq_inf q(fin.to_mpq(), inf.to_mpq()); S.set_value(node2simplex(i), q); } S.set_lower(node2simplex(get_zero()), mpq_inf(mpq(0), mpq(0))); S.set_upper(node2simplex(get_zero()), mpq_inf(mpq(0), mpq(0))); svector vars; unsynch_mpq_manager mgr; scoped_mpq_vector coeffs(mgr); coeffs.push_back(mpq(1)); coeffs.push_back(mpq(-1)); coeffs.push_back(mpq(-1)); vars.resize(3); for (unsigned i = m_num_simplex_edges; i < es.size(); ++i) { // t - s <= w // => // t - s - b = 0, b >= w dl_edge const& e = es[i]; unsigned base_var = edge2simplex(i); vars[0] = node2simplex(e.get_target()); vars[1] = node2simplex(e.get_source()); vars[2] = base_var; S.add_row(base_var, 3, vars.c_ptr(), coeffs.c_ptr()); } m_num_simplex_edges = es.size(); for (unsigned i = 0; i < es.size(); ++i) { dl_edge const& e = es[i]; unsigned base_var = edge2simplex(i); if (e.is_enabled()) { numeral const& w = e.get_weight(); rational fin = w.get_rational().to_rational(); rational inf = w.get_infinitesimal().to_rational(); mpq_inf q(fin.to_mpq(),inf.to_mpq()); S.set_upper(base_var, q); } else { S.unset_upper(base_var); } } for (unsigned v = m_objective_rows.size(); v < m_objectives.size(); ++v) { unsigned w = obj2simplex(v); objective_term const& objective = m_objectives[v]; // add objective function as row. coeffs.reset(); vars.reset(); for (unsigned i = 0; i < objective.size(); ++i) { coeffs.push_back(objective[i].second.to_mpq()); vars.push_back(node2simplex(objective[i].first)); } coeffs.push_back(mpq(1)); vars.push_back(w); Simplex::row row = S.add_row(w, vars.size(), vars.c_ptr(), coeffs.c_ptr()); m_objective_rows.push_back(row); } } template typename theory_diff_logic::inf_eps theory_diff_logic::value(theory_var v) { objective_term const& objective = m_objectives[v]; inf_eps r = inf_eps(m_objective_consts[v]); for (unsigned i = 0; i < objective.size(); ++i) { numeral n = m_graph.get_assignment(v); rational r1 = n.get_rational().to_rational(); rational r2 = n.get_infinitesimal().to_rational(); r += objective[i].second * inf_eps(rational(0), inf_rational(r1, r2)); } return r; } template typename theory_diff_logic::inf_eps theory_diff_logic::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { has_shared = false; Simplex& S = m_S; ast_manager& m = get_manager(); update_simplex(S); objective_term const& objective = m_objectives[v]; TRACE("arith", for (unsigned i = 0; i < objective.size(); ++i) { tout << "Coefficient " << objective[i].second << " of theory_var " << objective[i].first << "\n"; } tout << "Free coefficient " << m_objective_consts[v] << "\n"; ); TRACE("opt", S.display(tout); display(tout);); // optimize lbool is_sat = S.make_feasible(); if (is_sat == l_undef) { blocker = m.mk_false(); return inf_eps::infinity(); } TRACE("opt", S.display(tout); ); SASSERT(is_sat != l_false); unsigned w = obj2simplex(v); lbool is_fin = S.minimize(w); switch (is_fin) { case l_true: { simplex::mpq_ext::eps_numeral const& val = S.get_value(w); inf_rational r(-rational(val.first), -rational(val.second)); Simplex::row row = m_objective_rows[v]; TRACE("opt", tout << r << " " << "\n"; S.display_row(tout, row, true);); Simplex::row_iterator it = S.row_begin(row), end = S.row_end(row); expr_ref_vector& core = m_objective_assignments[v]; expr_ref tmp(m); core.reset(); for (; it != end; ++it) { unsigned v = it->m_var; if (is_simplex_edge(v)) { unsigned edge_id = simplex2edge(v); literal lit = m_graph.get_explanation(edge_id); get_context().literal2expr(lit, tmp); core.push_back(tmp); } } compute_delta(); for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) { unsigned w = node2simplex(i); simplex::mpq_ext::eps_numeral const& val = S.get_value(w); rational r = rational(val.first) + m_delta*rational(val.second); m_graph.set_assignment(i, numeral(r)); } blocker = mk_gt(v, r); return inf_eps(rational(0), r + m_objective_consts[v]); } default: TRACE("opt", tout << "unbounded\n"; ); blocker = m.mk_false(); return inf_eps::infinity(); } } template theory_var theory_diff_logic::add_objective(app* term) { objective_term objective; theory_var result = m_objectives.size(); rational q(1), r(0); expr_ref_vector vr(get_manager()); if (!is_linear(get_manager(), term)) { result = null_theory_var; } else if (internalize_objective(term, q, r, objective)) { m_objectives.push_back(objective); m_objective_consts.push_back(r); m_objective_assignments.push_back(vr); } else { result = null_theory_var; } return result; } template expr_ref theory_diff_logic::mk_ineq(theory_var v, inf_rational const& val, bool is_strict) { ast_manager& m = get_manager(); objective_term const& t = m_objectives[v]; expr_ref e(m), f(m), f2(m); if (t.size() == 1 && t[0].second.is_one()) { f = get_enode(t[0].first)->get_owner(); } else if (t.size() == 1 && t[0].second.is_minus_one()) { f = m_util.mk_uminus(get_enode(t[0].first)->get_owner()); } else if (t.size() == 2 && t[0].second.is_one() && t[1].second.is_minus_one()) { f = get_enode(t[0].first)->get_owner(); f2 = get_enode(t[1].first)->get_owner(); f = m_util.mk_sub(f, f2); } else if (t.size() == 2 && t[1].second.is_one() && t[0].second.is_minus_one()) { f = get_enode(t[1].first)->get_owner(); f2 = get_enode(t[0].first)->get_owner(); f = m_util.mk_sub(f, f2); } else { // expr_ref_vector const& core = m_objective_assignments[v]; f = m.mk_and(core.size(), core.c_ptr()); if (is_strict) { f = m.mk_not(f); } TRACE("arith", tout << "block: " << f << "\n";); return f; } inf_rational new_val = val; // - inf_rational(m_objective_consts[v]); e = m_util.mk_numeral(new_val.get_rational(), m.get_sort(f)); if (new_val.get_infinitesimal().is_neg()) { if (is_strict) { f = m_util.mk_ge(f, e); } else { expr_ref_vector const& core = m_objective_assignments[v]; f = m.mk_and(core.size(), core.c_ptr()); } } else { if (is_strict) { f = m_util.mk_gt(f, e); } else { f = m_util.mk_ge(f, e); } } return f; } template expr_ref theory_diff_logic::mk_gt(theory_var v, inf_rational const& val) { return mk_ineq(v, val, true); } template expr_ref theory_diff_logic::mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val) { return mk_ineq(v, val, false); } #if 0 context & ctx = get_context(); model_ref mdl; ctx.get_model(mdl); ptr_vector formulas(ctx.get_num_asserted_formulas(), ctx.get_asserted_formulas()); ast_manager& m = get_manager(); model_implicant impl_extractor(m); expr_ref_vector implicants = impl_extractor.minimize_literals(formulas, mdl); return m.mk_and(o, m.mk_not(m.mk_and(implicants.size(), implicants.c_ptr()))); #endif template bool theory_diff_logic::internalize_objective(expr * n, rational const& m, rational& q, objective_term & objective) { // Compile term into objective_term format rational r; expr* x, *y; if (m_util.is_numeral(n, r)) { q += r; } else if (m_util.is_add(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { if (!internalize_objective(to_app(n)->get_arg(i), m, q, objective)) { return false; } } } else if (m_util.is_mul(n, x, y) && m_util.is_numeral(x, r)) { return internalize_objective(y, m*r, q, objective); } else if (m_util.is_mul(n, y, x) && m_util.is_numeral(x, r)) { return internalize_objective(y, m*r, q, objective); } else if (!is_app(n)) { return false; } else if (to_app(n)->get_family_id() == m_util.get_family_id()) { return false; } else { theory_var v = mk_var(to_app(n)); objective.push_back(std::make_pair(v, m)); } return true; } #endif /* THEORY_DIFF_LOGIC_DEF_H_ */ z3-z3-4.4.1/src/smt/theory_dl.cpp000066400000000000000000000210721260446376700165160ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: theory_dl.h Abstract: Theory for DL constants. DL constants are discrete and ordered by the linear order LT. Constants have a parameter which indicates the numeric value that ranges from 0 up to the size of the domain. The procedure works by a simple reduction to bit-vectors. We enforce an injection into bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2011-1-10 Revision History: --*/ #include "smt_theory.h" #include "dl_decl_plugin.h" #include "value_factory.h" #include "smt_model_generator.h" #include "bv_decl_plugin.h" #include "theory_bv.h" #include "smt_context.h" #include "ast_pp.h" // Basic approach: reduce theory to bit-vectors: // // rep(c(n)) = n // LT(x,y) <=> rep(x) < rep(y) // val(rep(x)) = x // 0 <= rep(x) <= max_value // namespace smt { class dl_factory : public simple_factory { datalog::dl_decl_util& m_util; public: dl_factory(datalog::dl_decl_util& u, proto_model& m): simple_factory(u.get_manager(), u.get_family_id()), m_util(u) {} virtual app * mk_value_core(unsigned const & val, sort * s) { return m_util.mk_numeral(val, s); } }; class theory_dl : public theory { datalog::dl_decl_util m_util; bv_util m_bv; ast_ref_vector m_trail; obj_map m_reps; obj_map m_vals; ast_manager& m() { return get_manager(); } datalog::dl_decl_util& u() { return m_util; } bv_util& b() { return m_bv; } class dl_value_proc : public smt::model_value_proc { theory_dl& m_th; smt::enode* m_node; public: dl_value_proc(theory_dl& th, smt::enode* n) : m_th(th), m_node(n) {} virtual void get_dependencies(buffer & result) {} virtual app * mk_value(smt::model_generator & mg, ptr_vector & ) { smt::context& ctx = m_th.get_context(); app* result = 0; expr* n = m_node->get_owner(); sort* s = m_th.m().get_sort(n); func_decl* r, *v; m_th.get_rep(s, r, v); app_ref rep_of(m_th.m()); rep_of = m_th.m().mk_app(r, m_node->get_owner()); theory_id bv_id = m_th.m().mk_family_id("bv"); theory_bv* th_bv = dynamic_cast(ctx.get_theory(bv_id)); SASSERT(th_bv); rational val; if (ctx.e_internalized(rep_of) && th_bv && th_bv->get_fixed_value(rep_of.get(), val)) { result = m_th.u().mk_numeral(val.get_int64(), s); } else { result = m_th.u().mk_numeral(0, s); } TRACE("theory_dl", tout << mk_pp(result, m_th.m()) << "\n";); return result; } }; public: theory_dl(ast_manager& m): theory(m.mk_family_id("datalog_relation")), m_util(m), m_bv(m), m_trail(m) { } virtual char const * get_name() const { return "datalog"; } virtual bool internalize_atom(app * atom, bool gate_ctx) { TRACE("theory_dl", tout << mk_pp(atom, m()) << "\n";); context& ctx = get_context(); if (ctx.b_internalized(atom)) { return true; } switch(atom->get_decl_kind()) { case datalog::OP_DL_LT: { app* a = to_app(atom->get_arg(0)); app* b = to_app(atom->get_arg(1)); ctx.internalize(a, false); ctx.internalize(b, false); literal l(ctx.mk_bool_var(atom)); ctx.set_var_theory(l.var(), get_id()); mk_lt(a,b); return true; } default: break; } return false; } virtual bool internalize_term(app * term) { TRACE("theory_dl", tout << mk_pp(term, m()) << "\n";); if (u().is_finite_sort(term)) { return mk_rep(term); } else { return false; } } virtual void new_eq_eh(theory_var v1, theory_var v2) { } virtual void new_diseq_eh(theory_var v1, theory_var v2) { } virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_dl, get_manager()); } virtual void init_model(smt::model_generator & m) { m.register_factory(alloc(dl_factory, m_util, m.get_model())); } virtual smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator&) { return alloc(dl_value_proc, *this, n); } virtual void apply_sort_cnstr(enode * n, sort * s) { app* term = n->get_owner(); if (u().is_finite_sort(term)) { mk_rep(term); } } virtual void relevant_eh(app * n) { if (u().is_finite_sort(n)) { sort* s = m().get_sort(n); func_decl* r, *v; get_rep(s, r, v); if (n->get_decl() != v) { expr* rep = m().mk_app(r, n); uint64 vl; if (u().is_numeral_ext(n, vl)) { assert_cnstr(m().mk_eq(rep, mk_bv_constant(vl, s))); } else { assert_cnstr(m().mk_eq(m().mk_app(v,rep), n)); assert_cnstr(b().mk_ule(rep, max_value(s))); } } } } private: void get_rep(sort* s, func_decl*& r, func_decl*& v) { if(!m_reps.find(s, r) || !m_vals.find(s,v)) { SASSERT(!m_reps.contains(s)); sort* bv = b().mk_sort(64); r = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_REP, 0, 0, 1, &s, bv); v = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_ABS, 0, 0, 1, &bv, s); m_reps.insert(s, r); m_vals.insert(s, v); add_trail(r); add_trail(v); get_context().push_trail(insert_obj_map(m_reps, s)); get_context().push_trail(insert_obj_map(m_vals, s)); } } bool mk_rep(app* n) { context & ctx = get_context(); unsigned num_args = n->get_num_args(); enode * e = 0; for (unsigned i = 0; i < num_args; i++) { ctx.internalize(n->get_arg(i), false); } if (ctx.e_internalized(n)) { e = ctx.get_enode(n); } else { e = ctx.mk_enode(n, false, false, true); } if (is_attached_to_var(e)) { return false; } TRACE("theory_dl", tout << mk_pp(n, m()) << "\n";); theory_var var = mk_var(e); ctx.attach_th_var(e, this, var); return true; } app* mk_bv_constant(uint64 val, sort* s) { return b().mk_numeral(rational(val, rational::ui64()), 64); } app* max_value(sort* s) { uint64 sz; VERIFY(u().try_get_size(s, sz)); SASSERT(sz > 0); return mk_bv_constant(sz-1, s); } void mk_lt(app* x, app* y) { sort* s = m().get_sort(x); func_decl* r, *v; get_rep(s, r, v); app* lt1 = u().mk_lt(x,y); app* lt2 = m().mk_not(b().mk_ule(m().mk_app(r,y),m().mk_app(r,x))); assert_cnstr(m().mk_iff(lt1, lt2)); } void assert_cnstr(expr* e) { TRACE("theory_dl", tout << mk_pp(e, m()) << "\n";); context& ctx = get_context(); ctx.internalize(e, false); literal lit(ctx.get_literal(e)); ctx.mark_as_relevant(lit); ctx.mk_th_axiom(get_id(), 1, &lit); } void add_trail(ast* a) { m_trail.push_back(a); get_context().push_trail(push_back_vector(m_trail)); } }; theory* mk_theory_dl(ast_manager& m) { return alloc(theory_dl, m); } }; z3-z3-4.4.1/src/smt/theory_dl.h000066400000000000000000000005331260446376700161620ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: theory_dl.h Abstract: Basic theory solver for DL finite domains. Author: Nikolaj Bjorner (nbjorner) 2011-10-3 Revision History: --*/ #ifndef THEORY_DL_H_ #define THEORY_DL_H_ namespace smt { theory* mk_theory_dl(ast_manager& m); }; #endif /* THEORY_DL_H_ */ z3-z3-4.4.1/src/smt/theory_dummy.cpp000066400000000000000000000026201260446376700172500ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_dummy.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-12-30. Revision History: --*/ #include"smt_context.h" #include"theory_dummy.h" namespace smt { void theory_dummy::found_theory_expr() { if (!m_theory_exprs) { get_context().push_trail(value_trail(m_theory_exprs)); m_theory_exprs = true; } } theory_dummy::theory_dummy(family_id fid, char const * name): theory(fid), m_theory_exprs(false), m_name(name) { } bool theory_dummy::internalize_atom(app * atom, bool gate_ctx) { found_theory_expr(); return false; } bool theory_dummy::internalize_term(app * term) { found_theory_expr(); return false; } void theory_dummy::new_eq_eh(theory_var v1, theory_var v2) { UNREACHABLE(); } bool theory_dummy::use_diseqs() const { return false; } void theory_dummy::new_diseq_eh(theory_var v1, theory_var v2) { UNREACHABLE(); } void theory_dummy::reset_eh() { m_theory_exprs = true; theory::reset_eh(); } final_check_status theory_dummy::final_check_eh() { return m_theory_exprs ? FC_GIVEUP : FC_DONE; } char const * theory_dummy::get_name() const { return m_name; } }; z3-z3-4.4.1/src/smt/theory_dummy.h000066400000000000000000000025541260446376700167230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_no_arith.h Abstract: Author: Leonardo de Moura (leonardo) 2008-12-30. Revision History: --*/ #ifndef THEORY_DUMMY_H_ #define THEORY_DUMMY_H_ #include"smt_theory.h" namespace smt { /** \brief Do nothing theory. Tracks whether theory expressions were internalized. When theory expressions were internalized, it returns FC_GIVEUP in the final_check_eh. */ class theory_dummy : public theory { bool m_theory_exprs; char const * m_name; void found_theory_expr(); protected: virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); virtual void new_eq_eh(theory_var v1, theory_var v2); virtual bool use_diseqs() const; virtual void new_diseq_eh(theory_var v1, theory_var v2); virtual void reset_eh(); virtual final_check_status final_check_eh(); virtual bool build_models() const { return false; } public: theory_dummy(family_id fid, char const * name); virtual ~theory_dummy() {} virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_dummy, get_family_id(), m_name); } virtual char const * get_name() const; }; }; #endif /* THEORY_DUMMY_H_ */ z3-z3-4.4.1/src/smt/theory_fpa.cpp000066400000000000000000000752431260446376700166760ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: theory_fpa.cpp Abstract: Floating-Point Theory Plugin Author: Christoph (cwinter) 2014-04-23 Revision History: --*/ #include"ast_smt2_pp.h" #include"smt_context.h" #include"theory_fpa.h" #include"theory_bv.h" #include"smt_model_generator.h" namespace smt { class fpa2bv_conversion_trail_elem : public trail { ast_manager & m; obj_map & m_conversions; expr * m_e; public: fpa2bv_conversion_trail_elem(ast_manager & m, obj_map & c, expr * e) : m(m), m_conversions(c), m_e(e) {} virtual ~fpa2bv_conversion_trail_elem() {} virtual void undo(theory_fpa & th) { expr * v = m_conversions.find(m_e); m_conversions.remove(m_e); m.dec_ref(v); } }; void theory_fpa::fpa2bv_converter_wrapped::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 * s = f->get_range(); expr_ref bv(m); bv = m_th.wrap(m.mk_const(f)); unsigned bv_sz = m_th.m_bv_util.get_bv_size(bv); unsigned ebits = m_th.m_fpa_util.get_ebits(s); unsigned sbits = m_th.m_fpa_util.get_sbits(s); SASSERT(bv_sz == ebits + sbits); m_th.m_converter.mk_fp(m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, bv), m_bv_util.mk_extract(bv_sz - 2, sbits - 1, bv), m_bv_util.mk_extract(sbits - 2, 0, bv), result); SASSERT(m_th.m_fpa_util.is_float(result)); m_const2bv.insert(f, result); m.inc_ref(f); m.inc_ref(result); } } void theory_fpa::fpa2bv_converter_wrapped::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_th.wrap(m.mk_const(f)); m_rm_const2bv.insert(f, result); m.inc_ref(f); m.inc_ref(result); } } void theory_fpa::fpa2bv_converter_wrapped::mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { fpa2bv_converter::mk_uninterpreted_function(f, num, args, result); } theory_fpa::theory_fpa(ast_manager & m) : theory(m.mk_family_id("fpa")), m_converter(m, this), m_rw(m, m_converter, params_ref()), m_th_rw(m), m_trail_stack(*this), m_fpa_util(m_converter.fu()), m_bv_util(m_converter.bu()), m_arith_util(m_converter.au()) { params_ref p; p.set_bool("arith_lhs", true); m_th_rw.updt_params(p); } theory_fpa::~theory_fpa() { ast_manager & m = get_manager(); dec_ref_map_values(m, m_conversions); dec_ref_map_values(m, m_wraps); dec_ref_map_values(m, m_unwraps); } app * theory_fpa::fpa_value_proc::mk_value(model_generator & mg, ptr_vector & values) { ast_manager & m = m_th.get_manager(); TRACE("t_fpa_detail", for (unsigned i = 0; i < values.size(); i++) tout << "value[" << i << "] = " << mk_ismt2_pp(values[i], m) << std::endl;); mpf_manager & mpfm = m_fu.fm(); unsynch_mpz_manager & mpzm = mpfm.mpz_manager(); app * result; scoped_mpz bias(mpzm); mpzm.power(mpz(2), m_ebits - 1, bias); mpzm.dec(bias); scoped_mpz sgn_z(mpzm), sig_z(mpzm), exp_z(mpzm); unsigned bv_sz; if (values.size() == 1) { SASSERT(m_bu.is_bv(values[0])); SASSERT(m_bu.get_bv_size(values[0]) == (m_ebits + m_sbits)); rational all_r(0); scoped_mpz all_z(mpzm); bool r = m_bu.is_numeral(values[0], all_r, bv_sz); SASSERT(r); SASSERT(bv_sz == (m_ebits + m_sbits)); SASSERT(all_r.is_int()); mpzm.set(all_z, all_r.to_mpq().numerator()); mpzm.machine_div2k(all_z, m_ebits + m_sbits - 1, sgn_z); mpzm.mod(all_z, mpfm.m_powers2(m_ebits + m_sbits - 1), all_z); mpzm.machine_div2k(all_z, m_sbits - 1, exp_z); mpzm.mod(all_z, mpfm.m_powers2(m_sbits - 1), all_z); mpzm.set(sig_z, all_z); } else if (values.size() == 3) { rational sgn_r(0), exp_r(0), sig_r(0); bool r = m_bu.is_numeral(values[0], sgn_r, bv_sz); SASSERT(r && bv_sz == 1); r = m_bu.is_numeral(values[1], exp_r, bv_sz); SASSERT(r && bv_sz == m_ebits); r = m_bu.is_numeral(values[2], sig_r, bv_sz); SASSERT(r && bv_sz == m_sbits - 1); SASSERT(mpzm.is_one(sgn_r.to_mpq().denominator())); SASSERT(mpzm.is_one(exp_r.to_mpq().denominator())); SASSERT(mpzm.is_one(sig_r.to_mpq().denominator())); mpzm.set(sgn_z, sgn_r.to_mpq().numerator()); mpzm.set(exp_z, exp_r.to_mpq().numerator()); mpzm.set(sig_z, sig_r.to_mpq().numerator()); } else UNREACHABLE(); scoped_mpz exp_u = exp_z - bias; SASSERT(mpzm.is_int64(exp_u)); scoped_mpf f(mpfm); mpfm.set(f, m_ebits, m_sbits, mpzm.is_one(sgn_z), sig_z, mpzm.get_int64(exp_u)); result = m_fu.mk_value(f); TRACE("t_fpa", tout << "fpa_value_proc::mk_value [" << mpzm.to_string(sgn_z) << "," << mpzm.to_string(exp_z) << "," << mpzm.to_string(sig_z) << "] --> " << mk_ismt2_pp(result, m_th.get_manager()) << "\n";); return result; } app * theory_fpa::fpa_rm_value_proc::mk_value(model_generator & mg, ptr_vector & values) { SASSERT(values.size() == 1); ast_manager & m = m_th.get_manager(); TRACE("t_fpa_detail", for (unsigned i = 0; i < values.size(); i++) tout << "value[" << i << "] = " << mk_ismt2_pp(values[i], m) << std::endl;); app * result = 0; unsigned bv_sz; rational val(0); bool r = m_bu.is_numeral(values[0], val, bv_sz); SASSERT(r); SASSERT(bv_sz == 3); switch (val.get_uint64()) { case BV_RM_TIES_TO_AWAY: result = m_fu.mk_round_nearest_ties_to_away(); break; case BV_RM_TIES_TO_EVEN: result = m_fu.mk_round_nearest_ties_to_even(); break; case BV_RM_TO_NEGATIVE: result = m_fu.mk_round_toward_negative(); break; case BV_RM_TO_POSITIVE: result = m_fu.mk_round_toward_positive(); break; case BV_RM_TO_ZERO: default: result = m_fu.mk_round_toward_zero(); } TRACE("t_fpa", tout << "fpa_rm_value_proc::mk_value result: " << mk_ismt2_pp(result, m_th.get_manager()) << "\n";); return result; } app_ref theory_fpa::wrap(expr * e) { SASSERT(!m_fpa_util.is_wrap(e)); ast_manager & m = get_manager(); sort * e_srt = m.get_sort(e); func_decl *w; if (!m_wraps.find(e_srt, w)) { SASSERT(!m_wraps.contains(e_srt)); sort * bv_srt; if (m_converter.is_rm(e_srt)) bv_srt = m_bv_util.mk_sort(3); else { SASSERT(m_converter.is_float(e_srt)); unsigned ebits = m_fpa_util.get_ebits(e_srt); unsigned sbits = m_fpa_util.get_sbits(e_srt); bv_srt = m_bv_util.mk_sort(ebits + sbits); } w = m.mk_func_decl(get_family_id(), OP_FPA_INTERNAL_BVWRAP, 0, 0, 1, &e_srt, bv_srt); m_wraps.insert(e_srt, w); m.inc_ref(w); } app_ref res(m); res = m.mk_app(w, e); return res; } app_ref theory_fpa::unwrap(expr * e, sort * s) { SASSERT(!m_fpa_util.is_unwrap(e)); ast_manager & m = get_manager(); sort * bv_srt = m.get_sort(e); func_decl *u; if (!m_unwraps.find(bv_srt, u)) { SASSERT(!m_unwraps.contains(bv_srt)); u = m.mk_func_decl(get_family_id(), OP_FPA_INTERNAL_BVUNWRAP, 0, 0, 1, &bv_srt, s); m_unwraps.insert(bv_srt, u); m.inc_ref(u); } app_ref res(m); res = m.mk_app(u, e); return res; } expr_ref theory_fpa::convert_atom(expr * e) { ast_manager & m = get_manager(); expr_ref res(m); proof_ref pr(m); m_rw(e, res); m_th_rw(res, res); SASSERT(is_app(res)); SASSERT(m.is_bool(res)); return res; } expr_ref theory_fpa::convert_term(expr * e) { SASSERT(m_fpa_util.is_rm(e) || m_fpa_util.is_float(e)); ast_manager & m = get_manager(); expr_ref e_conv(m), res(m); proof_ref pr(m); m_rw(e, e_conv); if (is_app(e_conv) && to_app(e_conv)->get_family_id() != get_family_id()) { m_th_rw(e_conv, res); } else if (m_fpa_util.is_rm(e)) { SASSERT(is_sort_of(m.get_sort(e_conv), m_bv_util.get_family_id(), BV_SORT)); SASSERT(m_bv_util.get_bv_size(e_conv) == 3); m_th_rw(e_conv, res); } else if (m_fpa_util.is_float(e)) { expr_ref sgn(m), sig(m), exp(m); m_converter.split_fp(e_conv, sgn, exp, sig); m_th_rw(sgn); m_th_rw(exp); m_th_rw(sig); m_converter.mk_fp(sgn, exp, sig, res); } else UNREACHABLE(); SASSERT(res.get() != 0); return res; } #if 0 expr_ref theory_fpa::convert_uf(expr * e) { SASSERT(is_app(e)); ast_manager & m = get_manager(); expr_ref res(m); app * a = to_app(e); func_decl * f = a->get_decl(); sort * const * domain = f->get_domain(); unsigned arity = f->get_arity(); expr_ref_buffer new_args(m); expr_ref unwrapped(m); for (unsigned i = 0; i < arity; i++) { expr * ai = a->get_arg(i); if (m_fpa_util.is_float(ai) || m_fpa_util.is_rm(ai)) { if (m_fpa_util.is_unwrap(ai)) unwrapped = ai; else { // unwrapped = unwrap(wrap(ai), domain[i]); // assert_cnstr(m.mk_eq(unwrapped, ai)); // assert_cnstr(); unwrapped = convert_term(ai); } new_args.push_back(unwrapped); TRACE("t_fpa_detail", tout << "UF arg(" << i << ") = " << mk_ismt2_pp(unwrapped, get_manager()) << "\n";); } else new_args.push_back(ai); } sort * rng = f->get_range(); if (m_fpa_util.is_float(rng)) { unsigned sbits = m_fpa_util.get_sbits(rng); unsigned bv_sz = m_fpa_util.get_ebits(rng) + sbits; expr_ref wrapped(m); wrapped = wrap(m.mk_app(f, new_args.size(), new_args.c_ptr())); m_converter.mk_fp(m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, wrapped), m_bv_util.mk_extract(bv_sz - 2, sbits - 1, wrapped), m_bv_util.mk_extract(sbits - 2, 0, wrapped), res); } else res = m.mk_app(f, new_args.size(), new_args.c_ptr()); TRACE("t_fpa_detail", tout << "UF call = " << mk_ismt2_pp(res, get_manager()) << "\n";); return res; } #endif expr_ref theory_fpa::convert_conversion_term(expr * e) { /* This is for the conversion functions fp.to_* */ ast_manager & m = get_manager(); expr_ref res(m); proof_ref pr(m); SASSERT(m_arith_util.is_real(e) || m_bv_util.is_bv(e)); m_rw(e, res); m_th_rw(res, res); return res; } expr_ref theory_fpa::convert_unwrap(expr * e) { SASSERT(m_fpa_util.is_unwrap(e)); ast_manager & m = get_manager(); sort * srt = m.get_sort(e); expr_ref res(m); if (m_fpa_util.is_rm(srt)) { res = to_app(e)->get_arg(0); } else { SASSERT(m_fpa_util.is_float(srt)); unsigned sbits = m_fpa_util.get_sbits(srt); expr_ref bv(m); bv = to_app(e)->get_arg(0); unsigned bv_sz = m_bv_util.get_bv_size(bv); m_converter.mk_fp(m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, bv), m_bv_util.mk_extract(bv_sz - 2, sbits - 1, bv), m_bv_util.mk_extract(sbits - 2, 0, bv), res); } return res; } expr_ref theory_fpa::convert(expr * e) { ast_manager & m = get_manager(); expr_ref res(m); TRACE("t_fpa", tout << "converting " << mk_ismt2_pp(e, m) << std::endl;); if (m_conversions.contains(e)) { res = m_conversions.find(e); TRACE("t_fpa_detail", tout << "cached:" << std::endl; tout << mk_ismt2_pp(e, m) << std::endl << " -> " << std::endl << mk_ismt2_pp(res, m) << std::endl;); return res; } else { if (m_fpa_util.is_unwrap(e)) res = convert_unwrap(e); else if (m.is_bool(e)) res = convert_atom(e); else if (m_fpa_util.is_float(e) || m_fpa_util.is_rm(e)) res = convert_term(e); else if (m_arith_util.is_real(e) || m_bv_util.is_bv(e)) res = convert_conversion_term(e); else UNREACHABLE(); TRACE("t_fpa_detail", tout << "converted; caching:" << std::endl; tout << mk_ismt2_pp(e, m) << std::endl << " -> " << std::endl << mk_ismt2_pp(res, m) << std::endl;); m_conversions.insert(e, res); m.inc_ref(res); m_trail_stack.push(fpa2bv_conversion_trail_elem(m, m_conversions, e)); } return res; } expr_ref theory_fpa::mk_side_conditions() { ast_manager & m = get_manager(); context & ctx = get_context(); simplifier & simp = ctx.get_simplifier(); expr_ref res(m), t(m); proof_ref t_pr(m); res = m.mk_true(); expr_ref_vector::iterator it = m_converter.m_extra_assertions.begin(); expr_ref_vector::iterator end = m_converter.m_extra_assertions.end(); for (; it != end; it++) { simp(*it, t, t_pr); res = m.mk_and(res, t); } m_converter.m_extra_assertions.reset(); m_th_rw(res); CTRACE("t_fpa", !m.is_true(res), tout << "side condition: " << mk_ismt2_pp(res, m) << "\n";); return res; } void theory_fpa::assert_cnstr(expr * e) { if (get_manager().is_true(e)) return; TRACE("t_fpa_detail", tout << "asserting " << mk_ismt2_pp(e, get_manager()) << "\n";); context & ctx = get_context(); ctx.internalize(e, false); literal lit(ctx.get_literal(e)); ctx.mark_as_relevant(lit); ctx.mk_th_axiom(get_id(), 1, &lit); TRACE("t_fpa_detail", tout << "done asserting " << mk_ismt2_pp(e, get_manager()) << "\n";); } void theory_fpa::attach_new_th_var(enode * n) { context & ctx = get_context(); theory_var v = mk_var(n); ctx.attach_th_var(n, this, v); TRACE("t_fpa_detail", tout << "new theory var: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " := " << v << "\n";); } bool theory_fpa::internalize_atom(app * atom, bool gate_ctx) { TRACE("t_fpa", tout << "internalizing atom: " << mk_ismt2_pp(atom, get_manager()) << "\n";); SASSERT(atom->get_family_id() == get_family_id()); ast_manager & m = get_manager(); context & ctx = get_context(); if (ctx.b_internalized(atom)) return true; unsigned num_args = atom->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(atom->get_arg(i), false); literal l(ctx.mk_bool_var(atom)); ctx.set_var_theory(l.var(), get_id()); expr_ref bv_atom(m); bv_atom = convert_atom(atom); SASSERT(is_app(bv_atom) && m.is_bool(bv_atom)); bv_atom = m.mk_and(bv_atom, mk_side_conditions()); assert_cnstr(m.mk_iff(atom, bv_atom)); return true; } bool theory_fpa::internalize_term(app * term) { ast_manager & m = get_manager(); context & ctx = get_context(); TRACE("t_fpa", tout << "internalizing term: " << mk_ismt2_pp(term, get_manager()) << "\n";); SASSERT(term->get_family_id() == get_family_id()); SASSERT(!ctx.e_internalized(term)); unsigned num_args = term->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(term->get_arg(i), false); enode * e = (ctx.e_internalized(term)) ? ctx.get_enode(term) : ctx.mk_enode(term, false, false, true); if (is_attached_to_var(e)) return false; attach_new_th_var(e); // The conversion operators fp.to_* appear in non-FP constraints. // The corresponding constraints will not be translated and added // via convert(...) and assert_cnstr(...) in initialize_atom(...). // Therefore, we translate and assert them here. fpa_op_kind k = (fpa_op_kind)term->get_decl_kind(); switch (k) { case OP_FPA_TO_UBV: case OP_FPA_TO_SBV: case OP_FPA_TO_REAL: case OP_FPA_TO_IEEE_BV: { expr_ref conv(m); conv = convert(term); assert_cnstr(m.mk_eq(term, conv)); assert_cnstr(mk_side_conditions()); break; } default: /* ignore */; } return true; } void theory_fpa::apply_sort_cnstr(enode * n, sort * s) { TRACE("t_fpa", tout << "apply sort cnstr for: " << mk_ismt2_pp(n->get_owner(), get_manager()) << "\n";); SASSERT(s->get_family_id() == get_family_id()); ast_manager & m = get_manager(); context & ctx = get_context(); app_ref owner(m); owner = n->get_owner(); SASSERT(owner->get_decl()->get_range() == s); if ((m_fpa_util.is_float(s) || m_fpa_util.is_rm(s)) && !is_attached_to_var(n)) { attach_new_th_var(n); if (m_fpa_util.is_rm(s)) { // For every RM term, we need to make sure that it's // associated bit-vector is within the valid range. if (!m_fpa_util.is_unwrap(owner)) { expr_ref valid(m), limit(m); limit = m_bv_util.mk_numeral(4, 3); valid = m_bv_util.mk_ule(wrap(owner), limit); assert_cnstr(valid); } } if (!ctx.relevancy() && !m_fpa_util.is_unwrap(owner)) assert_cnstr(m.mk_eq(unwrap(wrap(owner), s), owner)); } } void theory_fpa::new_eq_eh(theory_var x, theory_var y) { ast_manager & m = get_manager(); enode * e_x = get_enode(x); enode * e_y = get_enode(y); TRACE("t_fpa", tout << "new eq: " << x << " = " << y << std::endl;); TRACE("t_fpa_detail", tout << mk_ismt2_pp(e_x->get_owner(), m) << " = " << mk_ismt2_pp(e_y->get_owner(), m) << std::endl;); fpa_util & fu = m_fpa_util; expr_ref xe(m), ye(m); xe = e_x->get_owner(); ye = e_y->get_owner(); if (m_fpa_util.is_wrap(xe) || m_fpa_util.is_wrap(ye)) return; expr_ref xc(m), yc(m); xc = convert(xe); yc = convert(ye); TRACE("t_fpa_detail", tout << "xc = " << mk_ismt2_pp(xc, m) << std::endl << "yc = " << mk_ismt2_pp(yc, m) << std::endl;); expr_ref c(m); if (fu.is_float(xe) && fu.is_float(ye)) m_converter.mk_eq(xc, yc, c); else if (fu.is_rm(xe) && fu.is_rm(ye)) c = m.mk_eq(xc, yc); else UNREACHABLE(); m_th_rw(c); assert_cnstr(m.mk_iff(m.mk_eq(xe, ye), c)); assert_cnstr(mk_side_conditions()); return; } void theory_fpa::new_diseq_eh(theory_var x, theory_var y) { ast_manager & m = get_manager(); enode * e_x = get_enode(x); enode * e_y = get_enode(y); TRACE("t_fpa", tout << "new diseq: " << x << " != " << y << std::endl;); TRACE("t_fpa_detail", tout << mk_ismt2_pp(get_enode(x)->get_owner(), m) << " != " << mk_ismt2_pp(get_enode(y)->get_owner(), m) << std::endl;); fpa_util & fu = m_fpa_util; expr_ref xe(m), ye(m); xe = e_x->get_owner(); ye = e_y->get_owner(); if (m_fpa_util.is_wrap(xe) || m_fpa_util.is_wrap(ye)) return; expr_ref xc(m), yc(m); xc = convert(xe); yc = convert(ye); expr_ref c(m); if (fu.is_float(xe) && fu.is_float(ye)) { m_converter.mk_eq(xc, yc, c); c = m.mk_not(c); } else if (fu.is_rm(xe) && fu.is_rm(ye)) c = m.mk_not(m.mk_eq(xc, yc)); else UNREACHABLE(); m_th_rw(c); assert_cnstr(m.mk_iff(m.mk_not(m.mk_eq(xe, ye)), c)); assert_cnstr(mk_side_conditions()); return; } void theory_fpa::push_scope_eh() { theory::push_scope_eh(); m_trail_stack.push_scope(); } void theory_fpa::pop_scope_eh(unsigned num_scopes) { m_trail_stack.pop_scope(num_scopes); TRACE("t_fpa", tout << "pop " << num_scopes << "; now " << m_trail_stack.get_num_scopes() << "\n";); // unsigned num_old_vars = get_old_num_vars(num_scopes); theory::pop_scope_eh(num_scopes); } void theory_fpa::assign_eh(bool_var v, bool is_true) { ast_manager & m = get_manager(); context & ctx = get_context(); expr * e = ctx.bool_var2expr(v); TRACE("t_fpa", tout << "assign_eh for: " << v << " (" << is_true << "):\n" << mk_ismt2_pp(e, m) << "\n";); expr_ref converted(m); converted = m.mk_and(convert(e), mk_side_conditions()); if (is_true) assert_cnstr(m.mk_implies(e, converted)); else assert_cnstr(m.mk_implies(m.mk_not(e), m.mk_not(converted))); } void theory_fpa::relevant_eh(app * n) { ast_manager & m = get_manager(); TRACE("t_fpa", tout << "relevant_eh for: " << mk_ismt2_pp(n, m) << "\n";); mpf_manager & mpfm = m_fpa_util.fm(); if (m_fpa_util.is_float(n) || m_fpa_util.is_rm(n)) { if (!m_fpa_util.is_unwrap(n)) { expr_ref wrapped(m), c(m); wrapped = wrap(n); mpf_rounding_mode rm; scoped_mpf val(mpfm); if (m_fpa_util.is_rm_numeral(n, rm)) { c = m.mk_eq(wrapped, m_bv_util.mk_numeral(rm, 3)); assert_cnstr(c); } else if (m_fpa_util.is_numeral(n, val)) { expr_ref bv_val_e(m); bv_val_e = convert(n); SASSERT(is_app(bv_val_e)); SASSERT(to_app(bv_val_e)->get_num_args() == 3); app_ref bv_val_a(to_app(bv_val_e.get()), m); expr * args[] = { bv_val_a->get_arg(0), bv_val_a->get_arg(1), bv_val_a->get_arg(2) }; c = m.mk_eq(wrapped, m_bv_util.mk_concat(3, args)); c = m.mk_and(c, mk_side_conditions()); assert_cnstr(c); } else { c = m.mk_eq(unwrap(wrapped, m.get_sort(n)), n); assert_cnstr(c); } } } else if (n->get_family_id() == get_family_id()) { // These are the conversion functions fp.to_* */ SASSERT(!m_fpa_util.is_float(n) && !m_fpa_util.is_rm(n)); } else UNREACHABLE(); } void theory_fpa::reset_eh() { TRACE("t_fpa", tout << "reset_eh\n";); pop_scope_eh(m_trail_stack.get_num_scopes()); m_converter.reset(); m_rw.reset(); m_th_rw.reset(); m_trail_stack.pop_scope(m_trail_stack.get_num_scopes()); if (m_factory) dealloc(m_factory); m_factory = 0; ast_manager & m = get_manager(); dec_ref_map_values(m, m_conversions); dec_ref_map_values(m, m_wraps); dec_ref_map_values(m, m_unwraps); theory::reset_eh(); } final_check_status theory_fpa::final_check_eh() { TRACE("t_fpa", tout << "final_check_eh\n";); SASSERT(m_converter.m_extra_assertions.empty()); return FC_DONE; } void theory_fpa::init_model(model_generator & mg) { TRACE("t_fpa", tout << "initializing model" << std::endl; display(tout);); m_factory = alloc(fpa_value_factory, get_manager(), get_family_id()); mg.register_factory(m_factory); } model_value_proc * theory_fpa::mk_value(enode * n, model_generator & mg) { TRACE("t_fpa", tout << "mk_value for: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " (sort " << mk_ismt2_pp(get_manager().get_sort(n->get_owner()), get_manager()) << ")\n";); ast_manager & m = get_manager(); context & ctx = get_context(); app_ref owner(m); owner = n->get_owner(); // If the owner is not internalized, it doesn't have an enode associated. SASSERT(ctx.e_internalized(owner)); if (m_fpa_util.is_rm_numeral(owner) || m_fpa_util.is_numeral(owner)) { return alloc(expr_wrapper_proc, owner); } model_value_proc * res = 0; app_ref wrapped(m); wrapped = wrap(owner); SASSERT(m_bv_util.is_bv(wrapped)); CTRACE("t_fpa_detail", !ctx.e_internalized(wrapped), tout << "Model dependency not internalized: " << mk_ismt2_pp(wrapped, m) << " (owner " << (!ctx.e_internalized(owner) ? "not" : "is") << " internalized)" << std::endl;); if (is_app_of(owner, get_family_id(), OP_FPA_FP)) { SASSERT(to_app(owner)->get_num_args() == 3); app_ref a0(m), a1(m), a2(m); a0 = to_app(owner->get_arg(0)); a1 = to_app(owner->get_arg(1)); a2 = to_app(owner->get_arg(2)); unsigned ebits = m_fpa_util.get_ebits(m.get_sort(owner)); unsigned sbits = m_fpa_util.get_sbits(m.get_sort(owner)); fpa_value_proc * vp = alloc(fpa_value_proc, this, ebits, sbits); vp->add_dependency(ctx.get_enode(a0)); vp->add_dependency(ctx.get_enode(a1)); vp->add_dependency(ctx.get_enode(a2)); TRACE("t_fpa_detail", tout << "Depends on: " << mk_ismt2_pp(a0, m) << " eq. cls. #" << get_enode(a0)->get_root()->get_owner()->get_id() << std::endl << mk_ismt2_pp(a1, m) << " eq. cls. #" << get_enode(a1)->get_root()->get_owner()->get_id() << std::endl << mk_ismt2_pp(a2, m) << " eq. cls. #" << get_enode(a2)->get_root()->get_owner()->get_id() << std::endl;); res = vp; } else if (ctx.e_internalized(wrapped)) { if (m_fpa_util.is_rm(owner)) { fpa_rm_value_proc * vp = alloc(fpa_rm_value_proc, this); vp->add_dependency(ctx.get_enode(wrapped)); res = vp; } else if (m_fpa_util.is_float(owner)) { unsigned ebits = m_fpa_util.get_ebits(m.get_sort(owner)); unsigned sbits = m_fpa_util.get_sbits(m.get_sort(owner)); fpa_value_proc * vp = alloc(fpa_value_proc, this, ebits, sbits); enode * en = ctx.get_enode(wrapped); vp->add_dependency(en); TRACE("t_fpa_detail", tout << "Depends on: " << mk_ismt2_pp(wrapped, m) << " eq. cls. #" << en->get_root()->get_owner()->get_id() << std::endl;); res = vp; } } else { unsigned ebits = m_fpa_util.get_ebits(m.get_sort(owner)); unsigned sbits = m_fpa_util.get_sbits(m.get_sort(owner)); return alloc(expr_wrapper_proc, m_fpa_util.mk_pzero(ebits, sbits)); } SASSERT(res != 0); return res; } void theory_fpa::finalize_model(model_generator & mg) {} void theory_fpa::display(std::ostream & out) const { ast_manager & m = get_manager(); context & ctx = get_context(); out << "fpa theory variables:" << std::endl; ptr_vector::const_iterator it = ctx.begin_enodes(); ptr_vector::const_iterator end = ctx.end_enodes(); for (; it != end; it++) { theory_var v = (*it)->get_th_var(get_family_id()); if (v != -1) out << v << " -> " << mk_ismt2_pp((*it)->get_owner(), m) << std::endl; } out << "bv theory variables:" << std::endl; it = ctx.begin_enodes(); end = ctx.end_enodes(); for (; it != end; it++) { theory_var v = (*it)->get_th_var(m_bv_util.get_family_id()); if (v != -1) out << v << " -> " << mk_ismt2_pp((*it)->get_owner(), m) << std::endl; } out << "arith theory variables:" << std::endl; it = ctx.begin_enodes(); end = ctx.end_enodes(); for (; it != end; it++) { theory_var v = (*it)->get_th_var(m_arith_util.get_family_id()); if (v != -1) out << v << " -> " << mk_ismt2_pp((*it)->get_owner(), m) << std::endl; } out << "equivalence classes:\n"; it = ctx.begin_enodes(); end = ctx.end_enodes(); for (; it != end; ++it) { expr * n = (*it)->get_owner(); expr * r = (*it)->get_root()->get_owner(); out << r->get_id() << " --> " << mk_ismt2_pp(n, m) << std::endl; } } }; z3-z3-4.4.1/src/smt/theory_fpa.h000066400000000000000000000143051260446376700163330ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: theory_fpa.h Abstract: Floating-Point Theory Plugin Author: Christoph (cwinter) 2014-04-23 Revision History: --*/ #ifndef THEORY_FPA_H_ #define THEORY_FPA_H_ #include"smt_theory.h" #include"trail.h" #include"fpa2bv_converter.h" #include"fpa2bv_rewriter.h" #include"th_rewriter.h" #include"value_factory.h" #include"smt_model_generator.h" namespace smt { class fpa_value_factory : public value_factory { fpa_util m_util; virtual app * mk_value_core(mpf const & val, sort * s) { SASSERT(m_util.get_ebits(s) == val.get_ebits()); SASSERT(m_util.get_sbits(s) == val.get_sbits()); return m_util.mk_value(val); } public: fpa_value_factory(ast_manager & m, family_id fid) : value_factory(m, fid), m_util(m) {} virtual ~fpa_value_factory() {} virtual expr * get_some_value(sort * s) { mpf_manager & mpfm = m_util.fm(); scoped_mpf q(mpfm); mpfm.set(q, m_util.get_ebits(s), m_util.get_sbits(s), 0); return m_util.mk_value(q); } virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { mpf_manager & mpfm = m_util.fm(); scoped_mpf q(mpfm); mpfm.set(q, m_util.get_ebits(s), m_util.get_sbits(s), 0); v1 = m_util.mk_value(q); mpfm.set(q, m_util.get_ebits(s), m_util.get_sbits(s), 1); v2 = m_util.mk_value(q); return true; } virtual expr * get_fresh_value(sort * s) { NOT_IMPLEMENTED_YET(); } virtual void register_value(expr * n) { /* Ignore */ } app * mk_value(mpf const & x) { return m_util.mk_value(x); } }; class theory_fpa : public theory { protected: typedef trail_stack th_trail_stack; class fpa2bv_converter_wrapped : public fpa2bv_converter { public: theory_fpa & m_th; fpa2bv_converter_wrapped(ast_manager & m, theory_fpa * th) : fpa2bv_converter(m), m_th(*th) {} virtual ~fpa2bv_converter_wrapped() {} virtual void mk_const(func_decl * f, expr_ref & result); virtual void mk_rm_const(func_decl * f, expr_ref & result); virtual void mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result); }; class fpa_value_proc : public model_value_proc { protected: theory_fpa & m_th; ast_manager & m; fpa_util & m_fu; bv_util & m_bu; buffer m_deps; unsigned m_ebits; unsigned m_sbits; public: fpa_value_proc(theory_fpa * th, unsigned ebits, unsigned sbits) : m_th(*th), m(th->get_manager()), m_fu(th->m_fpa_util), m_bu(th->m_bv_util), m_ebits(ebits), m_sbits(sbits) {} virtual ~fpa_value_proc() {} void add_dependency(enode * e) { m_deps.push_back(model_value_dependency(e)); } virtual void get_dependencies(buffer & result) { result.append(m_deps); } virtual app * mk_value(model_generator & mg, ptr_vector & values); }; class fpa_rm_value_proc : public model_value_proc { theory_fpa & m_th; ast_manager & m; fpa_util & m_fu; bv_util & m_bu; buffer m_deps; public: fpa_rm_value_proc(theory_fpa * th) : m_th(*th), m(th->get_manager()), m_fu(th->m_fpa_util), m_bu(th->m_bv_util) {} void add_dependency(enode * e) { m_deps.push_back(model_value_dependency(e)); } virtual void get_dependencies(buffer & result) { result.append(m_deps); } virtual ~fpa_rm_value_proc() {} virtual app * mk_value(model_generator & mg, ptr_vector & values); }; protected: fpa2bv_converter_wrapped m_converter; fpa2bv_rewriter m_rw; th_rewriter m_th_rw; th_trail_stack m_trail_stack; fpa_value_factory * m_factory; fpa_util & m_fpa_util; bv_util & m_bv_util; arith_util & m_arith_util; obj_map m_wraps; obj_map m_unwraps; obj_map m_conversions; virtual final_check_status final_check_eh(); virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); virtual void apply_sort_cnstr(enode * n, sort * s); virtual void new_eq_eh(theory_var, theory_var); virtual void new_diseq_eh(theory_var, theory_var); virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual void reset_eh(); virtual theory* mk_fresh(context*) { return alloc(theory_fpa, get_manager()); } virtual char const * get_name() const { return "fpa"; } virtual model_value_proc * mk_value(enode * n, model_generator & mg); void assign_eh(bool_var v, bool is_true); virtual void relevant_eh(app * n); virtual void init_model(model_generator & m); virtual void finalize_model(model_generator & mg); public: theory_fpa(ast_manager& m); virtual ~theory_fpa(); virtual void display(std::ostream & out) const; protected: expr_ref mk_side_conditions(); expr_ref convert(expr * e); expr_ref convert_atom(expr * e); expr_ref convert_term(expr * e); expr_ref convert_conversion_term(expr * e); expr_ref convert_unwrap(expr * e); void add_trail(ast * a); void attach_new_th_var(enode * n); void assert_cnstr(expr * e); app_ref wrap(expr * e); app_ref unwrap(expr * e, sort * s); }; }; #endif /* THEORY_FPA_H_ */ z3-z3-4.4.1/src/smt/theory_opt.cpp000066400000000000000000000035111260446376700167170ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_opt.cpp Abstract: Interface utilities used by optimization providing theory solvers. Author: Nikolaj Bjorner (nbjorner) 2013-10-18 Notes: --*/ #include "arith_decl_plugin.h" #include "smt_types.h" #include "smt_theory.h" #include "theory_opt.h" namespace smt { bool theory_opt::is_linear(ast_manager& m, expr* term) { arith_util a(m); ptr_vector todo; ast_mark mark; todo.push_back(term); expr* t1, *t2; while (!todo.empty()) { term = todo.back(); todo.pop_back(); if (mark.is_marked(term)) { continue; } mark.mark(term, true); if (!is_app(term)) { return false; } app* t = to_app(term); if (t->get_family_id() != a.get_family_id()) { // done } else if (a.is_add(t) || a.is_to_real(t) || a.is_to_int(t) || a.is_uminus(t) || a.is_numeral(t) || a.is_sub(t)) { todo.append(t->get_num_args(), t->get_args()); } else if (a.is_mul(t, t1, t2)) { if (is_numeral(a, t1)) { todo.push_back(t2); } else if (is_numeral(a, t2)) { todo.push_back(t1); } else { return false; } } else { return false; } } return true; } bool theory_opt::is_numeral(arith_util& a, expr* term) { while (a.is_uminus(term) || a.is_to_real(term) || a.is_to_int(term)) { term = to_app(term)->get_arg(0); } return a.is_numeral(term); } }; z3-z3-4.4.1/src/smt/theory_opt.h000066400000000000000000000017071260446376700163710ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_opt.h Abstract: Interface utilities used by optimization providing theory solvers. Author: Nikolaj Bjorner (nbjorner) 2013-10-18 Notes: --*/ #include "inf_rational.h" #include "inf_eps_rational.h" #include "arith_decl_plugin.h" #ifndef THEORY_OPT_H_ #define THEORY_OPT_H_ class filter_model_converter; namespace smt { class theory_opt { public: typedef inf_eps_rational inf_eps; virtual inf_eps value(theory_var) = 0; virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) = 0; virtual theory_var add_objective(app* term) = 0; virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val) { UNREACHABLE(); return expr_ref(*((ast_manager*)0)); } bool is_linear(ast_manager& m, expr* term); bool is_numeral(arith_util& a, expr* term); }; } #endif z3-z3-4.4.1/src/smt/theory_pb.cpp000066400000000000000000002164231260446376700165260ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_pb.cpp Abstract: Pseudo-Boolean theory plugin. Author: Nikolaj Bjorner (nbjorner) 2013-11-05 Notes: --*/ #include #include "theory_pb.h" #include "smt_context.h" #include "ast_pp.h" #include "sorting_network.h" #include "uint_set.h" #include "smt_model_generator.h" #include "pb_rewriter_def.h" #include "sparse_matrix_def.h" #include "simplex_def.h" namespace smt { class pb_lit_rewriter_util { public: typedef std::pair arg_t; typedef vector args_t; typedef rational numeral; literal negate(literal l) { return ~l; } void display(std::ostream& out, literal l) { out << l; } bool is_negated(literal l) const { return l.sign(); } bool is_true(literal l) const { return l == true_literal; } bool is_false(literal l) const { return l == false_literal; } struct compare { bool operator()(arg_t const& a, arg_t const& b) { return a.first < b.first; } }; }; const unsigned theory_pb::null_index = UINT_MAX; unsigned theory_pb::arg_t::get_hash() const { return get_composite_hash(*this, size()); } bool theory_pb::arg_t::operator==(arg_t const& other) const { if (size() != other.size()) return false; for (unsigned i = 0; i < size(); ++i) { if (lit(i) != other.lit(i)) return false; if (coeff(i) != other.coeff(i)) return false; } return true; } void theory_pb::arg_t::remove_negations() { for (unsigned i = 0; i < size(); ++i) { if (lit(i).sign()) { (*this)[i].first.neg(); (*this)[i].second.neg(); m_k += coeff(i); } } } void theory_pb::arg_t::negate() { numeral sum(0); for (unsigned i = 0; i < size(); ++i) { (*this)[i].first.neg(); sum += coeff(i); } m_k = sum - m_k + numeral::one(); VERIFY(l_undef == normalize(false)); } lbool theory_pb::arg_t::normalize(bool is_eq) { pb_lit_rewriter_util pbu; pb_rewriter_util util(pbu); return util.normalize(*this, m_k, is_eq); } void theory_pb::arg_t::prune(bool is_eq) { pb_lit_rewriter_util pbu; pb_rewriter_util util(pbu); util.prune(*this, m_k, is_eq); } std::ostream& theory_pb::arg_t::display(context& ctx, std::ostream& out, bool values) const { for (unsigned i = 0; i < size(); ++i) { literal l(lit(i)); if (!coeff(i).is_one()) { out << coeff(i) << "*"; } out << l; if (values) { out << "@(" << ctx.get_assignment(l); if (ctx.get_assignment(l) != l_undef) { out << ":" << ctx.get_assign_level(l); } out << ")"; } if (i + 1 < size()) { out << " + "; } } out << " ~ " << k() << "\n"; return out; } app_ref theory_pb::arg_t::to_expr(bool is_eq, context& ctx, ast_manager& m) { expr_ref tmp(m); app_ref result(m); svector coeffs; expr_ref_vector args(m); for (unsigned i = 0; i < size(); ++i) { ctx.literal2expr(lit(i), tmp); args.push_back(tmp); coeffs.push_back(coeff(i)); } pb_util pb(m); if (is_eq) { result = pb.mk_eq(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k()); } else { result = pb.mk_ge(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k()); } return result; } void theory_pb::ineq::reset() { m_max_watch.reset(); m_watch_sz = 0; m_watch_sum.reset(); m_num_propagations = 0; m_compilation_threshold = UINT_MAX; m_compiled = l_false; m_args[0].reset(); m_args[0].m_k.reset(); m_args[1].reset(); m_args[1].m_k.reset(); m_nfixed = 0; m_max_sum.reset(); m_min_sum.reset(); } void theory_pb::ineq::unique() { pb_lit_rewriter_util pbu; pb_rewriter_util util(pbu); util.unique(m_args[0], m_args[0].m_k, m_is_eq); } void theory_pb::ineq::post_prune() { if (!m_args[0].empty() && is_ge()) { m_args[0].negate(); m_args[0].negate(); m_args[1].reset(); m_args[1].m_k = m_args[0].m_k; m_args[1].append(m_args[0]); m_args[1].negate(); SASSERT(m_args[0].size() == m_args[1].size()); SASSERT(m_args[0].well_formed()); SASSERT(m_args[1].well_formed()); } } void theory_pb::ineq::negate() { SASSERT(!m_is_eq); m_lit.neg(); } void theory_pb::ineq::prune() { m_args[0].prune(m_is_eq); } lbool theory_pb::ineq::normalize() { return m_args[0].normalize(m_is_eq); } app_ref theory_pb::ineq::to_expr(context& ctx, ast_manager& m) { return args().to_expr(m_is_eq, ctx, m); } bool theory_pb::arg_t::well_formed() const { SASSERT(k().is_pos()); uint_set vars; numeral sum = numeral::zero(); for (unsigned i = 0; i < size(); ++i) { SASSERT(coeff(i) <= k()); SASSERT(numeral::one() <= coeff(i)); SASSERT(lit(i) != true_literal); SASSERT(lit(i) != false_literal); SASSERT(lit(i) != null_literal); SASSERT(!vars.contains(lit(i).var())); vars.insert(lit(i).var()); sum += coeff(i); } SASSERT(sum >= k()); return true; } theory_pb::theory_pb(ast_manager& m, theory_pb_params& p): theory(m.mk_family_id("pb")), m_params(p), m_util(m), m_max_compiled_coeff(rational(8)) { m_learn_complements = p.m_pb_learn_complements; m_conflict_frequency = p.m_pb_conflict_frequency; m_enable_compilation = p.m_pb_enable_compilation; m_enable_simplex = p.m_pb_enable_simplex; } theory_pb::~theory_pb() { reset_eh(); } theory * theory_pb::mk_fresh(context * new_ctx) { return alloc(theory_pb, new_ctx->get_manager(), m_params); } class theory_pb::remove_var : public trail { theory_pb& pb; unsigned v; public: remove_var(theory_pb& pb, unsigned v): pb(pb), v(v) {} virtual void undo(context& ctx) { pb.m_vars.remove(v); pb.m_simplex.unset_lower(v); pb.m_simplex.unset_upper(v); } }; class theory_pb::undo_bound : public trail { theory_pb& pb; unsigned m_v; bool m_is_lower; scoped_eps_numeral m_last_bound; bool m_last_bound_valid; literal m_last_explain; public: undo_bound(theory_pb& pb, unsigned v, bool is_lower, scoped_eps_numeral& last_bound, bool last_bound_valid, literal last_explain): pb(pb), m_v(v), m_is_lower(is_lower), m_last_bound(last_bound), m_last_bound_valid(last_bound_valid), m_last_explain(last_explain) {} virtual void undo(context& ctx) { if (m_is_lower) { if (m_last_bound_valid) { pb.m_simplex.set_lower(m_v, m_last_bound); } else { pb.m_simplex.unset_lower(m_v); } pb.set_explain(pb.m_explain_lower, m_v, m_last_explain); } else { if (m_last_bound_valid) { pb.m_simplex.set_upper(m_v, m_last_bound); } else { pb.m_simplex.unset_upper(m_v); } pb.set_explain(pb.m_explain_upper, m_v, m_last_explain); } m_last_bound.reset(); } }; literal theory_pb::set_explain(literal_vector& explains, unsigned var, literal expl) { if (var >= explains.size()) { explains.resize(var+1, null_literal); } literal last_explain = explains[var]; explains[var] = expl; return last_explain; } bool theory_pb::update_bound(bool_var v, literal explain, bool is_lower, mpq_inf const& bound) { if (is_lower) { if (m_simplex.above_lower(v, bound)) { scoped_eps_numeral last_bound(m_mpq_inf_mgr); if (m_simplex.upper_valid(v)) { m_simplex.get_upper(v, last_bound); if (m_mpq_inf_mgr.gt(bound, last_bound)) { literal lit = m_explain_upper.get(v, null_literal); get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain)); return false; } } bool last_bound_valid = m_simplex.lower_valid(v); if (last_bound_valid) { m_simplex.get_lower(v, last_bound); } m_simplex.set_lower(v, bound); literal last_explain = set_explain(m_explain_lower, v, explain); get_context().push_trail(undo_bound(*this, v, true, last_bound, last_bound_valid, last_explain)); } } else { if (m_simplex.below_upper(v, bound)) { scoped_eps_numeral last_bound(m_mpq_inf_mgr); if (m_simplex.lower_valid(v)) { m_simplex.get_lower(v, last_bound); if (m_mpq_inf_mgr.gt(last_bound, bound)) { literal lit = m_explain_lower.get(v, null_literal); get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain)); return false; } } bool last_bound_valid = m_simplex.upper_valid(v); if (last_bound_valid) { m_simplex.get_upper(v, last_bound); } m_simplex.set_upper(v, bound); literal last_explain = set_explain(m_explain_upper, v, explain); get_context().push_trail(undo_bound(*this, v, false, last_bound, last_bound_valid, last_explain)); } } return true; }; bool theory_pb::check_feasible() { context& ctx = get_context(); lbool is_sat = m_simplex.make_feasible(); if (l_false != is_sat) { return true; } row r = m_simplex.get_infeasible_row(); // m_simplex.display_row(std::cout, r, true); mpz const& coeff = m_simplex.get_base_coeff(r); bool_var base_var = m_simplex.get_base_var(r); SASSERT(m_simplex.below_lower(base_var) || m_simplex.above_upper(base_var)); bool cant_increase = m_simplex.below_lower(base_var)?m_mpz_mgr.is_pos(coeff):m_mpz_mgr.is_neg(coeff); literal_vector explains; row_iterator it = m_simplex.row_begin(r), end = m_simplex.row_end(r); for (; it != end; ++it) { bool_var v = it->m_var; if (v == base_var) { if (m_simplex.below_lower(base_var)) { explains.push_back(m_explain_lower.get(v, null_literal)); } else { explains.push_back(m_explain_upper.get(v, null_literal)); } } else if (cant_increase == m_mpz_mgr.is_pos(it->m_coeff)) { explains.push_back(m_explain_lower.get(v, null_literal)); } else { explains.push_back(m_explain_upper.get(v, null_literal)); } } literal_vector lits; for (unsigned i = 0; i < explains.size(); ++i) { literal lit(explains[i]); if (lit != null_literal) { lits.push_back(~lit); } } m_stats.m_num_conflicts++; justification* js = 0; if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); } ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); return false; } bool theory_pb::internalize_atom(app * atom, bool gate_ctx) { context& ctx = get_context(); if (ctx.b_internalized(atom)) { return false; } SASSERT(!ctx.b_internalized(atom)); m_stats.m_num_predicates++; if (m_util.is_aux_bool(atom)) { bool_var abv = ctx.mk_bool_var(atom); ctx.set_var_theory(abv, get_id()); return true; } SASSERT(m_util.is_at_most_k(atom) || m_util.is_le(atom) || m_util.is_ge(atom) || m_util.is_at_least_k(atom) || m_util.is_eq(atom)); unsigned num_args = atom->get_num_args(); bool_var abv = ctx.mk_bool_var(atom); ctx.set_var_theory(abv, get_id()); ineq* c = alloc(ineq, m_mpz_mgr, literal(abv), m_util.is_eq(atom)); c->m_args[0].m_k = m_util.get_k(atom); numeral& k = c->m_args[0].m_k; arg_t& args = c->m_args[0]; // extract literals and coefficients. for (unsigned i = 0; i < num_args; ++i) { expr* arg = atom->get_arg(i); literal l = compile_arg(arg); numeral c = m_util.get_coeff(atom, i); args.push_back(std::make_pair(l, c)); } if (m_util.is_at_most_k(atom) || m_util.is_le(atom)) { // turn W <= k into -W >= -k for (unsigned i = 0; i < args.size(); ++i) { args[i].second = -args[i].second; } k = -k; } else { SASSERT(m_util.is_at_least_k(atom) || m_util.is_ge(atom) || m_util.is_eq(atom)); } TRACE("pb", display(tout, *c);); //app_ref fml1(m), fml2(m); //fml1 = c->to_expr(ctx, m); c->unique(); lbool is_true = c->normalize(); c->prune(); c->post_prune(); //fml2 = c->to_expr(ctx, m); //expr_ref validate_pb = pb_rewriter(m).mk_validate_rewrite(fml1, fml2); //pb_rewriter(m).dump_pb_rewrite(validate_pb); literal lit(abv); TRACE("pb", display(tout, *c); tout << " := " << lit << "\n";); switch(is_true) { case l_false: lit = ~lit; // fall-through case l_true: ctx.mk_th_axiom(get_id(), 1, &lit); dealloc(c); return true; case l_undef: break; } if (c->k().is_one() && c->is_ge() && !m_enable_simplex) { literal_vector& lits = get_lits(); lits.push_back(~lit); for (unsigned i = 0; i < c->size(); ++i) { lits.push_back(c->lit(i)); SASSERT(c->coeff(i).is_one()); ctx.mk_th_axiom(get_id(), lit, ~c->lit(i)); } ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); return true; } // maximal coefficient: scoped_mpz& max_watch = c->m_max_watch; max_watch.reset(); for (unsigned i = 0; i < args.size(); ++i) { mpz const& num = args[i].second.to_mpq().numerator(); if (m_mpz_mgr.lt(max_watch, num)) { max_watch = num; } } // pre-compile threshold for cardinality bool enable_compile = m_enable_compilation && c->is_ge() && !c->k().is_one(); for (unsigned i = 0; enable_compile && i < args.size(); ++i) { enable_compile = (args[i].second <= m_max_compiled_coeff); } if (enable_compile) { unsigned log = 1, n = 1; while (n <= args.size()) { ++log; n *= 2; } unsigned th = args.size()*log; // 10* c->m_compilation_threshold = th; IF_VERBOSE(2, verbose_stream() << "(smt.pb setting compilation threhshold to " << th << ")\n";); TRACE("pb", tout << "compilation threshold: " << th << "\n";); } else { c->m_compilation_threshold = UINT_MAX; } init_watch_var(*c); m_ineqs.insert(abv, c); m_ineqs_trail.push_back(abv); if (m_enable_simplex) { // // TBD: using abv as slack identity doesn't quite // work if psuedo-Booleans are used // in a nested way. So assume // arg_t rep(c->args()); rep.remove_negations(); // normalize representative numeral k = rep.k(); theory_var slack; bool_var abv2; TRACE("pb", display(tout << abv <<"\n", rep);); if (m_ineq_rep.find(rep, abv2)) { slack = abv2; TRACE("pb", tout << "Old row: " << abv << " |-> " << slack << " "; tout << m_ineq_row_info.find(abv2).m_bound << " vs. " << k << "\n"; display(tout, rep);); } else { m_ineq_rep.insert(rep, abv); svector vars; scoped_mpz_vector coeffs(m_mpz_mgr); for (unsigned i = 0; i < rep.size(); ++i) { unsigned v = rep.lit(i).var(); m_simplex.ensure_var(v); vars.push_back(v); if (!m_vars.contains(v)) { mpq_inf zero(mpq(0),mpq(0)), one(mpq(1),mpq(0)); switch(ctx.get_assignment(rep.lit(i))) { case l_true: VERIFY(update_bound(v, literal(v), true, one)); m_simplex.set_lower(v, one); break; case l_false: VERIFY(update_bound(v, ~literal(v), false, zero)); m_simplex.set_upper(v, zero); break; default: m_simplex.set_lower(v, zero); m_simplex.set_upper(v, one); break; } m_vars.insert(v); ctx.push_trail(remove_var(*this, v)); } coeffs.push_back(rep.coeff(i).to_mpq().numerator()); } slack = abv; m_simplex.ensure_var(slack); vars.push_back(slack); coeffs.push_back(mpz(-1)); m_simplex.add_row(slack, vars.size(), vars.c_ptr(), coeffs.c_ptr()); TRACE("pb", tout << "New row: " << abv << " " << k << "\n"; display(tout, rep);); } m_ineq_row_info.insert(abv, row_info(slack, k, rep)); } TRACE("pb", display(tout, *c);); return true; } literal theory_pb::compile_arg(expr* arg) { context& ctx = get_context(); ast_manager& m = get_manager(); bool_var bv; bool has_bv = false; bool negate = m.is_not(arg, arg); SASSERT(!m.is_not(arg)); if (!ctx.b_internalized(arg)) { ctx.internalize(arg, false); } if (ctx.b_internalized(arg)) { bv = ctx.get_bool_var(arg); if (is_uninterp(arg) && null_theory_var == ctx.get_var_theory(bv)) { ctx.set_var_theory(bv, get_id()); } has_bv = (ctx.get_var_theory(bv) == get_id()); } else if (m.is_true(arg)) { bv = true_bool_var; has_bv = true; } else if (m.is_false(arg)) { bv = true_bool_var; has_bv = true; negate = !negate; } // assumes relevancy level = 2 or 0. // TBD: should should have been like an uninterpreted // function internalize, where enodes for each argument // is available. if (!has_bv) { app_ref tmp(m), fml(m); pb_util pb(m); tmp = pb.mk_fresh_bool(); fml = m.mk_iff(tmp, arg); TRACE("pb", tout << "create proxy " << fml << "\n";); ctx.internalize(fml, false); SASSERT(ctx.b_internalized(tmp)); bv = ctx.get_bool_var(tmp); SASSERT(get_id() == ctx.get_var_theory(bv)); literal lit(ctx.get_bool_var(fml)); ctx.mk_th_axiom(get_id(), 1, &lit); ctx.mark_as_relevant(tmp.get()); } return negate?~literal(bv):literal(bv); } void theory_pb::del_watch(watch_list& watch, unsigned index, ineq& c, unsigned ineq_index) { SASSERT(c.is_ge()); if (index < watch.size()) { std::swap(watch[index], watch[watch.size()-1]); } watch.pop_back(); SASSERT(ineq_index < c.watch_size()); scoped_mpz coeff(m_mpz_mgr); coeff = c.ncoeff(ineq_index); if (ineq_index + 1 < c.watch_size()) { std::swap(c.args()[ineq_index], c.args()[c.watch_size()-1]); } --c.m_watch_sz; c.m_watch_sum -= coeff; if (coeff == c.max_watch()) { coeff = c.ncoeff(0); for (unsigned i = 1; coeff != c.max_watch() && i < c.watch_size(); ++i) { if (coeff < c.ncoeff(i)) coeff = c.ncoeff(i); } c.set_max_watch(coeff); } // current index of unwatched literal is c.watch_size(). } void theory_pb::add_watch(ineq& c, unsigned i) { SASSERT(c.is_ge()); literal lit = c.lit(i); scoped_mpz coeff(m_mpz_mgr); coeff = c.ncoeff(i); c.m_watch_sum += coeff; SASSERT(i >= c.watch_size()); if (i > c.watch_size()) { std::swap(c.args()[i], c.args()[c.watch_size()]); } ++c.m_watch_sz; if (coeff > c.max_watch()) { c.set_max_watch(coeff); } watch_literal(lit, &c); } void theory_pb::watch_literal(literal lit, ineq* c) { ptr_vector* ineqs; if (!m_lwatch.find(lit.index(), ineqs)) { ineqs = alloc(ptr_vector); m_lwatch.insert(lit.index(), ineqs); } ineqs->push_back(c); } void theory_pb::watch_var(bool_var v, ineq* c) { ptr_vector* ineqs; if (!m_vwatch.find(v, ineqs)) { ineqs = alloc(ptr_vector); m_vwatch.insert(v, ineqs); } ineqs->push_back(c); } void theory_pb::unwatch_var(bool_var v, ineq* c) { ptr_vector* ineqs = 0; if (m_vwatch.find(v, ineqs)) { remove(*ineqs, c); } } void theory_pb::unwatch_literal(literal w, ineq* c) { ptr_vector* ineqs = 0; if (m_lwatch.find(w.index(), ineqs)) { remove(*ineqs, c); } } void theory_pb::remove(ptr_vector& ineqs, ineq* c) { for (unsigned j = 0; j < ineqs.size(); ++j) { if (ineqs[j] == c) { std::swap(ineqs[j], ineqs[ineqs.size()-1]); ineqs.pop_back(); break; } } } void theory_pb::collect_statistics(::statistics& st) const { st.update("pb conflicts", m_stats.m_num_conflicts); st.update("pb propagations", m_stats.m_num_propagations); st.update("pb predicates", m_stats.m_num_predicates); st.update("pb compilations", m_stats.m_num_compiles); st.update("pb compiled clauses", m_stats.m_num_compiled_clauses); st.update("pb compiled vars", m_stats.m_num_compiled_vars); m_simplex.collect_statistics(st); } void theory_pb::reset_eh() { // m_watch; u_map*>::iterator it = m_lwatch.begin(), end = m_lwatch.end(); for (; it != end; ++it) { dealloc(it->m_value); } it = m_vwatch.begin(), end = m_vwatch.end(); for (; it != end; ++it) { dealloc(it->m_value); } u_map::iterator itc = m_ineqs.begin(), endc = m_ineqs.end(); for (; itc != endc; ++itc) { dealloc(itc->m_value); } m_lwatch.reset(); m_vwatch.reset(); m_ineqs.reset(); m_ineqs_trail.reset(); m_ineqs_lim.reset(); m_stats.reset(); m_to_compile.reset(); } void theory_pb::new_eq_eh(theory_var v1, theory_var v2) { UNREACHABLE(); } final_check_status theory_pb::final_check_eh() { TRACE("pb", display(tout);); DEBUG_CODE(validate_final_check();); return FC_DONE; } void theory_pb::assign_eh(bool_var v, bool is_true) { ptr_vector* ineqs = 0; literal nlit(v, is_true); TRACE("pb", tout << "assign: " << ~nlit << "\n";); if (m_lwatch.find(nlit.index(), ineqs)) { if (m_enable_simplex) { mpq_inf num(mpq(is_true?1:0),mpq(0)); if (!update_bound(v, ~nlit, is_true, num)) { return; } if (!check_feasible()) { return; } } for (unsigned i = 0; i < ineqs->size(); ++i) { ineq* c = (*ineqs)[i]; SASSERT(c->is_ge()); if (assign_watch_ge(v, is_true, *ineqs, i)) { // i was removed from watch list. --i; } } } if (m_vwatch.find(v, ineqs)) { for (unsigned i = 0; i < ineqs->size(); ++i) { ineq* c = (*ineqs)[i]; assign_watch(v, is_true, *c); } } ineq* c = 0; if (m_ineqs.find(v, c)) { if (m_enable_simplex) { row_info const& info = m_ineq_row_info.find(v); scoped_eps_numeral coeff(m_mpq_inf_mgr); coeff = std::make_pair(info.m_bound.to_mpq(), mpq(0)); unsigned slack = info.m_slack; if (is_true) { update_bound(slack, literal(v), true, coeff); if (c->is_eq()) { update_bound(slack, literal(v), false, coeff); } } else if (c->is_ge()) { m_mpq_inf_mgr.sub(coeff, std::make_pair(mpq(1),mpq(0)), coeff); update_bound(slack, ~literal(v), false, coeff); } if (!check_feasible()) { return; } } if (c->is_ge()) { assign_ineq(*c, is_true); } else { assign_eq(*c, is_true); } } } literal_vector& theory_pb::get_all_literals(ineq& c, bool negate) { context& ctx = get_context(); literal_vector& lits = get_lits(); for (unsigned i = 0; i < c.size(); ++i) { literal l = c.lit(i); switch(ctx.get_assignment(l)) { case l_true: lits.push_back(negate?(~l):l); break; case l_false: lits.push_back(negate?l:(~l)); break; default: break; } } return lits; } literal_vector& theory_pb::get_helpful_literals(ineq& c, bool negate) { scoped_mpz sum(m_mpz_mgr); mpz const& k = c.mpz_k(); context& ctx = get_context(); literal_vector& lits = get_lits(); for (unsigned i = 0; sum < k && i < c.size(); ++i) { literal l = c.lit(i); if (ctx.get_assignment(l) == l_true) { sum += c.ncoeff(i); if (negate) l = ~l; lits.push_back(l); } } SASSERT(sum >= k); return lits; } literal_vector& theory_pb::get_unhelpful_literals(ineq& c, bool negate) { context& ctx = get_context(); literal_vector& lits = get_lits(); for (unsigned i = 0; i < c.size(); ++i) { literal l = c.lit(i); if (ctx.get_assignment(l) == l_false) { if (negate) l = ~l; lits.push_back(l); } } return lits; } class theory_pb::rewatch_vars : public trail { theory_pb& pb; ineq& c; public: rewatch_vars(theory_pb& p, ineq& c): pb(p), c(c) {} virtual void undo(context& ctx) { for (unsigned i = 0; i < c.size(); ++i) { pb.watch_var(c.lit(i).var(), &c); } } }; class theory_pb::negate_ineq : public trail { ineq& c; public: negate_ineq(ineq& c): c(c) {} virtual void undo(context& ctx) { c.negate(); } }; /** \brief propagate assignment to inequality. This is a basic, non-optimized implementation based on the assumption that inequalities are mostly units and/or relatively few compared to number of argumets. */ void theory_pb::assign_ineq(ineq& c, bool is_true) { context& ctx = get_context(); ctx.push_trail(value_trail(c.m_max_sum)); ctx.push_trail(value_trail(c.m_min_sum)); ctx.push_trail(value_trail(c.m_nfixed)); ctx.push_trail(rewatch_vars(*this, c)); clear_watch(c); SASSERT(c.is_ge()); unsigned sz = c.size(); if (c.lit().sign() == is_true) { c.negate(); ctx.push_trail(negate_ineq(c)); } scoped_mpz maxsum(m_mpz_mgr), mininc(m_mpz_mgr); for (unsigned i = 0; i < sz; ++i) { lbool asgn = ctx.get_assignment(c.lit(i)); if (asgn != l_false) { maxsum += c.ncoeff(i); } if (asgn == l_undef && (mininc.is_zero() || mininc > c.ncoeff(i))) { mininc = c.ncoeff(i); } } TRACE("pb", tout << "assign: " << c.lit() << "\n"; display(tout, c); ); if (maxsum < c.mpz_k()) { literal_vector& lits = get_unhelpful_literals(c, false); lits.push_back(~c.lit()); add_clause(c, lits); } else { init_watch_literal(c); SASSERT(c.m_watch_sum >= c.mpz_k()); DEBUG_CODE(validate_watch(c);); } // perform unit propagation if (maxsum >= c.mpz_k() && maxsum - mininc < c.mpz_k()) { literal_vector& lits = get_unhelpful_literals(c, true); lits.push_back(c.lit()); for (unsigned i = 0; i < sz; ++i) { if (ctx.get_assignment(c.lit(i)) == l_undef) { DEBUG_CODE(validate_assign(c, lits, c.lit(i));); add_assign(c, lits, c.lit(i)); } } } } /** \brief propagate assignment to equality. */ void theory_pb::assign_eq(ineq& c, bool is_true) { SASSERT(c.is_eq()); } /** Propagation rules: nfixed = N & minsum = k -> T nfixed = N & minsum != k -> F minsum > k or maxsum < k -> F minsum = k & = -> fix 0 variables nfixed+1 = N & = -> fix unassigned variable or conflict nfixed+1 = N & != -> maybe forced unassigned to ensure disequal minsum >= k -> T maxsum < k -> F */ void theory_pb::assign_watch(bool_var v, bool is_true, ineq& c) { context& ctx = get_context(); unsigned i; literal l = c.lit(); lbool asgn = ctx.get_assignment(l); if (c.max_sum() < c.mpz_k() && asgn == l_false) { return; } if (c.is_ge() && c.min_sum() >= c.mpz_k() && asgn == l_true) { return; } for (i = 0; i < c.size(); ++i) { if (c.lit(i).var() == v) { break; } } TRACE("pb", display(tout << "assign watch " << literal(v,!is_true) << " ", c, true);); SASSERT(i < c.size()); if (c.lit(i).sign() == is_true) { ctx.push_trail(value_trail(c.m_max_sum)); c.m_max_sum -= c.ncoeff(i); } else { ctx.push_trail(value_trail(c.m_min_sum)); c.m_min_sum += c.ncoeff(i); } DEBUG_CODE( scoped_mpz sum(m_mpz_mgr); scoped_mpz maxs(m_mpz_mgr); for (unsigned i = 0; i < c.size(); ++i) { if (ctx.get_assignment(c.lit(i)) == l_true) sum += c.ncoeff(i); if (ctx.get_assignment(c.lit(i)) != l_false) maxs += c.ncoeff(i); } CTRACE("pb", (maxs > c.max_sum()), display(tout, c, true);); SASSERT(c.min_sum() <= sum); SASSERT(sum <= maxs); SASSERT(maxs <= c.max_sum()); ); SASSERT(c.min_sum() <= c.max_sum()); SASSERT(!m_mpz_mgr.is_neg(c.min_sum())); ctx.push_trail(value_trail(c.m_nfixed)); ++c.m_nfixed; SASSERT(c.nfixed() <= c.size()); if (c.is_ge() && c.min_sum() >= c.mpz_k() && asgn != l_true) { TRACE("pb", display(tout << "Set " << l << "\n", c, true);); add_assign(c, get_helpful_literals(c, false), l); } else if (c.max_sum() < c.mpz_k() && asgn != l_false) { TRACE("pb", display(tout << "Set " << ~l << "\n", c, true);); add_assign(c, get_unhelpful_literals(c, true), ~l); } else if (c.is_eq() && c.nfixed() == c.size() && c.min_sum() == c.mpz_k() && asgn != l_true) { TRACE("pb", display(tout << "Set " << l << "\n", c, true);); add_assign(c, get_all_literals(c, false), l); } else if (c.is_eq() && c.nfixed() == c.size() && c.min_sum() != c.mpz_k() && asgn != l_false) { TRACE("pb", display(tout << "Set " << ~l << "\n", c, true);); add_assign(c, get_all_literals(c, false), ~l); } #if 0 else if (c.is_eq() && c.min_sum() > c.mpz_k() && asgn != l_false) { TRACE("pb", display(tout << "Set " << ~l << "\n", c, true);); add_assign(c, get_all_literals(c, false), ~l); } else if (c.is_eq() && asgn == l_true && c.min_sum() == c.mpz_k() && c.max_sum() > c.mpz_k()) { literal_vector& lits = get_all_literals(c, false); lits.push_back(c.lit()); for (unsigned i = 0; i < c.size(); ++i) { if (ctx.get_assignment(c.lit(i)) == l_undef) { add_assign(c, lits, ~c.lit(i)); } } } #endif else { IF_VERBOSE(3, display(verbose_stream() << "no propagation ", c, true);); } } /** \brief v is assigned in inequality c. Update current bounds and watch list. Optimize for case where the c.lit() is True. This covers the case where inequalities are unit literals and formulas in negation normal form (inequalities are closed under negation). */ bool theory_pb::assign_watch_ge(bool_var v, bool is_true, watch_list& watch, unsigned watch_index) { bool removed = false; context& ctx = get_context(); ineq& c = *watch[watch_index]; //display(std::cout << v << " ", c, true); unsigned w = c.find_lit(v, 0, c.watch_size()); SASSERT(ctx.get_assignment(c.lit()) == l_true); SASSERT(is_true == c.lit(w).sign()); // // watch_sum is decreased. // Adjust set of watched literals. // scoped_mpz k_coeff(m_mpz_mgr), k(m_mpz_mgr); k = c.mpz_k(); k_coeff = k; k_coeff += c.ncoeff(w); bool add_more = c.watch_sum() < k_coeff + c.max_watch(); for (unsigned i = c.watch_size(); add_more && i < c.size(); ++i) { if (ctx.get_assignment(c.lit(i)) != l_false) { add_watch(c, i); add_more = c.watch_sum() < k_coeff + c.max_watch(); } } if (c.watch_sum() < k_coeff) { // // L: 3*x1 + 2*x2 + x4 >= 3, but x1 <- 0, x2 <- 0 // create clause x1 or x2 or ~L // literal_vector& lits = get_unhelpful_literals(c, false); lits.push_back(~c.lit()); add_clause(c, lits); } else { del_watch(watch, watch_index, c, w); removed = true; SASSERT(c.watch_sum() >= k); if (c.watch_sum() < k + c.max_watch()) { // // opportunities for unit propagation for unassigned // literals whose coefficients satisfy // c.watch_sum() < k // // L: 3*x1 + 2*x2 + x4 >= 3, but x1 <- 0 // Create clauses x1 or ~L or x2 // x1 or ~L or x4 // literal_vector& lits = get_unhelpful_literals(c, true); lits.push_back(c.lit()); scoped_mpz deficit(m_mpz_mgr); deficit = c.watch_sum() - k; for (unsigned i = 0; i < c.size(); ++i) { if (ctx.get_assignment(c.lit(i)) == l_undef && deficit < c.ncoeff(i)) { DEBUG_CODE(validate_assign(c, lits, c.lit(i));); add_assign(c, lits, c.lit(i)); // break; } } } // // else: c.watch_sum() >= k + c.max_watch() // } TRACE("pb", tout << "assign: " << literal(v,!is_true) << "\n"; display(tout, c); ); return removed; } struct theory_pb::psort_expr { context& ctx; ast_manager& m; theory_pb& th; pb_util pb; typedef smt::literal literal; typedef smt::literal_vector literal_vector; psort_expr(context& c, theory_pb& th): ctx(c), m(c.get_manager()), th(th), pb(m) {} literal fresh() { app_ref y(m); y = pb.mk_fresh_bool(); return literal(ctx.mk_bool_var(y)); } literal mk_max(literal a, literal b) { if (a == b) return a; expr_ref t1(m), t2(m), t3(m); ctx.literal2expr(a, t1); ctx.literal2expr(b, t2); t3 = m.mk_or(t1, t2); bool_var v = ctx.b_internalized(t3)?ctx.get_bool_var(t3):ctx.mk_bool_var(t3); return literal(v); } literal mk_min(literal a, literal b) { if (a == b) return a; expr_ref t1(m), t2(m), t3(m); ctx.literal2expr(a, t1); ctx.literal2expr(b, t2); t3 = m.mk_and(t1, t2); bool_var v = ctx.b_internalized(t3)?ctx.get_bool_var(t3):ctx.mk_bool_var(t3); return literal(v); } literal mk_not(literal a) { return ~a; } void mk_clause(unsigned n, literal const* ls) { literal_vector tmp(n, ls); ctx.mk_clause(n, tmp.c_ptr(), th.justify(tmp), CLS_AUX, 0); } literal mk_false() { return false_literal; } literal mk_true() { return true_literal; } std::ostream& pp(std::ostream& out, literal l) { return out << l; } }; // for testing literal theory_pb::assert_ge(context& ctx, unsigned k, unsigned n, literal const* xs) { theory_pb_params p; theory_pb th(ctx.get_manager(), p); psort_expr ps(ctx, th); psort_nw sort(ps); return sort.ge(false, k, n, xs); } void theory_pb::inc_propagations(ineq& c) { ++c.m_num_propagations; if (c.m_compiled == l_false && c.m_num_propagations > c.m_compilation_threshold) { c.m_compiled = l_undef; m_to_compile.push_back(&c); } } void theory_pb::restart_eh() { for (unsigned i = 0; i < m_to_compile.size(); ++i) { compile_ineq(*m_to_compile[i]); } m_to_compile.reset(); } void theory_pb::compile_ineq(ineq& c) { ++m_stats.m_num_compiles; context& ctx = get_context(); // only cardinality constraints are compiled. SASSERT(c.m_compilation_threshold < UINT_MAX); DEBUG_CODE(for (unsigned i = 0; i < c.size(); ++i) SASSERT(c.coeff(i).is_int()); ); unsigned k = c.k().get_unsigned(); unsigned num_args = c.size(); literal thl = c.lit(); literal at_least_k; literal_vector in; for (unsigned i = 0; i < num_args; ++i) { rational n = c.coeff(i); lbool val = ctx.get_assignment(c.lit()); if (val != l_undef && ctx.get_assign_level(thl) == ctx.get_base_level()) { if (val == l_true) { unsigned m = n.get_unsigned(); if (k < m) { return; } k -= m; } continue; } while (n.is_pos()) { in.push_back(c.lit(i)); n -= rational::one(); } } if (ctx.get_assignment(thl) == l_true && ctx.get_assign_level(thl) == ctx.get_base_level()) { psort_expr ps(ctx, *this); psort_nw sortnw(ps); sortnw.m_stats.reset(); at_least_k = sortnw.ge(false, k, in.size(), in.c_ptr()); ctx.mk_clause(~thl, at_least_k, justify(~thl, at_least_k)); m_stats.m_num_compiled_vars += sortnw.m_stats.m_num_compiled_vars; m_stats.m_num_compiled_clauses += sortnw.m_stats.m_num_compiled_clauses; } else { psort_expr ps(ctx, *this); psort_nw sortnw(ps); sortnw.m_stats.reset(); literal at_least_k = sortnw.ge(true, k, in.size(), in.c_ptr()); ctx.mk_clause(~thl, at_least_k, justify(~thl, at_least_k)); ctx.mk_clause(~at_least_k, thl, justify(thl, ~at_least_k)); m_stats.m_num_compiled_vars += sortnw.m_stats.m_num_compiled_vars; m_stats.m_num_compiled_clauses += sortnw.m_stats.m_num_compiled_clauses; } IF_VERBOSE(1, verbose_stream() << "(smt.pb compile sorting network bound: " << k << " literals: " << in.size() << ")\n";); TRACE("pb", tout << thl << "\n";); // auxiliary clauses get removed when popping scopes. // we have to recompile the circuit after back-tracking. c.m_compiled = l_false; ctx.push_trail(value_trail(c.m_compiled)); c.m_compiled = l_true; } void theory_pb::init_search_eh() { m_to_compile.reset(); } void theory_pb::push_scope_eh() { m_ineqs_lim.push_back(m_ineqs_trail.size()); } void theory_pb::pop_scope_eh(unsigned num_scopes) { // remove inequalities. unsigned new_lim = m_ineqs_lim.size()-num_scopes; unsigned sz = m_ineqs_lim[new_lim]; while (m_ineqs_trail.size() > sz) { bool_var v = m_ineqs_trail.back(); ineq* c = 0; VERIFY(m_ineqs.find(v, c)); clear_watch(*c); m_ineqs.remove(v); m_ineqs_trail.pop_back(); if (m_enable_simplex) { row_info r_info; VERIFY(m_ineq_row_info.find(v, r_info)); m_ineq_row_info.erase(v); bool_var v2 = m_ineq_rep.find(r_info.m_rep); if (v == v2) { m_simplex.del_row(r_info.m_slack); m_ineq_rep.erase(r_info.m_rep); } } dealloc(c); } m_ineqs_lim.resize(new_lim); } void theory_pb::clear_watch(ineq& c) { for (unsigned i = 0; i < c.size(); ++i) { literal w = c.lit(i); unwatch_var(w.var(), &c); unwatch_literal(w, &c); } c.m_watch_sum.reset(); c.m_watch_sz = 0; c.m_max_watch.reset(); c.m_nfixed = 0; c.m_max_sum.reset(); c.m_min_sum.reset(); } class theory_pb::unwatch_ge : public trail { theory_pb& pb; ineq& c; public: unwatch_ge(theory_pb& p, ineq& c): pb(p), c(c) {} virtual void undo(context& ctx) { for (unsigned i = 0; i < c.watch_size(); ++i) { pb.unwatch_literal(c.lit(i), &c); } c.m_watch_sz = 0; c.m_watch_sum.reset(); c.m_max_watch.reset(); } }; void theory_pb::init_watch_literal(ineq& c) { context& ctx = get_context(); scoped_mpz max_k(m_mpz_mgr); c.m_watch_sum.reset(); c.m_watch_sz = 0; c.m_max_watch.reset(); bool watch_more = true; for (unsigned i = 0; watch_more && i < c.size(); ++i) { if (ctx.get_assignment(c.lit(i)) != l_false) { add_watch(c, i); max_k = c.mpz_k(); max_k += c.max_watch(); watch_more = c.m_watch_sum < max_k; } } ctx.push_trail(unwatch_ge(*this, c)); } void theory_pb::init_watch_var(ineq& c) { c.m_min_sum.reset(); c.m_max_sum.reset(); c.m_nfixed = 0; c.m_watch_sum.reset(); c.m_max_watch.reset(); c.m_watch_sz = 0; for (unsigned i = 0; i < c.size(); ++i) { watch_var(c.lit(i).var(), &c); c.m_max_sum += c.ncoeff(i); } } literal_vector& theory_pb::get_lits() { m_literals.reset(); return m_literals; } class theory_pb::pb_justification : public theory_propagation_justification { ineq& m_ineq; public: pb_justification(ineq& c, family_id fid, region & r, unsigned num_lits, literal const * lits, literal consequent): theory_propagation_justification(fid, r, num_lits, lits, consequent), m_ineq(c) {} ineq& get_ineq() { return m_ineq; } }; void theory_pb::add_assign(ineq& c, literal_vector const& lits, literal l) { inc_propagations(c); m_stats.m_num_propagations++; context& ctx = get_context(); TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - "; for (unsigned i = 0; i < lits.size(); ++i) { tout << lits[i] << " "; } tout << "=> " << l << "\n"; display(tout, c, true);); ctx.assign(l, ctx.mk_justification( pb_justification( c, get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), l))); } void theory_pb::add_clause(ineq& c, literal_vector const& lits) { inc_propagations(c); m_stats.m_num_conflicts++; context& ctx = get_context(); #if 0 if (m_stats.m_num_conflicts == 1000) { display(std::cout); } #endif TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - "; for (unsigned i = 0; i < lits.size(); ++i) { tout << lits[i] << " "; } tout << "\n"; display(tout, c, true);); justification* js = 0; if (m_conflict_frequency == 0 || (m_conflict_frequency -1 == (c.m_num_propagations % m_conflict_frequency))) { resolve_conflict(c); } if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); } ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); } void theory_pb::set_mark(bool_var v, unsigned idx) { SASSERT(v != null_bool_var); if (v >= static_cast(m_conseq_index.size())) { m_conseq_index.resize(v+1, null_index); } SASSERT(!is_marked(v) || m_conseq_index[v] == idx); m_marked.push_back(v); m_conseq_index[v] = idx; } bool theory_pb::is_marked(bool_var v) const { return (v < static_cast(m_conseq_index.size())) && (m_conseq_index[v] != null_index); } void theory_pb::unset_mark(bool_var v) { SASSERT(v != null_bool_var); if (v < static_cast(m_conseq_index.size())) { m_conseq_index[v] = null_index; } } void theory_pb::unset_marks() { for (unsigned i = 0; i < m_marked.size(); ++i) { unset_mark(m_marked[i]); } m_marked.reset(); } void theory_pb::process_antecedent(literal l, numeral coeff) { context& ctx = get_context(); bool_var v = l.var(); unsigned lvl = ctx.get_assign_level(v); if (ctx.get_assignment(l) != l_false) { m_lemma.m_k -= coeff; if (m_learn_complements && is_marked(v)) { SASSERT(ctx.get_assignment(l) == l_true); numeral& lcoeff = m_lemma[m_conseq_index[v]].second; lcoeff -= coeff; if (!lcoeff.is_pos()) { // perhaps let lemma simplification change coefficient // when negative? remove_from_lemma(m_conseq_index[v]); } } } else if (lvl > ctx.get_base_level()) { if (is_marked(v)) { m_lemma[m_conseq_index[v]].second += coeff; SASSERT(m_lemma[m_conseq_index[v]].second.is_pos()); } else { if (lvl == m_conflict_lvl) { TRACE("pb", tout << "add mark: " << l << " " << coeff << "\n";); ++m_num_marks; } set_mark(v, m_lemma.size()); m_lemma.push_back(std::make_pair(l, coeff)); } TRACE("pb_verbose", tout << "ante: " << m_lemma.lit(m_conseq_index[v]) << "*" << m_lemma.coeff(m_conseq_index[v]) << " " << lvl << "\n";); } } void theory_pb::process_ineq(ineq& c, literal conseq, numeral coeff1) { // // Create CUT. // // // . find coeff2 // . find lcm of coefficients to conseq. // . multiply m_lemma by lcm/coeff coefficient to align. // . create lcm/coeff_2 to multiply on this side. // . cut resolve constraints. // context& ctx = get_context(); numeral coeff2 = (conseq==null_literal)?numeral::one():numeral::zero(); for (unsigned i = 0; i < c.size(); ++i) { if (c.lit(i) == conseq) { coeff2 = c.coeff(i); break; } } SASSERT(coeff2.is_pos()); numeral lc = lcm(coeff1, coeff2); numeral g = lc/coeff1; SASSERT(g.is_int()); if (g > numeral::one()) { for (unsigned i = 0; i < m_lemma.size(); ++i) { m_lemma[i].second *= g; } m_lemma.m_k *= g; } g = lc/coeff2; SASSERT(g.is_int()); m_lemma.m_k += g*c.k(); for (unsigned i = 0; i < c.size(); ++i) { process_antecedent(c.lit(i), g*c.coeff(i)); } SASSERT(ctx.get_assignment(c.lit()) == l_true); if (ctx.get_assign_level(c.lit()) > ctx.get_base_level()) { m_ineq_literals.push_back(c.lit()); } } // // modeled after sat_solver/smt_context // bool theory_pb::resolve_conflict(ineq& c) { if (!c.is_ge()) { return false; } TRACE("pb", display(tout, c, true);); bool_var v; literal conseq; context& ctx = get_context(); unsigned& lvl = m_conflict_lvl = 0; for (unsigned i = 0; i < c.size(); ++i) { if (ctx.get_assignment(c.lit(i)) == l_false) { lvl = std::max(lvl, ctx.get_assign_level(c.lit(i))); } } if (lvl < ctx.get_assign_level(c.lit()) || lvl == ctx.get_base_level()) { return false; } unset_marks(); m_num_marks = 0; m_lemma.reset(); m_lemma.m_k.reset(); m_ineq_literals.reset(); process_ineq(c, null_literal, numeral::one()); // add consequent to lemma. // point into stack of assigned literals literal_vector const& lits = ctx.assigned_literals(); SASSERT(!lits.empty()); unsigned idx = lits.size()-1; while (m_num_marks > 0) { TRACE("pb_verbose", display(tout << "lemma ", m_lemma);); lbool is_sat = m_lemma.normalize(false); if (is_sat == l_false) { break; } if (is_sat == l_true) { IF_VERBOSE(0, verbose_stream() << "lemma already evaluated\n";); TRACE("pb", tout << "lemma already evaluated\n";); return false; } TRACE("pb", display(tout, m_lemma);); SASSERT(m_lemma.well_formed()); // // find the next marked variable in the assignment stack // do { conseq = lits[idx]; v = conseq.var(); --idx; } while (!is_marked(v) && idx > 0); if (idx == 0 && !is_marked(v)) { // // Yes, this can (currently) happen because // the decisions for performing unit propagation // are made asynchronously. // In other words, PB unit propagation does not follow the // same order as the assignment stack. // It is not a correctness bug but causes to miss lemmas. // IF_VERBOSE(2, display_resolved_lemma(verbose_stream());); TRACE("pb", display_resolved_lemma(tout);); return false; } unsigned conseq_index = m_conseq_index[v]; numeral conseq_coeff = m_lemma.coeff(conseq_index); TRACE("pb", display(tout, m_lemma, true); tout << "conseq: " << conseq << " at index: " << conseq_index << "\n";); SASSERT(~conseq == m_lemma.lit(conseq_index)); remove_from_lemma(conseq_index); b_justification js = ctx.get_justification(v); // // Resolve selected conseq with antecedents. // switch(js.get_kind()) { case b_justification::CLAUSE: { clause& cls = *js.get_clause(); justification* cjs = cls.get_justification(); if (cjs && !is_proof_justification(*cjs)) { TRACE("pb", tout << "skipping justification for clause over: " << conseq << " " << typeid(*cjs).name() << "\n";); m_ineq_literals.push_back(conseq); break; } unsigned num_lits = cls.get_num_literals(); if (cls.get_literal(0) == conseq) { process_antecedent(cls.get_literal(1), conseq_coeff); } else { SASSERT(cls.get_literal(1) == conseq); process_antecedent(cls.get_literal(0), conseq_coeff); } for (unsigned i = 2; i < num_lits; ++i) { process_antecedent(cls.get_literal(i), conseq_coeff); } TRACE("pb", for (unsigned i = 0; i < num_lits; ++i) tout << cls.get_literal(i) << " "; tout << "\n";); break; } case b_justification::BIN_CLAUSE: process_antecedent(~js.get_literal(), conseq_coeff); TRACE("pb", tout << "binary: " << js.get_literal() << "\n";); break; case b_justification::AXIOM: if (ctx.get_assign_level(v) > ctx.get_base_level()) { m_ineq_literals.push_back(conseq); } TRACE("pb", tout << "axiom " << conseq << "\n";); break; case b_justification::JUSTIFICATION: { justification* j = js.get_justification(); pb_justification* pbj = 0; if (!conseq.sign() && j->get_from_theory() == get_id()) { pbj = dynamic_cast(j); } if (pbj && pbj->get_ineq().is_eq()) { // only resolve >= that are positive consequences. pbj = 0; } if (pbj && pbj->get_ineq().lit() == conseq) { // can't resolve against literal representing inequality. pbj = 0; } if (pbj) { // weaken the lemma and resolve. TRACE("pb", display(tout << "resolve with inequality", pbj->get_ineq(), true);); process_ineq(pbj->get_ineq(), conseq, conseq_coeff); } else { TRACE("pb", tout << "skipping justification for " << conseq << " from theory " << j->get_from_theory() << " " << typeid(*j).name() << "\n";); m_ineq_literals.push_back(conseq); } break; } default: UNREACHABLE(); } } TRACE("pb", for (unsigned i = 0; i < m_ineq_literals.size(); ++i) { tout << m_ineq_literals[i] << " "; } display(tout << "=> ", m_lemma);); // 3x + 3y + z + u >= 4 // ~x /\ ~y => z + u >= IF_VERBOSE(4, display(verbose_stream() << "lemma1: ", m_lemma);); hoist_maximal_values(); lbool is_true = m_lemma.normalize(false); m_lemma.prune(false); IF_VERBOSE(4, display(verbose_stream() << "lemma2: ", m_lemma);); //unsigned l_size = m_ineq_literals.size() + ((is_true==l_false)?0:m_lemma.size()); //if (s_min_l_size >= l_size) { // verbose_stream() << "(pb.conflict min size: " << l_size << ")\n"; // s_min_l_size = l_size; //} //IF_VERBOSE(1, verbose_stream() << "(pb.conflict " << m_ineq_literals.size() << " " << m_lemma.size() << "\n";); switch(is_true) { case l_true: UNREACHABLE(); return false; case l_false: inc_propagations(c); m_stats.m_num_conflicts++; for (unsigned i = 0; i < m_ineq_literals.size(); ++i) { m_ineq_literals[i].neg(); } ctx.mk_clause(m_ineq_literals.size(), m_ineq_literals.c_ptr(), justify(m_ineq_literals), CLS_AUX_LEMMA, 0); break; default: { app_ref tmp = m_lemma.to_expr(false, ctx, get_manager()); internalize_atom(tmp, false); ctx.mark_as_relevant(tmp.get()); literal l(ctx.get_bool_var(tmp)); add_assign(c, m_ineq_literals, l); break; } } return true; } bool theory_pb::is_proof_justification(justification const& j) const { return typeid(smt::justification_proof_wrapper) == typeid(j); } justification* theory_pb::justify(literal l1, literal l2) { literal lits[2] = { l1, l2 }; justification* js = 0; if (proofs_enabled()) { js = get_context().mk_justification(theory_axiom_justification(get_id(), get_context().get_region(), 2, lits)); } return js; } justification* theory_pb::justify(literal_vector const& lits) { justification* js = 0; if (proofs_enabled()) { js = get_context().mk_justification(theory_axiom_justification(get_id(), get_context().get_region(), lits.size(), lits.c_ptr())); } return js; } void theory_pb::hoist_maximal_values() { for (unsigned i = 0; i < m_lemma.size(); ++i) { if (m_lemma.coeff(i) >= m_lemma.k()) { m_ineq_literals.push_back(~m_lemma.lit(i)); std::swap(m_lemma[i], m_lemma[m_lemma.size()-1]); m_lemma.pop_back(); --i; } } } void theory_pb::remove_from_lemma(unsigned idx) { // Remove conseq from lemma: literal lit = m_lemma.lit(idx); unsigned last = m_lemma.size()-1; if (idx != last) { m_lemma[idx] = m_lemma[last]; m_conseq_index[m_lemma.lit(idx).var()] = idx; } m_lemma.pop_back(); unset_mark(lit.var()); --m_num_marks; } // debug methods void theory_pb::validate_watch(ineq const& c) const { scoped_mpz sum(m_mpz_mgr), max(m_mpz_mgr); for (unsigned i = 0; i < c.watch_size(); ++i) { sum += c.ncoeff(i); if (max < c.ncoeff(i)) { max = c.ncoeff(i); } } SASSERT(c.watch_sum() == sum); SASSERT(sum >= c.mpz_k()); SASSERT(max == c.max_watch()); } void theory_pb::validate_assign(ineq const& c, literal_vector const& lits, literal l) const { uint_set nlits; context& ctx = get_context(); for (unsigned i = 0; i < lits.size(); ++i) { SASSERT(ctx.get_assignment(lits[i]) == l_true); nlits.insert((~lits[i]).index()); } SASSERT(ctx.get_assignment(l) == l_undef); SASSERT(ctx.get_assignment(c.lit()) == l_true); nlits.insert(l.index()); numeral sum = numeral::zero(); for (unsigned i = 0; i < c.size(); ++i) { literal lit = c.lit(i); if (!nlits.contains(lit.index())) { sum += c.coeff(i); } } CTRACE("pb", (sum >= c.k()), display(tout << "invalid assign" , c, true); for (unsigned i = 0; i < lits.size(); ++i) { tout << lits[i] << " "; } tout << " => " << l << "\n";); SASSERT(sum < c.k()); } void theory_pb::validate_final_check() { u_map::iterator itc = m_ineqs.begin(), endc = m_ineqs.end(); for (; itc != endc; ++itc) { validate_final_check(*itc->m_value); } } void theory_pb::validate_final_check(ineq& c) { context& ctx = get_context(); if (ctx.get_assignment(c.lit()) == l_undef) { return; } if (!ctx.is_relevant(c.lit())) { return; } numeral sum = numeral::zero(), maxsum = numeral::zero(); for (unsigned i = 0; i < c.size(); ++i) { switch(ctx.get_assignment(c.lit(i))) { case l_true: sum += c.coeff(i); case l_undef: maxsum += c.coeff(i); break; case l_false: break; } } TRACE("pb", display(tout << "validate: ", c, true); tout << "sum: " << sum << " " << maxsum << " "; tout << ctx.get_assignment(c.lit()) << "\n"; ); SASSERT(sum <= maxsum); SASSERT(!c.is_ge() || (sum >= c.k()) == (ctx.get_assignment(c.lit()) == l_true)); SASSERT(!c.is_ge() || (maxsum < c.k()) == (ctx.get_assignment(c.lit()) == l_false)); SASSERT(!c.is_eq() || (sum == c.k()) == (ctx.get_assignment(c.lit()) == l_true)); } // display methods void theory_pb::display_resolved_lemma(std::ostream& out) const { context& ctx = get_context(); literal_vector const& lits = ctx.assigned_literals(); bool_var v; unsigned lvl; out << "num marks: " << m_num_marks << "\n"; out << "conflict level: " << m_conflict_lvl << "\n"; for (unsigned i = 0; i < lits.size(); ++i) { v = lits[i].var(); lvl = ctx.get_assign_level(v); out << lits[i] << "@ " << lvl << " " << (is_marked(v)?"m":"u") << "\n"; if (lvl == m_conflict_lvl && is_marked(v)) { out << "skipped: " << lits[i] << ":"<< i << "\n"; } } display(out, m_lemma, true); unsigned nc = 0; for (unsigned i = 0; i < m_lemma.size(); ++i) { v = m_lemma.lit(i).var(); lvl = ctx.get_assign_level(v); if (lvl == m_conflict_lvl) ++nc; out << m_lemma.lit(i) << "@" << lvl << " " << (is_marked(v)?"m":"u") << " " << ctx.get_assignment(m_lemma.lit(i)) << "\n"; } out << "num conflicts: " << nc << "\n"; } std::ostream& theory_pb::display(std::ostream& out, arg_t const& c, bool values) const { return c.display(get_context(), out, values); } std::ostream& theory_pb::display(std::ostream& out, ineq const& c, bool values) const { ast_manager& m = get_manager(); context& ctx = get_context(); out << c.lit(); if (c.lit() != null_literal) { if (values) { out << "@(" << ctx.get_assignment(c.lit()); if (ctx.get_assignment(c.lit()) != l_undef) { out << ":" << ctx.get_assign_level(c.lit()); } out << ")"; } expr_ref tmp(m); ctx.literal2expr(c.lit(), tmp); out << " " << tmp << "\n"; } else { out << " "; } for (unsigned i = 0; i < c.size(); ++i) { literal l(c.lit(i)); if (!c.coeff(i).is_one()) { out << c.coeff(i) << "*"; } out << l; if (values) { out << "@(" << ctx.get_assignment(l); if (ctx.get_assignment(l) != l_undef) { out << ":" << ctx.get_assign_level(l); } out << ")"; } if (i + 1 == c.watch_size()) { out << " .w "; } if (i + 1 < c.size()) { out << " + "; } } out << (c.is_ge()?" >= ":" = ") << c.k() << "\n"; if (c.m_num_propagations) out << "propagations: " << c.m_num_propagations << " "; if (c.m_max_watch.is_pos()) out << "max_watch: " << c.max_watch() << " "; if (c.watch_size()) out << "watch size: " << c.watch_size() << " "; if (c.m_watch_sum.is_pos()) out << "watch-sum: " << c.watch_sum() << " "; if (!c.m_max_sum.is_zero()) out << "sum: [" << c.min_sum() << ":" << c.max_sum() << "] "; if (c.m_num_propagations || c.m_max_watch.is_pos() || c.watch_size() || c.m_watch_sum.is_pos() || !c.m_max_sum.is_zero()) out << "\n"; return out; } class theory_pb::pb_model_value_proc : public model_value_proc { app* m_app; svector m_dependencies; public: pb_model_value_proc(app* a): m_app(a) {} void add(enode* n) { m_dependencies.push_back(model_value_dependency(n)); } virtual void get_dependencies(buffer & result) { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } virtual app * mk_value(model_generator & mg, ptr_vector & values) { ast_manager& m = mg.get_manager(); SASSERT(values.size() == m_dependencies.size()); SASSERT(values.size() == m_app->get_num_args()); pb_util u(m); rational sum(0); for (unsigned i = 0; i < m_app->get_num_args(); ++i) { if (!m.is_true(values[i]) && !m.is_false(values[i])) { return m_app; } if (m.is_true(values[i])) { sum += u.get_coeff(m_app, i); } } rational k = u.get_k(m_app); switch(m_app->get_decl_kind()) { case OP_AT_MOST_K: return (sum <= k)?m.mk_true():m.mk_false(); case OP_AT_LEAST_K: return (sum >= k)?m.mk_true():m.mk_false(); case OP_PB_LE: return (sum <= k)?m.mk_true():m.mk_false(); case OP_PB_GE: return (sum >= k)?m.mk_true():m.mk_false(); default: UNREACHABLE(); return 0; } return 0; } }; class pb_factory : public value_factory { public: pb_factory(ast_manager& m, family_id fid): value_factory(m, fid) {} virtual expr * get_some_value(sort * s) { return m_manager.mk_true(); } virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { v1 = m_manager.mk_true(); v2 = m_manager.mk_false(); return true; } virtual expr * get_fresh_value(sort * s) { return 0; } virtual void register_value(expr * n) { } }; void theory_pb::init_model(model_generator & m) { m.register_factory(alloc(pb_factory, get_manager(), get_id())); } model_value_proc * theory_pb::mk_value(enode * n, model_generator & mg) { context& ctx = get_context(); app* a = n->get_owner(); pb_model_value_proc* p = alloc(pb_model_value_proc, a); for (unsigned i = 0; i < a->get_num_args(); ++i) { p->add(ctx.get_enode(a->get_arg(i))); } return p; } void theory_pb::display(std::ostream& out) const { u_map*>::iterator it = m_lwatch.begin(), end = m_lwatch.end(); for (; it != end; ++it) { out << "watch: " << to_literal(it->m_key) << " |-> "; watch_list const& wl = *it->m_value; for (unsigned i = 0; i < wl.size(); ++i) { out << wl[i]->lit() << " "; } out << "\n"; } it = m_vwatch.begin(), end = m_vwatch.end(); for (; it != end; ++it) { out << "watch (v): " << literal(it->m_key) << " |-> "; watch_list const& wl = *it->m_value; for (unsigned i = 0; i < wl.size(); ++i) { out << wl[i]->lit() << " "; } out << "\n"; } u_map::iterator itc = m_ineqs.begin(), endc = m_ineqs.end(); for (; itc != endc; ++itc) { ineq& c = *itc->m_value; display(out, c, true); } } } z3-z3-4.4.1/src/smt/theory_pb.h000066400000000000000000000307401260446376700161670ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_pb.h Abstract: Cardinality theory plugin. Author: Nikolaj Bjorner (nbjorner) 2013-11-05 Notes: This custom theory handles cardinality constraints It performs unit propagation and switches to creating sorting circuits if it keeps having to propagate (create new clauses). --*/ #include "smt_theory.h" #include "pb_decl_plugin.h" #include "smt_clause.h" #include "theory_pb_params.h" #include "simplex.h" namespace smt { class theory_pb : public theory { struct psort_expr; class pb_justification; class pb_model_value_proc; class unwatch_ge; class rewatch_vars; class negate_ineq; class remove_var; class undo_bound; typedef rational numeral; typedef simplex::simplex simplex; typedef simplex::row row; typedef simplex::row_iterator row_iterator; typedef unsynch_mpq_inf_manager eps_manager; typedef _scoped_numeral scoped_eps_numeral; struct arg_t : public vector > { numeral m_k; // invariants: m_k > 0, coeffs[i] > 0 unsigned get_hash() const; bool operator==(arg_t const& other) const; numeral const& k() const { return m_k; } struct hash { unsigned operator()(arg_t const& i) const { return i.get_hash(); } }; struct eq { bool operator()(arg_t const& a, arg_t const& b) const { return a == b; } }; struct child_hash { unsigned operator()(arg_t const& args, unsigned idx) const { return args[idx].first.hash() ^ args[idx].second.hash(); } }; struct kind_hash { unsigned operator()(arg_t const& args) const { return args.size(); } }; void remove_negations(); void negate(); lbool normalize(bool is_eq); void prune(bool is_eq); literal lit(unsigned i) const { return (*this)[i].first; } numeral const & coeff(unsigned i) const { return (*this)[i].second; } std::ostream& display(context& ctx, std::ostream& out, bool values = false) const; app_ref to_expr(bool is_eq, context& ctx, ast_manager& m); bool well_formed() const; }; struct stats { unsigned m_num_conflicts; unsigned m_num_propagations; unsigned m_num_predicates; unsigned m_num_compiles; unsigned m_num_compiled_vars; unsigned m_num_compiled_clauses; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; struct ineq { unsynch_mpz_manager& m_mpz; // mpz manager. literal m_lit; // literal repesenting predicate bool m_is_eq; // is this an = or >=. arg_t m_args[2]; // encode args[0]*coeffs[0]+...+args[n-1]*coeffs[n-1] >= k(); // Watch the first few positions until the sum satisfies: // sum coeffs[i] >= m_lower + max_watch scoped_mpz m_max_watch; // maximal coefficient. unsigned m_watch_sz; // number of literals being watched. scoped_mpz m_watch_sum; // maximal sum of watch literals. // Watch infrastructure for = and unassigned >=: unsigned m_nfixed; // number of variables that are fixed. scoped_mpz m_max_sum; // maximal possible sum. scoped_mpz m_min_sum; // minimal possible sum. unsigned m_num_propagations; unsigned m_compilation_threshold; lbool m_compiled; ineq(unsynch_mpz_manager& m, literal l, bool is_eq) : m_mpz(m), m_lit(l), m_is_eq(is_eq), m_max_watch(m), m_watch_sum(m), m_max_sum(m), m_min_sum(m) { reset(); } arg_t const& args() const { return m_args[m_lit.sign()]; } arg_t& args() { return m_args[m_lit.sign()]; } literal lit() const { return m_lit; } numeral const & k() const { return args().m_k; } mpz const & mpz_k() const { return k().to_mpq().numerator(); } literal lit(unsigned i) const { return args()[i].first; } numeral const & coeff(unsigned i) const { return args()[i].second; } class mpz const& ncoeff(unsigned i) const { return coeff(i).to_mpq().numerator(); } unsigned size() const { return args().size(); } scoped_mpz const& watch_sum() const { return m_watch_sum; } scoped_mpz const& max_watch() const { return m_max_watch; } void set_max_watch(mpz const& n) { m_max_watch = n; } unsigned watch_size() const { return m_watch_sz; } // variable watch infrastructure scoped_mpz const& min_sum() const { return m_min_sum; } scoped_mpz const& max_sum() const { return m_max_sum; } unsigned nfixed() const { return m_nfixed; } bool vwatch_initialized() const { return !m_mpz.is_zero(max_sum()); } void vwatch_reset() { m_min_sum.reset(); m_max_sum.reset(); m_nfixed = 0; } unsigned find_lit(bool_var v, unsigned begin, unsigned end) { while (lit(begin).var() != v) { ++begin; SASSERT(begin < end); } return begin; } void reset(); void negate(); lbool normalize(); void unique(); void prune(); void post_prune(); app_ref to_expr(context& ctx, ast_manager& m); bool is_eq() const { return m_is_eq; } bool is_ge() const { return !m_is_eq; } }; struct row_info { unsigned m_slack; // slack variable in simplex tableau numeral m_bound; // bound arg_t m_rep; // representative row_info(theory_var slack, numeral const& b, arg_t const& r): m_slack(slack), m_bound(b), m_rep(r) {} row_info(): m_slack(0) {} }; typedef ptr_vector watch_list; typedef map arg_map; theory_pb_params m_params; u_map m_lwatch; // per literal. u_map m_vwatch; // per variable. u_map m_ineqs; // per inequality. arg_map m_ineq_rep; // Simplex: representative inequality u_map m_ineq_row_info; // Simplex: row information per variable uint_set m_vars; // Simplex: 0-1 variables. simplex m_simplex; // Simplex: tableau literal_vector m_explain_lower; // Simplex: explanations for lower bounds literal_vector m_explain_upper; // Simplex: explanations for upper bounds unsynch_mpq_inf_manager m_mpq_inf_mgr; // Simplex: manage inf_mpq numerals mutable unsynch_mpz_manager m_mpz_mgr; // Simplex: manager mpz numerals unsigned_vector m_ineqs_trail; unsigned_vector m_ineqs_lim; literal_vector m_literals; // temporary vector pb_util m_util; stats m_stats; ptr_vector m_to_compile; // inequalities to compile. unsigned m_conflict_frequency; bool m_learn_complements; bool m_enable_compilation; bool m_enable_simplex; rational m_max_compiled_coeff; // internalize_atom: literal compile_arg(expr* arg); void add_watch(ineq& c, unsigned index); void del_watch(watch_list& watch, unsigned index, ineq& c, unsigned ineq_index); void init_watch_literal(ineq& c); void init_watch_var(ineq& c); void clear_watch(ineq& c); void watch_literal(literal lit, ineq* c); void watch_var(bool_var v, ineq* c); void unwatch_literal(literal w, ineq* c); void unwatch_var(bool_var v, ineq* c); void remove(ptr_vector& ineqs, ineq* c); bool assign_watch_ge(bool_var v, bool is_true, watch_list& watch, unsigned index); void assign_watch(bool_var v, bool is_true, ineq& c); void assign_ineq(ineq& c, bool is_true); void assign_eq(ineq& c, bool is_true); // simplex: literal set_explain(literal_vector& explains, unsigned var, literal expl); bool update_bound(bool_var v, literal explain, bool is_lower, mpq_inf const& bound); bool check_feasible(); std::ostream& display(std::ostream& out, ineq const& c, bool values = false) const; std::ostream& display(std::ostream& out, arg_t const& c, bool values = false) const; virtual void display(std::ostream& out) const; void display_resolved_lemma(std::ostream& out) const; void add_clause(ineq& c, literal_vector const& lits); void add_assign(ineq& c, literal_vector const& lits, literal l); literal_vector& get_lits(); literal_vector& get_all_literals(ineq& c, bool negate); literal_vector& get_helpful_literals(ineq& c, bool negate); literal_vector& get_unhelpful_literals(ineq& c, bool negate); // // Utilities to compile cardinality // constraints into a sorting network. // void compile_ineq(ineq& c); void inc_propagations(ineq& c); unsigned get_compilation_threshold(ineq& c); // // Conflict resolution, cutting plane derivation. // unsigned m_num_marks; unsigned m_conflict_lvl; arg_t m_lemma; literal_vector m_ineq_literals; svector m_marked; // bool_var |-> index into m_lemma unsigned_vector m_conseq_index; static const unsigned null_index; bool is_marked(bool_var v) const; void set_mark(bool_var v, unsigned idx); void unset_mark(bool_var v); void unset_marks(); bool resolve_conflict(ineq& c); void process_antecedent(literal l, numeral coeff); void process_ineq(ineq& c, literal conseq, numeral coeff); void remove_from_lemma(unsigned idx); bool is_proof_justification(justification const& j) const; void hoist_maximal_values(); void validate_final_check(); void validate_final_check(ineq& c); void validate_assign(ineq const& c, literal_vector const& lits, literal l) const; void validate_watch(ineq const& c) const; bool proofs_enabled() const { return get_manager().proofs_enabled(); } justification* justify(literal l1, literal l2); justification* justify(literal_vector const& lits); public: theory_pb(ast_manager& m, theory_pb_params& p); virtual ~theory_pb(); virtual theory * mk_fresh(context * new_ctx); virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term) { UNREACHABLE(); return false; } virtual void new_eq_eh(theory_var v1, theory_var v2); virtual void new_diseq_eh(theory_var v1, theory_var v2) { } virtual bool use_diseqs() const { return false; } virtual bool build_models() const { return false; } virtual final_check_status final_check_eh(); virtual void reset_eh(); virtual void assign_eh(bool_var v, bool is_true); virtual void init_search_eh(); virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual void restart_eh(); virtual void collect_statistics(::statistics & st) const; virtual model_value_proc * mk_value(enode * n, model_generator & mg); virtual void init_model(model_generator & m); static literal assert_ge(context& ctx, unsigned k, unsigned n, literal const* xs); }; }; z3-z3-4.4.1/src/smt/theory_seq_empty.h000066400000000000000000000021341260446376700175700ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: theory_seq_empty.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2011-14-11 Revision History: --*/ #ifndef THEORY_SEQ_EMPTY_H_ #define THEORY_SEQ_EMPTY_H_ #include "smt_theory.h" namespace smt { class theory_seq_empty : public theory { bool m_used; virtual final_check_status final_check_eh() { return m_used?FC_GIVEUP:FC_DONE; } virtual bool internalize_atom(app*, bool) { if (!m_used) { get_context().push_trail(value_trail(m_used)); m_used = true; } return false; } virtual bool internalize_term(app*) { return internalize_atom(0,false); } virtual void new_eq_eh(theory_var, theory_var) { } virtual void new_diseq_eh(theory_var, theory_var) {} virtual theory* mk_fresh(context*) { return alloc(theory_seq_empty, get_manager()); } virtual char const * get_name() const { return "seq-empty"; } public: theory_seq_empty(ast_manager& m):theory(m.mk_family_id("seq")), m_used(false) {} }; }; #endif /* THEORY_SEQ_EMPTY_H_ */ z3-z3-4.4.1/src/smt/theory_utvpi.cpp000066400000000000000000000111561260446376700172700ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_utvpi.h Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: The implementaton is derived from theory_diff_logic. --*/ #include "theory_utvpi.h" #include "theory_utvpi_def.h" namespace smt { template class theory_utvpi; template class theory_utvpi; // similar to test_diff_logic: utvpi_tester::utvpi_tester(ast_manager& m): m(m), a(m) {} bool utvpi_tester::operator()(expr* e) { m_todo.reset(); m_mark.reset(); m_todo.push_back(e); expr* e1, *e2; while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); if (!m_mark.is_marked(e)) { m_mark.mark(e, true); if (is_var(e)) { continue; } if (!is_app(e)) { return false; } app* ap = to_app(e); if (m.is_eq(ap, e1, e2)) { if (!linearize(e1, e2)) { return false; } } else if (ap->get_family_id() == m.get_basic_family_id()) { continue; } else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) || a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { if (!linearize(e1, e2)) { return false; } } else if (is_uninterp_const(e)) { continue; } else { return false; } } } return true; } vector > const& utvpi_tester::get_linearization() const { SASSERT(m_terms.size() <= 2); return m_terms; } bool utvpi_tester::operator()(unsigned num_fmls, expr* const* fmls) { for (unsigned i = 0; i < num_fmls; ++i) { if (!(*this)(fmls[i])) { return false; } } return true; } bool utvpi_tester::linearize(expr* e) { m_terms.reset(); m_terms.push_back(std::make_pair(e, rational(1))); return linearize(); } bool utvpi_tester::linearize(expr* e1, expr* e2) { m_terms.reset(); m_terms.push_back(std::make_pair(e1, rational(1))); m_terms.push_back(std::make_pair(e2, rational(-1))); return linearize(); } bool utvpi_tester::linearize() { m_weight.reset(); m_coeff_map.reset(); while (!m_terms.empty()) { expr* e1, *e2; rational num; rational mul = m_terms.back().second; expr* e = m_terms.back().first; m_terms.pop_back(); if (a.is_add(e)) { for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul)); } } else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) { m_terms.push_back(std::make_pair(e2, mul*num)); } else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) { m_terms.push_back(std::make_pair(e2, mul*num)); } else if (a.is_sub(e, e1, e2)) { m_terms.push_back(std::make_pair(e1, mul)); m_terms.push_back(std::make_pair(e2, -mul)); } else if (a.is_uminus(e, e1)) { m_terms.push_back(std::make_pair(e1, -mul)); } else if (a.is_numeral(e, num)) { m_weight += num*mul; } else if (a.is_to_real(e, e1)) { m_terms.push_back(std::make_pair(e1, mul)); } else if (!is_uninterp_const(e)) { return false; } else { m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul; } } obj_map::iterator it = m_coeff_map.begin(); obj_map::iterator end = m_coeff_map.end(); for (; it != end; ++it) { rational r = it->m_value; if (r.is_zero()) { continue; } m_terms.push_back(std::make_pair(it->m_key, r)); if (m_terms.size() > 2) { return false; } if (!r.is_one() && !r.is_minus_one()) { return false; } } return true; } } z3-z3-4.4.1/src/smt/theory_utvpi.h000066400000000000000000000232211260446376700167310ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_utvpi.h Abstract: use Bellman Ford traversal algorithm for UTVPI. Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: The implementaton is derived from theory_diff_logic. --*/ #ifndef THEORY_UTVPI_H_ #define THEORY_UTVPI_H_ #include"theory_diff_logic.h" namespace smt { class utvpi_tester { ast_manager& m; arith_util a; ptr_vector m_todo; ast_mark m_mark; obj_map m_coeff_map; rational m_weight; vector > m_terms; public: utvpi_tester(ast_manager& m); // test if formula is in the Horn inequality fragment: bool operator()(expr* fml); bool operator()(unsigned num_fmls, expr* const* fmls); // linearize inequality/equality bool linearize(expr* e); bool linearize(expr* e1, expr* e2); // retrieve linearization vector > const& get_linearization() const; rational const& get_weight() const { return m_weight; } private: bool linearize(); }; template class theory_utvpi : public theory, private Ext { typedef typename Ext::numeral numeral; typedef theory_var th_var; typedef svector th_var_vector; typedef vector > coeffs; class assignment_trail; class parent_trail; struct GExt : public Ext { typedef std::pair explanation; }; class atom { protected: bool_var m_bvar; bool m_true; int m_pos; int m_neg; public: atom(bool_var bv, int pos, int neg) : m_bvar(bv), m_true(false), m_pos(pos), m_neg(neg) {} ~atom() {} bool_var get_bool_var() const { return m_bvar; } void assign_eh(bool is_true) { m_true = is_true; } int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } int get_pos() const { return m_pos; } int get_neg() const { return m_neg; } std::ostream& display(theory_utvpi const& th, std::ostream& out) const; }; typedef svector atoms; struct scope { unsigned m_atoms_lim; unsigned m_asserted_atoms_lim; unsigned m_asserted_qhead_old; }; struct stats { unsigned m_num_conflicts; unsigned m_num_assertions; unsigned m_num_core2th_eqs; unsigned m_num_core2th_diseqs; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; // Functor used to collect the proofs for a conflict due to // a negative cycle. class nc_functor { literal_vector m_antecedents; unsigned_vector m_coeffs; theory_utvpi& m_super; public: nc_functor(theory_utvpi& s) : m_super(s) {} void reset() { m_antecedents.reset(); m_coeffs.reset(); } literal_vector const& get_lits() const { return m_antecedents; } unsigned_vector const& get_coeffs() const { return m_coeffs; } void operator()(std::pair const & ex) { if (ex.first != null_literal) { m_antecedents.push_back(ex.first); m_coeffs.push_back(ex.second); } } void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { m_super.new_edge(src, dst, num_edges, edges); } }; stats m_stats; smt_params m_params; arith_util a; arith_eq_adapter m_arith_eq_adapter; th_var m_zero; //cache the variable representing the zero variable. dl_graph m_graph; nc_functor m_nc_functor; atoms m_atoms; unsigned_vector m_asserted_atoms; // set of asserted atoms unsigned m_asserted_qhead; u_map m_bool_var2atom; svector m_scopes; double m_agility; bool m_lia; bool m_lra; bool m_non_utvpi_exprs; utvpi_tester m_test; arith_factory * m_factory; rational m_delta; // Set a conflict due to a negative cycle. void set_conflict(); void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {} // Create a new theory variable. virtual th_var mk_var(enode* n); virtual th_var mk_var(expr* n); void compute_delta(); void found_non_utvpi_expr(expr * n); bool is_interpreted(app* n) const { return n->get_family_id() == get_family_id(); } public: theory_utvpi(ast_manager& m); virtual ~theory_utvpi(); virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_utvpi, get_manager()); } virtual char const * get_name() const { return "utvpi-logic"; } /** \brief See comment in theory::mk_eq_atom */ virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); } virtual void init(context * ctx); virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); virtual void internalize_eq_eh(app * atom, bool_var v); virtual void assign_eh(bool_var v, bool is_true); virtual void new_eq_eh(th_var v1, th_var v2) { m_stats.m_num_core2th_eqs++; m_arith_eq_adapter.new_eq_eh(v1, v2); } virtual bool use_diseqs() const { return true; } virtual void new_diseq_eh(th_var v1, th_var v2) { m_arith_eq_adapter.new_diseq_eh(v1, v2); } virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual void restart_eh() { m_arith_eq_adapter.restart_eh(); } virtual void relevant_eh(app* e) {} virtual void init_search_eh() { m_arith_eq_adapter.init_search_eh(); } virtual final_check_status final_check_eh(); virtual bool is_shared(th_var v) const { return false; } virtual bool can_propagate() { SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); return m_asserted_qhead != m_asserted_atoms.size(); } virtual void propagate(); virtual justification * why_is_diseq(th_var v1, th_var v2) { UNREACHABLE(); return 0; } virtual void reset_eh(); virtual void init_model(model_generator & m); virtual model_value_proc * mk_value(enode * n, model_generator & mg); virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const { return true; } virtual void display(std::ostream & out) const; virtual void collect_statistics(::statistics & st) const; private: rational mk_value(theory_var v, bool is_int); bool is_parity_ok(unsigned v) const; void enforce_parity(); void validate_model(); bool eval(expr* e); rational eval_num(expr* e); bool check_z_consistency(); virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { m_stats.m_num_core2th_eqs++; new_eq_or_diseq(true, v1, v2, j); } virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { m_stats.m_num_core2th_diseqs++; new_eq_or_diseq(false, v1, v2, j); } void negate(coeffs& coeffs, rational& weight); numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); void del_atoms(unsigned old_size); bool propagate_atom(atom const& a); th_var mk_term(app* n); th_var mk_num(app* n, rational const& r); bool is_consistent() const; th_var expand(bool pos, th_var v, rational & k); void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); th_var get_zero(sort* s) const { return m_zero; } th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } void inc_conflicts(); edge_id add_ineq(vector > const& terms, numeral const& weight, literal l); bool enable_edge(edge_id id); th_var to_var(th_var v) const { return 2*v; } th_var from_var(th_var v) const { return v/2; } th_var pos(th_var v) const { return v & 0xFFFFFFFE; } th_var neg(th_var v) const { return v | 0x1; } }; typedef theory_utvpi theory_rutvpi; typedef theory_utvpi theory_iutvpi; }; #endif /* THEORY_UTVPI_H_ */ z3-z3-4.4.1/src/smt/theory_utvpi_def.h000066400000000000000000000776421260446376700175670ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_utvpi_def.h Abstract: Implementation of UTVPI solver. Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: 1. introduce x^+ and x^-, such that 2*x := x^+ - x^- 2. rewrite constraints as follows: x - y <= k => x^+ - y^+ <= k y^- - x^- <= k x <= k => x^+ - x^- <= 2k x + y <= k => x^+ - y^- <= k y^+ - x^- <= k - x - y <= k => x^- - y^+ <= k y^- - x^+ <= k 3. Solve for x^+ and x^- 4. Check parity condition for integers (see Lahiri and Musuvathi 05) This checks if x^+ and x^- are in the same component but of different parities. 5. Enforce parity on variables. This checks if x^+ and x^- have different parities. If they have different parities, the assignment to one of the variables is decremented (choose the variable that is not tightly constrained with 0). The process that adjusts parities converges: Suppose we break a parity of a different variable y while fixing x's parity. A cyclic breaking/fixing of parities implies there is a strongly connected component between x, y and the two polarities of the variables. This contradicts the test in 4. 6. extract model for M(x) := (M(x^+)- M(x^-))/2 --*/ #ifndef THEORY_UTVPI_DEF_H_ #define THEORY_UTVPI_DEF_H_ #include "theory_utvpi.h" #include "heap.h" #include "ast_pp.h" #include "smt_context.h" namespace smt { template theory_utvpi::theory_utvpi(ast_manager& m): theory(m.mk_family_id("arith")), a(m), m_arith_eq_adapter(*this, m_params, a), m_zero(null_theory_var), m_nc_functor(*this), m_asserted_qhead(0), m_agility(0.5), m_lia(false), m_lra(false), m_non_utvpi_exprs(false), m_test(m), m_factory(0) { } template theory_utvpi::~theory_utvpi() { reset_eh(); } template std::ostream& theory_utvpi::atom::display(theory_utvpi const& th, std::ostream& out) const { context& ctx = th.get_context(); lbool asgn = ctx.get_assignment(m_bvar); bool sign = (l_undef == l_false); return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; if (l_undef == asgn) { out << "unassigned\n"; } else { th.m_graph.display_edge(out, get_asserted_edge()); } return out; } template theory_var theory_utvpi::mk_var(enode* n) { th_var v = theory::mk_var(n); TRACE("utvpi", tout << v << " " << mk_pp(n->get_owner(), get_manager()) << "\n";); m_graph.init_var(to_var(v)); m_graph.init_var(neg(to_var(v))); get_context().attach_th_var(n, this, v); return v; } template theory_var theory_utvpi::mk_var(expr* n) { context & ctx = get_context(); enode* e = 0; th_var v = null_theory_var; m_lia |= a.is_int(n); m_lra |= a.is_real(n); if (!is_app(n)) { return v; } if (ctx.e_internalized(n)) { e = ctx.get_enode(n); v = e->get_th_var(get_id()); } else { ctx.internalize(n, false); e = ctx.get_enode(n); } if (v == null_theory_var) { v = mk_var(e); } if (is_interpreted(to_app(n))) { found_non_utvpi_expr(n); } return v; } template void theory_utvpi::reset_eh() { m_graph .reset(); m_zero = null_theory_var; m_atoms .reset(); m_asserted_atoms .reset(); m_stats .reset(); m_scopes .reset(); m_asserted_qhead = 0; m_agility = 0.5; m_lia = false; m_lra = false; m_non_utvpi_exprs = false; theory::reset_eh(); } template void theory_utvpi::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) { rational k; th_var s = expand(true, v1, k); th_var t = expand(false, v2, k); context& ctx = get_context(); ast_manager& m = get_manager(); if (s == t) { if (is_eq != k.is_zero()) { // conflict 0 /= k; inc_conflicts(); ctx.set_conflict(&eq_just); } } else { // // Create equality ast, internalize_atom // assign the corresponding equality literal. // app_ref eq(m), s2(m), t2(m); app* s1 = get_enode(s)->get_owner(); app* t1 = get_enode(t)->get_owner(); s2 = a.mk_sub(t1, s1); t2 = a.mk_numeral(k, m.get_sort(s2.get())); // t1 - s1 = k eq = m.mk_eq(s2.get(), t2.get()); TRACE("utvpi", tout << v1 << " .. " << v2 << "\n"; tout << mk_pp(eq.get(), m) <<"\n";); if (!internalize_atom(eq.get(), false)) { UNREACHABLE(); } literal l(ctx.get_literal(eq.get())); if (!is_eq) { l = ~l; } ctx.assign(l, b_justification(&eq_just), false); } } template void theory_utvpi::inc_conflicts() { m_stats.m_num_conflicts++; if (m_params.m_arith_adaptive) { double g = m_params.m_arith_adaptive_propagation_threshold; m_agility = m_agility*g + 1 - g; } } template void theory_utvpi::set_conflict() { inc_conflicts(); literal_vector const& lits = m_nc_functor.get_lits(); context & ctx = get_context(); IF_VERBOSE(2, verbose_stream() << "conflict:\n"; for (unsigned i = 0; i < lits.size(); ++i) { ast_manager& m = get_manager(); expr_ref e(m); ctx.literal2expr(lits[i], e); verbose_stream() << mk_pp(e, m) << "\n"; } verbose_stream() << "\n";); TRACE("utvpi", tout << "conflict: "; for (unsigned i = 0; i < lits.size(); ++i) { ctx.display_literal_info(tout, lits[i]); } tout << "\n"; ); if (m_params.m_arith_dump_lemmas) { char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"; ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); } vector params; if (get_manager().proofs_enabled()) { params.push_back(parameter(symbol("farkas"))); for (unsigned i = 0; i < m_nc_functor.get_coeffs().size(); ++i) { params.push_back(parameter(rational(m_nc_functor.get_coeffs()[i]))); } } ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); m_nc_functor.reset(); } template void theory_utvpi::found_non_utvpi_expr(expr* n) { if (!m_non_utvpi_exprs) { std::stringstream msg; msg << "found non utvpi logic expression:\n" << mk_pp(n, get_manager()) << "\n"; TRACE("utvpi", tout << msg.str();); warning_msg(msg.str().c_str()); get_context().push_trail(value_trail(m_non_utvpi_exprs)); m_non_utvpi_exprs = true; } } template void theory_utvpi::init(context* ctx) { theory::init(ctx); m_zero = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); } /** \brief Create negated literal. The negation of: E <= 0 -E + epsilon <= 0 or -E + 1 <= 0 */ template void theory_utvpi::negate(coeffs& coeffs, rational& weight) { for (unsigned i = 0; i < coeffs.size(); ++i) { coeffs[i].second.neg(); } weight.neg(); } template typename theory_utvpi::numeral theory_utvpi::mk_weight(bool is_real, bool is_strict, rational const& w) const { if (is_strict) { return numeral(w) + (is_real?Ext::m_epsilon:numeral(1)); } else { return numeral(w); } } template void theory_utvpi::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) { coeffs.reset(); w = m_test.get_weight(); for (unsigned i = 0; i < terms.size(); ++i) { coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second)); } } template void theory_utvpi::internalize_eq_eh(app * atom, bool_var v) { context & ctx = get_context(); app * lhs = to_app(atom->get_arg(0)); app * rhs = to_app(atom->get_arg(1)); if (a.is_numeral(rhs)) { std::swap(rhs, lhs); } if (!a.is_numeral(rhs)) { return; } if (a.is_add(lhs) || a.is_sub(lhs)) { // force axioms for (= (+ x y) k) // this is necessary because (+ x y) is not expressible as a utvpi term. m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs)); } } template bool theory_utvpi::internalize_atom(app * n, bool) { context & ctx = get_context(); if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) { found_non_utvpi_expr(n); return false; } SASSERT(!ctx.b_internalized(n)); expr* e1 = n->get_arg(0), *e2 = n->get_arg(1); if (a.is_ge(n) || a.is_gt(n)) { std::swap(e1, e2); } bool is_strict = a.is_gt(n) || a.is_lt(n); bool cl = m_test.linearize(e1, e2); if (!cl) { found_non_utvpi_expr(n); return false; } rational w; coeffs coeffs; mk_coeffs(m_test.get_linearization(), coeffs, w); bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); literal l(bv); numeral w1 = mk_weight(a.is_real(e1), is_strict, w); edge_id pos = add_ineq(coeffs, w1, l); negate(coeffs, w); numeral w2 = mk_weight(a.is_real(e1), !is_strict, w); edge_id neg = add_ineq(coeffs, w2, ~l); m_bool_var2atom.insert(bv, m_atoms.size()); m_atoms.push_back(atom(bv, pos, neg)); TRACE("utvpi", tout << mk_pp(n, get_manager()) << "\n"; m_graph.display_edge(tout << "pos: ", pos); m_graph.display_edge(tout << "neg: ", neg); ); return true; } template bool theory_utvpi::internalize_term(app * term) { bool result = null_theory_var != mk_term(term); CTRACE("utvpi", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); return result; } template void theory_utvpi::assign_eh(bool_var v, bool is_true) { m_stats.m_num_assertions++; unsigned idx = m_bool_var2atom.find(v); SASSERT(get_context().get_assignment(v) != l_undef); SASSERT((get_context().get_assignment(v) == l_true) == is_true); m_atoms[idx].assign_eh(is_true); m_asserted_atoms.push_back(idx); } template void theory_utvpi::push_scope_eh() { theory::push_scope_eh(); m_graph.push(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_atoms_lim = m_atoms.size(); s.m_asserted_atoms_lim = m_asserted_atoms.size(); s.m_asserted_qhead_old = m_asserted_qhead; } template void theory_utvpi::pop_scope_eh(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; del_atoms(s.m_atoms_lim); m_asserted_atoms.shrink(s.m_asserted_atoms_lim); m_asserted_qhead = s.m_asserted_qhead_old; m_scopes.shrink(new_lvl); m_graph.pop(num_scopes); theory::pop_scope_eh(num_scopes); } template final_check_status theory_utvpi::final_check_eh() { SASSERT(is_consistent()); if (can_propagate()) { propagate(); return FC_CONTINUE; } else if (!check_z_consistency()) { return FC_CONTINUE; } else if (m_non_utvpi_exprs) { return FC_GIVEUP; } else { return FC_DONE; } } template bool theory_utvpi::check_z_consistency() { int_vector scc_id; m_graph.compute_zero_edge_scc(scc_id); unsigned sz = get_num_vars(); for (unsigned i = 0; i < sz; ++i) { enode* e = get_enode(i); if (!a.is_int(e->get_owner())) { continue; } th_var v1 = to_var(i); th_var v2 = neg(v1); rational r1 = m_graph.get_assignment(v1).get_rational(); rational r2 = m_graph.get_assignment(v2).get_rational(); SASSERT(r1.is_int()); SASSERT(r2.is_int()); if (r1.is_even() == r2.is_even()) { continue; } if (scc_id[v1] != scc_id[v2]) { continue; } if (scc_id[v1] == -1) { continue; } // they are in the same SCC and have different parities => contradiction. m_nc_functor.reset(); VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, UINT_MAX, m_nc_functor)); VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, UINT_MAX, m_nc_functor)); IF_VERBOSE(1, verbose_stream() << "parity conflict " << mk_pp(e->get_owner(), get_manager()) << "\n";); set_conflict(); return false; } return true; } template void theory_utvpi::display(std::ostream& out) const { for (unsigned i = 0; i < m_atoms.size(); ++i) { m_atoms[i].display(*this, out); out << "\n"; } m_graph.display(out); } template void theory_utvpi::collect_statistics(::statistics& st) const { st.update("utvpi conflicts", m_stats.m_num_conflicts); st.update("utvpi asserts", m_stats.m_num_assertions); st.update("core->utvpi eqs", m_stats.m_num_core2th_eqs); st.update("core->utvpi diseqs", m_stats.m_num_core2th_diseqs); m_arith_eq_adapter.collect_statistics(st); m_graph.collect_statistics(st); } template void theory_utvpi::del_atoms(unsigned old_size) { typename atoms::iterator begin = m_atoms.begin() + old_size; typename atoms::iterator it = m_atoms.end(); while (it != begin) { --it; m_bool_var2atom.erase(it->get_bool_var()); } m_atoms.shrink(old_size); } template void theory_utvpi::propagate() { bool consistent = true; while (consistent && can_propagate()) { unsigned idx = m_asserted_atoms[m_asserted_qhead]; m_asserted_qhead++; consistent = propagate_atom(m_atoms[idx]); } } template bool theory_utvpi::propagate_atom(atom const& a) { context& ctx = get_context(); TRACE("utvpi", a.display(*this, tout); tout << "\n";); if (ctx.inconsistent()) { return false; } int edge_id = a.get_asserted_edge(); if (!enable_edge(edge_id)) { m_graph.traverse_neg_cycle2(m_params.m_arith_stronger_lemmas, m_nc_functor); set_conflict(); return false; } return true; } template theory_var theory_utvpi::mk_term(app* n) { TRACE("utvpi", tout << mk_pp(n, get_manager()) << "\n";); context& ctx = get_context(); bool cl = m_test.linearize(n); if (!cl) { found_non_utvpi_expr(n); return null_theory_var; } coeffs coeffs; rational w; mk_coeffs(m_test.get_linearization(), coeffs, w); if (coeffs.empty()) { return mk_num(n, w); } if (coeffs.size() == 1 && coeffs[0].second.is_one()) { return coeffs[0].first; } if (coeffs.size() == 2) { // do not create an alias. return null_theory_var; } for (unsigned i = 0; i < n->get_num_args(); ++i) { mk_term(to_app(n->get_arg(i))); } th_var target = mk_var(ctx.mk_enode(n, false, false, true)); coeffs.push_back(std::make_pair(target, rational(-1))); VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); negate(coeffs, w); VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); return target; } template theory_var theory_utvpi::mk_num(app* n, rational const& r) { theory_var v = null_theory_var; context& ctx = get_context(); if (r.is_zero()) { v = m_zero; } else if (ctx.e_internalized(n)) { enode* e = ctx.get_enode(n); v = e->get_th_var(get_id()); SASSERT(v != null_theory_var); } else { v = mk_var(ctx.mk_enode(n, false, false, true)); // v = k: v <= k k <= v coeffs coeffs; coeffs.push_back(std::make_pair(v, rational(-1))); VERIFY(enable_edge(add_ineq(coeffs, numeral(r), null_literal))); coeffs.back().second.neg(); VERIFY(enable_edge(add_ineq(coeffs, numeral(-r), null_literal))); } return v; } template theory_var theory_utvpi::expand(bool pos, th_var v, rational & k) { context& ctx = get_context(); enode* e = get_enode(v); expr* x, *y; rational r; for (;;) { app* n = e->get_owner(); if (a.is_add(n, x, y)) { if (a.is_numeral(x, r)) { e = ctx.get_enode(y); } else if (a.is_numeral(y, r)) { e = ctx.get_enode(x); } v = e->get_th_var(get_id()); SASSERT(v != null_theory_var); if (v == null_theory_var) { break; } if (pos) { k += r; } else { k -= r; } } else { break; } } return v; } // m_graph(source, target, weight, ex); // target - source <= weight template edge_id theory_utvpi::add_ineq(vector > const& terms, numeral const& weight, literal l) { SASSERT(terms.size() <= 2); SASSERT(terms.size() < 1 || terms[0].second.is_one() || terms[0].second.is_minus_one()); SASSERT(terms.size() < 2 || terms[1].second.is_one() || terms[1].second.is_minus_one()); th_var v1 = null_theory_var, v2 = null_theory_var; bool pos1 = true, pos2 = true; if (terms.size() >= 1) { v1 = terms[0].first; pos1 = terms[0].second.is_one(); SASSERT(v1 != null_theory_var); SASSERT(pos1 || terms[0].second.is_minus_one()); } if (terms.size() >= 2) { v2 = terms[1].first; pos2 = terms[1].second.is_one(); SASSERT(v1 != null_theory_var); SASSERT(pos2 || terms[1].second.is_minus_one()); } // TRACE("utvpi", tout << (pos1?"$":"-$") << v1 << (pos2?" + $":" - $") << v2 << " + " << weight << " <= 0\n";); edge_id id = m_graph.get_num_edges(); th_var w1 = to_var(v1), w2 = to_var(v2); if (terms.size() == 1 && pos1) { m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2)); m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2)); } else if (terms.size() == 1 && !pos1) { m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2)); m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2)); } else if (pos1 && pos2) { m_graph.add_edge(neg(w2), pos(w1), -weight, std::make_pair(l,1)); m_graph.add_edge(neg(w1), pos(w2), -weight, std::make_pair(l,1)); } else if (pos1 && !pos2) { m_graph.add_edge(pos(w2), pos(w1), -weight, std::make_pair(l,1)); m_graph.add_edge(neg(w1), neg(w2), -weight, std::make_pair(l,1)); } else if (!pos1 && pos2) { m_graph.add_edge(neg(w2), neg(w1), -weight, std::make_pair(l,1)); m_graph.add_edge(pos(w1), pos(w2), -weight, std::make_pair(l,1)); } else { m_graph.add_edge(pos(w1), neg(w2), -weight, std::make_pair(l,1)); m_graph.add_edge(pos(w2), neg(w1), -weight, std::make_pair(l,1)); } return id; } template bool theory_utvpi::enable_edge(edge_id id) { return (id == null_edge_id) || (m_graph.enable_edge(id) && m_graph.enable_edge(id+1)); } template bool theory_utvpi::is_consistent() const { return m_graph.is_feasible(); } template bool theory_utvpi::is_parity_ok(unsigned i) const { th_var v1 = to_var(i); th_var v2 = neg(v1); rational r1 = m_graph.get_assignment(v1).get_rational(); rational r2 = m_graph.get_assignment(v2).get_rational(); return r1.is_even() == r2.is_even(); } /** \brief adjust values for variables in the difference graph such that for variables of integer sort it is the case that x^+ - x^- is even. The informal justification for the procedure enforce_parity relies on a set of properties: 1. the graph does not contain a strongly connected component where x^+ and x+- are connected. They can be independently changed. This is checked prior to enforce_parity. 2. When x^+ - x^- is odd, the values are adjusted by first decrementing the value of x^+, provided x^- is not 0-dependent. Otherwise decrement x^-. x^- is "0-dependent" if there is a set of tight inequalities from x^+ to x^-. 3. The affinity to x^+ (the same component of x^+) ensures that the parity is broken only a finite number of times when traversing that component. Namely, suppose that the parity of y gets broken when fixing 'x'. Then first note that 'y' cannot be equal to 'x'. If it were, then we have a state where: parity(x^+) != parity(x^-) and parity(y^+) == parity(y^-) but x^+ and y^+ are tightly connected and x^- and y^- are also tightly connected using two copies of the same inequalities. This is a contradiction. Thus, 'y' cannot be equal to 'x' if 'y's parity gets broken when repairing 'x'. */ template void theory_utvpi::enforce_parity() { unsigned_vector todo; unsigned sz = get_num_vars(); for (unsigned i = 0; i < sz; ++i) { enode* e = get_enode(i); if (a.is_int(e->get_owner()) && !is_parity_ok(i)) { todo.push_back(i); } } if (todo.empty()) { return; } while (!todo.empty()) { unsigned i = todo.back(); todo.pop_back(); if (is_parity_ok(i)) { continue; } th_var v1 = to_var(i); th_var v2 = neg(v1); int_vector zero_v; m_graph.compute_zero_succ(v1, zero_v); for (unsigned j = 0; j < zero_v.size(); ++j) { if (zero_v[j] == v2) { zero_v.reset(); m_graph.compute_zero_succ(v2, zero_v); break; } } TRACE("utvpi", tout << "Disparity: " << v1 << "\n"; for (unsigned j = 0; j < zero_v.size(); ++j) { tout << "decrement: " << zero_v[j] << "\n"; }); for (unsigned j = 0; j < zero_v.size(); ++j) { int v = zero_v[j]; m_graph.inc_assignment(v, numeral(-1)); th_var k = from_var(v); if (!is_parity_ok(k)) { todo.push_back(k); } } } SASSERT(m_graph.is_feasible()); DEBUG_CODE( for (unsigned i = 0; i < sz; ++i) { enode* e = get_enode(i); if (a.is_int(e->get_owner()) && !is_parity_ok(i)) { IF_VERBOSE(0, verbose_stream() << "disparities not fixed\n";); UNREACHABLE(); } }); } // models: template void theory_utvpi::init_model(model_generator & m) { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); enforce_parity(); m_graph.set_to_zero(to_var(m_zero), neg(to_var(m_zero))); compute_delta(); DEBUG_CODE(validate_model();); } template void theory_utvpi::validate_model() { context& ctx = get_context(); unsigned sz = m_atoms.size(); for (unsigned i = 0; i < sz; ++i) { bool_var b = m_atoms[i].get_bool_var(); if (!ctx.is_relevant(b)) { continue; } bool ok = true; expr* e = ctx.bool_var2expr(b); lbool assign = ctx.get_assignment(b); switch(assign) { case l_true: ok = eval(e); break; case l_false: ok = !eval(e); break; default: break; } CTRACE("utvpi", !ok, tout << "validation failed:\n"; tout << "Assignment: " << assign << "\n"; m_atoms[i].display(*this, tout); tout << "\n"; display(tout); m_graph.display_agl(tout); ); if (!ok) { std::cout << "validation failed:\n"; std::cout << "Assignment: " << assign << "\n"; m_atoms[i].display(*this, std::cout); std::cout << "\n"; display(std::cout); m_graph.display_agl(std::cout); } // CTRACE("utvpi", ok, tout << "validation success: " << mk_pp(e, get_manager()) << "\n";); SASSERT(ok); } } template bool theory_utvpi::eval(expr* e) { expr* e1, *e2; if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) { return eval_num(e1) <= eval_num(e2); } if (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { return eval_num(e1) < eval_num(e2); } if (get_manager().is_eq(e, e1, e2)) { return eval_num(e1) == eval_num(e2); } TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); return false; } template rational theory_utvpi::eval_num(expr* e) { rational r; expr* e1, *e2; if (a.is_numeral(e, r)) { return r; } if (a.is_sub(e, e1, e2)) { return eval_num(e1) - eval_num(e2); } if (a.is_add(e)) { r.reset(); for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { r += eval_num(to_app(e)->get_arg(i)); } return r; } if (a.is_mul(e)) { r = rational(1); for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { r *= eval_num(to_app(e)->get_arg(i)); } return r; } if (a.is_uminus(e, e1)) { return -eval_num(e1); } if (a.is_to_real(e, e1)) { return eval_num(e1); } if (is_uninterp_const(e)) { return mk_value(mk_var(e), a.is_int(e)); } TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); UNREACHABLE(); return rational(0); } template rational theory_utvpi::mk_value(th_var v, bool is_int) { SASSERT(v != null_theory_var); numeral val1 = m_graph.get_assignment(to_var(v)); numeral val2 = m_graph.get_assignment(neg(to_var(v))); numeral val = val1 - val2; rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational()); num = num/rational(2); SASSERT(!is_int || num.is_int()); TRACE("utvpi", expr* n = get_enode(v)->get_owner(); tout << mk_pp(n, get_manager()) << " |-> (" << val1 << " - " << val2 << ")/2 = " << num << "\n";); return num; } template model_value_proc * theory_utvpi::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); bool is_int = a.is_int(n->get_owner()); rational num = mk_value(v, is_int); TRACE("utvpi", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); return alloc(expr_wrapper_proc, m_factory->mk_value(num, is_int)); } /** \brief Compute numeral values for the infinitesimals to satisfy the inequalities. */ template void theory_utvpi::compute_delta() { m_delta = rational(1); unsigned sz = m_graph.get_num_edges(); for (unsigned i = 0; i < sz; ++i) { if (!m_graph.is_enabled(i)) { continue; } numeral w = m_graph.get_weight(i); numeral tgt = m_graph.get_assignment(m_graph.get_target(i)); numeral src = m_graph.get_assignment(m_graph.get_source(i)); numeral b = tgt - src - w; SASSERT(b.is_nonpos()); rational eps_r = b.get_infinitesimal(); // Given: b <= 0 // suppose that 0 < b.eps // then we have 0 > b.num // then delta must ensure: // 0 >= b.num + delta*b.eps // <=> // -b.num/b.eps >= delta if (eps_r.is_pos()) { rational num_r = -b.get_rational(); SASSERT(num_r.is_pos()); rational new_delta = num_r/eps_r; if (new_delta < m_delta) { m_delta = new_delta; } } } } }; #endif z3-z3-4.4.1/src/smt/theory_wmaxsat.cpp000066400000000000000000000210751260446376700176060ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_wmaxsat.h Abstract: Weighted Max-SAT theory plugin. Author: Nikolaj Bjorner (nbjorner) 2013-11-05 Notes: --*/ #include #include "smt_context.h" #include "ast_pp.h" #include "theory_wmaxsat.h" namespace smt { theory_wmaxsat::theory_wmaxsat(ast_manager& m, filter_model_converter& mc): theory(m.mk_family_id("weighted_maxsat")), m_mc(mc), m_vars(m), m_fmls(m), m_zweights(m_mpz), m_old_values(m_mpz), m_zcost(m_mpz), m_zmin_cost(m_mpz), m_found_optimal(false), m_propagate(false), m_normalize(false) {} theory_wmaxsat::~theory_wmaxsat() { m_old_values.reset(); } /** \brief return the complement of variables that are currently assigned. */ void theory_wmaxsat::get_assignment(svector& result) { result.reset(); if (!m_found_optimal) { for (unsigned i = 0; i < m_vars.size(); ++i) { result.push_back(false); } } else { std::sort(m_cost_save.begin(), m_cost_save.end()); for (unsigned i = 0,j = 0; i < m_vars.size(); ++i) { if (j < m_cost_save.size() && m_cost_save[j] == static_cast(i)) { result.push_back(false); ++j; } else { result.push_back(true); } } } TRACE("opt", tout << "cost save: "; for (unsigned i = 0; i < m_cost_save.size(); ++i) { tout << m_cost_save[i] << " "; } tout << "\nvars: "; for (unsigned i = 0; i < m_vars.size(); ++i) { tout << mk_pp(m_vars[i].get(), get_manager()) << " "; } tout << "\nassignment: "; for (unsigned i = 0; i < result.size(); ++i) { tout << result[i] << " "; } tout << "\n";); } void theory_wmaxsat::init_search_eh() { m_propagate = true; } bool_var theory_wmaxsat::assert_weighted(expr* fml, rational const& w) { context & ctx = get_context(); ast_manager& m = get_manager(); app_ref var(m), wfml(m); var = m.mk_fresh_const("w", m.mk_bool_sort()); m_mc.insert(var->get_decl()); wfml = m.mk_or(var, fml); ctx.assert_expr(wfml); m_rweights.push_back(w); m_vars.push_back(var); m_fmls.push_back(fml); m_assigned.push_back(false); m_rmin_cost += w; m_normalize = true; return register_var(var, true); } bool_var theory_wmaxsat::register_var(app* var, bool attach) { context & ctx = get_context(); bool_var bv; SASSERT(!ctx.e_internalized(var)); enode* x = ctx.mk_enode(var, false, true, true); if (ctx.b_internalized(var)) { bv = ctx.get_bool_var(var); } else { bv = ctx.mk_bool_var(var); } ctx.set_enode_flag(bv, true); if (attach) { ctx.set_var_theory(bv, get_id()); theory_var v = mk_var(x); ctx.attach_th_var(x, this, v); m_bool2var.insert(bv, v); SASSERT(v == static_cast(m_var2bool.size())); m_var2bool.push_back(bv); SASSERT(ctx.bool_var2enode(bv)); } return bv; } rational const& theory_wmaxsat::get_min_cost() { unsynch_mpq_manager mgr; scoped_mpq q(mgr); mgr.set(q, m_zmin_cost, m_den.to_mpq().numerator()); m_rmin_cost = rational(q); return m_rmin_cost; } void theory_wmaxsat::assign_eh(bool_var v, bool is_true) { TRACE("opt", tout << "Assign " << mk_pp(m_vars[m_bool2var[v]].get(), get_manager()) << " " << is_true << "\n";); if (is_true) { if (m_normalize) normalize(); context& ctx = get_context(); theory_var tv = m_bool2var[v]; if (m_assigned[tv]) return; scoped_mpz w(m_mpz); w = m_zweights[tv]; ctx.push_trail(numeral_trail(m_zcost, m_old_values)); ctx.push_trail(push_back_vector >(m_costs)); ctx.push_trail(value_trail(m_assigned[tv])); m_zcost += w; m_costs.push_back(tv); m_assigned[tv] = true; if (m_zcost > m_zmin_cost) { block(); } } } final_check_status theory_wmaxsat::final_check_eh() { if (m_normalize) normalize(); return FC_DONE; } void theory_wmaxsat::reset_eh() { theory::reset_eh(); reset_local(); } void theory_wmaxsat::reset_local() { m_vars.reset(); m_fmls.reset(); m_rweights.reset(); m_rmin_cost.reset(); m_rcost.reset(); m_zweights.reset(); m_zcost.reset(); m_zmin_cost.reset(); m_cost_save.reset(); m_bool2var.reset(); m_var2bool.reset(); m_propagate = false; m_found_optimal = false; m_assigned.reset(); } void theory_wmaxsat::propagate() { context& ctx = get_context(); for (unsigned i = 0; m_propagate && i < m_vars.size(); ++i) { bool_var bv = m_var2bool[i]; lbool asgn = ctx.get_assignment(bv); if (asgn == l_true) { assign_eh(bv, true); } } m_propagate = false; } bool theory_wmaxsat::is_optimal() const { return !m_found_optimal || m_zcost < m_zmin_cost; } expr_ref theory_wmaxsat::mk_block() { ++m_stats.m_num_blocks; ast_manager& m = get_manager(); expr_ref_vector disj(m); compare_cost compare_cost(*this); svector costs(m_costs); std::sort(costs.begin(), costs.end(), compare_cost); scoped_mpz weight(m_mpz); m_mpz.reset(weight); for (unsigned i = 0; i < costs.size() && m_mpz.lt(weight, m_zmin_cost); ++i) { weight += m_zweights[costs[i]]; disj.push_back(m.mk_not(m_vars[costs[i]].get())); } if (is_optimal()) { unsynch_mpq_manager mgr; scoped_mpq q(mgr); mgr.set(q, m_zmin_cost, m_den.to_mpq().numerator()); rational rw = rational(q); m_zmin_cost = weight; m_found_optimal = true; m_cost_save.reset(); m_cost_save.append(m_costs); TRACE("opt", tout << "costs: "; for (unsigned i = 0; i < m_costs.size(); ++i) { tout << mk_pp(get_enode(m_costs[i])->get_owner(), get_manager()) << " "; } tout << "\n"; get_context().display(tout); ); } expr_ref result(m.mk_or(disj.size(), disj.c_ptr()), m); TRACE("opt", tout << result << " weight: " << weight << "\n"; tout << "cost: " << m_zcost << " min-cost: " << m_zmin_cost << "\n";); return result; } expr_ref theory_wmaxsat::mk_optimal_block(svector const& ws, rational const& weight) { ast_manager& m = get_manager(); expr_ref_vector disj(m); rational new_w = weight*m_den; m_zmin_cost = new_w.to_mpq().numerator(); m_cost_save.reset(); for (unsigned i = 0; i < ws.size(); ++i) { bool_var bv = ws[i]; theory_var v = m_bool2var[bv]; m_cost_save.push_back(v); disj.push_back(m.mk_not(m_vars[v].get())); } expr_ref result(m.mk_or(disj.size(), disj.c_ptr()), m); return result; } void theory_wmaxsat::block() { if (m_vars.empty()) { return; } ++m_stats.m_num_blocks; ast_manager& m = get_manager(); context& ctx = get_context(); literal_vector lits; compare_cost compare_cost(*this); svector costs(m_costs); std::sort(costs.begin(), costs.end(), compare_cost); scoped_mpz weight(m_mpz); m_mpz.reset(weight); for (unsigned i = 0; i < costs.size() && weight < m_zmin_cost; ++i) { weight += m_zweights[costs[i]]; lits.push_back(~literal(m_var2bool[costs[i]])); } TRACE("opt", tout << "block: "; for (unsigned i = 0; i < lits.size(); ++i) { expr_ref tmp(m); ctx.literal2expr(lits[i], tmp); tout << tmp << " "; } tout << "\n"; ); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } void theory_wmaxsat::normalize() { m_den = rational::one(); for (unsigned i = 0; i < m_rweights.size(); ++i) { m_den = lcm(m_den, denominator(m_rweights[i])); } m_den = lcm(m_den, denominator(m_rmin_cost)); SASSERT(!m_den.is_zero()); m_zweights.reset(); for (unsigned i = 0; i < m_rweights.size(); ++i) { rational r = m_rweights[i]*m_den; SASSERT(r.is_int()); mpq const& q = r.to_mpq(); m_zweights.push_back(q.numerator()); } rational r = m_rcost* m_den; m_zcost = r.to_mpq().numerator(); r = m_rmin_cost * m_den; m_zmin_cost = r.to_mpq().numerator(); m_normalize = false; } }; z3-z3-4.4.1/src/smt/theory_wmaxsat.h000066400000000000000000000105271260446376700172530ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_wmaxsat.h Abstract: Weighted Max-SAT theory plugin. Author: Nikolaj Bjorner (nbjorner) 2013-11-05 Notes: --*/ #ifndef THEORY_WMAXSAT_H_ #define THEORY_WMAXSAT_H_ #include "smt_theory.h" #include "smt_clause.h" #include "filter_model_converter.h" namespace smt { class theory_wmaxsat : public theory { struct stats { unsigned m_num_blocks; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; filter_model_converter& m_mc; mutable unsynch_mpz_manager m_mpz; app_ref_vector m_vars; // Auxiliary variables per soft clause expr_ref_vector m_fmls; // Formulas per soft clause vector m_rweights; // weights of theory variables. scoped_mpz_vector m_zweights; scoped_mpz_vector m_old_values; svector m_costs; // set of asserted theory variables svector m_cost_save; // set of asserted theory variables rational m_rcost; // current sum of asserted costs rational m_rmin_cost; // current maximal cost assignment. scoped_mpz m_zcost; // current sum of asserted costs scoped_mpz m_zmin_cost; // current maximal cost assignment. bool m_found_optimal; u_map m_bool2var; // bool_var -> theory_var svector m_var2bool; // theory_var -> bool_var bool m_propagate; bool m_normalize; rational m_den; // lcm of denominators for rational weights. svector m_assigned; stats m_stats; public: theory_wmaxsat(ast_manager& m, filter_model_converter& mc); virtual ~theory_wmaxsat(); void get_assignment(svector& result); virtual void init_search_eh(); bool_var assert_weighted(expr* fml, rational const& w); bool_var register_var(app* var, bool attach); rational const& get_min_cost(); class numeral_trail : public trail { typedef scoped_mpz T; T & m_value; scoped_mpz_vector& m_old_values; public: numeral_trail(T & value, scoped_mpz_vector& old): m_value(value), m_old_values(old) { old.push_back(value); } virtual ~numeral_trail() { } virtual void undo(context & ctx) { m_value = m_old_values.back(); m_old_values.shrink(m_old_values.size() - 1); } }; virtual void assign_eh(bool_var v, bool is_true); virtual final_check_status final_check_eh(); virtual bool use_diseqs() const { return false; } virtual bool build_models() const { return false; } void reset_local(); virtual void reset_eh(); virtual theory * mk_fresh(context * new_ctx) { return 0; } virtual bool internalize_atom(app * atom, bool gate_ctx) { return false; } virtual bool internalize_term(app * term) { return false; } virtual void new_eq_eh(theory_var v1, theory_var v2) { } virtual void new_diseq_eh(theory_var v1, theory_var v2) { } virtual void collect_statistics(::statistics & st) const { st.update("wmaxsat num blocks", m_stats.m_num_blocks); } virtual bool can_propagate() { return m_propagate; } virtual void propagate(); bool is_optimal() const; expr_ref mk_block(); expr_ref mk_optimal_block(svector const& ws, rational const& weight); private: void block(); void normalize(); class compare_cost { theory_wmaxsat& m_th; public: compare_cost(theory_wmaxsat& t):m_th(t) {} bool operator() (theory_var v, theory_var w) const { return m_th.m_mpz.gt(m_th.m_zweights[v], m_th.m_zweights[w]); } }; }; }; #endif z3-z3-4.4.1/src/smt/user_plugin/000077500000000000000000000000001260446376700163535ustar00rootroot00000000000000z3-z3-4.4.1/src/smt/user_plugin/user_decl_plugin.cpp000066400000000000000000000053451260446376700224110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: user_decl_plugin.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-22. Revision History: --*/ #include"user_decl_plugin.h" #include"warning.h" user_decl_plugin::user_decl_plugin() { } void user_decl_plugin::finalize() { m_manager->dec_array_ref(m_kind2func.size(), m_kind2func.c_ptr()); m_manager->dec_array_ref(m_kind2sort.size(), m_kind2sort.c_ptr()); } decl_plugin * user_decl_plugin::mk_fresh() { user_decl_plugin * p = alloc(user_decl_plugin); // TODO copy sorts and other goodness return p; } sort * user_decl_plugin::mk_sort(symbol const & name) { unsigned kind = m_kind2sort.size(); sort * s = m_manager->mk_sort(name, sort_info(m_family_id, kind)); m_kind2sort.push_back(s); m_manager->inc_ref(s); if (!name.is_numerical()) { m_sort_names.push_back(builtin_name(name.bare_str(), static_cast(kind))); } return s; } func_decl * user_decl_plugin::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range) { unsigned kind = m_kind2func.size(); func_decl * f = m_manager->mk_func_decl(name, arity, domain, range, func_decl_info(m_family_id, kind)); m_kind2func.push_back(f); m_manager->inc_ref(f); if (!name.is_numerical()) { m_op_names.push_back(builtin_name(name.bare_str(), static_cast(kind))); } return f; } func_decl * user_decl_plugin::mk_value_decl(symbol const & name, sort * s) { func_decl * f = mk_func_decl(name, 0, 0, s); m_values.insert(f); return f; } sort * user_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { if (num_parameters > 0) { throw default_exception("invalid user theory sort"); return 0; } return m_kind2sort.get(k, 0); } func_decl * user_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { func_decl * f = m_kind2func.get(k, 0); if (num_parameters > 0 || f == 0) { throw default_exception("invalid user theory function operator"); return 0; } return f; } bool user_decl_plugin::is_value(app * v) const { return m_values.contains(v->get_decl()); } bool user_decl_plugin::is_value(func_decl * f) const { return m_values.contains(f); } void user_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.append(m_op_names); } void user_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { sort_names.append(m_sort_names); } z3-z3-4.4.1/src/smt/user_plugin/user_decl_plugin.h000066400000000000000000000030631260446376700220510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: user_decl_plugin.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-22. Revision History: --*/ #ifndef USER_DECL_PLUGIN_H_ #define USER_DECL_PLUGIN_H_ #include"ast.h" #include"obj_hashtable.h" class user_decl_plugin : public decl_plugin { ptr_vector m_kind2sort; ptr_vector m_kind2func; obj_hashtable m_values; svector m_op_names; svector m_sort_names; public: user_decl_plugin(); virtual ~user_decl_plugin() {} virtual void finalize(); virtual decl_plugin * mk_fresh(); sort * mk_sort(symbol const & name); func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range); func_decl * mk_value_decl(symbol const & name, sort * s); 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 bool is_value(app*) const; virtual bool is_unique_value(app * a) const { return is_value(a); } bool is_value(func_decl *) const; virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); }; #endif /* USER_DECL_PLUGIN_H_ */ z3-z3-4.4.1/src/smt/user_plugin/user_simplifier_plugin.cpp000066400000000000000000000043651260446376700236460ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: user_simplifier_plugin.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-22. Revision History: --*/ #include"user_simplifier_plugin.h" #include"ast_pp.h" #include"warning.h" user_simplifier_plugin::user_simplifier_plugin(symbol const & fname, ast_manager & m): simplifier_plugin(fname, m), m_owner(0), m_enabled(true), m_reduce_app_fptr(0), m_reduce_eq_fptr(0), m_reduce_distinct_fptr(0) { } simplifier_plugin * user_simplifier_plugin::mk_fresh() { ast_manager & m = get_manager(); user_simplifier_plugin * new_sp = alloc(user_simplifier_plugin, m.get_family_name(get_family_id()), m); new_sp->m_reduce_app_fptr = m_reduce_app_fptr; new_sp->m_reduce_eq_fptr = m_reduce_eq_fptr; new_sp->m_reduce_distinct_fptr = m_reduce_distinct_fptr; return new_sp; } bool user_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (m_reduce_app_fptr == 0 || !m_enabled) return false; expr * _result = 0; bool flag = m_reduce_app_fptr(m_owner, f, num_args, args, &_result); if (flag) { if (_result == 0) throw default_exception("invalid reduce_app callback: result is null"); result = _result; } return flag; } bool user_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) { if (m_reduce_eq_fptr == 0 || !m_enabled) return false; expr * _result = 0; bool flag = m_reduce_eq_fptr(m_owner, lhs, rhs, &_result); if (flag) { if (_result == 0) throw default_exception("invalid reduce_eq callback: result is null"); result = _result; } return flag; } bool user_simplifier_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) { if (m_reduce_distinct_fptr == 0 || !m_enabled) return false; expr * _result = 0; bool flag = m_reduce_distinct_fptr(m_owner, num_args, args, &_result); if (flag) { if (_result == 0) throw default_exception("invalid reduce_distinct callback: result is null"); result = _result; } return flag; } void user_simplifier_plugin::flush_caches() { } z3-z3-4.4.1/src/smt/user_plugin/user_simplifier_plugin.h000066400000000000000000000032301260446376700233010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: user_simplifier_plugin.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-22. Revision History: --*/ #ifndef USER_SIMPLIFIER_PLUGIN_H_ #define USER_SIMPLIFIER_PLUGIN_H_ #include"simplifier_plugin.h" typedef bool (*reduce_app_fptr)(void *, func_decl *, unsigned, expr * const *, expr **); typedef bool (*reduce_eq_fptr)(void *, expr *, expr *, expr **); typedef bool (*reduce_distinct_fptr)(void *, unsigned, expr * const *, expr **); class user_simplifier_plugin : public simplifier_plugin { void * m_owner; bool m_enabled; reduce_app_fptr m_reduce_app_fptr; reduce_eq_fptr m_reduce_eq_fptr; reduce_distinct_fptr m_reduce_distinct_fptr; public: user_simplifier_plugin(symbol const & fname, ast_manager & m); virtual simplifier_plugin * mk_fresh(); void set_reduce_app_fptr(reduce_app_fptr ptr) { m_reduce_app_fptr = ptr; } void set_reduce_eq_fptr(reduce_eq_fptr ptr) { m_reduce_eq_fptr = ptr; } void set_reduce_distinct_fptr(reduce_distinct_fptr ptr) { m_reduce_distinct_fptr = ptr; } void enable(bool flag) { m_enabled = flag; } void set_owner(void * ptr) { m_owner = ptr; } 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(); }; #endif /* USER_SIMPLIFIER_PLUGIN_H_ */ z3-z3-4.4.1/src/smt/user_plugin/user_smt_theory.cpp000066400000000000000000000551551260446376700223250ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: user_smt_theory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-22. Revision History: --*/ #include"smt_context.h" #include"user_smt_theory.h" #include"ast_pp.h" #include"smt_model_generator.h" #include"stats.h" #include"warning.h" namespace smt { // // value factory for user sorts. // // NB. This value factory for user theories // does not address theories where model // values are structured objects such as // arrays, records, or data-types. // class user_smt_theory_factory : public simple_factory { app* mk_value_core(unsigned const& val, sort* s) { return m_manager.mk_model_value(val, s); } public: user_smt_theory_factory(ast_manager& m, family_id fid): simple_factory(m, fid) {} }; user_theory::user_theory(ast_manager & m, smt_params const& p, void * ext_context, void * ext_data, char const * name, family_id fid, user_decl_plugin * dp, user_simplifier_plugin * sp): theory(fid), m_params(p), m_ext_context(ext_context), m_ext_data(ext_data), m_name(name), m_simplify_axioms(false), m_decl_plugin(dp), m_simplifier_plugin(sp), m_find(*this), m_trail_stack(*this), m_asserted_axioms(m), m_persisted_axioms(m), m_persisted_axioms_qhead(0), m_delete_fptr(0), m_new_app_fptr(0), m_new_elem_fptr(0), m_init_search_fptr(0), m_push_fptr(0), m_pop_fptr(0), m_restart_fptr(0), m_reset_fptr(0), m_final_check_fptr(0), m_new_eq_fptr(0), m_new_diseq_fptr(0), m_new_assignment_fptr(0), m_new_relevant_fptr(0), m_mk_fresh_fptr(0), m_delete_invoking(false), m_new_app_invoking(false), m_new_elem_invoking(false), m_init_search_invoking(false), m_push_invoking(false), m_pop_invoking(false), m_restart_invoking(false), m_reset_invoking(false), m_final_check_invoking(false), m_new_eq_invoking(false), m_new_diseq_invoking(false), m_new_assignment_invoking(false), m_new_relevant_invoking(false) { } user_theory::~user_theory() { if (m_delete_fptr != 0) { flet i(m_delete_invoking, true); m_delete_fptr(this); } } theory * user_theory::mk_fresh(context * new_ctx) { if (m_mk_fresh_fptr == 0) { throw default_exception("The mk_fresh_ext_data callback was not set for user theory, you must use Z3_theory_set_mk_fresh_ext_data_callback"); return 0; } user_simplifier_plugin * new_sp = static_cast(new_ctx->get_simplifier().get_plugin(get_family_id())); SASSERT(new_sp != 0); user_theory * new_th = alloc(user_theory, get_manager(), new_ctx->get_fparams(), m_ext_context, m_mk_fresh_fptr(this), get_name(), get_family_id(), m_decl_plugin, new_sp); new_sp->set_owner(new_th); new_th->m_delete_fptr = m_delete_fptr; new_th->m_new_app_fptr = m_new_app_fptr; new_th->m_new_elem_fptr = m_new_elem_fptr; new_th->m_init_search_fptr = m_init_search_fptr; new_th->m_push_fptr = m_push_fptr; new_th->m_pop_fptr = m_pop_fptr; new_th->m_restart_fptr = m_restart_fptr; new_th->m_reset_fptr = m_reset_fptr; new_th->m_final_check_fptr = m_final_check_fptr; new_th->m_new_eq_fptr = m_new_eq_fptr; new_th->m_new_diseq_fptr = m_new_diseq_fptr; new_th->m_new_assignment_fptr = m_new_assignment_fptr; new_th->m_new_relevant_fptr = m_new_relevant_fptr; new_th->m_mk_fresh_fptr = m_mk_fresh_fptr; return new_th; } void user_theory::assert_axiom_core(app* a) { if (m_asserted_axiom_set.contains(a)) { return; } m_asserted_axiom_set.insert(a); m_asserted_axioms.push_back(a); if (m_params.m_user_theory_persist_axioms) { m_persisted_axioms.push_back(a); } } /** TODO: discuss semantics of assert_axiom. Should we simplify axiom of not? */ void user_theory::assert_axiom(ast * axiom) { ++m_stats.m_num_user_axioms; TRACE("user_smt_theory", tout << mk_pp(axiom, get_manager()) << "\n";); if (!is_expr(axiom)) { throw default_exception("invalid expression"); } if (!get_manager().is_bool(to_expr(axiom))) { throw default_exception("invalid theory axiom: axioms must have Boolean sort"); } if (!m_new_eq_invoking && !m_new_diseq_invoking && !m_new_assignment_invoking && !m_new_relevant_invoking && !m_final_check_invoking) { throw default_exception("theory axioms can only be invoked during callbacks " "for new (dis)equalities/assignments and final check"); } context & ctx = get_context(); ast_manager & m = get_manager(); if (!is_app(axiom) || !to_app(axiom)->is_ground() || ctx.get_fparams().m_user_theory_preprocess_axioms) { asserted_formulas asf(m, ctx.get_fparams()); asf.assert_expr(to_app(axiom)); asf.reduce(); unsigned sz = asf.get_num_formulas(); unsigned qhead = asf.get_qhead(); while (qhead < sz) { expr * f = asf.get_formula(qhead); assert_axiom_core(to_app(f)); ++qhead; } } else { if (!m_simplify_axioms) { m_simplifier_plugin->enable(false); } expr_ref s_axiom(m); proof_ref pr(m); simplifier & s = ctx.get_simplifier(); s(to_app(axiom), s_axiom, pr); if (!is_app(s_axiom)) { throw default_exception("invalid theory axiom: axioms must be applications"); } axiom = s_axiom; m_simplifier_plugin->enable(true); assert_axiom_core(to_app(axiom)); } } void user_theory::assume_eq(ast * _lhs, ast * _rhs) { if (!is_expr(_lhs) || !is_expr(_rhs)) { throw default_exception("assume_eq must take expressions as arguments"); } expr* lhs = to_expr(_lhs); expr* rhs = to_expr(_rhs); ast_manager& m = get_manager(); context& ctx = get_context(); if (m.is_true(rhs)) { std::swap(lhs, rhs); } if (m.is_true(lhs)) { theory_var v2 = mk_var(rhs); if (v2 == null_theory_var) { throw default_exception("invalid assume eq: lhs or rhs is not a theory term"); } bool_var bv = ctx.get_bool_var(rhs); ctx.set_true_first_flag(bv); ctx.mark_as_relevant(get_enode(v2)); return; } if (m.is_bool(lhs)) { throw default_exception("assume_eq on Booleans must take 'true' as one of the arguments"); } theory_var v1 = mk_var(lhs); theory_var v2 = mk_var(rhs); if (v1 == null_theory_var || v2 == null_theory_var) { throw default_exception("invalid assume eq: lhs or rhs is not a theory term"); } ctx.assume_eq(get_enode(v1), get_enode(v2)); } void user_theory::reset_propagation_queues() { m_new_eqs.reset(); m_new_diseqs.reset(); m_new_assignments.reset(); m_new_relevant_apps.reset(); } theory_var user_theory::get_var(ast * n) const { if (!is_app(n)) return null_theory_var; context & ctx = get_context(); if (ctx.e_internalized(to_app(n))) { enode * e = ctx.get_enode(to_app(n)); return e->get_th_var(get_id()); } return null_theory_var; } theory_var user_theory::mk_var(ast * n) { theory_var v = get_var(n); if (v != null_theory_var || !is_app(n)) { return v; } app* a = to_app(n); if (a->get_family_id() == get_id() && internalize_term(a)) { return mk_var(get_context().get_enode(a)); } return v; } ast * user_theory::get_root(ast * n) const { theory_var v = get_var(n); if (v != null_theory_var) { theory_var r = m_find.find(v); return get_ast(r); } return n; } ast * user_theory::get_next(ast * n) const { theory_var v = get_var(n); if (v != null_theory_var) { theory_var r = m_find.next(v); return get_ast(r); } return n; } void user_theory::shrink_use_list(unsigned sz) { SASSERT(m_use_list.size() >= sz); std::for_each(m_use_list.begin() + sz, m_use_list.end(), delete_proc >()); m_use_list.shrink(sz); } ptr_vector * user_theory::get_non_null_use_list(theory_var v) { SASSERT(v != null_theory_var); if (m_use_list[v] == 0) m_use_list[v] = alloc(ptr_vector); return m_use_list[v]; } unsigned user_theory::get_num_parents(ast * n) const { theory_var v = get_var(n); if (v != null_theory_var && m_use_list[v] != 0) return m_use_list[v]->size(); return 0; } ast * user_theory::get_parent(ast * n, unsigned i) const { theory_var v = get_var(n); if (v != null_theory_var && m_use_list[v] != 0) return m_use_list[v]->get(i, 0); return 0; } theory_var user_theory::mk_var(enode * n) { if (is_attached_to_var(n)) return n->get_th_var(get_id()); theory_var r = theory::mk_var(n); theory_var r2 = m_find.mk_var(); m_use_list.push_back(0); SASSERT(r == r2); get_context().attach_th_var(n, this, r); if (m_new_elem_fptr != 0) { flet invoking(m_new_elem_invoking, true); m_new_elem_fptr(this, n->get_owner()); } return r; } bool user_theory::internalize_atom(app * atom, bool gate_ctx) { return internalize_term(atom); } bool user_theory::internalize_term(app * term) { context & ctx = get_context(); unsigned num_args = term->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(term->get_arg(i), false); // the internalization of the arguments may trigger the internalization of term. if (ctx.e_internalized(term)) return true; m_parents.push_back(term); enode * e = ctx.mk_enode(term, false, get_manager().is_bool(term), true); if (get_manager().is_bool(term)) { bool_var bv = ctx.mk_bool_var(term); ctx.set_var_theory(bv, get_id()); ctx.set_enode_flag(bv, true); } // make sure every argument is attached to a theory variable... for (unsigned i = 0; i < num_args; i++) { enode * arg = e->get_arg(i); theory_var v_arg = mk_var(arg); ptr_vector * arg_use_list = get_non_null_use_list(v_arg); arg_use_list->push_back(term); m_trail_stack.push(push_back_trail(*arg_use_list)); } if (m_new_app_fptr != 0) { flet invoking(m_new_app_invoking, true); m_new_app_fptr(this, term); } return true; } void user_theory::apply_sort_cnstr(enode * n, sort * s) { mk_var(n); } void user_theory::assign_eh(bool_var v, bool is_true) { m_new_assignments.push_back(v); } void user_theory::new_eq_eh(theory_var v1, theory_var v2) { m_new_eqs.push_back(var_pair(v1, v2)); } void user_theory::new_diseq_eh(theory_var v1, theory_var v2) { m_new_diseqs.push_back(var_pair(v1, v2)); } void user_theory::relevant_eh(app * n) { m_new_relevant_apps.push_back(n); } void user_theory::push_scope_eh() { SASSERT(m_new_assignments.empty()); SASSERT(m_new_eqs.empty()); SASSERT(m_new_diseqs.empty()); SASSERT(m_new_relevant_apps.empty()); theory::push_scope_eh(); m_trail_stack.push_scope(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_asserted_axioms_old_sz = m_asserted_axioms.size(); s.m_parents_old_sz = m_parents.size(); if (m_push_fptr != 0) { flet invoke(m_push_invoking, true); m_push_fptr(this); } } void user_theory::pop_scope_eh(unsigned num_scopes) { reset_propagation_queues(); if (m_pop_fptr != 0) { for (unsigned i = 0; i < num_scopes; i++) { flet invoke(m_pop_invoking, true); m_pop_fptr(this); } } unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; m_parents.shrink(s.m_parents_old_sz); unsigned curr_sz = m_asserted_axioms.size(); unsigned old_sz = s.m_asserted_axioms_old_sz; for (unsigned i = old_sz; i < curr_sz; i++) { m_asserted_axiom_set.erase(m_asserted_axioms.get(i)); } m_asserted_axioms.shrink(old_sz); m_scopes.shrink(new_lvl); m_trail_stack.pop_scope(num_scopes); shrink_use_list(get_old_num_vars(num_scopes)); theory::pop_scope_eh(num_scopes); } void user_theory::restart_eh() { if (m_restart_fptr != 0) { flet invoke(m_restart_invoking, true); m_restart_fptr(this); } } void user_theory::init_search_eh() { if (m_init_search_fptr != 0) { flet invoke(m_init_search_invoking, true); m_init_search_fptr(this); } } final_check_status user_theory::final_check_eh() { if (m_final_check_fptr != 0) { unsigned old_sz = m_asserted_axioms.size(); flet invoke(m_final_check_invoking, true); Z3_bool r = m_final_check_fptr(this); if (old_sz != m_asserted_axioms.size()) { assert_axioms_into_context(old_sz); return r ? FC_CONTINUE : FC_GIVEUP; } return r ? FC_DONE : FC_GIVEUP; } return FC_DONE; } bool user_theory::can_propagate() { return (m_persisted_axioms.size() > m_persisted_axioms_qhead) || !m_new_eqs.empty() || !m_new_diseqs.empty() || !m_new_relevant_apps.empty() || !m_new_assignments.empty(); } literal user_theory::internalize_literal(expr * arg) { context & ctx = get_context(); ast_manager& m = get_manager(); if (is_app(arg) && m.is_not(arg)) { expr * arg_arg = to_app(arg)->get_arg(0); if (!ctx.b_internalized(arg_arg)) ctx.internalize(arg_arg, true); return literal(ctx.get_bool_var(arg_arg), true); } else if (m.is_false(arg)) { return false_literal; } else if (m.is_true(arg)) { return true_literal; } else { if (!ctx.b_internalized(arg)) ctx.internalize(arg, true); return literal(ctx.get_bool_var(arg)); } } void user_theory::assert_axioms_into_context(unsigned old_sz) { for (unsigned i = old_sz; i < m_asserted_axioms.size(); i++) { expr * axiom = m_asserted_axioms.get(i); assert_axiom_into_context(axiom); } } void user_theory::mark_as_relevant(literal l) { if (l == false_literal || l == true_literal) return; get_context().mark_as_relevant(l); } void user_theory::assert_axiom_into_context(expr * axiom) { TRACE("user_smt_theory", tout << mk_pp(axiom, get_manager()) << "\n";); ast_manager & m = get_manager(); context & ctx = get_context(); if (m.is_or(axiom)) { literal_buffer lits; unsigned num_args = to_app(axiom)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { lits.push_back(internalize_literal(to_app(axiom)->get_arg(i))); mark_as_relevant(lits.back()); } ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } else { literal l = internalize_literal(axiom); mark_as_relevant(l); ctx.mk_th_axiom(get_id(), 1, &l); } } void user_theory::propagate() { unsigned old_sz = m_asserted_axioms.size(); if (m_persisted_axioms_qhead < m_persisted_axioms.size()) { get_context().push_trail(value_trail(m_persisted_axioms_qhead)); for (; m_persisted_axioms_qhead < m_persisted_axioms.size(); ++m_persisted_axioms_qhead) { m_asserted_axioms.push_back(m_persisted_axioms[m_persisted_axioms_qhead].get()); } } do { for (unsigned i = 0; i < m_new_eqs.size(); i++) { var_pair & p = m_new_eqs[i]; if (m_new_eq_fptr != 0) { ++m_stats.m_num_eq; flet invoke(m_new_eq_invoking, true); m_new_eq_fptr(this, get_app(p.first), get_app(p.second)); } m_find.merge(p.first, p.second); } m_new_eqs.reset(); if (m_new_diseq_fptr != 0) { for (unsigned i = 0; i < m_new_diseqs.size(); i++) { ++m_stats.m_num_diseq; var_pair & p = m_new_diseqs[i]; flet invoke(m_new_diseq_invoking, true); m_new_diseq_fptr(this, get_app(p.first), get_app(p.second)); } } m_new_diseqs.reset(); if (m_new_assignment_fptr != 0) { context & ctx = get_context(); for (unsigned i = 0; i < m_new_assignments.size(); i++) { ++m_stats.m_num_assignment; bool_var bv = m_new_assignments[i]; lbool val = ctx.get_assignment(bv); SASSERT(val != l_undef); flet invoke(m_new_assignment_invoking, true); m_new_assignment_fptr(this, to_app(ctx.bool_var2expr(bv)), val == l_true); } } m_new_assignments.reset(); if (m_new_relevant_fptr != 0) { for (unsigned i = 0; i < m_new_relevant_apps.size(); i++) { flet invoke(m_new_relevant_invoking, true); m_new_relevant_fptr(this, m_new_relevant_apps[i]); } } m_new_relevant_apps.reset(); assert_axioms_into_context(old_sz); old_sz = m_asserted_axioms.size(); } while (!m_new_eqs.empty() || !m_new_diseqs.empty() || !m_new_relevant_apps.empty() || !m_new_assignments.empty()); } void user_theory::flush_eh() { reset(false); } void user_theory::reset_eh() { reset(true); } void user_theory::reset(bool full_reset) { if (m_reset_fptr != 0) { flet invoke(m_reset_invoking, true); m_reset_fptr(this); } m_trail_stack.reset(); reset_propagation_queues(); m_asserted_axioms.reset(); m_asserted_axiom_set.reset(); shrink_use_list(0); m_parents.reset(); m_scopes.reset(); m_persisted_axioms.reset(); m_persisted_axioms_qhead = 0; m_stats.reset(); if (full_reset) theory::reset_eh(); } void user_theory::display_statistics(std::ostream & out) const { print_stat(out, "num. user eqs: ", m_stats.m_num_eq); print_stat(out, "num. user diseq: ", m_stats.m_num_diseq); print_stat(out, "num. assignments: ", m_stats.m_num_assignment); print_stat(out, "num. user axioms: ", m_stats.m_num_user_axioms); } void user_theory::display_istatistics(std::ostream & out) const { out << "NUM_USER_EQS " << m_stats.m_num_eq << "\n"; out << "NUM_USER_DISEQ " << m_stats.m_num_diseq << "\n"; out << "NUM_ASSIGNMENTS " << m_stats.m_num_assignment << "\n"; out << "NUM_USER_AXIOMS " << m_stats.m_num_user_axioms << "\n"; } class user_smt_model_value_proc : public model_value_proc { func_decl_ref m_decl; public: user_smt_model_value_proc(ast_manager& m, func_decl* f) : m_decl(f, m) {} virtual app * mk_value(model_generator & mg, ptr_vector & values) { ast_manager& m = mg.get_manager(); return m.mk_app(m_decl, values.size(), values.c_ptr()); } }; bool user_theory::build_models() const { return true; } void user_theory::init_model(model_generator & m) { m.register_factory(alloc(user_smt_theory_factory, get_manager(), get_id())); } void user_theory::finalize_model(model_generator &) { // No-op } model_value_proc * user_theory::mk_value(enode * n, model_generator & mg) { ast_manager& m = get_manager(); func_decl* f = n->get_decl(); if (m_decl_plugin->is_value(f)) { return alloc(user_smt_model_value_proc, m, n->get_decl()); } else { return mg.mk_model_value(n); } } bool user_theory::get_value(enode * n, expr_ref & r) { return false; } char const * user_theory::get_name() const { return m_name.c_str(); } void user_theory::display(std::ostream & out) const { out << "Theory " << get_name() << ":\n"; } user_theory * mk_user_theory(kernel & _s, void * ext_context, void * ext_data, char const * name) { context & ctx = _s.get_context(); // HACK symbol _name(name); ast_manager & m = ctx.get_manager(); family_id fid = m.mk_family_id(_name); user_decl_plugin * dp = alloc(user_decl_plugin); m.register_plugin(fid, dp); simplifier & s = ctx.get_simplifier(); user_simplifier_plugin * sp = alloc(user_simplifier_plugin, _name, m); s.register_plugin(sp); user_theory * th = alloc(user_theory, m, ctx.get_fparams(), ext_context, ext_data, name, fid, dp, sp); ctx.register_plugin(th); sp->set_owner(th); return th; } }; z3-z3-4.4.1/src/smt/user_plugin/user_smt_theory.h000066400000000000000000000233211260446376700217600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: user_smt_theory.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-22. Revision History: --*/ #ifndef USER_SMT_THEORY_H_ #define USER_SMT_THEORY_H_ #include"user_decl_plugin.h" #include"user_simplifier_plugin.h" #include"smt_theory.h" #include"union_find.h" #include"smt_kernel.h" namespace smt { class user_theory; typedef int Z3_bool; typedef void (*theory_callback_fptr)(user_theory * th); typedef Z3_bool (*theory_final_check_callback_fptr)(user_theory * th); typedef void (*theory_app_callback_fptr)(user_theory * th, app *); typedef void (*theory_app_bool_callback_fptr)(user_theory * th, app *, Z3_bool); typedef void (*theory_app_app_callback_fptr)(user_theory * th, app *, app *); typedef void * (*theory_mk_fresh_ext_data_fptr)(user_theory * th); class user_theory : public theory { typedef trail_stack th_trail_stack; typedef union_find th_union_find; typedef std::pair var_pair; smt_params const& m_params; void * m_ext_context; void * m_ext_data; std::string m_name; bool m_simplify_axioms; user_decl_plugin * m_decl_plugin; user_simplifier_plugin * m_simplifier_plugin; th_union_find m_find; th_trail_stack m_trail_stack; svector m_new_eqs; svector m_new_diseqs; svector m_new_assignments; ptr_vector m_new_relevant_apps; obj_hashtable m_asserted_axiom_set; expr_ref_vector m_asserted_axioms; ptr_vector m_parents; ptr_vector > m_use_list; app_ref_vector m_persisted_axioms; unsigned m_persisted_axioms_qhead; struct scope { unsigned m_asserted_axioms_old_sz; unsigned m_parents_old_sz; }; svector m_scopes; theory_callback_fptr m_delete_fptr; theory_app_callback_fptr m_new_app_fptr; theory_app_callback_fptr m_new_elem_fptr; theory_callback_fptr m_init_search_fptr; theory_callback_fptr m_push_fptr; theory_callback_fptr m_pop_fptr; theory_callback_fptr m_restart_fptr; theory_callback_fptr m_reset_fptr; theory_final_check_callback_fptr m_final_check_fptr; theory_app_app_callback_fptr m_new_eq_fptr; theory_app_app_callback_fptr m_new_diseq_fptr; theory_app_bool_callback_fptr m_new_assignment_fptr; theory_app_callback_fptr m_new_relevant_fptr; theory_mk_fresh_ext_data_fptr m_mk_fresh_fptr; bool m_delete_invoking; bool m_new_app_invoking; bool m_new_elem_invoking; bool m_init_search_invoking; bool m_push_invoking; bool m_pop_invoking; bool m_restart_invoking; bool m_reset_invoking; bool m_final_check_invoking; bool m_new_eq_invoking; bool m_new_diseq_invoking; bool m_new_assignment_invoking; bool m_new_relevant_invoking; struct statistics { unsigned m_num_eq; unsigned m_num_diseq; unsigned m_num_assignment; unsigned m_num_user_axioms; statistics() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; statistics m_stats; protected: virtual theory_var mk_var(enode * n); literal internalize_literal(expr * arg); void assert_axioms_into_context(unsigned old_sz); void assert_axiom_into_context(expr * axiom); void reset_propagation_queues(); void shrink_use_list(unsigned sz); ptr_vector * get_non_null_use_list(theory_var v); void mark_as_relevant(literal l); void assert_axiom_core(app* axiom); public: user_theory(ast_manager & m, smt_params const& p, void * ext_context, void * ext_data, char const * name, family_id fid, user_decl_plugin * dp, user_simplifier_plugin * sp); virtual ~user_theory(); virtual theory * mk_fresh(context * new_ctx); void * get_ext_context() const { return m_ext_context; } void * get_ext_data() { return m_ext_data; } sort * mk_sort(symbol const & name) { return m_decl_plugin->mk_sort(name); } func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range) { return m_decl_plugin->mk_func_decl(name, arity, domain, range); } func_decl * mk_value_decl(symbol const & name, sort * s) { return m_decl_plugin->mk_value_decl(name, s); } void assert_axiom(ast * axiom); void assume_eq(ast * lhs, ast * rhs); void enable_axiom_simplification(bool flag) { m_simplify_axioms = flag; } void set_delete_fptr(theory_callback_fptr ptr) { m_delete_fptr = ptr; } void set_reduce_app_fptr(reduce_app_fptr ptr) { m_simplifier_plugin->set_reduce_app_fptr(ptr); } void set_reduce_eq_fptr(reduce_eq_fptr ptr) { m_simplifier_plugin->set_reduce_eq_fptr(ptr); } void set_reduce_distinct_fptr(reduce_distinct_fptr ptr) { m_simplifier_plugin->set_reduce_distinct_fptr(ptr); } void set_new_app_fptr(theory_app_callback_fptr ptr) { m_new_app_fptr = ptr; } void set_new_elem_fptr(theory_app_callback_fptr ptr) { m_new_elem_fptr = ptr; } void set_init_search_fptr(theory_callback_fptr ptr) { m_init_search_fptr = ptr; } void set_push_fptr(theory_callback_fptr ptr) { m_push_fptr = ptr; } void set_pop_fptr(theory_callback_fptr ptr) { m_pop_fptr = ptr; } void set_restart_fptr(theory_callback_fptr ptr) { m_restart_fptr = ptr; } void set_reset_fptr(theory_callback_fptr ptr) { m_reset_fptr = ptr; } void set_final_check_fptr(theory_final_check_callback_fptr ptr) { m_final_check_fptr = ptr; } void set_new_eq_fptr(theory_app_app_callback_fptr ptr) { m_new_eq_fptr = ptr; } void set_new_diseq_fptr(theory_app_app_callback_fptr ptr) { m_new_diseq_fptr = ptr; } void set_new_assignment_fptr(theory_app_bool_callback_fptr ptr) { m_new_assignment_fptr = ptr; } void set_new_relevant_fptr(theory_app_callback_fptr ptr) { m_new_relevant_fptr = ptr; } th_trail_stack & get_trail_stack() { return m_trail_stack; } theory_var get_var(ast * n) const; theory_var mk_var(ast * n); ast * get_root(ast * n) const; ast * get_next(ast * n) const; unsigned get_num_parents(ast * n) const; ast * get_parent(ast * n, unsigned i) const; unsigned get_num_parents() const { return m_parents.size(); } ast * get_parent(unsigned i) const { return m_parents[i]; } unsigned get_num_apps() const { return get_num_vars(); } app * get_app(unsigned i) const { return get_enode(i)->get_owner(); } unsigned get_num_asts() const { return get_num_apps(); } ast * get_ast(unsigned i) const { return get_app(i); } static void merge_eh(theory_var, theory_var, theory_var, theory_var) {} static void after_merge_eh(theory_var, theory_var, theory_var, theory_var) {} virtual void unmerge_eh(theory_var, theory_var) {} virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); virtual void apply_sort_cnstr(enode * n, sort * s); virtual void assign_eh(bool_var v, bool is_true); virtual void new_eq_eh(theory_var v1, theory_var v2); virtual void new_diseq_eh(theory_var v1, theory_var v2); virtual void relevant_eh(app * n); virtual void push_scope_eh(); virtual void pop_scope_eh(unsigned num_scopes); virtual void restart_eh(); virtual void init_search_eh(); virtual final_check_status final_check_eh(); virtual bool can_propagate(); virtual void propagate(); virtual void flush_eh(); virtual void reset_eh(); void reset(bool full_reset); virtual void display_statistics(std::ostream & out) const; virtual void display_istatistics(std::ostream & out) const; virtual bool build_models() const; virtual void init_model(model_generator & m); virtual void finalize_model(model_generator & m); virtual model_value_proc * mk_value(enode * n, model_generator & mg); virtual bool get_value(enode * n, expr_ref & r); virtual char const * get_name() const; virtual void display(std::ostream & out) const; }; user_theory * mk_user_theory(kernel & s, void * ext_context, void * ext_data, char const * name); }; #endif /* USER_SMT_THEORY_H_ */ z3-z3-4.4.1/src/smt/uses_theory.cpp000066400000000000000000000016001260446376700170710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: uses_theory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-21. Revision History: --*/ #include"uses_theory.h" #include"for_each_expr.h" bool uses_theory(expr * n, family_id fid) { expr_mark visited; return uses_theory(n, fid, visited); } namespace uses_theory_ns { struct found {}; struct proc { family_id m_fid; proc(family_id fid):m_fid(fid) {} void operator()(var * n) {} void operator()(app * n) { if (n->get_family_id() == m_fid) throw found(); } void operator()(quantifier * n) {} }; }; bool uses_theory(expr * n, family_id fid, expr_mark & visited) { uses_theory_ns::proc p(fid); try { for_each_expr(p, visited, n); } catch (uses_theory_ns::found) { return true; } return false; } z3-z3-4.4.1/src/smt/uses_theory.h000066400000000000000000000012731260446376700165440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: uses_theory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-21. Revision History: --*/ #ifndef USES_THEORY_H_ #define USES_THEORY_H_ #include"ast.h" /** \brief Return true if the given expression contains a symbol of the given theory. */ bool uses_theory(expr * n, family_id fid); /** \brief Return true if the given expression contains a symbol of the given theory. Only the expressions not marked as visited are checked. The set visited is updated with the new checked expressions. */ bool uses_theory(expr * n, family_id fid, expr_mark & visited); #endif /* USES_THEORY_H_ */ z3-z3-4.4.1/src/smt/watch_list.cpp000066400000000000000000000067461260446376700167010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: watch_list.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-23. Revision History: --*/ #include"watch_list.h" namespace smt { #define DEFAULT_WATCH_LIST_SIZE (sizeof(clause *) * 4) #ifdef _AMD64_ // make sure data is aligned in 64 bit machines #define HEADER_SIZE (4 * sizeof(unsigned)) #else #define HEADER_SIZE (3 * sizeof(unsigned)) #endif void watch_list::destroy() { if (m_data) { dealloc_svect(reinterpret_cast(m_data) - HEADER_SIZE); } } void watch_list::expand() { if (m_data == 0) { unsigned size = DEFAULT_WATCH_LIST_SIZE + HEADER_SIZE; unsigned * mem = reinterpret_cast(alloc_svect(char, size)); #ifdef _AMD64_ ++mem; // make sure data is aligned in 64 bit machines #endif *mem = 0; ++mem; *mem = DEFAULT_WATCH_LIST_SIZE; ++mem; *mem = DEFAULT_WATCH_LIST_SIZE; ++mem; m_data = reinterpret_cast(mem); SASSERT( begin_lits_core() % sizeof(literal) == 0 ); } else { unsigned curr_begin_bin = begin_lits_core(); unsigned curr_capacity = end_lits_core(); unsigned bin_bytes = curr_capacity - curr_begin_bin; /* dvitek: Added +3&~3U to fix alignment issues on * sparc64/solaris. ("literal"s must be 4-byte aligned). Should * also help performance elsewhere. */ unsigned new_capacity = (((curr_capacity * 3 + sizeof(clause *)) >> 1)+3)&~3U; unsigned * mem = reinterpret_cast(alloc_svect(char, new_capacity + HEADER_SIZE)); unsigned curr_end_cls = end_cls_core(); #ifdef _AMD64_ ++mem; // make sure data is aligned in 64 bit machines #endif *mem = curr_end_cls; ++mem; SASSERT(bin_bytes <= new_capacity); unsigned new_begin_bin = new_capacity - bin_bytes; *mem = new_begin_bin; ++mem; *mem = new_capacity; ++mem; memcpy(mem, m_data, curr_end_cls); memcpy(reinterpret_cast(mem) + new_begin_bin, m_data + curr_begin_bin, bin_bytes); destroy(); m_data = reinterpret_cast(mem); SASSERT( begin_lits_core() % sizeof(literal) == 0 ); } } void watch_list::remove_clause(clause * c) { clause_iterator begin = begin_clause(); clause_iterator end = end_clause(); clause_iterator it = std::find(begin, end, c); if (it == end) { return; } clause_iterator prev = it; ++it; for(; it != end; ++it, ++prev) { *prev = *it; } end_cls_core() -= sizeof(clause *); } void watch_list::remove_literal(literal l) { literal * begin = begin_literals(); literal * end = end_literals(); literal * it = std::find(begin, end, l); if (it == end) { return; } literal * prev = it; while (it != begin) { SASSERT(it == prev); --it; *prev = *it; --prev; } SASSERT(prev == begin); begin_lits_core() += sizeof(literal); } }; z3-z3-4.4.1/src/smt/watch_list.h000066400000000000000000000123161260446376700163340ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: watch_list.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-23. Revision History: --*/ #ifndef WATCH_LIST_H_ #define WATCH_LIST_H_ #include"smt_clause.h" #include"memory_manager.h" namespace smt { /** \brief List of clauses and literals watching a given literal. ------------------------------------------------------------------------------------------- | end_nbegin | begin_lits | end | regular clauses | -> <- | literals | ------------------------------------------------------------------------------------------- ^ ^ ^ ^ | | | | m_data end_cls begin_lits end_lits When this class is used to implement unit propagation, a literal l1 in m_watch_list[l2] represents the binary clause (or l1 (not l2)) */ class watch_list { char * m_data; void expand(); unsigned & end_cls_core() { SASSERT(m_data); return reinterpret_cast(m_data)[-3]; } unsigned end_cls() { return m_data ? end_cls_core() : 0; } unsigned & begin_lits_core() { SASSERT(m_data); return reinterpret_cast(m_data)[-2]; } unsigned begin_lits_core() const { SASSERT(m_data); return reinterpret_cast(m_data)[-2]; } unsigned begin_lits() const { return m_data ? begin_lits_core() : 0; } unsigned & end_lits_core() { SASSERT(m_data); return reinterpret_cast(m_data)[-1]; } unsigned end_lits_core() const { SASSERT(m_data); return reinterpret_cast(m_data)[-1]; } unsigned end_lits() const { return m_data ? end_lits_core() : 0; } void destroy(); public: watch_list(): m_data(0) { } ~watch_list() { destroy(); } unsigned size() const { if (m_data) { return reinterpret_cast(m_data)[-3] + reinterpret_cast(m_data)[-1] - reinterpret_cast(m_data)[-2]; } return 0; } typedef clause ** clause_iterator; void reset() { if (m_data) { end_cls_core() = 0; begin_lits_core() = end_lits_core(); } } void reset_and_release_memory() { destroy(); m_data = 0; } clause_iterator begin_clause() { return reinterpret_cast(m_data); } clause_iterator end_clause() { return reinterpret_cast(m_data + end_cls()); } clause_iterator find_clause(clause const * c) { return std::find(begin_clause(), end_clause(), c); } literal * begin_literals() { return reinterpret_cast(m_data + begin_lits()); } literal * end_literals() { return reinterpret_cast(m_data + end_lits()); } literal const * begin_literals() const { return reinterpret_cast(m_data + begin_lits()); } literal const * end_literals() const { return reinterpret_cast(m_data + end_lits()); } literal * find_literal(literal const & l) { return std::find(begin_literals(), end_literals(), l); } literal const * find_literal(literal const & l) const { return std::find(begin_literals(), end_literals(), l); } void insert_clause(clause * c) { if (m_data == 0 || end_cls_core() + sizeof(clause *) >= begin_lits_core()) { expand(); } *(reinterpret_cast(m_data + end_cls_core())) = c; end_cls_core() += sizeof(clause *); } void insert_literal(literal const & l) { if (m_data == 0 || begin_lits_core() <= end_cls_core() + sizeof(literal)) { expand(); } SASSERT(begin_lits_core() >= sizeof(literal)); begin_lits_core() -= sizeof(literal); *(reinterpret_cast(m_data + begin_lits_core())) = l; } void remove_clause(clause * c); void remove_literal(literal l); void set_end_clause(clause_iterator new_end) { SASSERT(new_end <= end_clause()); if (m_data) { end_cls_core() = static_cast(reinterpret_cast(new_end) - m_data); } } }; }; #endif /* WATCH_LIST_H_ */ z3-z3-4.4.1/src/solver/000077500000000000000000000000001260446376700145265ustar00rootroot00000000000000z3-z3-4.4.1/src/solver/check_sat_result.cpp000066400000000000000000000021021260446376700205470ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/solver/check_sat_result.h000066400000000000000000000044221260446376700202230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/solver/combined_solver.cpp000066400000000000000000000235231260446376700204110ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/solver/combined_solver.h000066400000000000000000000010541260446376700200510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/solver/combined_solver_params.pyg000066400000000000000000000012161260446376700217640ustar00rootroot00000000000000def_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-z3-4.4.1/src/solver/progress_callback.h000066400000000000000000000010541260446376700203570ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/solver/solver.cpp000066400000000000000000000007071260446376700165500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/solver/solver.h000066400000000000000000000100331260446376700162060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/solver/solver_na2as.cpp000066400000000000000000000045641260446376700176410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/solver/solver_na2as.h000066400000000000000000000026721260446376700173040ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/solver/tactic2solver.cpp000066400000000000000000000203531260446376700200210ustar00rootroot00000000000000/*++ 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_pp_util.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_pp_util visitor(m_assertions.m()); visitor.collect(m_assertions); visitor.display_decls(out); visitor.display_asserts(out, m_assertions, true); #if 0 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 << ")"; #endif } 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-z3-4.4.1/src/solver/tactic2solver.h000066400000000000000000000017111260446376700174630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/000077500000000000000000000000001260446376700144635ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/aig/000077500000000000000000000000001260446376700152235ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/aig/aig.cpp000066400000000000000000001571161260446376700165020ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/aig/aig.h000066400000000000000000000042411260446376700161350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/aig/aig_tactic.cpp000066400000000000000000000070011260446376700200140ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/aig/aig_tactic.h000066400000000000000000000006561260446376700174720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/000077500000000000000000000000001260446376700155725ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/arith/add_bounds_tactic.cpp000066400000000000000000000126631260446376700217370ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/add_bounds_tactic.h000066400000000000000000000014011260446376700213700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/arith_bounds_tactic.cpp000066400000000000000000000114551260446376700223140ustar00rootroot00000000000000 /*++ 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-z3-4.4.1/src/tactic/arith/arith_bounds_tactic.h000066400000000000000000000017401260446376700217550ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/bound_manager.cpp000066400000000000000000000132641260446376700211050ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/bound_manager.h000066400000000000000000000047371260446376700205570ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/bound_propagator.cpp000066400000000000000000000742321260446376700216530ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/bound_propagator.h000066400000000000000000000245551260446376700213230ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/bv2int_rewriter.cpp000066400000000000000000000434031260446376700214310ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/bv2int_rewriter.h000066400000000000000000000103221260446376700210700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/bv2real_rewriter.cpp000066400000000000000000000536601260446376700215700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/bv2real_rewriter.h000066400000000000000000000203771260446376700212340ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/card2bv_tactic.cpp000066400000000000000000000404541260446376700211570ustar00rootroot00000000000000/*++ 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" #include"ast_pp.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()); 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-z3-4.4.1/src/tactic/arith/card2bv_tactic.h000066400000000000000000000062431260446376700206220ustar00rootroot00000000000000/*++ 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_H_ #define CARD2BV_TACTIC_H_ #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-z3-4.4.1/src/tactic/arith/degree_shift_tactic.cpp000066400000000000000000000300321260446376700222530ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/degree_shift_tactic.h000066400000000000000000000014131260446376700217210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/diff_neq_tactic.cpp000066400000000000000000000330241260446376700214020ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/diff_neq_tactic.h000066400000000000000000000014341260446376700210470ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/elim01_tactic.cpp000066400000000000000000000214231260446376700207160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/elim01_tactic.h000066400000000000000000000010071260446376700203570ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/eq2bv_tactic.cpp000066400000000000000000000235421260446376700206520ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: eq2bv_tactic.cpp Abstract: Extract integer variables that are used as finite domain indicators. The integer variables can only occur in equalities. Author: Nikolaj Bjorner (nbjorner) 2015-8-19 Notes: --*/ #include"tactical.h" #include"cooperate.h" #include"bound_manager.h" #include"ast_pp.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"rewriter_def.h" #include"ast_util.h" #include"ast_pp_util.h" class eq2bv_tactic : public tactic { struct eq_rewriter_cfg : public default_rewriter_cfg { ast_manager& m; eq2bv_tactic& t; bool is_fd(expr* x, expr* y, expr_ref& result) { expr* z; rational r; if (t.m_fd.find(x, z) && t.a.is_numeral(y, r)) { result = m.mk_eq(z, t.bv.mk_numeral(r, m.get_sort(z))); return true; } else { return false; } } br_status mk_app_core(func_decl* f, unsigned sz, expr*const* es, expr_ref& result) { if (m.is_eq(f)) { if (is_fd(es[0], es[1], result)) { return BR_DONE; } else if (is_fd(es[1], es[0], result)) { return BR_DONE; } } return BR_FAILED; } 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 mk_app_core(f, num, args, result); } eq_rewriter_cfg(eq2bv_tactic& t):m(t.m), t(t) {} }; class eq_rewriter : public rewriter_tpl { eq_rewriter_cfg m_cfg; public: eq_rewriter(eq2bv_tactic& t): rewriter_tpl(t.m, false, m_cfg), m_cfg(t) {} }; class bvmc : public model_converter { obj_map m_map; public: void insert(func_decl* c_new, func_decl* c_old) { m_map.insert(c_new, c_old); } virtual void operator()(model_ref& mdl) { ast_manager& m = mdl->get_manager(); bv_util bv(m); arith_util a(m); rational r; model_ref new_m = alloc(model, m); new_m->copy_func_interps(*mdl); new_m->copy_usort_interps(*mdl); unsigned sz = mdl->get_num_constants(), bvsz; for (unsigned i = 0; i < sz; ++i) { func_decl* f = mdl->get_constant(i), *g; expr* val = mdl->get_const_interp(f); if (m_map.find(f, g) && bv.is_numeral(val, r, bvsz)) { val = a.mk_numeral(r, true); new_m->register_decl(g, val); } else { new_m->register_decl(f, val); } } mdl = new_m; } virtual model_converter* translate(ast_translation & translator) { bvmc* v = alloc(bvmc); obj_map::iterator it = m_map.begin(), end = m_map.end(); for (; it != end; ++it) { v->m_map.insert(translator(it->m_key), translator(it->m_value)); } return v; } }; public: ast_manager & m; arith_util a; bv_util bv; eq_rewriter m_rw; expr_ref_vector m_trail; bound_manager m_bounds; obj_map m_fd; obj_map m_max; expr_mark m_nonfd; ptr_vector m_todo; eq2bv_tactic(ast_manager & _m): m(_m), a(m), bv(m), m_rw(*this), m_trail(m), m_bounds(m) { } virtual ~eq2bv_tactic() { } void set_cancel(bool f) { m_rw.set_cancel(f); } void updt_params(params_ref const & p) { } 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_trail.reset(); m_fd.reset(); m_max.reset(); m_nonfd.reset(); m_bounds.reset(); ref mc1 = alloc(bvmc); tactic_report report("eq2bv", *g); m_bounds(*g); for (unsigned i = 0; i < g->size(); i++) { collect_fd(g->form(i)); } cleanup_fd(mc1); if (m_max.empty()) { result.push_back(g.get()); return; } for (unsigned i = 0; i < g->size(); i++) { expr_ref new_curr(m); proof_ref new_pr(m); if (is_bound(g->form(i))) { g->update(i, m.mk_true(), 0, 0); continue; } m_rw(g->form(i), new_curr, new_pr); if (m.proofs_enabled() && !new_pr) { new_pr = m.mk_rewrite(g->form(i), new_curr); new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } g->update(i, new_curr, new_pr, g->dep(i)); } obj_map::iterator it = m_max.begin(), end = m_max.end(); for (; it != end; ++it) { expr* c = it->m_key; bool strict; rational r; if (m_bounds.has_lower(c, r, strict)) { SASSERT(!strict); expr* d = m_fd.find(c); g->assert_expr(bv.mk_ule(bv.mk_numeral(r, m.get_sort(d)), d), m_bounds.lower_dep(c)); } if (m_bounds.has_upper(c, r, strict)) { SASSERT(!strict); expr* d = m_fd.find(c); g->assert_expr(bv.mk_ule(d, bv.mk_numeral(r, m.get_sort(d))), m_bounds.upper_dep(c)); } } g->inc_depth(); mc = mc1.get(); result.push_back(g.get()); TRACE("pb", g->display(tout);); SASSERT(g->is_well_sorted()); } virtual tactic * translate(ast_manager & m) { return alloc(eq2bv_tactic, m); } virtual void collect_param_descrs(param_descrs & r) { } virtual void cleanup() { } void cleanup_fd(ref& mc) { SASSERT(m_fd.empty()); ptr_vector rm; obj_map::iterator it = m_max.begin(), end = m_max.end(); for (; it != end; ++it) { if (m_nonfd.is_marked(it->m_key)) { rm.push_back(it->m_key); } } for (unsigned i = 0; i < rm.size(); ++i) { m_max.erase(rm[i]); } it = m_max.begin(); end = m_max.end(); for (; it != end; ++it) { // ensure there are enough elements. bool strict; rational val; if (m_bounds.has_upper(it->m_key, val, strict)) { SASSERT(!strict); if (val.get_unsigned() > it->m_value) it->m_value = val.get_unsigned(); } else { ++it->m_value; } unsigned p = next_power_of_two(it->m_value); if (p <= 1) p = 2; unsigned n = log2(p); app* z = m.mk_fresh_const("z", bv.mk_sort(n)); m_trail.push_back(z); m_fd.insert(it->m_key, z); mc->insert(z->get_decl(), to_app(it->m_key)->get_decl()); } } bool is_var_const_pair(expr* e, expr* c, unsigned& k) { rational r; if (is_uninterp_const(e) && a.is_numeral(c, r) && r.is_unsigned() && !m_nonfd.is_marked(e)) { k = r.get_unsigned(); return true; } else { return false; } } bool is_upper(expr* f) { expr* e1, *e2; unsigned k; if ((a.is_le(f, e1, e2) || a.is_ge(f, e2, e1)) && is_var_const_pair(e1, e2, k)) { SASSERT(m_bounds.has_upper(e1)); return true; } return false; } bool is_lower(expr* f) { expr* e1, *e2; unsigned k; if ((a.is_le(f, e1, e2) || a.is_ge(f, e2, e1)) && is_var_const_pair(e2, e1, k)) { SASSERT(m_bounds.has_lower(e2)); return true; } return false; } bool is_bound(expr* f) { return is_lower(f) || is_upper(f); } void collect_fd(expr* f) { if (is_bound(f)) return; m_todo.push_back(f); while (!m_todo.empty()) { f = m_todo.back(); m_todo.pop_back(); if (m_nonfd.is_marked(f)) { continue; } m_nonfd.mark(f, true); expr* e1, *e2; if (m.is_eq(f, e1, e2)) { if (is_fd(e1, e2)) { continue; } if (is_fd(e2, e1)) { continue; } } m_todo.append(to_app(f)->get_num_args(), to_app(f)->get_args()); } } bool is_fd(expr* v, expr* c) { unsigned val; rational r; if (is_uninterp_const(v) && a.is_numeral(c, r) && !m_nonfd.is_marked(v) && a.is_int(v) && r.is_unsigned()) { val = r.get_unsigned(); add_fd(v, val); return true; } return false; } void add_fd(expr* c, unsigned val) { unsigned val2; if (!m_max.find(c, val2) || val2 < val) { m_max.insert(c, val); } } }; tactic * mk_eq2bv_tactic(ast_manager & m) { return clean(alloc(eq2bv_tactic, m)); } z3-z3-4.4.1/src/tactic/arith/eq2bv_tactic.h000066400000000000000000000010711260446376700203100ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: eq2bv_tactic.h Abstract: Extract integer variables that are used as finite domain indicators. The integer variables can only occur in equalities. Author: Nikolaj Bjorner (nbjorner) 2015-8-19 Notes: --*/ #ifndef EQ2BV_TACTIC_H_ #define EQ2BV_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_eq2bv_tactic(ast_manager & m); /* ADD_TACTIC("eq2bv", "convert integer variables used as finite domain elements to bit-vectors.", "mk_eq2bv_tactic(m)") */ #endif z3-z3-4.4.1/src/tactic/arith/factor_tactic.cpp000066400000000000000000000270101260446376700211030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/factor_tactic.h000066400000000000000000000007451260446376700205560ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/fix_dl_var_tactic.cpp000066400000000000000000000270541260446376700217520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/fix_dl_var_tactic.h000066400000000000000000000015741260446376700214160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/fm_tactic.cpp000066400000000000000000001723051260446376700202370ustar00rootroot00000000000000/*++ 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; signed_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-z3-4.4.1/src/tactic/arith/fm_tactic.h000066400000000000000000000012471260446376700177000ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/lia2card_tactic.cpp000066400000000000000000000305221260446376700213100ustar00rootroot00000000000000/*++ 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: --*/ /*++ 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"pb_decl_plugin.h" #include"arith_decl_plugin.h" #include"rewriter_def.h" #include"ast_util.h" #include"ast_pp_util.h" class lia2card_tactic : public tactic { struct lia_rewriter_cfg : public default_rewriter_cfg { ast_manager& m; lia2card_tactic& t; arith_util a; expr_ref_vector args; vector coeffs; rational coeff; bool is_pb(expr* x, expr* y, expr_ref_vector& args, vector& coeffs, rational& coeff) { args.reset(); coeffs.reset(); coeff.reset(); return t.get_pb_sum(x, rational::one(), args, coeffs, coeff) && t.get_pb_sum(y, -rational::one(), args, coeffs, coeff); } bool is_le(expr* x, expr* y, expr_ref& result) { if (is_pb(x, y, args, coeffs, coeff)) { result = t.mk_le(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), -coeff); return true; } else { return false; } } br_status mk_app_core(func_decl* f, unsigned sz, expr*const* es, expr_ref& result) { if (is_decl_of(f, a.get_family_id(), OP_LE) && is_le(es[0], es[1], result)) { } else if (is_decl_of(f, a.get_family_id(), OP_GE) && is_le(es[1], es[0], result)) { } else if (is_decl_of(f, a.get_family_id(), OP_LT) && is_le(es[1], es[0], result)) { result = m.mk_not(result); } else if (is_decl_of(f, a.get_family_id(), OP_GT) && is_le(es[0], es[1], result)) { result = m.mk_not(result); } else if (m.is_eq(f) && is_pb(es[0], es[1], args, coeffs, coeff)) { result = t.mk_eq(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), -coeff); } else { return BR_FAILED; } TRACE("pbsum", tout << expr_ref(m.mk_app(f, sz, es), m) << " ==>\n" << result << "\n";); #if 0 expr_ref vc(m); vc = m.mk_not(m.mk_eq(m.mk_app(f, sz, es), result)); ast_pp_util pp(m); pp.collect(vc); std::cout << "(push)\n" << "(echo \"" << result << "\")\n" ; pp.display_decls(std::cout); std::cout << "(assert " << vc << ")\n" << "(check-sat)\n" << "(pop)\n"; #endif return BR_DONE; } 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 mk_app_core(f, num, args, result); } lia_rewriter_cfg(lia2card_tactic& t):m(t.m), t(t), a(m), args(m) {} }; class lia_rewriter : public rewriter_tpl { lia_rewriter_cfg m_cfg; public: lia_rewriter(lia2card_tactic& t): rewriter_tpl(t.m, false, m_cfg), m_cfg(t) {} }; public: typedef obj_hashtable expr_set; ast_manager & m; arith_util a; lia_rewriter m_rw; 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_rw(*this), 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) { m_rw.set_cancel(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";); } } for (unsigned i = 0; i < g->size(); i++) { expr_ref new_curr(m); proof_ref new_pr(m); m_rw(g->form(i), new_curr, new_pr); if (m.proofs_enabled() && !new_pr) { new_pr = m.mk_rewrite(g->form(i), 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..) } 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); } 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]); } if (w.is_neg()) { DEBUG_CODE(for (unsigned i = 0; i < sz; ++i) SASSERT(weights[i].is_nonneg()); ); return m.mk_false(); } return m_pb.mk_le(sz, weights, args, w); } expr* mk_eq(unsigned sz, rational const* weights, expr* const* args, rational const& w) { if (w.is_neg()) { DEBUG_CODE(for (unsigned i = 0; i < sz; ++i) SASSERT(weights[i].is_nonneg()); ); return m.mk_false(); } 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]); } if (w.is_neg()) { DEBUG_CODE(for (unsigned i = 0; i < sz; ++i) SASSERT(weights[i].is_nonneg()); ); return m.mk_true(); } 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_ref_vector conds(m); return get_sum(x, mul, conds, args, coeffs, coeff); } bool get_sum(expr* x, rational const& mul, expr_ref_vector& conds, 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_sum(f->get_arg(i), mul, conds, args, coeffs, coeff); } } else if (a.is_sub(x, y, z)) { ok = get_sum(y, mul, conds, args, coeffs, coeff); ok = ok && get_sum(z, -mul, conds, args, coeffs, coeff); } else if (a.is_uminus(x, y)) { ok = get_sum(y, -mul, conds, args, coeffs, coeff); } else if (a.is_mul(x, y, z) && is_numeral(y, r)) { ok = get_sum(z, r*mul, conds, args, coeffs, coeff); } else if (a.is_mul(x, z, y) && is_numeral(y, r)) { ok = get_sum(z, r*mul, conds, args, coeffs, coeff); } else if (a.is_to_real(x, y)) { ok = get_sum(y, mul, conds, args, coeffs, coeff); } else if (m.is_ite(x, y, z, u)) { conds.push_back(y); ok = get_sum(z, mul, conds, args, coeffs, coeff); conds.pop_back(); conds.push_back(m.mk_not(y)); ok &= get_sum(u, mul, conds, args, coeffs, coeff); conds.pop_back(); } else if (is_01var(x)) { insert_arg(mul, conds, mk_01(x), args, coeffs, coeff); } else if (is_numeral(x, r)) { insert_arg(mul*r, conds, m.mk_true(), args, coeffs, coeff); } else { TRACE("pb", tout << "Can't handle " << mk_pp(x, m) << "\n";); ok = false; } return ok; } expr_ref add_conds(expr_ref_vector& es, expr* e) { expr_ref result(m); if (!m.is_true(e)) { es.push_back(e); } result = mk_and(m, es.size(), es.c_ptr()); if (!m.is_true(e)) { es.pop_back(); } return result; } 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_ref_vector& conds, expr* x, expr_ref_vector& args, vector& coeffs, rational& coeff) { expr_ref cond = add_conds(conds, x); if (m.is_true(cond)) { coeff += p; } else if (p.is_neg()) { // -p*x = p*(1-x) - p args.push_back(m.mk_not(cond)); coeffs.push_back(-p); coeff += p; } else if (p.is_pos()) { args.push_back(cond); 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-z3-4.4.1/src/tactic/arith/lia2card_tactic.h000066400000000000000000000012411260446376700207510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/lia2pb_tactic.cpp000066400000000000000000000306261260446376700210050ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/lia2pb_tactic.h000066400000000000000000000010451260446376700204430ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/linear_equation.cpp000066400000000000000000000177151260446376700214700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/linear_equation.h000066400000000000000000000053361260446376700211310ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/nla2bv_tactic.cpp000066400000000000000000000410771260446376700210220ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/nla2bv_tactic.h000066400000000000000000000012561260446376700204620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/normalize_bounds_tactic.cpp000066400000000000000000000147011260446376700232020ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/normalize_bounds_tactic.h000066400000000000000000000012351260446376700226450ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/pb2bv_model_converter.cpp000066400000000000000000000061201260446376700225570ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/pb2bv_model_converter.h000066400000000000000000000016271260446376700222330ustar00rootroot00000000000000/*++ 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_H_ #define PB2BV_MODEL_CONVERTER_H_ #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-z3-4.4.1/src/tactic/arith/pb2bv_tactic.cpp000066400000000000000000001115251260446376700206450ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/pb2bv_tactic.h000066400000000000000000000011711260446376700203050ustar00rootroot00000000000000/*++ 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_H_ #define PB2BV_TACTIC_H_ #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-z3-4.4.1/src/tactic/arith/probe_arith.cpp000066400000000000000000000410501260446376700205740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/probe_arith.h000066400000000000000000000055161260446376700202500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/propagate_ineqs_tactic.cpp000066400000000000000000000423161260446376700230140ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/propagate_ineqs_tactic.h000066400000000000000000000020341260446376700224520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/purify_arith_tactic.cpp000066400000000000000000000712051260446376700223370ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/purify_arith_tactic.h000066400000000000000000000035601260446376700220030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/recover_01_tactic.cpp000066400000000000000000000361571260446376700216060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/arith/recover_01_tactic.h000066400000000000000000000014521260446376700212410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/bv/000077500000000000000000000000001260446376700150725ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/bv/bit_blaster_model_converter.cpp000066400000000000000000000163531260446376700233470ustar00rootroot00000000000000/*++ 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* new_val = old_model->get_const_interp(m_vars.get(i)); if (new_val) { new_model->register_decl(m_vars.get(i), new_val); continue; } 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++; } } 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-z3-4.4.1/src/tactic/bv/bit_blaster_model_converter.h000066400000000000000000000010501260446376700230000ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/bv/bit_blaster_tactic.cpp000066400000000000000000000130741260446376700214240ustar00rootroot00000000000000/*++ 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_base_rewriter; bit_blaster_rewriter* m_rewriter; unsigned m_num_steps; bool m_blast_quant; imp(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p): m_base_rewriter(m, p), m_rewriter(rw?rw:&m_base_rewriter) { 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; bit_blaster_rewriter* m_rewriter; params_ref m_params; public: bit_blaster_tactic(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p): m_rewriter(rw), m_params(p) { m_imp = alloc(imp, m, m_rewriter, p); } virtual tactic * translate(ast_manager & m) { SASSERT(!m_rewriter); // assume translation isn't used where rewriter is external. return alloc(bit_blaster_tactic, m, 0, 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_rewriter, 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, 0, p)); } tactic * mk_bit_blaster_tactic(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p) { return clean(alloc(bit_blaster_tactic, m, rw, p)); } z3-z3-4.4.1/src/tactic/bv/bit_blaster_tactic.h000066400000000000000000000012171260446376700210650ustar00rootroot00000000000000/*++ 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" #include"bit_blaster_rewriter.h" class ast_manager; class tactic; tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_bit_blaster_tactic(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p = params_ref()); /* ADD_TACTIC("bit-blast", "reduce bit-vector expressions into SAT.", "mk_bit_blaster_tactic(m, p)") */ #endif z3-z3-4.4.1/src/tactic/bv/bv1_blaster_tactic.cpp000066400000000000000000000417271260446376700213440ustar00rootroot00000000000000/*++ 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" #include"bv_rewriter.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) { bv_rewriter rw(g.m()); if (!rw.hi_div0()) return false; bv1_blaster_tactic t(g.m()); return t.is_target(g); } }; probe * mk_is_qfbv_eq_probe() { return alloc(is_qfbv_eq_probe); } z3-z3-4.4.1/src/tactic/bv/bv1_blaster_tactic.h000066400000000000000000000020161260446376700207750ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/bv/bv_size_reduction_tactic.cpp000066400000000000000000000426301260446376700226470ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/bv/bv_size_reduction_tactic.h000066400000000000000000000014561260446376700223150ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/bv/max_bv_sharing_tactic.cpp000066400000000000000000000260061260446376700221200ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/bv/max_bv_sharing_tactic.h000066400000000000000000000014411260446376700215610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/converter.h000066400000000000000000000061211260446376700166430ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/000077500000000000000000000000001260446376700154135ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/core/blast_term_ite_tactic.cpp000066400000000000000000000147111260446376700224470ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/blast_term_ite_tactic.h000066400000000000000000000013601260446376700221100ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/cofactor_elim_term_ite.cpp000066400000000000000000000602721260446376700226240ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/cofactor_elim_term_ite.h000066400000000000000000000015431260446376700222650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/cofactor_term_ite_tactic.cpp000066400000000000000000000045011260446376700231360ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/cofactor_term_ite_tactic.h000066400000000000000000000011771260446376700226110ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/ctx_simplify_tactic.cpp000066400000000000000000000432401260446376700221630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/ctx_simplify_tactic.h000066400000000000000000000026631260446376700216340ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/der_tactic.cpp000066400000000000000000000051241260446376700202220ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/der_tactic.h000066400000000000000000000006141260446376700176660ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/distribute_forall_tactic.cpp000066400000000000000000000112631260446376700231660ustar00rootroot00000000000000/*++ 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 (!old_q->is_forall()) { return false; } 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-z3-4.4.1/src/tactic/core/distribute_forall_tactic.h000066400000000000000000000007751260446376700226410ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/elim_term_ite_tactic.cpp000066400000000000000000000135041260446376700222670ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/elim_term_ite_tactic.h000066400000000000000000000011221260446376700217250ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/elim_uncnstr_tactic.cpp000066400000000000000000001235071260446376700221600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/elim_uncnstr_tactic.h000066400000000000000000000010631260446376700216150ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/nnf_tactic.cpp000066400000000000000000000071551260446376700202370ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/nnf_tactic.h000066400000000000000000000011451260446376700176750ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/occf_tactic.cpp000066400000000000000000000165111260446376700203640ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/occf_tactic.h000066400000000000000000000014101260446376700200210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/pb_preprocess_tactic.cpp000066400000000000000000000530551260446376700223240ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/pb_preprocess_tactic.h000066400000000000000000000011671260446376700217660ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/propagate_values_tactic.cpp000066400000000000000000000217451260446376700230200ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/propagate_values_tactic.h000066400000000000000000000011441260446376700224540ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/reduce_args_tactic.cpp000066400000000000000000000510131260446376700217310ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/reduce_args_tactic.h000066400000000000000000000011451260446376700213770ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/simplify_tactic.cpp000066400000000000000000000066131260446376700213100ustar00rootroot00000000000000/*++ 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_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-z3-4.4.1/src/tactic/core/simplify_tactic.h000066400000000000000000000030461260446376700207520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/solve_eqs_tactic.cpp000066400000000000000000000760751260446376700214650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/solve_eqs_tactic.h000066400000000000000000000011301260446376700211060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/split_clause_tactic.cpp000066400000000000000000000120361260446376700221370ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/split_clause_tactic.h000066400000000000000000000011111260446376700215740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/symmetry_reduce_tactic.cpp000066400000000000000000000522771260446376700227030ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/symmetry_reduce_tactic.h000066400000000000000000000010051260446376700223270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/tseitin_cnf_tactic.cpp000066400000000000000000000777011260446376700217670ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/core/tseitin_cnf_tactic.h000066400000000000000000000016631260446376700214260ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/equiv_proof_converter.cpp000066400000000000000000000013361260446376700216170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/equiv_proof_converter.h000066400000000000000000000021501260446376700212570ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/extension_model_converter.cpp000066400000000000000000000071361260446376700224610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/extension_model_converter.h000066400000000000000000000022711260446376700221210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/filter_model_converter.cpp000066400000000000000000000037161260446376700217320ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/filter_model_converter.h000066400000000000000000000017551260446376700214000ustar00rootroot00000000000000/*++ 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); }; typedef ref filter_model_converter_ref; #endif z3-z3-4.4.1/src/tactic/fpa/000077500000000000000000000000001260446376700152315ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/fpa/fpa2bv_model_converter.cpp000066400000000000000000000252031260446376700223660ustar00rootroot00000000000000/*++ 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) << ")"; } 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; } expr_ref fpa2bv_model_converter::convert_bv2fp(sort * s, expr * sgn, expr * exp, expr * sig) const { fpa_util fu(m); bv_util bu(m); unsynch_mpz_manager & mpzm = fu.fm().mpz_manager(); unsynch_mpq_manager & mpqm = fu.fm().mpq_manager(); expr_ref res(m); mpf fp_val; unsigned ebits = fu.get_ebits(s); unsigned sbits = fu.get_sbits(s); unsigned sgn_sz = 1; unsigned exp_sz = ebits; unsigned sig_sz = sbits - 1; rational sgn_q(0), sig_q(0), exp_q(0); if (sgn) bu.is_numeral(sgn, sgn_q, sgn_sz); if (exp) bu.is_numeral(exp, exp_q, exp_sz); if (sig) bu.is_numeral(sig, sig_q, sig_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()); fu.fm().set(fp_val, ebits, sbits, !mpqm.is_zero(sgn_q.to_mpq()), sig_z, exp_z); mpzm.del(sig_z); res = fu.mk_value(fp_val); TRACE("fpa2bv_mc", tout << "[" << mk_ismt2_pp(sgn, m) << " " << mk_ismt2_pp(exp, m) << " " << mk_ismt2_pp(sig, m) << "] == " << mk_ismt2_pp(res, m) << std::endl;); fu.fm().del(fp_val); return res; } expr_ref fpa2bv_model_converter::convert_bv2fp(model * bv_mdl, sort * s, expr * bv) const { fpa_util fu(m); bv_util bu(m); SASSERT(bu.is_bv(bv)); unsigned ebits = fu.get_ebits(s); unsigned sbits = fu.get_sbits(s); unsigned bv_sz = sbits + ebits; expr_ref sgn(m), exp(m), sig(m); sgn = bu.mk_extract(bv_sz - 1, bv_sz - 1, bv); exp = bu.mk_extract(bv_sz - 2, sbits - 1, bv); sig = bu.mk_extract(sbits - 2, 0, bv); expr_ref v_sgn(m), v_exp(m), v_sig(m); bv_mdl->eval(sgn, v_sgn); bv_mdl->eval(exp, v_exp); bv_mdl->eval(sig, v_sig); return convert_bv2fp(s, v_sgn, v_exp, v_sig); } expr_ref fpa2bv_model_converter::convert_bv2rm(expr * eval_v) const { fpa_util fu(m); bv_util bu(m); expr_ref res(m); rational bv_val(0); unsigned sz = 0; if (bu.is_numeral(eval_v, bv_val, sz)) { SASSERT(bv_val.is_uint64()); switch (bv_val.get_uint64()) { case BV_RM_TIES_TO_AWAY: res = fu.mk_round_nearest_ties_to_away(); break; case BV_RM_TIES_TO_EVEN: res = fu.mk_round_nearest_ties_to_even(); break; case BV_RM_TO_NEGATIVE: res = fu.mk_round_toward_negative(); break; case BV_RM_TO_POSITIVE: res = fu.mk_round_toward_positive(); break; case BV_RM_TO_ZERO: default: res = fu.mk_round_toward_zero(); } } return res; } expr_ref fpa2bv_model_converter::convert_bv2rm(model * bv_mdl, func_decl * var, expr * val) const { fpa_util fu(m); bv_util bu(m); expr_ref res(m); expr_ref eval_v(m); if (val && bv_mdl->eval(val, eval_v, true)) res = convert_bv2rm(eval_v); return res; } void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { fpa_util fu(m); bv_util bu(m); 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 * val = to_app(it->m_value); SASSERT(fu.is_float(var->get_range())); SASSERT(var->get_range()->get_num_parameters() == 2); expr_ref sgn(m), sig(m), exp(m); bv_mdl->eval(val->get_arg(0), sgn, true); bv_mdl->eval(val->get_arg(1), exp, true); bv_mdl->eval(val->get_arg(2), sig, true); SASSERT(val->is_app_of(fu.get_family_id(), OP_FPA_FP)); #ifdef Z3DEBUG SASSERT(to_app(val->get_arg(0))->get_decl()->get_arity() == 0); SASSERT(to_app(val->get_arg(1))->get_decl()->get_arity() == 0); SASSERT(to_app(val->get_arg(2))->get_decl()->get_arity() == 0); seen.insert(to_app(val->get_arg(0))->get_decl()); seen.insert(to_app(val->get_arg(1))->get_decl()); seen.insert(to_app(val->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(val->get_arg(0))->get_arg(0))->get_decl()); #endif if (!sgn && !sig && !exp) continue; expr_ref cv(m); cv = convert_bv2fp(var->get_range(), sgn, exp, sig); float_mdl->register_decl(var, cv); TRACE("fpa2bv_mc", tout << var->get_name() << " == " << mk_ismt2_pp(cv, m) << std::endl;); } for (obj_map::iterator it = m_rm_const2bv.begin(); it != m_rm_const2bv.end(); it++) { func_decl * var = it->m_key; SASSERT(fu.is_rm(var->get_range())); expr * val = it->m_value; expr_ref fv(m); fv = convert_bv2rm(bv_mdl, var, val); TRACE("fpa2bv_mc", tout << var->get_name() << " == " << mk_ismt2_pp(fv, m) << std::endl;); float_mdl->register_decl(var, fv); seen.insert(to_app(val)->get_decl()); } for (obj_map::iterator it = m_uf2bvuf.begin(); it != m_uf2bvuf.end(); it++) { seen.insert(it->m_value); func_decl * f = it->m_key; unsigned arity = f->get_arity(); sort * rng = f->get_range(); func_interp * flt_fi = alloc(func_interp, m, f->get_arity()); func_interp * bv_fi = bv_mdl->get_func_interp(it->m_value); SASSERT(bv_fi->args_are_values()); for (unsigned i = 0; i < bv_fi->num_entries(); i++) { func_entry const * bv_fe = bv_fi->get_entry(i); expr * const * bv_args = bv_fe->get_args(); expr_ref_buffer new_args(m); for (unsigned j = 0; j < arity; j++) { sort * dj = f->get_domain(j); expr * aj = bv_args[j]; if (fu.is_float(dj)) new_args.push_back(convert_bv2fp(bv_mdl, dj, aj)); else if (fu.is_rm(dj)) { expr_ref fv(m); fv = convert_bv2rm(aj); new_args.push_back(fv); } else new_args.push_back(aj); } expr_ref ret(m); ret = bv_fe->get_result(); if (fu.is_float(rng)) ret = convert_bv2fp(bv_mdl, rng, ret); else if (fu.is_rm(rng)) ret = convert_bv2rm(ret); flt_fi->insert_new_entry(new_args.c_ptr(), ret); } expr_ref els(m); els = bv_fi->get_else(); if (fu.is_float(rng)) els = convert_bv2fp(bv_mdl, rng, els); else if (fu.is_rm(rng)) els = convert_bv2rm(els); flt_fi->set_else(els); float_mdl->register_decl(f, flt_fi); } // 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) { return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv, uf2bvuf); } z3-z3-4.4.1/src/tactic/fpa/fpa2bv_model_converter.h000066400000000000000000000060061260446376700220330ustar00rootroot00000000000000/*++ 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; public: fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, obj_map const & rm_const2bv, obj_map const & uf2bvuf) : 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); } } virtual ~fpa2bv_model_converter() { 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); } 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); expr_ref convert_bv2fp(sort * s, expr * sgn, expr * exp, expr * sig) const; expr_ref convert_bv2fp(model * bv_mdl, sort * s, expr * bv) const; expr_ref convert_bv2rm(expr * eval_v) const; expr_ref convert_bv2rm(model * bv_mdl, func_decl * var, expr * val) const; }; model_converter * mk_fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, obj_map const & rm_const2bv, obj_map const & uf2bvuf); #endifz3-z3-4.4.1/src/tactic/fpa/fpa2bv_tactic.cpp000066400000000000000000000127201260446376700204460ustar00rootroot00000000000000/*++ 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()); 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-z3-4.4.1/src/tactic/fpa/fpa2bv_tactic.h000066400000000000000000000007721260446376700201170ustar00rootroot00000000000000/*++ 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_H_ #define FPA2BV_TACTIC_H_ #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-z3-4.4.1/src/tactic/fpa/qffp_tactic.cpp000066400000000000000000000075571260446376700202360ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/fpa/qffp_tactic.h000066400000000000000000000016461260446376700176740ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/goal.cpp000066400000000000000000000470701260446376700161210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/goal.h000066400000000000000000000162341260446376700155640ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/goal_num_occurs.cpp000066400000000000000000000006421260446376700203500ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/goal_num_occurs.h000066400000000000000000000010121260446376700200050ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/goal_shared_occs.cpp000066400000000000000000000007761260446376700204600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/goal_shared_occs.h000066400000000000000000000021051260446376700201110ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/goal_util.cpp000066400000000000000000000010761260446376700171520ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/goal_util.h000066400000000000000000000004351260446376700166150ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/horn_subsume_model_converter.cpp000066400000000000000000000142051260446376700231510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/horn_subsume_model_converter.h000066400000000000000000000034501260446376700226160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/model_converter.cpp000066400000000000000000000075761260446376700203750ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/model_converter.h000066400000000000000000000030101260446376700200150ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/nlsat_smt/000077500000000000000000000000001260446376700164675ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/nlsat_smt/nl_purify_tactic.cpp000066400000000000000000000676211260446376700225450ustar00rootroot00000000000000/*++ 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" #include "ast_util.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 ptr_vector m_ctx_asms; // assumptions passed by context obj_hashtable m_ctx_asms_set; // assumptions passed by context obj_hashtable m_used_asms; obj_map m_bool2dep; 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; proof_ref_vector m_proofs; 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_proofs(m), m_mode(mode_interface_var) { } virtual ~rw_cfg() {} arith_util & u() { return m_owner.m_util; } expr * mk_interface_var(expr* arg, proof_ref& arg_pr) { 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); } if (m_owner.m_produce_proofs) { arg_pr = m.mk_oeq(arg, r); } 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, proof_ref& pr) { 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), old_pred)); } if (pol != pol_pos) { m_owner.m_nl_g->assert_expr(m.mk_or(result, m.mk_not(old_pred))); } if (m_owner.m_produce_proofs) { pr = m.mk_oeq(old_pred, result); } 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, pr); 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, pr); return BR_DONE; default: return BR_FAILED; } } return BR_FAILED; } // (+ (f x) y) // (f (+ x y)) // bool is_arith_op(expr* e) { return is_app(e) && to_app(e)->get_family_id() == u().get_family_id(); } br_status reduce_app_real(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref & pr) { bool has_interface = false; bool is_arith = false; if (f->get_family_id() == u().get_family_id()) { switch (f->get_decl_kind()) { case OP_NUM: case OP_IRRATIONAL_ALGEBRAIC_NUM: return BR_FAILED; default: is_arith = true; break; } } m_args.reset(); m_proofs.reset(); for (unsigned i = 0; i < num; ++i) { expr* arg = args[i]; proof_ref arg_pr(m); if (is_arith && !is_arith_op(arg)) { has_interface = true; m_args.push_back(mk_interface_var(arg, arg_pr)); } else if (!is_arith && u().is_real(arg)) { has_interface = true; m_args.push_back(mk_interface_var(arg, arg_pr)); } else { m_args.push_back(arg); } if (arg_pr) { m_proofs.push_back(arg_pr); } } if (has_interface) { result = m.mk_app(f, num, m_args.c_ptr()); if (m_owner.m_produce_proofs) { pr = m.mk_oeq_congruence(m.mk_app(f, num, args), to_app(result), m_proofs.size(), m_proofs.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_with_dependencies(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 const& g, goal_ref_buffer& result, expr_dependency_ref& core, 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)) { core2result(core, g, 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 (nl_mc.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 ecore, asms; expr_ref_vector clause(m); expr_ref fml(m); get_unsat_core(ecore, asms); // // assumptions should also be used for the nlsat tactic, // but since it does not support assumptions at this time // we overapproximate the necessary core and accumulate // all assumptions that are ever used. // for (unsigned i = 0; i < asms.size(); ++i) { m_used_asms.insert(asms[i]); } if (ecore.empty()) { core2result(core, g, result); break; } for (unsigned i = 0; i < ecore.size(); ++i) { clause.push_back(mk_not(m, ecore[i])); } fml = mk_or(m, 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 get_unsat_core(ptr_vector& core, ptr_vector& asms) { m_solver->get_unsat_core(core); for (unsigned i = 0; i < core.size(); ++i) { if (m_ctx_asms_set.contains(core[i])) { asms.push_back(core[i]); core[i] = core.back(); core.pop_back(); --i; } } } void core2result(expr_dependency_ref & lcore, goal_ref const& g, goal_ref_buffer& result) { result.reset(); proof * pr = 0; lcore = 0; g->reset(); obj_hashtable::iterator it = m_used_asms.begin(), end = m_used_asms.end(); for (; it != end; ++it) { lcore = m.mk_join(lcore, m.mk_leaf(m_bool2dep.find(*it))); } g->assert_expr(m.mk_false(), pr, lcore); TRACE("nlsat_smt", g->display_with_dependencies(tout);); result.push_back(g.get()); } void setup_assumptions(model_ref& mdl) { m_asms.reset(); m_asms.append(m_ctx_asms.size(), m_ctx_asms.c_ptr()); 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" << m_asms << "\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(), g->pr(i), g->dep(i)); } } } 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 collect_statistics(statistics & st) const { m_nl_tac->collect_statistics(st); m_solver->collect_statistics(st); } virtual void reset_statistics() { m_nl_tac->reset_statistics(); } 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(); m_ctx_asms.reset(); m_ctx_asms_set.reset(); m_used_asms.reset(); m_bool2dep.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); expr_ref_vector clauses(m); 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); if (!g->unsat_core_enabled()) { remove_pure_arith(g); } get_polarities(*g.get()); r.set_bool_mode(); rewrite_goal(r, g); extract_clauses_and_dependencies(g, clauses, m_ctx_asms, m_bool2dep, m_fmc); TRACE("nlsat_smt", tout << clauses << "\n";); for (unsigned i = 0; i < m_ctx_asms.size(); ++i) { m_ctx_asms_set.insert(m_ctx_asms[i]); } for (unsigned i = 0; i < clauses.size(); ++i) { m_solver->assert_expr(clauses[i].get()); } g->inc_depth(); solve(g, result, core, mc); } }; tactic * mk_nl_purify_tactic(ast_manager& m, params_ref const& p) { return alloc(nl_purify_tactic, m, p); } z3-z3-4.4.1/src/tactic/nlsat_smt/nl_purify_tactic.h000066400000000000000000000013021260446376700221720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/portfolio/000077500000000000000000000000001260446376700165005ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/portfolio/default_tactic.cpp000066400000000000000000000035261260446376700221650ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/portfolio/default_tactic.h000066400000000000000000000006541260446376700216310ustar00rootroot00000000000000/*++ 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_H_ #define DEFAULT_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_default_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-z3-4.4.1/src/tactic/portfolio/smt_strategic_solver.cpp000066400000000000000000000073441260446376700234560ustar00rootroot00000000000000/*++ 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" #include"inc_sat_solver.h" #include"bv_rewriter.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); } static solver* mk_solver_for_logic(ast_manager & m, params_ref const & p, symbol const& logic) { bv_rewriter rw(m); if (logic == "QF_BV" && rw.hi_div0()) return mk_inc_sat_solver(m, p); return mk_smt_solver(m, p, logic); } 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_solver_for_logic(m, p, 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-z3-4.4.1/src/tactic/portfolio/smt_strategic_solver.h000066400000000000000000000006451260446376700231200ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/probe.cpp000066400000000000000000000267571260446376700163170ustar00rootroot00000000000000/*++ 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" #include"bv_rewriter.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) { bv_rewriter rw(g.m()); if (!rw.hi_div0()) return false; 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-z3-4.4.1/src/tactic/probe.h000066400000000000000000000073561260446376700157560ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/proof_converter.cpp000066400000000000000000000113441260446376700204060ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/proof_converter.h000066400000000000000000000027021260446376700200510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/replace_proof_converter.cpp000066400000000000000000000045671260446376700221120ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/replace_proof_converter.h000066400000000000000000000020751260446376700215470ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/sls/000077500000000000000000000000001260446376700152645ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/sls/bvsls_opt_engine.cpp000066400000000000000000000301271260446376700213330ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/sls/bvsls_opt_engine.h000066400000000000000000000043531260446376700210020ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/sls/sls_engine.cpp000066400000000000000000000514701260446376700201250ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/sls/sls_engine.h000066400000000000000000000100251260446376700175610ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/sls/sls_evaluator.h000066400000000000000000001002601260446376700203170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/sls/sls_params.pyg000066400000000000000000000040061260446376700201510ustar00rootroot00000000000000def_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-z3-4.4.1/src/tactic/sls/sls_powers.h000066400000000000000000000015631260446376700176420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/sls/sls_tactic.cpp000066400000000000000000000103521260446376700201210ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/sls/sls_tactic.h000066400000000000000000000007651260446376700175750ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/sls/sls_tracker.h000066400000000000000000001107261260446376700177600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/000077500000000000000000000000001260446376700164675ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/smtlogics/nra_tactic.cpp000066400000000000000000000021271260446376700213040ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/nra_tactic.h000066400000000000000000000004531260446376700207510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfaufbv_tactic.cpp000066400000000000000000000037331260446376700221620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfaufbv_tactic.h000066400000000000000000000005651260446376700216270ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfauflia_tactic.cpp000066400000000000000000000025341260446376700223160ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfauflia_tactic.h000066400000000000000000000005721260446376700217630ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfbv_tactic.cpp000066400000000000000000000112141260446376700214570ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfbv_tactic.h000066400000000000000000000011751260446376700211310ustar00rootroot00000000000000/*++ 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_H_ #define QFBV_TACTIC_H_ #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-z3-4.4.1/src/tactic/smtlogics/qfidl_tactic.cpp000066400000000000000000000076251260446376700216330ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfidl_tactic.h000066400000000000000000000005531260446376700212710ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qflia_tactic.cpp000066400000000000000000000211701260446376700216170ustar00rootroot00000000000000/*++ 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 annotate_tactic("no-cut-smt-tactic", 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 annotate_tactic("no-cut-relevancy-tactic", 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 annotate_tactic( "pb-tactic", 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 annotate_tactic( "lia2sat-tactic", 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 annotate_tactic( "ilp-model-finder-tactic", 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 annotate_tactic( "bounded-tactic", 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-z3-4.4.1/src/tactic/smtlogics/qflia_tactic.h000066400000000000000000000011201260446376700212550ustar00rootroot00000000000000/*++ 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_H_ #define QFLIA_TACTIC_H_ #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-z3-4.4.1/src/tactic/smtlogics/qflra_tactic.cpp000066400000000000000000000045201260446376700216300ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qflra_tactic.h000066400000000000000000000007211260446376700212740ustar00rootroot00000000000000/*++ 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_H_ #define QFLRA_TACTIC_H_ #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-z3-4.4.1/src/tactic/smtlogics/qfnia_tactic.cpp000066400000000000000000000054411260446376700216240ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfnia_tactic.h000066400000000000000000000007211260446376700212650ustar00rootroot00000000000000/*++ 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_H_ #define QFNIA_TACTIC_H_ #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-z3-4.4.1/src/tactic/smtlogics/qfnra_tactic.cpp000066400000000000000000000026771260446376700216450ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfnra_tactic.h000066400000000000000000000007211260446376700212760ustar00rootroot00000000000000/*++ 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_H_ #define QFNRA_TACTIC_H_ #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-z3-4.4.1/src/tactic/smtlogics/qfuf_tactic.cpp000066400000000000000000000016221260446376700214640ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfuf_tactic.h000066400000000000000000000005571260446376700211370ustar00rootroot00000000000000/*++ 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_H_ #define QFUF_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfuf_tactic(ast_manager & m, params_ref const & p); #endif z3-z3-4.4.1/src/tactic/smtlogics/qfufbv_tactic.cpp000066400000000000000000000027141260446376700220170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfufbv_tactic.h000066400000000000000000000005611260446376700214620ustar00rootroot00000000000000/*++ 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_H_ #define QFUFBV_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfufbv_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-z3-4.4.1/src/tactic/smtlogics/qfufnra_tactic.cpp000066400000000000000000000026151260446376700221700ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/qfufnra_tactic.h000066400000000000000000000007411260446376700216330ustar00rootroot00000000000000/*++ 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_H_ #define QFUFNRA_TACTIC_H_ #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-z3-4.4.1/src/tactic/smtlogics/quant_tactics.cpp000066400000000000000000000072511260446376700220420ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/smtlogics/quant_tactics.h000066400000000000000000000015341260446376700215050ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/tactic.cpp000066400000000000000000000166261260446376700164510ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/tactic.h000066400000000000000000000137051260446376700161110ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/tactic_exception.h000066400000000000000000000015201260446376700201570ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/tactical.cpp000066400000000000000000001675041260446376700167700ustar00rootroot00000000000000/*++ 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 annotate_tactical : public unary_tactical { std::string m_name; struct scope { std::string m_name; scope(std::string const& name) : m_name(name) { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(" << m_name << " start)\n";); } ~scope() { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(" << m_name << " done)\n";); } }; public: annotate_tactical(char const* name, tactic* t): unary_tactical(t), m_name(name) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { scope _scope(m_name); m_t->operator()(in, result, mc, pc, core); } virtual tactic * translate(ast_manager & m) { tactic * new_t = m_t->translate(m); return alloc(annotate_tactical, m_name.c_str(), new_t); } }; tactic * annotate_tactic(char const* name, tactic * t) { return alloc(annotate_tactical, name, t); } 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-z3-4.4.1/src/tactic/tactical.h000066400000000000000000000070371260446376700164270ustar00rootroot00000000000000/*++ 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); tactic * annotate_tactic(char const* name, tactic * t); // 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-z3-4.4.1/src/tactic/ufbv/000077500000000000000000000000001260446376700154255ustar00rootroot00000000000000z3-z3-4.4.1/src/tactic/ufbv/macro_finder_tactic.cpp000066400000000000000000000117361260446376700221200ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/ufbv/macro_finder_tactic.h000066400000000000000000000007511260446376700215600ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/ufbv/quasi_macros_tactic.cpp000066400000000000000000000120571260446376700221530ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/ufbv/quasi_macros_tactic.h000066400000000000000000000007571260446376700216240ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/ufbv/ufbv_rewriter.cpp000066400000000000000000000760661260446376700210350ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/ufbv/ufbv_rewriter.h000066400000000000000000000264051260446376700204720ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/ufbv/ufbv_rewriter_tactic.cpp000066400000000000000000000073331260446376700223530ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/ufbv/ufbv_rewriter_tactic.h000066400000000000000000000006271260446376700220170ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/ufbv/ufbv_tactic.cpp000066400000000000000000000046661260446376700204360ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/tactic/ufbv/ufbv_tactic.h000066400000000000000000000013011260446376700200620ustar00rootroot00000000000000/*++ 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-z3-4.4.1/src/test/000077500000000000000000000000001260446376700141735ustar00rootroot00000000000000z3-z3-4.4.1/src/test/algebraic.cpp000066400000000000000000000443011260446376700166120ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: algebraic.cpp Abstract: Test Algebraic Numbers Author: Leonardo (leonardo) 2011-11-22 Notes: --*/ #include"algebraic_numbers.h" #include"polynomial_var2value.h" #include"mpbq.h" static void display_anums(std::ostream & out, scoped_anum_vector const & rs) { out << "numbers in decimal:\n"; algebraic_numbers::manager & m = rs.m(); for (unsigned i = 0; i < rs.size(); i++) { m.display_decimal(out, rs[i], 10); out << "\n"; } out << "numbers as root objects\n"; for (unsigned i = 0; i < rs.size(); i++) { m.display_root(out, rs[i]); out << "\n"; } out << "numbers as intervals\n"; for (unsigned i = 0; i < rs.size(); i++) { m.display_interval(out, rs[i]); out << "\n"; } } static void tst1() { unsynch_mpq_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); p = 3*x - 2; algebraic_numbers::manager am(nm); scoped_anum_vector rs1(am); std::cout << "p: " << p << "\n"; am.isolate_roots(p, rs1); display_anums(std::cout, rs1); SASSERT(rs1.size() == 1); std::cout.flush(); p = (x^2) - 2; std::cout << "p: " << p << "\n"; rs1.reset(); am.isolate_roots(p, rs1); display_anums(std::cout, rs1); SASSERT(rs1.size() == 2); scoped_anum sqrt2(am); am.set(sqrt2, rs1[1]); scoped_mpq q(nm); nm.set(q, 1, 3); scoped_anum aq(am); am.set(aq, q); // create algebraic number representing 1/3 am.add(sqrt2, aq, aq); std::cout << "sqrt(2) + 1/3: "; am.display_decimal(std::cout, aq, 10); std::cout << " "; am.display_interval(std::cout, aq); std::cout << " "; am.display_root(std::cout, aq); std::cout << "\n"; am.set(aq, q); am.add(rs1[0], aq, aq); std::cout << "-sqrt(2) + 1/3: "; am.display_decimal(std::cout, aq, 10); std::cout << " "; am.display_interval(std::cout, aq); std::cout << " "; am.display_root(std::cout, aq); std::cout << "\n"; p = ((x^5) - x - 1)*(x-1)*(x-2); std::cout << "p: " << p << "\n"; rs1.reset(); am.isolate_roots(p, rs1); display_anums(std::cout, rs1); SASSERT(rs1.size() == 3); scoped_anum gauss(am); am.set(gauss, rs1[1]); std::cout << "compare(" << sqrt2 << ", " << gauss << "): " << am.compare(sqrt2, gauss) << "\n"; statistics st; am.collect_statistics(st); st.display_smt2(std::cout); p = ((x^2) - 2)*((x^2) - 3); std::cout << "p: " << p << "\n"; rs1.reset(); am.isolate_roots(p, rs1); display_anums(std::cout, rs1); SASSERT(rs1.size() == 4); scoped_anum hidden_sqrt2(am); am.set(hidden_sqrt2, rs1[2]); std::cout << "compare(" << sqrt2 << ", " << hidden_sqrt2 << "): " << am.compare(sqrt2, hidden_sqrt2) << "\n"; st.reset(); am.collect_statistics(st); st.display_smt2(std::cout); std::cout << "sqrt(2)^4: " << (sqrt2^4) << "\n"; SASSERT(is_int(power(sqrt2, 4))); SASSERT(power(sqrt2, 4) == 4); scoped_anum sqrt2_gauss(am); am.add(sqrt2, gauss, sqrt2_gauss); std::cout << "sqrt2 + gauss: " << sqrt2_gauss << " "; am.display_root(std::cout, sqrt2_gauss); std::cout << "\n"; std::cout << "sqrt2*sqrt2: " << sqrt2*sqrt2 << "\n"; std::cout << "sqrt2*sqrt2 == 2: " << (sqrt2*sqrt2 == 2) << std::endl; scoped_anum three(am); am.set(three, -3); std::cout << "(-3)^(1/5): " << root(three, 5) << "\n"; std::cout << "sqrt(2)^(1/3): " << root(sqrt2, 3) << "\n"; std::cout << "as-root-object(sqrt(2)^(1/3)): " << root_obj_pp(root(sqrt2, 3)) << "\n"; std::cout << "(sqrt(2) + 1)^(1/3): " << root(sqrt2 + 1, 3) << "\n"; std::cout << "as-root-object((sqrt(2) + 1)^(1/3)): " << root_obj_pp(root(sqrt2 + 1, 3)) << "\n"; std::cout << "(sqrt(2) + gauss)^(1/5): " << root(sqrt2 + gauss, 5) << "\n"; std::cout << "as-root-object(sqrt(2) + gauss)^(1/5): " << root_obj_pp(root(sqrt2 + gauss, 5)) << "\n"; std::cout << "(sqrt(2) / sqrt(2)): " << sqrt2 / hidden_sqrt2 << "\n"; std::cout << "(sqrt(2) / gauss): " << sqrt2 / gauss << "\n"; std::cout << "(sqrt(2) / gauss) 30 digits: " << decimal_pp(sqrt2 / gauss, 30) << "\n"; std::cout << "as-root-object(sqrt(2) / gauss): " << root_obj_pp(sqrt2 / gauss) << "\n"; std::cout << "is_int(sqrt(2)^(1/3)): " << am.is_int(root(sqrt2, 3)) << "\n"; scoped_anum tmp(am); scoped_anum four(am); am.set(four, 4); am.set(tmp, sqrt2); am.inv(tmp); std::cout << "1/sqrt(2): " << tmp << "\n"; am.mul(tmp, four, tmp); std::cout << "4*1/sqrt(2): " << tmp << " " << root_obj_pp(tmp) << "\n"; am.mul(tmp, sqrt2, tmp); std::cout << "sqrt(2)*4*(1/sqrt2): " << tmp << " " << root_obj_pp(tmp) << "\n"; std::cout << "is_int(sqrt(2)*4*(1/sqrt2)): " << am.is_int(tmp) << ", after is-int: " << tmp << "\n"; p = (998*x - 1414)*((x^2) - 15); std::cout << "p: " << p << "\n"; rs1.reset(); am.isolate_roots(p, rs1); std::cout << "is-rational(sqrt2): " << am.is_rational(sqrt2) << "\n"; scoped_anum qr(am); am.set(qr, rs1[1]); std::cout << "qr: " << root_obj_pp(qr); std::cout << ", is-rational: " << am.is_rational(qr) << ", val: " << root_obj_pp(qr) << "\n"; return; std::cout << "compare(" << sqrt2 << ", " << gauss << "): " << am.compare(sqrt2, gauss) << "\n"; p = (x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225; std::cout << "p: " << p << "\n"; rs1.reset(); am.isolate_roots(p, rs1); display_anums(std::cout, rs1); } void tst_refine_mpbq(int n, int d) { unsynch_mpq_manager qm; mpbq_manager bqm(qm); scoped_mpq q1(qm); qm.set(q1, n, d); scoped_mpbq l(bqm); scoped_mpbq u(bqm); std::cout << "using refine upper...\n"; bqm.to_mpbq(q1, l); bqm.set(u, l); bqm.mul2(u); for (unsigned i = 0; i < 20; i++) { std::cout << l << " < " << q1 << " < " << u << "\n"; bqm.display_decimal(std::cout, l, 20); std::cout << " < "; qm.display_decimal(std::cout, q1, 20); std::cout << " < "; bqm.display_decimal(std::cout, u, 20); std::cout << std::endl; bqm.refine_upper(q1, l, u); } std::cout << "using refine lower...\n"; bqm.to_mpbq(q1, l); bqm.set(u, l); bqm.mul2(u); for (unsigned i = 0; i < 20; i++) { std::cout << l << " < " << q1 << " < " << u << "\n"; bqm.display_decimal(std::cout, l, 20); std::cout << " < "; qm.display_decimal(std::cout, q1, 20); std::cout << " < "; bqm.display_decimal(std::cout, u, 20); std::cout << std::endl; bqm.refine_lower(q1, l, u); } } void tst_refine_mpbq() { tst_refine_mpbq(5, 7); } void tst_mpbq_root() { unsynch_mpq_manager qm; mpbq_manager bqm(qm); // scoped_mpbq q(bqm); // q.set(q1, 1.4142135 , 7); } static void tst_wilkinson() { // Test Wilkinson Polynomial unsynch_mpq_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); for (int i = 1; i <= 20; i++) { if (i > 1) p = p*(x - i); else p = (x - i); } std::cout << "Wilkinson's polynomial: " << p << "\n"; algebraic_numbers::manager am(nm); scoped_anum_vector rs1(am); std::cout << "p: " << p << "\n"; am.isolate_roots(p, rs1); display_anums(std::cout, rs1); SASSERT(rs1.size() == 20); for (unsigned i = 0; i < rs1.size(); i++) { SASSERT(am.is_int(rs1[i])); } } static void tst_dejan() { unsynch_mpq_manager qm; algebraic_numbers::manager am(qm); scoped_anum two101(am); am.set(two101, 2); am.root(two101, 11, two101); scoped_anum two103(am); am.set(two103, 2); am.root(two103, 7, two103); std::cout << "two101: " << two101 << " " << root_obj_pp(two101) << std::endl; std::cout << "two103: " << two103 << " " << root_obj_pp(two103) << std::endl; scoped_anum sum1(am); am.add(two103, two101, sum1); std::cout << "sum1: " << sum1 << " " << root_obj_pp(sum1) << "\n"; } static void tst_select_small(mpbq_manager & m, scoped_mpbq const & l, scoped_mpbq const & u, bool expected) { scoped_mpbq r(m); std::cout << "----------\n"; std::cout << "lower: " << l << " as decimal: "; m.display_decimal(std::cout, l); std::cout << std::endl; std::cout << "upper: " << u << " as decimal: "; m.display_decimal(std::cout, u); std::cout << std::endl; VERIFY(m.select_small(l, u, r) == expected); std::cout << "choice: " << r << " as decimal: "; m.display_decimal(std::cout, r); std::cout << std::endl; } static void tst_select_small(mpbq_manager & m, int64 n1, unsigned k1, int64 n2, unsigned k2, bool expected) { scoped_mpbq l(m); scoped_mpbq u(m); m.set(l, n1, k1); m.set(u, n2, k2); tst_select_small(m, l, u, expected); } static void tst_select_small() { unsynch_mpz_manager m; mpbq_manager bqm(m); tst_select_small(bqm, 1, 3, 3, 2, true); tst_select_small(bqm, 10000000000000ll, 40, 11000, 10, true); tst_select_small(bqm, 10000000000000ll, 40, 10001, 10, true); tst_select_small(bqm, 1, 0, 1, 0, true); tst_select_small(bqm, 1, 0, 2, 0, true); tst_select_small(bqm, -1, 0, -1, 0, true); tst_select_small(bqm, -2, 0, -1, 0, true); tst_select_small(bqm, 0, 0, 1100, 10, true); tst_select_small(bqm, 7, 3, 1001, 10, true); tst_select_small(bqm, 1000, 10, 1001, 10, true); scoped_mpbq l1(bqm); l1 = 11; bqm.power(l1, 64, l1); scoped_mpbq l2(bqm); l2 = l1 + 1; bqm.div2k(l1, 64*3); bqm.div2k(l2, 64*3); tst_select_small(bqm, l1, l2, true); l1 = 11; bqm.power(l1, 64, l1); l2 = l1 + 256; bqm.div2k(l1, 64*3); bqm.div2k(l2, 64*3); tst_select_small(bqm, l1, l2, true); } static void tst_eval_sign(polynomial_ref const & p, anum_manager & am, polynomial::var x0, anum const & v0, polynomial::var x1, anum const & v1, polynomial::var x2, anum const & v2, int expected) { polynomial::simple_var2value x2v(am); x2v.push_back(x0, v0); x2v.push_back(x1, v1); x2v.push_back(x2, v2); std::cout << "--------------\n"; std::cout << "p: " << p << "\n"; std::cout << "x0 -> "; am.display_root(std::cout, v0); std::cout << "\n"; std::cout << "x1 -> "; am.display_root(std::cout, v1); std::cout << "\n"; std::cout << "x2 -> "; am.display_root(std::cout, v2); std::cout << "\n"; int s = am.eval_sign_at(p, x2v); SASSERT((s == 0) == (expected == 0)); SASSERT((s < 0) == (expected < 0)); SASSERT((s > 0) == (expected > 0)); std::cout << "sign: " << s << "\n"; } static void tst_eval_sign() { enable_trace("anum_eval_sign"); unsynch_mpq_manager qm; polynomial::manager pm(qm); algebraic_numbers::manager am(qm); polynomial_ref x0(pm); polynomial_ref x1(pm); polynomial_ref x2(pm); x0 = pm.mk_polynomial(pm.mk_var()); x1 = pm.mk_polynomial(pm.mk_var()); x2 = pm.mk_polynomial(pm.mk_var()); polynomial_ref p(pm); p = x0*x1 + (x1^2) + x2 + 2; scoped_anum v0(am), v1(am), v2(am); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 1); am.set(v2, -2); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 0); am.set(v1, 1); am.set(v0, -3); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, -1); am.set(v0, 2); am.root(v0, 2, v0); am.set(v1, 0); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 0); am.set(v2, 1); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 1); am.set(v2, -3); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, -1); am.set(v1, 1); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 1); am.set(v2, -4); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 1); am.set(v2, -5); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, -1); am.set(v2, -2); am.set(v1, v0); am.neg(v1); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 0); am.set(v2, -3); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, -1); p = x0*x1 + (x1^2) - x2 + 2; tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 1); } static void tst_isolate_roots(polynomial_ref const & p, anum_manager & am, polynomial::var x0, anum const & v0, polynomial::var x1, anum const & v1, polynomial::var x2, anum const & v2) { polynomial::simple_var2value x2v(am); x2v.push_back(x0, v0); x2v.push_back(x1, v1); x2v.push_back(x2, v2); std::cout << "--------------\n"; std::cout << "p: " << p << "\n"; std::cout << "x0 -> "; am.display_root(std::cout, v0); std::cout << "\n"; std::cout << "x1 -> "; am.display_root(std::cout, v1); std::cout << "\n"; std::cout << "x2 -> "; am.display_root(std::cout, v2); std::cout << "\n"; scoped_anum_vector roots(am); svector signs; am.isolate_roots(p, x2v, roots, signs); SASSERT(roots.size() + 1 == signs.size()); std::cout << "roots:\n"; for (unsigned i = 0; i < roots.size(); i++) { am.display_root(std::cout, roots[i]); std::cout << " "; am.display_decimal(std::cout, roots[i]); std::cout << "\n"; } std::cout << "signs:\n"; for (unsigned i = 0; i < signs.size(); i++) { if (i > 0) std::cout << " 0 "; if (signs[i] < 0) std::cout << "-"; else if (signs[i] == 0) std::cout << "0"; else std::cout << "+"; } std::cout << "\n"; } static void tst_isolate_roots() { enable_trace("isolate_roots"); unsynch_mpq_manager qm; polynomial::manager pm(qm); algebraic_numbers::manager am(qm); polynomial_ref x0(pm); polynomial_ref x1(pm); polynomial_ref x2(pm); polynomial_ref x3(pm); x0 = pm.mk_polynomial(pm.mk_var()); x1 = pm.mk_polynomial(pm.mk_var()); x2 = pm.mk_polynomial(pm.mk_var()); x3 = pm.mk_polynomial(pm.mk_var()); polynomial_ref p(pm); p = x3*x1 + 1; scoped_anum v0(am), v1(am), v2(am); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); am.set(v1, 1); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); am.set(v1, 2); am.root(v1, 2, v1); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 + x2)*x3 + 1; am.set(v2, v1); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 + x2)*x3 + x1*x2 + 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 + x2)*(x3^3) + x1*x2 + 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 + x2)*(x3^2) - x1*x2 - 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = x0*(x1 + x2)*(x3^2) - x0*x1*x2 - 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 - x2)*x3 + x1*x2 - 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 - x2)*(x3^3) + x1*x2 - 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x3 - x0)*(x3 - x0 - x1); am.set(v0, 2); am.root(v0, 2, v0); // x2 -> sqrt(2) am.set(v1, 3); am.root(v1, 2, v1); // x1 -> sqrt(3) am.reset(v2); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x3 - x0)*((x3 - x0 - x1)^2); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x3 - x0)*(x3 - 2)*((x3 - 1)^2)*(x3 - x1); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); } static void pp(polynomial_ref const & p, polynomial::var x) { unsigned d = degree(p, x); for (unsigned i = 0; i <= d; i++) { std::cout << "(" << coeff(p, x, i) << ") "; } std::cout << "\n"; } static void ex1() { unsynch_mpq_manager qm; polynomial::manager pm(qm); polynomial_ref x(pm); polynomial_ref a(pm); polynomial_ref b(pm); polynomial_ref c(pm); x = pm.mk_polynomial(pm.mk_var()); a = pm.mk_polynomial(pm.mk_var()); b = pm.mk_polynomial(pm.mk_var()); c = pm.mk_polynomial(pm.mk_var()); polynomial_ref p(pm); p = (a + 2*b)*(x^3) + x*a + (b^2); polynomial_ref p1(pm); p1 = derivative(p, 0); polynomial_ref h2(pm); unsigned d; h2 = pseudo_remainder(p, p1, 0, d); std::cout << "d: " << d << "\n"; std::cout << "p: "; pp(p, 0); std::cout << "\np': "; pp(p1, 0); std::cout << "\nh2: "; pp(h2, 0); std::cout << "\n"; polynomial_ref h3(pm); h3 = pseudo_remainder(p1, h2, 0, d); std::cout << "d: " << d << "\n"; std::cout << "h3: "; pp(h3, 0); std::cout << "\n"; algebraic_numbers::manager am(qm); scoped_anum v1(am), v2(am); am.set(v1, 2); am.root(v1, 3, v1); am.set(v2, 3); am.root(v2, 3, v2); polynomial::simple_var2value x2v(am); x2v.push_back(1, v1); x2v.push_back(2, v2); std::cout << "sign(h3(v1,v2)): " << am.eval_sign_at(h3, x2v) << "\n"; scoped_anum v0(am); am.set(v0, -1); x2v.push_back(0, v0); std::cout << "sign(h2(v1,v2)): " << am.eval_sign_at(h2, x2v) << "\n"; std::cout << "sign(p'(v1,v2)): " << am.eval_sign_at(p1, x2v) << "\n"; std::cout << "sign(p(v1,v2)): " << am.eval_sign_at(p, x2v) << "\n"; polynomial::simple_var2value x2v2(am); x2v2.push_back(1, v1); x2v2.push_back(2, v2); scoped_mpq tmp(qm); qm.set(tmp, -1); qm.div(tmp, mpz(2), tmp); std::cout << "tmp: "; qm.display(std::cout, tmp); std::cout << " "; qm.display_decimal(std::cout, tmp, 10); std::cout << "\n"; am.set(v0, tmp); x2v2.push_back(0, v0); std::cout << "v0: " << v0 << "\n"; std::cout << "sign(h2(v1,v2)): " << am.eval_sign_at(h2, x2v2) << "\n"; std::cout << "sign(p'(v1,v2)): " << am.eval_sign_at(p1, x2v2) << "\n"; std::cout << "sign(p(v1,v2)): " << am.eval_sign_at(p, x2v2) << "\n"; } static void tst_root() { unsynch_mpq_manager qm; algebraic_numbers::manager am(qm); scoped_anum v1(am), v2(am); am.set(v1, 4); am.root(v1, 2, v2); std::cout << "root: " << v2 << "\n"; am.set(v1, 4); am.root(v1, 4, v2); std::cout << "root: " << root_obj_pp(v2) << "\n"; } void tst_algebraic() { // enable_trace("resultant_bug"); // enable_trace("poly_sign"); disable_trace("algebraic"); // enable_trace("mpbq_bug"); // enable_trace("mpz_mul2k"); // enable_trace("mpz_gcd"); tst_root(); tst_isolate_roots(); ex1(); tst_eval_sign(); tst_select_small(); tst_dejan(); tst_wilkinson(); tst1(); tst_refine_mpbq(); } z3-z3-4.4.1/src/test/api.cpp000066400000000000000000001063741260446376700154630ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include "z3.h" #include "z3_private.h" #include #include "util.h" #include "trace.h" #include #include "trace.h" void bv_invariant() { #define SET(_i, _v) m[_i] = _v #define GET(_ty,_i) reinterpret_cast<_ty>(m[_i]) std::map m; Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg,"MODEL","true"); Z3_context ctx = Z3_mk_context(cfg); Z3_model _m = 0; enable_trace("after_internalization"); enable_trace("final_check"); enable_trace("bv"); enable_trace("propagate_atoms"); enable_trace("assign_core"); enable_trace("bv_bug"); enable_trace("bv_bit_prop"); enable_trace("mark_as_relevant_core"); {SET(0x03BC7FD8, Z3_mk_bv_sort(ctx,32));} {SET(0x03BCCD88, Z3_mk_int(ctx,0,GET(Z3_sort,0x03BC7FD8)));} {SET(0x03BCCE08, Z3_mk_int(ctx,1,GET(Z3_sort,0x03BC7FD8)));} {SET(0x03BC9428, Z3_mk_eq(ctx,GET(Z3_ast,0x03BCCD88),GET(Z3_ast,0x03BCCD88)));} {SET(0x03CEC820, Z3_get_app_decl(ctx,GET(Z3_app,0x03BC9428)));} {Z3_mk_string_symbol(ctx,"null");} {Z3_mk_string_symbol(ctx,"isnull");} {SET(0x03CEC870, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"null"),Z3_mk_string_symbol(ctx,"isnull"),0,0,0,0));} {Z3_mk_string_symbol(ctx,"ArgumentException");} {Z3_mk_string_symbol(ctx,"isArgumentException");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE130[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE160[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE190[1] = {0, }; SET(0x03CEC8C0, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"ArgumentException"),Z3_mk_string_symbol(ctx,"isArgumentException"),1,args03CEE130,args03CEE160,args03CEE190));} {Z3_mk_string_symbol(ctx,"String");} {Z3_mk_string_symbol(ctx,"isString");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE130[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE160[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE190[1] = {0, }; SET(0x03CEC910, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"String"),Z3_mk_string_symbol(ctx,"isString"),1,args03CEE130,args03CEE160,args03CEE190));} {Z3_mk_string_symbol(ctx,"MethodBase");} {Z3_mk_string_symbol(ctx,"isMethodBase");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE130[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE160[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE190[1] = {0, }; SET(0x03CEC960, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"MethodBase"),Z3_mk_string_symbol(ctx,"isMethodBase"),1,args03CEE130,args03CEE160,args03CEE190));} {Z3_mk_string_symbol(ctx,"Exception");} {Z3_mk_string_symbol(ctx,"isException");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE130[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE160[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE190[1] = {0, }; SET(0x03CEC9B0, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Exception"),Z3_mk_string_symbol(ctx,"isException"),1,args03CEE130,args03CEE160,args03CEE190));} {Z3_mk_string_symbol(ctx,"Object");} {Z3_mk_string_symbol(ctx,"isObject");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE130[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE160[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE190[1] = {0, }; SET(0x03CECA00, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Object"),Z3_mk_string_symbol(ctx,"isObject"),1,args03CEE130,args03CEE160,args03CEE190));} {Z3_mk_string_symbol(ctx,"Box");} {Z3_mk_string_symbol(ctx,"isBox");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03CECA50, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Box"),Z3_mk_string_symbol(ctx,"isBox"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"Term");} {Z3_mk_string_symbol(ctx,"isTerm");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE9430, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Term"),Z3_mk_string_symbol(ctx,"isTerm"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"Box");} {Z3_mk_string_symbol(ctx,"isBox");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE9480, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Box"),Z3_mk_string_symbol(ctx,"isBox"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"SystemException");} {Z3_mk_string_symbol(ctx,"isSystemException");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE9520, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"SystemException"),Z3_mk_string_symbol(ctx,"isSystemException"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"RuntimeFieldHandle");} {Z3_mk_string_symbol(ctx,"isRuntimeFieldHandle");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE94D0, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"RuntimeFieldHandle"),Z3_mk_string_symbol(ctx,"isRuntimeFieldHandle"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"Box");} {Z3_mk_string_symbol(ctx,"isBox");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE9570, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Box"),Z3_mk_string_symbol(ctx,"isBox"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"RuntimeTypeHandle");} {Z3_mk_string_symbol(ctx,"isRuntimeTypeHandle");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE95C0, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"RuntimeTypeHandle"),Z3_mk_string_symbol(ctx,"isRuntimeTypeHandle"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"Box");} {Z3_mk_string_symbol(ctx,"isBox");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE9610, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Box"),Z3_mk_string_symbol(ctx,"isBox"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"NullReferenceException");} {Z3_mk_string_symbol(ctx,"isNullReferenceException");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE9660, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"NullReferenceException"),Z3_mk_string_symbol(ctx,"isNullReferenceException"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"InvalidCastException");} {Z3_mk_string_symbol(ctx,"isInvalidCastException");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE96B0, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"InvalidCastException"),Z3_mk_string_symbol(ctx,"isInvalidCastException"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"IndexOutOfRangeException");} {Z3_mk_string_symbol(ctx,"isIndexOutOfRangeException");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE9700, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"IndexOutOfRangeException"),Z3_mk_string_symbol(ctx,"isIndexOutOfRangeException"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"StackOverflowException");} {Z3_mk_string_symbol(ctx,"isStackOverflowException");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE9750, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"StackOverflowException"),Z3_mk_string_symbol(ctx,"isStackOverflowException"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"ExecutionEngineException");} {Z3_mk_string_symbol(ctx,"isExecutionEngineException");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE97A0, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"ExecutionEngineException"),Z3_mk_string_symbol(ctx,"isExecutionEngineException"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"Box");} {Z3_mk_string_symbol(ctx,"isBox");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE97F0, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Box"),Z3_mk_string_symbol(ctx,"isBox"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"Value");} {Z3_mk_string_symbol(ctx,"isValue");} {Z3_mk_string_symbol(ctx,"value");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"value"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE9840, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Value"),Z3_mk_string_symbol(ctx,"isValue"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"Value");} {Z3_mk_string_symbol(ctx,"isValue");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE9890, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Value"),Z3_mk_string_symbol(ctx,"isValue"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"Int32[]");} {Z3_mk_string_symbol(ctx,"isInt32[]");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE160[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE190[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1C0[1] = {0, }; SET(0x03BE98E0, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Int32[]"),Z3_mk_string_symbol(ctx,"isInt32[]"),1,args03CEE160,args03CEE190,args03CEE1C0));} {Z3_mk_string_symbol(ctx,"Add");} {Z3_mk_string_symbol(ctx,"isAdd");} {Z3_mk_string_symbol(ctx,"left");} {Z3_mk_string_symbol(ctx,"right");} {Z3_symbol args03BEA4A0[2] = {Z3_mk_string_symbol(ctx,"left"), Z3_mk_string_symbol(ctx,"right"), }; Z3_sort args03BEA548[2] = {GET(Z3_sort,0x00000000), GET(Z3_sort,0x00000000), }; unsigned args03BEA580[2] = {0, 0, }; SET(0x03BE9930, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Add"),Z3_mk_string_symbol(ctx,"isAdd"),2,args03BEA4A0,args03BEA548,args03BEA580));} {Z3_mk_string_symbol(ctx,"Add");} {Z3_mk_string_symbol(ctx,"isAdd");} {Z3_mk_string_symbol(ctx,"refId");} {Z3_symbol args03CEE190[1] = {Z3_mk_string_symbol(ctx,"refId"), }; Z3_sort args03CEE1C0[1] = {GET(Z3_sort,0x03BC7FD8), }; unsigned args03CEE1F0[1] = {0, }; SET(0x03BE9980, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"Add"),Z3_mk_string_symbol(ctx,"isAdd"),1,args03CEE190,args03CEE1C0,args03CEE1F0));} {Z3_mk_string_symbol(ctx,"address");} {Z3_mk_string_symbol(ctx,"isaddress");} {Z3_mk_string_symbol(ctx,"enc");} {Z3_mk_string_symbol(ctx,"value1");} {Z3_mk_string_symbol(ctx,"value2");} {Z3_symbol args03BEA580[3] = {Z3_mk_string_symbol(ctx,"enc"), Z3_mk_string_symbol(ctx,"value1"), Z3_mk_string_symbol(ctx,"value2"), }; Z3_sort args03BEA628[3] = {GET(Z3_sort,0x03BC7FD8), GET(Z3_sort,0x00000000), GET(Z3_sort,0x00000000), }; unsigned args03BEA660[3] = {0, 0, 0, }; SET(0x03BE99D0, Z3_mk_constructor(ctx,Z3_mk_string_symbol(ctx,"address"),Z3_mk_string_symbol(ctx,"isaddress"),3,args03BEA580,args03BEA628,args03BEA660));} {Z3_mk_string_symbol(ctx,"object");} {Z3_constructor args03BEAC18[25] = {GET(Z3_constructor,0x03CEC870), GET(Z3_constructor,0x03CEC8C0), GET(Z3_constructor,0x03CEC910), GET(Z3_constructor,0x03CEC960), GET(Z3_constructor,0x03CEC9B0), GET(Z3_constructor,0x03CECA00), GET(Z3_constructor,0x03CECA50), GET(Z3_constructor,0x03BE9430), GET(Z3_constructor,0x03BE9480), GET(Z3_constructor,0x03BE9520), GET(Z3_constructor,0x03BE94D0), GET(Z3_constructor,0x03BE9570), GET(Z3_constructor,0x03BE95C0), GET(Z3_constructor,0x03BE9610), GET(Z3_constructor,0x03BE9660), GET(Z3_constructor,0x03BE96B0), GET(Z3_constructor,0x03BE9700), GET(Z3_constructor,0x03BE9750), GET(Z3_constructor,0x03BE97A0), GET(Z3_constructor,0x03BE97F0), GET(Z3_constructor,0x03BE9840), GET(Z3_constructor,0x03BE9890), GET(Z3_constructor,0x03BE98E0), GET(Z3_constructor,0x03BE9930), GET(Z3_constructor,0x03BE9980), }; SET(0x03CEE1C0, Z3_mk_constructor_list(ctx,25,args03BEAC18));} {Z3_mk_string_symbol(ctx,"address");} {Z3_constructor args03CEE1F0[1] = {GET(Z3_constructor,0x03BE99D0), }; SET(0x03CEE220, Z3_mk_constructor_list(ctx,1,args03CEE1F0));} {Z3_symbol args03BEA580[2] = {Z3_mk_string_symbol(ctx,"object"), Z3_mk_string_symbol(ctx,"address"), }; Z3_sort args03BEA660[2] = {GET(Z3_sort,0x03BCD088), GET(Z3_sort,0x03BCD0C8), }; Z3_constructor_list args03BEA628[2] = {GET(Z3_constructor_list,0x03CEE1C0), GET(Z3_constructor_list,0x03CEE220), }; Z3_mk_datatypes(ctx,2, args03BEA580, args03BEA660, args03BEA628);SET(0x03BCD088, args03BEA660[0]);SET(0x03BCD0C8, args03BEA660[1]);} {Z3_del_constructor_list(ctx,GET(Z3_constructor_list,0x03CEE1C0));} {Z3_del_constructor_list(ctx,GET(Z3_constructor_list,0x03CEE220));} {Z3_func_decl out002DE0DC; Z3_func_decl out002DE0E0; Z3_query_constructor(ctx,GET(Z3_constructor,0x03CEC870), 0, &out002DE0DC, &out002DE0E0, 0);SET(0x03BEAC30, out002DE0DC);SET(0x03BEB2F0, out002DE0E0);} {Z3_func_decl out002DE0E0; Z3_func_decl out002DE0E4; Z3_func_decl args03CEE220[1] = {GET(Z3_func_decl,0x03BEB380), }; Z3_query_constructor(ctx,GET(Z3_constructor,0x03BE9840), 1, &out002DE0E0, &out002DE0E4, args03CEE220);SET(0x03BEB1D0, out002DE0E0);SET(0x03BEB338, out002DE0E4);SET(0x03BEB380, args03CEE220[0]);} {Z3_func_decl out002DE0E0; Z3_func_decl out002DE0E4; Z3_func_decl args03BEA580[2] = {0, GET(Z3_func_decl,0x03BEC478), }; Z3_query_constructor(ctx,GET(Z3_constructor,0x03BE9930), 2, &out002DE0E0, &out002DE0E4, args03BEA580);SET(0x03BE9A20, out002DE0E0);SET(0x03BEB3C8, out002DE0E4);SET(0x03BEC430, args03BEA580[0]);SET(0x03BEC478, args03BEA580[1]); } {Z3_func_decl out002DE0DC; Z3_func_decl out002DE0E0; Z3_func_decl args03BEA580[3] = {GET(Z3_func_decl,0x03BEC508), GET(Z3_func_decl,0x03BEC550), GET(Z3_func_decl,0x03BEC598), }; Z3_query_constructor(ctx,GET(Z3_constructor,0x03BE99D0), 3, &out002DE0DC, &out002DE0E0, args03BEA580);SET(0x03BE9A70, out002DE0DC);SET(0x03BEC4C0, out002DE0E0);SET(0x03BEC508, args03BEA580[0]);SET(0x03BEC550, args03BEA580[1]);SET(0x03BEC598, args03BEA580[2]);} {SET(0x03BEB7F0, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEAC30),0,0));} {SET(0x03BCD088, Z3_get_domain(ctx,GET(Z3_func_decl,0x03BEC430),0));} {SET(0x03BEB4F0, Z3_mk_bound(ctx,0,GET(Z3_sort,0x03BCD088)));} {Z3_ast args03CEE220[1] = {GET(Z3_ast,0x03BEB4F0), }; SET(0x03BEC5E0, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEC430),1,args03CEE220));} {SET(0x03BEB4F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC5E0),0));} {SET(0x03BEC628, Z3_mk_eq(ctx,GET(Z3_ast,0x03BEC5E0),GET(Z3_ast,0x03BEB7F0)));} {SET(0x03BE9AC0, Z3_get_app_decl(ctx,GET(Z3_app,0x03BEC628)));} {Z3_ast args03CEE220[1] = {GET(Z3_ast,0x03BEC5E0), }; SET(0x03BEC670, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB338),1,args03CEE220));} {Z3_ast args03CEE220[1] = {GET(Z3_ast,0x03BEC5E0), }; SET(0x03BEC6B8, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB3C8),1,args03CEE220));} {Z3_ast args03BEA580[2] = {GET(Z3_ast,0x03BEC670), GET(Z3_ast,0x03BEC6B8), }; SET(0x03BEC700, Z3_mk_or(ctx,2,args03BEA580));} {SET(0x03BEC670, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC700),0));} {SET(0x03BEC6B8, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC700),1));} {Z3_ast args03BEA580[3] = {GET(Z3_ast,0x03BEC670), GET(Z3_ast,0x03BEC6B8), GET(Z3_ast,0x03BEC628), }; SET(0x03BE9B10, Z3_mk_or(ctx,3,args03BEA580));} {Z3_mk_string_symbol(ctx,"x0");} {Z3_sort args03CEE250[1] = {GET(Z3_sort,0x03BCD088), }; Z3_symbol args03CEE1F0[1] = {Z3_mk_string_symbol(ctx,"x0"), }; SET(0x03BED418, Z3_mk_quantifier(ctx,1,0,0,0,1,args03CEE250,args03CEE1F0,GET(Z3_ast,0x03BE9B10)));} {Z3_assert_cnstr(ctx,GET(Z3_ast,0x03BED418));} {SET(0x03BCD088, Z3_get_domain(ctx,GET(Z3_func_decl,0x03BEC478),0));} {Z3_ast args03CEE1F0[1] = {GET(Z3_ast,0x03BEB4F0), }; SET(0x03BEC790, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEC478),1,args03CEE1F0));} {Z3_ast args03BEAB30[2] = {GET(Z3_ast,0x03BEC790), GET(Z3_ast,0x03BEB7F0), }; SET(0x03BEC820, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BE9AC0),2,args03BEAB30));} {Z3_ast args03CEE1F0[1] = {GET(Z3_ast,0x03BEC790), }; SET(0x03BEC868, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB338),1,args03CEE1F0));} {Z3_ast args03CEE1F0[1] = {GET(Z3_ast,0x03BEC790), }; SET(0x03BEC8B0, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB3C8),1,args03CEE1F0));} {Z3_ast args03BEAB30[2] = {GET(Z3_ast,0x03BEC868), GET(Z3_ast,0x03BEC8B0), }; SET(0x03BEC8F8, Z3_mk_or(ctx,2,args03BEAB30));} {SET(0x03BEC868, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC8F8),0));} {SET(0x03BEC8B0, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC8F8),1));} {Z3_ast args03BEAB30[3] = {GET(Z3_ast,0x03BEC868), GET(Z3_ast,0x03BEC8B0), GET(Z3_ast,0x03BEC820), }; SET(0x03BE9BB0, Z3_mk_or(ctx,3,args03BEAB30));} {Z3_mk_string_symbol(ctx,"x0");} {Z3_sort args03CEE280[1] = {GET(Z3_sort,0x03BCD088), }; Z3_symbol args03CEE2B0[1] = {Z3_mk_string_symbol(ctx,"x0"), }; SET(0x03BEE758, Z3_mk_quantifier(ctx,1,0,0,0,1,args03CEE280,args03CEE2B0,GET(Z3_ast,0x03BE9BB0)));} {Z3_assert_cnstr(ctx,GET(Z3_ast,0x03BEE758));} {SET(0x03BEB8F0, Z3_mk_fresh_const(ctx,"x",GET(Z3_sort,0x03BCD088)));} {Z3_ast args03BEAB30[2] = {GET(Z3_ast,0x03BEB8F0), GET(Z3_ast,0x03BEB7F0), }; SET(0x03BEC9D0, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BE9AC0),2,args03BEAB30));} {Z3_ast args03CEE2B0[1] = {GET(Z3_ast,0x03BEB8F0), }; SET(0x03BECA18, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB338),1,args03CEE2B0));} {Z3_ast args03CEE2B0[1] = {GET(Z3_ast,0x03BEB8F0), }; SET(0x03BECA60, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB3C8),1,args03CEE2B0));} {Z3_ast args03BEAB30[2] = {GET(Z3_ast,0x03BECA18), GET(Z3_ast,0x03BECA60), }; SET(0x03BECAA8, Z3_mk_or(ctx,2,args03BEAB30));} {SET(0x03BECA18, Z3_get_app_arg(ctx,GET(Z3_app,0x03BECAA8),0));} {SET(0x03BECA60, Z3_get_app_arg(ctx,GET(Z3_app,0x03BECAA8),1));} {Z3_ast args03BEAB30[3] = {GET(Z3_ast,0x03BECA18), GET(Z3_ast,0x03BECA60), GET(Z3_ast,0x03BEC9D0), }; SET(0x03BE9C50, Z3_mk_or(ctx,3,args03BEAB30));} {Z3_assert_cnstr(ctx,GET(Z3_ast,0x03BE9C50));} Z3_push(ctx); {(Z3_check_and_get_model(ctx,0));} {SET(0x03BFD780, Z3_mk_int(ctx,2,GET(Z3_sort,0x03BC7FD8)));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03BFD780), 10);} {SET(0x03C0DC08, Z3_mk_not(ctx,GET(Z3_ast,0x03BEC9D0)));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0DC08), 10);} {SET(0x03C094A0, Z3_mk_ite(ctx,GET(Z3_ast,0x03BECA18),GET(Z3_ast,0x03BEB8F0),GET(Z3_ast,0x03BEB7F0)));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C094A0), 10);} {SET(0x03BECA18, Z3_get_app_arg(ctx,GET(Z3_app,0x03C094A0),0));} {SET(0x03BEB8F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C094A0),1));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C094A0),2));} {SET(0x03C0DC50, Z3_mk_not(ctx,GET(Z3_ast,0x03BECA18)));} {SET(0x03BECA18, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DC50),0));} {Z3_ast args03C03310[1] = {GET(Z3_ast,0x03BEC9D0), }; SET(0x03C0DC98, Z3_mk_or(ctx,1,args03C03310));} {SET(0x03C0DCE0, Z3_mk_implies(ctx,GET(Z3_ast,0x03BECA18),GET(Z3_ast,0x03C0DC98)));} {SET(0x03C0DD28, Z3_mk_not(ctx,GET(Z3_ast,0x03C0DCE0)));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0DD28), 10);} {SET(0x03C0DCE0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DD28),0));} {SET(0x03BECA18, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DCE0),0));} {SET(0x03C0DC98, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DCE0),1));} {SET(0x03BEC9D0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DC98),0));} {SET(0x03BEB8F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC9D0),0));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC9D0),1));} {Z3_ast args03C07F90[3] = {GET(Z3_ast,0x03BCCE08), GET(Z3_ast,0x03BEB7F0), GET(Z3_ast,0x03BEB7F0), }; SET(0x03C094F0, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BE9A70),3,args03C07F90));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C094F0), 10);} {Z3_ast args03C07F90[3] = {GET(Z3_ast,0x03BFD780), GET(Z3_ast,0x03BEB7F0), GET(Z3_ast,0x03BEB7F0), }; SET(0x03C09540, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BE9A70),3,args03C07F90));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C09540), 10);} {SET(0x03BECA18, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DCE0),0));} {SET(0x03C0DC98, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DCE0),1));} {SET(0x03BEC9D0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DC98),0));} {Z3_pop(ctx,1);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03BECA18), GET(Z3_ast,0x03BEC9D0), }; SET(0x03BFF5D8, Z3_mk_or(ctx,2,args03BFED58));} {SET(0x03BECA18, Z3_get_app_arg(ctx,GET(Z3_app,0x03BFF5D8),0));} {SET(0x03BEC9D0, Z3_get_app_arg(ctx,GET(Z3_app,0x03BFF5D8),1));} {SET(0x03BEB8F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC9D0),0));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC9D0),1));} Z3_push(ctx); {Z3_ast args03BC3DF8[1] = {GET(Z3_ast,0x03BEB8F0), }; SET(0x03C0D980, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB380),1,args03BC3DF8));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0D980), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0D980), GET(Z3_ast,0x03BCCE08), }; SET(0x03C0D9C8, Z3_mk_app(ctx,GET(Z3_func_decl,0x03CEC820),2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0D9C8), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0D980), GET(Z3_ast,0x03BFD780), }; SET(0x03C0DD70, Z3_mk_app(ctx,GET(Z3_func_decl,0x03CEC820),2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0DD70), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0D9C8), GET(Z3_ast,0x03C0DD70), }; SET(0x03C0DDB8, Z3_mk_or(ctx,2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0DDB8), 10);} {SET(0x03C0DCE0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DD28),0));} {SET(0x03BECA18, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DCE0),0));} {SET(0x03C0DC98, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DCE0),1));} {SET(0x03BEC9D0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DC98),0));} {Z3_pop(ctx,1);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03BECA60), GET(Z3_ast,0x03BEC9D0), }; SET(0x03C0DE00, Z3_mk_or(ctx,2,args03BFED58));} {SET(0x03BECA60, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DE00),0));} {SET(0x03BEC9D0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DE00),1));} {SET(0x03BEB8F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC9D0),0));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEC9D0),1));} Z3_push(ctx); {Z3_ast args03BC3DF8[1] = {GET(Z3_ast,0x03BEB8F0), }; SET(0x03BFF590, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEC430),1,args03BC3DF8));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03BFF590), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03BFF590), GET(Z3_ast,0x03BEB7F0), }; SET(0x03C0DE90, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BE9AC0),2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0DE90), 10);} {SET(0x03C0DE48, Z3_mk_not(ctx,GET(Z3_ast,0x03C0DE90)));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0DE48), 10);} {Z3_ast args03BC3DF8[1] = {GET(Z3_ast,0x03BFF590), }; SET(0x03BFFC08, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB338),1,args03BC3DF8));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03BFFC08), 10);} {SET(0x03C00440, Z3_mk_ite(ctx,GET(Z3_ast,0x03BFFC08),GET(Z3_ast,0x03BFF590),GET(Z3_ast,0x03BEB7F0)));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C00440), 10);} {SET(0x03BFFC08, Z3_get_app_arg(ctx,GET(Z3_app,0x03C00440),0));} {SET(0x03BFF590, Z3_get_app_arg(ctx,GET(Z3_app,0x03C00440),1));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C00440),2));} {SET(0x03C0DED8, Z3_mk_not(ctx,GET(Z3_ast,0x03BFFC08)));} {SET(0x03BFFC08, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DED8),0));} {Z3_ast args03BC3DF8[1] = {GET(Z3_ast,0x03C0DE90), }; SET(0x03C0DF20, Z3_mk_or(ctx,1,args03BC3DF8));} {SET(0x03C0DF68, Z3_mk_implies(ctx,GET(Z3_ast,0x03BFFC08),GET(Z3_ast,0x03C0DF20)));} {SET(0x03C0DFB0, Z3_mk_not(ctx,GET(Z3_ast,0x03C0DF68)));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0DFB0), 10);} {SET(0x03C0DF68, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DFB0),0));} {SET(0x03BCCE08, Z3_get_app_arg(ctx,GET(Z3_app,0x03C094F0),0));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C094F0),1));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C094F0),2));} {SET(0x03BFD780, Z3_get_app_arg(ctx,GET(Z3_app,0x03C09540),0));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C09540),1));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C09540),2));} {SET(0x03BFFC08, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DF68),0));} {SET(0x03C0DF20, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DF68),1));} {SET(0x03C0DE90, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DF20),0));} {Z3_pop(ctx,1);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03BFFC08), GET(Z3_ast,0x03C0DE90), }; SET(0x03C0DFF8, Z3_mk_or(ctx,2,args03BFED58));} {SET(0x03BFFC08, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DFF8),0));} {SET(0x03C0DE90, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DFF8),1));} {SET(0x03BFF590, Z3_get_app_arg(ctx,GET(Z3_app,0x03BFFC08),0));} {SET(0x03BEB8F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03BFF590),0));} {SET(0x03BFF590, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DE90),0));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DE90),1));} Z3_push(ctx); {Z3_ast args03BC3DF8[1] = {GET(Z3_ast,0x03BFF590), }; SET(0x03C0E040, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB380),1,args03BC3DF8));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E040), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0E040), GET(Z3_ast,0x03BCCE08), }; SET(0x03C0E088, Z3_mk_app(ctx,GET(Z3_func_decl,0x03CEC820),2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E088), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0E040), GET(Z3_ast,0x03BFD780), }; SET(0x03C0E0D0, Z3_mk_app(ctx,GET(Z3_func_decl,0x03CEC820),2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E0D0), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0E088), GET(Z3_ast,0x03C0E0D0), }; SET(0x03C0E118, Z3_mk_or(ctx,2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E118), 10);} {Z3_ast args03BC3DF8[1] = {GET(Z3_ast,0x03BEB8F0), }; SET(0x03BFF7D0, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEC478),1,args03BC3DF8));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03BFF7D0), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03BFF7D0), GET(Z3_ast,0x03BEB7F0), }; SET(0x03C0E1A8, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BE9AC0),2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E1A8), 10);} {SET(0x03C0E160, Z3_mk_not(ctx,GET(Z3_ast,0x03C0E1A8)));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E160), 10);} {Z3_ast args03BC3DF8[1] = {GET(Z3_ast,0x03BFF7D0), }; SET(0x03BFF980, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB338),1,args03BC3DF8));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03BFF980), 10);} {SET(0x03C00580, Z3_mk_ite(ctx,GET(Z3_ast,0x03BFF980),GET(Z3_ast,0x03BFF7D0),GET(Z3_ast,0x03BEB7F0)));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C00580), 10);} {SET(0x03BFF980, Z3_get_app_arg(ctx,GET(Z3_app,0x03C00580),0));} {SET(0x03BFF7D0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C00580),1));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C00580),2));} {SET(0x03C0E1F0, Z3_mk_not(ctx,GET(Z3_ast,0x03BFF980)));} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0E1A8), GET(Z3_ast,0x03C0E1F0), }; SET(0x03C0E238, Z3_mk_or(ctx,2,args03BFED58));} {SET(0x03C0E1A8, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E238),0));} {SET(0x03C0E1F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E238),1));} {SET(0x03BFF980, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E1F0),0));} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03BFF980), GET(Z3_ast,0x03C0E160), }; SET(0x03C0E280, Z3_mk_and(ctx,2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E280), 10);} {SET(0x03C0E2C8, Z3_mk_not(ctx,GET(Z3_ast,0x03C0E280)));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E2C8), 10);} {SET(0x03C0E280, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E2C8),0));} {SET(0x03BFF980, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E280),0));} {SET(0x03C0E160, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E280),1));} {SET(0x03C0E1A8, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E160),0));} {Z3_pop(ctx,1);} Z3_push(ctx); {Z3_pop(ctx,1);} {SET(0x03C0E280, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E2C8),0));} {SET(0x03BFF980, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E280),0));} {SET(0x03C0E160, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E280),1));} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03BFF980), GET(Z3_ast,0x03C0E1A8), }; SET(0x03C0E310, Z3_mk_or(ctx,2,args03BFED58));} {SET(0x03BFF980, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E310),0));} {SET(0x03C0E1A8, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E310),1));} {SET(0x03BFF7D0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E1A8),0));} {SET(0x03BEB7F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E1A8),1));} Z3_push(ctx); {Z3_ast args03BC3DF8[1] = {GET(Z3_ast,0x03BFF7D0), }; SET(0x03C0E358, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BEB380),1,args03BC3DF8));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E358), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0E358), GET(Z3_ast,0x03BCCE08), }; SET(0x03C0E3A0, Z3_mk_app(ctx,GET(Z3_func_decl,0x03CEC820),2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E3A0), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0E358), GET(Z3_ast,0x03BFD780), }; SET(0x03C0E3E8, Z3_mk_app(ctx,GET(Z3_func_decl,0x03CEC820),2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C0E3E8), 10);} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0E3A0), GET(Z3_ast,0x03C0E3E8), }; SET(0x03BEEC08, Z3_mk_or(ctx,2,args03BFED58));} {Z3_persist_ast(ctx,GET(Z3_ast,0x03BEEC08), 10);} {SET(0x03C0DCE0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DD28),0));} {Z3_pop(ctx,1);} Z3_push(ctx); {SET(0x03C0E280, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E2C8),0));} {SET(0x03BFF980, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E280),0));} {SET(0x03C0E160, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0E280),1));} {Z3_pop(ctx,1);} {Z3_ast args03BEEC50[7] = {GET(Z3_ast,0x03BFF980), GET(Z3_ast,0x03C0DE48), GET(Z3_ast,0x03C0DFB0), GET(Z3_ast,0x03C0DCE0), GET(Z3_ast,0x03C0E118), GET(Z3_ast,0x03C0E160), GET(Z3_ast,0x03BEEC08), }; SET(0x03BEE6E8, Z3_mk_and(ctx,7,args03BEEC50));} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03C0DD28), GET(Z3_ast,0x03C0DDB8), }; SET(0x03BEEC50, Z3_mk_and(ctx,2,args03BFED58));} {Z3_ast args03BFED58[2] = {GET(Z3_ast,0x03BEE6E8), GET(Z3_ast,0x03BEEC50), }; SET(0x03BEEC98, Z3_mk_or(ctx,2,args03BFED58));} {SET(0x03BEE6E8, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEEC98),0));} {SET(0x03BEEC50, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEEC98),1));} {SET(0x03C0DD28, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEEC50),0));} {SET(0x03C0DDB8, Z3_get_app_arg(ctx,GET(Z3_app,0x03BEEC50),1));} {SET(0x03C0D9C8, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DDB8),0));} {SET(0x03C0DD70, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DDB8),1));} {SET(0x03C0D980, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DD70),0));} {SET(0x03BFD780, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0DD70),1));} {SET(0x03BEB8F0, Z3_get_app_arg(ctx,GET(Z3_app,0x03C0D980),0));} Z3_push(ctx); {Z3_assert_cnstr(ctx,GET(Z3_ast,0x03C0DC08));} {Z3_assert_cnstr(ctx,GET(Z3_ast,0x03BEEC98));} Z3_push(ctx); {(Z3_check_and_get_model(ctx,&_m));} {Z3_ast out002DE120; Z3_eval(ctx,_m, GET(Z3_ast,0x03BEB8F0), &out002DE120);SET(0x03C14AE8, out002DE120);} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C14AE8), 2);} {Z3_ast args03C123F8[2] = {GET(Z3_ast,0x03BEB8F0), GET(Z3_ast,0x03C14AE8), }; SET(0x03C14B78, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BE9AC0),2,args03C123F8));} {Z3_ast args03BC3F18[1] = {GET(Z3_ast,0x03C14B78), }; SET(0x03C14B30, Z3_mk_and(ctx,1,args03BC3F18));} {SET(0x03C14BC0, Z3_mk_not(ctx,GET(Z3_ast,0x03C14B30)));} {Z3_assert_cnstr(ctx,GET(Z3_ast,0x03C14BC0));} {(Z3_check_and_get_model(ctx,&_m));} {Z3_ast out002DE120; Z3_eval(ctx,_m, GET(Z3_ast,0x03BEB8F0), &out002DE120);SET(0x03C127D0, out002DE120);} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C127D0), 2);} {Z3_ast args03C037E8[2] = {GET(Z3_ast,0x03BEB8F0), GET(Z3_ast,0x03C127D0), }; SET(0x03C028D0, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BE9AC0),2,args03C037E8));} {Z3_ast args03BC3EE8[1] = {GET(Z3_ast,0x03C028D0), }; SET(0x03C1F100, Z3_mk_and(ctx,1,args03BC3EE8));} {SET(0x03C1F148, Z3_mk_not(ctx,GET(Z3_ast,0x03C1F100)));} {Z3_assert_cnstr(ctx,GET(Z3_ast,0x03C1F148));} {(Z3_check_and_get_model(ctx,&_m));} {Z3_ast out002DE120; Z3_eval(ctx,_m, GET(Z3_ast,0x03BEB8F0), &out002DE120);SET(0x03C1F5C8, out002DE120);} {Z3_persist_ast(ctx,GET(Z3_ast,0x03C1F5C8), 2);} {Z3_ast args03C05F78[2] = {GET(Z3_ast,0x03BEB8F0), GET(Z3_ast,0x03C1F5C8), }; SET(0x03C23580, Z3_mk_app(ctx,GET(Z3_func_decl,0x03BE9AC0),2,args03C05F78));} {Z3_ast args03CEE490[1] = {GET(Z3_ast,0x03C23580), }; SET(0x03C23538, Z3_mk_and(ctx,1,args03CEE490));} {SET(0x03C235C8, Z3_mk_not(ctx,GET(Z3_ast,0x03C23538)));} {Z3_assert_cnstr(ctx,GET(Z3_ast,0x03C235C8));} //{Z3_check(ctx);} } void test_apps() { Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg,"MODEL","true"); Z3_context ctx = Z3_mk_context(cfg); Z3_symbol A = Z3_mk_string_symbol(ctx, "A"); Z3_symbol F = Z3_mk_string_symbol(ctx, "f"); Z3_sort SA = Z3_mk_uninterpreted_sort(ctx, A); Z3_func_decl f = Z3_mk_func_decl(ctx, F, 1, &SA, SA); Z3_symbol X = Z3_mk_string_symbol(ctx, "x"); Z3_ast x = Z3_mk_const(ctx, X, SA); Z3_ast fx = Z3_mk_app(ctx, f, 1, &x); Z3_ast ffx = Z3_mk_app(ctx, f, 1, &fx); Z3_ast fffx = Z3_mk_app(ctx, f, 1, &ffx); Z3_ast ffffx = Z3_mk_app(ctx, f, 1, &fffx); Z3_ast fffffx = Z3_mk_app(ctx, f, 1, &ffffx); Z3_ast fml = Z3_mk_not(ctx, Z3_mk_eq(ctx, x, fffffx)); Z3_assert_cnstr(ctx, fml); Z3_lbool r = Z3_check(ctx); std::cout << r << "\n"; Z3_del_config(cfg); Z3_del_context(ctx); } void test_bvneg() { Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg,"MODEL","true"); Z3_context ctx = Z3_mk_context(cfg); { Z3_sort bv30 = Z3_mk_bv_sort(ctx, 30); Z3_ast x30 = Z3_mk_fresh_const(ctx, "x", bv30); Z3_ast fml = Z3_mk_eq(ctx, Z3_mk_int(ctx, -1, bv30), Z3_mk_bvadd(ctx, Z3_mk_int(ctx, 0, bv30), x30)); Z3_assert_cnstr(ctx, fml); Z3_lbool r = Z3_check(ctx); std::cout << r << "\n"; } { Z3_sort bv31 = Z3_mk_bv_sort(ctx, 31); Z3_ast x31 = Z3_mk_fresh_const(ctx, "x", bv31); Z3_ast fml = Z3_mk_eq(ctx, Z3_mk_int(ctx, -1, bv31), Z3_mk_bvadd(ctx, Z3_mk_int(ctx, 0, bv31), x31)); Z3_assert_cnstr(ctx, fml); Z3_lbool r = Z3_check(ctx); std::cout << r << "\n"; } { Z3_sort bv32 = Z3_mk_bv_sort(ctx, 32); Z3_ast x32 = Z3_mk_fresh_const(ctx, "x", bv32); Z3_ast fml = Z3_mk_eq(ctx, Z3_mk_int(ctx,-1, bv32), Z3_mk_bvadd(ctx, Z3_mk_int(ctx, 0, bv32), x32)); Z3_assert_cnstr(ctx, fml); Z3_lbool r = Z3_check(ctx); std::cout << r << "\n"; } Z3_del_config(cfg); Z3_del_context(ctx); } static bool cb_called = false; static void my_cb(Z3_context, Z3_error_code) { cb_called = true; } static void test_mk_distinct() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_set_error_handler(ctx, my_cb); Z3_sort bv8 = Z3_mk_bv_sort(ctx, 8); Z3_sort bv32 = Z3_mk_bv_sort(ctx, 32); Z3_ast args[] = { Z3_mk_int64(ctx, 0, bv8), Z3_mk_int64(ctx, 0, bv32) }; Z3_ast d = Z3_mk_distinct(ctx, 2, args); SASSERT(cb_called); } void tst_api() { test_apps(); test_bvneg(); test_mk_distinct(); // bv_invariant(); } #else void tst_api() { } #endif z3-z3-4.4.1/src/test/api_bug.cpp000066400000000000000000000022721260446376700163100ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include"z3.h" void tst_api_bug() { unsigned vmajor, vminor, vbuild, vrevision; Z3_get_version(&vmajor, &vminor, &vbuild, &vrevision); printf("Using Z3 Version %u.%u (build %u, revision %u)\n", vmajor, vminor, vbuild, vrevision); Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg, "MODEL", "true"); Z3_context ctx = Z3_mk_context(cfg); Z3_sort is = Z3_mk_int_sort(ctx); Z3_sort ss = Z3_mk_set_sort(ctx, is); Z3_ast e = Z3_mk_empty_set(ctx, is); // { 42 } Z3_ast fortytwo = Z3_mk_set_add(ctx, e, Z3_mk_int(ctx, 42, is)); // { 42, 43 } Z3_ast fortythree = Z3_mk_set_add(ctx, fortytwo, Z3_mk_int(ctx, 43, is)); // { 42 } U { 42, 43 } Z3_ast uargs[2] = { fortytwo, fortythree }; Z3_ast u = Z3_mk_set_union(ctx, 2, uargs); Z3_symbol sym = Z3_mk_string_symbol(ctx, "mySet"); Z3_ast s = Z3_mk_const(ctx, sym, ss); Z3_ast c = Z3_mk_eq(ctx, s, u); Z3_push(ctx); Z3_assert_cnstr(ctx, c); Z3_model m; printf("result %d\n", Z3_check_and_get_model(ctx, &m)); Z3_string ms = Z3_model_to_string(ctx, m); printf("model : %s\n", ms); } z3-z3-4.4.1/src/test/arith_rewriter.cpp000066400000000000000000000033421260446376700177330ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "arith_rewriter.h" #include "bv_decl_plugin.h" #include "ast_pp.h" #include "reg_decl_plugins.h" #include "th_rewriter.h" #include "model.h" #include "pdr_util.h" #include "smt2parser.h" static expr_ref parse_fml(ast_manager& m, char const* str) { expr_ref result(m); cmd_context ctx(false, &m); ctx.set_ignore_check(true); std::ostringstream buffer; buffer << "(declare-const x Real)\n" << "(declare-const y Real)\n" << "(declare-const z Real)\n" << "(declare-const a Real)\n" << "(declare-const b Real)\n" << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); SASSERT(ctx.begin_assertions() != ctx.end_assertions()); result = *ctx.begin_assertions(); return result; } static char const* example1 = "(<= (+ (* 1.3 x y) (* 2.3 y y) (* (- 1.1 x x))) 2.2)"; static char const* example2 = "(= (+ 4 3 (- (* 3 x x) (* 5 y)) y) 0)"; void tst_arith_rewriter() { ast_manager m; reg_decl_plugins(m); arith_rewriter ar(m); arith_util au(m); expr_ref t1(m), t2(m), result(m); t1 = au.mk_numeral(rational(0),false); t2 = au.mk_numeral(rational(-3),false); expr* args[2] = { t1, t2 }; ar.mk_mul(2, args, result); std::cout << mk_pp(result, m) << "\n"; th_rewriter rw(m); expr_ref fml = parse_fml(m, example1); rw(fml); std::cout << mk_pp(fml, m) << "\n"; pdr::normalize_arithmetic(fml); std::cout << mk_pp(fml, m) << "\n"; fml = parse_fml(m, example2); rw(fml); std::cout << mk_pp(fml, m) << "\n"; pdr::normalize_arithmetic(fml); std::cout << mk_pp(fml, m) << "\n"; } z3-z3-4.4.1/src/test/arith_simplifier_plugin.cpp000066400000000000000000000026731260446376700216170ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "arith_eq_solver.h" #include "smt_params.h" typedef rational numeral; typedef vector row; static void test_solve_integer_equations( arith_eq_solver& asimp, vector& rows ) { row r_unsat; if (asimp.solve_integer_equations(rows, r_unsat)) { std::cout << "solved\n"; } else { std::cout << "not solved\n"; for (unsigned i = 0; i < r_unsat.size(); ++i) { std::cout << " " << r_unsat[i]; } std::cout << "\n"; } } void tst_arith_simplifier_plugin() { smt_params params; ast_manager m; arith_eq_solver asimp(m); row r1; row r2; r1.push_back(numeral(1)); r1.push_back(numeral(2)); r1.push_back(numeral(1)); r1.push_back(numeral(2)); r2.push_back(numeral(1)); r2.push_back(numeral(2)); r2.push_back(numeral(1)); r2.push_back(numeral(2)); vector rows; rows.push_back(r1); rows.push_back(r2); #if 0 test_solve_integer_equations(asimp, rows); rows[1][3] = numeral(3); test_solve_integer_equations(asimp, rows); #endif rows[0][0] = numeral(1); rows[0][1] = numeral(3); rows[0][2] = numeral(0); rows[0][3] = numeral(0); rows[1][0] = numeral(1); rows[1][1] = numeral(0); rows[1][2] = numeral(3); rows[1][3] = numeral(1); test_solve_integer_equations(asimp, rows); } z3-z3-4.4.1/src/test/ast.cpp000066400000000000000000000106031260446376700154660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_ast.cpp Abstract: Test AST module Author: Leonardo de Moura (leonardo) 2006-09-29. Revision History: --*/ #include "ast.h" static void tst1() { ast_manager m; family_id fid = m.get_basic_family_id(); sort_ref b(m.mk_bool_sort(), m); expr_ref a(m.mk_const(symbol("a"), b.get()), m); expr_ref c(m.mk_const(symbol("c"), b.get()), m); expr_ref i1(m.mk_app(fid, OP_AND, a.get(), c.get()), m); expr_ref i2(m.mk_app(fid, OP_AND, a.get(), c.get()), m); expr_ref i3(m.mk_app(fid, OP_OR, a.get(), c.get()), m); SASSERT(i1.get() == i2.get()); SASSERT(i1.get() != i3.get()); // TODO use smart pointers to track references // ast_manager m; // ast_ref n1(m.mk_numeral(rational(2,3)), m); // ast_ref n2(m.mk_numeral(rational(2,3)), m); // SASSERT(n1 == n2); // ast_ref n3(m.mk_numeral(rational(1,2)), m); // SASSERT(n1 != n3); // ast_ref v1 (m.mk_var(1), m); // ast_ref v2 (m.mk_var(2), m); // ast_ref v3 (m.mk_var(1), m); // SASSERT(v1 != v2); // SASSERT(v1 == v3); // TRACE("ast", tout << "reseting v1\n";); // v1.reset(); // TRACE("ast", tout << "overwriting v3\n";); // v3 = v2; // ast_ref t1(m.mk_type_decl(symbol("int"), 0), m); // ast_ref i(m.mk_type(t1.get(), 0, 0), m); // ast_ref foo_decl(m.mk_const_decl(symbol("foo"), i.get(), i.get()), m); // ast_ref x_decl(m.mk_const_decl(symbol("x"), i.get()), m); // ast_ref x(m.mk_const(x_decl.get()), m); // ast_ref foo_x(m.mk_const(foo_decl.get(), x.get()), m); // ast_ref foo_foo_x(m.mk_const(foo_decl.get(), foo_x.get()), m); // ast_ref foo_foo_x2(m.mk_const(foo_decl.get(), m.mk_const(foo_decl.get(), m.mk_const(x_decl.get()))), m); // SASSERT(foo_foo_x2 == foo_foo_x); } static void tst2() { // ast_manager m; // ast_vector m_nodes(m); // m_nodes.push_back(m.mk_var(1)); // m_nodes.push_back(m.mk_numeral(rational(1,2))); // m_nodes.push_back(m.mk_var(2)); // m_nodes[1] = m.mk_var(3); // SASSERT(m_nodes[1]->kind() == AST_VAR); // SASSERT(m_nodes.get(1)->kind() == AST_VAR); // m_nodes.pop_back(); // SASSERT(m_nodes.size() == 2); // SASSERT(!m_nodes.empty()); // m_nodes.set(1, m.mk_var(4)); // SASSERT(&(m_nodes.get_manager()) == &m); } static void tst3() { // ast_manager m; // ast_ref<> n(m.mk_var(1), m); // n = m.mk_var(1); // TRACE("ast", tout << n->get_id() << "\n";); } static void tst4() { // ast_manager m; // ast_ref<> n1(m.mk_var(1), m); // ast_ref<> n2(m.mk_var(2), m); // ast_ref<> n3(m.mk_var(3), m); // weak_memoize wm1; // #ifdef Z3DEBUG // int r; // #endif // SASSERT(!wm1.find(n1, r)); // wm1.insert(n2, 10); // SASSERT(!wm1.find(n1, r)); // SASSERT(wm1.find(n2, r) && r == 10); // wm1.insert(n2, 20); // SASSERT(!wm1.find(n1, r)); // SASSERT(wm1.find(n2, r) && r == 20); // wm1.insert(n1, 0); // SASSERT(wm1.find(n1, r) && r == 0); // SASSERT(wm1.find(n2, r) && r == 20); } static void tst5() { ast_manager m; sort_ref b(m.mk_bool_sort(), m); expr_ref a1(m.mk_const(symbol("a1"), b.get()), m); expr_ref a2(m.mk_const(symbol("a2"), b.get()), m); expr_array arr1; expr_array arr2; expr_array arr3; m.push_back(arr1, a1); m.push_back(arr1, a2); m.pop_back(arr1, arr2); m.set(arr2, 0, a2, arr3); SASSERT(m.size(arr1) == 2); SASSERT(m.size(arr2) == 1); SASSERT(m.size(arr3) == 1); SASSERT(m.get(arr1, 0) == a1); SASSERT(m.get(arr1, 1) == a2); SASSERT(m.get(arr2, 0) == a1); SASSERT(m.get(arr3, 0) == a2); m.del(arr1); m.del(arr2); m.del(arr3); } struct foo { unsigned m_id; unsigned short m_ref_count; unsigned char m_kind; unsigned char m_arity; bool m_val1:1; bool m_val2:1; }; void tst_ast() { TRACE("ast", tout << "sizeof(ast): " << sizeof(ast) << "\n"; tout << "sizeof(expr): " << sizeof(expr) << "\n"; tout << "sizeof(app): " << sizeof(app) << "\n"; ); TRACE("ast", tout << "sizeof(foo): " << sizeof(foo) << "\n";); tst1(); tst2(); tst3(); tst4(); tst5(); } z3-z3-4.4.1/src/test/bit_blaster.cpp000066400000000000000000000063371260446376700172020ustar00rootroot00000000000000/*++ 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"ast_pp.h" #include"ast_ll_pp.h" void mk_bits(ast_manager & m, char const * prefix, unsigned sz, expr_ref_vector & r) { sort_ref b(m); b = m.mk_bool_sort(); for (unsigned i = 0; i < sz; ++i) { char buffer[128]; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "%s%d.smt", prefix, i); #else sprintf(buffer, "%s%d.smt", prefix, i); #endif r.push_back(m.mk_const(symbol(buffer), b)); } } void display(std::ostream & out, expr_ref_vector & r, bool ll=true) { ast_mark v; for (unsigned i = 0; i < r.size(); i++) { if (ll) ast_ll_pp(out, r.get_manager(), r.get(i), v); else out << mk_pp(r.get(i), r.get_manager()); out << "\n"; } } void tst_adder(ast_manager & m, unsigned sz) { // expr_ref_vector a(m); // expr_ref_vector b(m); // expr_ref_vector c(m); // mk_bits(m, "a", sz, a); // mk_bits(m, "b", sz, b); // bool t = true; // bit_blaster blaster(m, t); // blaster.mk_adder(sz, a.c_ptr(), b.c_ptr(), c); // TRACE("bit_blaster", display(tout, c);); } void tst_multiplier(ast_manager & m, unsigned sz) { // expr_ref_vector a(m); // expr_ref_vector b(m); // expr_ref_vector c(m); // mk_bits(m, "a", sz, a); // mk_bits(m, "b", sz, b); // bool t = true; // bit_blaster blaster(m, t); // blaster.mk_multiplier(sz, a.c_ptr(), b.c_ptr(), c); // TRACE("bit_blaster", display(tout, c);); } void tst_le(ast_manager & m, unsigned sz) { // expr_ref_vector a(m); // expr_ref_vector b(m); // expr_ref out(m); // mk_bits(m, "a", sz, a); // mk_bits(m, "b", sz, b); // bool t = true; // bit_blaster blaster(m, t); // blaster.mk_ule(sz, a.c_ptr(), b.c_ptr(), out); // TRACE("bit_blaster", tout << mk_pp(out, m) << "\n";); // blaster.mk_sle(sz, a.c_ptr(), b.c_ptr(), out); // TRACE("bit_blaster", tout << mk_pp(out, m) << "\n";); } void tst_eqs(ast_manager & m, unsigned sz) { // expr_ref_vector a(m); // expr_ref_vector b(m); // expr_ref out(m); // mk_bits(m, "a", sz, a); // bool t = true; // bit_blaster blaster(m, t); // blaster.mk_eqs(sz, a.c_ptr(), b); // TRACE("bit_blaster", display(tout, b, false);); } void tst_sh(ast_manager & m, unsigned sz) { // expr_ref_vector a(m); // expr_ref_vector b(m); // expr_ref_vector c(m); // mk_bits(m, "a", sz, a); // mk_bits(m, "b", sz, b); // bool t = true; // bit_blaster blaster(m, t); // blaster.mk_shl(sz, a.c_ptr(), b.c_ptr(), c); // TRACE("bit_blaster", tout << "shl\n"; display(tout, c);); // c.reset(); // blaster.mk_lshr(sz, a.c_ptr(), b.c_ptr(), c); // TRACE("bit_blaster", tout << "lshr\n"; display(tout, c);); // c.reset(); // blaster.mk_ashr(sz, a.c_ptr(), b.c_ptr(), c); // TRACE("bit_blaster", tout << "ashr " << c.size() << "\n"; display(tout, c, false);); } void tst_bit_blaster() { ast_manager m; tst_adder(m, 4); tst_multiplier(m, 4); tst_le(m, 4); tst_eqs(m, 8); tst_sh(m, 4); } z3-z3-4.4.1/src/test/bit_vector.cpp000066400000000000000000000147171260446376700170510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_bitvector.cpp Abstract: Test bitvector module Author: Leonardo de Moura (leonardo) 2006-10-03. Revision History: --*/ #include #include #include"bit_vector.h" #include"vector.h" static void tst1() { bit_vector v1; svector v2; unsigned n = rand()%10000; for (unsigned i = 0; i < n; i++) { int op = rand()%6; if (op <= 1) { bool val = (rand()%2) != 0; v1.push_back(val); v2.push_back(val); SASSERT(v1.size() == v2.size()); } else if (op <= 3) { SASSERT(v1.size() == v2.size()); if (v1.size() > 0) { bool val = (rand()%2) != 0; unsigned idx = rand()%v1.size(); SASSERT(v1.get(idx) == v2[idx]); v1.set(idx, val); v2[idx] = val; SASSERT(v1.get(idx) == v2[idx]); } } else if (op <= 4) { SASSERT(v1.size() == v2.size()); if (v1.size() > 0) { unsigned idx = rand()%v1.size(); SASSERT(v1.get(idx) == v2[idx]); } } else if (op <= 5) { SASSERT(v1.size() == v2.size()); for (unsigned j = 0; j < v1.size(); j++) { SASSERT(v1.get(j) == v2[j]); } } } } static void tst2() { bit_vector b; b.push_back(true); b.push_back(false); b.push_back(true); b.resize(30); SASSERT(b.get(0) == true); SASSERT(b.get(1) == false); SASSERT(b.get(2) == true); SASSERT(b.get(3) == false); SASSERT(b.get(29) == false); } static void tst_shift() { bit_vector b; b.resize(111); b.set(105); b.set(99); b.set(98); b.set(90); b.set(80); b.set(75); b.set(33); b.set(32); b.set(31); b.set(30); b.set(10); std::cout << "b: " << b << "\n"; b.shift_right(16); std::cout << "b: " << b << "\n"; b.shift_right(1); std::cout << "b: " << b << "\n"; b.shift_right(32); std::cout << "b: " << b << "\n"; b.shift_right(42); std::cout << "b: " << b << "\n"; b.shift_right(16); std::cout << "b: " << b << "\n"; b.shift_right(63); std::cout << "b: " << b << "\n"; } static void tst_or() { { bit_vector b1; bit_vector b2; b1.resize(5); b2.resize(10); b1.set(4); b2.set(8); b2.set(3); b2.set(2); b2.set(1); std::cout << b1 << "\n"; std::cout << b2 << "\n"; b1 |= b2; SASSERT(b1.size() == 10); std::cout << b1 << "\n"; SASSERT(b1 != b2); SASSERT(b1 != b2); b1.unset(4); SASSERT(b1 == b2); b1.unset(3); SASSERT(b1 != b2); } { bit_vector b1; bit_vector b2; b1.resize(2); b2.resize(5); b1.set(0); b2.set(4); b1 |= b2; SASSERT(b1 != b2); b2.set(0); SASSERT(b1 == b2); std::cout << "-----\n"; std::cout << b1 << "\n"; } { bit_vector b1; bit_vector b2; b1.resize(10); b2.resize(10); b1.set(5); b2.set(8); b2.set(3); b2.resize(5); b1 |= b2; SASSERT(!b1.get(8)); SASSERT(b1.get(5)); SASSERT(b1.get(3)); } { bit_vector b1; bit_vector b2; b1.resize(123); b2.resize(126); b2.set(124); b1.set(122); b1.set(100); b2.set(100); b1.set(80); b2.set(80); b1.set(4); b2.set(4); SASSERT(b1!=b2); b2.resize(123); SASSERT(b1!=b2); b1.resize(120); b2.resize(120); SASSERT(b1==b2); b1.unset(80); b1.unset(100); SASSERT(b1!=b2); b1 |= b2; SASSERT(b1 == b2); } { bit_vector b1; bit_vector b2; b1.resize(5); b2.resize(10); b2.set(8); b1.set(4); b2.set(1); b1.set(0); b1 |= b2; SASSERT(b1.size() == 10); SASSERT(b1.get(8) && b1.get(4) && b1.get(1) && b1.get(0) && !b1.get(9)); } { bit_vector b1; bit_vector b2; b1.resize(32); b2.resize(32); b1.set(1); b1.set(5); b2.set(8); b2.set(0); b1 |= b2; SASSERT(b1.get(1) && b1.get(5) && b1.get(8) && b1.get(0) && !b1.get(11)); std::cout << "b1(size32): " << b1 << "\n"; } } static void tst_and() { { bit_vector b1; bit_vector b2; b1.resize(5); b2.resize(3); b2.set(2); b1.set(2); b1.set(4); std::cout << "------\nb1: " << b1 << "\n"; b1 &= b2; std::cout << "------\nb1: " << b1 << "\n"; SASSERT(!b1.get(4)); SASSERT(b1.get(2)); } { bit_vector b1; bit_vector b2; b1.resize(241); b2.resize(128); b1.set(240); b1.set(232); b1.set(127); b1.set(128); b1.set(8); b2.set(127); b2.set(5); b1 &= b2; SASSERT(!b1.get(240) && !b1.get(232) && !b1.get(128) && b1.get(127) && !b1.get(8) && !b1.get(5)); } } static void tst_crash() { { bit_vector b; b.push_back(true); b.resize(64); SASSERT(!b.get(63)); SASSERT(b.get(0)); SASSERT(!b.get(1)); } { bit_vector b; b.push_back(false); b.resize(64, true); SASSERT(b.get(63)); SASSERT(!b.get(0)); SASSERT(b.get(1)); } } static void tst_bv_reset() { bit_vector b; bool bit = true; for (unsigned sz = 1; sz < 84; ++sz) { b.reset(); b.resize(sz, bit); for (unsigned i = 0; i < sz; ++i) { SASSERT(bit == b.get(i)); } for (unsigned sz2 = sz; sz2 < sz+10; ++sz2) { b.resize(sz2, !bit); for (unsigned i = sz; i < sz2; ++i) { SASSERT(bit != b.get(i)); } } bit = !bit; } } static void tst_eq() { bit_vector b1, b2, b3; b1.resize(32); b2.resize(32); b3.resize(32); b1.set(3, true); SASSERT(b1 != b2); SASSERT(!(b1 == b2)); SASSERT(b2 == b3); b3.set(3, true); SASSERT(b1 == b3); SASSERT(!(b1 != b3)); b2.set(31, true); b3.set(31); b3.unset(3); SASSERT(b2 == b3); SASSERT(!(b2 != b3)); } void tst_bit_vector() { tst_crash(); tst_shift(); tst_or(); tst_and(); tst_bv_reset(); tst_eq(); return; tst2(); for (unsigned i = 0; i < 20; i++) { std::cerr << i << std::endl; tst1(); } } z3-z3-4.4.1/src/test/bits.cpp000066400000000000000000000151561260446376700156500ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ // Test some bit hacks #include"util.h" #include"debug.h" #include"vector.h" #include"mpz.h" #include"bit_util.h" static void tst_shl(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned const * dst, bool trace = true) { if (trace) { std::cout << "shl({"; for (unsigned i = 0; i < src_sz; i++) { if (i > 0) std::cout << ", "; std::cout << src[i]; } std::cout << "}, " << k << ")" << std::endl; } svector actual_dst; actual_dst.resize(dst_sz, 0xAAAAAAAA); for (unsigned sz = 1; sz <= dst_sz; sz++) { if (trace) std::cout << " for sz = " << sz << std::endl; shl(src_sz, src, k, sz, actual_dst.c_ptr()); SASSERT(!has_one_at_first_k_bits(sz, actual_dst.c_ptr(), k)); for (unsigned i = 0; i < sz; i++) { if (trace && dst[i] != actual_dst[i]) std::cout << "UNEXPECTED RESULT at [" << i << "]: " << actual_dst[i] << ", expected: " << dst[i] << "\n"; SASSERT(dst[i] == actual_dst[i]); } if (sz == src_sz) { unsigned nz1 = nlz(sz, src); if (nz1 >= k && !is_zero(sz, src)) { unsigned nz2 = nlz(sz, actual_dst.c_ptr()); if (nz1 - k != nz2) { if (trace) std::cout << "nlz BUG, nlz1: " << nz1 << ", k: " << k << ", nlz2: " << nz2 << std::endl; UNREACHABLE(); } } } if (sz >= src_sz + (k/32) + 1) { svector new_src; new_src.resize(sz, 0xAAAAAAAA); shr(sz, actual_dst.c_ptr(), k, new_src.c_ptr()); for (unsigned i = 0; i < src_sz; i++) { if (trace && src[i] != new_src[i]) { std::cout << "shr BUG, inverting shl, at bit[" << i << "], " << new_src[i] << ", expected: " << src[i] << std::endl; } SASSERT(src[i] == new_src[i]); } } } if (trace) std::cout << " shift by 1, k times" << std::endl; copy(src_sz, src, dst_sz, actual_dst.c_ptr()); for (unsigned i = 0; i < k; i++) { shl(dst_sz, actual_dst.c_ptr(), 1, dst_sz, actual_dst.c_ptr()); } for (unsigned i = 0; i < dst_sz; i++) { if (trace && dst[i] != actual_dst[i]) std::cout << "UNEXPECTED RESULT at [" << i << "]: " << actual_dst[i] << ", expected: " << dst[i] << "\n"; SASSERT(dst[i] == actual_dst[i]); } if (src_sz <= dst_sz) { if (trace) std::cout << " self-shl" << std::endl; shl(src_sz, src, k, src_sz, const_cast(src)); for (unsigned i = 0; i < src_sz; i++) { if (trace && src[i] != dst[i]) std::cout << "UNEXPECTED RESULT at [" << i << "]: " << src[i] << ", expected: " << dst[i] << "\n"; SASSERT(src[i] == actual_dst[i]); } } } static void tst_shl() { { unsigned src[2] = {0, 2}; unsigned dst[2] = {0, 2<<10}; tst_shl(2, src, 10, 2, dst); } { unsigned src[2] = {2, 0}; unsigned dst[2] = {0, 2<<10}; tst_shl(2, src, 42, 2, dst); } { unsigned src[2] = {0, 0}; unsigned dst[3] = {0, 0, 0}; tst_shl(2, src, 1, 3, dst); } { unsigned src[2] = {0x80000009, 5}; unsigned dst[2] = {18, 11}; tst_shl(2, src, 1, 2, dst); } { unsigned src[2] = {0x80000009, 0x80000005}; unsigned dst[2] = {18, 11}; tst_shl(2, src, 1, 2, dst); } { unsigned src[2] = {0x80000009, 0x80000005}; unsigned dst[3] = {18, 11, 1}; tst_shl(2, src, 1, 3, dst); } { unsigned src[2] = {0x80000009, 0x80000005}; unsigned dst[3] = {0, 18, 11}; tst_shl(2, src, 33, 3, dst); } { unsigned src[2] = {0x80000009, 0x80000005}; unsigned dst[4] = {0, 18, 11, 1}; tst_shl(2, src, 33, 4, dst); } { unsigned src[2] = {0xFFFFFFFF, 0xFFFFFFFF}; unsigned dst[2] = {0xFFFFFFF0, 0xFFFFFFFF}; tst_shl(2, src, 4, 2, dst); } } static void tst_shr(unsigned src_sz, unsigned const * src, unsigned k, unsigned const * dst, bool trace = true) { if (trace) { std::cout << "shr({"; for (unsigned i = 0; i < src_sz; i++) { if (i > 0) std::cout << ", "; std::cout << src[i]; } std::cout << "}, " << k << ")" << std::endl; } svector actual_dst; actual_dst.resize(src_sz, 0xAAAAAAAA); shr(src_sz, src, k, actual_dst.c_ptr()); for (unsigned i = 0; i < src_sz; i++) { if (trace && dst[i] != actual_dst[i]) std::cout << "UNEXPECTED RESULT at [" << i << "]: " << actual_dst[i] << ", expected: " << dst[i] << "\n"; SASSERT(dst[i] == actual_dst[i]); } } static void tst_shr() { { unsigned src[2] = {0, 0}; unsigned dst[2] = {0, 0}; tst_shr(2, src, 1, dst); } } static void tst_shl_rand(unsynch_mpz_manager & m, unsigned sz, unsigned k, bool trace = true) { // create a random bitvector of of size sz svector src; for (unsigned i = 0; i < sz; i++) { src.push_back(rand()); } // convert src into a mpz number scoped_mpz _src(m); scoped_mpz tmp(m); unsigned i = sz; while (i > 0) { --i; m.mul2k(_src, 32); m.set(tmp, src[i]); m.add(_src, tmp, _src); } // shift left by multiplying by 2^k scoped_mpz _dst(m); m.set(_dst, _src); m.mul2k(_dst, k); // convert _dst into a vector of unsigned values svector dst; scoped_mpz max(m); m.set(max, 1); m.mul2k(max, 32); while (!m.is_zero(_dst)) { m.mod(_dst, max, tmp); SASSERT(m.is_uint64(tmp) && m.get_uint64(tmp) < UINT_MAX); dst.push_back(static_cast(m.get_uint64(tmp))); m.div(_dst, max, _dst); } while (dst.size() < src.size()) dst.push_back(0); dst.push_back(0); unsigned word_shift = (k / 32); for (unsigned i = 0; i < word_shift; i++) dst.push_back(0); tst_shl(src.size(), src.c_ptr(), k, dst.size(), dst.c_ptr(), trace); } static void tst_shl_rand(unsigned N, unsigned sz, unsigned k, bool trace = false) { unsynch_mpz_manager m; for (unsigned i = 0; i < N; i++) { unsigned _sz = rand() % sz; if (_sz == 0) _sz = 1; unsigned _k = rand() % k; if (_k == 0) _k = 1; tst_shl_rand(m, _sz, _k, trace); } } void tst_bits() { tst_shr(); tst_shl(); tst_shl_rand(100000, 4, 100); } z3-z3-4.4.1/src/test/buffer.cpp000066400000000000000000000015711260446376700161540ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: buffer.cpp Abstract: Test buffers. Author: Leonardo de Moura (leonardo) 2011-03-03. Revision History: --*/ #include"ptr_scoped_buffer.h" typedef std::pair point; template class ptr_scoped_buffer; static void tst1() { ptr_scoped_buffer b; SASSERT(b.empty()); b.push_back(alloc(point, 10, 20)); SASSERT(!b.empty()); point * p1 = alloc(point, 30, 20); b.push_back(p1); SASSERT(b.get(1) == p1); b.push_back(alloc(point, 40, 20)); SASSERT(b.size() == 3); b.pop_back(); SASSERT(b.get(0) != p1); SASSERT(b.get(1) == p1); point * p2 = alloc(point, 30, 20); SASSERT(b.get(0) != p2); b.set(0, p2); SASSERT(b.get(0) == p2); SASSERT(b.size() == 2); b.push_back(alloc(point, 40, 40)); } void tst_buffer() { tst1(); } z3-z3-4.4.1/src/test/bv_simplifier_plugin.cpp000066400000000000000000000263601260446376700211160ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "bv_simplifier_plugin.h" #include "arith_decl_plugin.h" #include "ast_pp.h" #include "reg_decl_plugins.h" class tst_bv_simplifier_plugin_cls { class mgr { public: mgr(ast_manager& m) { reg_decl_plugins(m); } }; ast_manager m_manager; mgr m_mgr; bv_simplifier_params m_bv_params; basic_simplifier_plugin m_bsimp; arith_util m_arith; bv_simplifier_plugin m_simp; bv_util m_bv_util; family_id m_fid; void get_num(expr* e, unsigned bv_size, rational& r) { unsigned bv_size0; if (!m_bv_util.is_numeral(e, r, bv_size0)) { UNREACHABLE(); } SASSERT(bv_size == bv_size0); } unsigned u32(expr* e) { rational r; std::cout << mk_pp(e,m_manager) << "\n"; get_num(e, 32, r); return r.get_unsigned(); } unsigned char u8(expr* e) { rational r; get_num(e, 8, r); return static_cast(r.get_unsigned()); } int i32(expr* e) { return static_cast(u32(e)); } uint64 u64(expr* e) { rational r; get_num(e, 64, r); return r.get_uint64(); } int64 i64(expr* e) { rational r; get_num(e, 64, r); if (r >= power(rational(2), 63)) { r -= power(rational(2), 64); } return r.get_int64(); } bool ast2bool(expr* e) { if (m_manager.is_true(e)) { return true; } if (m_manager.is_false(e)) { return false; } UNREACHABLE(); return false; } bool bit2bool(expr* e) { rational r; get_num(e, 1, r); return 0 != r.get_unsigned(); } expr* mk_int(unsigned i) { return m_arith.mk_numeral(rational(i), true); } public: tst_bv_simplifier_plugin_cls() : m_mgr(m_manager), m_bsimp(m_manager), m_arith(m_manager), m_simp(m_manager, m_bsimp, m_bv_params), m_bv_util(m_manager), m_fid(0) { m_fid = m_manager.mk_family_id("bv"); } ~tst_bv_simplifier_plugin_cls() {} void test_num(unsigned a) { expr_ref e(m_manager), e1(m_manager); app_ref ar(m_manager); uint64 a64 = static_cast(a); e1 = m_bv_util.mk_numeral(rational(a), 32); expr* const es[1] = { e1.get() }; ar = m_manager.mk_app(m_fid, OP_BNEG, e1.get()); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((0-a) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BNOT, e1.get()); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((~a) == u32(e.get())); parameter params[2] = { parameter(32), parameter(32) }; ar = m_manager.mk_app(m_fid, OP_SIGN_EXT, 1, params, 1, es); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(((int64)(int)a) == i64(e.get())); ar = m_manager.mk_app(m_fid, OP_ZERO_EXT, 1, params, 1, es); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(((uint64)a) == u64(e.get())); params[0] = parameter(7); params[1] = parameter(0); ar = m_manager.mk_app(m_fid, OP_EXTRACT, 2, params, 1, es); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(((unsigned char)a) == u8(e.get())); params[0] = parameter(2); ar = m_manager.mk_app(m_fid, OP_REPEAT, 1, params, 1, es); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(((a64 << 32) | a64) == u64(e.get())); ar = m_manager.mk_app(m_fid, OP_BREDOR, e1.get()); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a != 0) == bit2bool(e.get())); ar = m_manager.mk_app(m_fid, OP_BREDAND, e1.get()); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a == 0xFFFFFFFF) == bit2bool(e.get())); params[0] = parameter(8); ar = m_manager.mk_app(m_fid, OP_ROTATE_LEFT, 1, params, 1, es); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(((a << 8) | (a >> 24)) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_ROTATE_RIGHT, 1, params, 1, es); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(((a >> 8) | (a << 24)) == u32(e.get())); params[0] = parameter(m_manager.mk_sort(m_manager.mk_family_id("arith"), INT_SORT)); ar = m_manager.mk_app(m_fid, OP_BV2INT, 1, params, 1, es); expr* es2[1] = { ar.get() }; params[0] = parameter(32); ar = m_manager.mk_app(m_fid, OP_INT2BV, 1, params, 1, es2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(a == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BIT0); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(!bit2bool(e.get())); ar = m_manager.mk_app(m_fid, OP_BIT1); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(bit2bool(e.get())); } void test_pair(unsigned a, unsigned b) { expr_ref e(m_manager), e1(m_manager), e2(m_manager); app_ref ar(m_manager); int sa = static_cast(a); int sb = static_cast(b); uint64 a64 = static_cast(a); uint64 b64 = static_cast(b); e1 = m_bv_util.mk_numeral(rational(a), 32); e2 = m_bv_util.mk_numeral(rational(b), 32); expr* const e1e2[] = { e1.get(), e2.get() }; ar = m_manager.mk_app(m_fid, OP_BADD, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a + b) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BSUB, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a - b) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BMUL, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a * b) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BAND, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a & b) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BOR, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a | b) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BNOR, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(~(a | b) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BXOR, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a ^ b) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BXNOR, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((~(a ^ b)) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BNAND, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((~(a & b)) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_ULEQ, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a <= b) == ast2bool(e.get())); ar = m_manager.mk_app(m_fid, OP_UGEQ, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a >= b) == ast2bool(e.get())); ar = m_manager.mk_app(m_fid, OP_ULT, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a < b) == ast2bool(e.get())); ar = m_manager.mk_app(m_fid, OP_UGT, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a > b) == ast2bool(e.get())); ar = m_manager.mk_app(m_fid, OP_SLEQ, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((sa <= sb) == ast2bool(e.get())); ar = m_manager.mk_app(m_fid, OP_SGEQ, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((sa >= sb) == ast2bool(e.get())); ar = m_manager.mk_app(m_fid, OP_SLT, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((sa < sb) == ast2bool(e.get())); ar = m_manager.mk_app(m_fid, OP_SGT, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((sa > sb) == ast2bool(e.get())); ar = m_manager.mk_app(m_fid, OP_BSHL, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(((b>=32)?0:(a << b)) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BLSHR, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(((b>=32)?0:(a >> b)) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BASHR, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); std::cout << "compare: " << sa << " >> " << b << " = " << (sa >> b) << " with " << i32(e.get()) << "\n"; SASSERT(b >= 32 || ((sa >> b) == i32(e.get()))); if (b != 0) { ar = m_manager.mk_app(m_fid, OP_BSDIV, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((sa / sb) == i32(e.get())); ar = m_manager.mk_app(m_fid, OP_BUDIV, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a / b) == u32(e.get())); ar = m_manager.mk_app(m_fid, OP_BSREM, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); //SASSERT((sa % sb) == i32(e.get())); ar = m_manager.mk_app(m_fid, OP_BUREM, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a % b) == u32(e.get())); // TBD: BSMOD. } ar = m_manager.mk_app(m_fid, OP_CONCAT, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT(((a64 << 32) | b64) == u64(e.get())); ar = m_manager.mk_app(m_fid, OP_BCOMP, 2, e1e2); m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e); SASSERT((a == b) == bit2bool(e.get())); } void test() { unsigned_vector nums; nums.push_back(0); nums.push_back(1); nums.push_back(-1); nums.push_back(2); nums.push_back(31); nums.push_back(32); nums.push_back(33); nums.push_back(435562); nums.push_back(-43556211); // TBD add some random numbers. for (unsigned i = 0; i < nums.size(); ++i) { test_num(nums[i]); for (unsigned j = 0; j < nums.size(); ++j) { test_pair(nums[i], nums[j]); } } } }; void tst_bv_simplifier_plugin() { tst_bv_simplifier_plugin_cls tst_cls; tst_cls.test(); } z3-z3-4.4.1/src/test/chashtable.cpp000066400000000000000000000101111260446376700167670ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: chashtable.cpp Abstract: Hashtable with chaining. Author: Leonardo de Moura (leonardo) 2011-04-14. Revision History: --*/ #include"chashtable.h" #include"hashtable.h" #include"hash.h" #include"util.h" typedef chashtable > int_table; typedef cmap > int_map; template class chashtable >; template class cmap >; template static void display(T const & beg, T const & end) { for (T it = beg; it != end; ++it) std::cout << *it << " "; std::cout << "\n"; } static void tst1() { int_table t; t.insert(10); SASSERT(t.contains(10)); t.insert(20); SASSERT(t.contains(20)); t.insert(30); SASSERT(t.contains(30)); SASSERT(t.size() == 3); display(t.begin(), t.end()); t.erase(20); SASSERT(!t.contains(20)); SASSERT(t.size() == 2); } struct dummy_hash { unsigned operator()(int v) const { return v % 2; } }; typedef chashtable > dint_table; template class chashtable >; static void tst2() { dint_table t; t.insert(10); t.insert(12); SASSERT(t.used_slots() == 1); display(t.begin(), t.end()); t.insert(13); display(t.begin(), t.end()); SASSERT(t.used_slots() == 2); t.insert(14); SASSERT(t.used_slots() == 2); SASSERT(t.size() == 4); display(t.begin(), t.end()); t.erase(12); SASSERT(!t.contains(12)); SASSERT(t.size() == 3); SASSERT(t.contains(10)); SASSERT(!t.contains(12)); SASSERT(t.contains(14)); SASSERT(t.contains(13)); t.insert(16); SASSERT(t.size() == 4); t.insert(18); SASSERT(t.size() == 5); SASSERT(t.used_slots() == 2); display(t.begin(), t.end()); t.erase(10); display(t.begin(), t.end()); SASSERT(!t.contains(10)); SASSERT(!t.contains(12)); SASSERT(t.contains(14)); SASSERT(t.contains(13)); SASSERT(t.contains(16)); SASSERT(t.contains(18)); } static void tst3() { dint_table t; t.insert(10); t.insert(12); SASSERT(t.used_slots() == 1); SASSERT(t.contains(10)); SASSERT(t.contains(12)); t.erase(12); t.erase(10); SASSERT(t.size() == 0); SASSERT(t.empty()); SASSERT(t.used_slots() == 0); t.insert(10); SASSERT(t.used_slots() == 1); SASSERT(t.contains(10)); SASSERT(t.size() == 1); } typedef int_hashtable > int_set; template static void tst4(unsigned num, unsigned N) { int_set s; T t; for (unsigned i = 0; i < num; i++) { int v = rand() % N; if (rand() % 3 == 2) { TRACE("chashtable", tout << "erase " << v << "\n";); s.erase(v); t.erase(v); SASSERT(!t.contains(v)); } else { TRACE("chashtable", tout << "insert " << v << "\n";); s.insert(v); t.insert(v); SASSERT(t.contains(v)); } SASSERT(s.size() == t.size()); SASSERT(s.empty() == t.empty()); } std::cout << "size: " << s.size() << " " << t.size() << "\n"; int_set::iterator it1 = s.begin(); int_set::iterator end1 = s.end(); for(; it1 != end1; ++it1) { SASSERT(t.contains(*it1)); } typename T::iterator it2 = t.begin(); typename T::iterator end2 = t.end(); for(; it2 != end2; ++it2) { SASSERT(s.contains(*it2)); SASSERT(t.contains(*it2)); } } static void tst5() { dint_table t; t.insert(4); t.insert(9); t.insert(8); t.insert(1); t.erase(1); t.insert(7); t.insert(1); t.insert(2); } static void tst6() { int_map m; m.insert(10, 4); SASSERT(m.contains(10)); DEBUG_CODE({ int r; SASSERT(m.find(10, r) && r == 4); }); } void tst_chashtable() { tst1(); tst2(); tst3(); tst6(); tst4(1000,10); tst4(10000,10); tst4(50000,1000); tst5(); } z3-z3-4.4.1/src/test/check_assumptions.cpp000066400000000000000000000025231260446376700204230ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "memory_manager.h" #include "smt_params.h" #include "ast.h" #include "arith_decl_plugin.h" #include "bv_decl_plugin.h" #include "smt_context.h" #include "reg_decl_plugins.h" void tst_check_assumptions() { memory::initialize(0); smt_params params; ast_manager mgr; reg_decl_plugins(mgr); sort_ref b(mgr.mk_bool_sort(), mgr); func_decl_ref pPred(mgr.mk_func_decl(symbol("p"), 0, static_cast(0), b), mgr); func_decl_ref qPred(mgr.mk_func_decl(symbol("q"), 0, static_cast(0), b), mgr); func_decl_ref rPred(mgr.mk_func_decl(symbol("r"), 0, static_cast(0), b), mgr); app_ref p(mgr.mk_app(pPred,0,static_cast(0)), mgr); app_ref q(mgr.mk_app(qPred,0,static_cast(0)), mgr); app_ref r(mgr.mk_app(rPred,0,static_cast(0)), mgr); app_ref pOqOr(mgr.mk_or(p,q,r), mgr); app_ref np(mgr.mk_not(p), mgr); app_ref nq(mgr.mk_not(q), mgr); app_ref nr(mgr.mk_not(r), mgr); smt::context ctx(mgr, params); ctx.assert_expr(pOqOr); expr * npE = np.get(); lbool res1 = ctx.check(1, &npE); SASSERT(res1==l_true); ctx.assert_expr(npE); expr * assumpt[] = { nq.get(), nr.get() }; //here it should crash ctx.check(2, assumpt); } z3-z3-4.4.1/src/test/datalog_parser.cpp000066400000000000000000000031201260446376700176620ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "datalog_parser.h" #include "ast_pp.h" #include "arith_decl_plugin.h" #include "dl_context.h" #include "dl_register_engine.h" #include "smt_params.h" #include "reg_decl_plugins.h" using namespace datalog; static void dparse_string(char const* str) { ast_manager m; smt_params params; reg_decl_plugins(m); register_engine re; context ctx(m, re, params); parser* p = parser::create(ctx,m); bool res = p->parse_string(str); if (res) { std::cout << "Parsed\n"<parse_file(file)) { std::cout << "Failed to parse file\n"; } dealloc(p); } void tst_datalog_parser() { dparse_string("\nH :- C1(X,a,b), C2(Y,a,X) ."); dparse_string("N 128\n\nH :- C1(X,a,b), C2(Y,a,X) ."); dparse_string("N 128\nI 128\n\nC1(x : N, y : N, z : I)\nC2(x : N, y : N, z : N)\nH :- C1(X,a,b), C2(Y,a,X) ."); dparse_string("\nH :- C1(X,a,b), nC2(Y,a,X) ."); dparse_string("\nH :- C1(X,a,b),nC2(Y,a,X)."); dparse_string("\nH :- C1(X,a,b),\\\nC2(Y,a,X)."); dparse_string("\nH :- C1(X,a\\,\\b), C2(Y,a,X) ."); } void tst_datalog_parser_file(char** argv, int argc, int & i) { if (i + 1 < argc) { dparse_file(argv[i+1]); i++; } } z3-z3-4.4.1/src/test/ddnf.cpp000066400000000000000000000131351260446376700156150ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ddnf.h" #include "tbv.h" #include #include #include #include #include #include #include #include /* TBD: count number of nodes, number of operations accross all insertions */ void read_nums(std::istream& is, unsigned & x, unsigned& y) { x = 0; y = 0; is >> x; is >> y; std::string line; std::getline(is, line); } static char const* g_file = 0; void create_forwarding( char const* file, datalog::ddnf_core& ddnf, ptr_vector& tbvs, vector& fwd_indices) { IF_VERBOSE(1, verbose_stream() << "creating (and forgetting) forwarding index\n";); std::ifstream is(file); if (is.bad() || is.fail()) { std::cout << "could not load " << file << "\n"; exit(0); } std::string line; unsigned W, M; read_nums(is, W, M); tbv_manager& tbvm = ddnf.get_tbv_manager(); tbv* tX = tbvm.allocateX(); unsigned_vector forwarding_set; for (unsigned r = 0; r < M; ++r) { unsigned P, K; read_nums(is, K, P); ddnf.reset_accumulate(); unsigned p; fwd_indices.push_back(unsigned_vector()); unsigned_vector& forwarding_index = fwd_indices.back(); forwarding_index.resize(ddnf.size()); for (unsigned g = 0; g < K; ++g) { is >> p; std::getline(is, line); tbv* t = tbvm.allocate(line.c_str()); if (p > P) { std::cout << "port number " << p << " too big " << P << "\n"; tbvm.display(std::cout, *t) << " " << line << "\n"; exit(0); } forwarding_set.reset(); ddnf.accumulate(*t, forwarding_set); for (unsigned i = 0; i < forwarding_set.size(); ++i) { forwarding_index[forwarding_set[i]] = p; } tbvs.push_back(t); if (p == 0 && tbvm.equals(*t, *tX)) break; } } tbvm.deallocate(tX); } datalog::ddnf_core* populate_ddnf(char const* file, ptr_vector& tbvs) { IF_VERBOSE(1, verbose_stream() << "populate ddnf\n";); std::ifstream is(file); if (is.bad() || is.fail()) { std::cout << "could not load " << file << "\n"; exit(0); } std::string line; unsigned W, M; read_nums(is, W, M); datalog::ddnf_core* ddnf = alloc(datalog::ddnf_core, W); tbv_manager& tbvm = ddnf->get_tbv_manager(); tbv* tX = tbvm.allocateX(); for (unsigned r = 0; r < M; ++r) { unsigned P, K; read_nums(is, K, P); IF_VERBOSE(1, verbose_stream() << K << " " << P << "\n";); unsigned p; for (unsigned g = 0; g < K; ++g) { is >> p; std::getline(is, line); tbv* t = tbvm.allocate(line.c_str()); ddnf->insert(*t); IF_VERBOSE(2, tbvm.display(verbose_stream() << line << " ", *t) << "\n";); tbvs.push_back(t); if (p > P) { std::cout << "port number " << p << " too big " << P << "\n"; tbvm.display(std::cout, *t) << " " << line << "\n"; exit(0); } if (p == 0 && tbvm.equals(*t, *tX)) break; // std::cout << ddnf->well_formed() << "\n"; } } tbvm.deallocate(tX); return ddnf; } static void read_args(char ** argv, int argc, int& i) { if (argc == i + 2) { g_file = argv[i + 1]; ++i; return; } if (!g_file) { std::cout << "Need routing table file as argument. Arguments provided: "; for (int j = i; j < argc; ++j) { std::cout << argv[j] << " "; } std::cout << "\n"; exit(0); } } typedef std::pair u_pair; struct uu_eq { bool operator()(u_pair u1, u_pair u2) const { return u1 == u2; } }; typedef map, uu_eq > pair_table; static unsigned refine_forwarding( unsigned_vector& p, unsigned_vector const& q) { unsigned sz = p.size(); unsigned n = 0, m = 0; pair_table tbl; for (unsigned i = 0; i < sz; ++i) { u_pair pr = std::make_pair(p[i], q[i]); if (tbl.find(pr, m)) { p[i] = m; } else { p[i] = n; tbl.insert(pr, n++); } } return n; } static void refine_forwarding( datalog::ddnf_core& ddnf, vector const& fwd_indices) { unsigned_vector roots; roots.resize(ddnf.size()); for (unsigned i = 0; i < roots.size(); ++i) { roots[i] = 0; } unsigned max_class = 1; for (unsigned i = 0; i < fwd_indices.size(); ++i) { unsigned_vector const& fwd = fwd_indices[i]; max_class = refine_forwarding(roots, fwd); } std::cout << "num classes: " << max_class << "\n"; } void tst_ddnf(char ** argv, int argc, int& i) { read_args(argv, argc, i); ptr_vector tbvs; datalog::ddnf_core* ddnf = populate_ddnf(g_file, tbvs); IF_VERBOSE(1, ddnf->display(verbose_stream());); vector fwd_indices; create_forwarding(g_file, *ddnf, tbvs, fwd_indices); refine_forwarding(*ddnf, fwd_indices); std::cout << "resulting size: " << ddnf->size() << "\n"; ddnf->display_statistics(std::cout); IF_VERBOSE(1, ddnf->display(verbose_stream()); verbose_stream() << ddnf->well_formed() << "\n";); tbv_manager& tbvm = ddnf->get_tbv_manager(); for (unsigned i = 0; i < tbvs.size(); ++i) { tbvm.deallocate(tbvs[i]); } dealloc(ddnf); } z3-z3-4.4.1/src/test/diff_logic.cpp000066400000000000000000000103241260446376700167640ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: diff_logic.cpp Abstract: Unit tests for difference logic Author: Leonardo de Moura (leonardo) 2006-11-22. Revision History: --*/ #ifdef _WINDOWS #include"rational.h" #include"diff_logic.h" #include"smt_literal.h" #include"util.h" #include"debug.h" struct diff_logic_ext { typedef rational numeral; typedef smt::literal explanation; }; template class dl_graph; typedef dl_graph dlg; struct tst_dl_functor { smt::literal_vector m_literals; void operator()(smt::literal l) { m_literals.push_back(l); } }; static void tst1() { dlg g; smt::literal l; g.init_var(1); g.init_var(2); g.init_var(3); g.enable_edge(g.add_edge(1, 2, rational(1), l)); g.enable_edge(g.add_edge(2, 3, rational(2), l)); g.push(); g.enable_edge(g.add_edge(1, 3, rational(4), l)); g.init_var(4); g.enable_edge(g.add_edge(1, 4, rational(5), l)); g.enable_edge(g.add_edge(4, 2, rational(0), l)); g.pop(1); } static void tst2() { dlg g; rational w; smt::literal l1(1); smt::literal l2(2); smt::literal l3(3); smt::literal l4(4); smt::literal l5(5); smt::literal l6(6); g.init_var(0); g.init_var(1); g.init_var(2); g.init_var(3); g.init_var(4); smt::literal d; SASSERT(g.enable_edge(g.add_edge(1, 2, rational(-1), l1))); SASSERT(g.get_edge_weight(1, 2, w, d) && w == rational(-1)); SASSERT(!g.get_edge_weight(2, 3, w, d)); SASSERT(g.enable_edge(g.add_edge(2, 3, rational(-2), l2))); SASSERT(g.enable_edge(g.add_edge(1, 4, rational(1), l3))); SASSERT(g.get_edge_weight(1, 2, w, d) && w == rational(-1)); SASSERT(g.get_edge_weight(1, 4, w, d) && w == rational(1)); SASSERT(!g.get_edge_weight(1, 3, w, d)); SASSERT(g.enable_edge(g.add_edge(2, 4, rational(10), l6))); SASSERT(g.is_feasible()); g.push(); SASSERT(g.enable_edge(g.add_edge(3, 0, rational(2), l4))); SASSERT(!g.enable_edge(g.add_edge(0, 1, rational(-1), l5))); SASSERT(!g.is_feasible()); TRACE("diff_logic", g.display(tout);); struct proc { svector found; proc(): found(7, false) { } void operator()(smt::literal l) { found[l.var()] = true; } }; proc p; g.traverse_neg_cycle(true, p); SASSERT(p.found[0] == false); SASSERT(p.found[1] == true); SASSERT(p.found[2] == true); SASSERT(p.found[3] == false); SASSERT(p.found[4] == true); SASSERT(p.found[5] == true); SASSERT(p.found[6] == false); g.pop(1); SASSERT(g.is_feasible()); TRACE("diff_logic", g.display(tout);); } static int add_edge(dlg& g, dl_var src, dl_var dst, int weight, unsigned lit) { int id = g.add_edge(src, dst, rational(weight), smt::literal(lit)); bool ok = g.enable_edge(id); SASSERT(ok); return id; } static void tst3() { dlg g; for (unsigned i = 1; i <= 10; ++i) { g.init_var(i); } add_edge(g, 1, 2, 1, 12); add_edge(g, 1, 3, 1, 13); add_edge(g, 1, 4, 1, 14); add_edge(g, 2, 5, 1, 25); add_edge(g, 2, 6, 1, 26); add_edge(g, 3, 5, 1, 35); add_edge(g, 4, 5, 1, 45); add_edge(g, 4, 6, 1, 46); int xy = add_edge(g, 5, 6, 1, 56); add_edge(g, 5, 7, 1, 57); add_edge(g, 5, 9, 1, 59); add_edge(g, 6, 7, 1, 67); add_edge(g, 6, 8, 1, 68); add_edge(g, 6, 9, 1, 69); add_edge(g, 6, 10, 1, 610); add_edge(g, 8, 10, 1, 810); add_edge(g, 9, 10, 1, 910); TRACE("diff_logic", g.display(tout);); int e38 = g.add_edge(3, 8, rational(3), smt::literal(38)); std::cout << "Edge: " << e38 << "\n"; svector subsumed; g.find_subsumed(xy, subsumed); for (unsigned i = 0; i < subsumed.size(); ++i) { std::cout << "subsumed: " << subsumed[i] << "\n"; SASSERT(e38 == subsumed[i]); tst_dl_functor tst_fn; g.explain_subsumed_lazy(xy, subsumed[i], tst_fn); for (unsigned j = 0; j < tst_fn.m_literals.size(); ++j) { std::cout << tst_fn.m_literals[j] << " "; } std::cout << "\n"; } } void tst_diff_logic() { //tst1(); //tst2(); //tst3(); } #else void tst_diff_logic() { } #endif z3-z3-4.4.1/src/test/dl_context.cpp000066400000000000000000000056441260446376700170530ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "datalog_parser.h" #include "ast_pp.h" #include "arith_decl_plugin.h" #include "dl_context.h" #include "smt_params.h" #include "dl_register_engine.h" using namespace datalog; static lbool dl_context_eval_unary_predicate(ast_manager & m, context & ctx, char const* problem_text, const char * pred_name) { parser* p = parser::create(ctx,m); TRUSTME( p->parse_string(problem_text) ); dealloc(p); func_decl * pred = ctx.try_get_predicate_decl(symbol(pred_name)); SASSERT(pred); SASSERT(pred->get_arity()==1); app_ref query_app(m.mk_app(pred, m.mk_var(0, pred->get_domain()[0])), m); lbool status = ctx.query(query_app); SASSERT(status != l_undef); return status; } static void dl_context_simple_query_test(params_ref & params) { ast_manager m; dl_decl_util decl_util(m); register_engine re; smt_params fparams; context ctx(m, re, fparams); ctx.updt_params(params); /* lbool status = */ dl_context_eval_unary_predicate(m, ctx, "Z 64\n\nP(x:Z)\nP(\"a\").", "P"); #if 0 // TBD: //zero corresponds to the first constant the datalog parser encountered, in our case "a" app_ref c_0(decl_util.mk_constant(0, res1->get_signature()[0]), m); app_ref c_1(decl_util.mk_constant(1, res1->get_signature()[0]), m); relation_fact f(m); f.push_back(c_0); SASSERT(res1->contains_fact(f)); f[0]=c_1; SASSERT(!res1->contains_fact(f)); #endif } void dl_context_saturate_file(params_ref & params, const char * f) { ast_manager m; dl_decl_util decl_util(m); smt_params fparams; register_engine re; context ctx(m, re, fparams); ctx.updt_params(params); datalog::parser * parser = datalog::parser::create(ctx, m); if (!parser || !parser->parse_file(f)) { warning_msg("ERROR: failed to parse file"); dealloc(parser); return; } dealloc(parser); std::cerr << "Saturating...\n"; ctx.get_rel_context()->saturate(); std::cerr << "Done\n"; } void tst_dl_context() { symbol relations[] = { symbol("tr_skip"), symbol("tr_sparse"), symbol("tr_hashtable"), symbol("smt_relation2") }; const unsigned rel_cnt = sizeof(relations)/sizeof(symbol); return; #if 0 const char * test_file = "c:\\tvm\\src\\benchmarks\\datalog\\t0.datalog"; params_ref params; for(unsigned rel_index=0; rel_index=0; eager_checking--) { params.set_bool("eager_emptiness_checking", eager_checking!=0); std::cerr << "Testing " << relations[rel_index] << "\n"; std::cerr << "Eager emptiness checking " << (eager_checking!=0 ? "on" : "off") << "\n"; dl_context_simple_query_test(params); dl_context_saturate_file(params, test_file); } } #endif } z3-z3-4.4.1/src/test/dl_product_relation.cpp000066400000000000000000000272171260446376700207440ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include "dl_context.h" #include "dl_register_engine.h" #include "dl_finite_product_relation.h" #include "dl_sparse_table.h" #include "rel_context.h" namespace datalog { typedef scoped_rel table_aptr; class collector_of_reduced : public table_row_pair_reduce_fn { idx_set & m_acc; public: collector_of_reduced(idx_set & accumulator) : m_acc(accumulator) {} virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { m_acc.insert(static_cast(merged_func_columns[0])); } }; void test_functional_columns(smt_params fparams, params_ref& params) { ast_manager m; register_engine re; context ctx(m, re, fparams); rel_context_base& rctx = *ctx.get_rel_context(); ctx.updt_params(params); relation_manager & rmgr(rctx.get_rmanager()); sparse_table_plugin & plugin = static_cast(*rctx.get_rmanager().get_table_plugin(symbol("sparse"))); SASSERT(&plugin); table_signature sig2; sig2.push_back(2); sig2.push_back(2); sig2.set_functional_columns(1); SASSERT(plugin.can_handle_signature(sig2)); table_fact f00; f00.push_back(0); f00.push_back(0); table_fact f01; f01.push_back(0); f01.push_back(1); table_fact f11; f11.push_back(1); f11.push_back(1); { table_aptr t0 = plugin.mk_empty(sig2); SASSERT(t0->empty()); t0->add_fact(f00); SASSERT(!t0->empty()); SASSERT(t0->get_size_estimate_rows()==1); t0->add_fact(f01); SASSERT(t0->get_size_estimate_rows()==1); t0->add_fact(f11); SASSERT(t0->get_size_estimate_rows()==2); unsigned rem_cols0[]={0}; scoped_ptr project0 = rmgr.mk_project_fn(*t0, 1, rem_cols0); table_aptr t1 = (*project0)(*t0); SASSERT(t1->get_size_estimate_rows()==2); SASSERT(t1->get_signature().functional_columns()==0); //project on non-functional column cancels functional unsigned rem_cols1[]={1}; scoped_ptr project1 = rmgr.mk_project_fn(*t0, 1, rem_cols1); table_aptr t2 = (*project1)(*t0); SASSERT(t2->get_size_estimate_rows()==2); idx_set acc; collector_of_reduced * reducer = alloc(collector_of_reduced, acc); scoped_ptr rproject = rmgr.mk_project_with_reduce_fn(*t0, 1, rem_cols0, reducer); table_aptr rt = (*rproject)(*t0); SASSERT(acc.num_elems()==1); SASSERT(rt->get_size_estimate_rows()==1); } { table_aptr t0 = plugin.mk_empty(sig2); t0->add_fact(f01); unsigned join_cols[]={1}; scoped_ptr join0 = rmgr.mk_join_fn(*t0, *t0, 1, join_cols, join_cols); table_aptr t1 = (*join0)(*t0, *t0); SASSERT(t1->get_signature().size()==4); SASSERT(t1->get_signature().functional_columns()==2); table_fact f0011; f0011.push_back(0); f0011.push_back(0); f0011.push_back(1); f0011.push_back(1); SASSERT(t1->contains_fact(f0011)); table_fact f0111 = f0011; f0111[1] = 1; SASSERT(!t1->contains_fact(f0111)); } { table_aptr t0 = plugin.mk_empty(sig2); t0->display(std::cout<<"0:"); SASSERT(t0->get_signature().functional_columns()==1); table_fact aux_fact; aux_fact = f01; TRUSTME( t0->suggest_fact(aux_fact) ); t0->display(std::cout<<"1:"); SASSERT(t0->contains_fact(f01)); SASSERT(aux_fact[1]==1); aux_fact = f00; TRUSTME( !t0->suggest_fact(aux_fact) ); t0->display(std::cout<<"2:"); SASSERT(t0->contains_fact(f01)); SASSERT(!t0->contains_fact(f00)); SASSERT(aux_fact[1]==1); t0->ensure_fact(f00); t0->display(std::cout<<"3:"); SASSERT(t0->contains_fact(f00)); SASSERT(!t0->contains_fact(f01)); } } void test_finite_product_relation(smt_params fparams, params_ref& params) { ast_manager m; register_engine re; context ctx(m, re, fparams); ctx.updt_params(params); dl_decl_util dl_util(m); relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); relation_plugin & rel_plugin = *rmgr.get_relation_plugin(params.get_sym("default_relation", symbol("sparse"))); SASSERT(&rel_plugin); finite_product_relation_plugin plg(rel_plugin, rmgr); sort_ref byte_srt_ref(dl_util.mk_sort(symbol("BYTE"), 256), m); relation_sort byte_srt = byte_srt_ref; relation_signature sig2; sig2.push_back(byte_srt); sig2.push_back(byte_srt); relation_signature sig3(sig2); sig3.push_back(byte_srt); relation_signature sig4(sig3); sig4.push_back(byte_srt); app_ref seven_ref(dl_util.mk_numeral(7, byte_srt), m); app_ref nine_ref(dl_util.mk_numeral(9, byte_srt), m); relation_element seven = seven_ref; relation_element nine = nine_ref; relation_fact f7(m); f7.push_back(seven); relation_fact f9(m); f9.push_back(nine); relation_fact f77(f7); f77.push_back(seven); relation_fact f79(f7); f79.push_back(nine); relation_fact f97(f9); f97.push_back(seven); relation_fact f99(f9); f99.push_back(nine); relation_fact f779(f77); f779.push_back(nine); relation_fact f799(f79); f799.push_back(nine); relation_fact f977(f97); f977.push_back(seven); relation_fact f7797(f779); f7797.push_back(seven); relation_fact f7997(f799); f7997.push_back(seven); bool table_cols2[] = { true, false }; bool table_cols3[] = { true, false, false }; bool table_cols4[] = { true, true, false, false }; scoped_rel r1 = plg.mk_empty(sig2, table_cols2); scoped_rel r2 = r1->clone(); scoped_rel r3 = r2->clone(); SASSERT(!r1->contains_fact(f77)); r1->add_fact(f77); SASSERT(r1->contains_fact(f77)); r2->add_fact(f79); r3->add_fact(f99); r2->display( std::cout << "r2 0\n"); scoped_rel r4 = r2->clone(); r2->display( std::cout << "r2 1\n"); r4->display( std::cout << "r4 0\n"); SASSERT(!r4->contains_fact(f77)); SASSERT(r4->contains_fact(f79)); r4->add_fact(f77); r4->display( std::cout << "r4 1\n"); SASSERT(r4->contains_fact(f77)); SASSERT(r4->contains_fact(f79)); r4->add_fact(f99); r4->display( std::cout << "r4 2\n"); SASSERT(r4->contains_fact(f99)); std::cout << "------ testing union ------\n"; r2->display( std::cout << "r2\n"); scoped_ptr union_op = rmgr.mk_union_fn(*r1, *r2, r3.get()); SASSERT(union_op); (*union_op)(*r1, *r2, r3.get()); r1->display( std::cout << "r1\n"); r2->display( std::cout << "r2\n"); r3->display( std::cout << "r3\n"); SASSERT(r1->contains_fact(f77)); SASSERT(r1->contains_fact(f79)); SASSERT(!r1->contains_fact(f99)); SASSERT(!r3->contains_fact(f77)); SASSERT(r3->contains_fact(f79)); SASSERT(r3->contains_fact(f99)); std::cout << "------ testing join ------\n"; r1->reset(); r1->add_fact(f77); r1->add_fact(f79); r1->add_fact(f97); r2->reset(); r2->add_fact(f97); r2->add_fact(f99); unsigned col0[] = { 0 }; unsigned col1[] = { 1 }; scoped_ptr join_tt = rmgr.mk_join_fn(*r1, *r2, 1, col0, col0); scoped_ptr join_tr = rmgr.mk_join_fn(*r1, *r2, 1, col0, col1); scoped_ptr join_rr = rmgr.mk_join_fn(*r1, *r2, 1, col1, col1); r1->display( std::cout << "r1\n"); r2->display( std::cout << "r2\n"); scoped_rel jr_tt = (*join_tt)(*r1, *r2); scoped_rel jr_tr = (*join_tr)(*r1, *r2); scoped_rel jr_rr = (*join_rr)(*r1, *r2); jr_tt->display( std::cout << "tt\n"); jr_tr->display( std::cout << "tr\n"); jr_rr->display( std::cout << "rr\n"); SASSERT(!jr_tt->contains_fact(f7797)); SASSERT(jr_tr->contains_fact(f7797)); SASSERT(jr_rr->contains_fact(f7797)); std::cout << "------ testing project ------\n"; scoped_rel r31 = plg.mk_empty(sig3, table_cols3); r31->add_fact(f779); r31->add_fact(f977); r31->add_fact(f799); unsigned rem_1_rel[] = { 1 }; unsigned rem_2_rel[] = { 1, 2 }; unsigned rem_1_table[] = { 0 }; scoped_ptr proj_1r = rmgr.mk_project_fn(*r31, 1, rem_1_rel); scoped_ptr proj_2r = rmgr.mk_project_fn(*r31, 2, rem_2_rel); scoped_ptr proj_1t = rmgr.mk_project_fn(*r31, 1, rem_1_table); scoped_rel sr_1r = (*proj_1r)(*r31); scoped_rel sr_2r = (*proj_2r)(*r31); scoped_rel sr_1t = (*proj_1t)(*r31); SASSERT(sr_1r->contains_fact(f79)); SASSERT(sr_1r->contains_fact(f97)); SASSERT(!sr_1r->contains_fact(f77)); SASSERT(sr_2r->contains_fact(f7)); SASSERT(sr_2r->contains_fact(f9)); SASSERT(sr_1t->contains_fact(f79)); SASSERT(!sr_1t->contains_fact(f97)); SASSERT(sr_1t->contains_fact(f77)); SASSERT(sr_1t->contains_fact(f99)); std::cout << "------ testing filter_interpreted ------\n"; scoped_rel r41 = plg.mk_empty(sig4, table_cols4); r41->add_fact(f7797); r41->add_fact(f7997); app_ref cond(m.mk_and( m.mk_not(m.mk_eq(m.mk_var(1,byte_srt), m.mk_var(2,byte_srt))), //#1!=#2 m.mk_not(m.mk_eq(m.mk_var(3,byte_srt), m.mk_var(2,byte_srt))) //#3!=#2 ), m); scoped_ptr i_filter = rmgr.mk_filter_interpreted_fn(*r41, cond); (*i_filter)(*r41); SASSERT(r41->contains_fact(f7797)); SASSERT(!r41->contains_fact(f7997)); std::cout << "------ testing filter_by_negation ------\n"; r31->reset(); r31->add_fact(f779); r31->add_fact(f977); r31->add_fact(f799); r1->reset(); r1->add_fact(f77); r1->add_fact(f79); unsigned nf_r31_cols[] = {1, 0, 1}; unsigned nf_r1_cols[] = {0, 0, 1}; scoped_ptr neg_filter = rmgr.mk_filter_by_negation_fn(*r31, *r1, 3, nf_r31_cols, nf_r1_cols); (*neg_filter)(*r31, *r1); SASSERT(!r31->contains_fact(f779)); SASSERT(r31->contains_fact(f977)); SASSERT(r31->contains_fact(f799)); } } using namespace datalog; void tst_dl_product_relation() { smt_params fparams; params_ref params; test_functional_columns(fparams, params); params.set_sym("default_relation", symbol("tr_sparse")); test_finite_product_relation(fparams, params); } #else void tst_dl_product_relation() { } #endif z3-z3-4.4.1/src/test/dl_query.cpp000066400000000000000000000225361260446376700165330ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "datalog_parser.h" #include "ast_pp.h" #include "dl_table_relation.h" #include "dl_context.h" #include "dl_register_engine.h" #include "smt_params.h" #include "stopwatch.h" #include "reg_decl_plugins.h" #include "dl_relation_manager.h" using namespace datalog; void dl_query_ask_ground_query(context & ctx, func_decl * pred, relation_fact & f, bool should_be_successful) { expr * const * q_args = reinterpret_cast(f.c_ptr()); app * query = ctx.get_manager().mk_app(pred, q_args); lbool is_sat = ctx.query(query); std::cerr << "@@ query should succeed: " << should_be_successful << "\n"; SASSERT(is_sat != l_undef); if((is_sat != l_true) == should_be_successful) { std::cerr<<"wrong ground query answer!\n"; UNREACHABLE(); } } void dl_query_ask_for_last_arg(context & ctx, func_decl * pred, relation_fact & f, bool should_be_successful) { ast_manager & m = ctx.get_manager(); expr_ref_vector query_args(m); push_into_vector(query_args, f); query_args.pop_back(); query_args.push_back(m.mk_var(0, pred->get_domain(query_args.size()))); app * query = ctx.get_manager().mk_app(pred, query_args.c_ptr()); lbool is_sat = ctx.query(query); std::cerr << "@@ last arg query should succeed: " << should_be_successful << "\n"; SASSERT(is_sat != l_undef); relation_fact res_fact(m); res_fact.push_back(f.back()); if(ctx.result_contains_fact(res_fact)!=should_be_successful) { std::cerr<<"wrong arg query answer!\n"; UNREACHABLE(); } } void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, context & ctx_b, char const* problem_file, unsigned test_count, bool use_magic_sets) { dl_decl_util decl_util(m); random_gen ran(0); register_engine re; context ctx_q(m, re, fparams); params.set_bool("magic_sets_for_queries", use_magic_sets); ctx_q.updt_params(params); { parser* p = parser::create(ctx_q,m); bool ok = p && p->parse_file(problem_file); dealloc(p); if (!ok) { std::cout << "Could not parse: " << problem_file << "\n"; return; } } relation_manager & rel_mgr_q = ctx_b.get_rel_context()->get_rmanager(); decl_set out_preds = ctx_b.get_rules().get_output_predicates(); decl_set::iterator it = out_preds.begin(); decl_set::iterator end = out_preds.end(); for(; it!=end; ++it) { func_decl * pred_b = *it; std::cerr << "Checking queries on relation " << pred_b->get_name() << "\n"; func_decl * pred_q = ctx_q.try_get_predicate_decl(symbol(pred_b->get_name().bare_str())); SASSERT(pred_q); relation_base & rel_b = ctx_b.get_rel_context()->get_relation(pred_b); relation_signature sig_b = rel_b.get_signature(); relation_signature sig_q = ctx_q.get_rel_context()->get_relation(pred_q).get_signature(); SASSERT(sig_b.size()==sig_q.size()); std::cerr << "Queries on random facts...\n"; relation_fact f_b(m); relation_fact f_q(m); for(unsigned attempt=0; attempt(rel_b); table_base & table_b = tr_b.get_table(); table_fact tf; unsigned table_sz = table_b.get_size_estimate_rows(); table_base::iterator fit = table_b.begin(); table_base::iterator fend = table_b.end(); for(; fit!=fend; ++fit) { if(ran()%std::max(1u,table_sz/test_count)!=0) { continue; } fit->get_fact(tf); rel_mgr_q.table_fact_to_relation(sig_q, tf, f_q); dl_query_ask_ground_query(ctx_q, pred_q, f_q, true); dl_query_ask_for_last_arg(ctx_q, pred_q, f_q, true); } std::cerr << "Done.\n"; } } void dl_query_test_wpa(smt_params & fparams, params_ref& params) { params.set_bool("magic_sets_for_queries", true); ast_manager m; random_gen ran(0); reg_decl_plugins(m); arith_util arith(m); const char * problem_dir = "C:\\tvm\\src\\z3_2\\debug\\test\\w0.datalog"; dl_decl_util dl_util(m); std::cerr << "Testing queries on " << problem_dir <<"\n"; register_engine re; context ctx(m, re, fparams); ctx.updt_params(params); { wpa_parser* p = wpa_parser::create(ctx, m); bool ok = p->parse_directory(problem_dir); dealloc(p); if (!ok) { std::cout << "Could not parse: " << problem_dir << "\n"; return; } } const unsigned attempts = 10; func_decl * v_pred = ctx.try_get_predicate_decl(symbol("V")); SASSERT(v_pred); sort * var_sort = v_pred->get_domain(0); uint64 var_sz; TRUSTME( ctx.try_get_sort_constant_count(var_sort, var_sz) ); for(unsigned attempt=0; attemptparse_file(problem_file); dealloc(p); if (!ok) { std::cout << "Could not parse: " << problem_file << "\n"; return; } } ctx_base.get_rel_context()->saturate(); for(unsigned use_restarts=0; use_restarts<=1; use_restarts++) { params.set_uint("initial_restart_timeout", use_restarts ? 100 : 0); for(unsigned use_similar=0; use_similar<=1; use_similar++) { params.set_uint("similarity_compressor", use_similar != 0); for(unsigned use_magic_sets=0; use_magic_sets<=1; use_magic_sets++) { stopwatch watch; if (!(use_restarts == 1 && use_similar == 0 && use_magic_sets == 1)) { continue; } watch.start(); std::cerr << "------- " << (use_restarts ? "With" : "Without") << " restarts -------\n"; std::cerr << "------- " << (use_similar ? "With" : "Without") << " similar compressor -------\n"; std::cerr << "------- " << (use_magic_sets ? "With" : "Without") << " magic sets -------\n"; dl_query_test(m, fparams, params, ctx_base, problem_file, 1, use_magic_sets!=0); watch.stop(); std::cout << (use_restarts ? "With" : "Without") << " restarts\n"; std::cout << (use_similar ? "With" : "Without") << " similar compressor\n"; std::cout << (use_magic_sets ? "With" : "Without") << " magic sets\n"; std::cout << "Time: " << watch.get_current_seconds() << "\n\n"; std::cerr << "Time: " << watch.get_current_seconds() << "\n"; } } } } z3-z3-4.4.1/src/test/dl_relation.cpp000066400000000000000000000263701260446376700172030ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include "dl_context.h" #include "dl_register_engine.h" #include "dl_relation_manager.h" #include "dl_interval_relation.h" #include "dl_bound_relation.h" #include "dl_product_relation.h" #include "util.h" namespace datalog { static void test_interval_relation() { smt_params params; ast_manager ast_m; register_engine re; context ctx(ast_m, re, params); arith_util autil(ast_m); relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(interval_relation_plugin, m)); interval_relation_plugin& ip = dynamic_cast(*m.get_relation_plugin(symbol("interval_relation"))); SASSERT(&ip); relation_signature sig; sort* int_sort = autil.mk_int(); sig.push_back(int_sort); sig.push_back(int_sort); sig.push_back(int_sort); sig.push_back(int_sort); interval_relation& i1 = dynamic_cast(*ip.mk_empty(sig)); interval_relation& i2 = dynamic_cast(*ip.mk_full(0, sig)); i1.display(std::cout); i2.display(std::cout); SASSERT(i1.empty()); SASSERT(!i2.empty()); app_ref cond1(ast_m), cond2(ast_m), cond3(ast_m); app_ref cond4(ast_m), cond5(ast_m), cond6(ast_m); app_ref num1(ast_m); cond1 = autil.mk_le(ast_m.mk_var(0, int_sort), autil.mk_numeral(rational(0), true)); cond2 = autil.mk_le(ast_m.mk_var(1, int_sort), autil.mk_numeral(rational(1), true)); cond3 = autil.mk_le(ast_m.mk_var(2, int_sort), autil.mk_numeral(rational(2), true)); cond4 = autil.mk_ge(ast_m.mk_var(0, int_sort), autil.mk_numeral(rational(0), true)); cond5 = autil.mk_ge(ast_m.mk_var(1, int_sort), autil.mk_numeral(rational(0), true)); cond6 = autil.mk_ge(ast_m.mk_var(2, int_sort), autil.mk_numeral(rational(5), true)); num1 = autil.mk_numeral(rational(4), true); i2.filter_interpreted(cond1); i2.display(std::cout); // x0 <= 0 unsigned cols1[2] = { 1, 2}; unsigned cols2[2] = { 2, 3}; relation_join_fn* join1 = ip.mk_join_fn(i1, i2, 2, cols1, cols2); relation_transformer_fn* proj1 = ip.mk_project_fn(i1, 2, cols2); relation_transformer_fn* ren1 = ip.mk_rename_fn(i1, 2, cols2); relation_union_fn* union1 = ip.mk_union_fn(i1, i2, &i1); relation_mutator_fn* filterId1 = ip.mk_filter_identical_fn(i1, 2, cols1); relation_mutator_fn* filterEq1 = ip.mk_filter_equal_fn(i1, num1, 2); relation_mutator_fn* filterCond1 = ip.mk_filter_interpreted_fn(i1, cond2); relation_base* i3 = (*join1)(i2, i2); i3->display(std::cout); relation_transformer_fn* proj2 = ip.mk_project_fn(*i3, 2, cols2); (*filterEq1)(i2); i2.display(std::cout); // x0 <= 0 // x2 = 4 (*filterId1)(i2); i2.display(std::cout); // x0 <= 0 // x1 = x2 = 4 relation_fact fact1(ast_m); fact1.push_back(autil.mk_numeral(rational(0), true)); fact1.push_back(autil.mk_numeral(rational(4), true)); fact1.push_back(autil.mk_numeral(rational(4), true)); fact1.push_back(autil.mk_numeral(rational(5), true)); SASSERT(i2.contains_fact(fact1)); fact1[0] = autil.mk_numeral(rational(-1), true); SASSERT(i2.contains_fact(fact1)); fact1[0] = autil.mk_numeral(rational(1), true); SASSERT(!i2.contains_fact(fact1)); relation_base* i5 = (*ren1)(i2); i2.display(std::cout << "Orig\n"); i5->display(std::cout << "renamed 2 |-> 3 |-> 2\n"); (*filterCond1)(i2); i2.display(std::cout); // empty SASSERT(i2.empty()); relation_base* i4 = (*proj2)(*i3); i4->display(std::cout); i1.deallocate(); i2.deallocate(); i3->deallocate(); i4->deallocate(); i5->deallocate(); dealloc(join1); dealloc(proj1); dealloc(ren1); dealloc(union1); dealloc(filterId1); dealloc(filterEq1); dealloc(filterCond1); } static void test_bound_relation() { std::cout << "bound relation\n"; smt_params params; ast_manager ast_m; register_engine re; context ctx(ast_m, re, params); arith_util autil(ast_m); relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(bound_relation_plugin, m)); bound_relation_plugin& br = dynamic_cast(*m.get_relation_plugin(symbol("bound_relation"))); SASSERT(&br); relation_signature sig; sort* int_sort = autil.mk_int(); sig.push_back(int_sort); sig.push_back(int_sort); sig.push_back(int_sort); sig.push_back(int_sort); bound_relation& i1 = dynamic_cast(*br.mk_empty(sig)); bound_relation& i2 = dynamic_cast(*br.mk_full(0, sig)); i1.display(std::cout << "empty:\n"); i2.display(std::cout << "full:\n"); SASSERT(i1.empty()); SASSERT(!i2.empty()); app_ref cond1(ast_m), cond2(ast_m), cond3(ast_m); app_ref cond4(ast_m), cond5(ast_m), cond6(ast_m); app_ref num1(ast_m); cond1 = autil.mk_lt(ast_m.mk_var(0, int_sort), autil.mk_numeral(rational(0), true)); cond2 = autil.mk_lt(ast_m.mk_var(1, int_sort), autil.mk_numeral(rational(1), true)); cond3 = autil.mk_lt(ast_m.mk_var(2, int_sort), ast_m.mk_var(3, int_sort)); cond4 = autil.mk_ge(ast_m.mk_var(0, int_sort), autil.mk_numeral(rational(0), true)); cond5 = autil.mk_ge(ast_m.mk_var(1, int_sort), autil.mk_numeral(rational(0), true)); cond6 = autil.mk_ge(ast_m.mk_var(2, int_sort), autil.mk_numeral(rational(5), true)); app_ref lt_x0x1(ast_m), lt_x1x2(ast_m), lt_x0x3(ast_m), lt_x0x2(ast_m); lt_x0x1 = autil.mk_lt(ast_m.mk_var(0, int_sort), ast_m.mk_var(1, int_sort)); lt_x1x2 = autil.mk_lt(ast_m.mk_var(1, int_sort), ast_m.mk_var(2, int_sort)); lt_x0x2 = autil.mk_lt(ast_m.mk_var(0, int_sort), ast_m.mk_var(2, int_sort)); lt_x0x3 = autil.mk_lt(ast_m.mk_var(0, int_sort), ast_m.mk_var(3, int_sort)); num1 = autil.mk_numeral(rational(4), true); unsigned cols1[2] = { 1, 2}; unsigned cols2[2] = { 2, 3}; unsigned cols3[3] = { 0, 2, 3 }; relation_join_fn* join1 = br.mk_join_fn(i1, i2, 2, cols1, cols2); relation_transformer_fn* proj1 = br.mk_project_fn(i1, 2, cols2); relation_transformer_fn* ren1 = br.mk_rename_fn(i1, 3, cols3); relation_union_fn* union1 = br.mk_union_fn(i1, i2, &i1); relation_mutator_fn* filterId1 = br.mk_filter_identical_fn(i1, 2, cols1); relation_mutator_fn* filterEq1 = br.mk_filter_equal_fn(i1, num1, 2); relation_mutator_fn* filterCond1 = br.mk_filter_interpreted_fn(i1, cond3); relation_base* i3 = (*join1)(i2, i2); i3->display(std::cout); relation_transformer_fn* proj2 = br.mk_project_fn(*i3, 2, cols2); (*filterEq1)(i2); i2.display(std::cout << "no-op still full\n"); // no-op (*filterCond1)(i2); i2.display(std::cout << "x2 < x3\n"); // x2 < x3 (*filterId1)(i2); i2.display(std::cout << "id\n"); // x1 = x2 < x3 relation_fact fact1(ast_m); i2.display(std::cout << "Orig\n"); std::cout << "renamed "; for (unsigned i = 0; i < 3; ++i) { std::cout << cols3[i] << " "; } std::cout << "\n"; relation_base* i5 = (*ren1)(i2); i5->display(std::cout); //SASSERT(i2.empty()); relation_base* i4 = (*proj2)(*i3); i4->display(std::cout); // test that equivalence classes are expanded. // { x1 = x3, x0 < x1 x1 < x2} u { x2 = x3, x0 < x3 } = { x0 < x3 } { relation_base* b1 = br.mk_full(0, sig); relation_base* b2 = br.mk_full(0, sig); unsigned x1x3[2] = { 1, 3 }; unsigned x2x3[2] = { 2, 3 }; scoped_ptr id1 = br.mk_filter_identical_fn(*b1, 2, x1x3); scoped_ptr ltx0x1 = br.mk_filter_interpreted_fn(*b1, lt_x0x1); scoped_ptr ltx1x2 = br.mk_filter_interpreted_fn(*b1, lt_x1x2); scoped_ptr ltx0x3 = br.mk_filter_interpreted_fn(*b2, lt_x0x3); scoped_ptr id2 = br.mk_filter_identical_fn(*b2, 2, x2x3); (*id1)(*b1); (*ltx0x1)(*b1); (*ltx1x2)(*b1); b2->display(std::cout << "b2:\n"); (*id2)(*b2); b2->display(std::cout << "b2:\n"); (*ltx0x3)(*b2); b2->display(std::cout << "b2:\n"); scoped_ptr u = br.mk_union_fn(*b1, *b2, 0); b1->display(std::cout << "b1:\n"); b2->display(std::cout << "b2:\n"); (*u)(*b1, *b2, 0); b1->display(std::cout << "b1 u b2:\n"); // TBD check property; b1->deallocate(); b2->deallocate(); } // test that equivalence classes are expanded. // { x1 = x2 = x3, x0 < x1} u { x1 = x3, x0 < x3, x0 < x2 } = { x0 < x2, x0 < x3 } { relation_base* b1 = br.mk_full(0, sig); relation_base* b2 = br.mk_full(0, sig); unsigned x0x3[2] = { 0, 3 }; unsigned x1x3[2] = { 1, 3 }; unsigned x2x3[2] = { 2, 3 }; scoped_ptr id1 = br.mk_filter_identical_fn(*b1, 2, x1x3); scoped_ptr id2 = br.mk_filter_identical_fn(*b1, 2, x2x3); scoped_ptr ltx0x1 = br.mk_filter_interpreted_fn(*b1, lt_x0x1); scoped_ptr ltx0x2 = br.mk_filter_interpreted_fn(*b2, lt_x0x2); scoped_ptr ltx0x3 = br.mk_filter_interpreted_fn(*b2, lt_x0x3); scoped_ptr id3 = br.mk_filter_identical_fn(*b2, 2, x1x3); (*id1)(*b1); (*id2)(*b1); (*ltx0x1)(*b1); (*id3)(*b2); (*ltx0x2)(*b2); (*ltx0x3)(*b2); scoped_ptr u = br.mk_union_fn(*b1, *b2, 0); b1->display(std::cout << "b1:\n"); b2->display(std::cout << "b2:\n"); (*u)(*b1, *b2, 0); b1->display(std::cout << "b1 u b2:\n"); // TBD check property; b1->deallocate(); b2->deallocate(); } i1.deallocate(); i2.deallocate(); i3->deallocate(); i4->deallocate(); i5->deallocate(); dealloc(join1); dealloc(proj1); dealloc(ren1); dealloc(union1); dealloc(filterId1); dealloc(filterEq1); dealloc(filterCond1); } } void tst_dl_relation() { datalog::test_interval_relation(); datalog::test_bound_relation(); } #else void tst_dl_relation() { } #endif z3-z3-4.4.1/src/test/dl_table.cpp000066400000000000000000000103101260446376700164400ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation --*/ #if defined(_WINDOWS) || defined(_CYGWIN) #include "dl_context.h" #include "dl_table.h" #include "dl_register_engine.h" #include "dl_relation_manager.h" typedef datalog::table_base* (*mk_table_fn)(datalog::relation_manager& m, datalog::table_signature& sig); static datalog::table_base* mk_bv_table(datalog::relation_manager& m, datalog::table_signature& sig) { datalog::table_plugin * p = m.get_table_plugin(symbol("bitvector")); SASSERT(p); return p->mk_empty(sig); } static void test_table(mk_table_fn mk_table) { datalog::table_signature sig; sig.push_back(2); sig.push_back(4); sig.push_back(8); sig.push_back(4); smt_params params; ast_manager ast_m; datalog::register_engine re; datalog::context ctx(ast_m, re, params); datalog::relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(datalog::bitvector_table_plugin, m)); datalog::table_base* _tbl = mk_table(m, sig); datalog::table_base& table = *_tbl; datalog::table_fact row, row1, row2, row3; row.push_back(1); row.push_back(3); row.push_back(7); row.push_back(2); row1 = row; row[3] = 3; row2 = row; row[0] = 0; row[3] = 1; row3 = row; table.add_fact(row1); table.add_fact(row2); table.display(std::cout); datalog::table_base::iterator it = table.begin(); datalog::table_base::iterator end = table.end(); for (; it != end; ++it) { it->get_fact(row); for (unsigned j = 0; j < row.size(); ++j) { std::cout << row[j] << " "; } std::cout << "\n"; } SASSERT(table.contains_fact(row1)); SASSERT(table.contains_fact(row2)); SASSERT(!table.contains_fact(row3)); #if 0 table.remove_facts(1, &row1); SASSERT(!table.contains_fact(row1)); #endif table.add_fact(row1); datalog::table_base* _tbl2 = mk_table(m, sig); datalog::table_base& table2 = *_tbl2; table2.add_fact(row2); table2.add_fact(row3); unsigned cols1[1] = { 1 }; unsigned cols2[1] = { 3 }; datalog::table_join_fn * j1 = m.mk_join_fn(table2, table, 1, cols1, cols2); datalog::table_base* _tbl3 = (*j1)(table2,table); _tbl3->display(std::cout); datalog::table_join_fn * j2 = m.mk_join_fn(table2, table, 1, cols1, cols1); datalog::table_base* _tbl4 = (*j2)(table2,table); _tbl4->display(std::cout); dealloc(j1); dealloc(j2); _tbl->deallocate(); (_tbl2->deallocate()); (_tbl3->deallocate()); (_tbl4->deallocate()); } void test_dl_bitvector_table() { test_table(mk_bv_table); } void test_table_min() { std::cout << "----- test_table_min -----\n"; datalog::table_signature sig; sig.push_back(2); sig.push_back(4); sig.push_back(8); smt_params params; ast_manager ast_m; datalog::register_engine re; datalog::context ctx(ast_m, re, params); datalog::relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(datalog::bitvector_table_plugin, m)); datalog::table_base* tbl = mk_bv_table(m, sig); datalog::table_base& table = *tbl; datalog::table_fact row, row1, row2, row3; row.push_back(1); row.push_back(2); row.push_back(5); // Group (1,2,*) row1 = row; row[2] = 6; row2 = row; row[2] = 5; row3 = row; table.add_fact(row1); table.add_fact(row2); table.add_fact(row3); // Group (1,3,*) row[1] = 3; row1 = row; row[2] = 7; row2 = row; row[2] = 4; row3 = row; table.add_fact(row1); table.add_fact(row2); table.add_fact(row3); table.display(std::cout); unsigned_vector group_by(2); group_by[0] = 0; group_by[1] = 1; datalog::table_min_fn * min_fn = m.mk_min_fn(table, group_by, 2); datalog::table_base * min_tbl = (*min_fn)(table); min_tbl->display(std::cout); row[1] = 2; row[2] = 5; SASSERT(min_tbl->contains_fact(row)); row[1] = 3; row[2] = 4; SASSERT(min_tbl->contains_fact(row)); dealloc(min_fn); min_tbl->deallocate(); tbl->deallocate(); } void tst_dl_table() { test_dl_bitvector_table(); test_table_min(); } #else void tst_dl_table() { } #endif z3-z3-4.4.1/src/test/dl_util.cpp000066400000000000000000000024121260446376700163320ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "dl_util.h" using namespace datalog; void dl_util_two_array_sort() { const unsigned num = 97; unsigned a1[num]; unsigned a2[num]; for(unsigned i=0; i result; // VERIFY(!m.intersect(*d1,*d0, result)); // m.subtract(*d1,*d0, result); SASSERT(result.empty()); dX = m.allocateX(); m.display(std::cout, *d0) << "\n"; m.display(std::cout, *dX) << "\n"; SASSERT(m.contains(*dX,*d1)); SASSERT(m.contains(*dX,*d0)); SASSERT(!m.contains(*d0,*d1)); SASSERT(!m.contains(*d1,*d0)); d1->neg().push_back(m.tbvm().allocate0()); m.display(std::cout, *d1) << " -> "; VERIFY(m.fold_neg(*d1)); m.display(std::cout, *d1) << "\n"; bit_vector to_delete; to_delete.resize(n, false); to_delete.set(1); to_delete.set(3); doc_manager m1(n-2); doc_ref d1_1(m1, m.project(m1, to_delete, *d1)); doc_ref d1_2(m1, m1.allocate1()); m.display(std::cout, *d1) << " -> "; m1.display(std::cout, *d1_1) << "\n"; SASSERT(m1.equals(*d1_1,*d1_2)); m.set(*d1,2,BIT_x); m.set(*d1,4,BIT_x); d1_1 = m.project(m1, to_delete, *d1); m.display(std::cout, *d1) << " -> "; m1.display(std::cout, *d1_1) << "\n"; d1->neg().push_back(m.tbvm().allocate1()); SASSERT(m.well_formed(*d1)); d1_1 = m.project(m1, to_delete, *d1); m.display(std::cout, *d1) << " -> "; m1.display(std::cout, *d1_1) << "\n"; } // generate "all" clauses over num_vars // create XXXX \ clauses // project 0, 1, 2, 3 variables // check that result is the same as QE over those clauses. class test_doc_cls { random_gen m_ran; ast_manager m; doc_manager dm; expr_ref_vector m_vars; tbit choose_tbit() { switch (m_ran(3)) { case 0: return BIT_0; case 1: return BIT_1; default : return BIT_x; } } tbv* mk_rand_tbv() { tbv* result = dm.tbvm().allocate(); for (unsigned i = 0; i < dm.num_tbits(); ++i) { dm.tbvm().set(*result, i, choose_tbit()); } return result; } tbv* mk_rand_tbv(tbv const& pos) { tbv* result = dm.tbvm().allocate(); for (unsigned i = 0; i < dm.num_tbits(); ++i) { if (pos[i] == BIT_x) { dm.tbvm().set(*result, i, choose_tbit()); } else { dm.tbvm().set(*result, i, pos[i]); } } return result; } doc* mk_rand_doc(unsigned num_diff) { tbv_ref t(dm.tbvm()); t = mk_rand_tbv(); doc* result = dm.allocate(*t); SASSERT(dm.tbvm().equals(*t, result->pos())); for (unsigned i = 0; i < num_diff; ++i) { result->neg().push_back(mk_rand_tbv(result->pos())); } SASSERT(dm.well_formed(*result)); return result; } void mk_rand_udoc(unsigned num_elems, unsigned num_diff, udoc& result) { result.reset(dm); for (unsigned i = 0; i < num_elems; ++i) { result.push_back(mk_rand_doc(num_diff)); } } expr_ref mk_conj(tbv& t) { expr_ref result(m); expr_ref_vector conjs(m); for (unsigned i = 0; i < m_vars.size(); ++i) { tbit b = choose_tbit(); dm.tbvm().set(t, i, b); switch (b) { case BIT_1: conjs.push_back(m_vars[i].get()); break; case BIT_0: conjs.push_back(m.mk_not(m_vars[i].get())); break; default: break; } } result = mk_and(m, conjs.size(), conjs.c_ptr()); return result; } expr_ref to_formula(tbv const& t, doc_manager& m2) { expr_ref result(m); expr_ref_vector conjs(m); unsigned n = m2.num_tbits(); SASSERT(n <= m_vars.size()); for (unsigned i = 0; i < n; ++i) { switch (t[i]) { case BIT_x: break; case BIT_1: conjs.push_back(m_vars[i].get()); break; case BIT_0: conjs.push_back(m.mk_not(m_vars[i].get())); break; default: UNREACHABLE(); break; } } result = mk_and(m, conjs.size(), conjs.c_ptr()); return result; } expr_ref to_formula(doc const& d, doc_manager& m2) { expr_ref result(m); expr_ref_vector conjs(m); conjs.push_back(to_formula(d.pos(), m2)); for (unsigned i = 0; i < d.neg().size(); ++i) { conjs.push_back(m.mk_not(to_formula(d.neg()[i], m2))); } result = mk_and(m, conjs.size(), conjs.c_ptr()); return result; } expr_ref to_formula(udoc const& ud, doc_manager& m2) { expr_ref result(m); expr_ref_vector disjs(m); for (unsigned i = 0; i < ud.size(); ++i) { disjs.push_back(to_formula(ud[i], m2)); } result = mk_or(m, disjs.size(), disjs.c_ptr()); return result; } void project(doc const& d, doc_manager& m2, const bit_vector& to_delete, doc_ref& result) { result = dm.project(m2, to_delete, d); TRACE("doc", for (unsigned i = 0; i < m_vars.size(); ++i) { tout << (to_delete.get(i)?"0":"1"); } tout << " "; dm.display(tout, d) << " -> "; m2.display(tout, *result) << "\n"; ); } void test_project(unsigned num_clauses) { doc_ref d(dm); d = mk_rand_doc(3); expr_ref fml1(m), fml2(m), fml3(m), tmp1(m), tmp2(m), fml(m); fml1 = to_formula(*d, dm); bit_vector to_delete; to_delete.reserve(m_vars.size(), false); unsigned num_bits = 1; for (unsigned i = 1; i < to_delete.size(); ++i) { to_delete.set(i, m_ran(2) == 0); if (!to_delete.get(i)) ++num_bits; } doc_manager m2(num_bits); doc_ref result(m2); project(*d, m2, to_delete, result); TRACE("doc", dm.display(tout, *d) << "\n"; m2.display(tout, *result) << "\n";); fml2 = to_formula(*result, m2); project_expand(fml1, to_delete); project_rename(fml2, to_delete); check_equiv(fml1, fml2); } void project_expand(expr_ref& fml, bit_vector const& to_delete) { expr_ref tmp1(m), tmp2(m); for (unsigned i = 0; i < m_vars.size(); ++i) { if (to_delete.get(i)) { expr_safe_replace rep1(m), rep2(m); rep1.insert(m_vars[i].get(), m.mk_true()); rep1(fml, tmp1); rep2.insert(m_vars[i].get(), m.mk_false()); rep2(fml, tmp2); if (tmp1 == tmp2) { fml = tmp1; } else { fml = m.mk_or(tmp1, tmp2); } } } } void project_rename(expr_ref& fml, bit_vector const& to_delete) { expr_safe_replace rep(m); for (unsigned i = 0, j = 0; i < m_vars.size(); ++i) { if (!to_delete.get(i)) { rep.insert(m_vars[j].get(), m_vars[i].get()); ++j; } } rep(fml); } void test_merge(unsigned num_clauses) { doc_ref d(dm, dm.allocateX()); expr_ref_vector fmls(m), eqs(m); unsigned N = m_vars.size(); expr_ref fml1(m), fml2(m), fml3(m), tmp1(m), tmp2(m), fml(m); for (unsigned i = 0; i < num_clauses; ++i) { tbv* t = dm.tbvm().allocate(); fmls.push_back(m.mk_not(mk_conj(*t))); d->neg().push_back(t); } fml1 = mk_and(m, fmls.size(), fmls.c_ptr()); svector to_merge(N, false); bit_vector discard_cols; discard_cols.resize(N, false); unsigned num_bits = 1; union_find_default_ctx union_ctx; subset_ints equalities(union_ctx); unsigned lo = N; equalities.mk_var(); for (unsigned i = 1; i < N; ++i) { to_merge[i] = (m_ran(2) == 0); if (!to_merge[i]) ++num_bits; else lo = i; equalities.mk_var(); } if (lo == N) return; for (unsigned i = 0; i < N; ++i) { if (to_merge[i] && i != lo) { equalities.merge(i, lo); eqs.push_back(m.mk_eq(m_vars[i].get(), m_vars[lo].get())); } } eqs.push_back(to_formula(*d, dm)); fml1 = mk_and(m, eqs.size(), eqs.c_ptr()); if (dm.merge(*d, lo, 1, equalities, discard_cols)) { fml2 = to_formula(*d, dm); } else { fml2 = m.mk_false(); } check_equiv(fml1, fml2); } void check_equiv(expr_ref& fml1, expr_ref& fml2) { th_rewriter rw(m); rw(fml1); rw(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"; ); } SASSERT(res == l_false); } public: test_doc_cls(unsigned num_vars): dm(num_vars), m_vars(m) { reg_decl_plugins(m); for (unsigned i = 0; i < num_vars; ++i) { m_vars.push_back(m.mk_fresh_const("b", m.mk_bool_sort())); } } void test_project(unsigned num_rounds, unsigned num_clauses) { for (unsigned i = 0; i < num_rounds; ++i) { test_project(num_clauses); } } void test_merge(unsigned num_rounds, unsigned num_clauses) { for (unsigned i = 0; i < num_rounds; ++i) { test_merge(num_clauses); } } void test_project1() { expr_ref fml1(m), fml2(m); doc_ref d(dm, dm.allocateX()); tbv_ref t(dm.tbvm(), dm.tbvm().allocateX()); dm.tbvm().set(*t, 0, BIT_0); d->neg().push_back(t.detach()); unsigned num_bits = dm.num_tbits(); bit_vector to_delete; to_delete.reserve(num_bits, false); fml1 = to_formula(*d, dm); to_delete.set(0, true); doc_manager m2(num_bits-1); doc_ref result(m2); project(*d, m2, to_delete, result); dm.display(std::cout, *d) << "\n"; m2.display(std::cout, *result) << "\n"; fml2 = to_formula(*result, m2); project_rename(fml2, to_delete); project_expand(fml1, to_delete); std::cout << fml1 << " " << fml2 << "\n"; check_equiv(fml1, fml2); } void test_subtract() { doc_ref d1(dm); doc_ref d2(dm); doc_ref d3(dm); udoc ds1, ds2; d1 = dm.allocateX(); d2 = dm.allocateX(); d3 = dm.allocateX(); dm.set(*d1, 0, BIT_1); dm.set(*d1, 1, BIT_0); dm.set(*d2, 0, BIT_0); dm.set(*d2, 1, BIT_1); //ds1.push_back(d1.detach()); ds1.push_back(d2.detach()); // ds1 = {10x, 01x} d1 = dm.allocateX(); tbv_ref t1(dm.tbvm()); tbv_ref t2(dm.tbvm()); t1 = dm.tbvm().allocateX(); t2 = dm.tbvm().allocateX(); dm.tbvm().set(*t1, 0, BIT_1); dm.tbvm().set(*t1, 2, BIT_0); dm.tbvm().set(*t2, 0, BIT_0); dm.tbvm().set(*t2, 2, BIT_1); d1->neg().push_back(t1.detach()); d1->neg().push_back(t2.detach()); ds2.push_back(d1.detach()); ds1.display(dm, std::cout) << "\n"; ds2.display(dm, std::cout) << "\n"; expr_ref fml1 = to_formula(ds1, dm); expr_ref fml2 = to_formula(ds2, dm); ds1.subtract(dm, ds2); ds1.display(dm, std::cout) << "\n"; expr_ref fml3 = to_formula(ds1, dm); fml1 = m.mk_and(fml1, m.mk_not(fml2)); check_equiv(fml1, fml3); ds1.reset(dm); ds2.reset(dm); //sub:{xxx \ {1x0, 0x1}} //result:{100} for (unsigned i = 0; i < 1000; ++i) { udoc d1, d2; mk_rand_udoc(3, 3, d1); mk_rand_udoc(3, 3, d2); fml1 = to_formula(d1, dm); fml2 = to_formula(d2, dm); d1.subtract(dm, d2); fml3 = to_formula(d1, dm); fml1 = m.mk_and(fml1, m.mk_not(fml2)); check_equiv(fml1, fml3); d1.reset(dm); d2.reset(dm); } } void test_intersect() { expr_ref fml1(m), fml2(m), fml3(m); for (unsigned i = 0; i < 10000; ++i) { udoc d1, d2; mk_rand_udoc(3, 3, d1); mk_rand_udoc(3, 3, d2); fml1 = to_formula(d1, dm); fml2 = to_formula(d2, dm); TRACE("doc", d1.display(dm, tout) << "\n"; d2.display(dm, tout) << "\n";); d1.intersect(dm, d2); TRACE("doc", d1.display(dm, tout) << "\n";); SASSERT(d1.well_formed(dm)); fml3 = to_formula(d1, dm); fml1 = m.mk_and(fml1, fml2); check_equiv(fml1, fml3); d1.reset(dm); d2.reset(dm); } } }; void tst_doc() { test_doc_cls tp(4); tp.test_project1(); tp.test_project(200,7); tp.test_intersect(); tp.test_subtract(); tp.test_merge(200,7); tst_doc1(5); tst_doc1(10); tst_doc1(70); } z3-z3-4.4.1/src/test/escaped.cpp000066400000000000000000000017441260446376700163110ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: escaped_str.cpp Abstract: Escaped strings Author: Leonardo de Moura (leonardo) 2011-03-01. Revision History: --*/ #include"util.h" void tst_escaped() { std::cout << "[" << escaped("\"hello\"\"world\"\n\n") << "]\n"; std::cout << "[" << escaped("\"hello\"\nworld\"\n\n\n", true) << "]\n"; std::cout << "[" << escaped("\"hello\"\nworld\"\n", true) << "]\n"; std::cout << "[" << escaped("\"hello\"\nworld\"", true) << "]\n"; std::cout << "[" << escaped("\"hello\"\n\"world\"\n\n") << "]\n"; std::cout << "[" << escaped("\n\n\n", true) << "]\n"; std::cout << "[" << escaped("\n\n\n") << "]\n"; std::cout << "[" << escaped("\n", true) << "]\n"; std::cout << "[" << escaped("\n") << "]\n"; std::cout << "[" << escaped("", true) << "]\n"; std::cout << "[" << escaped("") << "]\n"; std::cout << "[" << escaped(0, true) << "]\n"; std::cout << "[" << escaped(0) << "]\n"; } z3-z3-4.4.1/src/test/ex.cpp000066400000000000000000000020221260446376700153070ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ex.cpp Abstract: Author: Leonardo de Moura (leonardo) 2011-04-28 Revision History: --*/ #include #include"z3_exception.h" class ex { public: virtual ~ex() {} virtual char const * msg() const = 0; }; class ex1 : public ex { char const * m_msg; public: ex1(char const * m):m_msg(m) {} virtual char const * msg() const { return m_msg; } }; class ex2 : public ex { std::string m_msg; public: ex2(char const * m):m_msg(m) {} virtual char const * msg() const { return m_msg.c_str(); } }; static void th() { throw ex2("testing exception"); } static void tst1() { try { th(); } catch (ex & e) { std::cerr << e.msg() << "\n"; } } static void tst2() { try { throw default_exception(default_exception::fmt(), "Format %d %s", 12, "twelve"); } catch (z3_exception& ex) { std::cerr << ex.msg() << "\n"; } } void tst_ex() { tst1(); tst2(); } z3-z3-4.4.1/src/test/expr_rand.cpp000066400000000000000000000056531260446376700166720ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "expr_rand.h" #include "ast_pp.h" #include "bv_decl_plugin.h" #include "array_decl_plugin.h" #include "arith_decl_plugin.h" #include "ast_smt_pp.h" #include #include #include "reg_decl_plugins.h" static unsigned rand_seed = 1; void tst_expr_arith(unsigned num_files) { ast_manager m; reg_decl_plugins(m); expr_rand er(m); er.seed(rand_seed); er.initialize_arith(20); family_id fid = m.mk_family_id("arith"); sort* int_ty = m.mk_sort(fid, INT_SORT, 0, 0); sort* real_ty = m.mk_sort(fid, REAL_SORT, 0, 0); er.initialize_array(3, int_ty, int_ty); er.initialize_array(3, int_ty, real_ty); er.initialize_basic(20); for (unsigned i = 0; i < num_files; ++i) { expr_ref e(m); er.get_next(m.mk_bool_sort(), e); ast_smt_pp pp(m); pp.set_logic("QF_AUFLIA"); std::ostringstream buffer; buffer << "random_arith_" << i << ".smt"; std::cout << buffer.str() << "\n"; std::ofstream file(buffer.str().c_str()); pp.display(file, e.get()); file.close(); } } void tst_expr_rand(unsigned num_files) { ast_manager m; m.register_plugin(symbol("bv"), alloc(bv_decl_plugin)); m.register_plugin(symbol("array"), alloc(array_decl_plugin)); expr_rand er(m); er.initialize_bv(20); er.seed(rand_seed); parameter p1(1); parameter p2(2); parameter p8(8); parameter p32(32); family_id bvfid = m.mk_family_id("bv"); sort* bv1 = m.mk_sort(bvfid, BV_SORT, 1, &p1); sort* bv2 = m.mk_sort(bvfid, BV_SORT, 1, &p2); sort* bv8 = m.mk_sort(bvfid, BV_SORT, 1, &p8); sort* bv32 = m.mk_sort(bvfid, BV_SORT, 1, &p32); er.initialize_array(3, bv8, bv32); er.initialize_array(3, bv1, bv1); er.initialize_array(3, bv1, bv2); er.initialize_array(3, bv2, bv1); er.initialize_array(3, bv2, bv2); er.initialize_array(3, bv8, bv8); er.initialize_array(3, bv32, bv32); er.initialize_basic(20); for (unsigned i = 0; i < num_files; ++i) { expr_ref e(m); er.get_next(m.mk_bool_sort(), e); ast_smt_pp pp(m); pp.set_logic("QF_AUFBV"); std::ostringstream buffer; buffer << "random_bv_" << i << ".smt"; std::cout << buffer.str() << "\n"; std::ofstream file(buffer.str().c_str()); pp.display(file, e.get()); file.close(); } } void tst_expr_rand(char** argv, int argc, int& i) { if (i + 1 < argc) { unsigned num_files = atol(argv[i+1]); i += 1; if (i + 1 < argc && 0 == strncmp(argv[i+1],"/rs:",3)) { rand_seed = atol(argv[i+1]+4); std::cout << "random seed:" << rand_seed << "\n"; i += 1; } if (i + 1 < argc && 0 == strcmp(argv[i+1],"/arith")) { tst_expr_arith(num_files); return; } tst_expr_rand(num_files); } } z3-z3-4.4.1/src/test/expr_substitution.cpp000066400000000000000000000030111260446376700205040ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "expr_substitution.h" #include "smt_params.h" #include "substitution.h" #include "unifier.h" #include "bv_decl_plugin.h" #include "ast_pp.h" #include "arith_decl_plugin.h" #include "reg_decl_plugins.h" #include "th_rewriter.h" expr* mk_bv_xor(bv_util& bv, expr* a, expr* b) { expr* args[2]; args[0] = a; args[1] = b; return bv.mk_bv_xor(2, args); } expr* mk_bv_and(bv_util& bv, expr* a, expr* b) { expr* args[2]; args[0] = a; args[1] = b; ast_manager& m = bv.get_manager(); return m.mk_app(bv.get_family_id(), OP_BAND, 2, args); } void tst_expr_substitution() { memory::initialize(0); ast_manager m; reg_decl_plugins(m); bv_util bv(m); expr_ref a(m), b(m), c(m), d(m); expr_ref x(m); expr_ref new_a(m); proof_ref pr(m); x = m.mk_const(symbol("x"), bv.mk_sort(8)); a = mk_bv_and(bv, mk_bv_xor(bv, x,bv.mk_numeral(8,8)), mk_bv_xor(bv,x,x)); b = x; c = bv.mk_bv_sub(x, bv.mk_numeral(4, 8)); expr_substitution subst(m); th_rewriter rw(m); std::cout << mk_pp(c, m) << "\n"; // normalizing c does not help. rw(c, d, pr); std::cout << mk_pp(d, m) << "\n"; // This portion diverges. It attempts to replace x by (bvadd #xfc x), which contains x. // subst.insert(b, d); // std::cout << mk_pp(a, m) << "\n"; // rw.set_substitution(&subst); // enable_trace("th_rewriter_step"); // rw(a, new_a, pr); // std::cout << mk_pp(new_a, m) << "\n"; } z3-z3-4.4.1/src/test/ext_numeral.cpp000066400000000000000000000437421260446376700172340ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ext_numeral.cpp Abstract: Unit tests for ext_numeral template. Author: Leonardo (leonardo) 2012-07-18 Notes: --*/ #include #include"mpq.h" #include"ext_numeral.h" #define MK_TST_UNARY(NAME) \ static void tst_ ## NAME(int a, ext_numeral_kind ak, int expected_c, ext_numeral_kind expected_ck) { \ unsynch_mpq_manager m; \ scoped_mpq _a(m); \ m.set(_a, a); \ NAME(m, _a, ak); \ SASSERT(ak == expected_ck); \ if (expected_ck == EN_NUMERAL) { \ scoped_mpq _expected_c(m); \ m.set(_expected_c, expected_c); \ SASSERT(m.eq(_a, _expected_c)); \ } \ } MK_TST_UNARY(neg); MK_TST_UNARY(inv); #define MK_TST_BIN_CORE(FUN_NAME, OP_NAME) \ static void FUN_NAME(int a, ext_numeral_kind ak, int b, ext_numeral_kind bk, int expected_c, ext_numeral_kind expected_ck) { \ unsynch_mpq_manager m; \ scoped_mpq _a(m), _b(m), _c(m); \ m.set(_a, a); \ m.set(_b, b); \ ext_numeral_kind ck; \ OP_NAME(m, _a, ak, _b, bk, _c, ck); \ SASSERT(ck == expected_ck); \ if (expected_ck == EN_NUMERAL) { \ scoped_mpq _expected_c(m); \ m.set(_expected_c, expected_c); \ SASSERT(m.eq(_c, _expected_c)); \ } \ } #define MK_TST_BIN(NAME) MK_TST_BIN_CORE(tst_ ## NAME, NAME) #define MK_TST_COMM_BIN(NAME) \ MK_TST_BIN_CORE(tst_ ## NAME ## _core, NAME) \ static void tst_ ## NAME(int a, ext_numeral_kind ak, int b, ext_numeral_kind bk, int expected_c, ext_numeral_kind expected_ck) { \ tst_ ## NAME ## _core(a, ak, b, bk, expected_c, expected_ck); \ tst_ ## NAME ## _core(b, bk, a, ak, expected_c, expected_ck); \ } MK_TST_COMM_BIN(add); MK_TST_BIN(sub); MK_TST_COMM_BIN(mul); static void tst1() { tst_neg(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY); tst_neg(30, EN_MINUS_INFINITY, 10, EN_PLUS_INFINITY); tst_neg(0, EN_NUMERAL, 0, EN_NUMERAL); tst_neg(10, EN_NUMERAL, -10, EN_NUMERAL); tst_neg(-7, EN_NUMERAL, 7, EN_NUMERAL); tst_neg(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_neg(30, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_neg(-7, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_inv(0, EN_MINUS_INFINITY, 0, EN_NUMERAL); tst_inv(0, EN_PLUS_INFINITY, 0, EN_NUMERAL); tst_inv(1, EN_NUMERAL, 1, EN_NUMERAL); tst_inv(-1, EN_NUMERAL, -1, EN_NUMERAL); tst_add(0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY); tst_add(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_add(0, EN_MINUS_INFINITY, -1, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_add(0, EN_MINUS_INFINITY, 1, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_add(1, EN_MINUS_INFINITY, -1, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_add(1, EN_NUMERAL, 0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY); tst_add(-1, EN_NUMERAL, 0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY); tst_add(0, EN_NUMERAL, 0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY); tst_add(0, EN_NUMERAL, 2, EN_NUMERAL, 2, EN_NUMERAL); tst_add(-3, EN_NUMERAL, 4, EN_NUMERAL, 1, EN_NUMERAL); tst_add(-2, EN_NUMERAL, 0, EN_NUMERAL, -2, EN_NUMERAL); tst_add(3, EN_NUMERAL, 4, EN_NUMERAL, 7, EN_NUMERAL); tst_add(0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY); tst_add(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_add(0, EN_PLUS_INFINITY, 1, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_add(0, EN_PLUS_INFINITY, -1, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_add(0, EN_NUMERAL, 0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY); tst_add(-1, EN_NUMERAL, 0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY); tst_add(1, EN_NUMERAL, 0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, 0, EN_NUMERAL); tst_mul(0, EN_MINUS_INFINITY, 1, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, 5, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, -1, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, -5, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, 0, EN_NUMERAL); tst_mul(0, EN_PLUS_INFINITY, 1, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, 5, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, -1, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, -5, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_mul(0, EN_NUMERAL, 3, EN_NUMERAL, 0, EN_NUMERAL); tst_mul(2, EN_NUMERAL, 3, EN_NUMERAL, 6, EN_NUMERAL); tst_mul(-2, EN_NUMERAL, 3, EN_NUMERAL, -6, EN_NUMERAL); tst_sub(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, -10, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, 10, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, -10, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 10, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 3, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, -3, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, 3, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, -3, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_NUMERAL, 2, EN_NUMERAL, -2, EN_NUMERAL); tst_sub(3, EN_NUMERAL, 2, EN_NUMERAL, 1, EN_NUMERAL); tst_sub(3, EN_NUMERAL, -3, EN_NUMERAL, 6, EN_NUMERAL); tst_sub(3, EN_NUMERAL, 3, EN_NUMERAL, 0, EN_NUMERAL); tst_sub(3, EN_NUMERAL, 0, EN_NUMERAL, 3, EN_NUMERAL); tst_sub(-3, EN_NUMERAL, -5, EN_NUMERAL, 2, EN_NUMERAL); } #define MK_TST_REL_CORE(FUN_NAME, OP_NAME) \ static void FUN_NAME(int a, ext_numeral_kind ak, int b, ext_numeral_kind bk, bool expected) { \ unsynch_mpq_manager m; \ scoped_mpq _a(m), _b(m); \ m.set(_a, a); \ m.set(_b, b); \ bool r = OP_NAME(m, _a, ak, _b, bk); \ SASSERT(r == expected); \ } #define MK_TST_REL(NAME) MK_TST_REL_CORE(tst_ ## NAME, NAME) #define MK_TST_SYMM_REL(NAME) \ MK_TST_REL_CORE(tst_ ## NAME ## _core, NAME) \ static void tst_ ## NAME(int a, ext_numeral_kind ak, int b, ext_numeral_kind bk, bool expected) { \ tst_ ## NAME ## _core(a, ak, b, bk, expected); \ tst_ ## NAME ## _core(b, bk, a, ak, expected); \ } MK_TST_SYMM_REL(eq); MK_TST_SYMM_REL(neq); MK_TST_REL(lt); MK_TST_REL(gt); MK_TST_REL(le); MK_TST_REL(ge); static void tst2() { tst_eq(0, EN_NUMERAL, 0, EN_NUMERAL, true); tst_eq(0, EN_NUMERAL, 2, EN_NUMERAL, false); tst_eq(3, EN_NUMERAL, 0, EN_NUMERAL, false); tst_eq(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, false); tst_eq(0, EN_PLUS_INFINITY, 3, EN_NUMERAL, false); tst_eq(0, EN_PLUS_INFINITY, -2, EN_NUMERAL, false); tst_eq(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, false); tst_neq(0, EN_NUMERAL, 0, EN_NUMERAL, false); tst_neq(0, EN_NUMERAL, 2, EN_NUMERAL, true); tst_neq(3, EN_NUMERAL, 0, EN_NUMERAL, true); tst_neq(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, true); tst_neq(0, EN_PLUS_INFINITY, 3, EN_NUMERAL, true); tst_neq(0, EN_PLUS_INFINITY, -2, EN_NUMERAL, true); tst_neq(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, true); tst_lt(0, EN_MINUS_INFINITY, 10, EN_NUMERAL, true); tst_lt(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, true); tst_lt(0, EN_MINUS_INFINITY, -3, EN_NUMERAL, true); tst_lt(30, EN_MINUS_INFINITY, 10, EN_NUMERAL, true); tst_lt(20, EN_MINUS_INFINITY, 0, EN_NUMERAL, true); tst_lt(-20, EN_MINUS_INFINITY, -3, EN_NUMERAL, true); tst_lt(0, EN_MINUS_INFINITY, 10, EN_PLUS_INFINITY, true); tst_lt(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, true); tst_lt(10, EN_MINUS_INFINITY, -30, EN_PLUS_INFINITY, true); tst_lt(0, EN_PLUS_INFINITY, 10, EN_NUMERAL, false); tst_lt(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, false); tst_lt(0, EN_PLUS_INFINITY, -3, EN_NUMERAL, false); tst_lt(30, EN_PLUS_INFINITY, 10, EN_NUMERAL, false); tst_lt(20, EN_PLUS_INFINITY, 0, EN_NUMERAL, false); tst_lt(-20, EN_PLUS_INFINITY, -3, EN_NUMERAL, false); tst_lt(0, EN_PLUS_INFINITY, 10, EN_MINUS_INFINITY, false); tst_lt(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, false); tst_lt(10, EN_PLUS_INFINITY, -30, EN_MINUS_INFINITY, false); tst_lt(0, EN_NUMERAL, 0, EN_PLUS_INFINITY, true); tst_lt(20, EN_NUMERAL, 10, EN_PLUS_INFINITY, true); tst_lt(-20, EN_NUMERAL, -100, EN_PLUS_INFINITY, true); tst_lt(0, EN_NUMERAL, 10, EN_NUMERAL, true); tst_lt(0, EN_NUMERAL, 0, EN_NUMERAL, false); tst_lt(10, EN_NUMERAL, 10, EN_NUMERAL, false); tst_lt(0, EN_NUMERAL, -3, EN_NUMERAL, false); tst_lt(30, EN_NUMERAL, 10, EN_NUMERAL, false); tst_lt(30, EN_NUMERAL, 40, EN_NUMERAL, true); tst_lt(20, EN_NUMERAL, 0, EN_NUMERAL, false); tst_lt(-20, EN_NUMERAL, -3, EN_NUMERAL, true); tst_lt(0, EN_NUMERAL, 10, EN_MINUS_INFINITY, false); tst_lt(0, EN_NUMERAL, 0, EN_MINUS_INFINITY, false); tst_lt(10, EN_NUMERAL, -30, EN_MINUS_INFINITY, false); tst_le(0, EN_MINUS_INFINITY, 10, EN_NUMERAL, true); tst_le(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, true); tst_le(0, EN_MINUS_INFINITY, -3, EN_NUMERAL, true); tst_le(30, EN_MINUS_INFINITY, 10, EN_NUMERAL, true); tst_le(20, EN_MINUS_INFINITY, 0, EN_NUMERAL, true); tst_le(-20, EN_MINUS_INFINITY, -3, EN_NUMERAL, true); tst_le(0, EN_MINUS_INFINITY, 10, EN_PLUS_INFINITY, true); tst_le(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, true); tst_le(10, EN_MINUS_INFINITY, -30, EN_PLUS_INFINITY, true); tst_le(0, EN_PLUS_INFINITY, 10, EN_NUMERAL, false); tst_le(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, false); tst_le(0, EN_PLUS_INFINITY, -3, EN_NUMERAL, false); tst_le(30, EN_PLUS_INFINITY, 10, EN_NUMERAL, false); tst_le(20, EN_PLUS_INFINITY, 0, EN_NUMERAL, false); tst_le(-20, EN_PLUS_INFINITY, -3, EN_NUMERAL, false); tst_le(0, EN_PLUS_INFINITY, 10, EN_MINUS_INFINITY, false); tst_le(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, false); tst_le(10, EN_PLUS_INFINITY, -30, EN_MINUS_INFINITY, false); tst_le(0, EN_NUMERAL, 0, EN_PLUS_INFINITY, true); tst_le(20, EN_NUMERAL, 10, EN_PLUS_INFINITY, true); tst_le(-20, EN_NUMERAL, -100, EN_PLUS_INFINITY, true); tst_le(0, EN_NUMERAL, 10, EN_NUMERAL, true); tst_le(0, EN_NUMERAL, 0, EN_NUMERAL, true); tst_le(10, EN_NUMERAL, 10, EN_NUMERAL, true); tst_le(0, EN_NUMERAL, -3, EN_NUMERAL, false); tst_le(30, EN_NUMERAL, 10, EN_NUMERAL, false); tst_le(30, EN_NUMERAL, 40, EN_NUMERAL, true); tst_le(20, EN_NUMERAL, 0, EN_NUMERAL, false); tst_le(-20, EN_NUMERAL, -3, EN_NUMERAL, true); tst_le(0, EN_NUMERAL, 10, EN_MINUS_INFINITY, false); tst_le(0, EN_NUMERAL, 0, EN_MINUS_INFINITY, false); tst_le(10, EN_NUMERAL, -30, EN_MINUS_INFINITY, false); tst_ge(0, EN_MINUS_INFINITY, 10, EN_NUMERAL, false); tst_ge(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, false); tst_ge(0, EN_MINUS_INFINITY, -3, EN_NUMERAL, false); tst_ge(30, EN_MINUS_INFINITY, 10, EN_NUMERAL, false); tst_ge(20, EN_MINUS_INFINITY, 0, EN_NUMERAL, false); tst_ge(-20, EN_MINUS_INFINITY, -3, EN_NUMERAL, false); tst_ge(0, EN_MINUS_INFINITY, 10, EN_PLUS_INFINITY, false); tst_ge(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, false); tst_ge(10, EN_MINUS_INFINITY, -30, EN_PLUS_INFINITY, false); tst_ge(0, EN_PLUS_INFINITY, 10, EN_NUMERAL, true); tst_ge(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, true); tst_ge(0, EN_PLUS_INFINITY, -3, EN_NUMERAL, true); tst_ge(30, EN_PLUS_INFINITY, 10, EN_NUMERAL, true); tst_ge(20, EN_PLUS_INFINITY, 0, EN_NUMERAL, true); tst_ge(-20, EN_PLUS_INFINITY, -3, EN_NUMERAL, true); tst_ge(0, EN_PLUS_INFINITY, 10, EN_MINUS_INFINITY, true); tst_ge(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, true); tst_ge(10, EN_PLUS_INFINITY, -30, EN_MINUS_INFINITY, true); tst_ge(0, EN_NUMERAL, 0, EN_PLUS_INFINITY, false); tst_ge(20, EN_NUMERAL, 10, EN_PLUS_INFINITY, false); tst_ge(-20, EN_NUMERAL, -100, EN_PLUS_INFINITY, false); tst_ge(0, EN_NUMERAL, 10, EN_NUMERAL, false); tst_ge(0, EN_NUMERAL, 0, EN_NUMERAL, true); tst_ge(10, EN_NUMERAL, 10, EN_NUMERAL, true); tst_ge(0, EN_NUMERAL, -3, EN_NUMERAL, true); tst_ge(30, EN_NUMERAL, 10, EN_NUMERAL, true); tst_ge(30, EN_NUMERAL, 40, EN_NUMERAL, false); tst_ge(20, EN_NUMERAL, 0, EN_NUMERAL, true); tst_ge(-20, EN_NUMERAL, -3, EN_NUMERAL, false); tst_ge(0, EN_NUMERAL, 10, EN_MINUS_INFINITY, true); tst_ge(0, EN_NUMERAL, 0, EN_MINUS_INFINITY, true); tst_ge(10, EN_NUMERAL, -30, EN_MINUS_INFINITY, true); tst_gt(0, EN_MINUS_INFINITY, 10, EN_NUMERAL, false); tst_gt(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, false); tst_gt(0, EN_MINUS_INFINITY, -3, EN_NUMERAL, false); tst_gt(30, EN_MINUS_INFINITY, 10, EN_NUMERAL, false); tst_gt(20, EN_MINUS_INFINITY, 0, EN_NUMERAL, false); tst_gt(-20, EN_MINUS_INFINITY, -3, EN_NUMERAL, false); tst_gt(0, EN_MINUS_INFINITY, 10, EN_PLUS_INFINITY, false); tst_gt(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, false); tst_gt(10, EN_MINUS_INFINITY, -30, EN_PLUS_INFINITY, false); tst_gt(0, EN_PLUS_INFINITY, 10, EN_NUMERAL, true); tst_gt(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, true); tst_gt(0, EN_PLUS_INFINITY, -3, EN_NUMERAL, true); tst_gt(30, EN_PLUS_INFINITY, 10, EN_NUMERAL, true); tst_gt(20, EN_PLUS_INFINITY, 0, EN_NUMERAL, true); tst_gt(-20, EN_PLUS_INFINITY, -3, EN_NUMERAL, true); tst_gt(0, EN_PLUS_INFINITY, 10, EN_MINUS_INFINITY, true); tst_gt(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, true); tst_gt(10, EN_PLUS_INFINITY, -30, EN_MINUS_INFINITY, true); tst_gt(0, EN_NUMERAL, 0, EN_PLUS_INFINITY, false); tst_gt(20, EN_NUMERAL, 10, EN_PLUS_INFINITY, false); tst_gt(-20, EN_NUMERAL, -100, EN_PLUS_INFINITY, false); tst_gt(0, EN_NUMERAL, 10, EN_NUMERAL, false); tst_gt(0, EN_NUMERAL, 0, EN_NUMERAL, false); tst_gt(10, EN_NUMERAL, 10, EN_NUMERAL, false); tst_gt(0, EN_NUMERAL, -3, EN_NUMERAL, true); tst_gt(30, EN_NUMERAL, 10, EN_NUMERAL, true); tst_gt(30, EN_NUMERAL, 40, EN_NUMERAL, false); tst_gt(20, EN_NUMERAL, 0, EN_NUMERAL, true); tst_gt(-20, EN_NUMERAL, -3, EN_NUMERAL, false); tst_gt(0, EN_NUMERAL, 10, EN_MINUS_INFINITY, true); tst_gt(0, EN_NUMERAL, 0, EN_MINUS_INFINITY, true); tst_gt(10, EN_NUMERAL, -30, EN_MINUS_INFINITY, true); } static void tst3() { unsynch_mpq_manager m; scoped_mpq a(m); SASSERT(is_zero(m, a, EN_NUMERAL)); SASSERT(!is_zero(m, a, EN_PLUS_INFINITY)); SASSERT(!is_zero(m, a, EN_MINUS_INFINITY)); SASSERT(!is_pos(m, a, EN_NUMERAL)); SASSERT(is_pos(m, a, EN_PLUS_INFINITY)); SASSERT(!is_pos(m, a, EN_MINUS_INFINITY)); SASSERT(!is_infinite(EN_NUMERAL)); SASSERT(is_infinite(EN_PLUS_INFINITY)); SASSERT(is_infinite(EN_MINUS_INFINITY)); SASSERT(!is_neg(m, a, EN_NUMERAL)); SASSERT(!is_neg(m, a, EN_PLUS_INFINITY)); SASSERT(is_neg(m, a, EN_MINUS_INFINITY)); m.set(a, 10); SASSERT(!is_zero(m, a, EN_NUMERAL)); SASSERT(is_pos(m, a, EN_NUMERAL)); SASSERT(!is_neg(m, a, EN_NUMERAL)); SASSERT(!is_infinite(EN_NUMERAL)); m.set(a, -5); SASSERT(!is_zero(m, a, EN_NUMERAL)); SASSERT(!is_pos(m, a, EN_NUMERAL)); SASSERT(is_neg(m, a, EN_NUMERAL)); SASSERT(!is_infinite(EN_NUMERAL)); ext_numeral_kind ak; ak = EN_MINUS_INFINITY; reset(m, a, ak); SASSERT(is_zero(m, a, EN_NUMERAL)); { std::ostringstream buffer; display(buffer, m, a, ak); SASSERT(buffer.str() == "0"); } { std::ostringstream buffer; m.set(a, -10); display(buffer, m, a, ak); SASSERT(buffer.str() == "-10"); } { std::ostringstream buffer; display(buffer, m, a, EN_PLUS_INFINITY); SASSERT(buffer.str() == "+oo"); } { std::ostringstream buffer; display(buffer, m, a, EN_MINUS_INFINITY); SASSERT(buffer.str() == "-oo"); } } void tst_ext_numeral() { tst1(); tst2(); tst3(); } z3-z3-4.4.1/src/test/f2n.cpp000066400000000000000000000030151260446376700153630ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: f2n.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-08-17. Revision History: --*/ #include"f2n.h" #include"hwf.h" #include"mpf.h" static void tst1() { hwf_manager hm; f2n m(hm); hwf a, b; m.set(a, 11, 3); m.floor(a, b); std::cout << "floor(11/3): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(11/3): " << m.to_double(b) << "\n"; m.set(a, -11, 3); m.floor(a, b); std::cout << "floor(-11/3): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(-11/3): " << m.to_double(b) << "\n"; m.set(a, 11, 1); m.floor(a, b); std::cout << "floor(11): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(11): " << m.to_double(b) << "\n"; } static void tst2() { std::cout << "using mpf...\n"; mpf_manager fm; f2n m(fm); scoped_mpf a(fm), b(fm); m.set(a, 11, 3); m.floor(a, b); std::cout << "floor(11/3): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(11/3): " << m.to_double(b) << "\n"; m.set(a, -11, 3); m.floor(a, b); std::cout << "floor(-11/3): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(-11/3): " << m.to_double(b) << "\n"; m.set(a, 11, 1); m.floor(a, b); std::cout << "floor(11): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(11): " << m.to_double(b) << "\n"; } void tst_f2n() { tst1(); tst2(); } z3-z3-4.4.1/src/test/factor_rewriter.cpp000066400000000000000000000011661260446376700201040ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "factor_rewriter.h" #include "bv_decl_plugin.h" #include "ast_pp.h" #include "reg_decl_plugins.h" void tst_factor_rewriter() { ast_manager m; reg_decl_plugins(m); factor_rewriter_star fw(m); arith_util a(m); expr_ref fml1(m), fml2(m); expr_ref z(m.mk_const(symbol("z"), a.mk_real()), m); expr_ref two(a.mk_numeral(rational(2),false),m); expr_ref zero(a.mk_numeral(rational(0),false),m); fml1 = a.mk_le(zero, a.mk_mul(two, z, z)); fw(fml1, fml2); std::cout << mk_pp(fml1, m) << " -> " << mk_pp(fml2, m) << "\n"; } z3-z3-4.4.1/src/test/fixed_bit_vector.cpp000066400000000000000000000045021260446376700202170ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: fixed_bit_vector.cpp Abstract: Test fixed-size bit vector module Author: Nikolaj Bjorner (nbjorner) 2014-9-15. Revision History: based on bit_vector.cpp --*/ #include #include #include"fixed_bit_vector.h" #include"vector.h" static void tst1() { fixed_bit_vector_manager m(30); fixed_bit_vector *b; b = m.allocate0(); m.set(*b, 0, true); m.set(*b, 1, false); m.set(*b, 2, true); SASSERT(b->get(0) == true); SASSERT(b->get(1) == false); SASSERT(b->get(2) == true); SASSERT(b->get(3) == false); SASSERT(b->get(29) == false); m.deallocate(b); } static void tst_or() { { fixed_bit_vector_manager m(10); fixed_bit_vector *b1, *b2; b1 = m.allocate0(); b2 = m.allocate0(); m.set(*b1, 4); m.set(*b2, 8); m.set(*b2, 3); m.set(*b2, 2); m.set(*b2, 1); m.display(std::cout, *b1) << "\n"; m.display(std::cout, *b2) << "\n"; m.set_or(*b1, *b2); m.display(std::cout, *b1) << "\n"; SASSERT(!m.equals(*b1, *b2)); m.unset(*b1, 4); SASSERT(m.equals(*b1, *b2)); m.unset(*b1, 3); SASSERT(!m.equals(*b1, *b2)); m.deallocate(b1); m.deallocate(b2); } } static void tst_and() { } static void tst_eq(unsigned num_bits) { fixed_bit_vector_manager m(num_bits); fixed_bit_vector* b1 = m.allocate0(); fixed_bit_vector* b2 = m.allocate0(); fixed_bit_vector* b3 = m.allocate0(); m.set(*b1, 3, true); SASSERT(!m.equals(*b1, *b2)); SASSERT(m.equals(*b2, *b3)); m.set(*b3, 3, true); SASSERT(m.equals(*b1, *b3)); m.set(*b2, num_bits-1, true); m.set(*b3, num_bits-1); m.unset(*b3, 3); SASSERT(m.equals(*b2, *b3)); m.fill0(*b1); m.set_neg(*b1); m.fill1(*b2); SASSERT(m.equals(*b1, *b2)); m.fill0(*b1); for (unsigned i = 0; i < num_bits; ++i) { m.set(*b1, i, true); } SASSERT(m.equals(*b1, *b2)); m.deallocate(b1); m.deallocate(b2); m.deallocate(b3); } void tst_fixed_bit_vector() { tst1(); tst_or(); tst_and(); tst_eq(15); tst_eq(16); tst_eq(17); tst_eq(31); tst_eq(32); tst_eq(33); tst_eq(63); tst_eq(64); tst_eq(65); } z3-z3-4.4.1/src/test/for_each_file.cpp000066400000000000000000000030521260446376700174440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: foreach_file.cpp Abstract: Traverse files in a directory that match a given suffix. Apply a method to each of the files. Author: Nikolaj Bjorner (nbjorner) 2006-11-3. Revision History: --*/ #ifdef _WINDOWS #include #include #include #include "for_each_file.h" bool for_each_file(for_each_file_proc& proc, const char* base, const char* suffix) { std::string pattern(base); pattern += "\\"; pattern += suffix; char buffer[MAX_PATH]; WIN32_FIND_DATAA data; HANDLE h = FindFirstFileA(pattern.c_str(),&data); while (h != INVALID_HANDLE_VALUE) { StringCchPrintfA(buffer, ARRAYSIZE(buffer), "%s\\%s", base, data.cFileName); if (!proc(buffer)) { return false; } if (!FindNextFileA(h,&data)) { break; } } // // Now recurse through sub-directories. // pattern = base; pattern += "\\*"; h = FindFirstFileA(pattern.c_str(),&data); while (h != INVALID_HANDLE_VALUE) { if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && data.cFileName[0] != '.' ){ std::string subdir(base); subdir += "\\"; subdir += data.cFileName; if (!for_each_file(proc, subdir.c_str(), suffix)) { return false; } } if (!FindNextFileA(h,&data)) { break; } } return true; }; #endif z3-z3-4.4.1/src/test/for_each_file.h000066400000000000000000000010411260446376700171050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: foreach_file.h Abstract: Traverse files in a directory that match a given suffix. Apply a method to each of the files. Author: Nikolaj Bjorner (nbjorner) 2006-11-3. Revision History: --*/ #pragma once #ifndef FOR_EACH_FILE_H_ #define FOR_EACH_FILE_H_ struct for_each_file_proc { virtual bool operator()(const char* file_path) = 0; }; bool for_each_file(for_each_file_proc& proc, const char* base, const char* suffix); #endif /* FOR_EACH_FILE_H_ */ z3-z3-4.4.1/src/test/fuzzing/000077500000000000000000000000001260446376700156675ustar00rootroot00000000000000z3-z3-4.4.1/src/test/fuzzing/expr_delta.cpp000066400000000000000000000040211260446376700205170ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "expr_delta.h" #include "ast_pp.h" expr_delta::expr_delta(ast_manager& m) : m_manager(m), m_exprs(m) {} void expr_delta::assert_cnstr(expr* n) { m_exprs.push_back(n); } bool expr_delta::delta_dfs(unsigned n, expr_ref_vector& result) { return delta_dfs(n, m_exprs.size(), m_exprs.c_ptr(), result); } bool expr_delta::delta_dfs(unsigned& n, unsigned sz, expr* const* exprs, expr_ref_vector& result) { expr_ref r(m_manager); for (unsigned i = 0; i < sz; ++i) { expr* e = exprs[i]; if (delta_dfs(n, e, r)) { result.push_back(r.get()); for (unsigned j = i+1; j < sz; ++j) { result.push_back(exprs[j]); } return true; } else { result.push_back(e); } } return false; } bool expr_delta::delta_dfs(unsigned& n, app* a, expr_ref& result) { expr_ref_vector args(m_manager); if (delta_dfs(n, a->get_num_args(), a->get_args(), args)) { result = m_manager.mk_app(a->get_decl(), args.size(), args.c_ptr()); return true; } else { return false; } } bool expr_delta::delta_dfs(unsigned& n, expr* e, expr_ref& result) { ast_manager& m = m_manager; if (m.is_true(e) || m.is_false(e)) { return false; } if (n == 0 && m.is_bool(e)) { result = m.mk_true(); return true; } else if (n == 1 && m.is_bool(e)) { result = m.mk_false(); return true; } else if (is_app(e)) { if (m.is_bool(e)) { SASSERT(n >= 2); n -= 2; } return delta_dfs(n, to_app(e), result); } else if (is_quantifier(e)) { SASSERT(n >= 2); n -= 2; quantifier* q = to_quantifier(e); if (delta_dfs(n, q->get_expr(), result)) { result = m.update_quantifier(q, result.get()); return true; } else { return false; } } return false; } z3-z3-4.4.1/src/test/fuzzing/expr_delta.h000066400000000000000000000016461260446376700201760ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: expr_delta.h Abstract: Delta debugging support for specifications. A specification is a list of assumptions. Author: Nikolaj Bjorner (nbjorner) 2008-21-06 Revision History: --*/ #ifndef EXPR_DELTA_H_ #define EXPR_DELTA_H_ #include "ast.h" class expr_delta { ast_manager& m_manager; expr_ref_vector m_exprs; public: expr_delta(ast_manager& m); // Assert a constraint. void assert_cnstr(expr* e); // // Create the n'th delta in dfs mode. // resturn 'true' if a delta was obtained. // bool delta_dfs(unsigned n, expr_ref_vector& result); private: // perform delta bool delta_dfs(unsigned& n, expr* e, expr_ref& result); bool delta_dfs(unsigned& n, app* a, expr_ref& result); bool delta_dfs(unsigned& n, unsigned sz, expr* const* exprs, expr_ref_vector& result); }; #endif z3-z3-4.4.1/src/test/fuzzing/expr_rand.cpp000066400000000000000000000247671260446376700203750ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "expr_rand.h" #include "bv_decl_plugin.h" #include "array_decl_plugin.h" #include "arith_decl_plugin.h" #include "ast_pp.h" expr_rand::expr_rand(ast_manager& m): m_manager(m), m_num_vars(0), m_num_apps(0), m_num_nodes(0), m_max_steps(10), m_funcs(m) {} expr_rand::~expr_rand() { map_t::iterator it = m_nodes.begin(); map_t::iterator end = m_nodes.end(); for (; it != end; ++it) { dealloc(it->m_value); } } void expr_rand::add_var(sort* s) { add_expr(m_manager.mk_fresh_const("x", s)); } void expr_rand::add_func_decl(func_decl* f) { m_funcs.push_back(f); } void expr_rand::add_expr(expr* t) { sort* s = m_manager.get_sort(t); expr_ref_vector* vals = 0; if (!m_nodes.find(s, vals)) { vals = alloc(expr_ref_vector, m_manager); m_nodes.insert(s, vals); } vals->push_back(t); } void expr_rand::get_next(sort* s, expr_ref& e) { walk(m_max_steps); e = choose_expr(s); } void expr_rand::walk() { func_decl* f = choose_func_decl(); unsigned arity = f->get_arity(); expr_ref_vector args(m_manager); for (unsigned i = 0; i < arity; ++i) { args.push_back(choose_expr(f->get_domain(i))); } expr* r = m_manager.mk_app(f, args.size(), args.c_ptr()); add_expr(r); } void expr_rand::walk(unsigned n) { for (unsigned i = 0; i < n; ++i) { walk(); } } func_decl* expr_rand::choose_func_decl() { unsigned idx = m_random(m_funcs.size()); return m_funcs[idx].get(); } expr* expr_rand::choose_expr(sort* s) { expr_ref_vector* vals = 0; if (!m_nodes.find(s, vals)) { add_var(s); if (!m_nodes.find(s, vals)) { UNREACHABLE(); } SASSERT(vals); } unsigned idx = m_random(vals->size()); return (*vals)[idx].get(); } void expr_rand::initialize_arith(unsigned num_vars) { arith_util u(m_manager); family_id afid = m_manager.mk_family_id("arith"); sort* i_ty = m_manager.mk_sort(afid, INT_SORT, 0, 0); for(unsigned i = 0; i < num_vars; ++i) { add_var(i_ty); } sort* is[2] = { i_ty, i_ty }; decl_kind kinds[7] = {OP_ADD, OP_MUL, OP_SUB, OP_LE, OP_LT, OP_GE, OP_GT }; for (unsigned i = 0; i < 7; ++i) { add_func_decl(m_manager.mk_func_decl(afid, kinds[i], 0, 0, 2, is)); } add_expr(u.mk_numeral(rational(0), true)); add_expr(u.mk_numeral(rational(1), true)); add_expr(u.mk_numeral(rational(2), true)); add_expr(u.mk_numeral(rational(3), true)); add_expr(u.mk_numeral(rational(6), true)); add_expr(u.mk_numeral(rational(7), true)); add_expr(u.mk_numeral(rational(-1), true)); add_expr(u.mk_numeral(rational(-2), true)); } void expr_rand::initialize_bv(unsigned num_vars) { bv_util u(m_manager); family_id bfid = m_manager.get_basic_family_id(); family_id bvfid = m_manager.mk_family_id("bv"); const unsigned num_sizes = 6; unsigned sizes[num_sizes] = { 1, 2, 8, 16, 24, 32 }; parameter p1(1), p2(2), p3(3), p4(4), p8(8), p16(16), p24(24), p32(32); for (unsigned i = 0; i < num_sizes; ++i) { add_expr(u.mk_numeral(rational(0), sizes[i])); add_expr(u.mk_numeral(rational(1), sizes[i])); } add_expr(u.mk_numeral(rational(2), 2)); add_expr(u.mk_numeral(rational(3), 2)); add_expr(u.mk_numeral(rational(6), 8)); add_expr(u.mk_numeral(rational(7), 8)); add_expr(u.mk_numeral(rational(static_cast(-2)), 32)); add_expr(u.mk_numeral(rational(static_cast(-1)), 32)); for (unsigned i = 0; num_vars > 0; ++i, --num_vars) { i = i % num_sizes; parameter param(sizes[i]); add_var(m_manager.mk_sort(bvfid, BV_SORT, 1, ¶m)); } for (unsigned i = 0; i < num_sizes; ++i) { parameter param(sizes[i]); sort* s = m_manager.mk_sort(bvfid, BV_SORT, 1, ¶m); sort* ss[3] = { s, s, s }; add_func_decl(m_manager.mk_func_decl(bvfid, OP_BNEG, 0, 0, 1, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BADD, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BSUB, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BMUL, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BSDIV, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BUDIV, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BSREM, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BUREM, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BSMOD, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ULEQ, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SLEQ, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_UGEQ, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SGEQ, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ULT, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SLT, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_UGT, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SGT, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BAND, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BOR, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BNOT, 0, 0, 1, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BXOR, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BXNOR, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BNAND, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BCOMP, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BREDAND, 0, 0, 1, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BREDOR, 0, 0, 1, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BSHL, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BLSHR, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BASHR, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bfid, OP_EQ, 0, 0, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ROTATE_LEFT, 1, &p1, 1, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ROTATE_RIGHT, 1, &p1, 1, ss)); } sort* b8 = m_manager.mk_sort(bvfid, BV_SORT, 1, &p8); sort* b16 = m_manager.mk_sort(bvfid, BV_SORT, 1, &p16); sort* b24 = m_manager.mk_sort(bvfid, BV_SORT, 1, &p24); sort* b32 = m_manager.mk_sort(bvfid, BV_SORT, 1, &p32); // OP_CONCAT: { sort* ss[2] = { b8, b8 }; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, 0, 2, ss)); ss[0] = b16; ss[1] = b8; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, 0, 2, ss)); ss[0] = b8; ss[1] = b16; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, 0, 2, ss)); ss[0] = b16; ss[1] = b16; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, 0, 2, ss)); ss[0] = b24; ss[1] = b8; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, 0, 2, ss)); ss[0] = b8; ss[1] = b24; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, 0, 2, ss)); } add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p8, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p8, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p16, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p16, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p8, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p16, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p8, 1, &b24)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p8, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p8, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p16, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p16, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p8, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p16, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p8, 1, &b24)); parameter bounds[2] = { parameter(7), parameter(0) }; add_func_decl(m_manager.mk_func_decl(bvfid, OP_EXTRACT, 2, bounds, 1, &b32)); bounds[0] = parameter(15); add_func_decl(m_manager.mk_func_decl(bvfid, OP_EXTRACT, 2, bounds, 1, &b32)); bounds[0] = parameter(23); add_func_decl(m_manager.mk_func_decl(bvfid, OP_EXTRACT, 2, bounds, 1, &b32)); bounds[1] = parameter(8); add_func_decl(m_manager.mk_func_decl(bvfid, OP_EXTRACT, 2, bounds, 1, &b32)); bounds[1] = parameter(16); add_func_decl(m_manager.mk_func_decl(bvfid, OP_EXTRACT, 2, bounds, 1, &b32)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_REPEAT, 1, &p4, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_REPEAT, 1, &p3, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_REPEAT, 1, &p2, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_REPEAT, 1, &p1, 1, &b8)); /* OP_ROTATE_LEFT, OP_ROTATE_RIGHT, */ } void expr_rand::initialize_array(unsigned num_vars, sort* dom, sort* rng) { family_id afid = m_manager.mk_family_id("array"); parameter p1(dom), p2(rng); parameter ps[2] = { p1, p2 }; sort* a = m_manager.mk_sort(afid, ARRAY_SORT, 2, ps); sort* ss[3] = { a, dom, rng }; add_func_decl(m_manager.mk_func_decl(afid, OP_STORE, 0, 0, 3, ss)); add_func_decl(m_manager.mk_func_decl(afid, OP_SELECT, 0, 0, 2, ss)); for (unsigned i = 0; i < num_vars; ++i) { add_var(a); } } void expr_rand::initialize_basic(unsigned amplification) { family_id bfid = m_manager.get_basic_family_id(); sort* bools[2] = { m_manager.mk_bool_sort(), m_manager.mk_bool_sort() }; for (unsigned i = 0; i < amplification; ++i) { add_func_decl(m_manager.mk_func_decl(bfid, OP_OR, 0, 0, 2, bools)); add_func_decl(m_manager.mk_func_decl(bfid, OP_NOT, 0, 0, 1, bools)); } map_t::iterator it = m_nodes.begin(); map_t::iterator end = m_nodes.end(); for (; it != end; ++it) { sort* s = it->m_key; sort* ites[3] = { bools[0], s, s }; add_func_decl(m_manager.mk_func_decl(bfid, OP_ITE, 0, 0, 3, ites)); } } z3-z3-4.4.1/src/test/fuzzing/expr_rand.h000066400000000000000000000022651260446376700200270ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: expr_rand.h Abstract: Generator of random ASTs. Author: Nikolaj Bjorner 2008-04-10. Revision History: --*/ #ifndef EXPR_RAND_H_ #define EXPR_RAND_H_ #include"ast.h" #include"obj_hashtable.h" class expr_rand { ast_manager& m_manager; unsigned m_num_vars; unsigned m_num_apps; unsigned m_num_nodes; unsigned m_max_steps; random_gen m_random; typedef obj_map map_t; func_decl_ref_vector m_funcs; map_t m_nodes; public: expr_rand(ast_manager& m); ~expr_rand(); void add_var(sort*); void add_func_decl(func_decl*); void add_expr(expr* t); void get_next(sort* s, expr_ref& e); void initialize_bv(unsigned num_vars); void initialize_arith(unsigned num_vars); void initialize_array(unsigned num_vars, sort* dom, sort* rng); void initialize_basic(unsigned amplification); void seed(unsigned n) { m_random = random_gen(n); } private: void walk(); void walk(unsigned n); func_decl* choose_func_decl(); expr* choose_expr(sort*); }; #endif z3-z3-4.4.1/src/test/get_implied_equalities.cpp000066400000000000000000000103041260446376700214040ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "z3.h" #include "trace.h" #include "debug.h" static Z3_ast mk_var(Z3_context ctx, char const* name, Z3_sort s) { return Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, name), s); } static Z3_ast mk_int_var(Z3_context ctx, char const* name) { return mk_var(ctx, name, Z3_mk_int_sort(ctx)); } static void tst_get_implied_equalities1() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); Z3_sort int_ty = Z3_mk_int_sort(ctx); Z3_ast a = mk_int_var(ctx,"a"); Z3_ast b = mk_int_var(ctx,"b"); Z3_ast c = mk_int_var(ctx,"c"); Z3_ast d = mk_int_var(ctx,"d"); Z3_func_decl f = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx,"f"), 1, &int_ty, int_ty); Z3_ast fa = Z3_mk_app(ctx, f, 1, &a); Z3_ast fb = Z3_mk_app(ctx, f, 1, &b); Z3_ast fc = Z3_mk_app(ctx, f, 1, &c); unsigned const num_terms = 7; unsigned i; Z3_ast terms[7] = { a, b, c, d, fa, fb, fc }; unsigned class_ids[7] = { 0, 0, 0, 0, 0, 0, 0 }; Z3_solver solver = Z3_mk_simple_solver(ctx); Z3_solver_inc_ref(ctx, solver); Z3_solver_assert(ctx, solver, Z3_mk_eq(ctx, a, b)); Z3_solver_assert(ctx, solver, Z3_mk_eq(ctx, b, d)); Z3_solver_assert(ctx, solver, Z3_mk_le(ctx, fa, fc)); Z3_solver_assert(ctx, solver, Z3_mk_le(ctx, fc, d)); Z3_get_implied_equalities(ctx, solver, num_terms, terms, class_ids); for (i = 0; i < num_terms; ++i) { printf("Class %s |-> %d\n", Z3_ast_to_string(ctx, terms[i]), class_ids[i]); } SASSERT(class_ids[1] == class_ids[0]); SASSERT(class_ids[2] != class_ids[0]); SASSERT(class_ids[3] == class_ids[0]); SASSERT(class_ids[4] != class_ids[0]); SASSERT(class_ids[5] != class_ids[0]); SASSERT(class_ids[6] != class_ids[0]); SASSERT(class_ids[4] == class_ids[5]); printf("asserting b <= f(a)\n"); Z3_solver_assert(ctx, solver, Z3_mk_le(ctx, b, fa)); Z3_get_implied_equalities(ctx, solver, num_terms, terms, class_ids); for (i = 0; i < num_terms; ++i) { printf("Class %s |-> %d\n", Z3_ast_to_string(ctx, terms[i]), class_ids[i]); } SASSERT(class_ids[1] == class_ids[0]); SASSERT(class_ids[2] != class_ids[0]); SASSERT(class_ids[3] == class_ids[0]); SASSERT(class_ids[4] == class_ids[0]); SASSERT(class_ids[5] == class_ids[0]); SASSERT(class_ids[6] == class_ids[0]); Z3_solver_dec_ref(ctx, solver); /* delete logical context */ Z3_del_context(ctx); } static void tst_get_implied_equalities2() { enable_trace("after_search"); enable_trace("get_implied_equalities"); enable_trace("implied_equalities"); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); Z3_solver solver = Z3_mk_simple_solver(ctx); Z3_solver_inc_ref(ctx, solver); Z3_sort int_ty = Z3_mk_int_sort(ctx); Z3_ast a = mk_int_var(ctx,"a"); Z3_ast b = mk_int_var(ctx,"b"); Z3_ast one = Z3_mk_numeral(ctx, "1", int_ty); Z3_ast two = Z3_mk_numeral(ctx, "2", int_ty); Z3_ast x = Z3_mk_const_array(ctx, int_ty, one); Z3_ast y = Z3_mk_store(ctx, x, one, a); Z3_ast z = Z3_mk_store(ctx, y, two , b); Z3_ast u = Z3_mk_store(ctx, x, two , b); Z3_ast v = Z3_mk_store(ctx, u, one , a); unsigned const num_terms = 5; unsigned i; Z3_ast terms[5] = { x, y, z, u, v}; unsigned class_ids[5] = { 0, 0, 0, 0, 0}; Z3_get_implied_equalities(ctx, solver, num_terms, terms, class_ids); for (i = 0; i < num_terms; ++i) { printf("Class %s |-> %d\n", Z3_ast_to_string(ctx, terms[i]), class_ids[i]); } SASSERT(class_ids[1] != class_ids[0]); SASSERT(class_ids[2] != class_ids[0]); SASSERT(class_ids[3] != class_ids[0]); SASSERT(class_ids[4] != class_ids[0]); SASSERT(class_ids[4] == class_ids[2]); SASSERT(class_ids[2] != class_ids[1]); SASSERT(class_ids[3] != class_ids[1]); SASSERT(class_ids[4] != class_ids[1]); SASSERT(class_ids[3] != class_ids[2]); /* delete logical context */ Z3_solver_dec_ref(ctx, solver); Z3_del_context(ctx); } void tst_get_implied_equalities() { tst_get_implied_equalities1(); tst_get_implied_equalities2(); } z3-z3-4.4.1/src/test/hashtable.cpp000066400000000000000000000057661260446376700166500ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_hashtable.cpp Abstract: Test hashtable module Author: Leonardo de Moura (leonardo) 2006-09-12. Revision History: --*/ #ifdef _WINDOWS #include #include #include #include"hashtable.h" #ifndef Z3DEBUG #undef SASSERT #define SASSERT(COND) { if (!(COND)) std::cerr << "ERROR: " << #COND << "\n"; } ((void) 0) #endif struct int_hash_proc { unsigned operator()(int x) const { return x * 3; } }; typedef int_hashtable > int_set; typedef stdext::hash_set > > safe_int_set; // typedef safe_int_set int_set; inline bool contains(int_set & h, int i) { // return h.find(i) != h.end(); return h.contains(i); } const int N = 10000; int vals[N]; static void tst1() { int_set h1; int size = 0; for (int i = 1; i < N; i ++) { int v = rand() % (N / 2); h1.insert(v); vals[i] = v; SASSERT(contains(h1, v)); } std::cout << "step1\n"; std::cout.flush(); for (int i = 1; i < N; i ++) { SASSERT(contains(h1, vals[i])); } std::cout << "step2\n"; std::cout.flush(); for (int i = 1; i < N; i += 2) { h1.erase(vals[i]); SASSERT(!contains(h1, vals[i])); } std::cout << "step3\n"; std::cout.flush(); for (int i = 1; i < N; i += 2) { h1.insert(vals[i]); } std::cout << "step4\n"; std::cout.flush(); for (int i = 1; i < N; i ++) { SASSERT(contains(h1, vals[i])); } } static void tst2() { int_set h1; safe_int_set h2; int N = rand() % 1000; for (int i = 0; i < N; i++) { int v = rand()%1000; if (rand() % 3 == 2) { h1.erase(v); h2.erase(v); SASSERT(!contains(h1, v)); } else { h1.insert(v); h2.insert(v); SASSERT(contains(h1, v)); } } { safe_int_set::iterator it = h2.begin(); safe_int_set::iterator end = h2.end(); for(; it != end; ++it) { SASSERT(contains(h1, *it)); } } { int_set::iterator it = h1.begin(); int_set::iterator end = h1.end(); int n = 0; for (; it != end; ++it) { SASSERT(contains(h1, *it)); n++; } SASSERT(n == h1.size()); } SASSERT(h1.size() == h2.size()); // std::cout << "size: " << h1.size() << ", capacity: " << h1.capacity() << "\n"; std::cout.flush(); } static void tst3() { int_set h1; h1.insert(10); h1.insert(20); h1.insert(30); h1.erase(20); int_set h2(h1); SASSERT(h1.contains(10)); SASSERT(!h1.contains(20)); SASSERT(h1.contains(30)); SASSERT(h2.contains(10)); SASSERT(!h2.contains(20)); SASSERT(h2.contains(30)); SASSERT(h2.size() == 2); } void tst_hashtable() { tst3(); for (int i = 0; i < 100; i++) tst2(); tst1(); } #else void tst_hashtable() { } #endif z3-z3-4.4.1/src/test/heap.cpp000066400000000000000000000067111260446376700156210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_heap.cpp Abstract: Test heap template. Author: Leonardo de Moura (leonardo) 2006-09-18. Revision History: --*/ #include #include"heap.h" #include"hashtable.h" #include"trace.h" struct lt_proc { bool operator()(int v1, int v2) const { return v1 < v2; } }; typedef heap int_heap; struct int_hash_proc { unsigned operator()(int v) const { return v * 17; }}; typedef int_hashtable > int_set; #define N 10000 static void tst1() { int_heap h(N); int_set t; for (int i = 0; i < N * 3; i++) { int val = rand() % N; if (!h.contains(val)) { SASSERT(!t.contains(val)); h.insert(val); t.insert(val); } else { SASSERT(t.contains(val)); } } SASSERT(h.check_invariant()); int_set::iterator it = t.begin(); int_set::iterator end = t.end(); for (; it != end; ++it) { SASSERT(h.contains(*it)); } int last = -1; while (!h.empty()) { int m1 = h.min_value(); int m2 = h.erase_min(); SASSERT(m1 == m2); SASSERT(last < m2); } } int g_value[N]; struct lt_proc2 { bool operator()(int v1, int v2) const { SASSERT(v1 < N && v2 < N); return g_value[v1] < g_value[v2]; } }; typedef heap int_heap2; static void init_values() { for (unsigned i = 0; i < N; i++) g_value[i] = rand(); } static void dump_heap(const int_heap2 & h, std::ostream & out) { // int_heap2::const_iterator it = h.begin(); // int_heap2::const_iterator end = h.end(); // for (; it != end; ++it) { // out << *it << ":" << g_value[*it] << " "; // } // out << "\n"; } static void tst2() { int_heap2 h(N); for (int i = 0; i < N * 10; i++) { if (i % 1000 == 0) std::cout << "i: " << i << std::endl; int cmd = rand() % 10; if (cmd <= 3) { // insert int val = rand() % N; if (!h.contains(val)) { TRACE("heap", tout << "inserting: " << val << "\n";); h.insert(val); TRACE("heap", dump_heap(h, tout);); SASSERT(h.contains(val)); } } else if (cmd <= 6) { int val = rand() % N; if (h.contains(val)) { TRACE("heap", tout << "removing: " << val << "\n";); h.erase(val); TRACE("heap", dump_heap(h, tout);); SASSERT(!h.contains(val)); } } else if (cmd <= 8) { // increased & decreased int val = rand() % N; int old_v = g_value[val]; int new_v = rand(); if (h.contains(val)) { g_value[val] = new_v; if (old_v < new_v) { TRACE("heap", tout << "value of: " << val << " increased old: " << old_v << " new: " << new_v << "\n";); h.increased(val); } else { TRACE("heap", tout << "value of: " << val << " decreased old: " << old_v << " new: " << new_v << "\n";); h.decreased(val); } } } else { SASSERT(h.check_invariant()); } } SASSERT(h.check_invariant()); } void tst_heap() { // enable_debug("heap"); enable_trace("heap"); tst1(); init_values(); tst2(); } z3-z3-4.4.1/src/test/heap_trie.cpp000066400000000000000000000026441260446376700166450ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "heap_trie.h" struct unsigned_le { static bool le(unsigned i, unsigned j) { return i <= j; } }; typedef heap_trie heap_trie_t; static void find_le(heap_trie_t& ht, unsigned num_keys, unsigned const* keys) { statistics st; vector vals; ht.find_all_le(keys, vals); std::cout << "find_le: "; for (unsigned i = 0; i < num_keys; ++i) { std::cout << keys[i] << " "; } std::cout << " |-> "; for (unsigned i = 0; i < vals.size(); ++i) { std::cout << vals[i] << " "; } std::cout << "\n"; ht.collect_statistics(st); st.display(std::cout); } void tst_heap_trie() { unsigned_le le; heap_trie_t ht(le); ht.reset(3); unsigned keys1[3] = { 1, 2, 3}; ht.insert(keys1, 1); unsigned keys2[3] = { 2, 2, 3}; ht.insert(keys2, 2); unsigned keys3[3] = { 1, 1, 3}; ht.insert(keys3, 3); unsigned keys4[3] = { 2, 1, 3}; unsigned keys5[3] = { 2, 3, 3}; unsigned val; VERIFY (ht.find_eq(keys1, val) && val == 1); VERIFY (ht.find_eq(keys2, val) && val == 2); VERIFY (ht.find_eq(keys3, val) && val == 3); VERIFY (!ht.find_eq(keys4, val)); find_le(ht, 3, keys1); find_le(ht, 3, keys2); find_le(ht, 3, keys3); find_le(ht, 3, keys4); find_le(ht, 3, keys5); ht.display(std::cout); } z3-z3-4.4.1/src/test/hilbert_basis.cpp000066400000000000000000000374101260446376700175160ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "hilbert_basis.h" #include "ast_pp.h" #include "reg_decl_plugins.h" #include "arith_decl_plugin.h" #include "quant_tactics.h" #include "tactic.h" #include "tactic2solver.h" #include "solver.h" #include #include #include static bool g_use_ordered_support = false; static bool g_use_ordered_subsumption = false; static bool g_use_support = false; class hilbert_basis_validate { ast_manager& m; void validate_solution(hilbert_basis& hb, vector const& v, bool is_initial); rational eval_ineq(hilbert_basis& hb, unsigned idx, vector const& v, bool& is_eq) { vector w; rational bound; hb.get_ge(idx, w, bound, is_eq); rational sum(0); for (unsigned j = 0; j < v.size(); ++j) { sum += w[j]*v[j]; } sum -= bound; return sum; } public: hilbert_basis_validate(ast_manager& m); expr_ref mk_validate(hilbert_basis& hb); }; hilbert_basis_validate::hilbert_basis_validate(ast_manager& m): m(m) { } void hilbert_basis_validate::validate_solution(hilbert_basis& hb, vector const& v, bool is_initial) { unsigned sz = hb.get_num_ineqs(); rational bound; for (unsigned i = 0; i < sz; ++i) { bool is_eq; vector w; rational bound; hb.get_ge(i, w, bound, is_eq); rational sum(0); for (unsigned j = 0; j < v.size(); ++j) { sum += w[j]*v[j]; } if (sum >= bound && !is_eq) { continue; } if (sum == bound && is_eq) { continue; } // homogeneous solutions should be non-negative. if (!is_initial && sum.is_nonneg()) { continue; } // validation failed. std::cout << "validation failed\n"; std::cout << "constraint: "; for (unsigned j = 0; j < v.size(); ++j) { std::cout << v[j] << " "; } std::cout << (is_eq?" = ":" >= ") << bound << "\n"; std::cout << "vector: "; for (unsigned j = 0; j < w.size(); ++j) { std::cout << w[j] << " "; } std::cout << "\n"; std::cout << "sum: " << sum << "\n"; } } expr_ref hilbert_basis_validate::mk_validate(hilbert_basis& hb) { arith_util a(m); unsigned sz = hb.get_basis_size(); vector v; // check that claimed solution really satisfies inequalities: for (unsigned i = 0; i < sz; ++i) { bool is_initial; hb.get_basis_solution(i, v, is_initial); validate_solution(hb, v, is_initial); } // check that solutions satisfying inequalities are in solution. // build a formula that says solutions to linear inequalities // coincide with linear combinations of basis. vector offsets, increments; expr_ref_vector xs(m), vars(m); expr_ref var(m); svector names; sort_ref_vector sorts(m); #define mk_mul(_r,_x) (_r.is_one()?((expr*)_x):((expr*)a.mk_mul(a.mk_numeral(_r,true),_x))) for (unsigned i = 0; i < sz; ++i) { bool is_initial; hb.get_basis_solution(i, v, is_initial); for (unsigned j = 0; xs.size() < v.size(); ++j) { xs.push_back(m.mk_fresh_const("x", a.mk_int())); } if (is_initial) { expr_ref_vector tmp(m); for (unsigned j = 0; j < v.size(); ++j) { tmp.push_back(a.mk_numeral(v[j], true)); } offsets.push_back(tmp); } else { var = m.mk_var(vars.size(), a.mk_int()); expr_ref_vector tmp(m); for (unsigned j = 0; j < v.size(); ++j) { tmp.push_back(mk_mul(v[j], var)); } std::stringstream name; name << "u" << i; increments.push_back(tmp); vars.push_back(var); names.push_back(symbol(name.str().c_str())); sorts.push_back(a.mk_int()); } } expr_ref_vector bounds(m); for (unsigned i = 0; i < vars.size(); ++i) { bounds.push_back(a.mk_ge(vars[i].get(), a.mk_numeral(rational(0), true))); } expr_ref_vector fmls(m); expr_ref fml(m), fml1(m), fml2(m); for (unsigned i = 0; i < offsets.size(); ++i) { expr_ref_vector eqs(m); eqs.append(bounds); for (unsigned j = 0; j < xs.size(); ++j) { expr_ref_vector sum(m); sum.push_back(offsets[i][j].get()); for (unsigned k = 0; k < increments.size(); ++k) { sum.push_back(increments[k][j].get()); } eqs.push_back(m.mk_eq(xs[j].get(), a.mk_add(sum.size(), sum.c_ptr()))); } fml = m.mk_and(eqs.size(), eqs.c_ptr()); if (!names.empty()) { fml = m.mk_exists(names.size(), sorts.c_ptr(), names.c_ptr(), fml); } fmls.push_back(fml); } fml1 = m.mk_or(fmls.size(), fmls.c_ptr()); fmls.reset(); sz = hb.get_num_ineqs(); for (unsigned i = 0; i < sz; ++i) { bool is_eq; vector w; rational bound; hb.get_ge(i, w, bound, is_eq); expr_ref_vector sum(m); for (unsigned j = 0; j < w.size(); ++j) { if (!w[j].is_zero()) { sum.push_back(mk_mul(w[j], xs[j].get())); } } expr_ref lhs(m), rhs(m); lhs = a.mk_add(sum.size(), sum.c_ptr()); rhs = a.mk_numeral(bound, true); if (is_eq) { fmls.push_back(a.mk_eq(lhs, rhs)); } else { fmls.push_back(a.mk_ge(lhs, rhs)); } } fml2 = m.mk_and(fmls.size(), fmls.c_ptr()); fml = m.mk_eq(fml1, fml2); bounds.reset(); for (unsigned i = 0; i < xs.size(); ++i) { if (!hb.get_is_int(i)) { bounds.push_back(a.mk_ge(xs[i].get(), a.mk_numeral(rational(0), true))); } } if (!bounds.empty()) { fml = m.mk_implies(m.mk_and(bounds.size(), bounds.c_ptr()), fml); } return fml; } hilbert_basis* g_hb = 0; static double g_start_time; static void display_statistics(hilbert_basis& hb) { double time = static_cast(clock()) - g_start_time; statistics st; hb.collect_statistics(st); st.display(std::cout); std::cout << "time: " << (time / CLOCKS_PER_SEC) << " secs\n"; } static void on_ctrl_c(int) { signal (SIGINT, SIG_DFL); display_statistics(*g_hb); raise(SIGINT); } #if 0 static void validate_sat(hilbert_basis& hb) { ast_manager m; reg_decl_plugins(m); hilbert_basis_validate val(m); expr_ref fml = val.mk_validate(hb); return; std::cout << mk_pp(fml, m) << "\n"; fml = m.mk_not(fml); params_ref p; tactic_ref tac = mk_lra_tactic(m, p); ref sol = mk_tactic2solver(m, tac.get(), p); sol->assert_expr(fml); lbool r = sol->check_sat(0,0); std::cout << r << "\n"; } #endif static void saturate_basis(hilbert_basis& hb) { signal(SIGINT, on_ctrl_c); g_hb = &hb; g_start_time = static_cast(clock()); hb.set_use_ordered_support(g_use_ordered_support); hb.set_use_support(g_use_support); hb.set_use_ordered_subsumption(g_use_ordered_subsumption); lbool is_sat = hb.saturate(); switch(is_sat) { case l_true: std::cout << "sat\n"; hb.display(std::cout); //validate_sat(hb); break; case l_false: std::cout << "unsat\n"; break; case l_undef: std::cout << "undef\n"; break; } display_statistics(hb); } /** n - number of variables. k - subset of variables to be non-zero bound - numeric value of upper and lower bound num_ineqs - number of inequalities to create */ static void gorrila_test(unsigned seed, unsigned n, unsigned k, unsigned bound, unsigned num_ineqs) { std::cout << "Gorrila test\n"; random_gen rand(seed); hilbert_basis hb; SASSERT(0 < bound); SASSERT(k <= n); int ibound = static_cast(bound); for (unsigned i = 0; i < num_ineqs; ++i) { vector nv; nv.resize(n); rational a0; unsigned num_selected = 0; while (num_selected < k) { unsigned s = rand(n); if (nv[s].is_zero()) { nv[s] = rational(ibound - static_cast(rand(2*bound+1))); if (!nv[s].is_zero()) { ++num_selected; } } } a0 = rational(ibound - static_cast(rand(2*bound+1))); hb.add_ge(nv, a0); } hb.display(std::cout << "Saturate\n"); saturate_basis(hb); } static vector vec(int i, int j) { vector nv; nv.resize(2); nv[0] = rational(i); nv[1] = rational(j); return nv; } static vector vec(int i, int j, int k) { vector nv; nv.resize(3); nv[0] = rational(i); nv[1] = rational(j); nv[2] = rational(k); return nv; } static vector vec(int i, int j, int k, int l) { vector nv; nv.resize(4); nv[0] = rational(i); nv[1] = rational(j); nv[2] = rational(k); nv[3] = rational(l); return nv; } static vector vec(int i, int j, int k, int l, int m) { vector nv; nv.resize(5); nv[0] = rational(i); nv[1] = rational(j); nv[2] = rational(k); nv[3] = rational(l); nv[4] = rational(m); return nv; } static vector vec(int i, int j, int k, int l, int x, int y, int z) { vector nv; nv.resize(7); nv[0] = rational(i); nv[1] = rational(j); nv[2] = rational(k); nv[3] = rational(l); nv[4] = rational(x); nv[5] = rational(y); nv[6] = rational(z); return nv; } // example 9, Ajili, Contenjean // x + y - 2z = 0 // x - z = 0 // -y + z <= 0 static void tst1() { hilbert_basis hb; hb.add_eq(vec(1,1,-2)); hb.add_eq(vec(1,0,-1)); hb.add_le(vec(0,1,-1)); saturate_basis(hb); } // example 10, Ajili, Contenjean // 23x - 12y - 9z <= 0 // x - 8y - 8z <= 0 void tst2() { hilbert_basis hb; hb.add_eq(vec(-23,12,9)); hb.add_eq(vec(-1,8,8)); saturate_basis(hb); } // example 6, Ajili, Contenjean // 3x + 2y - z - 2u <= 0 static void tst3() { hilbert_basis hb; hb.add_le(vec(3,2,-1,-2)); saturate_basis(hb); } #define R rational // Sigma_1, table 1, Ajili, Contejean static void tst4() { hilbert_basis hb; hb.add_le(vec( 0,-2, 1, 3, 2,-2, 3), R(3)); hb.add_le(vec(-1, 7, 0, 1, 3, 5,-4), R(2)); hb.add_le(vec( 0,-1, 1,-1,-1, 0, 0), R(2)); hb.add_le(vec(-2, 0, 1, 4, 0, 0,-2), R(1)); hb.add_le(vec(-3, 2,-2, 2,-4,-1, 0), R(8)); hb.add_le(vec( 3,-2, 2,-2, 4, 1, 0), R(3)); hb.add_le(vec( 1, 0, 0,-1, 0, 1, 0), R(4)); hb.add_le(vec( 1,-2, 0, 0, 0, 0, 0), R(2)); hb.add_le(vec( 1, 1, 0, 0,-1, 0, 1), R(4)); hb.add_le(vec( 1, 0, 0, 0,-1, 0, 0), R(9)); saturate_basis(hb); } // Sigma_2 table 1, Ajili, Contejean static void tst5() { hilbert_basis hb; hb.add_le(vec( 1, 2,-1, 1), R(3)); hb.add_le(vec( 2, 4, 1, 2), R(12)); hb.add_le(vec( 1, 4, 2, 1), R(9)); hb.add_le(vec( 1, 1, 0,-1), R(10)); hb.add_le(vec( 1, 1,-1, 0), R(6)); hb.add_le(vec( 1,-1, 0, 0), R(0)); hb.add_le(vec( 0, 0, 1,-1), R(2)); saturate_basis(hb); } // Sigma_3 table 1, Ajili, Contejean static void tst6() { hilbert_basis hb; hb.add_le(vec( 4, 3, 0), R(6)); hb.add_le(vec(-3,-4, 0), R(-1)); hb.add_le(vec( 4, 0,-3), R(3)); hb.add_le(vec(-3, 0, 4), R(7)); hb.add_le(vec( 4, 0,-3), R(23)); hb.add_le(vec( 0,-3, 4), R(11)); saturate_basis(hb); } // Sigma_4 table 1, Ajili, Contejean static void tst7() { hilbert_basis hb; hb.add_eq(vec( 1, 1, 1, 0), R(5)); hb.add_le(vec( 2, 1, 0, 1), R(6)); hb.add_le(vec( 1, 2, 1, 1), R(7)); hb.add_le(vec( 1, 3,-1, 2), R(8)); hb.add_le(vec( 1, 2,-9,-12), R(-11)); hb.add_le(vec( 0, 0,-1, 3), R(10)); saturate_basis(hb); } // Sigma_5 table 1, Ajili, Contejean static void tst8() { hilbert_basis hb; hb.add_le(vec( 2, 1, 1), R(2)); hb.add_le(vec( 1, 2, 3), R(5)); hb.add_le(vec( 2, 2, 3), R(6)); hb.add_le(vec( 1,-1,-3), R(-2)); saturate_basis(hb); } // Sigma_6 table 1, Ajili, Contejean static void tst9() { hilbert_basis hb; hb.add_le(vec( 1, 2, 3), R(11)); hb.add_le(vec( 2, 2, 5), R(13)); hb.add_le(vec( 1,-1,-11), R(3)); saturate_basis(hb); } // Sigma_7 table 1, Ajili, Contejean static void tst10() { hilbert_basis hb; hb.add_le(vec( 1,-1,-1,-3), R(2)); hb.add_le(vec(-2, 3, 3,-5), R(3)); saturate_basis(hb); } // Sigma_8 table 1, Ajili, Contejean static void tst11() { hilbert_basis hb; hb.add_le(vec( 7,-2,11, 3, -5), R(5)); saturate_basis(hb); } // Sigma_9 table 1, Ajili, Contejean static void tst12() { hilbert_basis hb; hb.add_eq(vec( 1,-2,-3,4), R(0)); hb.add_le(vec(100,45,-78,-67), R(0)); saturate_basis(hb); } // Sigma_10 table 1, Ajili, Contejean static void tst13() { hilbert_basis hb; hb.add_le(vec( 23, -56, -34, 12, 11), R(0)); saturate_basis(hb); } // Sigma_11 table 1, Ajili, Contejean static void tst14() { hilbert_basis hb; hb.add_eq(vec(1, 0, -4, 8), R(2)); hb.add_le(vec(12,19,-11,-7), R(-7)); saturate_basis(hb); } static void tst15() { hilbert_basis hb; hb.add_le(vec(1, 0), R(1)); hb.add_le(vec(0, 1), R(1)); saturate_basis(hb); } static void tst16() { hilbert_basis hb; hb.add_le(vec(1, 0), R(100)); saturate_basis(hb); } static void tst17() { hilbert_basis hb; hb.add_eq(vec(1, 0), R(0)); hb.add_eq(vec(-1, 0), R(0)); hb.add_eq(vec(0, 2), R(0)); hb.add_eq(vec(0, -2), R(0)); saturate_basis(hb); } static void tst18() { hilbert_basis hb; hb.add_eq(vec(0, 1), R(0)); hb.add_eq(vec(1, -1), R(2)); saturate_basis(hb); } static void tst19() { hilbert_basis hb; hb.add_eq(vec(0, 1, 0), R(0)); hb.add_eq(vec(1, -1, 0), R(2)); saturate_basis(hb); } static void test_A_5_5_3() { hilbert_basis hb; for (unsigned i = 0; i < 15; ++i) { vector v; for (unsigned j = 0; j < 5; ++j) { for (unsigned k = 0; k < 15; ++k) { v.push_back(rational(k == i)); } } hb.add_ge(v, R(0)); } for (unsigned i = 1; i <= 15; ++i) { vector v; for (unsigned k = 1; k <= 5; ++k) { for (unsigned l = 1; l <= 5; ++l) { for (unsigned j = 1; j <= 3; ++j) { bool one = ((j*k <= i) && (((i - j) % 3) == 0)); // fixme v.push_back(rational(one)); } } } hb.add_ge(v, R(0)); } // etc. saturate_basis(hb); } void tst_hilbert_basis() { std::cout << "hilbert basis test\n"; // tst3(); // return; g_use_ordered_support = true; test_A_5_5_3(); return; tst18(); return; tst19(); return; tst17(); if (true) { tst1(); tst2(); tst3(); tst4(); tst4(); tst4(); tst4(); tst4(); tst4(); tst5(); tst6(); tst7(); tst8(); tst9(); tst10(); tst11(); tst12(); tst13(); tst14(); tst15(); tst16(); gorrila_test(0, 4, 3, 20, 5); gorrila_test(1, 4, 3, 20, 5); //gorrila_test(2, 4, 3, 20, 5); //gorrila_test(0, 4, 2, 20, 5); //gorrila_test(0, 4, 2, 20, 5); } else { gorrila_test(0, 10, 7, 20, 11); } return; std::cout << "ordered support\n"; g_use_ordered_support = true; tst4(); std::cout << "non-ordered support\n"; g_use_ordered_support = false; tst4(); } z3-z3-4.4.1/src/test/horn_subsume_model_converter.cpp000066400000000000000000000041041260446376700226560ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "horn_subsume_model_converter.h" #include "arith_decl_plugin.h" #include "model_smt2_pp.h" #include "reg_decl_plugins.h" void tst_horn_subsume_model_converter() { ast_manager m; reg_decl_plugins(m); arith_util a(m); ptr_vector ints; ints.push_back(a.mk_int()); ints.push_back(a.mk_int()); ints.push_back(a.mk_int()); func_decl_ref p(m), q(m), r(m); p = m.mk_func_decl(symbol("p"), 2, ints.c_ptr(), m.mk_bool_sort()); q = m.mk_func_decl(symbol("q"), 2, ints.c_ptr(), m.mk_bool_sort()); r = m.mk_func_decl(symbol("r"), 2, ints.c_ptr(), m.mk_bool_sort()); ref mc = alloc(horn_subsume_model_converter,m); model_ref mr = alloc(model, m); mc->insert(p, m.mk_app(q, a.mk_numeral(rational(1), true), a.mk_numeral(rational(2), true))); model_converter_ref mcr = mc.get(); apply(mcr, mr, 0); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); mc->insert(p, m.mk_app(q, a.mk_numeral(rational(3), true), a.mk_numeral(rational(5), true))); apply(mcr, mr, 0); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); mc->insert(p, m.mk_app(r, m.mk_var(0,a.mk_int()), m.mk_var(1, a.mk_int()))); apply(mcr, mr, 0); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); app_ref head1(m); expr_ref body1(m), body2(m); func_decl_ref pred(m); head1 = m.mk_app(p, m.mk_var(0, a.mk_int()), m.mk_var(0, a.mk_int())); body1 = m.mk_app(q, m.mk_var(1, a.mk_int()), m.mk_var(2, a.mk_int())); VERIFY(mc->mk_horn(head1, body1, pred, body2)); mc->insert(pred, body2); apply(mcr, mr, 0); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); head1 = m.mk_app(p, m.mk_var(0, a.mk_int()), m.mk_var(0, a.mk_int())); body1 = m.mk_app(q, m.mk_var(1, a.mk_int()), m.mk_var(0, a.mk_int())); VERIFY(mc->mk_horn(head1, body1, pred, body2)); mc->insert(pred, body2); apply(mcr, mr, 0); model_smt2_pp(std::cout, m, *mr.get(), 0); } z3-z3-4.4.1/src/test/hwf.cpp000066400000000000000000000041211260446376700154610ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: hwf.cpp Abstract: hwf repros... Author: Leonardo de Moura (leonardo) 2012-08-23. Revision History: --*/ #include"hwf.h" #include"f2n.h" #include"rational.h" static void bug_set_double() { hwf_manager m; hwf a; m.set(a, 0.1); SASSERT(m.is_regular(a)); m.set(a, 1.1); SASSERT(m.is_regular(a)); m.set(a, 11.3); SASSERT(m.is_regular(a)); m.set(a, 0.0); SASSERT(m.is_regular(a)); } static void bug_to_rational() { hwf_manager m; hwf a; unsynch_mpq_manager mq; scoped_mpq r(mq); double ad, rd; m.set(a, 0.0); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); SASSERT(ad == rd); m.set(a, 1.0); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); SASSERT(ad == rd); m.set(a, 1.5); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); SASSERT(ad == rd); m.set(a, 0.875); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); SASSERT(ad == rd); m.set(a, -1.0); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); SASSERT(ad == rd); m.set(a, -1.5); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); SASSERT(ad == rd); m.set(a, -0.875); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); SASSERT(ad == rd); m.set(a, 0.1); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); #ifdef _WINDOWS // CMW: This one depends on the rounding mode, // which is implicit in both hwf::set and in mpq::to_double. double diff = (ad-rd); SASSERT(diff >= -DBL_EPSILON && diff <= DBL_EPSILON); #endif } static void bug_is_int() { unsigned raw_val[2] = { 2147483648u, 1077720461u }; double val = *(double*)(raw_val); std::cout << val << "\n"; hwf_manager m; hwf a; m.set(a, val); SASSERT(!m.is_int(a)); } void tst_hwf() { bug_is_int(); bug_set_double(); bug_to_rational(); } z3-z3-4.4.1/src/test/im_float_config.h000066400000000000000000000046151260446376700174710ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: im_float_config.h Abstract: Auxiliary class for testing intervals with software floats end points. Author: Leonardo de Moura (leonardo) 2012-08-21. Revision History: --*/ #ifndef IM_FLOAT_CONFIG_H_ #define IM_FLOAT_CONFIG_H_ #include"f2n.h" #include"mpf.h" #include"hwf.h" template class im_float_config { f2n m_manager; public: typedef f2n numeral_manager; typedef typename f_manager::numeral numeral; im_float_config(f_manager & m, unsigned ebits = 11, unsigned sbits = 53):m_manager(m, ebits, sbits) {} struct interval { numeral m_lower; numeral m_upper; }; void round_to_minus_inf() { m_manager.round_to_minus_inf(); } void round_to_plus_inf() { m_manager.round_to_plus_inf(); } void set_rounding(bool up) { m_manager.set_rounding(up); } // Getters numeral const & lower(interval const & a) const { return a.m_lower; } numeral const & upper(interval const & a) const { return a.m_upper; } numeral & lower(interval & a) { return a.m_lower; } numeral & upper(interval & a) { return a.m_upper; } bool lower_is_inf(interval const & a) const { return m_manager.m().is_ninf(a.m_lower); } bool upper_is_inf(interval const & a) const { return m_manager.m().is_pinf(a.m_upper); } bool lower_is_open(interval const & a) const { return lower_is_inf(a); } bool upper_is_open(interval const & a) const { return upper_is_inf(a); } // Setters void set_lower(interval & a, numeral const & n) { m_manager.set(a.m_lower, n); } void set_upper(interval & a, numeral const & n) { m_manager.set(a.m_upper, n); } void set_lower_is_open(interval & a, bool v) {} void set_upper_is_open(interval & a, bool v) {} void set_lower_is_inf(interval & a, bool v) { if (v) m_manager.m().mk_ninf(m_manager.ebits(), m_manager.sbits(), a.m_lower); } void set_upper_is_inf(interval & a, bool v) { if (v) m_manager.m().mk_pinf(m_manager.ebits(), m_manager.sbits(), a.m_upper); } // Reference to numeral manager numeral_manager & m() const { return const_cast(m_manager); } }; template inline void del_f_interval(im_float_config & cfg, typename im_float_config::interval & a) { cfg.m().del(a.m_lower); cfg.m().del(a.m_upper); } #endif z3-z3-4.4.1/src/test/inf_rational.cpp000066400000000000000000000123251260446376700173470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_inf_rational.cpp Abstract: Test for Rational numbers with infinitesimals Author: Leonardo de Moura (leonardo) 2006-09-18. Nikolaj Bjorner (nbjorner) 2006-10-24. Revision History: --*/ #include"inf_rational.h" static void tst0() { inf_rational n(rational(0), false); TRACE("inf_rational", tout << n << "\n";); SASSERT(n < inf_rational::zero()); SASSERT(!(n >= inf_rational::zero())); } void test_inc_dec( inf_rational& r, inf_rational const & b_8_5, inf_rational const & b_7_5, inf_rational const & b_7_10, inf_rational const & b_17_10 ) { r += rational(1,5); SASSERT (r == b_8_5); r -= rational(1,5); SASSERT (r == b_7_5); r += inf_rational(1,5); SASSERT (r == b_8_5); r -= inf_rational(1,5); SASSERT (r == b_7_5); r /= rational(2,1); SASSERT (r == b_7_10); inf_rational r_pre = r++; SASSERT (r_pre == b_7_10); SASSERT (r == b_17_10); inf_rational r_post = --r; SASSERT (r_post == b_7_10); SASSERT (r == b_7_10); r_post = ++r; SASSERT (r_post == b_17_10); SASSERT (r == b_17_10); r_pre = r--; SASSERT (r_pre == b_17_10); SASSERT (r == b_7_10); r_pre = r; r_pre += inf_rational(1,2); r_post = r_pre; r_post -= inf_rational(1,2); SASSERT(r == r_post); SASSERT(r + inf_rational(1,2) == r_pre); r_pre = r; r_pre /= rational(2,1); r_post = r_pre; r_post /= rational(1,2); SASSERT(r == r_post); SASSERT(rational(1,2) * r == r_pre); SASSERT(r == r_pre / rational(1,2)); } void tst_inf_rational() { tst0(); inf_rational r1; inf_rational r2(r1); SASSERT (r1 == r2); inf_rational r3(1); inf_rational r4(0); SASSERT (r4 == r1); SASSERT (r3 != r4); inf_rational r5(0,1); inf_rational r6(1,1); inf_rational r7(2,2); inf_rational r8(7,5); SASSERT (r1 == r5); SASSERT (r6 == r3); SASSERT (r7 == r3); inf_rational r9(rational(7,5)); SASSERT (r8 == r9); r9.reset(); SASSERT (r1 == r9); SASSERT (r1.is_int()); SASSERT (!r8.is_int()); SASSERT (0 == r1.get_int64()); r9 = r8; SASSERT (r8 == r9); inf_rational n = numerator(r7); inf_rational d = denominator(r7); { inf_rational b_8_5 = inf_rational(8,5); inf_rational b_7_5 = inf_rational(7,5); inf_rational b_7_10 = inf_rational(7,10); inf_rational b_17_10 = inf_rational(17,10); inf_rational r = r9; test_inc_dec(r, b_8_5, b_7_5, b_7_10, b_17_10); } { inf_rational b_8_5 = inf_rational(rational(8,5),true); inf_rational b_7_5 = inf_rational(rational(7,5),true); inf_rational b_7_10 = inf_rational(rational(7,5),true) / rational(2); inf_rational b_17_10 = b_7_10 + inf_rational(1); inf_rational r (rational(7,5),true); test_inc_dec(r, b_8_5, b_7_5, b_7_10, b_17_10); } SASSERT(inf_rational(rational(1,2),true) > inf_rational(rational(1,2))); SASSERT(inf_rational(rational(1,2),false) < inf_rational(rational(1,2))); SASSERT(inf_rational(rational(1,2),true) >= inf_rational(rational(1,2))); SASSERT(inf_rational(rational(1,2)) >= inf_rational(rational(1,2),false)); SASSERT(inf_rational(rational(1,2),false) != inf_rational(rational(1,2))); SASSERT(inf_rational(rational(1,2),true) != inf_rational(rational(1,2))); SASSERT(inf_rational(rational(1,2),false) != inf_rational(rational(1,2),true)); inf_rational h_neg(rational(1,2),false); inf_rational h_pos(rational(1,2),true); h_neg.neg(); SASSERT(h_neg == -inf_rational(rational(1,2),false)); h_neg.neg(); SASSERT(h_neg == inf_rational(rational(1,2),false)); SASSERT(r1.is_zero() && !r1.is_one() && !r1.is_neg() && r1.is_nonneg() && r1.is_nonpos() && !r1.is_pos()); SASSERT(!r3.is_zero() && r3.is_one() && !r3.is_neg() && r3.is_nonneg() && !r3.is_nonpos() && r3.is_pos()); SASSERT(floor(inf_rational(rational(1,2),false)) == rational()); SASSERT(floor(inf_rational(rational(1,2))) == rational()); SASSERT(floor(inf_rational(rational(),false)) == rational(-1)); SASSERT(floor(inf_rational(rational())) == rational()); SASSERT(floor(inf_rational(rational(),true)) == rational()); SASSERT(floor(inf_rational(rational(1),false)) == rational()); SASSERT(floor(inf_rational(rational(1))) == rational(1)); SASSERT(floor(inf_rational(rational(1),true)) == rational(1)); SASSERT(ceil(inf_rational(rational(1,2),false)) == rational(1)); SASSERT(ceil(inf_rational(rational(1,2))) == rational(1)); SASSERT(ceil(inf_rational(rational(),false)) == rational()); SASSERT(ceil(inf_rational(rational())) == rational()); SASSERT(ceil(inf_rational(rational(),true)) == rational(1)); SASSERT(ceil(inf_rational(rational(1),false)) == rational(1)); SASSERT(ceil(inf_rational(rational(1))) == rational(1)); SASSERT(ceil(inf_rational(rational(1),true)) == rational(2)); inf_rational x(rational(1,2),true); inf_rational y(1,2); x.swap(y); SASSERT (x == inf_rational(1,2)); SASSERT (y == inf_rational(rational(1,2),true)); SASSERT(inf_rational(1,2) == abs(-inf_rational(1,2))); } z3-z3-4.4.1/src/test/interval.cpp000066400000000000000000000374721260446376700165400ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: interval.h Abstract: Goodies/Templates for interval arithmetic Author: Leonardo de Moura (leonardo) 2012-07-19. Revision History: --*/ #include #include"interval_def.h" #include"dependency.h" #include"mpq.h" #include"ast.h" #include"debug.h" template class interval_manager; typedef im_default_config::interval interval; static void display_preamble(std::ostream & out) { out << "(set-info :status unsat)\n"; out << "(set-option :auto-config true)\n"; out << "(set-option :numeral-as-real true)\n"; out << "(declare-const a Real)\n"; out << "(declare-const b Real)\n"; } static void display_smt2_pos_numeral(std::ostream & out, unsynch_mpq_manager & m, mpq const & n) { if (m.is_int(n)) { m.display(out, n); } else { out << "(/ "; m.display(out, n.numerator()); out << " "; m.display(out, n.denominator()); out << ")"; } } static void display_smt2_numeral(std::ostream & out, unsynch_mpq_manager & m, mpq const & n) { if (m.is_neg(n)) { scoped_mpq n_copy(m); n_copy = n; out << "(- "; n_copy.neg(); display_smt2_pos_numeral(out, m, n_copy); out << ")"; } else { display_smt2_pos_numeral(out, m, n); } } static void display_constraint(std::ostream & out, unsynch_mpq_manager & m, char const * a, interval const & i, bool include_lower = true, bool include_upper = true) { out << "(and true"; if (!i.m_lower_inf && include_lower) { out << " (" << (i.m_lower_open ? "<" : "<=") << " "; display_smt2_numeral(out, m, i.m_lower); out << " " << a << ")"; } if (!i.m_upper_inf && include_upper) { out << " (" << (i.m_upper_open ? "<" : "<=") << " " << a << " "; display_smt2_numeral(out, m, i.m_upper); out << ")"; } out << ")"; } static void assert_hyp(std::ostream & out, unsynch_mpq_manager & m, char const * a, interval const & i, bool include_lower = true, bool include_upper = true) { out << "(assert "; display_constraint(out, m, a, i, include_lower, include_upper); out << ")\n"; } static void assert_conj(std::ostream & out, unsynch_mpq_manager & m, char const * a, interval const & i, bool include_lower = true, bool include_upper = true) { out << "(assert (not "; display_constraint(out, m, a, i, include_lower, include_upper); out << "))\n"; } #if 0 static bool mk_interval(im_default_config & cfg, interval & a, bool l_inf, bool l_open, int l_val, bool u_inf, bool u_open, int u_val) { if (!l_inf && !u_inf) { if (l_val > u_val) return false; if (l_val == u_val && (l_open || u_open)) return false; } if (l_inf) { a.m_lower_open = true; a.m_lower_inf = true; } else { a.m_lower_open = l_open; a.m_lower_inf = false; cfg.m().set(a.m_lower, l_val); } if (u_inf) { a.m_upper_open = true; a.m_upper_inf = true; } else { a.m_upper_open = u_open; a.m_upper_inf = false; cfg.m().set(a.m_upper, u_val); } return true; } #endif static void mk_random_interval(im_default_config & cfg, interval & a, unsigned magnitude) { switch (rand()%3) { case 0: // Neg, Neg if (rand()%4 == 0) { a.m_lower_open = true; a.m_lower_inf = true; a.m_upper_open = (rand()%2 == 0); a.m_upper_inf = false; cfg.m().set(a.m_upper, -static_cast((rand()%magnitude))); } else { a.m_upper_open = (rand()%2 == 0); a.m_upper_inf = false; int upper = -static_cast((rand()%magnitude)); cfg.m().set(a.m_upper, upper); a.m_lower_open = (rand()%2 == 0); a.m_lower_inf = false; cfg.m().set(a.m_lower, upper - static_cast(rand()%magnitude) - (a.m_lower_open || a.m_upper_open ? 1 : 0)); } break; case 1: // Neg, Pos if (rand()%4 == 0) { a.m_lower_open = true; a.m_lower_inf = true; } else { a.m_lower_open = (rand()%2 == 0); a.m_lower_inf = false; cfg.m().set(a.m_lower, -static_cast((rand()%magnitude)) - 1); } if (rand()%4 == 0) { a.m_upper_open = true; a.m_upper_inf = true; } else { a.m_upper_open = (rand()%2 == 0); a.m_upper_inf = false; cfg.m().set(a.m_upper, rand()%magnitude + 1); } break; default: // Neg, Neg if (rand()%4 == 0) { a.m_upper_open = true; a.m_upper_inf = true; a.m_lower_open = (rand()%2 == 0); a.m_lower_inf = false; cfg.m().set(a.m_lower, (rand()%magnitude)); } else { a.m_lower_open = (rand()%2 == 0); a.m_lower_inf = false; int lower = (rand()%magnitude); cfg.m().set(a.m_lower, lower); a.m_upper_open = (rand()%2 == 0); a.m_upper_inf = false; cfg.m().set(a.m_upper, lower + rand()%magnitude + (a.m_lower_open || a.m_upper_open ? 1 : 0)); } break; } } static void del_interval(im_default_config & cfg, interval & a) { cfg.m().del(a.m_lower); cfg.m().del(a.m_upper); } #define BUFFER_SZ 256 static int g_problem_id = 0; static char g_buffer[BUFFER_SZ]; char const * get_next_file_name() { #ifdef _WINDOWS sprintf_s(g_buffer, BUFFER_SZ, "interval_lemma_%d.smt2", g_problem_id); #else sprintf(g_buffer, "interval_lemma_%d.smt2", g_problem_id); #endif g_problem_id++; return g_buffer; } static void display_lemmas(unsynch_mpq_manager & nm, char const * result_term, interval const & a, interval const & b, interval const & r, interval_deps const & deps) { { std::ofstream out(get_next_file_name()); display_preamble(out); assert_hyp(out, nm, "a", a, dep_in_lower1(deps.m_lower_deps), dep_in_upper1(deps.m_lower_deps)); assert_hyp(out, nm, "b", b, dep_in_lower2(deps.m_lower_deps), dep_in_upper2(deps.m_lower_deps)); assert_conj(out, nm, result_term, r, true, false); out << "(check-sat)\n"; } { std::ofstream out(get_next_file_name()); display_preamble(out); assert_hyp(out, nm, "a", a, dep_in_lower1(deps.m_upper_deps), dep_in_upper1(deps.m_upper_deps)); assert_hyp(out, nm, "b", b, dep_in_lower2(deps.m_upper_deps), dep_in_upper2(deps.m_upper_deps)); assert_conj(out, nm, result_term, r, false, true); out << "(check-sat)\n"; } } #define MK_BINARY(NAME, RES_TERM) \ static void tst_ ## NAME(unsigned N, unsigned magnitude) { \ unsynch_mpq_manager nm; \ im_default_config imc(nm); \ interval_manager im(imc); \ interval a, b, r; \ \ for (unsigned i = 0; i < N; i++) { \ mk_random_interval(imc, a, magnitude); \ mk_random_interval(imc, b, magnitude); \ interval_deps deps; \ im.NAME(a, b, r, deps); \ \ display_lemmas(nm, RES_TERM, a, b, r, deps); \ } \ del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); \ } MK_BINARY(mul, "(* a b)"); MK_BINARY(add, "(+ a b)"); MK_BINARY(sub, "(- a b)"); static void tst_neg(unsigned N, unsigned magnitude) { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); interval a, b, r; for (unsigned i = 0; i < N; i++) { mk_random_interval(imc, a, magnitude); interval_deps deps; im.neg(a, r, deps); display_lemmas(nm, "(- a)", a, b, r, deps); } del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); } static void tst_pw_2(unsigned N, unsigned magnitude) { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); interval a, b, r; for (unsigned i = 0; i < N; i++) { mk_random_interval(imc, a, magnitude); interval_deps deps; im.power(a, 2, r, deps); display_lemmas(nm, "(* a a)", a, b, r, deps); } del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); } static void tst_pw_3(unsigned N, unsigned magnitude) { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); interval a, b, r; for (unsigned i = 0; i < N; i++) { mk_random_interval(imc, a, magnitude); interval_deps deps; im.power(a, 3, r, deps); display_lemmas(nm, "(* a a a)", a, b, r, deps); } del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); } static void tst_root_2(unsigned N, unsigned magnitude, unsigned precision) { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); interval a, b, r; scoped_mpq p(nm); p = precision; nm.inv(p); unsigned i = 0; while (i < N) { mk_random_interval(imc, a, magnitude); if (!im.lower_is_neg(a)) { i++; interval_deps deps; im.nth_root(a, 2, p, r, deps); display_lemmas(nm, "(^ a (/ 1.0 2.0))", a, b, r, deps); } } del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); } static void tst_root_3(unsigned N, unsigned magnitude, unsigned precision) { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); interval a, b, r; scoped_mpq p(nm); p = precision; nm.inv(p); unsigned i = 0; while (i < N) { mk_random_interval(imc, a, magnitude); i++; interval_deps deps; im.nth_root(a, 3, p, r, deps); display_lemmas(nm, "(^ a (/ 1.0 3.0))", a, b, r, deps); } del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); } static void tst_inv(unsigned N, unsigned magnitude) { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); interval a, b, r; for (unsigned i = 0; i < N; i++) { while (true) { mk_random_interval(imc, a, magnitude); if (!im.contains_zero(a)) break; } interval_deps deps; im.inv(a, r, deps); display_lemmas(nm, "(/ 1 a)", a, b, r, deps); } del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); } static void tst_div(unsigned N, unsigned magnitude) { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); interval a, b, r; for (unsigned i = 0; i < N; i++) { mk_random_interval(imc, a, magnitude); while (true) { mk_random_interval(imc, b, magnitude); if (!im.contains_zero(b)) break; } interval_deps deps; im.div(a, b, r, deps); display_lemmas(nm, "(/ a b)", a, b, r, deps); } del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); } #include"im_float_config.h" #if 0 static void tst_float() { unsynch_mpq_manager qm; mpf_manager fm; im_float_config ifc(fm); interval_manager > im(ifc); im_float_config::interval a, b, c; scoped_mpq minus_one_third(qm), one_third(qm), two_third(qm), minus_two_third(qm); qm.set(minus_one_third, -1, 3); qm.set(one_third, 1, 3); qm.set(two_third, 2, 3); qm.set(minus_two_third, -2, 3); ifc.round_to_minus_inf(); ifc.m().set(a.m_lower, minus_one_third); ifc.round_to_plus_inf(); ifc.m().set(a.m_upper, two_third); ifc.round_to_minus_inf(); ifc.m().set(b.m_lower, minus_two_third); ifc.round_to_plus_inf(); ifc.m().set(b.m_upper, one_third); im.display(std::cout, a); std::cout << "\n"; im.display(std::cout, b); std::cout << "\n"; interval_deps deps; im.add(a, b, c, deps); im.display(std::cout, c); std::cout << "\n"; del_f_interval(ifc, a); del_f_interval(ifc, b); del_f_interval(ifc, c); } #endif void tst_pi() { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); interval r; for (unsigned i = 0; i < 8; i++) { im.pi(i, r); nm.display_decimal(std::cout, im.lower(r), 32); std::cout << " "; nm.display_decimal(std::cout, im.upper(r), 32); std::cout << "\n"; SASSERT(nm.lt(im.lower(r), im.upper(r))); } del_interval(imc, r); } #if 0 static void tst_pi_float() { std::cout << "pi float...\n"; unsynch_mpq_manager qm; mpf_manager fm; im_float_config ifc(fm, 22, 106); interval_manager > im(ifc); scoped_mpq q(qm); im_float_config::interval r; for (unsigned i = 0; i < 8; i++) { im.pi(i, r); fm.to_rational(im.lower(r), q); qm.display_decimal(std::cout, q, 32); std::cout << " "; fm.to_rational(im.upper(r), q); qm.display_decimal(std::cout, q, 32); std::cout << "\n"; } del_f_interval(ifc, r); } #endif #define NUM_TESTS 1000 #define SMALL_MAG 3 #define MID_MAG 10 void tst_interval() { // enable_trace("interval_bug"); // tst_float(); // return; // enable_trace("interval_nth_root"); // tst_pi(); // tst_pi_float(); tst_root_2(NUM_TESTS, MID_MAG, 100); tst_root_3(NUM_TESTS, MID_MAG, 100); tst_div(NUM_TESTS, SMALL_MAG); tst_inv(NUM_TESTS, SMALL_MAG); tst_pw_2(NUM_TESTS, SMALL_MAG); tst_pw_3(NUM_TESTS, SMALL_MAG); tst_neg(NUM_TESTS, SMALL_MAG); tst_sub(NUM_TESTS, SMALL_MAG); tst_mul(NUM_TESTS, SMALL_MAG); tst_add(NUM_TESTS, SMALL_MAG); } z3-z3-4.4.1/src/test/karr.cpp000066400000000000000000000173621260446376700156470ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "hilbert_basis.h" /* Test generation of linear congruences a la Karr. */ namespace karr { struct matrix { vector > A; vector b; unsigned size() const { return A.size(); } void reset() { A.reset(); b.reset(); } matrix& operator=(matrix const& other) { reset(); append(other); return *this; } void append(matrix const& other) { A.append(other.A); b.append(other.b); } void display(std::ostream& out) { for (unsigned i = 0; i < A.size(); ++i) { for (unsigned j = 0; j < A[i].size(); ++j) { out << A[i][j] << " "; } out << " = " << -b[i] << "\n"; } } }; // treat src as a homogeneous matrix. void dualizeH(matrix& dst, matrix const& src) { hilbert_basis hb; for (unsigned i = 0; i < src.size(); ++i) { vector v(src.A[i]); v.push_back(src.b[i]); hb.add_eq(v, rational(0)); } for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) { hb.set_is_int(i); } lbool is_sat = hb.saturate(); hb.display(std::cout); SASSERT(is_sat == l_true); dst.reset(); unsigned basis_size = hb.get_basis_size(); for (unsigned i = 0; i < basis_size; ++i) { bool is_initial; vector soln; hb.get_basis_solution(i, soln, is_initial); if (!is_initial) { dst.b.push_back(soln.back()); soln.pop_back(); dst.A.push_back(soln); } } } // treat src as an inhomegeneous matrix. void dualizeI(matrix& dst, matrix const& src) { hilbert_basis hb; for (unsigned i = 0; i < src.size(); ++i) { hb.add_eq(src.A[i], -src.b[i]); } for (unsigned i = 0; i < src.A[0].size(); ++i) { hb.set_is_int(i); } lbool is_sat = hb.saturate(); hb.display(std::cout); SASSERT(is_sat == l_true); dst.reset(); unsigned basis_size = hb.get_basis_size(); bool first_initial = true; for (unsigned i = 0; i < basis_size; ++i) { bool is_initial; vector soln; hb.get_basis_solution(i, soln, is_initial); if (is_initial && first_initial) { dst.A.push_back(soln); dst.b.push_back(rational(1)); first_initial = false; } else if (!is_initial) { dst.A.push_back(soln); dst.b.push_back(rational(0)); } } } void juxtapose(matrix& dst, matrix const& M, matrix const& N) { dst = M; dst.append(N); } void join(matrix& dst, matrix const& M, matrix const& N) { matrix MD, ND, dstD; dualizeI(MD, M); dualizeI(ND, N); juxtapose(dstD, MD, ND); dualizeH(dst, dstD); } void joinD(matrix& dst, matrix const& MD, matrix const& ND) { matrix dstD; juxtapose(dstD, MD, ND); dualizeH(dst, dstD); } void transition( matrix& dst, matrix const& src, matrix const& Ab) { matrix T; // length of rows in Ab are twice as long as // length of rows in src. SASSERT(2*src.A[0].size() == Ab.A[0].size()); vector zeros; for (unsigned i = 0; i < src.A[0].size(); ++i) { zeros.push_back(rational(0)); } for (unsigned i = 0; i < src.size(); ++i) { T.A.push_back(src.A[i]); T.A.back().append(zeros); } T.b.append(src.b); T.append(Ab); T.display(std::cout << "T:\n"); matrix TD; dualizeI(TD, T); TD.display(std::cout << "TD:\n"); for (unsigned i = 0; i < TD.size(); ++i) { vector v; v.append(src.size(), TD.A[i].c_ptr() + src.size()); dst.A.push_back(v); dst.b.push_back(TD.b[i]); } dst.display(std::cout << "dst\n"); } static vector V(int i, int j) { vector v; v.push_back(rational(i)); v.push_back(rational(j)); return v; } static vector V(int i, int j, int k, int l) { vector v; v.push_back(rational(i)); v.push_back(rational(j)); v.push_back(rational(k)); v.push_back(rational(l)); return v; } #if 0 static vector V(int i, int j, int k, int l, int m) { vector v; v.push_back(rational(i)); v.push_back(rational(j)); v.push_back(rational(k)); v.push_back(rational(l)); v.push_back(rational(m)); return v; } #endif static vector V(int i, int j, int k, int l, int x, int y, int z) { vector v; v.push_back(rational(i)); v.push_back(rational(j)); v.push_back(rational(k)); v.push_back(rational(l)); v.push_back(rational(x)); v.push_back(rational(y)); v.push_back(rational(z)); return v; } #define R(_x_) rational(_x_) static void tst1() { matrix Theta; matrix Ab; // Theta.A.push_back(V(1, 0)); Theta.b.push_back(R(0)); Theta.A.push_back(V(0, 1)); Theta.b.push_back(R(-2)); Theta.display(std::cout << "Theta\n"); Ab.A.push_back(V(-1, 0, 1, 0)); Ab.b.push_back(R(1)); Ab.A.push_back(V(-1, -2, 0, 1)); Ab.b.push_back(R(1)); Ab.display(std::cout << "Ab\n"); matrix ThetaD; dualizeI(ThetaD, Theta); ThetaD.display(std::cout); matrix t1D, e1; transition(t1D, Theta, Ab); joinD(e1, t1D, ThetaD); t1D.display(std::cout << "t1D\n"); e1.display(std::cout << "e1\n"); matrix t2D, e2; transition(t2D, e1, Ab); joinD(e2, t2D, ThetaD); t2D.display(std::cout << "t2D\n"); e2.display(std::cout << "e2\n"); } void tst2() { /** 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 0 0 = 0 0 0 0 0 -1 0 0 = 0 0 1 0 0 0 0 0 = 0 0 -1 0 0 0 0 0 = 0 0 0 0 2 0 0 0 = 0 0 0 0 -2 0 0 0 = 0 */ matrix ND; ND.A.push_back(V(0,0,0,0,1,0,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,0,0,0,-1,0,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,1,0,0,0,0,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,-1,0,0,0,0,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,0,0,2,0,0,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,0,0,-2,0,0,0)); ND.b.push_back(R(0)); ND.display(std::cout << "ND\n"); matrix N; dualizeH(N, ND); N.display(std::cout << "N\n"); } void tst3() { /** 0 0 0 0 1 0 0 = 0 0 0 0 0 -1 0 0 = 0 0 1 0 0 0 0 0 = 0 0 -1 0 0 0 0 0 = 0 0 0 0 2 0 0 0 = 0 0 0 0 -2 0 0 0 = 0 */ matrix ND; ND.A.push_back(V(1,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,2)); ND.b.push_back(R(0)); ND.display(std::cout << "ND\n"); matrix N; dualizeH(N, ND); N.display(std::cout << "N\n"); } }; void tst_karr() { karr::tst3(); return; karr::tst1(); } z3-z3-4.4.1/src/test/list.cpp000066400000000000000000000020731260446376700156540ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: list.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-07-10. Revision History: --*/ #include"trace.h" #include"util.h" #include"region.h" #include"list.h" static void tst1() { region r; list * l1 = new (r) list(10); list * l2 = new (r) list(20, l1); list * l3 = new (r) list(30); list * l4 = new (r) list(40, l3); SASSERT(append(r, l1, static_cast *>(0)) == l1); SASSERT(append(r, l2, static_cast *>(0)) == l2); SASSERT(append(r, static_cast *>(0), l2) == l2); SASSERT(append(r, static_cast *>(0), static_cast *>(0)) == 0); TRACE("list", display(tout, l2->begin(), l2->end()); tout << "\n";); list * l5 = append(r, l4, l2); TRACE("list", display(tout, l5->begin(), l5->end()); tout << "\n";); list * l6 = append(r, l5, l5); TRACE("list", display(tout, l6->begin(), l6->end()); tout << "\n";); } void tst_list() { tst1(); } z3-z3-4.4.1/src/test/main.cpp000066400000000000000000000137251260446376700156330ustar00rootroot00000000000000#include #include #include #include #include"util.h" #include"trace.h" #include"debug.h" #include"timeit.h" #include"warning.h" #include "memory_manager.h" // // Unit tests fail by asserting. // If they return, we assume the unit test succeeds // and print "PASS" to indicate success. // #define TST(MODULE) { \ std::string s("test "); \ s += #MODULE; \ void tst_##MODULE(); \ if (do_display_usage) \ std::cout << #MODULE << "\n"; \ for (int i = 0; i < argc; i++) \ if (test_all || strcmp(argv[i], #MODULE) == 0) { \ enable_trace(#MODULE); \ enable_debug(#MODULE); \ timeit timeit(true, s.c_str()); \ tst_##MODULE(); \ std::cout << "PASS" << std::endl; \ } \ } #define TST_ARGV(MODULE) { \ std::string s("test "); \ s += #MODULE; \ void tst_##MODULE(char** argv, int argc, int& i); \ if (do_display_usage) \ std::cout << #MODULE << "\n"; \ for (int i = 0; i < argc; i++) \ if (strcmp(argv[i], #MODULE) == 0) { \ enable_trace(#MODULE); \ enable_debug(#MODULE); \ timeit timeit(true, s.c_str()); \ tst_##MODULE(argv, argc, i); \ std::cout << "PASS" << std::endl; \ } \ } void error(const char * msg) { std::cerr << "Error: " << msg << "\n"; std::cerr << "For usage information: test /h\n"; exit(1); } void display_usage() { std::cout << "Z3 unit tests [version 1.0]. (C) Copyright 2006 Microsoft Corp.\n"; std::cout << "Usage: test [options] [module names]\n"; std::cout << "\nMisc.:\n"; std::cout << " /h prints this message.\n"; std::cout << " /v:level be verbose, where is the verbosity level.\n"; std::cout << " /w enable warning messages.\n"; std::cout << " /a run all unit tests that don't require arguments.\n"; #if defined(Z3DEBUG) || defined(_TRACE) std::cout << "\nDebugging support:\n"; #endif #ifdef _TRACE std::cout << " /tr:tag enable trace messages tagged with .\n"; #endif #ifdef Z3DEBUG std::cout << " /dbg:tag enable assertions tagged with .\n"; #endif } void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage, bool& test_all) { int i = 1; while (i < argc) { char * arg = argv[i]; if (arg[0] == '-' || arg[0] == '/') { char * opt_name = arg + 1; char * opt_arg = 0; char * colon = strchr(arg, ':'); if (colon) { opt_arg = colon + 1; *colon = 0; } if (strcmp(opt_name, "h") == 0 || strcmp(opt_name, "?") == 0) { display_usage(); do_display_usage = true; return; } else if (strcmp(opt_name, "v") == 0) { if (!opt_arg) error("option argument (/v:level) is missing."); long lvl = strtol(opt_arg, 0, 10); set_verbosity_level(lvl); } else if (strcmp(opt_name, "w") == 0) { enable_warning_messages(true); } else if (strcmp(opt_name, "a") == 0) { test_all = true; } #ifdef _TRACE else if (strcmp(opt_name, "tr") == 0) { if (!opt_arg) error("option argument (/tr:tag) is missing."); enable_trace(opt_arg); } #endif #ifdef Z3DEBUG else if (strcmp(opt_name, "dbg") == 0) { if (!opt_arg) error("option argument (/dbg:tag) is missing."); enable_debug(opt_arg); } #endif } i++; } } int main(int argc, char ** argv) { memory::initialize(0); bool do_display_usage = false; bool test_all = false; parse_cmd_line_args(argc, argv, do_display_usage, test_all); TST(random); TST(vector); TST(symbol_table); TST(region); TST(symbol); TST(heap); TST(hashtable); TST(rational); TST(inf_rational); TST(ast); TST(optional); TST(bit_vector); TST(fixed_bit_vector); TST(tbv); TST(doc); TST(udoc_relation); TST(string_buffer); TST(map); TST(diff_logic); TST(uint_set); TST_ARGV(expr_rand); TST(list); TST(small_object_allocator); TST(timeout); TST(proof_checker); TST(simplifier); TST(bv_simplifier_plugin); TST(bit_blaster); TST(var_subst); TST(simple_parser); TST(api); TST(old_interval); TST(get_implied_equalities); TST(arith_simplifier_plugin); TST(matcher); TST(object_allocator); TST(mpz); TST(mpq); TST(mpf); TST(total_order); TST(dl_table); TST(dl_context); TST(dl_util); TST(dl_product_relation); TST(dl_relation); TST(parray); TST(stack); TST(escaped); TST(buffer); TST(chashtable); TST(ex); TST(nlarith_util); TST(api_bug); TST(arith_rewriter); TST(check_assumptions); TST(smt_context); TST(theory_dl); TST(model_retrieval); TST(factor_rewriter); TST(smt2print_parse); TST(substitution); TST(polynomial); TST(upolynomial); TST(algebraic); TST(polynomial_factorization); TST(prime_generator); TST(permutation); TST(nlsat); TST(ext_numeral); TST(interval); TST(f2n); TST(hwf); TST(trigo); TST(bits); TST(mpbq); TST(mpfx); TST(mpff); TST(horn_subsume_model_converter); TST(model2expr); TST(hilbert_basis); TST(heap_trie); TST(karr); TST(no_overflow); TST(memory); TST(datalog_parser); TST_ARGV(datalog_parser_file); TST(dl_query); TST(quant_solve); TST(rcf); TST(polynorm); TST(qe_arith); TST(expr_substitution); TST(sorting_network); TST(theory_pb); TST(simplex); TST(sat_user_scope); TST(pdr); TST_ARGV(ddnf); //TST_ARGV(hs); } void initialize_mam() {} void finalize_mam() {} z3-z3-4.4.1/src/test/map.cpp000066400000000000000000000020521260446376700154530ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_map.cpp Abstract: Test simple mapping. Author: Leonardo de Moura (leonardo) 2006-10-14. Revision History: --*/ #include"map.h" #include"str_hashtable.h" static void tst1() { map str2int; str2int.insert("foo", 35); SASSERT(str2int.contains("foo")); SASSERT(str2int.find_iterator("foo") != str2int.end()); SASSERT((*(str2int.find_iterator("foo"))).m_value == 35); SASSERT(str2int.size() == 1); str2int.insert("boo", 32); SASSERT(str2int.contains("foo")); SASSERT(str2int.find_iterator("foo") != str2int.end()); SASSERT((*(str2int.find_iterator("foo"))).m_value == 35); SASSERT(str2int.contains("boo")); SASSERT(str2int.find_iterator("boo") != str2int.end()); SASSERT((*(str2int.find_iterator("boo"))).m_value == 32); SASSERT(str2int.size() == 2); str2int.remove("boo"); SASSERT(str2int.size() == 1); SASSERT(!str2int.contains("boo")); SASSERT(str2int.contains("foo")); } void tst_map() { tst1(); } z3-z3-4.4.1/src/test/matcher.cpp000066400000000000000000000065151260446376700163310ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: matcher.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-16. Revision History: --*/ #ifdef _WINDOWS #include"matcher.h" #include"ast_pp.h" #include "reg_decl_plugins.h" void tst_match(ast_manager & m, app * t, app * i) { substitution s(m); s.reserve(2, 10); // reserving a big number of variables to be safe. matcher match; std::cout << "Is " << mk_pp(i, m) << " an instance of " << mk_pp(t, m) << "\n"; if (match(t, i, s)) { std::cout << "yes\n"; s.display(std::cout); } else { std::cout << "no\n"; } s.reset(); if (t->get_decl() == i->get_decl()) { // trying to match the arguments of t and i std::cout << "Are the arguments of " << mk_pp(i, m) << " an instance of the arguments of " << mk_pp(t, m) << "\n"; unsigned num_args = t->get_num_args(); unsigned j; for (j = 0; j < num_args; j++) { if (!match(t->get_arg(j), i->get_arg(j), s)) break; } if (j == num_args) { std::cout << "yes\n"; s.display(std::cout); // create some dummy term to test for applying the substitution. sort_ref S( m.mk_uninterpreted_sort(symbol("S")), m); sort * domain[3] = {S, S, S}; func_decl_ref r( m.mk_func_decl(symbol("r"), 3, domain, S), m); expr_ref x1( m.mk_var(0, S), m); expr_ref x2( m.mk_var(1, S), m); expr_ref x3( m.mk_var(2, S), m); app_ref rxyzw( m.mk_app(r, x1.get(), x2.get(), x3.get()), m); expr_ref result(m); unsigned deltas[2] = {0,0}; s.apply(2, deltas, expr_offset(rxyzw, 0), result); std::cout << "applying substitution to\n" << mk_pp(rxyzw,m) << "\nresult:\n" << mk_pp(result,m) << "\n"; } else { std::cout << "no\n"; } } std::cout << "\n"; } void tst1() { ast_manager m; reg_decl_plugins(m); sort_ref s( m.mk_uninterpreted_sort(symbol("S")), m); func_decl_ref g( m.mk_func_decl(symbol("g"), s, s), m); func_decl_ref h( m.mk_func_decl(symbol("h"), s, s), m); sort * domain[2] = {s, s}; func_decl_ref f( m.mk_func_decl(symbol("f"), 2, domain, s), m); app_ref a( m.mk_const(symbol("a"), s), m); app_ref b( m.mk_const(symbol("b"), s), m); expr_ref x( m.mk_var(0, s), m); expr_ref y( m.mk_var(1, s), m); app_ref gx( m.mk_app(g, x), m); app_ref fgx_x( m.mk_app(f, gx.get(), x.get()), m); app_ref ha( m.mk_app(h, a.get()), m); app_ref gha( m.mk_app(g, ha.get()), m); app_ref fgha_ha( m.mk_app(f, gha.get(), ha.get()), m); tst_match(m, fgx_x, fgha_ha); app_ref fgha_gha( m.mk_app(f, gha.get(), gha.get()), m); tst_match(m, fgx_x, fgha_gha); app_ref fxy( m.mk_app(f, x.get(), y.get()), m); app_ref fyx( m.mk_app(f, y.get(), x.get()), m); tst_match(m, fxy, fyx); app_ref fygx( m.mk_app(f, y.get(), gx.get()), m); tst_match(m, fxy, fygx); tst_match(m, fygx, fxy); } void tst_matcher() { tst1(); } #else void tst_matcher() { } #endif z3-z3-4.4.1/src/test/memory.cpp000066400000000000000000000021771260446376700162160ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include "z3.h" #include "z3_private.h" #include #include "util.h" #include "trace.h" static bool oom = false; static void err_handler(Z3_context c, Z3_error_code e) { oom = true; throw std::bad_alloc(); } static void hit_me(char const* wm) { Z3_config cfg; Z3_context ctx; oom = false; cfg = Z3_mk_config(); if (!cfg) { return; } Z3_global_param_set("MEMORY_MAX_SIZE", wm); ctx = Z3_mk_context(cfg); if (ctx) { Z3_set_error_handler(ctx, &err_handler); unsigned i; for (i = 1; !oom ; ++i) { try { Z3_mk_bv_sort(ctx,i); } catch (std::bad_alloc) { std::cout << "caught\n"; } } std::cout << "oom " << i << "\n"; Z3_del_context(ctx); } Z3_del_config(cfg); } void tst_memory() { hit_me("10"); Z3_reset_memory(); hit_me("20"); Z3_reset_memory(); hit_me("30"); Z3_reset_memory(); } #else void tst_memory() { } #endif z3-z3-4.4.1/src/test/model2expr.cpp000066400000000000000000000027401260446376700167630ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "model2expr.h" #include "ast_pp.h" #include "arith_decl_plugin.h" #include "model_smt2_pp.h" #include "reg_decl_plugins.h" void tst_model2expr() { ast_manager m; reg_decl_plugins(m); arith_util a(m); ptr_vector ints; ints.push_back(a.mk_int()); ints.push_back(a.mk_int()); ints.push_back(a.mk_int()); func_decl_ref p(m), q(m), x(m); p = m.mk_func_decl(symbol("p"), 2, ints.c_ptr(), a.mk_int()); q = m.mk_func_decl(symbol("q"), 2, ints.c_ptr(), a.mk_int()); x = m.mk_const_decl(symbol("x"), a.mk_int()); expr_ref n0(m), n1(m), n2(m); n0 = a.mk_numeral(rational(0), true); n1 = a.mk_numeral(rational(1), true); n2 = a.mk_numeral(rational(2), true); model_ref md = alloc(model, m); func_interp* fip = alloc(func_interp, m, 2); func_interp* fiq = alloc(func_interp, m, 2); expr_ref_vector args(m); args.push_back(n1); args.push_back(n2); fip->insert_entry(args.c_ptr(), n1); fiq->insert_entry(args.c_ptr(), n1); args[0] = n0; args[1] = n1; fip->insert_entry(args.c_ptr(), n2); fiq->insert_entry(args.c_ptr(), n2); fip->set_else(n0); md->register_decl(x, a.mk_numeral(rational(0), true)); md->register_decl(p, fip); // full md->register_decl(q, fiq); // partial expr_ref result(m); model2expr(md, result); model_smt2_pp(std::cout, m, *md, 0); std::cout << mk_pp(result, m) << "\n"; } z3-z3-4.4.1/src/test/model_retrieval.cpp000066400000000000000000000037021260446376700200560ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast.h" #include "smt_params.h" #include "smt_context.h" #include "arith_decl_plugin.h" #include "bv_decl_plugin.h" #include "array_decl_plugin.h" #include "model_v2_pp.h" #include "reg_decl_plugins.h" void tst_model_retrieval() { memory::initialize(0); smt_params params; params.m_model = true; ast_manager m; reg_decl_plugins(m); family_id array_fid = m.mk_family_id(symbol("array")); array_util au(m); // arr_s and select_fn creation copy-pasted from z3.cpp parameter sparams[2] = { parameter(to_sort(m.mk_bool_sort())), parameter(to_sort(m.mk_bool_sort())) }; sort_ref arr_s(m.mk_sort(array_fid, ARRAY_SORT, 2, sparams), m); sort * domain2[2] = {arr_s, m.mk_bool_sort()}; func_decl_ref select_fn( m.mk_func_decl(array_fid, OP_SELECT, 2, arr_s->get_parameters(), 2, domain2), m); app_ref a1(m.mk_const(symbol("a1"), arr_s), m); app_ref a2(m.mk_const(symbol("a2"), arr_s), m); // (= true (select a1 true)) app_ref fml(m.mk_eq(m.mk_true(), m.mk_app(select_fn.get(), a1, m.mk_true())), m); smt::context ctx(m, params); ctx.assert_expr(fml); lbool check_result = ctx.check(); std::cout<<((check_result==l_true) ? "satisfiable" : (check_result==l_false) ? "unsatisfiable" : "unknown")<<"\n"; ref model; ctx.get_model(model); model_v2_pp(std::cout, *model, false); expr_ref a1_val(model->get_const_interp(a1->get_decl()), m); app_ref fml2(m.mk_eq(a2, a1_val), m); ctx.assert_expr(fml2); std::cout<<"--------------------------\n"; ctx.display(std::cout); std::cout<<"--------------------------\n"; check_result = ctx.check(); ctx.display(std::cout); std::cout<<"--------------------------\n"; std::cout<<((check_result==l_true) ? "satisfiable" : (check_result==l_false) ? "unsatisfiable" : "unknown")<<"\n"; } z3-z3-4.4.1/src/test/mpbq.cpp000066400000000000000000000017321260446376700156410ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpbq.cpp Abstract: mpbq tests... Author: Leonardo de Moura (leonardo) 2012-09-20 Revision History: --*/ #include "mpbq.h" static void tst1() { unsynch_mpz_manager zm; mpbq_manager m(zm); scoped_mpbq a(m), b(m); m.set(a, INT_MAX); a = a + 1; a = a * a; a = a*3 - 1; a = a * a - 5; a = a * a - 7; m.div2k(a, 67); std::cout << a << "\n"; b = a; m.approx(b, 32, true); std::cout << b << "\n"; b = a; m.approx(b, 32, false); std::cout << b << "\n"; b = a; m.neg(b); m.approx(b, 32, true); std::cout << b << "\n"; b = a; m.neg(b); m.approx(b, 32, false); std::cout << b << "\n"; } static void tst2() { unsynch_mpz_manager zm; mpbq_manager m(zm); scoped_mpbq a(m), b(m); m.set(a, 5); m.set(b, 3); m.approx_div(a, b, a, 128); std::cout << a << "\n"; } void tst_mpbq() { tst1(); tst2(); } z3-z3-4.4.1/src/test/mpf.cpp000066400000000000000000000034261260446376700154660ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpf.cpp Abstract: mpf repros... Author: Leonardo de Moura (leonardo) 2012-08-21. Revision History: --*/ #include"mpf.h" #include"f2n.h" static void bug_set_int() { mpf_manager fm; scoped_mpf a(fm); fm.set(a, 11, 53, 3); SASSERT(fm.to_double(a) == 3.0); fm.set(a, 11, 53, 0); SASSERT(fm.to_double(a) == 0.0); fm.set(a, 11, 53, -1); SASSERT(fm.to_double(a) == -1.0); fm.set(a, 11, 53, INT_MAX); SASSERT(fm.to_double(a) == (double)INT_MAX); fm.set(a, 11, 53, INT_MIN); SASSERT(fm.to_double(a) == (double)INT_MIN); fm.set(a, 8, 24, 3); SASSERT(fm.to_float(a) == 3.0); SASSERT(fm.to_double(a) == 3.0); fm.set(a, 8, 24, 0); SASSERT(fm.to_float(a) == 0.0); SASSERT(fm.to_double(a) == 0.0); fm.set(a, 8, 24, -1); SASSERT(fm.to_float(a) == -1.0); SASSERT(fm.to_double(a) == -1.0); fm.set(a, 8, 24, INT_MIN); SASSERT(fm.to_float(a) == (float)INT_MIN); // CMW: This one depends on the rounding mode, but fm.set(..., int) doesn't have one. // fm.set(a, 8, 24, INT_MAX); // SASSERT(fm.to_float(a) == (float)INT_MAX); } static void bug_set_double() { mpf_manager fm; scoped_mpf a(fm); fm.set(a, 11, 53, 2.5); SASSERT(fm.to_double(a) == 2.5); fm.set(a, 11, 53, -42.25); SASSERT(fm.to_double(a) == -42.25); fm.set(a, 8, 24, (double)2.5); SASSERT(fm.to_double(a) == 2.5); fm.set(a, 8, 24, (double)-42.25); SASSERT(fm.to_double(a) == -42.25); fm.set(a, 8, 24, (float)2.5); SASSERT(fm.to_float(a) == 2.5); fm.set(a, 8, 24, (float)-42.25); SASSERT(fm.to_float(a) == -42.25); } void tst_mpf() { enable_trace("mpf_mul_bug"); bug_set_int(); bug_set_double(); } z3-z3-4.4.1/src/test/mpff.cpp000066400000000000000000000455721260446376700156440ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpff.cpp Abstract: mpff tests... Author: Leonardo de Moura (leonardo) 2012-09-12. Revision History: --*/ #include #include"mpff.h" #include"mpz.h" #include"mpq.h" static void tst1() { try { mpff_manager m; // m.round_to_minus_inf(); scoped_mpff a(m), b(m); m.set(a, 100); m.set(b, -33); std::cout << "a: " << a << ", b: " << b << "\n"; std::cout << "a*b: " << a*b << "\n"; for (unsigned i = 0; i < 100; i++) { a = a*a; std::cout << i << ": " << a << "\n"; } } catch (z3_exception & ex) { std::cout << ex.msg() << "\n"; } } static void tst2() { mpff_manager m; scoped_mpff a(m), b(m); m.set(a, static_cast(100)); m.set(b, static_cast(-100)); std::cout << "[test2], a: " << a << ", b: " << b << "\n"; } static void tst3() { mpff_manager m; scoped_mpff a(m), b(m), c(m); m.set(a, 1); m.set(b, 3); m.div(a, b, c); std::cout << "[div] c: " << c << "\n"; m.round_to_plus_inf(); m.reset(c); m.div(a, b, c); std::cout << "[div] c: " << c << "\n"; } static void tst4() { unsynch_mpz_manager zm; mpff_manager m; scoped_mpz a(zm); scoped_mpff b(m); zm.set(a, 2); zm.power(a, 512, a); m.set(b, zm, a); std::cout << "[mpz->mpff] a: " << a << ", b: " << b << "\n"; } static void tst5() { mpff_manager m; scoped_mpff a(m), b(m); m.set(a, static_cast(1) << 63); m.display_raw(std::cout, a); std::cout << "\n"; SASSERT(m.is_zero(b)); SASSERT(m.lt(b, a)); m.set(b, -1); SASSERT(m.lt(b, a)); } static void tst6() { mpff_manager m; scoped_mpff a(m), b(m), one(m); m.set(a, 1, 3); std::cout << "mpff(1/3) " << a << "\n"; b = a; m.next(b); SASSERT(m.lt(a, b)); std::cout << "b: " << b << "\n"; m.prev(b); SASSERT(m.eq(a, b)); m.ceil(b); std::cout << "b: " << b << "\n"; m.set(b, 4, 3); std::cout << "b: " << b << "\n"; m.ceil(b); std::cout << "b: " << b << "\n"; } static void tst7() { mpff_manager m; scoped_mpff a(m); m.set(a, 2); m.display_smt2(std::cout, a); std::cout << "\n"; m.set(a, -2); m.display_smt2(std::cout, a); std::cout << "\n"; m.set(a, 1, 3); m.display_smt2(std::cout, a); std::cout << "\n"; } // if (!qm.le(qa, qt)) { TRACE("mpff_bug", tout << fa << "\n" << qa << "\n" << qt << "\n";); UNREACHABLE(); } #define MK_BIN_OP(OP) \ static void tst_ ## OP ## _core(int64 n1, uint64 d1, int64 n2, uint64 d2, unsigned precision = 2, unsigned exp = 0) { \ TRACE("mpff_bug", tout << n1 << "/" << d1 << ", " << n2 << "/" << d2 << "\n";); \ unsynch_mpq_manager qm; \ scoped_mpq qa(qm), qb(qm), qc(qm), qt(qm); \ \ mpff_manager fm(precision); \ scoped_mpff fa(fm), fb(fm), fc1(fm), fc2(fm); \ fm.set(fa, n1, d1); \ if (exp != 0) { int _exp = rand() % exp; if (rand() % 2 == 0) _exp = -_exp; fm.set_exponent(fa, _exp); } \ fm.to_mpq(fa, qm, qa); \ fm.set(fb, n2, d2); \ if (exp != 0) { int _exp = rand() % exp; if (rand() % 2 == 0) _exp = -_exp; fm.set_exponent(fb, _exp); } \ fm.to_mpq(fb, qm, qb); \ qm.OP(qa, qb, qc); \ { \ fm.round_to_plus_inf(); \ fm.OP(fa, fb, fc1); \ fm.to_mpq(fc1, qm, qt); \ SASSERT(qm.le(qc, qt)); \ } \ { \ fm.round_to_minus_inf(); \ fm.OP(fa, fb, fc2); \ fm.to_mpq(fc2, qm, qt); \ SASSERT(qm.le(qt, qc)); \ } \ SASSERT(fm.le(fc2, fc1)); \ } MK_BIN_OP(add); MK_BIN_OP(sub); MK_BIN_OP(mul); MK_BIN_OP(div); #define MK_BIN_RANDOM_TST(OP) \ static void tst_ ## OP(unsigned N, unsigned max, unsigned prec = 2, bool is_div = false) { \ for (unsigned i = 0; i < N; i++) { \ int n1 = rand() % max; \ int d1 = rand() % max + 1; \ int n2 = rand() % max; \ int d2 = rand() % max + 1; \ if (rand () % 2 == 0) \ n1 = -n1; \ if (rand () % 2 == 0) \ n2 = -n2; \ if (is_div && n2 == 0) n2 = 1; \ tst_ ## OP ## _core(n1, d1, n2, d2, prec); \ tst_ ## OP ## _core(n1, d1, n2, d2, prec, 512); \ } \ } MK_BIN_RANDOM_TST(add) MK_BIN_RANDOM_TST(sub) MK_BIN_RANDOM_TST(mul) MK_BIN_RANDOM_TST(div) static void tst_bug() { unsynch_mpq_manager qm; mpff_manager fm; fm.round_to_plus_inf(); scoped_mpff a(fm); fm.set(a, 41, 36); scoped_mpq b(qm), c(qm); qm.set(b, 41, 36); fm.to_mpq(a, qm, c); SASSERT(qm.le(b, c)); } static void tst_bug2() { mpff_manager fm(4); scoped_mpff a(fm), b(fm); fm.set(b, 1); fm.sub(a, b, b); fm.set(a, -1); SASSERT(fm.eq(a, b)); fm.set(a, 1); fm.set(b, 0); fm.sub(a, b, a); fm.set(b, 1); SASSERT(fm.eq(a, b)); fm.set(a, 1); fm.set(b, 1); fm.sub(a, b, a); SASSERT(fm.is_zero(a)); } static void tst_set64(unsigned N, unsigned prec) { mpff_manager fm(prec); scoped_mpff a(fm); fm.set(a, INT64_MAX); SASSERT(fm.is_int64(a)); SASSERT(fm.is_uint64(a)); fm.inc(a); SASSERT(!fm.is_int64(a)); SASSERT(fm.is_uint64(a)); SASSERT(fm.is_int(a)); fm.dec(a); SASSERT(fm.is_int64(a)); SASSERT(fm.is_uint64(a)); fm.dec(a); SASSERT(fm.is_int64(a)); SASSERT(fm.is_uint64(a)); fm.set(a, INT64_MIN); SASSERT(fm.is_int64(a)); SASSERT(!fm.is_uint64(a)); fm.dec(a); SASSERT(!fm.is_int64(a)); SASSERT(!fm.is_uint64(a)); SASSERT(fm.is_int(a)); fm.inc(a); SASSERT(fm.is_int64(a)); SASSERT(!fm.is_uint64(a)); fm.inc(a); SASSERT(fm.is_int64(a)); SASSERT(!fm.is_uint64(a)); fm.set(a, UINT64_MAX); SASSERT(fm.is_uint64(a)); SASSERT(!fm.is_int64(a)); fm.inc(a); SASSERT(!fm.is_uint64(a)); SASSERT(!fm.is_int64(a)); fm.dec(a); SASSERT(fm.is_uint64(a)); SASSERT(!fm.is_int64(a)); fm.dec(a); SASSERT(fm.is_uint64(a)); SASSERT(!fm.is_int64(a)); for (unsigned i = 0; i < N; i++) { { uint64 v = (static_cast(rand()) << 32) + static_cast(rand()); fm.set(a, v); SASSERT(fm.is_uint64(a)); v = (static_cast(rand() % 3) << 32) + static_cast(rand()); fm.set(a, v); SASSERT(fm.is_uint64(a)); } { int64 v = (static_cast(rand() % INT_MAX) << 32) + static_cast(rand()); if (rand()%2 == 0) v = -v; fm.set(a, v); SASSERT(fm.is_int64(a)); v = (static_cast(rand() % 3) << 32) + static_cast(rand()); if (rand()%2 == 0) v = -v; fm.set(a, v); SASSERT(fm.is_int64(a)); } } } static void tst_capacity(unsigned prec = 2) { mpff_manager m(prec); scoped_mpff_vector v(m); scoped_mpff a(m); for (unsigned i = 0; i < 50000; i++) { m.set(a, i); v.push_back(a); SASSERT(m.is_int(v.back())); SASSERT(m.is_int64(v.back())); SASSERT(m.is_uint64(v.back())); } for (unsigned i = 0; i < 50000; i++) { SASSERT(m.get_int64(v[i]) == i); } } static void tst_power(unsigned prec = 2) { mpff_manager m(prec); scoped_mpff a(m), b(m); // 0^k == 0 SASSERT(m.is_zero(a)); m.power(a, 10, a); SASSERT(m.is_zero(a)); // a != 0 ==> a^0 == 1 m.set(a, 33); m.power(a, 0, a); SASSERT(m.is_one(a)); m.set(a, -33); m.power(a, 0, a); SASSERT(m.is_one(a)); // a^1 == a m.set(a, 33); m.power(a, 1, b); SASSERT(m.eq(a, b)); m.set(a, -33); m.power(a, 1, b); SASSERT(m.eq(a, b)); // checking special support for powers of 2 #ifdef Z3DEBUG unsigned k; #endif m.set(a, 1); SASSERT(m.is_power_of_two(a, k) && k == 0); m.set(a, 2); SASSERT(m.is_power_of_two(a, k) && k == 1); m.set(a, 3); SASSERT(!m.is_power_of_two(a, k)); m.set(a, 4); SASSERT(m.is_power_of_two(a, k) && k == 2); m.set(a, -4); SASSERT(!m.is_power_of_two(a, k)); m.set(a, 8); SASSERT(m.is_power_of_two(a, k) && k == 3); m.set(a, 0); SASSERT(!m.is_power_of_two(a)); m.set(a, UINT_MAX); m.inc(a); SASSERT(m.is_power_of_two(a, k) && k == 32); SASSERT(m.get_uint64(a) == static_cast(UINT_MAX) + 1); m.power(a, 2, a); SASSERT(m.is_power_of_two(a, k) && k == 64); m.power(a, 4, a); SASSERT(m.is_power_of_two(a, k) && k == 256); m.round_to_plus_inf(); m.inc(a); SASSERT(!m.is_power_of_two(a, k)); m.set(a, -4); m.power(a, 3, a); m.set(b, -64); SASSERT(m.eq(a, b)); m.set(a, -4); m.power(a, 4, a); m.set(b, 256); SASSERT(m.eq(a, b)); // additional tests m.set(a, 5); m.power(a, 3, a); m.set(b, 5*5*5); SASSERT(m.eq(a,b)); m.set(a, -5); m.power(a, 3, a); m.set(b, -5*5*5); SASSERT(m.eq(a,b)); } static void tst_sgn(unsigned prec) { mpff_manager m(prec); scoped_mpff a(m), b(m); SASSERT(m.is_zero(a) && !m.is_pos(a) && !m.is_neg(a) && m.is_nonpos(a) && m.is_nonneg(a)); m.set(a, 3); SASSERT(!m.is_zero(a) && m.is_pos(a) && !m.is_neg(a) && !m.is_nonpos(a) && m.is_nonneg(a)); m.set(a, -3); SASSERT(!m.is_zero(a) && !m.is_pos(a) && m.is_neg(a) && m.is_nonpos(a) && !m.is_nonneg(a)); m.set(a, 8); m.power(a, 256, a); SASSERT(!m.is_zero(a) && m.is_pos(a) && !m.is_neg(a) && !m.is_nonpos(a) && m.is_nonneg(a)); b = a; m.neg(a); SASSERT(m.neq(a, b)); SASSERT(!m.is_zero(a) && !m.is_pos(a) && m.is_neg(a) && m.is_nonpos(a) && !m.is_nonneg(a)); m.neg(a); SASSERT(m.eq(a, b)); m.set(a, 1); SASSERT(m.is_one(a) && !m.is_zero(a) && !m.is_minus_one(a) && m.is_abs_one(a)); m.neg(a); SASSERT(!m.is_one(a) && !m.is_zero(a) && m.is_minus_one(a) && m.is_abs_one(a)); m.set(a, 3); SASSERT(!m.is_one(a) && !m.is_zero(a) && !m.is_minus_one(a)); m.set(a, 3); b = a; m.abs(a); SASSERT(m.eq(a, b)); m.set(a, -3); b = a; m.abs(a); SASSERT(!m.eq(a,b) && m.is_pos(a)); m.set(a, 1); m.swap(a, a); SASSERT(m.is_one(a)); m.set(b, -1); m.swap(a, b); SASSERT(m.is_one(b) && m.is_minus_one(a)); m.neg(a); SASSERT(m.eq(a, b)); } static void tst_limits(unsigned prec) { mpff_manager m(prec); scoped_mpff a(m), b(m), two(m); m.set_max(a); SASSERT(m.is_pos(a)); m.set_min(b); SASSERT(m.is_neg(b)); m.neg(a); SASSERT(m.eq(a, b)); m.set_max(a); m.set_max(b); m.round_to_minus_inf(); m.inc(a); SASSERT(m.eq(a, b)); m.dec(a); SASSERT(m.lt(a, b)); m.set_max(a); m.round_to_plus_inf(); bool overflow = false; try { m.inc(a); } catch (mpff_manager::overflow_exception) { overflow = true; } SASSERT(overflow); m.set_max(a); m.dec(a); SASSERT(m.eq(a, b)); m.set_min(a); m.set_min(b); m.round_to_minus_inf(); m.inc(a); SASSERT(m.eq(a, b)); overflow = true; try { m.dec(a); } catch (mpff_manager::overflow_exception) { overflow = true; } SASSERT(overflow); m.round_to_plus_inf(); m.set_min(a); m.inc(a); SASSERT(m.gt(a,b)); m.set_min(a); m.dec(a); SASSERT(m.eq(a,b)); m.set_plus_epsilon(a); m.set_plus_epsilon(b); SASSERT(!m.is_zero(a) && m.is_pos(a)); m.set(two, 2); m.round_to_plus_inf(); m.div(a, two, a); SASSERT(m.eq(a, b)); m.round_to_minus_inf(); m.div(a, two, a); SASSERT(m.is_zero(a)); m.round_to_plus_inf(); m.set_plus_epsilon(a); m.add(a, a, a); SASSERT(m.gt(a, b)); m.round_to_minus_inf(); m.set_plus_epsilon(a); m.add(a, a, a); SASSERT(m.gt(a, b)); m.set_plus_epsilon(a); m.sub(a, a, a); SASSERT(m.is_zero(a)); m.set_plus_epsilon(a); SASSERT(m.is_plus_epsilon(a)); SASSERT(!m.is_minus_epsilon(a)); m.neg(a); SASSERT(!m.is_plus_epsilon(a)); SASSERT(m.is_minus_epsilon(a)); for (unsigned i = 0; i < 2; i++) { m.set_rounding(i == 0); m.set_plus_epsilon(a); m.floor(a); SASSERT(m.is_zero(a)); m.set_plus_epsilon(a); m.ceil(a); SASSERT(m.is_one(a)); m.set_minus_epsilon(a); m.floor(a); SASSERT(m.is_minus_one(a)); m.set_minus_epsilon(a); m.ceil(a); SASSERT(m.is_zero(a)); } m.set_minus_epsilon(a); m.set_minus_epsilon(b); SASSERT(!m.is_zero(a) && m.is_neg(a)); m.set(two, 2); m.round_to_minus_inf(); m.div(a, two, a); SASSERT(m.eq(a, b)); m.round_to_plus_inf(); m.div(a, two, a); SASSERT(m.is_zero(a)); m.round_to_plus_inf(); m.set_minus_epsilon(a); m.add(a, a, a); SASSERT(m.lt(a, b)); m.round_to_minus_inf(); m.set_minus_epsilon(a); m.add(a, a, a); SASSERT(m.lt(a, b)); m.set_minus_epsilon(a); m.sub(a, a, a); SASSERT(m.is_zero(a)); m.set_minus_epsilon(a); SASSERT(!m.is_plus_epsilon(a)); SASSERT(m.is_minus_epsilon(a)); m.neg(a); SASSERT(m.is_plus_epsilon(a)); SASSERT(!m.is_minus_epsilon(a)); } #if 0 static void tst_add_corner(unsigned prec) { mpff_manager m(prec); scoped_mpff a(m), b(m); } #endif static void tst_decimal(int64 n, uint64 d, bool to_plus_inf, unsigned prec, char const * expected, unsigned decimal_places = UINT_MAX) { mpff_manager m(prec); scoped_mpff a(m); m.set_rounding(to_plus_inf); m.set(a, n, d); m.display(std::cout, a); std::cout << std::endl; m.display_decimal(std::cout, a, decimal_places); std::cout << std::endl; std::ostringstream buffer; m.display_decimal(buffer, a, decimal_places); SASSERT(strcmp(expected, buffer.str().c_str()) == 0); } static void tst_decimal() { tst_decimal(1, 3, false, 2, "0.3333333333333333333152632971252415927665424533188343048095703125"); tst_decimal(1, 3, false, 4, "0.33333333333333333333333333333333333333235375470764809374335938621898146193515111203602326039874270691143465228378772735595703125"); tst_decimal(-1, 3, true, 2, "-0.3333333333333333333152632971252415927665424533188343048095703125"); tst_decimal(-1, 3, false, 2, "-0.33333333333333333334236835143737920361672877334058284759521484375"); tst_decimal(0, 1, false, 2, "0"); tst_decimal(2, 1, false, 2, "2"); tst_decimal(-3, 1, false, 2, "-3"); tst_decimal(INT64_MAX, 1, false, 2, "9223372036854775807"); tst_decimal(4, 5, false, 2, "0.79999999999999999995663191310057982263970188796520233154296875"); tst_decimal(4, 5, false, 2, "0.7999999999?", 10); tst_decimal(32, 5, true, 2, "6.4000000000000000000867361737988403547205962240695953369140625"); tst_decimal(32, 5, false, 2, "6.39999999999999999965305530480463858111761510372161865234375"); tst_decimal(-32, 5, false, 2, "-6.4000000000000000000867361737988403547205962240695953369140625"); tst_decimal(-32, 5, true, 2, "-6.39999999999999999965305530480463858111761510372161865234375"); } static void tst_prev_power_2(int64 n, uint64 d, unsigned expected) { mpff_manager m; scoped_mpff a(m); m.set(a, n, d); SASSERT(m.prev_power_of_two(a) == expected); } static void tst_prev_power_2() { tst_prev_power_2(-10, 1, 0); tst_prev_power_2(0, 1, 0); tst_prev_power_2(1, 1, 0); tst_prev_power_2(2, 1, 1); tst_prev_power_2(3, 1, 1); tst_prev_power_2(4, 1, 2); tst_prev_power_2(5, 1, 2); tst_prev_power_2(8, 1, 3); tst_prev_power_2(9, 1, 3); tst_prev_power_2(9, 2, 2); tst_prev_power_2(9, 4, 1); tst_prev_power_2(9, 5, 0); tst_prev_power_2((1ll << 60) + 1, 1, 60); tst_prev_power_2((1ll << 60), 1, 60); tst_prev_power_2((1ll << 60) - 1, 1, 59); tst_prev_power_2((1ll << 60), 3, 58); } static void tst_div(unsigned prec) { mpff_manager m(prec); scoped_mpff a(m), b(m), c(m); m.round_to_plus_inf(); m.set(a, 1); m.set(b, UINT64_MAX); m.div(a, b, c); m.display_raw(std::cout, a); std::cout << "\n"; m.display_raw(std::cout, b); std::cout << "\n"; std::cout << a << "/" << b << " <= " << c << "\n"; // 10...0 0...0 // 11111 } void tst_mpff() { disable_trace("mpff"); enable_trace("mpff_trace"); // enable_trace("mpff_bug"); // enable_trace("mpff_to_mpq"); // tst_div(2); tst_prev_power_2(); tst_decimal(); tst_div_core(679, 396, 279, 756, 2, 0); tst_limits(2); tst_limits(4); tst_sgn(2); tst_sgn(4); tst_sgn(8); tst_power(2); tst_power(4); tst_power(18); tst_capacity(2); tst_capacity(4); tst_capacity(8); tst_capacity(16); tst_set64(1000, 2); tst_set64(1000, 4); tst_set64(1000, 6); tst_bug2(); tst_sub(1000, 1024, 2); tst_sub(1000, 1024, 4); tst_div(1000, 1024, 2, true); tst_div(1000, 1024, 4, true); tst_mul(1000, 1024, 2); tst_mul(1000, 1024, 4); tst_add(1000, 1024, 2); tst_add(1000, 1024, 4); tst_sub(1000, UINT_MAX, 2); tst_sub(1000, UINT_MAX, 4); tst_div(1000, UINT_MAX, 2, true); tst_div(1000, UINT_MAX, 4, true); tst_mul(1000, UINT_MAX, 2); tst_mul(1000, UINT_MAX, 4); tst_add(1000, UINT_MAX, 2); tst_add(1000, UINT_MAX, 4); tst_bug2(); tst_bug(); tst_add_core(1,1, 1,1); tst_add_core(1,3, 2,3); tst1(); tst2(); tst3(); tst4(); tst5(); tst6(); tst7(); } z3-z3-4.4.1/src/test/mpfx.cpp000066400000000000000000000031241260446376700156510ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpfx.cpp Abstract: Multi precision fixed point numbers. Author: Leonardo de Moura (leonardo) 2012-09-19 Revision History: --*/ #include"mpfx.h" static void tst1() { mpfx_manager m; scoped_mpfx a(m), b(m), c(m); m.set(a, 1); m.set(b, 2); std::cout << a << " + " << b << " == " << (a+b) << "\n"; m.set(a, 5); m.set(c, 3); m.display_raw(std::cout, (a*a*b)/c); std::cout << "\n"; m.display_decimal(std::cout, (a*a*b)/c); std::cout << "\n"; m.display_decimal(std::cout, (a*a*b)/c, 10); std::cout << "\n"; m.round_to_plus_inf(); m.display_decimal(std::cout, (a*a*b)/c); std::cout << "\n"; m.set(a, -1, 4); m.display_decimal(std::cout, a); std::cout << "\n"; } static void tst_prev_power_2(int64 n, uint64 d, unsigned expected) { mpfx_manager m; scoped_mpfx a(m); m.set(a, n, d); SASSERT(m.prev_power_of_two(a) == expected); } static void tst_prev_power_2() { tst_prev_power_2(-10, 1, 0); tst_prev_power_2(0, 1, 0); tst_prev_power_2(1, 1, 0); tst_prev_power_2(2, 1, 1); tst_prev_power_2(3, 1, 1); tst_prev_power_2(4, 1, 2); tst_prev_power_2(5, 1, 2); tst_prev_power_2(8, 1, 3); tst_prev_power_2(9, 1, 3); tst_prev_power_2(9, 2, 2); tst_prev_power_2(9, 4, 1); tst_prev_power_2(9, 5, 0); tst_prev_power_2((1ll << 60) + 1, 1, 60); tst_prev_power_2((1ll << 60), 1, 60); tst_prev_power_2((1ll << 60) - 1, 1, 59); tst_prev_power_2((1ll << 60), 3, 58); } void tst_mpfx() { tst_prev_power_2(); tst1(); } z3-z3-4.4.1/src/test/mpq.cpp000066400000000000000000000066401260446376700155020ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpq.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-21. Revision History: --*/ #include"mpq.h" #include"rational.h" #include"timeit.h" static void tst0() { synch_mpq_manager m; mpq a, b; m.set(a, 2, 3); m.set(b, 4, 3); m.div(a, b, b); SASSERT(m.eq(b, m.mk_q(1, 2))); } static void tst1() { synch_mpq_manager m; char const * str = "1002034040050606089383838288182"; mpz v; m.set(v, str); std::cout << str << "\n" << m.to_string(v) << "\n"; mpz v2, v3; m.mul(v, m.mk_z(-2), v2); std::cout << "*-2 = \n" << m.to_string(v2) << "\n"; m.add(v, v2, v3); m.neg(v3); SASSERT(m.eq(v, v3)); SASSERT(m.le(v, v3)); SASSERT(m.ge(v, v3)); SASSERT(m.lt(v2, v)); SASSERT(m.le(v2, v)); SASSERT(m.gt(v, v2)); SASSERT(m.ge(v, v2)); SASSERT(m.neq(v, v2)); SASSERT(!m.neq(v, v3)); m.del(v); m.del(v2); m.del(v3); } #if 0 static void mk_random_num_str(unsigned buffer_sz, char * buffer) { unsigned div_pos; unsigned sz = (rand() % (buffer_sz-2)) + 1; if (rand() % 2 == 0) { // integer div_pos = sz + 1; } else { div_pos = rand() % sz; if (div_pos == 0) div_pos++; } SASSERT(sz < buffer_sz); for (unsigned i = 0; i < sz-1; i++) { if (i == div_pos && i < sz-2) { buffer[i] = '/'; i++; buffer[i] = '1' + (rand() % 9); } else { buffer[i] = '0' + (rand() % 10); } } buffer[sz-1] = 0; } #endif static void bug1() { synch_mpq_manager m; mpq a; mpq b; m.set(a, 2); m.set(b, 1, 2); m.inv(a, a); SASSERT(m.eq(a, b)); } static void bug2() { synch_mpq_manager m; mpq a; mpq b; m.set(a, -2); m.set(b, -1, 2); m.inv(a, a); SASSERT(m.eq(a, b)); } static void tst2() { unsynch_mpq_manager m; scoped_mpq a(m); m.set(a, 1, 3); std::cout << "1/3: "; m.display_decimal(std::cout, a, 10); std::cout << "\n1/4: "; m.set(a, 1, 4); m.display_decimal(std::cout, a, 10); std::cout << "\n"; } static void set_str_bug() { unsynch_mpq_manager m; scoped_mpq a(m); scoped_mpq b(m); m.set(a, "1.0"); std::cout << a << "\n"; m.set(b, 1); SASSERT(a == b); m.set(a, "1.1"); std::cout << a << "\n"; m.set(b, 11, 10); SASSERT(a == b); m.set(a, "1/3"); m.set(b, 1, 3); std::cout << a << "\n"; SASSERT(a == b); } static void tst_prev_power_2(int64 n, uint64 d, unsigned expected) { unsynch_mpq_manager m; scoped_mpq a(m); m.set(a, n, d); SASSERT(m.prev_power_of_two(a) == expected); } static void tst_prev_power_2() { tst_prev_power_2(-10, 1, 0); tst_prev_power_2(0, 1, 0); tst_prev_power_2(1, 1, 0); tst_prev_power_2(2, 1, 1); tst_prev_power_2(3, 1, 1); tst_prev_power_2(4, 1, 2); tst_prev_power_2(5, 1, 2); tst_prev_power_2(8, 1, 3); tst_prev_power_2(9, 1, 3); tst_prev_power_2(9, 2, 2); tst_prev_power_2(9, 4, 1); tst_prev_power_2(9, 5, 0); tst_prev_power_2((1ll << 60) + 1, 1, 60); tst_prev_power_2((1ll << 60), 1, 60); tst_prev_power_2((1ll << 60) - 1, 1, 59); tst_prev_power_2((1ll << 60), 3, 58); } void tst_mpq() { tst_prev_power_2(); set_str_bug(); bug2(); bug1(); tst0(); tst1(); tst2(); } z3-z3-4.4.1/src/test/mpz.cpp000066400000000000000000000377071260446376700155230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpz.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-18. Revision History: --*/ #include"mpz.h" #include"rational.h" #include"timeit.h" #include"scoped_numeral.h" static void tst1() { synch_mpz_manager m; char const * str = "1002034040050606089383838288182"; mpz v; m.set(v, str); std::cout << str << "\n" << m.to_string(v) << "\n"; mpz v2, v3; m.mul(v, m.mk_z(-2), v2); std::cout << "*-2 = \n" << m.to_string(v2) << "\n"; m.add(v, v2, v3); m.neg(v3); SASSERT(m.eq(v, v3)); SASSERT(m.le(v, v3)); SASSERT(m.ge(v, v3)); SASSERT(m.lt(v2, v)); SASSERT(m.le(v2, v)); SASSERT(m.gt(v, v2)); SASSERT(m.ge(v, v2)); SASSERT(m.neq(v, v2)); SASSERT(!m.neq(v, v3)); m.del(v); m.del(v2); m.del(v3); } static void tst2() { synch_mpz_manager m; mpz v1, v2, v3; m.set(v1, static_cast(UINT_MAX)); m.add(v1, m.mk_z(1), v2); m.mul(v2, v2, v3); std::cout << "v2:\n" << m.to_string(v2) << "\n"; std::cout << "v2*v2:\n" << m.to_string(v3) << "\n"; m.del(v1); m.del(v2); m.del(v3); } static void tst2b() { synch_mpz_manager m; mpz v1, v2, v3; m.set(v1, static_cast(UINT_MAX)); m.add(v1, m.mk_z(1), v2); m.mul(v2, v2, v3); std::cout << "v2:\n" << m.to_string(v2) << "\n"; std::cout << "v2*v2:\n" << m.to_string(v3) << "\n"; m.mul(v2, v2, v2); m.mul(v2, v2, v2); m.mul(v2, v2, v2); std::cout << "v2: " << m.to_string(v2) << "\n"; m.del(v1); m.del(v2); m.del(v3); } #if 0 static void mk_random_num_str(unsigned buffer_sz, char * buffer) { unsigned sz = (rand() % (buffer_sz-2)) + 1; SASSERT(sz < buffer_sz); for (unsigned i = 0; i < sz-1; i++) { buffer[i] = '0' + (rand() % 10); } if (rand() % 2 == 0) buffer[0] = '-'; buffer[sz-1] = 0; } #endif static void bug1() { synch_mpz_manager m; mpz v1; m.set(v1, "1002043949858757875676767675747473"); mpz v2; m.sub(v1, v1, v2); SASSERT(m.is_zero(v2)); m.del(v1); m.del(v2); } static void bug2() { synch_mpz_manager m; mpz v1, v2, vout; m.set(v1, "78160958900072552148646898355155840547214990303507678364419557473408815232466629049311995556059463490539128818602490221544425042127795"); m.set(v2, "403412298227858394469856607272647132163832860126054679347881638761723785858733108109249157334220127"); m.sub(v1, v2, vout); m.del(v1); m.del(v2); m.del(vout); } static void bug3() { synch_mpz_manager m; mpz v1, v2; m.set(v1, INT_MIN); m.set(v2, INT_MAX); m.add(v2, m.mk_z(1), v2); m.neg(v1); SASSERT(m.eq(v1, v2)); m.del(v1); m.del(v2); } static void bug4() { synch_mpz_manager m; mpz x, y; m.set(y, 4294967295ull); m.set(x, 4026531839ull); mpz result1; m.bitwise_or(x, y, result1); mpz result2; m.set(result2, x); m.bitwise_or(result2, y, result2); std::cout << m.to_string(result1) << " " << m.to_string(result2) << "\n"; SASSERT(m.eq(result1, result2)); m.del(x); m.del(y); m.del(result1); m.del(result2); } void tst_div2k(synch_mpz_manager & m, mpz const & v, unsigned k) { mpz x, y, two(2), pw; m.machine_div2k(v, k, x); m.power(two, k, pw); m.machine_div(v, pw, y); bool is_eq = m.eq(x, y); CTRACE("mpz_2k", !is_eq, tout << "div: " << m.to_string(v) << ", k: " << k << " r: " << m.to_string(x) << ", expected: " << m.to_string(y) << "\n";); SASSERT(is_eq); m.del(x); m.del(y); m.del(pw); } void tst_div2k(synch_mpz_manager & m, int v, unsigned k) { mpz x; m.set(x, v); tst_div2k(m, x, k); m.del(x); } void tst_div2k(synch_mpz_manager & m, char const * v, unsigned k) { mpz x; m.set(x, v); tst_div2k(m, x, k); m.del(x); } void tst_mul2k(synch_mpz_manager & m, mpz const & v, unsigned k) { mpz x, y, two(2), pw; m.mul2k(v, k, x); m.power(two, k, pw); m.mul(v, pw, y); bool is_eq = m.eq(x, y); CTRACE("mpz_2k", !is_eq, tout << "mul: " << m.to_string(v) << ", k: " << k << " r: " << m.to_string(x) << ", expected: " << m.to_string(y) << "\n";); SASSERT(is_eq); m.del(x); m.del(y); m.del(pw); } void tst_mul2k(synch_mpz_manager & m, int v, unsigned k) { mpz x; m.set(x, v); tst_mul2k(m, x, k); m.del(x); } void tst_mul2k(synch_mpz_manager & m, char const * v, unsigned k) { mpz x; m.set(x, v); tst_mul2k(m, x, k); m.del(x); } static void tst_2k() { synch_mpz_manager m; tst_mul2k(m, 120, 32); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 22); tst_div2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 22); tst_div2k(m, 3, 1); tst_div2k(m, 3, 2); tst_div2k(m, 3, 0); tst_div2k(m, 120, 32); tst_div2k(m, 120, 0); tst_div2k(m, 81, 2); tst_div2k(m, -3, 1); tst_div2k(m, -3, 2); tst_div2k(m, -3, 0); tst_div2k(m, -102, 4); tst_div2k(m, 0, 3); tst_div2k(m, 0, 1000); tst_div2k(m, 7, 10000); tst_div2k(m, -7, 1000); tst_div2k(m, -7, 2); tst_div2k(m, "1029384848584832828327176162636436484", 4); tst_div2k(m, "1029384848584832828327176162636436484", 100); tst_div2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 1); tst_div2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 0); tst_div2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 4); tst_div2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 7); tst_div2k(m, "102938484858483282832717616263643648433838737661626264364583983298239291919", 100); tst_div2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 100); tst_div2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 177); tst_div2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 77); tst_div2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 32); tst_div2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 64); tst_div2k(m, "-11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 64); tst_div2k(m, "-11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 128); tst_div2k(m, "-1092983874757371817626399990000000", 100); tst_div2k(m, "-109298387475737181762639999000000231", 8); tst_div2k(m, "-109298387475737181762639999000000231", 16); tst_div2k(m, "-109298387475737181762639999000000231", 17); tst_div2k(m, "-109298387475737181762639999000000231", 32); tst_mul2k(m, 3, 1); tst_mul2k(m, 3, 2); tst_mul2k(m, 3, 0); tst_mul2k(m, 120, 32); tst_mul2k(m, 120, 0); tst_mul2k(m, 81, 2); tst_mul2k(m, -3, 1); tst_mul2k(m, -3, 2); tst_mul2k(m, -3, 0); tst_mul2k(m, -102, 4); tst_mul2k(m, 0, 3); tst_mul2k(m, 0, 1000); tst_mul2k(m, 7, 10000); tst_mul2k(m, -7, 1000000); tst_mul2k(m, -7, 2); tst_mul2k(m, "1029384848584832828327176162636436484", 4); tst_mul2k(m, "1029384848584832828327176162636436484", 100); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 1); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 0); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 4); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 22); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 7); tst_mul2k(m, "102938484858483282832717616263643648433838737661626264364583983298239291919", 100); tst_mul2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 100); tst_mul2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 177); tst_mul2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 77); tst_mul2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 32); tst_mul2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 64); tst_mul2k(m, "-11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 64); tst_mul2k(m, "-11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 128); tst_mul2k(m, "-1092983874757371817626399990000000", 100); tst_mul2k(m, "-109298387475737181762639999000000231", 8); tst_mul2k(m, "-109298387475737181762639999000000231", 16); tst_mul2k(m, "-109298387475737181762639999000000231", 17); tst_mul2k(m, "-109298387475737181762639999000000231", 32); } void tst_int_min_bug() { synch_mpz_manager m; mpz intmin(INT_MIN); mpz big; mpz expected; mpz r; m.set(big, UINT64_MAX); m.set(expected, "18446744075857035263"); m.sub(big, intmin, r); std::cout << "r: " << m.to_string(r) << "\nexpected: " << m.to_string(expected) << "\n"; SASSERT(m.eq(r, expected)); m.del(intmin); m.del(big); m.del(expected); m.del(r); } void tst_scoped() { synch_mpz_manager m; scoped_synch_mpz a(m); scoped_synch_mpz b(m); m.set(a, 1000231); m.set(b, "102928187172727273"); std::cout << "a: " << m.to_string(a) << "\n"; std::cout << "b: " << m.to_string(b) << "\n"; m.mul(a, b, b); std::cout << "b: " << m.to_string(b) << "\n"; } #define NUM_PRIMES 168 unsigned g_primes[NUM_PRIMES] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997 }; // Return a big number by multipling powers of the first NUM_PRIMES. // - ratio: rand() % ratio == 0 is used to decide whether a specific prime will be included or not. // - max_pw: if condition above is satisfied, then we use (rand() % max_pw) + 1 as the power. void mk_big_num(unsynch_mpz_manager & m, unsigned ratio, unsigned max_pw, mpz & r) { scoped_mpz tmp(m); m.set(r, 1); for (unsigned i = 0; i < NUM_PRIMES; i++) { if ((rand() % ratio) == 0) { m.power(mpz(g_primes[i]), (rand() % max_pw) + 1, tmp); m.mul(r, tmp, r); } } if ((rand() % 2) == 0) m.neg(r); } void slow_gcd(unsynch_mpz_manager & m, mpz const & a, mpz const & b, mpz & c) { scoped_mpz tmp1(m); scoped_mpz tmp2(m); scoped_mpz aux(m); m.set(tmp1, a); m.set(tmp2, b); m.abs(tmp1); m.abs(tmp2); if (m.le(tmp1, tmp2)) m.swap(tmp1, tmp2); if (m.is_zero(tmp2)) { m.set(c, tmp1); return; } for(;;) { m.rem(tmp1, tmp2, aux); if (m.is_zero(aux)) { m.set(c, tmp2); return; } m.set(tmp1, tmp2); m.set(tmp2, aux); } } void rand_tst_gcd(unsigned num, unsigned ratio, unsigned pw) { unsynch_mpz_manager m; scoped_mpz a(m); scoped_mpz b(m); scoped_mpz g1(m); scoped_mpz g2(m); for (unsigned i = 0; i < num; i++) { mk_big_num(m, ratio, pw, a); mk_big_num(m, ratio, pw, b); slow_gcd(m, a, b, g1); m.gcd(a, b, g2); std::cout << "a: " << m.to_string(a) << "\n"; std::cout << "b: " << m.to_string(b) << "\n"; std::cout << "g1: " << m.to_string(g1) << "\n"; std::cout << "g2: " << m.to_string(g2) << "\n"; std::cout << "\n"; if (!m.eq(g1, g2)) { std::cout << "\n\nBUG\n\n"; UNREACHABLE(); } } } void tst_gcd() { unsynch_mpz_manager m; scoped_mpz a(m); scoped_mpz b(m); scoped_mpz g(m); m.set(a, "125141154046000416200229049397707776"); m.set(b, "67062117506072642958648904906464"); m.gcd(a, b, g); std::cout << "g: " << m.to_string(g) << "\n"; m.set(a, "664877781119188360263909568610284290708591605105963082581413244598320881431041311468785283029437655134762231312337924555674674176"); m.set(b, "21691055098083293041646678174999125628463716392747356050705870375852789453851926624107939885328471215366825649627326658281728580399051770334114658498352848410853519374962852431831492868108719406669605254329669417322836882756478295264"); for (unsigned i = 0; i < 50000; i++) { m.del(g); m.gcd(a, b, g); // slow_gcd(m, a, b, g); } std::cout << "g: " << m.to_string(g) << "\n"; } void tst_log2(unsynch_mpz_manager & m, mpz const & a) { scoped_mpz b(m); unsigned k = m.log2(a); m.power(mpz(2), k, b); SASSERT(m.is_zero(a) || m.le(b, a)); m.power(mpz(2), k+1, b); SASSERT(m.le(a, b)); scoped_mpz neg_a(m); m.set(neg_a, a); m.neg(neg_a); k = m.mlog2(neg_a); m.power(mpz(2), k, b); m.neg(b); SASSERT(m.is_zero(neg_a) || m.le(neg_a, b)); m.power(mpz(2), k+1, b); m.neg(b); SASSERT(m.le(b, neg_a)); } void tst_log2() { unsynch_mpz_manager m; for (unsigned i = 0; i <= 64; i++) std::cout << "log2(" << i << "): " << m.log2(mpz(i)) << "\n"; for (unsigned i = 0; i < 1000; i++) tst_log2(m, mpz(i)); scoped_mpz a(m); m.set(a, "1029489380487098723984579237"); for (unsigned i = 0; i < 1000; i++) { m.inc(a); tst_log2(m, a); } } void tst_root() { unsynch_mpz_manager m; scoped_mpz a(m); m.set(a, 213); VERIFY(!m.root(a, 5)); std::cout << "213^{1/5}: " << a << "\n"; SASSERT(m.eq(a, mpz(3))); m.set(a, -213); VERIFY(!m.root(a, 5)); std::cout << "-213^{1/5}: " << a << "\n"; SASSERT(m.eq(a, mpz(-2))); m.set(a, 0); VERIFY(m.root(a, 3)); SASSERT(m.is_zero(a)); m.set(a, 8); VERIFY(m.root(a, 3)); SASSERT(m.eq(a, mpz(2))); m.set(a, -8); VERIFY(m.root(a, 3)); SASSERT(m.eq(a, mpz(-2))); } void tst_gcd_bug() { unsynch_mpz_manager m; scoped_mpz a(m), b(m), g(m); m.set(a, INT_MIN); m.set(b, INT_MIN); m.gcd(a, b, g); std::cout << "g: " << g << "\n"; } void tst_div2k_bug() { unsynch_mpz_manager m; scoped_mpz a(m), b(m), g(m); m.set(a, UINT_MAX); m.machine_div2k(a, 32, b); std::cout << "a: " << a << ", b: " << b << "\n"; } static void tst5() { unsynch_mpz_manager m; scoped_mpz a(m); a = 1; std::cout << "1 -> " << m.log2(a) << "\n"; a = 5; std::cout << "5 -> " << m.log2(a) << "\n"; a = 16; std::cout << "16 -> " << m.log2(a) << "\n"; a = INT_MAX; std::cout << "INT_MAX -> " << m.log2(a) << "\n"; a = INT_MAX/4; std::cout << "INT_MAX/4 -> " << m.log2(a) << "\n"; } static void tst_pw2() { unsynch_mpz_manager m; scoped_mpz a(m); for (unsigned i = 0; i < 128; i++) { m.power(mpz(2), i, a); std::cout << "i: " << i << ", a: " << a << std::endl; } } void tst_mpz() { disable_trace("mpz"); enable_trace("mpz_2k"); tst_pw2(); tst5(); tst_div2k_bug(); rand_tst_gcd(50, 3, 2); tst_2k(); tst_gcd_bug(); tst_root(); tst_log2(); // tst_gcd(); tst_scoped(); tst_int_min_bug(); bug4(); bug3(); bug1(); bug2(); tst1(); tst2(); tst2b(); } z3-z3-4.4.1/src/test/nlarith_util.cpp000066400000000000000000000027531260446376700174040ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "nlarith_util.h" #include "arith_decl_plugin.h" #include "ast_pp.h" #include "reg_decl_plugins.h" void tst_nlarith_util() { ast_manager M; reg_decl_plugins(M); arith_util A(M); sort_ref R(A.mk_real(), M); app_ref one(A.mk_numeral(rational(1), false), M); app_ref two(A.mk_numeral(rational(2), false), M); app_ref ten(A.mk_numeral(rational(10), false), M); app_ref x(M.mk_const(symbol("x"), R), M); app_ref y(M.mk_const(symbol("y"), R), M); app_ref z(M.mk_const(symbol("z"), R), M); expr_ref p1(A.mk_le(A.mk_add(A.mk_mul(x,x,y), y), one), M); expr_ref p2(A.mk_le(A.mk_add(A.mk_mul(x,x,y), z), one), M); enable_trace("nlarith"); nlarith::util u(M); nlarith::branch_conditions bc(M); expr_ref_vector preds(M), branches(M); vector substs; expr* lits[2] = { p1.get(), p2.get() }; if (!u.create_branches(x.get(), 2, lits, bc)) { std::cout << "Failed to create branches\n"; return; } for (unsigned i = 0; i < preds.size(); ++i) { std::cout << "Pred: " << mk_pp(preds[i].get(), M) << "\n"; } for (unsigned i = 0; i < branches.size(); ++i) { std::cout << "Branch:\n" << mk_pp(branches[i].get(), M) << "\n"; for (unsigned j = 0; j < substs[i].size(); ++j) { std::cout << mk_pp(preds[j].get(), M) << " |-> " << mk_pp(substs[i][j].get(), M) << "\n"; } } } z3-z3-4.4.1/src/test/nlsat.cpp000066400000000000000000000233061260446376700160240ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat.cpp Abstract: nlsat procedure Author: Leonardo (leonardo) 2012-01-09 Notes: --*/ #include"nlsat_assignment.h" #include"nlsat_interval_set.h" #include"nlsat_evaluator.h" #include"nlsat_solver.h" #include"util.h" nlsat::interval_set_ref tst_interval(nlsat::interval_set_ref const & s1, nlsat::interval_set_ref const & s2, unsigned expected_num_intervals, bool check_num_intervals = true) { nlsat::interval_set_manager & ism = s1.m(); nlsat::interval_set_ref r(ism); std::cout << "------------------\n"; std::cout << "s1: " << s1 << "\n"; std::cout << "s2: " << s2 << "\n"; r = ism.mk_union(s1, s2); std::cout << "union(s1, s2): " << r << std::endl; SASSERT(!check_num_intervals || ism.num_intervals(r) == expected_num_intervals); SASSERT(ism.subset(s1, r)); SASSERT(ism.subset(s2, r)); if (ism.set_eq(s1, s2)) { SASSERT(ism.set_eq(s1, r)); SASSERT(ism.set_eq(s2, r)); } else { SASSERT(ism.subset(s1, s2) || !ism.subset(r, s2)); SASSERT(ism.subset(s2, s1) || !ism.subset(r, s1)); } nlsat::interval_set_ref r2(ism); r2 = ism.mk_union(s2, s1); SASSERT(ism.set_eq(r, r2)); anum zero; nlsat::interval_set_ref full(ism); nlsat::literal dummy(131, false); full = ism.mk(true, true, zero, true, true, zero, dummy); SASSERT(ism.set_eq(r, full) == ism.is_full(r)); return r; } static void tst3() { enable_trace("nlsat_interval"); unsynch_mpq_manager qm; anum_manager am(qm); small_object_allocator allocator; nlsat::interval_set_manager ism(am, allocator); scoped_anum sqrt2(am), m_sqrt2(am), two(am), m_two(am), three(am), one(am), zero(am); am.set(two, 2); am.set(m_two, -2); am.set(one, 1); am.root(two, 2, sqrt2); am.set(m_sqrt2, sqrt2); am.neg(m_sqrt2); am.set(three, 3); nlsat::literal p1(1, false); nlsat::literal p2(2, false); nlsat::literal p3(3, false); nlsat::literal p4(4, false); nlsat::literal np2(2, true); nlsat::interval_set_ref s1(ism), s2(ism), s3(ism), s4(ism); s1 = ism.mk_empty(); std::cout << "s1: " << s1 << "\n"; s2 = ism.mk(true, true, zero, false, false, sqrt2, np2); std::cout << "s2: " << s2 << "\n"; s3 = ism.mk(false, false, zero, false, false, two, p1); std::cout << "s3: " << s3 << "\n"; s4 = ism.mk_union(s2, s3); std::cout << "s4: " << s4 << "\n"; // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, zero, false, false, two, p1); s2 = ism.mk(false, false, zero, false, false, two, p2); tst_interval(s1, s2, 1); // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, zero, false, false, two, p1); s2 = ism.mk(false, false, m_sqrt2, false, false, one, p2); s3 = ism.mk_union(s1, s2); tst_interval(s1, s2, 2); // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, m_sqrt2, false, false, one, p1); s2 = ism.mk(false, false, zero, false, false, two, p2); tst_interval(s1, s2, 2); // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, m_sqrt2, false, false, one, p1); s2 = ism.mk(false, false, two, false, false, three, p2); tst_interval(s1, s2, 2); // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, m_sqrt2, false, false, three, p1); s2 = ism.mk(false, false, zero, false, false, two, p2); tst_interval(s1, s2, 1); // Case // s1: [ ... ] // s2: [ ... ] [ ... ] s1 = ism.mk(false, false, m_two, false, false, two, p1); s2 = ism.mk(false, false, m_sqrt2, false, false, zero, p2); s3 = ism.mk(false, false, one, false, false, three, p2); s2 = ism.mk_union(s2, s3); tst_interval(s1, s2, 2); // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, m_two, false, false, two, p1); s2 = ism.mk(false, false, two, false, false, three, p2); tst_interval(s1, s2, 2); s2 = ism.mk(true, false, two, false, false, three, p2); tst_interval(s1, s2, 2); s2 = ism.mk(true, false, two, false, false, three, p1); tst_interval(s1, s2, 1); s1 = ism.mk(false, false, m_two, true, false, two, p1); tst_interval(s1, s2, 2); s1 = ism.mk(false, false, two, false, false, two, p1); s2 = ism.mk(false, false, two, false, false, three, p2); tst_interval(s1, s2, 1); // Case // s1: [ ... ] [ ... ] // s2: [ .. ] [ ... ] [ ... ] s1 = ism.mk(false, false, m_two, false, false, zero, p1); s3 = ism.mk(false, false, one, false, false, three, p1); s1 = ism.mk_union(s1, s3); s2 = ism.mk(true, true, zero, false, false, m_sqrt2, p2); tst_interval(s1, s2, 3); s3 = ism.mk(false, false, one, false, false, sqrt2, p2); s2 = ism.mk_union(s2, s3); s3 = ism.mk(false, false, two, true, true, zero, p2); s2 = ism.mk_union(s2, s3); tst_interval(s1, s2, 4); // Case s1 = ism.mk(true, true, zero, false, false, one, p1); s2 = ism.mk(true, false, one, true, true, zero, p2); tst_interval(s1, s2, 2); s2 = ism.mk(true, false, one, false, false, two, p2); s3 = ism.mk(false, false, two, true, true, zero, p1); s2 = ism.mk_union(s2, s3); tst_interval(s1, s2, 3); } static nlsat::interval_set_ref mk_random(nlsat::interval_set_manager & ism, anum_manager & am, int range, int space, int tries, bool minus_inf, bool plus_inf, nlsat::literal lit) { static random_gen gen; SASSERT(range > 0); SASSERT(space > 0); nlsat::interval_set_ref r(ism), curr(ism); scoped_anum lower(am); scoped_anum upper(am); int prev = -range + (gen() % (space*4)); if (gen() % 3 == 0 && minus_inf) { int next = prev + (gen() % space); bool open = gen() % 2 == 0; am.set(upper, next); r = ism.mk(true, true, lower, open, false, upper, lit); prev = next; } for (int i = 0; i < tries; i++) { int l = prev + (gen() % space); int u = l + (gen() % space); bool lower_open = gen() % 2 == 0; bool upper_open = gen() % 2 == 0; if ((lower_open || upper_open) && l == u) u++; am.set(lower, l); am.set(upper, u); curr = ism.mk(lower_open, false, lower, upper_open, false, upper, lit); r = ism.mk_union(r, curr); prev = u; } if (gen() % 3 == 0 && plus_inf) { int next = prev + (gen() % space); bool open = gen() % 2 == 0; am.set(lower, next); curr = ism.mk(open, false, lower, true, true, upper, lit); r = ism.mk_union(r, curr); } return r; } static void check_subset_result(nlsat::interval_set_ref const & s1, nlsat::interval_set_ref const & s2, nlsat::interval_set_ref const & r, nlsat::literal l1, nlsat::literal l2) { nlsat::interval_set_manager ism(s1.m()); nlsat::interval_set_ref tmp(ism); unsigned num = ism.num_intervals(r); nlsat::literal_vector lits; ism.get_justifications(r, lits); SASSERT(lits.size() <= 2); for (unsigned i = 0; i < num; i++) { tmp = ism.get_interval(r, i); ism.get_justifications(tmp, lits); SASSERT(lits.size() == 1); if (lits[0] == l1) { SASSERT(ism.subset(tmp, s1)); } else { SASSERT(lits[0] == l2); SASSERT(ism.subset(tmp, s2)); } } } static void tst4() { enable_trace("nlsat_interval"); unsynch_mpq_manager qm; anum_manager am(qm); small_object_allocator allocator; nlsat::interval_set_manager ism(am, allocator); nlsat::interval_set_ref s1(ism), s2(ism), r(ism); nlsat::literal l1(1, false); nlsat::literal l2(2, false); for (unsigned i = 0; i < 100; i++) { s1 = mk_random(ism, am, 20, 3, 10, true, true, l1); s2 = mk_random(ism, am, 20, 3, 10, true, true, l2); r = tst_interval(s1, s2, 0, false); check_subset_result(s1, s2, r, l1, l2); } for (unsigned i = 0; i < 100; i++) { s1 = mk_random(ism, am, 200, 100, 20, true, true, l1); s2 = mk_random(ism, am, 200, 100, 20, true, true, l2); r = tst_interval(s1, s2, 0, false); check_subset_result(s1, s2, r, l1, l2); } } static void tst5() { params_ref ps; reslimit rlim; nlsat::solver s(rlim, ps); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); small_object_allocator allocator; nlsat::interval_set_manager ism(am, allocator); nlsat::evaluator ev(as, pm, allocator); nlsat::var x0, x1; x0 = pm.mk_var(); x1 = pm.mk_var(); polynomial_ref p(pm); polynomial_ref _x0(pm), _x1(pm); _x0 = pm.mk_polynomial(x0); _x1 = pm.mk_polynomial(x1); p = (_x0^2) + (_x1^2) - 2; nlsat::poly * _p[1] = { p.get() }; bool is_even[1] = { false }; nlsat::bool_var b = s.mk_ineq_atom(nlsat::atom::GT, 1, _p, is_even); nlsat::atom * a = s.bool_var2atom(b); SASSERT(a != 0); nlsat::interval_set_ref i(ism); scoped_anum zero(am); am.set(zero, 0); as.set(0, zero); i = ev.infeasible_intervals(a, true); std::cout << "1) " << i << "\n"; as.set(1, zero); i = ev.infeasible_intervals(a, true); std::cout << "2) " << i << "\n"; } void tst_nlsat() { tst5(); tst4(); tst3(); } z3-z3-4.4.1/src/test/no_overflow.cpp000066400000000000000000000553341260446376700172500ustar00rootroot00000000000000/*++ Copyright (c) 2009 Microsoft Corporation Module Name: no_overflow.cpp Abstract: Test non-overflowing checks for arithmetic. Author: Yannick Moy (t-yanmoy) 2009-02-17. Revision History: --*/ #ifdef _WINDOWS #include "z3.h" #include "trace.h" #include "rational.h" #define TEST(TEST_NAME, TEST_OUTCOME, NEG_TEST_OUTCOME) \ do { \ if (TEST_NAME != NULL) \ { \ Z3_push(ctx); \ Z3_assert_cnstr(ctx, TEST_NAME); \ SASSERT(Z3_check(ctx) == TEST_OUTCOME); \ Z3_pop(ctx, 1); \ \ Z3_push(ctx); \ Z3_assert_cnstr(ctx, Z3_mk_not(ctx, TEST_NAME)); \ SASSERT(Z3_check(ctx) == NEG_TEST_OUTCOME); \ Z3_pop(ctx, 1); \ } \ } while (0) #define TEST_NO_OVERFLOW TEST(test_ovfl, Z3_L_TRUE, Z3_L_FALSE) #define TEST_OVERFLOW TEST(test_ovfl, Z3_L_FALSE, Z3_L_TRUE) #define TEST_NO_OVERFLOW_IFF(COND) \ do { \ if (COND) { \ TEST_NO_OVERFLOW; \ } \ else { \ TEST_OVERFLOW; \ } \ } while (0) #define TEST_NO_UNDERFLOW TEST(test_udfl, Z3_L_TRUE, Z3_L_FALSE) #define TEST_UNDERFLOW TEST(test_udfl, Z3_L_FALSE, Z3_L_TRUE) #define TEST_NO_UNDERFLOW_IFF(COND) \ do { \ if (COND) { \ TEST_NO_UNDERFLOW; \ } \ else { \ TEST_UNDERFLOW; \ } \ } while (0) #define TEST_ANY(TEST_NAME) \ do { \ Z3_push(ctx); \ Z3_assert_cnstr(ctx, TEST_NAME); \ Z3_check(ctx); /* ignore result of check */ \ Z3_pop(ctx, 1); \ } while (0) #define TEST_ANY_OVERFLOW TEST_ANY(test_ovfl) #define TEST_ANY_UNDERFLOW TEST_ANY(test_udfl) Z3_ast mk_min(Z3_context ctx, Z3_sort bv, bool is_signed) { unsigned bvsize = Z3_get_bv_sort_size(ctx, bv); if (! is_signed) return Z3_mk_numeral(ctx, "0", bv); unsigned sz = bvsize - 1; rational min_bound = power(rational(2), sz); min_bound.neg(); return Z3_mk_numeral(ctx, min_bound.to_string().c_str(), bv); } Z3_ast mk_max(Z3_context ctx, Z3_sort bv, bool is_signed) { unsigned bvsize = Z3_get_bv_sort_size(ctx, bv); unsigned sz = is_signed ? bvsize - 1 : bvsize; rational max_bound = power(rational(2), sz); --max_bound; return Z3_mk_numeral(ctx, max_bound.to_string().c_str(), bv); } void test_add(unsigned bvsize, bool is_signed) { TRACE("no_overflow", tout << "test_add: bvsize = " << bvsize << ", is_signed = " << is_signed << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_ast min = mk_min(ctx, bv, is_signed); Z3_ast max = mk_max(ctx, bv, is_signed); Z3_ast t1; Z3_ast t2; Z3_ast test_ovfl; Z3_ast test_udfl; t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); test_ovfl = Z3_mk_bvadd_no_overflow(ctx, t1, t2, is_signed); test_udfl = is_signed ? Z3_mk_bvadd_no_underflow(ctx, t1, t2) : NULL; Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW_IFF(bvsize == 1 && is_signed); TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(! (bvsize == 1 && is_signed)); Z3_pop(ctx, 1); if (is_signed) { Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; TEST_UNDERFLOW; Z3_pop(ctx, 1); } Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(! is_signed); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, max)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW_IFF(bvsize == 1 && is_signed); TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_del_config(cfg); Z3_del_context(ctx); } void test_sub(unsigned bvsize, bool is_signed) { TRACE("no_overflow", tout << "test_sub: bvsize = " << bvsize << ", is_signed = " << is_signed << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_ast min = mk_min(ctx, bv, is_signed); Z3_ast max = mk_max(ctx, bv, is_signed); Z3_ast t1; Z3_ast t2; Z3_ast test_ovfl; Z3_ast test_udfl; t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); test_ovfl = is_signed ? Z3_mk_bvsub_no_overflow(ctx, t1, t2) : NULL; test_udfl = Z3_mk_bvsub_no_underflow(ctx, t1, t2, is_signed); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(! (bvsize == 1 && is_signed)); TEST_NO_UNDERFLOW_IFF(is_signed); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(is_signed); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(bvsize == 1 || is_signed); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, max)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(! (bvsize == 1 && is_signed)); TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW_IFF(! (bvsize != 1 && is_signed)); TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(bvsize == 1 && is_signed); Z3_pop(ctx, 1); if (is_signed) { Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "-1", bv))); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); } Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(bvsize == 1 && is_signed); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, max)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW_IFF(! is_signed); TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, max)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_del_config(cfg); Z3_del_context(ctx); } void test_neg(unsigned bvsize) { TRACE("no_overflow", tout << "test_neg: bvsize = " << bvsize << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_ast min = mk_min(ctx, bv, /* is_signed = */ true); Z3_ast max = mk_max(ctx, bv, /* is_signed = */ true); Z3_ast t1; Z3_ast test_ovfl; t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); test_ovfl = Z3_mk_bvneg_no_overflow(ctx, t1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); TEST_NO_OVERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(bvsize != 1); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); TEST_NO_OVERFLOW_IFF(bvsize != 1); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, max)); TEST_NO_OVERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); TEST_OVERFLOW; Z3_pop(ctx, 1); Z3_del_config(cfg); Z3_del_context(ctx); } void test_mul(unsigned bvsize, bool is_signed) { TRACE("no_overflow", tout << "test_mul: bvsize = " << bvsize << ", is_signed = " << is_signed << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_ast min = mk_min(ctx, bv, is_signed); Z3_ast max = mk_max(ctx, bv, is_signed); Z3_ast t1; Z3_ast t2; Z3_ast test_ovfl; Z3_ast test_udfl; t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); test_ovfl = Z3_mk_bvmul_no_overflow(ctx, t1, t2, is_signed); test_udfl = is_signed ? Z3_mk_bvmul_no_underflow(ctx, t1, t2) : NULL; Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(! (bvsize == 1 && is_signed)); TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "0", bv))); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, max)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW_IFF(! (bvsize == 1 && is_signed)); TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); if (is_signed) { Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "-1", bv))); TEST_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); } Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW_IFF(! is_signed); TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(! (bvsize != 1 && is_signed)); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, max)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW_IFF(bvsize == 1); TEST_NO_UNDERFLOW; Z3_pop(ctx, 1); Z3_del_config(cfg); Z3_del_context(ctx); } void test_div(unsigned bvsize) { TRACE("no_overflow", tout << "test_div: bvsize = " << bvsize << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_ast min = mk_min(ctx, bv, /* is_signed = */ true); Z3_ast max = mk_max(ctx, bv, /* is_signed = */ true); Z3_ast t1; Z3_ast t2; Z3_ast test_ovfl; t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); test_ovfl = Z3_mk_bvsdiv_no_overflow(ctx, t1, t2); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(bvsize != 1); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "-1", bv))); TEST_NO_OVERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "0", bv))); TEST_ANY_OVERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "0", bv))); TEST_ANY_OVERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "0", bv))); TEST_ANY_OVERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "-1", bv))); TEST_OVERFLOW; Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(bvsize != 1); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, min)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW_IFF(bvsize != 1); Z3_pop(ctx, 1); Z3_push(ctx); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, max)); Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; Z3_pop(ctx, 1); Z3_del_config(cfg); Z3_del_context(ctx); } typedef Z3_ast (Z3_API *NO_OVFL_ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2, Z3_bool is_signed); typedef Z3_ast (Z3_API *ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2); typedef enum { OVFL_FUNC, UDFL_FUNC } overflow_type; typedef struct { std::string name; NO_OVFL_ARITH_FUNC no_overflow_func; ARITH_FUNC func; overflow_type type; int extsize; // negative size indicates size of arguments should be doubled bool do_unsigned; bool non_zero; // second argument should not be null (for division) bool sign_compar; // whether signed comparison should be used even for unsigned operation } Equivalence_params; Z3_ast Z3_API Z3_mk_bvsdiv_no_overflow_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2, Z3_bool is_signed) { return Z3_mk_bvsdiv_no_overflow(ctx, t1, t2); } Z3_ast Z3_API Z3_mk_bvneg_no_overflow_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2, Z3_bool is_signed) { return Z3_mk_bvneg_no_overflow(ctx, t1); } Z3_ast Z3_API Z3_mk_bvneg_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2) { return Z3_mk_bvneg(ctx, t1); } Z3_ast Z3_API Z3_mk_bvadd_no_underflow_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2, Z3_bool is_signed) { return Z3_mk_bvadd_no_underflow(ctx, t1, t2); } Z3_ast Z3_API Z3_mk_bvsub_no_overflow_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2, Z3_bool is_signed) { return Z3_mk_bvsub_no_overflow(ctx, t1, t2); } Z3_ast Z3_API Z3_mk_bvmul_no_underflow_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2, Z3_bool is_signed) { return Z3_mk_bvmul_no_underflow(ctx, t1, t2); } void test_equiv(Equivalence_params params, unsigned bvsize, bool is_signed) { TRACE("no_overflow", tout << "test_" << params.name << "_equiv: bvsize = " << bvsize << ", is_signed = " << is_signed << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_ast min = mk_min(ctx, bv, is_signed); Z3_ast max = mk_max(ctx, bv, is_signed); Z3_ast t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); Z3_ast t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); Z3_ast real_test = (*params.no_overflow_func)(ctx, t1, t2, is_signed); Z3_ast cond = NULL; if (params.non_zero) { cond = Z3_mk_not(ctx, Z3_mk_eq(ctx, t2, Z3_mk_int(ctx, 0, bv))); } unsigned extsize = params.extsize < 0 ? bvsize : params.extsize; if (is_signed) { min = Z3_mk_sign_ext(ctx, extsize, min); max = Z3_mk_sign_ext(ctx, extsize, max); t1 = Z3_mk_sign_ext(ctx, extsize, t1); t2 = Z3_mk_sign_ext(ctx, extsize, t2); } else { min = Z3_mk_zero_ext(ctx, extsize, min); max = Z3_mk_zero_ext(ctx, extsize, max); t1 = Z3_mk_zero_ext(ctx, extsize, t1); t2 = Z3_mk_zero_ext(ctx, extsize, t2); } Z3_ast r = (*params.func)(ctx, t1, t2); Z3_ast check; if (is_signed) { check = (params.type == UDFL_FUNC) ? Z3_mk_bvsle(ctx, min, r) : Z3_mk_bvsle(ctx, r, max); } else { if (params.sign_compar) { // check with signed comparison for subtraction of unsigned check = (params.type == UDFL_FUNC) ? Z3_mk_bvsle(ctx, min, r) : Z3_mk_bvsle(ctx, r, max); } else { check = (params.type == UDFL_FUNC) ? Z3_mk_bvule(ctx, min, r) : Z3_mk_bvule(ctx, r, max); } } Z3_push(ctx); Z3_ast equiv = Z3_mk_iff(ctx, real_test, check); if (cond != NULL) { equiv = Z3_mk_implies(ctx, cond, equiv); } Z3_assert_cnstr(ctx, Z3_mk_not(ctx, equiv)); SASSERT(Z3_check(ctx) == Z3_L_FALSE); Z3_pop(ctx, 1); Z3_del_config(cfg); Z3_del_context(ctx); } //void debug_mul() { // // unsigned bvsize = 2; // bool is_signed = true; // // Z3_config cfg = Z3_mk_config(); // Z3_context ctx = Z3_mk_context(cfg); // Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); // // Z3_ast min = mk_min(ctx, bv, is_signed); // Z3_ast max = mk_max(ctx, bv, is_signed); // Z3_ast t1; // Z3_ast t2; // Z3_ast test_udfl; // // t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); // t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); // test_udfl = Z3_mk_bvmul_no_underflow(ctx, t1, t2); // // Z3_push(ctx); // Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); // Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); // //TEST_NO_UNDERFLOW; // Z3_assert_cnstr(ctx, test_udfl); // SASSERT(Z3_check(ctx) == Z3_TRUE); // Z3_pop(ctx, 1); // // Z3_del_config(cfg); // Z3_del_context(ctx); //} #define BVSIZES 4 #define TESTNUM 3 #define EQUIV_BVSIZES 4 #define EQUIV_TESTNUM 8 typedef void (*TESTFUN)(unsigned bvsize, bool is_signed); void tst_no_overflow() { disable_debug("heap"); unsigned bvsizes[BVSIZES] = { 1, 16, 32, 42 }; TESTFUN tests[TESTNUM] = { test_add, test_sub, test_mul }; for (int i = 0; i < BVSIZES; ++i) { for (int j = 0; j < TESTNUM; ++j) { tests[j](bvsizes[i], /* is_signed = */ true); tests[j](bvsizes[i], /* is_signed = */ false); } test_neg(bvsizes[i]); test_div(bvsizes[i]); } unsigned equiv_bvsizes[EQUIV_BVSIZES] = { 1, 2, 7, 16 }; // before performing the bound test, arguments are extended by a few bits to prevent overflow: // * 1 is the default // * 2 is used for subtraction, so that 1 bit is used for the sign event for unsigned subtraction // * -1 to indicate that the bitsize should be doubled for multiplication Equivalence_params equiv_tests[EQUIV_TESTNUM] = { { "ovfl_add", Z3_mk_bvadd_no_overflow, Z3_mk_bvadd, OVFL_FUNC, 1, /* do_unsigned = */ true, /* non_zero = */ false, /* sign_compar = */ false }, { "udfl_add", Z3_mk_bvadd_no_underflow_wrapper, Z3_mk_bvadd, UDFL_FUNC, 1, /* do_unsigned = */ false, /* non_zero = */ false, /* sign_compar = */ false }, { "ovfl_sub", Z3_mk_bvsub_no_overflow_wrapper, Z3_mk_bvsub, OVFL_FUNC, 1, /* do_unsigned = */ false, /* non_zero = */ false, /* sign_compar = */ false }, { "udfl_sub", Z3_mk_bvsub_no_underflow, Z3_mk_bvsub, UDFL_FUNC, 2, /* do_unsigned = */ true, /* non_zero = */ false, /* sign_compar = */ true }, { "ovfl_mul", Z3_mk_bvmul_no_overflow, Z3_mk_bvmul, OVFL_FUNC, -1, /* do_unsigned = */ true, /* non_zero = */ false, /* sign_compar = */ false }, { "udfl_mul", Z3_mk_bvmul_no_underflow_wrapper, Z3_mk_bvmul, UDFL_FUNC, -1, /* do_unsigned = */ false, /* non_zero = */ false, /* sign_compar = */ false }, { "ovfl_div", Z3_mk_bvsdiv_no_overflow_wrapper, Z3_mk_bvsdiv, OVFL_FUNC, 1, /* do_unsigned = */ false, /* non_zero = */ true, /* sign_compar = */ false }, { "ovfl_neg", Z3_mk_bvneg_no_overflow_wrapper, Z3_mk_bvneg_wrapper, OVFL_FUNC, 1, /* do_unsigned = */ false, /* non_zero = */ false, /* sign_compar = */ false }, }; for (int i = 0; i < EQUIV_BVSIZES; ++i) { for (int j = 0; j < EQUIV_TESTNUM; ++j) { test_equiv(equiv_tests[j], equiv_bvsizes[i], /* is_signed = */ true); if (equiv_tests[j].do_unsigned) { test_equiv(equiv_tests[j], equiv_bvsizes[i], /* is_signed = */ false); } } } } #else void tst_no_overflow() { } #endif z3-z3-4.4.1/src/test/object_allocator.cpp000066400000000000000000000063431260446376700202130ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: object_allocator.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-09. Revision History: --*/ #include"rational.h" #include"object_allocator.h" struct cell { rational m_coeff; unsigned m_i; unsigned m_j; cell * m_next_row; cell * m_next_col; public: static unsigned g_num_allocated_cells; static unsigned g_num_deallocated_cells; static unsigned g_num_recycled_cells; cell() { g_num_allocated_cells++; } ~cell() { g_num_deallocated_cells++; } void reset() { m_coeff.reset(); g_num_recycled_cells++; } }; unsigned cell::g_num_allocated_cells = 0; unsigned cell::g_num_deallocated_cells = 0; unsigned cell::g_num_recycled_cells = 0; typedef object_allocator > cell_allocator; static void tst1() { cell_allocator m; cell * c1 = m.allocate(); /* cell * c2 = */ m.allocate(); c1->m_coeff = rational(10); m.recycle(c1); cell * c3 = m.allocate(); SASSERT(c3->m_coeff.is_zero()); } static void tst2() { cell_allocator m; SASSERT(m.capacity() >= 2); cell_allocator::worker_object_allocator m1 = m.get_worker_allocator(0); cell_allocator::worker_object_allocator m2 = m.get_worker_allocator(1); m.enable_concurrent(true); vector > object_coeff_pairs; unsigned num_resets = 0; for (unsigned i = 0; i < 100000; i++) { unsigned idx = rand() % 6; if (idx < 4) { cell * c; if (idx < 2) c = m1.allocate(); else c = m2.allocate(); SASSERT(c->m_coeff.is_zero()); int val = rand(); c->m_coeff = rational(val); object_coeff_pairs.push_back(std::make_pair(c, val)); } else { if (!object_coeff_pairs.empty()) { unsigned idx = rand() % object_coeff_pairs.size(); cell * c = object_coeff_pairs[idx].first; CTRACE("object_allocator", c->m_coeff != rational(object_coeff_pairs[idx].second), tout << c->m_coeff << " != " << rational(object_coeff_pairs[idx].second) << "\n";); SASSERT(c->m_coeff == rational(object_coeff_pairs[idx].second)); if (idx < 5) m1.recycle(c); else m2.recycle(c); object_coeff_pairs.erase(object_coeff_pairs.begin() + idx); } } if (rand() % 5000 == 0) { m.enable_concurrent(false); m.reset(); object_coeff_pairs.reset(); m.enable_concurrent(true); num_resets++; } } TRACE("object_allocator", tout << "num. resets: " << num_resets << "\n";); } void tst_object_allocator() { tst1(); tst2(); TRACE("object_allocator", tout << "num. allocated cells: " << cell::g_num_allocated_cells << "\nnum. deallocated cells: " << cell::g_num_deallocated_cells << "\nnum. recycled cells: " << cell::g_num_recycled_cells << "\n";); SASSERT(cell::g_num_allocated_cells == cell::g_num_deallocated_cells); } z3-z3-4.4.1/src/test/old_interval.cpp000066400000000000000000000155131260446376700173660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: interval.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-12-10. Revision History: --*/ #include"old_interval.h" static void tst1() { ext_numeral inf(true); ext_numeral minus_inf(false); ext_numeral zero(0); SASSERT(ext_numeral(10) + ext_numeral(3) == ext_numeral(13)); SASSERT(inf + zero == inf); SASSERT(minus_inf + zero == minus_inf); SASSERT(minus_inf + ext_numeral(3) == minus_inf); SASSERT(inf + inf == inf); SASSERT(minus_inf + minus_inf == minus_inf); SASSERT(minus_inf + ext_numeral(10) == minus_inf); SASSERT(minus_inf + ext_numeral(-10) == minus_inf); SASSERT(inf + ext_numeral(10) == inf); SASSERT(inf + ext_numeral(-10) == inf); SASSERT(ext_numeral(10) - ext_numeral(3) == ext_numeral(7)); SASSERT(inf - zero == inf); SASSERT(minus_inf - zero == minus_inf); SASSERT(minus_inf - ext_numeral(3) == minus_inf); SASSERT(inf - minus_inf == inf); SASSERT(minus_inf - inf == minus_inf); SASSERT(zero - minus_inf == inf); SASSERT(zero - inf == minus_inf); SASSERT(ext_numeral(-10) - minus_inf == inf); SASSERT(ext_numeral(10) - minus_inf == inf); SASSERT(ext_numeral(-10) - inf == minus_inf); SASSERT(ext_numeral(10) - inf == minus_inf); SASSERT(ext_numeral(10) * inf == inf); SASSERT(ext_numeral(-10) * inf == minus_inf); SASSERT(zero * inf == zero); SASSERT(zero * minus_inf == zero); SASSERT(zero * ext_numeral(10) == zero); SASSERT(ext_numeral(10) * ext_numeral(-20) == ext_numeral(-200)); SASSERT(ext_numeral(3) * ext_numeral(2) == ext_numeral(6)); SASSERT(inf * inf == inf); SASSERT(inf * minus_inf == minus_inf); SASSERT(minus_inf * minus_inf == inf); SASSERT(minus_inf * inf == minus_inf); SASSERT(minus_inf * ext_numeral(10) == minus_inf); SASSERT(minus_inf * ext_numeral(-10) == inf); SASSERT(minus_inf < inf); SASSERT(!(inf < minus_inf)); SASSERT(minus_inf < ext_numeral(10)); SASSERT(ext_numeral(-3) < inf); SASSERT(ext_numeral(-10) < ext_numeral(4)); SASSERT(ext_numeral(2) < ext_numeral(10)); SASSERT(!(inf < ext_numeral(30))); SASSERT(!(ext_numeral(10) < minus_inf)); SASSERT(!(inf < inf)); SASSERT(!(minus_inf < minus_inf)); SASSERT(!(zero < zero)); SASSERT(!(ext_numeral(10) < ext_numeral(10))); SASSERT(inf > minus_inf); SASSERT(inf > zero); SASSERT(inf > ext_numeral(10)); SASSERT(ext_numeral(10) > minus_inf); SASSERT(zero > minus_inf); SASSERT(!(zero > inf)); SASSERT(!(minus_inf > inf)); SASSERT(inf >= minus_inf); SASSERT(inf >= inf); SASSERT(minus_inf >= minus_inf); SASSERT(inf >= zero); SASSERT(zero >= minus_inf); SASSERT(inf <= inf); SASSERT(minus_inf <= minus_inf); SASSERT(zero <= inf); SASSERT(minus_inf <= zero); ext_numeral val(10); val.neg(); SASSERT(val == ext_numeral(-10)); val = inf; val.neg(); SASSERT(val == minus_inf); val.neg(); SASSERT(val == inf); SASSERT(minus_inf.sign()); SASSERT(!zero.sign()); SASSERT(!inf.sign()); SASSERT(ext_numeral(-10).sign()); SASSERT(!ext_numeral(10).sign()); SASSERT(inf.is_infinite()); SASSERT(minus_inf.is_infinite()); SASSERT(!zero.is_infinite()); SASSERT(!ext_numeral(10).is_infinite()); SASSERT(!inf.is_zero()); SASSERT(!minus_inf.is_zero()); SASSERT(zero.is_zero()); SASSERT(!ext_numeral(10).is_zero()); } class interval_tester { v_dependency_manager m; interval singleton(int i) { return interval(m, rational(i)); } interval all() { return interval(m); } interval l(int i, bool o = false, int idx = 0) { return interval(m, rational(i), o, true, idx == 0 ? 0 : m.mk_leaf(reinterpret_cast(idx))); } interval r(int i, bool o = false, int idx = 0) { return interval(m, rational(i), o, false, idx == 0 ? 0 : m.mk_leaf(reinterpret_cast(idx))); } interval b(int l, int u, bool lo = false, bool uo = false, int idx_l = 0, int idx_u = 0) { return interval(m, rational(l), lo, idx_l == 0 ? 0 : m.mk_leaf(reinterpret_cast(idx_l)), rational(u), uo, idx_u == 0 ? 0 : m.mk_leaf(reinterpret_cast(idx_u))); } void bugs() { interval r1 = l(0); interval r2 = b(-1, 0, false, true); r1 *= r2; } void tst1() { std::cerr << singleton(10) << "\n"; std::cerr << all() << "\n"; std::cerr << l(-10) << "\n"; std::cerr << r(10) << "\n"; std::cerr << l(-10, true) << "\n"; std::cerr << r(10, true) << "\n"; std::cerr << b(2, 10) << "\n"; std::cerr << wd(b(-5, 5, true, false, 1, 2) * b(-5, 5, false, true, 3, 4)) << "\n"; std::cerr << wd(l(2, false, 1) / b(2, 6, false, false, 2, 3)) << "\n"; std::cerr << wd(expt(b(-2, 3, true, false, 1, 2), 2)) << "\n"; std::cerr << wd(expt(b(-4, 3, true, false, 1, 2), 2)) << "\n"; std::cerr << wd(expt(b(2, 4, true, false, 1, 2), 2)) << "\n"; std::cerr << wd(expt(b(0, 3, true, false, 1, 2), 2)) << "\n"; std::cerr << wd(expt(b(-4, -2, true, false, 1, 2), 2)) << "\n"; std::cerr << b(2, 10, false, false, 1, 2) << " * " << l(10, false, 3).inv() << " = " << wd(b(2, 10, false, false, 1, 2) / l(10, false, 3)) << "\n"; std::cerr << b(-2, -1, false, true) << " * " << b(-3,0) << " = "; std::cerr.flush(); std::cerr << (b(-2, -1, false, true) * b(-3,0)) << "\n"; std::cerr << b(1, 2, true, false) << " * " << b(0,3) << " = "; std::cerr.flush(); std::cerr << (b(1, 2, true, false) * b(0,3)) << "\n"; std::cerr << b(1, 2, true, true) << " * " << b(-3,0) << " = "; std::cerr.flush(); std::cerr << (b(1, 2, true, true) * b(-3,0)) << "\n"; std::cerr << b(10,20) << " / " << b(0,1,true,false) << " = "; std::cerr.flush(); std::cerr << (b(10,20)/b(0,1,true,false)) << "\n"; std::cerr << (b(10,20)/b(0,2,true,false)) << "\n"; } public: void run() { bugs(); tst1(); } }; #include"basic_interval.h" #include"mpz.h" #include"scoped_numeral.h" static void tst2() { typedef basic_interval_manager mpzi_manager; typedef mpzi_manager::scoped_interval scoped_mpzi; unsynch_mpz_manager nm; mpzi_manager m(nm); scoped_mpzi x(m), y(m), z(m); m.set(x, mpz(1), mpz(2)); m.set(y, mpz(-2), mpz(3)); m.add(x, y, z); std::cout << "x: " << x << ", y: " << y << ", z: " << z << "\n"; SASSERT(nm.eq(z.lower(), mpz(-1))); SASSERT(nm.eq(z.upper(), mpz(5))); m.mul(x, y, z); std::cout << "x: " << x << ", y: " << y << ", z: " << z << "\n"; SASSERT(nm.eq(z.lower(), mpz(-4))); SASSERT(nm.eq(z.upper(), mpz(6))); } void tst_old_interval() { tst2(); enable_trace("interval_bug"); interval_tester tester; tester.run(); tst1(); } z3-z3-4.4.1/src/test/optional.cpp000066400000000000000000000023651260446376700165320ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_optional.cpp Abstract: Test optional module Author: Leonardo de Moura (leonardo) 2006-09-29. Revision History: --*/ #include"trace.h" #include"debug.h" #include"optional.h" static void tst1() { optional v; SASSERT(!v); SASSERT(v == false); v = 10; SASSERT(v); SASSERT(*v == 10); TRACE("optional", tout << sizeof(v) << "\n";); } struct OptFoo { int m_x; int m_y; OptFoo(int x, int y):m_x(x), m_y(y) { TRACE("optional", tout << "OptFoo created: " << m_x << " : " << m_y << "\n";); } ~OptFoo() { TRACE("optional", tout << "OptFoo deleted: " << m_x << " : " << m_y << "\n";); } }; static void tst2() { optional v; SASSERT(!v); v = OptFoo(10, 20); SASSERT(v->m_x == 10); SASSERT(v->m_y == 20); v = OptFoo(200, 300); SASSERT(v->m_x == 200); SASSERT(v->m_y == 300); TRACE("optional", tout << sizeof(v) << "\n";); } static void tst3() { optional v; SASSERT(!v); int x = 10; v = &x; SASSERT(v); SASSERT(*v == &x); TRACE("optional", tout << sizeof(v) << "\n";); SASSERT(*(*v) == 10); } void tst_optional() { tst1(); tst2(); tst3(); } z3-z3-4.4.1/src/test/parray.cpp000066400000000000000000000217471260446376700162100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: parray.cpp Abstract: Test persistent arrays. Author: Leonardo de Moura (leonardo) 2011-02-23. Revision History: --*/ #include"parray.h" #include"small_object_allocator.h" #include"ast.h" template struct int_parray_config { typedef int value; typedef dummy_value_manager value_manager; typedef small_object_allocator allocator; static const bool ref_count = false; static const bool preserve_roots = PRESERVE_ROOTS; static const unsigned max_trail_sz = 8; static const unsigned factor = 2; }; template static void tst1() { typedef parray_manager > int_parray_manager; typedef typename int_parray_manager::ref int_array; dummy_value_manager vm; small_object_allocator a; int_parray_manager m(vm, a); int_array a1; int_array a2; int_array a3; m.mk(a1); SASSERT(m.size(a1) == 0); m.push_back(a1, 10, a2); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); SASSERT(m.size(a1) == 0); SASSERT(m.size(a2) == 1); m.push_back(a1, 20, a1); m.push_back(a1, 30, a1); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); SASSERT(m.get(a1, 0) == 20); SASSERT(m.get(a1, 1) == 30); SASSERT(m.get(a2, 0) == 10); SASSERT(m.size(a1) == 2); SASSERT(m.size(a2) == 1); SASSERT(m.size(a3) == 0); m.push_back(a2, 100, a3); SASSERT(m.size(a3) == 2); SASSERT(m.get(a3, 0) == 10); SASSERT(m.get(a3, 1) == 100); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n";); m.push_back(a2, 50); SASSERT(m.get(a2, 0) == 10); SASSERT(m.get(a2, 1) == 50); SASSERT(m.size(a2) == 2); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n";); m.del(a1); m.del(a2); m.del(a3); } template static void tst2() { typedef parray_manager > int_parray_manager; typedef typename int_parray_manager::ref int_array; TRACE("parray", tout << "tst2\n";); dummy_value_manager vm; small_object_allocator a; int_parray_manager m(vm, a); int_array a1; int_array a2; for (unsigned i = 0; i < 100; i++) m.push_back(a1, i); SASSERT(m.size(a1) == 100); m.push_back(a1, 100, a2); for (unsigned i = 0; i < 10; i++) m.push_back(a2, i+101); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); SASSERT(m.get(a1, 0) == 0); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); for (unsigned i = 0; i < m.size(a1); i++) { SASSERT(static_cast(m.get(a1, i)) == i); } for (unsigned i = 0; i < m.size(a2); i++) { SASSERT(static_cast(m.get(a2, i)) == i); } TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); m.unshare(a1); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); m.del(a1); m.del(a2); } template static void tst3() { typedef parray_manager > int_parray_manager; typedef typename int_parray_manager::ref int_array; TRACE("parray", tout << "tst3\n";); dummy_value_manager vm; small_object_allocator a; int_parray_manager m(vm, a); int_array a1; int_array a2; int_array a3; int_array a4; for (unsigned i = 0; i < 20; i++) m.push_back(a1, i); SASSERT(m.size(a1) == 20); m.set(a1, 0, 1, a2); for (unsigned i = 1; i < 20; i++) { if (i == 6) { m.copy(a2, a3); m.pop_back(a3); m.pop_back(a3); m.push_back(a3, 40); } m.set(a2, i, i+1); } m.pop_back(a2, a4); m.pop_back(a4); m.push_back(a4, 30); for (unsigned i = 0; i < 20; i++) { SASSERT(static_cast(m.get(a2, i)) == i+1); } TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); SASSERT(m.get(a1, 10) == 10); TRACE("parray", tout << "after rerooting...\n"; m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); SASSERT(m.size(a1) == 20); SASSERT(m.size(a2) == 20); SASSERT(m.size(a3) == 19); SASSERT(m.size(a4) == 19); for (unsigned i = 0; i < 20; i++) { SASSERT(static_cast(m.get(a1, i)) == i); SASSERT(static_cast(m.get(a2, i)) == i+1); SASSERT(i >= 18 || static_cast(m.get(a4, i)) == i+1); SASSERT(i >= 6 || static_cast(m.get(a3, i)) == i+1); SASSERT(!(6 <= i && i <= 17) || static_cast(m.get(a3, i)) == i); } SASSERT(m.get(a4, 18) == 30); SASSERT(m.get(a3, 18) == 40); TRACE("parray", tout << "after many gets...\n"; m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); m.unshare(a1); TRACE("parray", tout << "after unshare...\n"; m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); m.reroot(a4); TRACE("parray", tout << "after reroot...\n"; m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); m.unshare(a2); TRACE("parray", tout << "after second unshare...\n"; m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); m.del(a1); m.del(a2); m.del(a3); m.del(a4); } #if 0 // Moved to ast.cpp struct expr_array_config { typedef expr * value; typedef ast_manager value_manager; typedef small_object_allocator allocator; static const bool ref_count = true; static const bool preserve_roots = true; static const unsigned max_trail_sz = 8; static const unsigned factor = 2; }; typedef parray_manager expr_array_manager; typedef expr_array_manager::ref expr_array; static void tst4() { TRACE("parray", tout << "tst4\n";); ast_manager m; expr_array_manager m2(m, m.get_allocator()); expr_array a1; expr_array a2; expr * v0 = m.mk_var(0, m.mk_bool_sort()); expr * v1 = m.mk_var(1, m.mk_bool_sort()); expr * v2 = m.mk_var(2, m.mk_bool_sort()); expr * v3 = m.mk_var(3, m.mk_bool_sort()); m2.push_back(a1, v0); m2.push_back(a1, v1); m2.push_back(a1, v2, a2); m2.push_back(a1, v3); m2.push_back(a2, v2); m2.pop_back(a1); TRACE("parray", m2.display_info(tout, a1); tout << "\n"; m2.display_info(tout, a2); tout << "\n"; ); m2.reroot(a1); TRACE("parray", m2.display_info(tout, a1); tout << "\n"; m2.display_info(tout, a2); tout << "\n"; ); m2.del(a1); m2.del(a2); } #endif static void tst5() { ast_manager m; expr_array a1; expr_array a2; m.mk(a1); for (unsigned i = 0; i < 100; i++) { m.push_back(a1, m.mk_var(i, m.mk_bool_sort())); } unsigned long long mem = memory::get_max_used_memory(); std::cout << "max. heap size: " << static_cast(mem)/static_cast(1024*1024) << " Mbytes\n"; m.copy(a1, a2); for (unsigned i = 0; i < 1000000; i++) { m.set(a1, i % 100, m.mk_var(rand() % 100, m.mk_bool_sort())); } mem = memory::get_max_used_memory(); std::cout << "max. heap size: " << static_cast(mem)/static_cast(1024*1024) << " Mbytes\n"; m.del(a2); m.del(a1); } void tst_parray() { // enable_trace("parray_mem"); tst1(); tst2(); tst3(); tst1(); tst2(); tst3(); // tst4(); tst5(); } z3-z3-4.4.1/src/test/pdr.cpp000066400000000000000000000065411260446376700154720ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "pdr_context.h" #include "reg_decl_plugins.h" using namespace pdr; static expr_ref mk_state(expr_ref_vector const& states, random_gen& rand) { expr_ref result(states.get_manager()); result = states[rand(states.size())]; return result; } struct test_model_search { struct init_test { init_test(func_decl_ref& fn) { ast_manager& m = fn.get_manager(); reg_decl_plugins(m); fn = m.mk_const_decl(symbol("f"), m.mk_bool_sort()); } }; ast_manager m; smt_params m_smt_params; fixedpoint_params fp_params; context ctx; manager pm; func_decl_ref fn; init_test initt; pred_transformer pt; random_gen rand; model_search search; expr_ref_vector states; test_model_search(): ctx(m_smt_params, fp_params, m), pm(m_smt_params, 10, m), fn(m), initt(fn), pt(ctx, pm, fn), rand(10), search(true), states(m) { } void add_tree(model_node* parent, bool force_goal) { unsigned level = parent->level(); search.add_leaf(*parent); expr_ref state(m); if (level > 0 && (force_goal || parent->is_goal())) { search.remove_goal(*parent); state = mk_state(states, rand); add_tree(alloc(model_node, parent, state, pt, level-1), false); state = mk_state(states, rand); add_tree(alloc(model_node, parent, state, pt, level-1), false); parent->check_pre_closed(); } } bool mutate() { model_node* leaf = search.next(); if (!leaf) return false; unsigned level = leaf->level(); if (level == 0) { if (rand(2) == 0) { leaf->display(std::cout << "backtrack to grandparent\n", 1); search.backtrack_level(false, *leaf->parent()); } else { leaf->display(std::cout << "backtrack to parent\n", 1); search.backtrack_level(false, *leaf); } } else { leaf->display(std::cout << "grow tree\n", 1); add_tree(leaf, true); } return true; } void init() { std::cout << "pdr state-hash search tree\n"; expr_ref state(m); func_decl_ref fn(m); for (unsigned i = 0; i < 10; ++i) { std::ostringstream strm; strm << "s" << i; state = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); fn = to_app(state)->get_decl(); states.push_back(state); } state = states[0].get(); unsigned level = 4; for(unsigned n = 0; n < 100; ++n) { state = mk_state(states, rand); model_node* root = alloc(model_node, 0, state, pt, level); search.set_root(root); add_tree(root, false); search.display(std::cout); while (true) { search.well_formed(); if (!mutate()) break; search.display(std::cout); } search.reset(); //++level; } search.reset(); } }; void tst_pdr() { test_model_search test; test.init(); } z3-z3-4.4.1/src/test/permutation.cpp000066400000000000000000000043301260446376700172460ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: permutation.cpp Abstract: Simple abstraction for managing permutations. Author: Leonardo de Moura (leonardo) 2012-01-04 Revision History: --*/ #include"permutation.h" #include"util.h" #include"vector.h" void apply_permutation_copy(unsigned sz, unsigned const * src, unsigned const * p, unsigned * target) { for (unsigned i = 0; i < sz; i++) { target[i] = src[p[i]]; } } static void tst1(unsigned sz, unsigned num_tries, unsigned max = UINT_MAX) { #if 0 unsigned_vector data; unsigned_vector p; unsigned_vector new_data; data.resize(sz); p.resize(sz); new_data.resize(sz); random_gen g; for (unsigned i = 0; i < sz; i++) p[i] = i; // fill data with random numbers for (unsigned i = 0; i < sz; i++) data[i] = g() % max; for (unsigned k = 0; k < num_tries; k ++) { shuffle(p.size(), p.c_ptr(), g); // std::cout << "p: "; display(std::cout, p.begin(), p.end()); std::cout << "\n"; // std::cout << "data: "; display(std::cout, data.begin(), data.end()); std::cout << "\n"; apply_permutation_copy(sz, data.c_ptr(), p.c_ptr(), new_data.c_ptr()); apply_permutation(sz, data.c_ptr(), p.c_ptr()); // std::cout << "data: "; display(std::cout, data.begin(), data.end()); std::cout << "\n"; for (unsigned i = 0; i < 0; i++) SASSERT(data[i] == new_data[i]); } #endif } void tst_permutation() { tst1(10, 1000, 5); tst1(10, 1000, 1000); tst1(10, 1000, UINT_MAX); tst1(100, 1000, 33); tst1(100, 1000, 1000); tst1(100, 1000, UINT_MAX); tst1(1000, 1000, 121); tst1(1000, 1000, 1000); tst1(1000, 1000, UINT_MAX); tst1(33, 1000, 121); tst1(33, 1000, 1000); tst1(33, 1000, UINT_MAX); tst1(121, 1000, 121); tst1(121, 1000, 1000); tst1(121, 1000, UINT_MAX); for (unsigned i = 0; i < 1000; i++) { tst1(1000, 2, 333); tst1(1000, 2, 10000); tst1(1000, 2, UINT_MAX); } random_gen g; for (unsigned i = 0; i < 100000; i++) { unsigned sz = (g() % 131) + 1; tst1(sz, 1, sz*2); tst1(sz, 1, UINT_MAX); tst1(sz, 1, sz/2 + 1); } } z3-z3-4.4.1/src/test/polynomial.cpp000066400000000000000000002336551260446376700171000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial.cpp Abstract: Goodies for creating and handling polynomials. Author: Leonardo (leonardo) 2011-11-15 Notes: --*/ #if !defined(__clang__) #include"polynomial.h" #include"polynomial_factorization.h" #include"polynomial_var2value.h" #include"polynomial_cache.h" #include"linear_eq_solver.h" static void tst1() { std::cout << "\n----- Basic testing -------\n"; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); std::cout << x0 << " " << x1 << " " << x2 << "\n"; polynomial_ref p(m); p = (x0^3) + x1*x0 + 2; std::cout << p << "\n"; std::cout << "max_var(p): " << max_var(p) << "\n"; SASSERT(max_var(p) == 1); std::cout << (2*x2 - x1*x0) << "\n"; std::cout << (p + (2*x2 - x1*x0)) << "\n"; std::cout << (p*p + 2*x2) << "\n"; std::cout << derivative(p*p + 2*x2, 0) << "\n"; polynomial_ref q(m); q = (x0^4) + x0 + 1; std::cout << "q(x): " << q << "\n"; std::cout << "q(y): " << compose_y(q, 2) << "\n"; std::cout << "q(x-y): " << compose_x_minus_y(q, 2) << "\n"; q = (x0 - 1)*(x0 - 2)*(x0 - 1)*(x0 + 2); std::cout << "q: " << q << "\n"; polynomial_ref s(m); s = (x0 - 1)*((x0 + 3)^2); std::cout << "s: " << s << "\n"; } static void tst_pseudo_div(polynomial_ref const & A, polynomial_ref const & B, polynomial::var x) { polynomial::manager & m = A.m(); std::cout << "---- Pseudo-division test ----\n"; std::cout << "A: " << A << "\n"; std::cout << "B: " << B << "\n"; std::cout << "x: " << x << "\n"; polynomial_ref Q(m); polynomial_ref R(m); unsigned d; Q = pseudo_division(A, B, x, d, R); std::cout << "d: " << d << "\n"; std::cout << "Q: " << Q << "\n"; std::cout << "R: " << R << "\n"; polynomial_ref l_B(m); l_B = coeff(B, x, degree(B, x)); std::cout << "l_B: " << l_B << "\n"; polynomial_ref l_B_d(m); l_B_d = l_B^d; polynomial_ref t(m); std::cout << "l_B^d: " << l_B_d << "\n"; std::cout << "Q * B + R: " << Q * B + R << "\n"; std::cout << "l_B_d * A: " << l_B_d * A << "\n"; SASSERT(eq((Q * B + R), (l_B_d * A))); } static void tst2() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = ((x0 - 1)^2)*x2 + (x1^2)*((x2 - 2)^2) + 1; q = (x0 - 1)*x2 + (x1^3)*(x2 - 2) + (x0 - 2)*(x1 - 2) + 10; tst_pseudo_div(p, q, 0); } static void tst3() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = (x1^2) + (x0^2) - 1; q = (x1*x0) - 1; tst_pseudo_div(p, q, 1); } static void tst4() { std::cout << "---- Testing renaming/reordering ----\n"; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p1 = x2 + ((x0 - 1)^2)*x1 + (x2^3) + 10; polynomial_ref p2 = x0*x1*x2 + x1*(x2^3) + ((x0 - 2)^2); std::cout << "p1: " << p1 << "\n"; std::cout << "p2: " << p2 << "\n"; polynomial::var new_order[3] = { 2, 0, 1 }; m.rename(3, new_order); std::cout << "----- x0 -> x2, x1 -> x0, x2 -> x1 \n"; std::cout << "p1: " << p1 << "\n"; std::cout << "p2: " << p2 << "\n"; } static void tst_quasi_resultant(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x) { std::cout << "---- Testing quasi-resultants ---- \n"; std::cout << "p : " << p << "\n"; std::cout << "q : " << q << "\n"; std::cout << "x : " << x << "\n--->\n"; std::cout << quasi_resultant(p, q, x) << "\n"; } static void tst5() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = ((x0 - x1)^2) - 2; q = (x1^2) - 3; // sqrt(2) + sqrt(3) must be a root of the quasi-resultant tst_quasi_resultant(p, q, 1); } static void tst6() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = (x0 - 2)*(x0 - 3)*(x0 + 2); std::cout << "p(x0): " << p << "\n"; std::cout << "p(-x0): " << compose_minus_x(p) << "\n"; std::cout << "x^3*p(1/x0): " << compose_1_div_x(p) << "\n"; std::cout << "p(x0 - x1): " << compose_x_minus_y(p, 1) << "\n"; std::cout << "x1^3*p(x0/x1): " << compose_x_div_y(p, 1) << "\n"; } static void tst7() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q1(m); polynomial_ref q2(m); p = (x0 - x1)*(x2 - 1); q1 = (x0^2) - 2; q2 = (x1^2) - 2; polynomial_ref r(m); r = quasi_resultant(p, q1, 0); std::cout << "1) r: " << r << "\n"; r = quasi_resultant(r, q2, 1); std::cout << "2) r: " << r << "\n"; } static void tst8() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref sqrt2(m); polynomial_ref sqrt3(m); p = x2 - (x0*x1 - (x0^2) + 1); sqrt3 = (x0^2) - 3; sqrt2 = (x1^2) - 2; polynomial_ref r(m); r = quasi_resultant(p, sqrt2, 1); r = quasi_resultant(r, sqrt3, 0); std::cout << "r: " << r << "\n"; } static void tst9() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref sqrt2(m); p = ((x0^3) - 1)*(x1^2) - 1; sqrt2 = ((x0^2) - 2)*(x0 - 2); // added garbage to polynomial // sqrt2 = (x0^2) - 2; // added garbage to polynomial // p(sqrt(2), x1) has the roots -0.7395 and 0.7395 polynomial_ref r(m); r = quasi_resultant(p, sqrt2, 0); std::cout << "p: " << p << "\n"; std::cout << "sqrt2: " << sqrt2 << "\n"; std::cout << "r: " << r << "\n"; // r contains the roots -0.7395 and 0.7395, plus garbage roots: 0, -0.3779, 0.3779 polynomial_ref q(m); q = x2 - (((x0^3) - 1)*(x1^2) - 1); std::cout << "q: " << q << "\n"; polynomial_ref r2(m); TRACE("polynomial", tout << "QUASI_RESULTANT: q, sqrt2.....\n";); r2 = quasi_resultant(q, sqrt2, 0); // TRACE("polynomial", tout << "QUASI_RESULTANT: sqrt2, q.....\n";); // std::cout << "r2: " << r2 << "\n"; // r2 = quasi_resultant(sqrt2, q, 0); // std::cout << "r2: " << r2 << "\n"; // return; std::cout << "r2: " << r2 << "\n"; r2 = normalize(quasi_resultant(r2, r, 1)); std::cout << "r2: " << r2 << "\n"; polynomial_ref_vector seq(m); r2 = normalize(quasi_resultant(sqrt2, q, 0)); // sturm_seq(r2, seq); std::cout << "r2:\n" << r2 << "\n"; } static void tst10() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref A(m); polynomial_ref B(m); polynomial_ref g(m); polynomial_ref h(m); polynomial_ref R(m); A = x2 - (((x0^3) - 1)*(x1^2) - 1); B = ((x0^2) - 2)*(x0 - 2); std::cout << "A: " << A << "\nB: " << B << "\n"; unsigned d; R = pseudo_remainder(A, B, 0, d); std::cout << "R: " << R << "\n"; // second iteration std::cout << "second iteration...\n"; A = B; B = R; g = coeff(A, 0, degree(A, 0)); std::cout << "A: " << A << "\nB: " << B << "\ng: " << g << "\n"; R = pseudo_remainder(A, B, 0, d); std::cout << "R: " << R << "\n"; // third iteration std::cout << "third iteration...\n"; A = B; B = R; g = coeff(A, 0, degree(A, 0)); std::cout << "A: " << A << "\nB: " << B << "\ng: " << g << "\n"; R = pseudo_remainder(A, B, 0, d); std::cout << "R: " << R << "\n"; } static void tst11() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); q = ((x1^2) + 1)*(x2 + 1); p = (x3 + 1)*q; polynomial_ref d(m); d = exact_div(p, q); std::cout << "p: " << p << "\nq: " << q << "\nd: " << d << "\n"; SASSERT(eq(q * d, p)); q = ((x1^3) + x1 + 1)*((x2^2) + x2 + x2 + 1)*((x3^2) + 2); p = (x1 + (x3^2) + x3 + x2 + (x2^2) + 1)*((x1^3) + x1 + 1)*((x2^2) + x2 + x2 + 1)*((x3^2) + 2); d = exact_div(p, q); std::cout << "p: " << p << "\nq: " << q << "\nd: " << d << "\n"; SASSERT(eq(q * d, p)); } static void tst_discriminant(polynomial_ref const & p, polynomial::var x, polynomial_ref const & expected) { polynomial::manager & m = p.m(); polynomial_ref r(m); r = discriminant(p, x); std::cout << "r: " << r << "\n"; std::cout << "expected: " << expected << "\n"; SASSERT(eq(r, expected)); m.lex_sort(r); std::cout << "r (sorted): " << r << "\n"; } static void tst_discriminant(polynomial_ref const & p, polynomial_ref const & expected) { tst_discriminant(p, max_var(p), expected); } static void tst_discriminant() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref a(m); polynomial_ref b(m); polynomial_ref c(m); polynomial_ref d(m); polynomial_ref x(m); a = m.mk_polynomial(m.mk_var()); b = m.mk_polynomial(m.mk_var()); c = m.mk_polynomial(m.mk_var()); d = m.mk_polynomial(m.mk_var()); x = m.mk_polynomial(m.mk_var()); tst_discriminant(a*(x^2) + b*x + c, (b^2) - 4*a*c); tst_discriminant(a*(x^3) + b*(x^2) + c*x + d, (b^2)*(c^2) - 4*a*(c^3) - 4*(b^3)*d + 18*a*b*c*d - 27*(a^2)*(d^2)); tst_discriminant(a*(x^3) + b*(x^2) + c*(x^2) + d, -4*(b^3)*d - 12*(b^2)*c*d - 12*b*(c^2)*d - 4*(c^3)*d - 27*(a^2)*(d^2)); tst_discriminant(a*(x^3) + b*(x^2) + c*(x^2) + d, -4*(b^3)*d - 12*(b^2)*c*d - 12*b*(c^2)*d - 4*(c^3)*d - 27*(a^2)*(d^2)); tst_discriminant(a*(x^3) + (b^2)*d*(x^2) + c*(x^2) + d, -4*(b^6)*(d^4) - 12*(b^4)*c*(d^3) - 12*(b^2)*(c^2)*(d^2) - 4*(c^3)*d - 27*(a^2)*(d^2)); tst_discriminant(a*(x^4) + b*(x^2) + c, 16*a*(b^4)*c - 128*(a^2)*(b^2)*(c^2) + 256*(a^3)*(c^3)); polynomial_ref one(m); one = m.mk_const(rational(1)); tst_discriminant(x, one); tst_discriminant(3*x, one); polynomial_ref zero(m); zero = m.mk_zero(); tst_discriminant(one, zero); tst_discriminant(a*(x^7) + b, -823543*(a^6)*(b^6)); tst_discriminant(((a^2)+(b^2)+c)*(x^4) + (d + a*b)*x + a, -27*(a^8)*(b^4) - 54*(a^6)*(b^6) - 27*(a^4)*(b^8) - 54*(a^6)*(b^4)*c - 54*(a^4)*(b^6)*c - 108*(a^7)*(b^3)*d - 216*(a^5)*(b^5)*d - 108*(a^3)*(b^7)*d - 27*(a^4)*(b^4)*(c^2) - 216*(a^5)*(b^3)*c*d - 216*(a^3)*(b^5)*c*d - 162*(a^6)*(b^2)*(d^2) - 324*(a^4)*(b^4)*(d^2) - 162*(a^2)*(b^6)*(d^2) + 256*(a^9) + 768*(a^7)*(b^2) + 768*(a^5)*(b^4) + 256*(a^3)*(b^6) - 108*(a^3)*(b^3)*(c^2)*d - 324*(a^4)*(b^2)*c*(d^2) - 324*(a^2)*(b^4)*c*(d^2) - 108*(a^5)*b*(d^3) - 216*(a^3)*(b^3)*(d^3) - 108*a*(b^5)*(d^3) + 768*(a^7)*c + 1536*(a^5)*(b^2)*c + 768*(a^3)*(b^4)*c - 162*(a^2)*(b^2)*(c^2)*(d^2) - 216*(a^3)*b*c*(d^3) - 216*a*(b^3)*c*(d^3) - 27*(a^4)*(d^4) - 54*(a^2)*(b^2)*(d^4) - 27*(b^4)*(d^4) + 768*(a^5)*(c^2) + 768*(a^3)*(b^2)*(c^2) - 108*a*b*(c^2)*(d^3) - 54*(a^2)*c*(d^4) - 54*(b^2)*c*(d^4) + 256*(a^3)*(c^3) - 27*(c^2)*(d^4)); tst_discriminant((x^5) + a*(x^2) + a, 108*(a^6) + 3125*(a^4)); tst_discriminant((x^5) + (a*b)*(x^2) + a, 108*(a^6)*(b^5) + 3125*(a^4)); tst_discriminant((x^5) + (a*b*c)*(x^2) + a, 108*(a^6)*(b^5)*(c^5) + 3125*(a^4)); tst_discriminant((x^5) + (a*b*c + d)*(x^2) + a, 108*(a^6)*(b^5)*(c^5) + 540*(a^5)*(b^4)*(c^4)*d + 1080*(a^4)*(b^3)*(c^3)*(d^2) + 1080*(a^3)*(b^2)*(c^2)*(d^3) + 540*(a^2)*b*c*(d^4) + 108*a*(d^5) + 3125*(a^4)); tst_discriminant((x^4) + a*(x^2) + (a + c)*x + (c^2), 16*(a^4)*(c^2) - 128*(a^2)*(c^4) + 256*(c^6) - 4*(a^5) - 8*(a^4)*c + 140*(a^3)*(c^2) + 288*(a^2)*(c^3) + 144*a*(c^4) - 27*(a^4) - 108*(a^3)*c - 162*(a^2)*(c^2) - 108*a*(c^3) - 27*(c^4)); tst_discriminant((x^4) + (a + b)*(x^2) + (a + c)*x, -4*(a^5) - 12*(a^4)*b - 12*(a^3)*(b^2) - 4*(a^2)*(b^3) - 8*(a^4)*c - 24*(a^3)*b*c - 24*(a^2)*(b^2)*c - 8*a*(b^3)*c - 4*(a^3)*(c^2) - 12*(a^2)*b*(c^2) - 12*a*(b^2)*(c^2) - 4*(b^3)*(c^2) - 27*(a^4) - 108*(a^3)*c - 162*(a^2)*(c^2) - 108*a*(c^3) - 27*(c^4)); tst_discriminant((x^4) + (a + c)*x + (c^2), 256*(c^6) - 27*(a^4) - 108*(a^3)*c - 162*(a^2)*(c^2) - 108*a*(c^3) - 27*(c^4) ); tst_discriminant((x^4) + (a + b)*(x^2) + (a + c)*x + (c^2), 16*(a^4)*(c^2) + 64*(a^3)*b*(c^2) + 96*(a^2)*(b^2)*(c^2) + 64*a*(b^3)*(c^2) + 16*(b^4)*(c^2) - 128*(a^2)*(c^4) - 256*a*b*(c^4) - 128*(b^2)*(c^4) + 256*(c^6) - 4*(a^5) - 12*(a^4)*b - 12*(a^3)*(b^2) - 4*(a^2)*(b^3) - 8*(a^4)*c - 24*(a^3)*b*c - 24*(a^2)*(b^2)*c - 8*a*(b^3)*c + 140*(a^3)*(c^2) + 132*(a^2)*b*(c^2) - 12*a*(b^2)*(c^2) - 4*(b^3)*(c^2) + 288*(a^2)*(c^3) + 288*a*b*(c^3) + 144*a*(c^4) + 144*b*(c^4) - 27*(a^4) - 108*(a^3)*c - 162*(a^2)*(c^2) - 108*a*(c^3) - 27*(c^4)); tst_discriminant((a + c)*(x^3) + (a + b)*(x^2) + (a + c)*x + (c^2), -27*(a^2)*(c^4) - 54*a*(c^5) - 27*(c^6) + 14*(a^3)*(c^2) + 6*(a^2)*b*(c^2) - 12*a*(b^2)*(c^2) - 4*(b^3)*(c^2) + 36*(a^2)*(c^3) + 36*a*b*(c^3) + 18*a*(c^4) + 18*b*(c^4) - 3*(a^4) + 2*(a^3)*b + (a^2)*(b^2) - 14*(a^3)*c + 4*(a^2)*b*c + 2*a*(b^2)*c - 23*(a^2)*(c^2) + 2*a*b*(c^2) + (b^2)*(c^2) - 16*a*(c^3) - 4*(c^4)); tst_discriminant((a^4) - 2*(a^3) + (a^2) - 3*(b^2)*a + 2*(b^4), max_var(a), 2048*(b^12) - 4608*(b^10) + 37*(b^8) + 12*(b^6)); tst_discriminant((a^4) - 2*(a^3) + (a^2) - 3*(b^2)*a + 2*(b^4), max_var(b), 2048*(a^12) - 12288*(a^11) + 26112*(a^10) - 22528*(a^9) + 5664*(a^8) + 960*(a^7) + 32*(a^6)); tst_discriminant((x^4) + a*(x^2) + b*x + c, -4*(a^3)*(b^2) + 16*(a^4)*c - 27*(b^4) + 144*a*(b^2)*c - 128*(a^2)*(c^2) + 256*(c^3)); tst_discriminant((((a-1)^2) + a*b + ((b-1)^2) - 1)*(x^3) + (a*b)*(x^2) + ((a^2) - (b^2))*x + c*a, -4*(a^8) - 4*(a^7)*b + 9*(a^6)*(b^2) + 12*(a^5)*(b^3) - 2*(a^4)*(b^4) - 12*(a^3)*(b^5) - 7*(a^2)*(b^6) + 4*a*(b^7) + 4*(b^8) + 18*(a^6)*b*c + 18*(a^5)*(b^2)*c - 4*(a^4)*(b^3)*c - 18*(a^3)*(b^4)*c - 18*(a^2)*(b^5)*c - 27*(a^6)*(c^2) - 54*(a^5)*b*(c^2) - 81*(a^4)*(b^2)*(c^2) - 54*(a^3)*(b^3)*(c^2) - 27*(a^2)*(b^4)*(c^2) + 8*(a^7) + 8*(a^6)*b - 24*(a^5)*(b^2) - 24*(a^4)*(b^3) + 24*(a^3)*(b^4) + 24*(a^2)*(b^5) - 8*a*(b^6) - 8*(b^7) - 36*(a^5)*b*c - 36*(a^4)*(b^2)*c + 36*(a^3)*(b^3)*c + 36*(a^2)*(b^4)*c + 108*(a^5)*(c^2) + 216*(a^4)*b*(c^2) + 216*(a^3)*(b^2)*(c^2) + 108*(a^2)*(b^3)*(c^2) - 4*(a^6) + 12*(a^4)*(b^2) - 12*(a^2)*(b^4) + 4*(b^6) + 18*(a^4)*b*c - 18*(a^2)*(b^3)*c - 162*(a^4)*(c^2) - 270*(a^3)*b*(c^2) - 162*(a^2)*(b^2)*(c^2) + 108*(a^3)*(c^2) + 108*(a^2)*b*(c^2) - 27*(a^2)*(c^2)); } static void tst_resultant(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x, polynomial_ref const & expected) { polynomial::manager & m = p.m(); polynomial_ref r(m); std::cout << "----------------\n"; std::cout << "p: " << p << "\n"; std::cout << "q: " << q << std::endl; r = resultant(p, q, x); std::cout << "r: " << r << "\n"; std::cout << "expected: " << expected << "\n"; if (degree(p, x) > 0 && degree(q, x) > 0) std::cout << "quasi-resultant: " << quasi_resultant(p, q, x) << "\n"; SASSERT(eq(r, expected)); m.lex_sort(r); std::cout << "r (sorted): " << r << "\n"; } static void tst_resultant(polynomial_ref const & p, polynomial_ref const & q, polynomial_ref const & expected) { tst_resultant(p, q, max_var(p), expected); } static void tst_resultant() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref a(m); polynomial_ref b(m); polynomial_ref c(m); polynomial_ref d(m); polynomial_ref x(m); a = m.mk_polynomial(m.mk_var()); b = m.mk_polynomial(m.mk_var()); c = m.mk_polynomial(m.mk_var()); d = m.mk_polynomial(m.mk_var()); x = m.mk_polynomial(m.mk_var()); tst_resultant((((a-1)^2) + a*b + ((b-1)^2) - 1)*(x^3) + (a*b)*(x^2) + ((a^2) - (b^2))*x + c*a, a*b*(x^2) - (a^2) - (b^2), -4*(a^9)*b - (a^10) - 9*(a^8)*(b^2) - 11*(a^7)*(b^3) - 14*(a^6)*(b^4) - 10*(a^5)*(b^5) - 10*(a^4)*(b^6) - 3*(a^3)*(b^7) - 5*(a^2)*(b^8) - (b^10) + 2*(a^6)*(b^3)*c + 2*(a^4)*(b^5)*c + (a^5)*(b^3)*(c^2) + 4*(a^9) + 12*(a^8)*b + 24*(a^7)*(b^2) + 32*(a^6)*(b^3) + 40*(a^5)*(b^4) + 32*(a^4)*(b^5) + 24*(a^3)*(b^6) + 16*(a^2)*(b^7) + 4*a*(b^8) + 4*(b^9) - 6*(a^8) - 12*(a^7)*b - 24*(a^6)*(b^2) - 32*(a^5)*(b^3) - 36*(a^4)*(b^4) - 28*(a^3)*(b^5) - 24*(a^2)*(b^6) - 8*a*(b^7) - 6*(b^8) + 4*(a^7) + 4*(a^6)*b + 12*(a^5)*(b^2) + 12*(a^4)*(b^3) + 12*(a^3)*(b^4) + 12*(a^2)*(b^5) + 4*a*(b^6) + 4*(b^7) - (a^6) - 3*(a^4)*(b^2) - 3*(a^2)*(b^4) - (b^6)); tst_resultant(a*(x^5) + b, c*x + d, a*(d^5) - b*(c^5)); tst_resultant(a*(x^5) + 3*(c + d)*(x^2) + 2*b, c*x + d, -2*b*(c^5) - 3*(c^4)*(d^2) - 3*(c^3)*(d^3) + a*(d^5)); tst_resultant(c*x + d, a*(x^5) + 3*(c + d)*(x^2) + 2*b, 2*b*(c^5) + 3*(c^4)*(d^2) + 3*(c^3)*(d^3) - a*(d^5)); tst_resultant((x^2) - (a^3)*(x^2) + b + 1, -49*(x^10) + 21*(x^8) + 5*(x^6) - (x^4), (a^18)*(b^4) + 4*(a^18)*(b^3) + 6*(a^18)*(b^2) - 10*(a^15)*(b^5) + 4*(a^18)*b - 56*(a^15)*(b^4) + (a^18) - 124*(a^15)*(b^3) - 17*(a^12)*(b^6) - 136*(a^15)*(b^2) - 52*(a^12)*(b^5) - 74*(a^15)*b + 10*(a^12)*(b^4) + 308*(a^9)*(b^7) - 16*(a^15) + 220*(a^12)*(b^3) + 2224*(a^9)*(b^6) + 335*(a^12)*(b^2) + 6776*(a^9)*(b^5) - 49*(a^6)*(b^8) + 208*(a^12)*b + 11280*(a^9)*(b^4) - 1316*(a^6)*(b^7) + 48*(a^12) + 11060*(a^9)*(b^3) - 7942*(a^6)*(b^6) - 2058*(a^3)*(b^9) + 6368*(a^9)*(b^2) - 22660*(a^6)*(b^5) - 18424*(a^3)*(b^8) + 1984*(a^9)*b - 36785*(a^6)*(b^4) - 72380*(a^3)*(b^7) + 2401*(b^10) + 256*(a^9) - 36064*(a^6)*(b^3) - 163592*(a^3)*(b^6) + 26068*(b^9) - 21216*(a^6)*(b^2) - 234058*(a^3)*(b^5) + 126518*(b^8) - 6912*(a^6)*b - 219344*(a^3)*(b^4) + 361508*(b^7) - 960*(a^6) - 134208*(a^3)*(b^3) + 673537*(b^6) - 51456*(a^3)*(b^2) + 855056*(b^5) - 11136*(a^3)*b + 749104*(b^4) - 1024*(a^3) + 447232*(b^3) + 174144*(b^2) + 39936*b + 4096); tst_resultant(((a - x)^2) + 2, (x^5) - x - 1, (a^10) + 10*(a^8) + 38*(a^6) - 2*(a^5) + 100*(a^4) + 40*(a^3) + 121*(a^2) - 38*a + 19); tst_resultant(c - (((a^3) - 1)*(b^2) - 1), ((a^2) - 2)*(a - 2), max_var(a), -49*(b^6) + 21*(b^4)*c + 21*(b^4) + 5*(b^2)*(c^2) + 10*(b^2)*c - (c^3) + 5*(b^2) - 3*(c^2) - 3*c - 1); tst_resultant(-49*(b^6) + 21*(b^4)*c + 21*(b^4) + 5*(b^2)*(c^2) + 10*(b^2)*c - (c^3) + 5*(b^2) - 3*(c^2) - 3*c - 1, (7*(b^4) - 2*(b^2) - 1), max_var(b), 117649*(c^12) + 1075648*(c^11) + 1651888*(c^10) - 12293120*(c^9) - 46560192*(c^8) - 9834496*(c^7) + 186855424*(c^6) + 314703872*(c^5) + 157351936*(c^4)); tst_resultant(144*(b^2) + 96*(a^2)*b + 9*(a^4) + 105*(a^2) + 70*a - 98, a*(b^2) + 6*a*b + (a^3) + 9*a, max_var(b), 81*(a^10) + 3330*(a^8) + 1260*(a^7) - 37395*(a^6) - 45780*(a^5) - 32096*(a^4) + 167720*(a^3) + 1435204*(a^2)); tst_resultant(144*(b^2) + 96*(a^2)*b + 9*(a^4) + 105*(a^2) + 70*a - 98, a*(b^2) + 6*a*b + (a^3) + 9*a, max_var(a), 11664*(b^10) + 31104*(b^9) - 119394*(b^8) - 1550448*(b^7) - 2167524*(b^6) + 7622712*(b^5) + 46082070*(b^4) + 46959720*(b^3) - 9774152*(b^2) - 35007168*b - 13984208); polynomial_ref n1(m); polynomial_ref n2(m); polynomial_ref one(m); n1 = m.mk_const(rational(10)); n2 = m.mk_const(rational(100)); one = m.mk_const(rational(1)); tst_resultant(n1, (x^2) + 2*x + 1, max_var(x), n2); tst_resultant(n1, 2*x + 1, max_var(x), n1); tst_resultant(n1, n2, 0, one); tst_resultant((x^2) + 2*x + 1, n1, max_var(x), n2); tst_resultant(2*x + 1, n1, max_var(x), n1); tst_resultant((x^2) + 8*x + 1, n1, max_var(x), n2); } static void tst_compose() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); p = (x0^3) - x0 + 3; std::cout << "p: " << p << "\np(x - y): " << compose_x_minus_y(p, 1) << "\np(x + y): " << compose_x_plus_y(p, 1) << "\np(x - x): " << compose_x_minus_y(p, 0) << "\np(x + x): " << compose_x_plus_y(p, 0) << "\n"; SASSERT(eq(compose_x_minus_y(p, 1), (x0^3) - 3*(x0^2)*x1 + 3*x0*(x1^2) - (x1^3) - x0 + x1 + 3)); SASSERT(eq(compose_x_plus_y(p, 1), (x0^3) + 3*(x0^2)*x1 + 3*x0*(x1^2) + (x1^3) - x0 - x1 + 3)); } void tst_prem() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); polynomial_ref y(m); x = m.mk_polynomial(m.mk_var()); y = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = (x^2) - 2; q = y*(x^3); std::cout << "p: " << p << "\n"; std::cout << "q: " << q << "\n"; // unsigned d; std::cout << "srem: " << exact_pseudo_remainder(q, p, 0) << "\n"; } void tst_sqrt() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); polynomial_ref y(m); x = m.mk_polynomial(m.mk_var()); y = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); p = (4*x*y + 3*(x^2)*y + (y^2) + 3)^4; polynomial_ref q(m); VERIFY(sqrt(p, q)); SASSERT(eq(p, q*q)); std::cout << "p: " << p << "\n"; std::cout << "q: " << q << "\n"; p = p - 1; SASSERT(!sqrt(p, q)); } static void tst_content(polynomial_ref const & p, polynomial::var x, polynomial_ref const & expected) { std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; std::cout << "content(p): " << content(p, x) << std::endl; std::cout << "expected: " << expected << std::endl; SASSERT(eq(content(p, x), expected)); } static void tst_primitive(polynomial_ref const & p, polynomial::var x, polynomial_ref const & expected) { std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; std::cout << "primitive(p): " << primitive(p, x) << std::endl; std::cout << "expected: " << expected << std::endl; SASSERT(eq(primitive(p, x), expected)); } static void tst_gcd(polynomial_ref const & p, polynomial_ref const & q, polynomial_ref const & expected) { std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; std::cout << "q: " << q << std::endl; polynomial_ref r(p.m()); r = gcd(p, q); std::cout << "gcd(p, q): " << r << std::endl; std::cout << "expected: " << expected << std::endl; SASSERT(eq(r, expected)); } static void tst_gcd() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); polynomial_ref three(m); three = m.mk_const(mpz(3)); std::cout << "tst_gcd\n======================\n"; tst_gcd(((x0^2) + x0*x1 + 1)*(x2*x2 + x3 + 2)*(x3*x1 + 2)*(x3*x1*x1 + x1*x2 + 1), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)*(x3*x1 + x1*x2 + 17), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)); tst_gcd((-1)*((x0^2) + x0*x1 + 1)*(x2*x2 + x3 + 2)*(x3*x1 + 2)*(x3*x1*x1 + x1*x2 + 1), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)*(x3*x1 + x1*x2 + 17), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)); tst_gcd(((x0^2) + x0*x1 + 1)*(x2*x2 + x3 + 2)*(x3*x1 + 2)*(x3*x1*x1 + x1*x2 + 1), (-1)*((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)*(x3*x1 + x1*x2 + 17), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)); tst_gcd((-1)*((x0^2) + x0*x1 + 1)*(x2*x2 + x3 + 2)*(x3*x1 + 2)*(x3*x1*x1 + x1*x2 + 1), (-1)*((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)*(x3*x1 + x1*x2 + 17), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)); tst_gcd(21*(x0 + 1), 6*x0^2, three); tst_content(x0*x1 + x0, 1, x0); tst_primitive(x0*x1 + x0, 1, x1 + 1); tst_primitive((x1^2) + x0*x1 + x0, 1, (x1^2) + x0*x1 + x0); tst_primitive((x0 + 1)*(2*x1) + 1, 1, (x0 + 1)*(2*x1) + 1); tst_primitive((x0 + 1)*(2*x1) + (x0^2)*(x0 + 1), 1, 2*x1 + (x0^2)); tst_primitive((x0 + 1)*(x2 + 1)*(x2^2)*(x0 + 1)*(x1^2) + (x0 + 1)*(x2^2)*x1 + (x0+1)*(x0+1), 1, (x2 + 1)*(x2^2)*(x0 + 1)*(x1^2) + (x2^2)*x1 + (x0+1)); tst_primitive((x0 + (x3^2))*(x2 + x3 + 1)*(x2^2)*(x1^2) + (x0 + (x3^2))*(x2 + x3 + 1)*x1 + (x0 + (x3^2))*(x2 + x3 + 1)*(x3^2), 1, (x2^2)*(x1^2) + x1 + (x3^2)); tst_content((x0 + (x3^2))*(x2 + x3 + 1)*(x2^2)*(x1^2) + (x0 + (x3^2))*(x2 + x3 + 1)*x1 + (x0 + (x3^2))*(x2 + x3 + 1)*(x3^2), 1, (x0 + (x3^2))*(x2 + x3 + 1)); tst_primitive(4*(x0 + (x3^2))*(x2 + x3 + 1)*(x2^2)*(x1^2) + 2*(x0 + (x3^2))*(x2 + x3 + 1)*x1 + 4*(x0 + (x3^2))*(x2 + x3 + 1)*(x3^2), 1, 2*(x2^2)*(x1^2) + x1 + 2*(x3^2)); tst_gcd(63*((x0^2) + x0*x1 + 1)*(x2*x2 + x3 + 2)*(x3*x1 + 2)*(x3*x1*x1 + x1*x2 + 1), 14*((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)*(x3*x1 + x1*x2 + 17), 7*((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)); } static void tst_psc(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x, polynomial_ref const & first, polynomial_ref const & second) { polynomial::manager & m = p.m(); polynomial_ref_vector S(m); std::cout << "---------" << std::endl; std::cout << "p: " << p << std::endl; std::cout << "q: " << q << std::endl; m.psc_chain(p, q, x, S); unsigned sz = S.size(); for (unsigned i = 0; i < sz; i++) { std::cout << "S_" << i << ": " << polynomial_ref(S.get(i), m) << std::endl; } if (sz > 0) { SASSERT(m.eq(S.get(0), first) || m.eq(S.get(0), neg(first))); } if (sz > 1) { SASSERT(m.eq(S.get(1), second) || m.eq(S.get(1), neg(second))); } if (sz > 0) { polynomial_ref Res(m); Res = resultant(p, q, x); SASSERT(m.eq(Res, S.get(0)) || m.eq(S.get(0), neg(Res))); } } #if 0 static void tst_psc_perf(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x) { polynomial::manager & m = p.m(); polynomial_ref_vector S(m); std::cout << "---------" << std::endl; std::cout << "p: " << p << std::endl; std::cout << "q: " << q << std::endl; m.psc_chain(p, q, x, S); unsigned sz = S.size(); for (unsigned i = 0; i < sz; i++) { std::cout << "S_" << i << ": " << m.size(S.get(i)) << std::endl; // polynomial_ref(S.get(i), m) << std::endl; } } #endif static void tst_psc() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); polynomial_ref x4(m); polynomial_ref x5(m), x6(m), x7(m), x8(m), x9(m), x10(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); x6 = m.mk_polynomial(m.mk_var()); x7 = m.mk_polynomial(m.mk_var()); x8 = m.mk_polynomial(m.mk_var()); x9 = m.mk_polynomial(m.mk_var()); x10 = m.mk_polynomial(m.mk_var()); tst_psc(x0*(x1^2) + (x0 + 1)*x1 + 2, x0*x1 + 3, 1, 6*x0 - (x0^2), x0); tst_psc(x0*(x1^4) + (x0 + 1)*(x1^3) + 2, x0*(x1^3) + 3, 1, 72*(x0^3) - (x0^4) - 27*(x0^2) - 27*(x0), 9*(x0^3)); polynomial_ref & a = x0; polynomial_ref & b = x1; polynomial_ref & c = x2; polynomial_ref & d = x3; polynomial_ref & e = x4; polynomial_ref & f = x5; polynomial_ref & x = x9; tst_psc((x^4) + a*(x^2) + b*x + c, 4*(x^3) + 2*a*x + b, 9, 16*(a^4)*c - 4*(a^3)*(b^2) - 128*(a^2)*(c^2) + 144*a*(b^2)*c - 27*(b^4) + 256*(c^3), 8*(a^3) - 32*a*c + 36*(b^2)); polynomial_ref & y = x10; tst_psc(((y^2) + 6)*(x - 1) - y*((x^2) + 1), ((x^2) + 6)*(y - 1) - x*((y^2) + 1), 10, 2*(x^6) - 22*(x^5) + 102*(x^4) - 274*(x^3) + 488*(x^2) - 552*x + 288, 5*x - (x^2) - 6 ); tst_psc(((y^3) + 6)*(x - 1) - y*((x^3) + 1), ((x^3) + 6)*(y - 1) - x*((y^3) + 1), 10, 3*(x^11) - 3*(x^10) - 37*(x^9) + 99*(x^8) + 51*(x^7) - 621*(x^6) + 1089*(x^5) - 39*(x^4) - 3106*(x^3) + 5868*(x^2) - 4968*x + 1728, (x^6) - 10*(x^4) + 12*(x^3) + 25*(x^2) - 60*x + 36); polynomial_ref p = (x^6) + a * (x^3) + b; polynomial_ref q = (x^6) + c * (x^3) + d; tst_psc(p, q, 9, (b^6) - 3*a*(b^5)*c + 3*(a^2)*(b^4)*(c^2) + 3*(b^5)*(c^2) - (a^3)*(b^3)*(c^3) - 6*a*(b^4)*(c^3) + 3*(a^2)*(b^3)*(c^4) + 3*(b^4)*(c^4) - 3*a*(b^3)*(c^5) + (b^3)*(c^6) + 3*(a^2)*(b^4)*d - 6*(b^5)*d - 6*(a^3)*(b^3)*c*d + 9*a*(b^4)*c*d + 3*(a^4)*(b^2)*(c^2)*d + 6*(a^2)*(b^3)*(c^2)*d - 12*(b^4)*(c^2)*d - 9*(a^3)*(b^2)*(c^3)*d + 6*a*(b^3)*(c^3)*d + 9*(a^2)*(b^2)*(c^4)*d - 6*(b^3)*(c^4)*d - 3*a*(b^2)*(c^5)*d + 3*(a^4)*(b^2)*(d^2) - 12*(a^2)*(b^3)*(d^2) + 15*(b^4)*(d^2) - 3*(a^5)*b*c*(d^2) + 6*(a^3)*(b^2)*c*(d^2) - 6*a*(b^3)*c*(d^2) + 9*(a^4)*b*(c^2)*(d^2) - 18*(a^2)*(b^2)*(c^2)*(d^2) + 18*(b^3)*(c^2)*(d^2) - 9*(a^3)*b*(c^3)*(d^2) + 6*a*(b^2)*(c^3)*(d^2) + 3*(a^2)*b*(c^4)*(d^2) + 3*(b^2)*(c^4)*(d^2) + (a^6)*(d^3) - 6*(a^4)*b*(d^3) + 18*(a^2)*(b^2)*(d^3) - 20*(b^3)*(d^3) - 3*(a^5)*c*(d^3) + 6*(a^3)*b*c*(d^3) - 6*a*(b^2)*c*(d^3) + 3*(a^4)*(c^2)*(d^3) + 6*(a^2)*b*(c^2)*(d^3) - 12*(b^2)*(c^2)*(d^3) - (a^3)*(c^3)*(d^3) - 6*a*b*(c^3)*(d^3) + 3*(a^4)*(d^4) - 12*(a^2)*b*(d^4) + 15*(b^2)*(d^4) - 6*(a^3)*c*(d^4) + 9*a*b*c*(d^4) + 3*(a^2)*(c^2)*(d^4) + 3*b*(c^2)*(d^4) + 3*(a^2)*(d^5) - 6*b*(d^5) - 3*a*c*(d^5) + (d^6), 3*(a^2)*c - (a^3) - 3*a*(c^2) + (c^3) ); tst_psc(x, a * x + b * c + d - e, 9, b*c + d - e, a); polynomial_ref zero(m); zero = m.mk_zero(); tst_psc( a*d*x + a*c*f + a*e - b*a, d*x + c*f + e - b, 9, zero, zero); #if 0 tst_psc_perf((x^7) + a*(x^3) + b*(x^2) + c*x + d, (x^7) + e*(x^3) + f*(x^2) + g*x + h, 9); tst_psc_perf((x^15) + a * (x^10) + b, (x^15) + c * (x^10) + d, 9); tst_psc_perf((y^5) + a * (y^4) + b * (y^3) + c * (y^2) + d * y + e, (y^5) + f * (y^4) + g * (y^3) + h * (y^2) + i * y + x, 10); #endif } static void tst_vars(polynomial_ref const & p, unsigned sz, polynomial::var * xs) { polynomial::var_vector r; p.m().vars(p, r); std::cout << "---------------\n"; std::cout << "p: " << p << "\nvars: "; for (unsigned i = 0; i < r.size(); i++) { std::cout << r[i] << " "; } std::cout << std::endl; SASSERT(r.size() == sz); std::sort(r.begin(), r.end()); std::sort(xs, xs + sz); for (unsigned i = 0; i < r.size(); i++) { SASSERT(r[i] == xs[i]); } } static void tst_vars() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); polynomial_ref x4(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); polynomial::var s023[3] = {0, 2, 3}; polynomial::var s14[2] = {1, 4}; polynomial::var s012[3] = {0, 1, 2}; polynomial::var s3[1] = {3}; polynomial::var s01234[5] = {0, 1, 2, 3, 4}; tst_vars((x0 + 1)*((x0^2) + (x3^2))*(x2*x3), 3, s023); tst_vars((x0 + x2)*((x0^2) + (x3^2))*(x2*x3), 3, s023); tst_vars(((x1 + x4 + 1)^5), 2, s14); tst_vars(((x1 + x4*x2 + 1)^4) + x0 + (x3^2), 5, s01234); tst_vars((x3 + 1)^5, 1, s3); tst_vars(x0*x1*x2, 3, s012); tst_vars(x0*x1*x2 + 1, 3, s012); } static void tst_sqf(polynomial_ref const & p, polynomial_ref const & expected) { polynomial_ref r(p.m()); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; r = square_free(p); std::cout << "sqf(p): " << r << std::endl; std::cout << "expected: " << expected << std::endl; SASSERT(is_square_free(r)); SASSERT(!eq(r, p) || is_square_free(p)); SASSERT(eq(expected, r)); } static void tst_sqf() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); tst_sqf(((x0 + x1)^2)*((x0^2) - 3)*((x2*x2 + x3 + 1)^3), (x0 + x1)*((x0^2) - 3)*(x2*x2 + x3 + 1)); tst_sqf((x0 + x1)*(x0 - x1), (x0 + x1)*(x0 - x1)); tst_sqf(((x0 + x1)^3)*(x0 - x1), (x0 + x1)*(x0 - x1)); polynomial_ref c1(m); c1 = m.mk_const(rational(3)); tst_sqf(c1, c1); polynomial_ref z(m); z = m.mk_zero(); tst_sqf(z, z); tst_sqf((x0 + x1 + x2 + x3)^5, (x0 + x1 + x2 + x3)); tst_sqf(((x0 + x1 + x2 + x3)^5) + 1, ((x0 + x1 + x2 + x3)^5) + 1); } static void tst_substitute(polynomial_ref const & p, polynomial::var x1, mpz const & v1, polynomial::var x2, mpz const & v2, polynomial_ref const & expected) { polynomial::numeral_manager & nm = p.m().m(); polynomial::var xs[2] = { x1, x2 }; scoped_mpz_vector vs(nm); vs.push_back(v1); vs.push_back(v2); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; polynomial_ref r(p.m()); r = p.m().substitute(p, 2, xs, vs.c_ptr()); std::cout << "r: " << r << std::endl; std::cout << "expected: " << expected << std::endl; SASSERT(eq(r, expected)); p.m().lex_sort(r); std::cout << "r (sorted): " << r << std::endl; } static void tst_substitute() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); tst_substitute(x0 + x1*x0 + x3, 1, mpz(1), 3, mpz(2), 2*x0 + 2); tst_substitute((x0^2) + x1*x0 + x3, 0, mpz(2), 3, mpz(2), 2*x1 + 6); tst_substitute((x0 + x1 + x2)^3, 0, mpz(2), 2, mpz(3), (x1 + 5)^3); tst_substitute(((x0 + x1 + x2)^3) + ((x0*x1 + x3)^2), 0, mpz(2), 2, mpz(3), ((x1 + 5)^3) + ((2*x1 + x3)^2)); tst_substitute((x0 + x1 + 1)^5, 2, mpz(2), 3, mpz(3), (x0 + x1 + 1)^5); polynomial_ref zero(m); zero = m.mk_zero(); tst_substitute(zero, 2, mpz(2), 3, mpz(3), zero); } static void tst_qsubstitute(polynomial_ref const & p, unsynch_mpq_manager & qm, polynomial::var x1, rational const & v1, polynomial::var x2, rational const & v2, polynomial_ref const & expected) { polynomial::var xs[2] = { x1, x2 }; scoped_mpq_vector vs(qm); vs.push_back(v1.to_mpq()); vs.push_back(v2.to_mpq()); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; polynomial_ref r(p.m()); r = p.m().substitute(p, 2, xs, vs.c_ptr()); std::cout << "r: " << r << std::endl; std::cout << "expected (modulo a constant): " << expected << std::endl; SASSERT(eq(r, normalize(expected))); p.m().lex_sort(r); std::cout << "r (sorted): " << r << std::endl; } static void tst_qsubstitute() { unsynch_mpq_manager qm; polynomial::manager m(qm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); tst_qsubstitute(x0 + x1 + x2, qm, 0, rational(1)/rational(2), 1, rational(3), 2*x2 + 2*3 + 1); tst_qsubstitute((x0^2)*x2 + x1 + x2*x0, qm, 0, rational(3)/rational(2), 1, rational(3), 9*x2 + (4*3) + (2*3)*x2); tst_qsubstitute(x0*x1*x2*(x3^2) + (x0^3)*x3 + (x1^2)*x0, qm, 0, rational(5)/rational(2), 1, rational(7)/rational(3), (2*2*3*7*5)*x2*(x3^2) + (5*5*5*3*3)*x3 + (7*7*5*2*2)); tst_qsubstitute((x2 + x3)^3, qm, 0, rational(5)/rational(2), 1, rational(7)/rational(3), (x2 + x3)^3); tst_qsubstitute((x0 + x1 + x2 + x3)^3, qm, 0, rational(5)/rational(2), 2, rational(7)/rational(3), (6*x1 + 6*x3 + 15 + 14)^3); tst_qsubstitute((x0 + 2*x1 + 11*(x2^2)*x3 + x2 + (x3^2))^3, qm, 0, rational(5)/rational(2), 3, rational(7)/rational(3), ((2*3*3*2)*x1 + (11*2*3*7)*(x2^2) + (2*3*3)*x2 + (5*9 + 7*7*2))^3); polynomial_ref zero(m); zero = m.mk_zero(); tst_qsubstitute(zero, qm, 0, rational(5)/rational(2), 3, rational(7)/rational(3), zero); } void tst_mfact(polynomial_ref const & p, unsigned num_distinct_factors) { std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; polynomial::factors fs(p.m()); factor(p, fs); std::cout << "factors:\n"; std::cout << p.m().m().to_string(fs.get_constant()) << "\n"; for (unsigned i = 0; i < fs.distinct_factors(); i++) { std::cout << "*(" << fs[i] << ")^" << fs.get_degree(i) << std::endl; } SASSERT(fs.distinct_factors() == num_distinct_factors); polynomial_ref p2(p.m()); fs.multiply(p2); SASSERT(eq(p, p2)); } static void tst_mfact() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); polynomial_ref x4(m); polynomial_ref x5(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); polynomial_ref & x = x0; tst_mfact((x0 - (x1^3))*(x0 - ((x2^3) - 2)), 2); tst_mfact((((x3^3) + 1)*x0 - (x1^3))*(x0 - ((x2^3) - 2)), 2); tst_mfact((((x3^3) + 1)*x0 - (x1^3))*((x3 - 1)*x0 - ((x2^3) - 2)), 2); tst_mfact((((x3^3) + 1)*x0 - (x1^3))*((-1)*x0 - ((x2^3) - 2)), 2); tst_mfact((-1)*(x0 - (x1^3))*(x0 - ((x2^3) - 2)), 2); tst_mfact((-1)*(x0 - (x1^3) + (x1^2) + 2)*(x0 - ((x2^3) - 2)), 2); tst_mfact(((x0 - (x1^3))*(x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*(x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((x3 - 1)*x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((-1)*x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((-1)*(x0 - (x1^3))*(x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((-1)*(x0 - (x1^3) + (x1^2) + 2)*(x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((x0 - (x1^3))*(x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*(x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((x3 - 1)*x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((-1)*x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((-1)*(x0 - (x1^3))*(x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((-1)*(x0 - (x1^3) + (x1^2) + 2)*(x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((x0 - (x1^3))*(x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*(x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((x3 - 1)*x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((-1)*x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((-1)*(x0 - (x1^3))*(x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((-1)*(x0 - (x1^3) + (x1^2) + 2)*(x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((x^5) - (x^2) + 1)*((-1)*x + 1)*((x^2) - 2*x + 3), 3); tst_mfact(11*((x^5) - (x^2) + 1)*((-1)*x + 1)*((x^2) - 2*x + 3), 3); tst_mfact(11*(7*(x^5) - (x^2) + 1)*((-1)*(x^2) + 1)*((x^2) - 2*x + 3), 4); tst_mfact(11*(7*(x^5) - (x^2) + 1)*((-1)*(x^2) + 1)*((x^2) - 2*x + 3)*((x^7) - x +2), 5); tst_mfact((7*(x^5) - (x^2) + 1)*((-1)*(x^2) + 1)*((x^2) - 2*x + 3)*((x^7) - x +2)*((x^3) - x + 1), 6); tst_mfact((7*(x^5) - (x^2) + 1)*((-1)*(x^2) + 1)*((x^2) - 2*x + 3)*((x^7) - x +2)*((x^3) - x + 1)*((x^7) - (x^5) + (x^3) + (x^2) + x + 3), 7); tst_mfact((7*(x^5) - (x^2) + 1)*((-1)*(x^2) + 1)*((x^2) - 2*x + 3)*((x^7) - x +2)* ((x^3) - x + 1)*((x^7) - (x^5) + (x^3) + (x^2) + x + 3)*(x - (x^3) + 11)* (x - 10)*(x - 9)*(33*x + 12)*((x^5) - x + 1), 12); tst_mfact((x^4) + (x^2) - 20, 3); tst_mfact((-11)*((x^5) - (x^2) + 1)*((-1)*x + 1)*((x^2) - 2*x + 3), 3); tst_mfact(x0 - 2*(x0^2) + 1, 2); tst_mfact((x0 + 1)*(x0 - 1)*(x0 + 2)*(((x1^5) - x1 - 1)^2), 4); tst_mfact((x0 + 1)*((x1 + 2)^2), 2); tst_mfact(7*(x0 + 1)*((x1 + 2)^2), 2); tst_mfact(11*(x0 + 1)*((x1 + 2)^2)*((x1 - x3)^4), 3); tst_mfact(11*(x0 + 1)*((x1 + 2)^2)*((x1 - x3)^4)*(((x0*(x2^2) + (x0 + x1)*x2 + x1))^3), 5); tst_mfact((11*(x0 + 1)*((x1 + 2)^2))^3, 2); tst_mfact((3*(x0 + 1)*(x1 + 2))^3, 2); tst_mfact((3*(x0 + 1)*(2*x1 + 2))^3, 2); tst_mfact(3*(2*(x0^2) + 4*(x1^2))*x2, 2); tst_mfact(13*((x0 - x2)^6)*((x1 - x2)^5)*((x0 - x3)^7), 3); tst_mfact((x0+1)^100, 1); tst_mfact((x0^70) - 6*(x0^65) - (x0^60) + 60*(x0^55) - 54*(x0^50) - 230*(x0^45) + 274*(x0^40) + 542*(x0^35) - 615*(x0^30) - 1120*(x0^25) + 1500*(x0^20) - 160*(x0^15) - 395*(x0^10) + 76*(x0^5) + 34, 3); tst_mfact(((x0^4) - 8*(x0^2)), 2); tst_mfact((x0^5) - 2*(x0^3) + x0 - 1, 1); tst_mfact( (x0^25) - 4*(x0^21) - 5*(x0^20) + 6*(x0^17) + 11*(x0^16) + 10*(x0^15) - 4*(x0^13) - 7*(x0^12) - 9*(x0^11) - 10*(x0^10) + (x0^9) + (x0^8) + (x0^7) + (x0^6) + 3*(x0^5) + x0 - 1, 2); tst_mfact( (x0^25) - 10*(x0^21) - 10*(x0^20) - 95*(x0^17) - 470*(x0^16) - 585*(x0^15) - 40*(x0^13) - 1280*(x0^12) - 4190*(x0^11) - 3830*(x0^10) + 400*(x0^9)+ 1760*(x0^8) + 760*(x0^7) - 2280*(x0^6) + 449*(x0^5) + 640*(x0^3) - 640*(x0^2) + 240*x0 - 32, 2); tst_mfact( x0^10, 1); polynomial_ref c(m); c = m.mk_zero(); tst_mfact(c, 0); c = m.mk_const(mpz(3)); tst_mfact(c, 0); tst_mfact(x0, 1); tst_mfact(x0 + x1, 1); tst_mfact(x0 - x1, 1); tst_mfact( (x0^10) - 10*(x0^8) + 38*(x0^6) - 2*(x0^5) - 100*(x0^4) - 40*(x0^3) + 121*(x0^2) - 38*x0 - 17, 1); tst_mfact( (x0^50) - 10*(x0^40) + 38*(x0^30) - 2*(x0^25) - 100*(x0^20) - 40*(x0^15) + 121*(x0^10) - 38*(x0^5) - 17, 1); polynomial_ref & y = x0; tst_mfact( (((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^10) + 10*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^9) + 35*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^8) + 40*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^7) - 32*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^6) - 82*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^5) - 30*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^4) - 140*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^3) - 284*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^2) - 168*((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y) - 47, 1); tst_mfact( (y^4) - 404*(y^2) + 39204, 2); tst_mfact( ((y^5) - 15552)* ((y^20)- 15708*(y^15) + rational("138771724")*(y^10)- rational("432104148432")*(y^5) + rational("614198284585616")), 2); tst_mfact( (y^25) - rational("3125")*(y^21) - rational("15630")*(y^20) + rational("3888750")*(y^17) + rational("38684375")*(y^16) + rational("95765635")*(y^15) - rational("2489846500")*(y^13) - rational("37650481875")*(y^12) - rational("190548065625")*(y^11) - rational("323785250010")*(y^10) + rational("750249453025")*(y^9) + rational("14962295699875")*(y^8) + rational("111775113235000")*(y^7) + rational("370399286731250")*(y^6) + rational("362903064503129")*(y^5) - rational("2387239013984400")*(y^4) - rational("23872390139844000")*(y^3) - rational("119361950699220000")*(y^2) - rational("298404876748050000")*y - rational("298500366308609376"), 2); tst_mfact( rational("54")*(y^24) - (y^27) - 324*(y^21) + rational("17496")*(y^18) - 34992*(y^15)+ rational("1889568")*(y^12)- 1259712*(y^9) + rational("68024448")*(y^6), 3); tst_mfact( ((y^3)- 432)*(((y^3)+54)^2)*((y^6)+108)*((y^6)+6912)*((y^6)- 324*(y^3)+37044), 5); tst_mfact( ((y^6)- 6*(y^4) - 864*(y^3) + 12*(y^2) - 5184*y + 186616)* (((y^6) - 6*(y^4) + 108*(y^3) + 12*(y^2) + 648*y + 2908)^2)* ((y^12) - 12*(y^10) + 60*(y^8) + 56*(y^6) + 6720*(y^4) + 12768*(y^2) + 13456)* ((y^12) - 12*(y^10) + 60*(y^8) + 13664*(y^6) + 414960*(y^4) + 829248*(y^2) + 47886400)* ((y^12) - 12*(y^10) - 648*(y^9)+ 60*(y^8) + 178904*(y^6) + 15552*(y^5) + 1593024*(y^4) - 24045984*(y^3) + 5704800*(y^2) - 143995968*y + 1372010896), 5); { polynomial_ref q1(m); polynomial_ref q2(m); polynomial_ref q3(m); polynomial_ref q4(m); polynomial_ref q5(m); polynomial_ref p(m); q1 = (x0^3) - 2; q2 = (x1^3) - 2; q3 = (x2^3) - 2; q4 = (x3^2) - 2; q5 = (x4^7) - x4 + 3; p = x5 - x0 - 2*x1 /* - 3*x2 - x3 */ + x4; p = resultant(p, q1, 0); std::cout << "finished resultant 1... size: " << size(p) << std::endl; p = resultant(p, q2, 1); std::cout << "finished resultant 2... size: " << size(p) << std::endl; // p = resultant(p, q3, 2); std::cout << "finished resultant 3... size: " << size(p) << std::endl; // p = resultant(p, q4, 3); std::cout << "finished resultant 4... size: " << size(p) << std::endl; p = resultant(p, q5, 4); tst_mfact(p, 2); } } static void tst_zp() { unsynch_mpz_manager z; polynomial::manager pm(z); polynomial_ref x(pm); polynomial_ref y(pm); x = pm.mk_polynomial(pm.mk_var()); y = pm.mk_polynomial(pm.mk_var()); polynomial_ref p(pm); polynomial_ref q(pm); p = (x^4) + 2*(x^3) + 2*(x^2) + x; q = (x^3) + x + 1; std::cout << "p: " << p << "\n"; std::cout << "q: " << q << "\n"; std::cout << "gcd: " << gcd(p, q) << "\n"; { polynomial::scoped_set_zp setZ3(pm, 3); polynomial_ref p3(pm); polynomial_ref q3(pm); p3 = normalize(p); q3 = normalize(q); std::cout << "p[Z_3]: " << p3 << "\n"; std::cout << "q[Z_3]: " << q3 << "\n"; std::cout << "gcd[Z_3]: " << gcd(p3, q3) << "\n"; } std::cout << "back into Z[x,y]\ngcd: " << gcd(p, q) << "\n"; p = 5*(x^2)*(y^2) + 3*(x^3) + 7*(y^3) + 3; { polynomial::scoped_set_zp setZ11(pm, 11); polynomial_ref p11(pm); std::cout << "---------------\n"; p11 = normalize(p); std::cout << "p[Z_11]: " << p11 << "\n"; p11 = pm.mk_glex_monic(p11); std::cout << "monic p[Z_11]: " << p11 << "\n"; } std::cout << "back into Z[x,y]\n"; std::cout << "p: " << p << "\n"; std::cout << "gcd: " << gcd(p, q) << "\n"; } static void tst_translate(polynomial_ref const & p, polynomial::var x0, int v0, polynomial::var x1, int v1, polynomial::var x2, int v2, polynomial_ref const & expected) { std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; polynomial::var xs[3] = { x0, x1, x2 }; mpz vs[3] = { mpz(v0), mpz(v1), mpz(v2) }; polynomial_ref r(p.m()); p.m().translate(p, 3, xs, vs, r); std::cout << "r: " << r << std::endl; SASSERT(eq(expected, r)); } static void tst_translate() { unsynch_mpq_manager qm; polynomial::manager m(qm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); tst_translate((x0^2) + x1 + 1, 0, 1, 1, 2, 3, 0, (x0^2) + 2*x0 + x1 + 4 ); tst_translate(x3 + 1, 0, 1, 1, 2, 2, 3, x3 + 1 ); tst_translate(x3 + 1, 0, 1, 1, 2, 3, 0, x3 + 1 ); tst_translate(x3 + 1, 0, 1, 1, 2, 3, 10, x3 + 11 ); tst_translate((x0^3)*(x1^2) + (x0^2)*(x1^3) + 10, 0, -3, 1, -2, 3, 0, (x0^3)*(x1^2) + (x0^2)*(x1^3) - 4*(x0^3)*x1 - 15*(x0^2)*(x1^2) - 6*x0*(x1^3) + 4*(x0^3) + 48*(x0^2)*x1 + 63*x0*(x1^2) + 9*(x1^3) - 44*(x0^2) - 180*x0*x1 - 81*(x1^2) + 156*x0 + 216*x1 - 170 ); } #if 0 static void tst_p25() { unsynch_mpq_manager qm; polynomial::manager m(qm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); polynomial_ref x4(m); polynomial_ref x5(m); polynomial_ref x6(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); x6 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); p = (x0 + x1 + x2 + x3 + x4 + x5 + x6)^25; std::cout << "size(p): " << size(p) << "\n"; } #endif static void tst_mm() { unsynch_mpq_manager qm; // pm1 and pm2 share the same monomial manager polynomial::manager * pm1_ptr = alloc(polynomial::manager, qm); polynomial::manager & pm1 = *pm1_ptr; polynomial::manager pm2(qm, &pm1.mm()); polynomial::manager pm3(qm); // pm3 has its own manager polynomial_ref p2(pm2); { polynomial_ref x0(pm1); polynomial_ref x1(pm1); polynomial_ref x2(pm1); x0 = pm1.mk_polynomial(pm1.mk_var()); x1 = pm1.mk_polynomial(pm1.mk_var()); x2 = pm1.mk_polynomial(pm1.mk_var()); polynomial_ref p1(pm1); p1 = (x0 + x1 + x2)^2; std::cout << "p1: " << p1 << "\n"; p2 = convert(pm1, p1, pm2); std::cout << "p2: " << p2 << "\n"; SASSERT(pm1.get_monomial(p1, 0) == pm2.get_monomial(p2, 0)); polynomial_ref p3(pm3); p3 = convert(pm1, p1, pm3); SASSERT(pm1.get_monomial(p1, 0) != pm3.get_monomial(p3, 0)); } dealloc(pm1_ptr); // p2 is still ok std::cout << "p2: " << p2 << "\n"; } static void tst_eval(polynomial_ref const & p, polynomial::var x0, rational v0, polynomial::var x1, rational v1, polynomial::var x2, rational v2, rational expected) { TRACE("eval_bug", tout << "tst_eval, " << p << "\n";); std::cout << "p: " << p << "\nx" << x0 << " -> " << v0 << "\nx" << x1 << " -> " << v1 << "\nx" << x2 << " -> " << v2 << "\n"; unsynch_mpq_manager qm; polynomial::simple_var2value x2v(qm); x2v.push_back(x0, v0.to_mpq()); x2v.push_back(x1, v1.to_mpq()); x2v.push_back(x2, v2.to_mpq()); scoped_mpq r(qm); p.m().eval(p, x2v, r); std::cout << "r: " << r << "\nexpected: " << expected << "\n"; scoped_mpq ex(qm); qm.set(ex, expected.to_mpq()); SASSERT(qm.eq(r, ex)); } static void tst_eval() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); tst_eval(2000*x1 - x0, 0, rational(1), 1, rational(2), 2, rational(3), rational(3999)); tst_eval(x0 + 1, 0, rational(1), 1, rational(2), 2, rational(3), rational(2)); tst_eval((x0^3) + x0 + 1, 0, rational(2), 1, rational(2), 2, rational(3), rational(11)); tst_eval((x0^3) - 2*x0 + 1, 0, rational(2), 1, rational(2), 2, rational(3), rational(5)); tst_eval((x0^3) - 2*x0 + 1, 0, rational(-2), 1, rational(2), 2, rational(3), rational(-3)); tst_eval((x0^4) - 2*x0 + x1 + 1, 0, rational(-2), 1, rational(10), 2, rational(3), rational(31)); tst_eval((x0^4) - 2*x0 + ((x0^3) + 1)*x1 + 1, 0, rational(-2), 1, rational(10), 2, rational(3), rational(-49)); tst_eval(((x0^4) - 2*x0)*(x1^2) + ((x0^3) + 1)*x1 + (x0^2) + 1, 0, rational(-2), 1, rational(10), 2, rational(3), rational(1935)); tst_eval(((x0^4) - 2*x0)*(x1^2)*(x2^3) + ((x0^3) + 1)*x1 + (x0^2) + 1, 0, rational(-2), 1, rational(10), 2, rational(0), rational(-65)); tst_eval(((x0^4) - 2*x0)*((x1^2) + 1)*(x2^3) + ((x0^3) + 1)*x1 + (x0^2) + 1, 0, rational(-2), 1, rational(10), 2, rational(1, 2), rational(375, 2)); tst_eval(x0*x1*x2, 0, rational(2), 1, rational(3), 2, rational(1), rational(6)); tst_eval(x0*x1*x2 + 1, 0, rational(2), 1, rational(3), 2, rational(1), rational(7)); polynomial_ref one(m); one = x0 - x0 + 1; tst_eval(one, 0, rational(2), 1, rational(3), 2, rational(1), rational(1)); tst_eval(x0*(x1^2)*x2 + 1, 0, rational(2), 1, rational(3), 2, rational(1), rational(19)); tst_eval(x0*(x1^2)*x2 + x1 + 1, 0, rational(2), 1, rational(3), 2, rational(1), rational(22)); tst_eval(x0*(x1^2)*x2 + x1 + 1 + (x2^2)*(2*x1 - 1), 0, rational(2), 1, rational(3), 2, rational(1), rational(27)); tst_eval((x0^5) + 1, 0, rational(2), 1, rational(3), 2, rational(1), rational(33)); tst_eval((x0^5) + x0*x1 + 1, 0, rational(2), 1, rational(1), 2, rational(5), rational(35)); tst_eval((x1^5) + x0*x1 + 1, 0, rational(2), 1, rational(1), 2, rational(5), rational(4)); tst_eval((x1^5) + x0*(x1^2) + 1, 0, rational(2), 1, rational(-2), 2, rational(5), rational(-23)); } static void tst_mk_unique() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial::cache uniq(m); polynomial_ref p(m); polynomial_ref q(m); polynomial_ref r(m); p = (x0^3) + (x2^5) + x0*x1 + x0*x1*x1 + 3*x0*x0 + 5; q = x0*x1*x1 + (x0^3) + 3*x0*x0 + (x2^5) + 5 + x0*x1; r = x0*x1*x1 + (x0^3) + 3*x0*x0 + (x2^5) + 6 + x0*x1; std::cout << "p: " << p << "\n"; std::cout << "q: " << q << "\n"; std::cout << "r: " << r << "\n"; SASSERT(m.eq(p, q)); SASSERT(!m.eq(p, r)); SASSERT(p.get() != q.get()); q = uniq.mk_unique(q); p = uniq.mk_unique(p); r = uniq.mk_unique(r); std::cout << "after mk_unique\np: " << p << "\n"; std::cout << "q: " << q << "\n"; std::cout << "r: " << r << "\n"; SASSERT(m.eq(p, q)); SASSERT(!m.eq(r, q)); SASSERT(p.get() == q.get()); } struct dummy_del_eh : public polynomial::manager::del_eh { unsigned m_counter; dummy_del_eh():m_counter(0) {} virtual void operator()(polynomial::polynomial * p) { m_counter++; } }; static void tst_del_eh() { dummy_del_eh eh1; dummy_del_eh eh2; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); m.add_del_eh(&eh1); x1 = 0; SASSERT(eh1.m_counter == 1); m.add_del_eh(&eh2); x1 = m.mk_polynomial(m.mk_var()); x1 = 0; SASSERT(eh1.m_counter == 2); SASSERT(eh2.m_counter == 1); m.remove_del_eh(&eh1); x0 = 0; x1 = m.mk_polynomial(m.mk_var()); x1 = 0; SASSERT(eh1.m_counter == 2); SASSERT(eh2.m_counter == 3); m.remove_del_eh(&eh2); x1 = m.mk_polynomial(m.mk_var()); x1 = 0; SASSERT(eh1.m_counter == 2); SASSERT(eh2.m_counter == 3); } static void tst_const_coeff() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); scoped_mpz c(nm); polynomial_ref p(m); p = (x0^2)*x1 + 3*x0 + x1; SASSERT(!m.const_coeff(p, 0, 2, c)); SASSERT(m.const_coeff(p, 0, 1, c) && c == 3); SASSERT(!m.const_coeff(p, 0, 0, c)); p = (x0^2)*x1 + 3*x0 + x1 + 1; SASSERT(!m.const_coeff(p, 0, 2, c)); SASSERT(m.const_coeff(p, 0, 1, c) && c == 3); SASSERT(!m.const_coeff(p, 0, 0, c)); p = (x0^2)*x1 + 3*x0 + 1; SASSERT(!m.const_coeff(p, 0, 2, c)); SASSERT(m.const_coeff(p, 0, 1, c) && c == 3); SASSERT(m.const_coeff(p, 0, 0, c) && c == 1); p = x1 + 3*x0 + 1; SASSERT(m.const_coeff(p, 0, 2, c) && c == 0); SASSERT(m.const_coeff(p, 0, 1, c) && c == 3); SASSERT(!m.const_coeff(p, 0, 0, c)); p = 5*(x0^2) + 3*x0 + 7; SASSERT(m.const_coeff(p, 0, 5, c) && c == 0); SASSERT(m.const_coeff(p, 0, 2, c) && c == 5); SASSERT(m.const_coeff(p, 0, 1, c) && c == 3); SASSERT(m.const_coeff(p, 0, 0, c) && c == 7); p = 5*(x0^2) + 3*x0; SASSERT(m.const_coeff(p, 0, 0, c) && c == 0); p = - x0*x1 - x1 + 1; SASSERT(!m.const_coeff(p, 0, 1, c)); } static void tst_gcd2() { // enable_trace("mgcd"); polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref one(m); one = m.mk_const(mpz(1)); tst_gcd(one, one, one); tst_gcd((5*x0 + 3*x1)*(x1 + 2)*(x2 + 3), (10*x0 + 6*x1)*(x1 + 2)*(x2 + 5), (5*x0 + 3*x1)*(x1 + 2)); tst_gcd((x0 + 1)*(x1 + 2)*(x2 + 3), (x0 + 1)*(x1 + 2)*(x2 + 5), (x0 + 1)*(x1 + 2)); tst_gcd(((x1^2) + 2*(x2^2) + 3*(x3^3))*(x1 + x2 + x3 + 1), ((x1^2) + 2*(x2^2) + 3*(x3^3))*(x1 + x2 + x3 + 2), ((x1^2) + 2*(x2^2) + 3*(x3^3))); tst_gcd(5*(x1^3) + 11 + 7*(x0^2), 5*(x1^3) + 13 + 7*(x0^2), one); tst_gcd((5*3*(x1^2) + 5*6*(x2^2) + 5*21*(x3^3))*(5*(x1^3) + 7*(x0^2) + 11), (7*3*(x1^2) + 7*6*(x2^2) + 7*21*(x3^3))*(5*(x1^3) + 7*(x0^2) + 13), (3*(x1^2) + 6*(x2^2) + 21*(x3^3))); tst_gcd((x2^6)*(x3^6) - 4*(x2^3)*(x3^6) + 2*(x2^6)*(x3^3) - 8*(x2^3)*(x3^3) + 4*(x1^3)*(x2^3)*(x3^3) - 8*(x1^3)*(x3^3) + 4*(x3^6) + 8*(x3^3) + (x2^6) - 4*(x2^3) + 4*(x1^3)*(x2^3) - 8*(x1^3) + 4 + (x1^6), (-2)*(x2^3)*(x3^6) - 4*(x2^3)*(x3^3) + 4*(x3^6) + 8*(x3^3) - 2*(x1^3)*(x3^3) - 2*(x2^3) + 4 - 2*(x1^3), one); tst_gcd((x1^2) - 2*x0 + 1 + (x0^2) + x0*x1 - 2*x1, x0*x1, one); tst_gcd((5*3*(x1^2) + 5*6*(x2^2) + 5*21*(x3^3))*(x1 + x2 + x3 + 1)*(5*(x1^3) + 7*(x0^2) + 11), (7*3*(x1^2) + 7*6*(x2^2) + 7*21*(x3^3))*(x1 + x2 + x3 + 2)*(5*(x1^3) + 7*(x0^2) + 13), (3*(x1^2) + 6*(x2^2) + 21*(x3^3))); p = 169*(x1^12)*(x2^16) - 468*x0*(x1^11)*(x2^16) + 428*(x0^2)*(x1^10)*(x2^16) - 92*(x0^3)*(x1^9)*(x2^16) - 82*(x0^4)*(x1^8)*(x2^16) + 52*(x0^5)*(x1^7)*(x2^16) - 4*(x0^6)*(x1^6)*(x2^16) - 4*(x0^7)*(x1^5)*(x2^16) + (x0^8)*(x1^4)*(x2^16) - 581*(x1^14)*(x2^14) + 1828*x0*(x1^13)*(x2^14) - 2452*(x0^2)*(x1^12)*(x2^14) + 548*(x0^3)*(x1^11)*(x2^14) + 1002*(x0^4)*(x1^10)*(x2^14) - 756*(x0^5)*(x1^9)*(x2^14) + 124*(x0^6)*(x1^8)*(x2^14) + 44*(x0^7)*(x1^7)*(x2^14) - 13*(x0^8)*(x1^6)*(x2^14) + 895*(x1^16)*(x2^12) - 1556*x0*(x1^15)*(x2^12) + 2864*(x0^2)*(x1^14)*(x2^12); tst_gcd(p, derivative(p, 2), (x1^4)*(x2^11)); tst_gcd((11*5*3)*((x0^2) + 1)*(x1 + 3), (11*5*7)*((x0^2) + 1)*(x1 + 5), (11*5)*((x0^2) + 1)); p = (x0^4)*(x3^8) - 2*(x0^4)*(x3^7) - 2*(x0^3)*(x2^3)*(x3^7) + 4*(x0^3)*(x3^7) + 2*(x0^4)*(x3^5) - 4*(x0^4)*(x3^4) - 4*(x0^3)*(x2^3)*(x3^4) + 8*(x0^3)*(x3^4) - 2*(x0^3)*(x1^3)*(x3^5) + 4*(x0^3)*(x1^3)*(x3^4) + 4*(x0^2)*(x1^3)*(x2^3)*(x3^4) - 8*(x0^2)*(x1^3)*(x3^4) + (x0^4)*(x3^6) + 2*(x0^3)*(x2^3)*(x3^6) - 4*(x0^3)*(x3^6) + 2*(x0^4)*(x3^3) + 4*(x0^3)*(x2^3)*(x3^3) - 8*(x0^3)*(x3^3) - 2*(x0^3)*(x1^3)*(x3^3) - 4*(x0^2)*(x1^3)*(x2^3)*(x3^3) + 8*(x0^2)*(x1^3)*(x3^3) + (x0^2)*(x2^6)*(x3^6) - 4*(x0^2)*(x2^3)*(x3^6) + 2*(x0^2)*(x2^6)*(x3^3) - 8*(x0^2)*(x2^3)*(x3^3) - 2*x0*(x1^3)*(x2^6)*(x3^3) + 8*x0*(x1^3)*(x2^3)*(x3^3) + 4*(x0^2)*(x3^6) + 8*(x0^2)*(x3^3) - 8*x0*(x1^3)*(x3^3) + (x0^4)*(x3^2) - 2*(x0^4)*x3 - 2*(x0^3)*(x2^3)*x3 + 4*(x0^3)*x3 - 2*(x0^3)*(x1^3)*(x3^2) + 4*(x0^3)*(x1^3)*x3 + 4*(x0^2)*(x1^3)*(x2^3)*x3 - 8*(x0^2)*(x1^3)*x3 + (x0^4) + 2*(x0^3)*(x2^3) - 4*(x0^3) - 2*(x0^3)*(x1^3) - 4*(x0^2)*(x1^3)*(x2^3) + 8*(x0^2)*(x1^3) + (x0^2)*(x2^6) - 4*(x0^2)*(x2^3) - 2*x0*(x1^3)*(x2^6) + 8*x0*(x1^3)*(x2^3) + 4*(x0^2) - 8*x0*(x1^3) + (x0^2)*(x1^6)*(x3^2) - 2*(x0^2)*(x1^6)*x3 - 2*x0*(x1^6)*(x2^3)*x3 + 4*x0*(x1^6)*x3 + (x0^2)*(x1^6) + 2*x0*(x1^6)*(x2^3) - 4*x0*(x1^6) + (x1^6)*(x2^6) - 4*(x1^6)*(x2^3) + 4*(x1^6); // polynomial_ref p1(m); // p1 = derivative(p, 0); // polynomial_ref g(m); // for (unsigned i = 0; i < 50; i++) // g = gcd(p, p1); // return; tst_gcd(p, derivative(p, 1), x0*(x2^6)*(x3^3) - (x1^3)*(x2^6) - 2*(x0^2)*(x2^3)*(x3^4) + 2*x0*(x1^3)*(x2^3)*x3 + 2*(x0^2)*(x2^3)*(x3^3) + (x0^3)*(x3^5) - 2*x0*(x1^3)*(x2^3) + x0*(x2^6) - (x0^2)*(x1^3)*(x3^2) - 4*x0*(x2^3)*(x3^3) - 2*(x0^3)*(x3^4) + 4*(x1^3)*(x2^3) + 2*(x0^2)*(x1^3)*x3 - 2*(x0^2)*(x2^3)*x3 + (x0^3)*(x3^3) + 4*(x0^2)*(x3^4) - (x0^2)*(x1^3) + 2*(x0^2)*(x2^3) - 4*x0*(x1^3)*x3 + (x0^3)*(x3^2) - 4*(x0^2)*(x3^3) + 4*x0*(x1^3) - 4*x0*(x2^3) - 2*(x0^3)*x3 + 4*x0*(x3^3) + (x0^3) - 4*(x1^3) + 4*(x0^2)*x3 - 4*(x0^2) + 4*x0 ); tst_gcd(p, derivative(p, 0), neg((-1)*x0*(x2^3)*(x3^3) + (x1^3)*(x2^3) + (x0^2)*(x3^4) - x0*(x1^3)*x3 - (x0^2)*(x3^3) + x0*(x1^3) - x0*(x2^3) + 2*x0*(x3^3) - 2*(x1^3) + (x0^2)*x3 - (x0^2) + 2*x0)); tst_gcd(p, derivative(p, 2), neg((-1)*(x0^2)*(x2^3)*(x3^6) + 2*x0*(x1^3)*(x2^3)*(x3^3) + (x0^3)*(x3^7) - (x1^6)*(x2^3) - 2*(x0^2)*(x1^3)*(x3^4) - (x0^3)*(x3^6) + x0*(x1^6)*x3 + 2*(x0^2)*(x1^3)*(x3^3) - 2*(x0^2)*(x2^3)*(x3^3) + 2*(x0^2)*(x3^6) - x0*(x1^6) + 2*x0*(x1^3)*(x2^3) - 4*x0*(x1^3)*(x3^3) + 2*(x0^3)*(x3^4) + 2*(x1^6) - 2*(x0^2)*(x1^3)*x3 - 2*(x0^3)*(x3^3) + 2*(x0^2)*(x1^3) - (x0^2)*(x2^3) + 4*(x0^2)*(x3^3) - 4*x0*(x1^3) + (x0^3)*x3 - (x0^3) + 2*(x0^2)) ); tst_gcd(((11*5*3)*(x0^2) + 1)*(x1 + 3), ((11*5*3)*(x0^2) + 1)*(x1 + 5), ((11*5*3)*(x0^2) + 1)); return; p = 169*(x1^12)*(x2^16) - 468*x0*(x1^11)*(x2^16) + 428*(x0^2)*(x1^10)*(x2^16) - 92*(x0^3)*(x1^9)*(x2^16) - 82*(x0^4)*(x1^8)*(x2^16) + 52*(x0^5)*(x1^7)*(x2^16) - 4*(x0^6)*(x1^6)*(x2^16) - 4*(x0^7)*(x1^5)*(x2^16) + (x0^8)*(x1^4)*(x2^16) - 581*(x1^14)*(x2^14) + 1828*x0*(x1^13)*(x2^14) - 2452*(x0^2)*(x1^12)*(x2^14) + 548*(x0^3)*(x1^11)*(x2^14) + 1002*(x0^4)*(x1^10)*(x2^14) - 756*(x0^5)*(x1^9)*(x2^14) + 124*(x0^6)*(x1^8)*(x2^14) + 44*(x0^7)*(x1^7)*(x2^14) - 13*(x0^8)*(x1^6)*(x2^14) + 895*(x1^16)*(x2^12) - 1556*x0*(x1^15)*(x2^12) + 2864*(x0^2)*(x1^14)*(x2^12) + 520*(x0^3)*(x1^13)*(x2^12) - 5402*(x0^4)*(x1^12)*(x2^12) + 3592*(x0^5)*(x1^11)*(x2^12) - 156*(x0^6)*(x1^10)*(x2^12) - 680*(x0^7)*(x1^9)*(x2^12) + 171*(x0^8)*(x1^8)*(x2^12) + 12*(x0^9)*(x1^7)*(x2^12) - 4*(x0^10)*(x1^6)*(x2^12) - 957*(x1^18)*(x2^10) - 1132*x0*(x1^17)*(x2^10) + 206*(x0^2)*(x1^16)*(x2^10) + 588*(x0^3)*(x1^15)*(x2^10) + 6861*(x0^4)*(x1^14)*(x2^10) - 5016*(x0^5)*(x1^13)*(x2^10) - 2756*(x0^6)*(x1^12)*(x2^10) + 3952*(x0^7)*(x1^11)*(x2^10) - 1143*(x0^8)*(x1^10)*(x2^10) - 124*(x0^9)*(x1^9)*(x2^10) + 30*(x0^10)*(x1^8)*(x2^10) + 4*(x0^11)*(x1^7)*(x2^10) - (x0^12)*(x1^6)*(x2^10) + 1404*(x1^20)*(x2^8) + 684*x0*(x1^19)*(x2^8) - 1224*(x0^2)*(x1^18)*(x2^8) - 4412*(x0^3)*(x1^17)*(x2^8) - 1442*(x0^4)*(x1^16)*(x2^8) + 4164*(x0^5)*(x1^15)*(x2^8) + 4116*(x0^6)*(x1^14)*(x2^8) - 5308*(x0^7)*(x1^13)*(x2^8) + 392*(x0^8)*(x1^12)*(x2^8) + 1600*(x0^9)*(x1^11)*(x2^8) - 468*(x0^10)*(x1^10)*(x2^8) - 24*(x0^11)*(x1^9)*(x2^8) + 6*(x0^12)*(x1^8)*(x2^8) - 594*(x1^22)*(x2^6) - 324*x0*(x1^21)*(x2^6) + 1980*(x0^2)*(x1^20)*(x2^6) + 1136*(x0^3)*(x1^19)*(x2^6) + 405*(x0^4)*(x1^18)*(x2^6) - 3916*(x0^5)*(x1^17)*(x2^6) - 396*(x0^6)*(x1^16)*(x2^6) + 1876*(x0^7)*(x1^15)*(x2^6) + 1108*(x0^8)*(x1^14)*(x2^6) - 2064*(x0^9)*(x1^13)*(x2^6) + 248*(x0^10)*(x1^12)*(x2^6) + 380*(x0^11)*(x1^11)*(x2^6) - 95*(x0^12)*(x1^10)*(x2^6) + 81*(x1^24)*(x2^4) + 108*x0*(x1^23)*(x2^4) - 432*(x0^2)*(x1^22)*(x2^4) - 276*(x0^3)*(x1^21)*(x2^4) + 481*(x0^4)*(x1^20)*(x2^4) + 144*(x0^5)*(x1^19)*(x2^4) + 788*(x0^6)*(x1^18)*(x2^4) - 1152*(x0^7)*(x1^17)*(x2^4) + 231*(x0^8)*(x1^16)*(x2^4) + 244*(x0^9)*(x1^15)*(x2^4) + 396*(x0^10)*(x1^14)*(x2^4) - 476*(x0^11)*(x1^13)*(x2^4) + 119*(x0^12)*(x1^12)*(x2^4) + 72*(x0^4)*(x1^22)*(x2^2) - 96*(x0^5)*(x1^21)*(x2^2) - 40*(x0^6)*(x1^20)*(x2^2) - 32*(x0^7)*(x1^19)*(x2^2) + 340*(x0^8)*(x1^18)*(x2^2) - 368*(x0^9)*(x1^17)*(x2^2) + 112*(x0^10)*(x1^16)*(x2^2) + 16*(x0^11)*(x1^15)*(x2^2) - 4*(x0^12)*(x1^14)*(x2^2) + 16*(x0^8)*(x1^20) - 64*(x0^9)*(x1^19) + 96*(x0^10)*(x1^18) - 64*(x0^11)*(x1^17) + 16*(x0^12)*(x1^16); polynomial_ref p_prime(m); p_prime = derivative(p, 2); tst_gcd(p, p_prime, x1^4); } #if 0 static void tst_gcd3() { enable_trace("polynomial_gcd"); enable_trace("polynomial_gcd_detail"); enable_trace("mpzzp"); polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = (x^8) + (x^6) + (x^4) + (x^3) + (x^2) + 1; q = (x^6) + (x^4) + x + 1; { polynomial::scoped_set_zp setZ2(m, 2); std::cout << "Z_p: " << nm.to_string(m.p()) << "\n"; tst_gcd(normalize(p), normalize(q), x + 1); } { polynomial::scoped_set_zp setZ3(m, 3); std::cout << "Z_p: " << nm.to_string(m.p()) << "\n"; polynomial_ref one(m); one = m.mk_const(mpz(1)); tst_gcd(normalize(p), normalize(q), one); } } static void tst_gcd4() { enable_trace("mgcd"); // enable_trace("CRA"); polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = (15*x + 15)*((x + 2)^8)*(10000*x + 1)*(x + 3); q = (6*x + 6)*((x + 20)^8)*(10000*x + 3)*(x + 30); tst_gcd(p, q, 3*x + 3); p = (3*x + 2)*((x + 2)^8)*(10000*x + 1)*(x + 3); q = (3*x + 2)*((x + 20)^8)*(10000*x + 3)*(x + 30); tst_gcd(p, q, 3*x + 2); p = ((3*x + 2)*((x + 2)^8)*(10000*x + 1)*(x + 3))^3; q = ((3*x + 2)*((x + 20)^8)*(10000*x + 3)*(x + 30))^3; tst_gcd(p, q, (3*x + 2)^3); p = ((x + 3)^10)*((x^5) - x - 1)*(x + 1)*(x + 2)*(x + 4)*(10000*x + 33)*(x + 6)*(x + 11)*(x+33)* ((x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225)* (1000000*x + 1)*(333333333*x + 1)*(77777777*x + 1)*(11111111*x + 1)*(x + 128384747)*(x + 82837437)*(x + 22848481); tst_gcd(p, derivative(p, 0), (x + 3)^9); } #endif static void tst_newton_interpolation() { // enable_trace("newton"); polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); polynomial_ref y(m); x = m.mk_polynomial(m.mk_var()); y = m.mk_polynomial(m.mk_var()); polynomial_ref p1(m), p2(m), p3(m); p1 = (-9)*y - 21; p2 = (-3)*y + 20; p3 = 5*y - 36; scoped_mpz_vector ins(nm); ins.push_back(mpz(0)); ins.push_back(mpz(1)); ins.push_back(mpz(2)); polynomial::polynomial * outs[3] = { p1.get(), p2.get(), p3.get() }; polynomial_ref r(m); { polynomial::scoped_set_zp setZ97(m, 97); m.newton_interpolation(0, 2, ins.c_ptr(), outs, r); } std::cout << "interpolation result: " << r << "\n"; SASSERT(m.eq((x^2)*y + 5*x*y + 41*x - 9*y - 21, r)); } static void tst_slow_mod_gcd() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m), x1(m), x2(m), x3(m), x4(m), x5(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m), q(m), b(m); polynomial_ref p_prime(m); p = ((x0^3)*x1*x2*x3*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 - 2)^2; q = derivative(p, 0); tst_gcd(p, q, (x0^3)*x1*x2*x3*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 - 2); b = (x0^10) + (x1^10) + (x2^10) + (x3^10); p = b*(x0 + 1); q = b*(x0 + 2); tst_gcd(p, q, b); return; p = (x0^8) * (((x0^3)*x1*x2*x3*x4*x5 + x0*(x1^3)*x2*x3*x4*x5 + x0*x1*(x2^3)*x3*x4*x5 + x0*x1*x2*(x3^3)*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 - 2)^2) * (((x0^3)*x1*x2*x3*x4*x5 + x0*(x1^3)*x2*x3*x4*x5 + x0*x1*(x2^3)*x3*x4*x5 + x0*x1*x2*(x3^3)*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 + 2)^2); p_prime = derivative(p, 0); tst_gcd(p, p_prime, (x0^7) * ((x0^3)*x1*x2*x3*x4*x5 + x0*(x1^3)*x2*x3*x4*x5 + x0*x1*(x2^3)*x3*x4*x5 + x0*x1*x2*(x3^3)*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 - 2) * ((x0^3)*x1*x2*x3*x4*x5 + x0*(x1^3)*x2*x3*x4*x5 + x0*x1*(x2^3)*x3*x4*x5 + x0*x1*x2*(x3^3)*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 + 2)); } void tst_linear_solver() { unsynch_mpq_manager qm; scoped_mpq_vector as(qm); scoped_mpq b(qm); scoped_mpq_vector xs(qm); linear_eq_solver solver(qm); solver.resize(3); xs.resize(3); as.reset(); as.push_back(mpq(2)); as.push_back(mpq(1)); as.push_back(mpq(-1)); qm.set(b, 8); solver.add(0, as.c_ptr(), b); as.reset(); as.push_back(mpq(-3)); as.push_back(mpq(-1)); as.push_back(mpq(2)); qm.set(b, -11); solver.add(1, as.c_ptr(), b); as.reset(); as.push_back(mpq(-2)); as.push_back(mpq(1)); as.push_back(mpq(2)); qm.set(b, -3); solver.add(2, as.c_ptr(), b); VERIFY(solver.solve(xs.c_ptr())); SASSERT(qm.eq(xs[0], mpq(2))); SASSERT(qm.eq(xs[1], mpq(3))); SASSERT(qm.eq(xs[2], mpq(-1))); } static void tst_lex(polynomial_ref const & p1, polynomial_ref const & p2, int lex_expected, polynomial::var min, int lex2_expected) { polynomial::manager & m = p1.m(); std::cout << "compare "; m.display(std::cout, m.get_monomial(p1, 0)); std::cout << " "; m.display(std::cout, m.get_monomial(p2, 0)); std::cout << " "; std::cout.flush(); int r1 = lex_compare(m.get_monomial(p1, 0), m.get_monomial(p2, 0)); int r2 = lex_compare2(m.get_monomial(p1, 0), m.get_monomial(p2, 0), min); SASSERT(r1 == lex_expected); SASSERT(r2 == lex2_expected); std::cout << r1 << " " << r2 << "\n"; SASSERT(lex_compare(m.get_monomial(p2, 0), m.get_monomial(p1, 0)) == -r1); SASSERT(lex_compare2(m.get_monomial(p2, 0), m.get_monomial(p1, 0), min) == -r2); } static void tst_lex() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m), x1(m), x2(m), x3(m), x4(m), x5(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); polynomial_ref one(m); one = m.mk_const(mpz(1)); tst_lex(x0*x4*x1, (x0^10)*(x1^3), 1, 4, -1); tst_lex(x0*x3*(x1^2)*x4, x0*(x3^2)*(x1^2)*x4, -1, 3, -1); tst_lex((x0^2)*x3*(x1^2)*x4, x0*(x3^2)*(x1^2)*x4, -1, 3, 1); tst_lex(x0*x3*(x1^2)*x4, x0*x3*(x1^2)*x4, 0, 3, 0); tst_lex(x0*(x3^2)*(x1^2)*x4, x0*x3*(x1^2)*x4, 1, 3, 1); tst_lex((x1^2)*x4, x0*x2*x3*x4*x5, -1, 1, -1); tst_lex((x1^2)*x3*x4, x0*x1, 1, 1, 1); tst_lex(x1*x3*x4, x2*x3*x4, -1, 2, 1); tst_lex(x1*x3*x4, x2*x3*x4, -1, 1, -1); tst_lex(x1*x3*x4, x0*x2*x3*x4, -1, 1, -1); tst_lex(x3, x4, -1, 1, -1); tst_lex(x3, x4, -1, 4, 1); tst_lex(x2*x3, x4, -1, 1, -1); tst_lex(x2*x3, x4, -1, 4, 1); tst_lex(x3, x2*x4, -1, 1, -1); tst_lex(x3, x2*x4, -1, 4, 1); tst_lex(x3, x2*x4, -1, 4, 1); tst_lex(one, x3, -1, 1, -1); tst_lex(one, x3, -1, 3, -1); tst_lex(x3, one, 1, 3, 1); tst_lex(x4*x5, (x4^3)*x5, -1, 4, -1); } static void tst_divides() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); x0 = m.mk_polynomial(m.mk_var()); polynomial_ref q(m); polynomial_ref p(m); q = 16*(x0^27) - 1984*(x0^26) + 1762*(x0^25) + 17351*(x0^24) - 14165*(x0^23) + 16460*(x0^22) + 2919*(x0^21) - 16823*(x0^20) + 1530*(x0^19) + 10646*(x0^18) + 19217*(x0^17); p = 16*(x0^39) - 3648*(x0^38) + 338136*(x0^37) - 16037936*(x0^36) + 392334357*(x0^35) - rational("3851617443")*(x0^34) - rational("14636221526")*(x0^33) + rational("377151717618")*(x0^32) + rational("677140776981")*(x0^31) - rational("4308280094419")*(x0^30) + rational("312708087606")*(x0^29) + rational("8205543533730")*(x0^28) + rational("3331586202704")*(x0^27) - rational("15291636627072")*(x0^26) + rational("433482645282")*(x0^25) + rational("7397104817486")*(x0^24) + rational("1021197979053")*(x0^23) - rational("1373737505247")*(x0^22) - rational("639394669026")*(x0^21) - rational("118513560618")*(x0^20) - rational("10405319535")*(x0^19) - rational("358722675")*(x0^18); std::cout << "----------------------\n"; std::cout << "q: " << q << "\n"; std::cout << "p: " << p << std::endl; std::cout << "divides(q, p): " << m.divides(q, p) << "\n"; } void tst_polynomial() { set_verbosity_level(1000); // enable_trace("factor"); // enable_trace("poly_bug"); // enable_trace("factor_bug"); disable_trace("polynomial"); enable_trace("psc_chain_classic"); enable_trace("Lazard"); // enable_trace("eval_bug"); // enable_trace("mgcd"); tst_psc(); return; tst_eval(); tst_divides(); tst_gcd2(); tst_slow_mod_gcd(); tst_gcd(); tst_lex(); tst_linear_solver(); tst_newton_interpolation(); tst_resultant(); // // tst_gcd4(); // tst_gcd3(); tst_zp(); tst_const_coeff(); tst_psc(); tst_del_eh(); tst_mk_unique(); tst_qsubstitute(); tst_substitute(); tst_discriminant(); tst_mfact(); tst_mm(); // tst_p25(); // return; tst_translate(); // enable_trace("mpz_gcd"); tst_vars(); tst_sqf(); enable_trace("resultant"); enable_trace("psc"); disable_trace("polynomial"); enable_trace("pseudo_remainder"); enable_trace("resultant_bug"); tst_sqrt(); tst_prem(); tst_compose(); tst11(); tst10(); tst9(); tst8(); tst7(); tst6(); tst5(); tst3(); tst2(); tst1(); tst4(); } #else void tst_polynomial() { // it takes forever to compiler these regressions using clang++ } #endif z3-z3-4.4.1/src/test/polynomial_factorization.cpp000066400000000000000000000613061260446376700220240ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial_factorization.cpp Abstract: Testing of factorization. Author: Dejan (t-dejanj) 2011-11-29 Notes: --*/ #include"upolynomial_factorization_int.h" #include"timeit.h" #include"polynomial.h" #if 0 #include"polynomial_factorization.h" #endif using std::cout; using std::endl; // some prime numbers unsigned primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 }; // [i,l]: how many factors the Knuth example has over p_i, when i = 0 it's Z, p_1 = 2, for l=0 distinct, for l = 1 total unsigned knuth_factors[2][11] = { // x^8 + x^6 + 10*x^4 + 10*x^3 + 8*x^2 + 2*x + 8 {2, 2, 3, 3, 2, 3, 1, 4, 3, 1, 1}, {8, 2, 3, 3, 2, 3, 1, 4, 3, 1, 1}, }; // [k,l,i]: how many factors the S_k has over p_i, when i = 0 it's Z, p_1 = 2, for l=0 distinct, for l = 1 total unsigned swinnerton_dyer_factors[5][2][11] = { // S1 = (x^2) - 2 { // 2, 3, 5, 7,11,13,17,19,23,29, Z {1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1}, {2, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1} }, // S2 = (x^4) - 10*(x^2) + 1 { {1, 1, 2, 2, 2, 2, 2, 2, 4, 2, 1}, {4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 1} }, // S3 = (x^8) - 40*(x^6) + 352*(x^4) - 960*(x^2) + 576 { {1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 1}, {8, 6, 4, 4, 4, 4, 4, 4, 4, 4, 1} }, // S4 = (x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225 { {1, 4, 3, 4, 8, 8, 8, 8, 8, 8, 1}, {16, 12, 10, 8, 8, 8, 8, 8, 8, 8, 1} }, // SA = S1*S2*S3*S4 { //p = 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, Z { 2, 6, 3, 6, 15, 11, 16, 15, 18, 15, 1}, {30, 21, 17, 16, 15, 15, 16, 15, 18, 15, 1} } }; int random_polynomial[20][2][11] = { { // 3*x^10 + 2*x^9 + 4*x^8 + 4*x^7 + 4*x^6 + x^5 + 3*x^2 + 3*x { 4, 3, 4, 4, 3, 4, 4, 4, 3, 4, 2 }, { 7, 7, 4, 4, 3, 4, 4, 4, 3, 4, 2 }, }, { // 4*x^9 + 4*x^8 + x^7 + x^6 + 2*x^5 + 3*x^4 + 4*x^2 + 4*x { 2, 2, 3, 3, 4, 2, 5, 3, 4, 2, 2 }, { 5, 2, 3, 3, 4, 2, 5, 3, 5, 2, 2 }, }, { // 3*x^10 + 4*x^9 + 3*x^8 + x^6 + 4*x^5 + 4*x^4 + x^2 { 3, 2, 4, 4, 5, 3, 4, 2, 4, 5, 2 }, { 6, 3, 5, 5, 6, 4, 5, 3, 5, 7, 3 }, }, { // x^10 + 4*x^9 + x^8 + 3*x^7 + 3*x^4 + 3*x^3 + x^2 + 4*x { 3, 4, 4, 3, 3, 3, 4, 4, 5, 3, 2 }, { 8, 4, 4, 3, 3, 3, 4, 4, 5, 3, 2 }, }, { // x^9 + 2*x^8 + 3*x^7 + x^6 + 2*x^5 + 4*x^4 + 3*x^2 { 3, 3, 3, 3, 4, 4, 4, 3, 3, 4, 2 }, { 5, 6, 4, 5, 5, 6, 5, 4, 4, 5, 3 }, }, { // x^10 + x^9 + 4*x^7 + x^6 + 3*x^5 + x^4 + x^3 + x { 3, 2, 3, 3, 3, 5, 3, 2, 4, 4, 2 }, { 3, 2, 3, 3, 3, 5, 3, 2, 4, 4, 2 }, }, { // 4*x^10 + 4*x^9 + x^8 + 2*x^7 + 3*x^6 + 4*x^5 + 3*x^4 + x^3 + 2*x^2 + 4*x { 3, 3, 2, 5, 3, 4, 2, 4, 5, 5, 2 }, { 5, 3, 2, 5, 3, 4, 2, 4, 5, 5, 2 }, }, { // 3*x^10 + 4*x^9 + 3*x^8 + x^7 + x^6 + 2*x^5 + x^4 + 2*x^3 + 2*x^2 + x { 3, 4, 6, 4, 4, 4, 4, 6, 6, 4, 3 }, { 4, 4, 7, 4, 4, 4, 4, 6, 6, 4, 3 }, }, { // 4*x^10 + x^9 + x^7 + 2*x^5 + 3*x^3 + x^2 + 4*x { 3, 3, 3, 4, 4, 5, 4, 5, 2, 4, 2 }, { 4, 4, 3, 4, 4, 5, 4, 5, 2, 4, 2 }, }, { // x^10 + 3*x^9 + 3*x^8 + x^7 + 3*x^6 + 3*x^5 + 3*x^4 + x^2 + 3*x { 2, 3, 4, 4, 3, 3, 4, 3, 3, 4, 2 }, { 2, 4, 5, 4, 3, 3, 4, 3, 3, 4, 2 }, }, { // x^10 + x^9 + 2*x^8 + x^7 + 4*x^6 + 2*x^5 + 3*x^4 + 4*x^3 + x^2 + 2*x { 3, 4, 4, 3, 3, 3, 3, 4, 5, 3, 2 }, { 4, 4, 4, 3, 3, 3, 3, 4, 5, 3, 2 }, }, { // 3*x^9 + x^8 + 3*x^7 + 3*x^6 + x^5 + 2*x^4 + 4*x^3 + 4*x^2 + 3*x { 4, 3, 3, 3, 5, 3, 6, 4, 2, 2, 2 }, { 6, 4, 3, 3, 5, 3, 6, 4, 2, 2, 2 }, }, { // 2*x^10 + 3*x^9 + 2*x^8 + 4*x^7 + x^6 + 3*x^5 + 2*x^3 + 3*x^2 + 2*x + 2 { 3, 3, 3, 5, 4, 5, 6, 7, 4, 6, 3 }, { 8, 4, 3, 7, 4, 5, 6, 7, 4, 7, 3 }, }, { // 3*x^10 + x^9 + 4*x^8 + 2*x^7 + x^6 + 4*x^5 + x^4 + 3*x^3 + x + 2 { 3, 3, 3, 2, 6, 4, 4, 4, 3, 3, 2 }, { 3, 3, 3, 2, 6, 5, 4, 5, 3, 3, 2 }, }, { // 4*x^10 + 2*x^9 + x^8 + x^6 + x^5 + 3*x^4 + 4*x^3 + x^2 + x { 3, 4, 2, 4, 4, 4, 4, 2, 3, 3, 2 }, { 6, 4, 2, 4, 4, 4, 4, 2, 3, 3, 2 }, }, { // 4*x^10 + 2*x^7 + 4*x^6 + 2*x^3 + x { 1, 3, 3, 3, 4, 4, 4, 3, 3, 2, 2 }, { 1, 3, 3, 3, 4, 4, 4, 3, 3, 2, 2 }, }, { // 4*x^10 + x^9 + x^8 + 4*x^7 + 4*x^4 + 2*x^2 + x + 4 { 3, 4, 2, 5, 3, 6, 3, 6, 3, 3, 2 }, { 3, 6, 2, 5, 3, 6, 3, 6, 3, 3, 2 }, }, { // 3*x^10 + 2*x^8 + x^7 + x^6 + 3*x^4 + 3*x^3 + 4*x^2 + 3*x { 4, 3, 4, 3, 3, 3, 2, 4, 4, 3, 2 }, { 5, 4, 4, 3, 3, 3, 2, 4, 4, 3, 2 }, }, { // x^10 + 2*x^9 + 2*x^6 + 4*x^3 + 4*x^2 { 1, 2, 2, 3, 3, 4, 3, 3, 3, 3, 2 }, { 10, 3, 3, 4, 4, 6, 4, 4, 4, 4, 3 }, }, { // x^10 + 2*x^9 + 2*x^8 + 4*x^7 + 4*x^6 + x^5 + x^3 + x^2 + 3*x { 2, 4, 2, 3, 3, 3, 5, 5, 6, 2, 2 }, { 2, 5, 2, 3, 3, 3, 5, 5, 6, 2, 2 }, } }; #if 0 static void tst_square_free_finite_1() { polynomial::numeral_manager nm; polynomial::manager pm(nm); // example from Knuth, p. 442 polynomial_ref x(pm); x = pm.mk_polynomial(pm.mk_var()); // polynomials \prod_{i < p} (x - i)^i for (unsigned prime_i = 0; prime_i < 5; ++ prime_i) { int p = primes[prime_i]; // make the polynomial polynomial_ref f(pm); f = x - 1; for (int i = 2; i < p; ++ i) { f = f*((x + (-i))^i); } cout << "Factoring " << f << " into square-free over Z_" << p << endl; // convert to univariate over Z_p upolynomial::zp_manager upm(nm); upm.set_zp(p); upolynomial::numeral_vector f_u; upm.to_numeral_vector(f, f_u); cout << "Input: "; upm.display(cout, f_u); cout << endl; // factor it upolynomial::zp_factors f_factors(upm); cout << "Start: " << f_factors << endl; upolynomial::zp_square_free_factor(upm, f_u, f_factors); upolynomial::numeral_vector mult; f_factors.multiply(mult); cout << "Multiplied: "; upm.display(cout, mult); cout << endl; SASSERT(upm.eq(mult, f_u)); // remove the temps upm.reset(f_u); upm.reset(mult); } } static void tst_factor_finite_1() { polynomial::numeral_manager nm; polynomial::manager pm(nm); // example from Knuth, p. 442 polynomial_ref x(pm); x = pm.mk_polynomial(pm.mk_var()); polynomial_ref K(pm); K = (x^8) + (x^6) + 10*(x^4) + 10*(x^3) + 8*(x^2) + 2*x + 8; // factor them for all the prime numbers for (unsigned prime_i = 0; prime_i < sizeof(primes)/sizeof(unsigned); ++ prime_i) { // make the Z_p unsigned prime = primes[prime_i]; upolynomial::zp_manager upm(nm); upm.set_zp(prime); // make the polynomial in Z_p upolynomial::numeral_vector K_u; upm.to_numeral_vector(K, K_u); cout << "Factoring " << K << "("; upm.display(cout, K_u); cout << ") in Z_" << prime << endl; cout << "Expecting " << knuth_factors[0][prime_i] << " distinct factors, " << knuth_factors[1][prime_i] << " total" << endl; // factor it upolynomial::zp_factors factors(upm); /* bool factorized = */ upolynomial::zp_factor(upm, K_u, factors); // check the result unsigned distinct = factors.distinct_factors(); unsigned total = factors.total_factors(); cout << "Got " << factors << endl; cout << "Thats " << distinct << " distinct factors, " << total << " total" << endl; SASSERT(knuth_factors[0][prime_i] == distinct); SASSERT(knuth_factors[1][prime_i] == total); upolynomial::numeral_vector multiplied; factors.multiply(multiplied); SASSERT(upm.eq(K_u, multiplied)); upm.reset(multiplied); // remove the temp upm.reset(K_u); } } static void tst_factor_finite_2() { polynomial::numeral_manager nm; polynomial::manager pm(nm); polynomial_ref x(pm); x = pm.mk_polynomial(pm.mk_var()); // Swinnerton-Dyer polynomials (irreducible, modular factors of degree at most 2) polynomial_ref S1 = (x^2) - 2; polynomial_ref S2 = (x^4) - 10*(x^2) + 1; polynomial_ref S3 = (x^8) - 40*(x^6) + 352*(x^4) - 960*(x^2) + 576; polynomial_ref S4 = (x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225; vector S; S.push_back(S1); S.push_back(S2); S.push_back(S3); S.push_back(S4); S.push_back(S1*S2*S3*S4); // factor all the S_i them for all the prime numbers for (unsigned S_i = 0; S_i < S.size(); ++ S_i) { for (unsigned prime_i = 0; prime_i < sizeof(primes)/sizeof(unsigned); ++ prime_i) { unsigned prime = primes[prime_i]; upolynomial::zp_manager upm(nm); upm.set_zp(prime); upolynomial::numeral_vector S_i_u; upm.to_numeral_vector(S[S_i], S_i_u); cout << "Factoring "; upm.display(cout, S_i_u); cout << " over Z_" << prime << endl; cout << "Expecting " << swinnerton_dyer_factors[S_i][0][prime_i] << " distinct factors, " << swinnerton_dyer_factors[S_i][1][prime_i] << " total" << endl; upolynomial::zp_factors factors(upm); upolynomial::zp_factor(upm, S_i_u, factors); // check the result unsigned distinct = factors.distinct_factors(); unsigned total = factors.total_factors(); cout << "Got " << factors << endl; cout << "Thats " << distinct << " distinct factors, " << total << " total" << endl; SASSERT(swinnerton_dyer_factors[S_i][0][prime_i] == distinct); SASSERT(swinnerton_dyer_factors[S_i][1][prime_i] == total); upolynomial::numeral_vector multiplied; factors.multiply(multiplied); SASSERT(upm.eq(S_i_u, multiplied)); upm.reset(multiplied); // remove the temp upm.reset(S_i_u); } } } static void tst_factor_finite_3() { polynomial::numeral_manager nm; polynomial::manager pm(nm); polynomial_ref x(pm); x = pm.mk_polynomial(pm.mk_var()); // random polynomials vector random_p; random_p.push_back( 3*(x^10) + 2*(x^9) + 4*(x^8) + 4*(x^7) + 4*(x^6) + 1*(x^5) + 3*(x^2) + 3*x + 0 ); random_p.push_back( 4*(x^9) + 4*(x^8) + 1*(x^7) + 1*(x^6) + 2*(x^5) + 3*(x^4) + 4*(x^2) + 4*x + 0 ); random_p.push_back( 3*(x^10) + 4*(x^9) + 3*(x^8) + 1*(x^6) + 4*(x^5) + 4*(x^4) + 1*(x^2) + 0 ); random_p.push_back( 1*(x^10) + 4*(x^9) + 1*(x^8) + 3*(x^7) + 3*(x^4) + 3*(x^3) + 1*(x^2) + 4*x + 0 ); random_p.push_back( 1*(x^9) + 2*(x^8) + 3*(x^7) + 1*(x^6) + 2*(x^5) + 4*(x^4) + 3*(x^2) + 0 ); random_p.push_back( 1*(x^10) + 1*(x^9) + 4*(x^7) + 1*(x^6) + 3*(x^5) + 1*(x^4) + 1*(x^3) + 1*x + 0 ); random_p.push_back( 4*(x^10) + 4*(x^9) + 1*(x^8) + 2*(x^7) + 3*(x^6) + 4*(x^5) + 3*(x^4) + 1*(x^3) + 2*(x^2) + 4*x + 0 ); random_p.push_back( 3*(x^10) + 4*(x^9) + 3*(x^8) + 1*(x^7) + 1*(x^6) + 2*(x^5) + 1*(x^4) + 2*(x^3) + 2*(x^2) + 1*x + 0 ); random_p.push_back( 4*(x^10) + 1*(x^9) + 1*(x^7) + 2*(x^5) + 3*(x^3) + 1*(x^2) + 4*x + 0 ); random_p.push_back( 1*(x^10) + 3*(x^9) + 3*(x^8) + 1*(x^7) + 3*(x^6) + 3*(x^5) + 3*(x^4) + 1*(x^2) + 3*x + 0 ); random_p.push_back( 1*(x^10) + 1*(x^9) + 2*(x^8) + 1*(x^7) + 4*(x^6) + 2*(x^5) + 3*(x^4) + 4*(x^3) + 1*(x^2) + 2*x + 0 ); random_p.push_back( 3*(x^9) + 1*(x^8) + 3*(x^7) + 3*(x^6) + 1*(x^5) + 2*(x^4) + 4*(x^3) + 4*(x^2) + 3*x + 0 ); random_p.push_back( 2*(x^10) + 3*(x^9) + 2*(x^8) + 4*(x^7) + 1*(x^6) + 3*(x^5) + 2*(x^3) + 3*(x^2) + 2*x + 2 ); random_p.push_back( 3*(x^10) + 1*(x^9) + 4*(x^8) + 2*(x^7) + 1*(x^6) + 4*(x^5) + 1*(x^4) + 3*(x^3) + 1*x + 2 ); random_p.push_back( 4*(x^10) + 2*(x^9) + 1*(x^8) + 1*(x^6) + 1*(x^5) + 3*(x^4) + 4*(x^3) + 1*(x^2) + 1*x + 0 ); random_p.push_back( 4*(x^10) + 2*(x^7) + 4*(x^6) + 2*(x^3) + 1*x + 0 ); random_p.push_back( 4*(x^10) + 1*(x^9) + 1*(x^8) + 4*(x^7) + 4*(x^4) + 2*(x^2) + 1*x + 4 ); random_p.push_back( 3*(x^10) + 2*(x^8) + 1*(x^7) + 1*(x^6) + 3*(x^4) + 3*(x^3) + 4*(x^2) + 3*x + 0 ); random_p.push_back( 1*(x^10) + 2*(x^9) + 2*(x^6) + 4*(x^3) + 4*(x^2) + 0 ); random_p.push_back( 1*(x^10) + 2*(x^9) + 2*(x^8) + 4*(x^7) + 4*(x^6) + 1*(x^5) + 1*(x^3) + 1*(x^2) + 3*x + 0 ); // factor all the randoms them for all the prime numbers for (unsigned random_i = 0; random_i < random_p.size(); ++ random_i) { for (unsigned prime_i = 0; prime_i < sizeof(primes)/sizeof(unsigned); ++ prime_i) { unsigned prime = primes[prime_i]; upolynomial::zp_manager upm(nm); upm.set_zp(prime); upolynomial::numeral_vector poly; upm.to_numeral_vector(random_p[random_i], poly); cout << "Factoring "; upm.display(cout, poly); cout << " over Z_" << prime << endl; cout << "Expecting " << swinnerton_dyer_factors[random_i][0][prime_i] << " distinct factors, " << random_polynomial[random_i][1][prime_i] << " total" << endl; upolynomial::zp_factors factors(upm); upolynomial::zp_factor(upm, poly, factors); // check the result unsigned distinct = factors.distinct_factors(); unsigned total = factors.total_factors(); cout << "Got " << factors << endl; cout << "Thats " << distinct << " distinct factors, " << total << " total" << endl; // SASSERT(random_polynomial[random_i][0][prime_i] == distinct); // SASSERT(random_polynomial[random_i][1][prime_i] == total); upolynomial::numeral_vector multiplied; factors.multiply(multiplied); bool equal = upm.eq(poly, multiplied); cout << (equal ? "equal" : "not equal") << endl; SASSERT(equal); upm.reset(multiplied); // remove the temp upm.reset(poly); } } } static void tst_factor_enumeration() { polynomial::numeral_manager nm; polynomial::manager pm(nm); polynomial_ref x(pm); x = pm.mk_polynomial(pm.mk_var()); vector factors; for (int i = 0; i < 5; ++ i) { polynomial_ref factor(pm); factor = x + i; factors.push_back(factor); } upolynomial::manager upm(nm); upolynomial::zp_manager upm_13(nm); upm_13.set_zp(13); upolynomial::zp_factors factors_13(upm_13); upolynomial::numeral constant; nm.set(constant, 10); factors_13.set_constant(constant); for (unsigned i = 0; i < 5; ++ i) { upolynomial::numeral_vector ufactor; upm_13.to_numeral_vector(factors[i], ufactor); factors_13.push_back(ufactor, 1); upm.reset(ufactor); } cout << "All: " << factors_13 << endl; upolynomial::factorization_degree_set degrees(factors_13); degrees.display(cout); cout << endl; scoped_mpz_vector left(nm), right(nm); upolynomial::ufactorization_combination_iterator it(factors_13, degrees); unsigned i = 0; it.display(cout); bool remove = false; while (it.next(remove)) { it.left(left); it.right(right); cout << "Left " << i << ": "; upm.display(cout, left); cout << endl; cout << "Right " << i << ": "; upm.display(cout, right); cout << endl; i ++; if (i % 3 == 0) { remove = true; } else { remove = false; } it.display(cout); } // SASSERT(i == 15); return; for (unsigned i = 0; i < 5; ++ i) { factors_13.set_degree(i, factors_13.get_degree(i) + i); } cout << "Different: " << factors_13 << " of degree " << factors_13.get_degree() << endl; upolynomial::factorization_degree_set degrees1(factors_13); degrees1.display(cout); cout << endl; // [0, ..., 15] polynomial_ref tmp1 = (x^3) + 1; polynomial_ref tmp2 = (x^5) + 2; polynomial_ref tmp3 = (x^7) + 3; upolynomial::numeral_vector up1, up2, up3; upm_13.to_numeral_vector(tmp1, up1); upm_13.to_numeral_vector(tmp2, up2); upm_13.to_numeral_vector(tmp3, up3); upolynomial::zp_factors tmp(upm_13); tmp.push_back(up1, 1); tmp.push_back(up2, 1); tmp.push_back(up3, 1); upm_13.reset(up1); upm_13.reset(up2); upm_13.reset(up3); cout << "Different: " << tmp << " of degree " << tmp.get_degree() << endl; upolynomial::factorization_degree_set degrees2(tmp); degrees2.display(cout); cout << endl; tmp1 = (x^2) + 1; tmp2 = (x^10) + 2; tmp3 = x + 3; upm_13.to_numeral_vector(tmp1, up1); upm_13.to_numeral_vector(tmp2, up2); upm_13.to_numeral_vector(tmp3, up3); tmp.clear(); tmp.push_back(up1, 2); tmp.push_back(up2, 1); tmp.push_back(up3, 1); cout << "Different: " << tmp << " of degree " << tmp.get_degree() << endl; upm_13.reset(up1); upm_13.reset(up2); upm_13.reset(up3); upolynomial::factorization_degree_set degrees3(tmp); degrees3.display(cout); cout << endl; degrees1.intersect(degrees3); degrees1.display(cout); cout << endl; } static void tst_factor_square_free_univariate_1(unsigned max_length) { polynomial::numeral_manager nm; upolynomial::numeral test; upolynomial::numeral p; nm.set(test, -9); nm.set(p, 5); nm.mod(test, p, test); polynomial::manager pm(nm); polynomial_ref x(pm); x = pm.mk_polynomial(pm.mk_var()); cout << "R. = QQ['x']" << endl; // let's start with \prod (p_i x^{p_{i+1} - p_{i+1}) unsigned n_primes = sizeof(primes)/sizeof(unsigned); max_length = std::min(max_length, n_primes); for(unsigned length = 1; length < max_length; ++ length) { // starting from prime_i going for length for(unsigned start_i = 0; start_i < n_primes; ++ start_i) { polynomial_ref f(pm); bool first = true; for (unsigned prime_i = 0; prime_i < length; ++ prime_i) { int p1 = primes[(start_i + prime_i) % n_primes]; int p2 = primes[(start_i + prime_i + 1) % n_primes]; if (first) { f = (p1*(x^p2) - p2); first = false; } else { f = f*(p1*(x^p2) - p2); } } upolynomial::manager upm(nm); scoped_mpz_vector f_u(nm); upm.to_numeral_vector(f, f_u); cout << "factoring "; upm.display(cout, f_u); cout << endl; cout << "expecting " << length << " factors "; upolynomial::factors factors(upm); /* bool ok = */ upolynomial::factor_square_free(upm, f_u, factors); cout << "got " << factors << endl; SASSERT(factors.distinct_factors() == length); } } } static void tst_factor_square_free_univariate_2() { polynomial::numeral_manager nm; polynomial::manager pm(nm); polynomial_ref x(pm); x = pm.mk_polynomial(pm.mk_var()); // Swinnerton-Dyer polynomials (irreducible, modular factors of degree at most 2) polynomial_ref S1 = (x^2) - 2; polynomial_ref S2 = (x^4) - 10*(x^2) + 1; polynomial_ref S3 = (x^8) - 40*(x^6) + 352*(x^4) - 960*(x^2) + 576; polynomial_ref S4 = (x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225; vector S; S.push_back(S1); S.push_back(S2); S.push_back(S3); S.push_back(S4); upolynomial::manager upm(nm); // factor all the S_i them for all the prime numbers for (unsigned S_i = 0; S_i < S.size(); ++ S_i) { upolynomial::numeral_vector S_i_u; upm.to_numeral_vector(S[S_i], S_i_u); cout << "Factoring "; upm.display(cout, S_i_u); cout << " over Z " << endl; upolynomial::factors factors(upm); upolynomial::factor_square_free(upm, S_i_u, factors); // check the result cout << "Got " << factors << endl; // remove the temp upm.reset(S_i_u); } } static void tst_factor_square_free_univariate_3() { polynomial::numeral_manager nm; polynomial::manager pm(nm); polynomial_ref x(pm); x = pm.mk_polynomial(pm.mk_var()); polynomial_ref deg70 = (x^70) - 6*(x^65) - (x^60) + 60*(x^55) - 54*(x^50) - 230*(x^45) + 274*(x^40) + 542*(x^35) - 615*(x^30) - 1120*(x^25) + 1500*(x^20) - 160*(x^15) - 395*(x^10) + 76*(x^5) + 34; upolynomial::manager upm(nm); upolynomial::numeral_vector deg70_u; upm.to_numeral_vector(deg70, deg70_u); cout << "Factoring "; upm.display(cout, deg70_u); cout << " over Z " << endl; upolynomial::factors factors(upm); upolynomial::factor_square_free(upm, deg70_u, factors); cout << "Got " << factors << endl; upm.reset(deg70_u); } #endif void tst_factor_swinnerton_dyer_big(unsigned max) { polynomial::numeral_manager nm; polynomial::manager pm(nm); polynomial_ref x(pm); x = pm.mk_polynomial(pm.mk_var()); vector roots; vector vars; unsigned n = std::min(max, static_cast(sizeof(primes)/sizeof(unsigned))); for(unsigned prime_i = 0; prime_i < n; ++ prime_i) { int prime = primes[prime_i]; cout << "Computing Swinnerton-Dyer[" << prime_i + 1 << "]" << endl; polynomial_ref y(pm); vars.push_back(pm.mk_var()); y = pm.mk_polynomial(vars.back()); polynomial_ref p(pm); p = (y^2) - prime; roots.push_back(p); polynomial_ref computation = x; for (unsigned i = 0; i < roots.size(); ++ i) { polynomial_ref var(pm); var = pm.mk_polynomial(vars[i]); computation = computation - var; } { timeit timer(true, "computing swinnerton-dyer"); for (unsigned i = 0; i < roots.size(); ++ i) { polynomial_ref tmp(pm); pm.resultant(computation, roots[i], vars[i], tmp); computation = tmp; } } cout << "Computed Swinnerton-Dyer[" << prime_i + 1 << "], degree = " << pm.total_degree(computation) << ", size = " << pm.size(computation) << endl; cout << "Starting factoring " << endl; { timeit timer(true, "factoring swinnerton-dyer"); upolynomial::manager upm(nm); scoped_mpz_vector sd_u(nm); upm.to_numeral_vector(computation, sd_u); upolynomial::factors factors(upm); upolynomial::factor_square_free(upm, sd_u, factors); cout << "Got " << factors.distinct_factors() << " factors" << endl; } } } static void tst_factor_square_free_multivariate_1(unsigned max_n) { #if 0 polynomial::numeral_manager nm; upolynomial::numeral test; upolynomial::numeral p; nm.set(test, -9); nm.set(p, 5); nm.mod(test, p, test); polynomial::manager pm(nm); polynomial_ref x(pm); x = pm.mk_polynomial(pm.mk_var()); polynomial_ref y(pm); y = pm.mk_polynomial(pm.mk_var()); // lets start simple x^n - y^n for (unsigned prime_i = 0; prime_i < sizeof(primes)/sizeof(unsigned); ++ prime_i) { unsigned prime = primes[prime_i]; if (prime > max_n) { break; } polynomial_ref f = (x^prime) - (y^prime); cout << "factoring: " << f << endl; // factor polynomial::factors factors(pm); polynomial::factor_square_free_primitive(f, factors); cout << "got: " << factors << endl; } #endif } void tst_polynomial_factorization() { enable_trace("polynomial::factorization"); // enable_trace("polynomial::factorization::bughunt"); enable_trace("polynomial::factorization::multivariate"); // enable_trace("upolynomial"); // Z_p square-free factorization tests // tst_square_free_finite_1(); // Z_p factorization tests // tst_factor_finite_1(); // tst_factor_finite_2(); // tst_factor_finite_3(); // Z factorization // tst_factor_enumeration(); // tst_factor_square_free_univariate_1(3); // tst_factor_square_free_univariate_2(); // tst_factor_square_free_univariate_3(); // tst_factor_swinnerton_dyer_big(3); // Multivariate factorization tst_factor_square_free_multivariate_1(3); } z3-z3-4.4.1/src/test/polynorm.cpp000066400000000000000000000146541260446376700165700ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "th_rewriter.h" #include "smt2parser.h" #include "arith_decl_plugin.h" #include "reg_decl_plugins.h" #include "arith_rewriter.h" #include "ast_pp.h" static expr_ref parse_fml(ast_manager& m, char const* str) { expr_ref result(m); cmd_context ctx(false, &m); ctx.set_ignore_check(true); std::ostringstream buffer; buffer << "(declare-const x Int)\n" << "(declare-const y Int)\n" << "(declare-const z Int)\n" << "(declare-const a Int)\n" << "(declare-const b Int)\n" << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); SASSERT(ctx.begin_assertions() != ctx.end_assertions()); result = *ctx.begin_assertions(); return result; } static char const* example1 = "(= (+ (- (* x x) (* 2 y)) y) 0)"; static char const* example2 = "(= (+ 4 3 (- (* x x) (* 2 y)) y) 0)"; class poly_nf { expr_ref m_coefficient; expr_ref_vector m_coefficients; expr_ref_vector m_factors; public: poly_nf(ast_manager& m): m_coefficient(m), m_coefficients(m), m_factors(m) {} expr_ref& coefficient() { return m_coefficient; } expr_ref_vector& coefficients() { return m_coefficients; } expr_ref_vector& factors() { return m_factors; } void reset() { m_coefficient.reset(); m_coefficients.reset(); m_factors.reset(); } }; class polynorm { ast_manager& m; arith_util m_arith; arith_rewriter m_arith_rw; th_rewriter m_rw; public: polynorm(ast_manager& m): m(m), m_arith(m), m_arith_rw(m), m_rw(m) {} private: expr_ref_vector mk_fresh_constants(unsigned num, sort* s) { expr_ref_vector result(m); for (unsigned i = 0; i < num; ++i) { result.push_back(m.mk_fresh_const("fresh", s)); } return result; } expr_ref_vector mk_fresh_reals(unsigned num) { return mk_fresh_constants(num, m_arith.mk_real()); } expr_ref mk_mul(unsigned num_args, expr* const* args) { expr_ref result(m); m_arith_rw.mk_mul(num_args, args, result); return result; } void nf(expr_ref& term, obj_hashtable& constants, poly_nf& poly) { expr_ref_vector& factors = poly.factors(); expr_ref_vector& coefficients = poly.coefficients(); expr_ref& coefficient = poly.coefficient(); m_rw(term); if (m_arith.is_add(term)) { factors.append(to_app(term)->get_num_args(), to_app(term)->get_args()); } else { factors.push_back(term); } for (unsigned i = 0; i < factors.size(); ++i) { expr* f = factors[i].get(); unsigned num_args = 1; expr* const* args = &f; if (m_arith.is_mul(f)) { num_args = to_app(f)->get_num_args(); args = to_app(f)->get_args(); } for (unsigned j = 0; j < num_args; ++j) { if (m_arith.is_numeral(args[j]) || constants.contains(args[j])) { //consts.push_back(args[j]); } else { // vars.push_back(args[j]); } // deal with the relevant corner cases. } #if 0 rational r; if (m_arith.is_mul(f) && m_arith.is_numeral(to_app(f)->get_arg(0), r)) { coefficients.push_back(r); factors[i] = mk_mul(to_app(f)->get_num_args()-1, to_app(f)->get_args()+1); } else if (m_arith.is_numeral(f, r)) { factors[i] = factors.back(); factors.pop_back(); SASSERT(coefficient.is_zero()); SASSERT(!r.is_zero()); coefficient = r; --i; // repeat examining 'i' } else { coefficients.push_back(rational(1)); } #endif } TRACE("polynorm", tout << mk_pp(coefficient, m) << "\n"; for (unsigned i = 0; i < factors.size(); ++i) { tout << mk_pp(factors[i].get(), m) << " * " << mk_pp(coefficients[i].get(), m) << "\n"; }); } }; // ast /// sort : ast /// func_decl : ast /// expr : ast /// app : expr /// quantifier : expr /// var : expr /// static expr_ref mk_mul(arith_util& arith, unsigned num_args, expr* const* args) { ast_manager& m = arith.get_manager(); expr_ref result(m); switch (num_args) { case 0: UNREACHABLE(); break; case 1: result = args[0]; break; default: result = arith.mk_mul(num_args, args); break; } return result; } static void nf(expr_ref& term) { ast_manager& m = term.get_manager(); expr *e1, *e2; th_rewriter rw(m); arith_util arith(m); VERIFY(m.is_eq(term, e1, e2)); term = e1; rw(term); std::cout << mk_pp(term, m) << "\n"; std::cout << arith.is_add(term) << "\n"; expr_ref_vector factors(m); vector coefficients; rational coefficient(0); if (arith.is_add(term)) { factors.append(to_app(term)->get_num_args(), to_app(term)->get_args()); } else { factors.push_back(term); } for (unsigned i = 0; i < factors.size(); ++i) { expr* f = factors[i].get(); rational r; if (arith.is_mul(f) && arith.is_numeral(to_app(f)->get_arg(0), r)) { coefficients.push_back(r); factors[i] = mk_mul(arith, to_app(f)->get_num_args()-1, to_app(f)->get_args()+1); } else if (arith.is_numeral(f, r)) { factors[i] = factors.back(); factors.pop_back(); SASSERT(coefficient.is_zero()); SASSERT(!r.is_zero()); coefficient = r; --i; // repeat examining 'i' } else { coefficients.push_back(rational(1)); } } std::cout << coefficient << "\n"; for (unsigned i = 0; i < factors.size(); ++i) { std::cout << mk_pp(factors[i].get(), m) << " * " << coefficients[i] << "\n"; } } void tst_polynorm() { ast_manager m; reg_decl_plugins(m); expr_ref fml(m); fml = parse_fml(m, example1); std::cout << mk_pp(fml, m) << "\n"; nf(fml); fml = parse_fml(m, example2); std::cout << mk_pp(fml, m) << "\n"; nf(fml); } z3-z3-4.4.1/src/test/prime_generator.cpp000066400000000000000000000013671260446376700200700ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: prime_generator.cpp Abstract: Prime generator Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include"mpz.h" #include"prime_generator.h" void tst_prime_generator() { unsynch_mpz_manager m; scoped_mpz sqrt_p(m); prime_generator gen; for (unsigned i = 0; i < 10000; i++) { uint64 p = gen(i); std::cout << p << ", "; if (i % 11 == 0) std::cout << "\n"; std::cout.flush(); if (p == 2) continue; m.set(sqrt_p, p); m.root(sqrt_p, 2); uint64 k = m.get_uint64(sqrt_p); for (uint64 i = 2; i <= k; i++) { SASSERT(p % i != 0); } } std::cout << std::endl; } z3-z3-4.4.1/src/test/proof_checker.cpp000066400000000000000000000016351260446376700175150ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "proof_checker.h" #include "ast_ll_pp.h" void tst_checker1() { ast_manager m(PGM_FINE); expr_ref a(m); proof_ref p1(m), p2(m), p3(m), p4(m); bool result; expr_ref_vector side_conditions(m); a = m.mk_const(symbol("a"), m.mk_bool_sort()); p1 = m.mk_hypothesis(a.get()); p2 = m.mk_hypothesis(m.mk_not(a.get())); ast_ll_pp(std::cout, m, p1.get()); ast_ll_pp(std::cout, m, p2.get()); proof* proofs[2] = { p1.get(), p2.get() }; p3 = m.mk_unit_resolution(2, proofs); p4 = m.mk_lemma(p3.get(), a.get()); ast_ll_pp(std::cout, m, p4.get()); proof_checker checker(m); p4 = m.mk_lemma(p3.get(), m.mk_or(a.get(), m.mk_not(a.get()))); ast_ll_pp(std::cout, m, p4.get()); result = checker.check(p4.get(), side_conditions); SASSERT(result); } void tst_proof_checker() { tst_checker1(); } z3-z3-4.4.1/src/test/qe_arith.cpp000066400000000000000000000076461260446376700165100ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe_arith.h" #include "qe.h" #include "th_rewriter.h" #include "smt2parser.h" #include "arith_decl_plugin.h" #include "reg_decl_plugins.h" #include "arith_rewriter.h" #include "ast_pp.h" #include "qe_util.h" #include "smt_context.h" #include "expr_abstract.h" static expr_ref parse_fml(ast_manager& m, char const* str) { expr_ref result(m); cmd_context ctx(false, &m); ctx.set_ignore_check(true); std::ostringstream buffer; buffer << "(declare-const x Real)\n" << "(declare-const y Real)\n" << "(declare-const z Real)\n" << "(declare-const u Real)\n" << "(declare-const v Real)\n" << "(declare-const t Real)\n" << "(declare-const a Real)\n" << "(declare-const b Real)\n" << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); SASSERT(ctx.begin_assertions() != ctx.end_assertions()); result = *ctx.begin_assertions(); return result; } static char const* example1 = "(and (<= x 3.0) (<= (* 3.0 x) y) (<= z y))"; static char const* example2 = "(and (<= z x) (<= x 3.0) (<= (* 3.0 x) y) (<= z y))"; static char const* example3 = "(and (<= z x) (<= x 3.0) (< (* 3.0 x) y) (<= z y))"; static char const* example4 = "(and (<= z x) (<= x 3.0) (not (>= (* 3.0 x) y)) (<= z y))"; static char const* example5 = "(and (<= y x) (<= z x) (<= x u) (<= x v) (<= x t))"; static char const* example6 = "(and (<= 0 (+ x z))\ (>= y x) \ (<= y x)\ (<= (- u y) 0.0)\ (>= x (+ v z))\ (>= x 0.0)\ (<= x 1.0))"; static void test(char const *ex) { smt_params params; params.m_model = true; ast_manager m; reg_decl_plugins(m); arith_util a(m); expr_ref fml = parse_fml(m, ex); app_ref_vector vars(m); expr_ref_vector lits(m); vars.push_back(m.mk_const(symbol("x"), a.mk_real())); flatten_and(fml, lits); smt::context ctx(m, params); ctx.assert_expr(fml); lbool result = ctx.check(); SASSERT(result == l_true); ref md; ctx.get_model(md); expr_ref pr = qe::arith_project(*md, vars, lits); std::cout << mk_pp(fml, m) << "\n"; std::cout << mk_pp(pr, m) << "\n"; } static void test2(char const *ex) { smt_params params; params.m_model = true; ast_manager m; reg_decl_plugins(m); arith_util a(m); expr_ref fml = parse_fml(m, ex); app_ref_vector vars(m); expr_ref_vector lits(m); vars.push_back(m.mk_const(symbol("x"), a.mk_real())); vars.push_back(m.mk_const(symbol("y"), a.mk_real())); vars.push_back(m.mk_const(symbol("z"), a.mk_real())); flatten_and(fml, lits); smt::context ctx(m, params); ctx.push(); ctx.assert_expr(fml); lbool result = ctx.check(); SASSERT(result == l_true); ref md; ctx.get_model(md); ctx.pop(1); std::cout << mk_pp(fml, m) << "\n"; expr_ref pr2(m), fml2(m); expr_ref_vector bound(m); ptr_vector sorts; svector names; for (unsigned i = 0; i < vars.size(); ++i) { bound.push_back(vars[i].get()); names.push_back(vars[i]->get_decl()->get_name()); sorts.push_back(m.get_sort(vars[i].get())); } expr_abstract(m, 0, bound.size(), bound.c_ptr(), fml, fml2); fml2 = m.mk_exists(bound.size(), sorts.c_ptr(), names.c_ptr(), fml2); qe::expr_quant_elim qe(m, params); expr_ref pr1 = qe::arith_project(*md, vars, lits); qe(m.mk_true(), fml2, pr2); std::cout << mk_pp(pr1, m) << "\n"; std::cout << mk_pp(pr2, m) << "\n"; expr_ref npr2(m); npr2 = m.mk_not(pr2); ctx.push(); ctx.assert_expr(pr1); ctx.assert_expr(npr2); VERIFY(l_false == ctx.check()); ctx.pop(1); } void tst_qe_arith() { test2(example6); return; test(example1); test(example2); test(example3); test(example4); test(example5); } z3-z3-4.4.1/src/test/quant_elim.cpp000066400000000000000000000457201260446376700170450ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast.h" #include "smt_params.h" #include "simplifier.h" #include "qe.h" #include "basic_simplifier_plugin.h" #include "arith_simplifier_plugin.h" #include "array_simplifier_plugin.h" #include "bv_simplifier_plugin.h" #include "ast_pp.h" #include "smtlib.h" #include "smtparser.h" #include "lbool.h" #include #include "reg_decl_plugins.h" static void test_qe(ast_manager& m, lbool expected_outcome, expr* fml, char const* option) { // enable_trace("bit2int"); //enable_trace("gomory_cut"); enable_trace("final_check_arith"); enable_trace("arith_final_check"); //enable_trace("arith_branching"); enable_trace("theory_arith_int"); enable_trace("presburger"); enable_trace("quant_elim"); // enable_trace("arith_simplifier_plugin"); // enable_trace("non_linear"); // enable_trace("gomory_cut_detail"); // enable_trace("arith"); // enable_trace("bv"); // enable_trace("after_search"); // enable_trace("bv_bit_prop"); simplifier simp(m); smt_params params; // params.m_quant_elim = true; std::cout << mk_pp(fml, m) << "\n"; qe::expr_quant_elim qe(m, params); expr_ref result(m); qe(m.mk_true(), fml, result); std::cout << " -> " << mk_pp(result, m) << " " << expected_outcome << "\n"; if (expected_outcome == l_true && !m.is_true(result)) { std::cout << "ERROR: expected true, instead got " << mk_pp(result, m) << "\n"; //exit(-1); } if (expected_outcome == l_false && !m.is_false(result)) { std::cout << "ERROR: expected false, instead got " << mk_pp(result, m) << "\n"; //exit(-1); } } static void test_formula(lbool expected_outcome, char const* fml) { ast_manager m; reg_decl_plugins(m); scoped_ptr parser = smtlib::parser::create(m); parser->initialize_smtlib(); std::ostringstream buffer; buffer << "(benchmark presburger :status unknown :logic AUFLIA :extrapreds ((p1) (p2) (p3)) " << ":extrafuns ((a Int) (b Int))\n" << ":extrapreds ((p) (q) (r))\n" << ":datatypes ((list (nil) (cons (hd Int) (tl list))))\n" << ":datatypes ((cell (cnil) (ccons (car cell) (cdr cell))))\n" << ":extrasorts (U)\n" << ":extrafuns ((f U U))\n" << ":formula " << fml << ")"; parser->parse_string(buffer.str().c_str()); smtlib::benchmark* b = parser->get_benchmark(); smtlib::theory::expr_iterator it = b->begin_formulas(); smtlib::theory::expr_iterator end = b->end_formulas(); for (; it != end; ++it) { test_qe(m, expected_outcome, *it, 0); } } void tst_quant_elim() { disable_debug("heap"); test_formula(l_undef, "(exists ((p1 Bool) (q1 Bool) (r1 Bool))\ (and (or (not p1) (not q1) r1)\ (or (and (not p) (not q) (not p1) q1)\ (and (not p) q p1 (not q1))\ (and p (not q) p1 q1)\ (and p q p1 q1))\ (or (and (not r) (not r1))\ (and (= p p1) (= q q1) r r1)\ (and (not (and (= p p1) (= q q1))) (not (= r r1))))))"); test_formula(l_false,"(forall (x Int) (y Int) (or (= x 0) (< (* 5 y) (* 6 x)) (> (* 5 y) (* 6 x))))"); test_formula(l_false, "(forall (a Int) (b Int) (exists (x Int) (and (< a (* 20 x)) (< (* 20 x) b))))"); test_formula(l_undef, "(exists (u U) (= (f u) u))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 7 v))))))))"); test_formula(l_true, "(forall (x Int) (y Int) (implies (= (* 6 x) (* 5 y)) (exists (d Int) (= y (* 3 d)))))"); test_formula(l_undef, "(exists (x Int) (= (- a (mod x 4)) 0))"); // return; // test_formula(l_true, "(exists (x Int) (y Int) (= 1 (+ (* 5 x) (* 3 y))))"); test_formula(l_undef, "(exists (a Bool) (b Bool) (or (and p1 a) (and p2 (not b))))"); test_formula(l_false, "(forall (x Int) (q1 Int) (q2 Int) (r1 Int) (r2 Int) " " (implies " " (and (< x 4699) " " (= (* 2622 x) (+ (* 65536 q1) r1)) " " (<= 0 q1) " " (<= 0 r1) " " (< r1 65536) " " (= x (+ (* 100 q2) r2)) " " (<= 0 q2) " " (<= 0 r2) " " (< r2 100)) " " (= q1 q2)))"); test_formula(l_undef, "(forall (l list) (or (= l nil) (exists (x Int) (ll list) (= l (cons x ll)))))"); test_formula(l_false, "(exists (x Real) (forall (y Real) (>= x y)))"); test_formula(l_false, "(exists (x Real) (forall (y Real) (> x y)))"); test_formula(l_false, "(exists (x Real) (forall (y Real) (< x y)))"); test_formula(l_false, "(exists (x Real) (forall (y Real) (<= x y)))"); test_formula(l_true, "(exists (x Real) (exists (y Real) (< x y)))"); test_formula(l_true, "(exists (x Real) (exists (y Real) (<= x y)))"); test_formula(l_true, "(exists (x Real) (exists (y Real) (>= x y)))"); test_formula(l_true, "(exists (x Real) (exists (y Real) (> x y)))"); test_formula(l_true, "(forall (x Real) (exists (y Real) (< x y)))"); test_formula(l_true, "(forall (x Real) (exists (y Real) (<= x y)))"); test_formula(l_true, "(forall (x Real) (exists (y Real) (>= x y)))"); test_formula(l_true, "(forall (x Real) (exists (y Real) (> x y)))"); test_formula(l_false, "(forall (x Real) (forall (y Real) (< x y)))"); test_formula(l_false, "(forall (x Real) (forall (y Real) (<= x y)))"); test_formula(l_false, "(forall (x Real) (forall (y Real) (>= x y)))"); test_formula(l_false, "(forall (x Real) (forall (y Real) (> x y)))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 5 v))))))))"); test_formula(l_false, "(forall (d Int) (implies (>= d 0) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (+ (* 3 x) (* 5 y)))))))"); test_formula(l_true, "(forall (y Int) (implies (exists (d Int) (= y (* 6 d))) (exists (d Int) (= y (* 2 d)))))"); test_formula(l_true, "(forall (y Int) (implies (exists (d Int) (= y (* 65 d))) (exists (d Int) (= y (* 5 d)))))"); test_formula(l_true, "(exists (z Int) (forall (w Int) (exists (x Int) (y Int) " " (or (and (< (+ (* 3 x) w) 2) (< 1 (- (+ (* 2 x) z) w))) " " (and (< z (* 2 y)) (> z y))))))"); test_formula(l_true, "(exists (x Int) (y Int) (and (> x 0) (>= y 0) (= 1 (- (* 3 x) (* 5 y)))))"); test_formula(l_true, "(exists (a Int) (b Int) " " (and (not (= a 1)) (= a b) (or (= a (* 2 b)) (= (* 2 b) (+ 1 (* 3 a))))))"); test_formula(l_true, "(forall (x Int) (iff (and (not (= 0 (mod x 2))) (= 0 (mod (- x 1) 3))) " " (or (= 0 (mod (- x 1) 12)) (= 0 (mod (- x 7) 12)))))"); test_formula(l_false, "(exists (x Int) (and (< (* 3 x) 2) (< 1 (* 2 x))))"); test_formula(l_true, "(forall (x Int) (y Int) (or (= 0 (mod x 5)) (not (= (* 6 x) (* 5 y)))))"); test_formula(l_false, "(forall (x Int) (exists (y Int) (= x (* 2 y))))"); test_formula(l_false, "(forall (x Int) " " (implies (not (= 0 (mod x 2))) " " (or (= 0 (mod (- x 1) 4)) " " (= 0 (mod (- x 1) 8)) " " (= 0 (mod (- x 3) 8)) " " (= 0 (mod (- x 1) 6)) " " (= 0 (mod (- x 1) 14)) " " (= 0 (mod (- x 9) 14)) " " (= 0 (mod (- x 11) 14)) " " (= 0 (mod (- x 5) 24)) " " (= 0 (mod (- x 11) 24))))) "); test_formula(l_true, "(forall (x Int) (iff (and (not (= 0 (mod x 2))) (= 0 (mod (- x 1) 3))) " " (or (= 0 (mod (- x 1) 12)) (= 0 (mod (- x 7) 12)))))"); test_formula(l_false, "(forall (d Int) (c Int) (b Int) " " (and (= c 0) (= d (* b c)) (= d 0)))"); //return; test_formula(l_undef, "(exists (k!12 Int) (k!11 Int) (and (= (ite (= k!11 0) 0 k!11) k!11) (not (= (ite (= k!12 (+ 1)) 1 0) 0))))"); //return; test_formula(l_false, "(forall (a Int) (b Int) (x Int) (y Int) (z Int) " " (implies (and (= (+ a 2) b) (= x (+ 1 (- b a))) (= y (- b 2)) (= z 3)) false))"); test_formula(l_false, "(exists (a Int) (b Int) " " (and (> a 1) (> b 1) (= a b) (or (= a (* 2 b)) (= (* 2 b) (+ 1 (* 3 a))))))"); test_formula(l_true, "(forall (d Int) (implies true (exists (x Int) (y Int) (and true true (= d (+ (* 3 x) (* 5 y)))))))"); // This one takes forever without bit-vectors test_formula(l_true, "(forall (d Int) (implies (>= d 8) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (+ (* 3 x) (* 5 y)))))))"); test_formula(l_true, "(forall (d Int) (implies (>= d 0) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (- (* 3 x) (* 5 y)))))))"); test_formula(l_false, "(exists (x Int) (y Int) (z Int) (= 1 (- (* 4 x) (* 6 y))))"); //return; test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 8 v))))))))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 8 v))))))))"); #if 0 // too slow. test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 7 u) (* 8 v))))))))"); #endif test_formula(l_true, "(forall (x Int) (exists (y Int) (and (<= (* 2 y) x) (< x (* 2 (+ y 1))))))"); test_formula(l_false, "(exists (x Int) (y Int) (and (> y 0) (> y (* 2 x)) (< y (+ x 2)) (= 0 (mod y 2))))"); test_formula(l_false, "(exists (x Int) (and (< (* 3 x) 3) (< 1 (* 2 x))))"); test_formula(l_true, "(exists (x Int) (and (< (* 3 x) 4) (< 1 (* 2 x))))"); test_formula(l_false, "(exists (x Int) (and (< (+ (* 3 x) 1) 10) (> (- (* 7 x) 6) 7) (= 0 (mod x 3))))"); test_formula(l_false, "(exists (x Int) (y Int) (and (< (- 1 (* 5 y)) x) (< (+ 1 y) (* 13 x)) (< (+ x 2) 0) (> y 0)))"); test_formula(l_false, "(exists (x Int) (y Int) (and (< (- 1 (* 5 y)) x) (< (+ 1 y) (* 13 x)) (< x -2)))"); test_formula(l_true, "(exists (w Int) (z Int) (y Int) (x Int) (and (< (- 1 (* 5 y)) (+ x (* 2 z))) (< (+ 1 y w (* -4 z)) (* 13 x)) (< x -2) (> z 0)))"); test_formula(l_true, "(forall (w Int) " " (exists (z Int) (y Int) (x Int) " " (and (< (- 1 (* 5 y)) (+ x (* 2 z))) " " (< (- (+ 1 y) (* 4 z)) (* 13 x)) " " (< x -2) (> z 0) (< x 10)))) "); test_formula(l_false, "(forall (d Int) (c Int) (b Int) " " (and (= c 0) (= d (* b c)) (= d 4)))"); test_formula(l_undef, "(exists (d Int) (c Int) (b Int) " " (and (= c 0) (= d (* b c)) (= d 0)))"); test_formula(l_undef, "(exists (d Int) (c Int) (b Int) " " (and (= c 0) (= d (* b c)) (= d 4)))"); // Tests from Harrison's HOL-light version of Cooper. test_formula(l_true, "(forall (x Int) (y Int) (not (= (+ 1 (* 2 x)) (* 2 y))))"); test_formula(l_false, "(exists (x Int) (y Int) (= 1 (- (* 4 x) (* 6 y))))"); // "(forall (x Int) (implies (< b x) (<= a x)))" // "(forall (x Int) (implies (< b x) (< a x)))" test_formula(l_false, "(forall (d Int) (implies (>= d 0) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (+ (* 3 x) (* 5 y)))))))"); test_formula(l_true, "(forall (d Int) (implies true (exists (x Int) (y Int) (and true true (= d (+ (* 3 x) (* 5 y)))))))"); // This one takes forever without bit-vectors test_formula(l_true, "(forall (d Int) (implies (>= d 8) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (+ (* 3 x) (* 5 y)))))))"); test_formula(l_true, "(forall (d Int) (implies (>= d 0) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (- (* 3 x) (* 5 y)))))))"); test_formula(l_true, "(exists (x Int) (y Int) (and (> x 0) (>= y 0) (= 1 (- (* 3 x) (* 5 y)))))"); test_formula(l_false, "(exists (x Int) (y Int) (z Int) (= 1 (- (* 4 x) (* 6 y))))"); // "(forall (x Int) (implies (< b (* 3 x)) (a < (* 3 x))))" test_formula(l_false, "(forall (x Int) (y Int) (implies (<= x y) (< (+ 1 (* 2 x)) (* 2 y))))"); test_formula(l_true, "(forall (x Int) (y Int) (z Int) (implies (= (+ 1 (* 2 x)) (* 2 y)) (> (+ x y z) 129)))"); // Formula examples from Cooper's paper. test_formula(l_true, "(forall (a Int) (exists (b Int) (or (< a (+ (* 4 b) (* 3 a))) (and (not (< a b)) (> a (+ b 1))))))"); test_formula(l_false, "(exists (y Int) (forall (x Int) (and (> (+ x (* 5 y)) 1) (> (- (* 13 x) y) 1) (< (+ x 2) 0))))"); // Harrison's formulas: test_formula(l_false, "(forall (x Int) (y Int) (implies (and (>= x 0) (>= y 0)) (or (< (- (* 12 x) (* 8 y)) 0) (> (- (* 12 x) (* 8 y)) 2))))"); // test_formula(l_true, "(exists (x Int) (y Int) (= 1 (+ (* 5 x) (* 3 y))))"); test_formula(l_false, "(exists (x Int) (y Int) (= 1 (+ (* 5 x) (* 10 y))))"); test_formula(l_true, "(exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= 1 (- (* 5 x) (* 6 y)))))"); test_formula(l_true, "(exists (x Int) (y Int) (z Int) (w Int) (= 1 (+ (* 2 w) (* 3 x) (* 4 y) (* 5 z))))"); test_formula(l_true, "(exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= 1 (- (* 5 x) (* 3 y)))))"); test_formula(l_true, "(exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= 1 (- (* 3 x) (* 5 y)))))"); test_formula(l_false,"(exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= 1 (- (* 6 x) (* 3 y)))))"); test_formula(l_true, "(forall (x Int) (y Int) (or (= 0 (mod x 5)) (= 0 (mod y 6)) (not (= (* 6 x) (* 5 y)))))"); test_formula(l_false,"(forall (x Int) (y Int) (or (not (= (* 6 x) (* 5 y)))))"); // Positive variant of the Bezout theorem (see the exercise). *) test_formula(l_true, "(forall (z Int) (implies (> z 7) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= (+ (* 3 x) (* 5 y)) z)))))"); test_formula(l_false,"(forall (z Int) (implies (> z 2) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= (+ (* 3 x) (* 5 y)) z)))))"); test_formula(l_true, "(forall (z Int) (implies (<= z 7) " " (iff (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= z (+ (* 3 x) (* 5 y))))) " " (not (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= (- 7 z) (+ (* 3 x) (* 5 y))))))))) "); // Basic result about congruences. test_formula(l_true, "(forall (x Int) " " (iff (and (not (exists (m Int) (= x (* 2 m)))) (exists (m Int) (= x (+ (* 3 m) 1)))) " " (or (exists (m Int) (= x (+ (* 12 m) 1))) (exists (m Int) (= x (+ (* 12 m) 7))))))"); // Inspired by the Collatz conjecture. test_formula(l_false, "(forall (a Int) (b Int) (x Int) (y Int) (z Int) " " (implies (and (= (+ a 2) b) (= x (+ 1 (- b a))) (= y (- b 2)) (= z 3)) false))"); test_formula(l_true, "(exists (a Int) (b Int) " " (and (not (= a 1)) (= a b) (or (= a (* 2 b)) (= (* 2 b) (+ 1 (* 3 a))))))"); test_formula(l_false, "(exists (a Int) (b Int) " " (and (> a 1) (> b 1) (= a b) (or (= a (* 2 b)) (= (* 2 b) (+ 1 (* 3 a))))))"); test_formula(l_false, "(exists (a Int) (b Int) " " (and (> a 1) (> b 1) " " (or (= a (* 2 b)) (= (* 2 b) (+ 1 (* 3 a)))) " " (or (= b (* 2 a)) (= (* 2 a) (+ 1 (* 3 b))))))"); #if 0 // Bob Constable's "stamp problem". test_formula(l_true, "(forall (x Int) (implies (>= x 8) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 5 v)))))))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 5 v))))))))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 7 v))))))))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 8 v))))))))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 7 u) (* 8 v))))))))"); #endif // Example from reciprocal mult: (2622 * x)>>16 = x/100 within a range. test_formula(l_true, "(forall (x Int) (y Int) " " (iff (exists (d Int) (= (+ x y) (* 2 d))) " " (iff (exists (d Int) (= x (* 2 d))) (exists (d Int) (= y (* 2 d))))))"); test_formula(l_true, "(forall (n Int) " " (implies (and (< 0 n) (< n 2400)) " " (or (and (<= n 2) (<= 2 (* 2 n))) " " (and (<= n 3) (<= 3 (* 2 n))) " " (and (<= n 5) (<= 5 (* 2 n))) " " (and (<= n 7) (<= 7 (* 2 n))) " " (and (<= n 13) (<= 13 (* 2 n))) " " (and (<= n 23) (<= 23 (* 2 n))) " " (and (<= n 43) (<= 43 (* 2 n))) " " (and (<= n 83) (<= 83 (* 2 n))) " " (and (<= n 163) (<= 163 (* 2 n))) " " (and (<= n 317) (<= 317 (* 2 n))) " " (and (<= n 631) (<= 631 (* 2 n))) " " (and (<= n 1259) (<= 1259 (* 2 n))) " " (and (<= n 2503) (<= 2503 (* 2 n)))))) "); memory::finalize(); #ifdef _WINDOWS _CrtDumpMemoryLeaks(); #endif exit(0); } z3-z3-4.4.1/src/test/quant_solve.cpp000066400000000000000000000226061260446376700172450ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast.h" #include "smt_params.h" #include "qe.h" #include "arith_decl_plugin.h" #include "ast_pp.h" #include "lbool.h" #include #include "expr_replacer.h" #include "smt_kernel.h" #include "reg_decl_plugins.h" #include "expr_abstract.h" #include "model_smt2_pp.h" #include "smt2parser.h" #include "var_subst.h" static void validate_quant_solution(ast_manager& m, expr* fml, expr* guard, qe::def_vector const& defs) { // verify: // new_fml => fml[t/x] scoped_ptr rep = mk_expr_simp_replacer(m); app_ref_vector xs(m); expr_substitution sub(m); for (unsigned i = 0; i < defs.size(); ++i) { xs.push_back(m.mk_const(defs.var(i))); sub.insert(xs.back(), defs.def(i)); } rep->set_substitution(&sub); expr_ref fml1(fml, m); (*rep)(fml1); expr_ref tmp(m); tmp = m.mk_not(m.mk_implies(guard, fml1)); std::cout << "validating: " << mk_pp(tmp, m) << "\n"; smt_params fp; smt::kernel solver(m, fp); solver.assert_expr(tmp); lbool res = solver.check(); //SASSERT(res == l_false); if (res != l_false) { std::cout << "Validation failed: " << res << "\n"; std::cout << mk_pp(tmp, m) << "\n"; model_ref model; solver.get_model(model); model_smt2_pp(std::cout, m, *model, 0); fatal_error(0); } } #if 0 static void validate_quant_solutions(app* x, expr* fml, expr_ref_vector& guards) { return; // quant_elim option got removed... // verify: // fml <=> guard_1 \/ guard_2 \/ ... ast_manager& m = guards.get_manager(); expr_ref tmp(m), fml2(m); tmp = m.mk_or(guards.size(), guards.c_ptr()); expr* _x = x; std::cout << mk_pp(fml, m) << "\n"; expr_abstract(m, 0, 1, &_x, fml, fml2); std::cout << mk_pp(fml2, m) << "\n"; symbol name(x->get_decl()->get_name()); sort* s = m.get_sort(x); fml2 = m.mk_exists(1, &s, &name, fml2); std::cout << mk_pp(fml2, m) << "\n"; tmp = m.mk_not(m.mk_iff(fml2, tmp)); std::cout << mk_pp(tmp, m) << "\n"; smt_params fp; smt::kernel solver(m, fp); solver.assert_expr(tmp); lbool res = solver.check(); std::cout << "checked\n"; SASSERT(res == l_false); if (res != l_false) { std::cout << res << "\n"; fatal_error(0); } } #endif static void test_quant_solver(ast_manager& m, unsigned sz, app*const* xs, expr* fml, bool validate) { smt_params params; qe::expr_quant_elim qe(m, params); qe::guarded_defs defs(m); bool success = qe.solve_for_vars(sz, xs, fml, defs); std::cout << "------------------------\n"; std::cout << mk_pp(fml, m) << "\n"; if (success) { defs.display(std::cout); for (unsigned i = 0; validate && i < defs.size(); ++i) { validate_quant_solution(m, fml, defs.guard(i), defs.defs(i)); } } else { std::cout << "failed\n"; } } static expr_ref parse_fml(ast_manager& m, char const* str) { expr_ref result(m); cmd_context ctx(false, &m); ctx.set_ignore_check(true); std::ostringstream buffer; buffer << "(declare-const x Int)\n" << "(declare-const y Int)\n" << "(declare-const z Int)\n" << "(declare-const a Int)\n" << "(declare-const b Int)\n" << "(declare-const P Bool)\n" << "(declare-const Q Bool)\n" << "(declare-const r1 Real)\n" << "(declare-const r2 Real)\n" << "(declare-datatypes () ((IList (nil) (cons (car Int) (cdr IList)))))\n" << "(declare-const l1 IList)\n" << "(declare-const l2 IList)\n" << "(declare-datatypes () ((Cell (null) (cell (car Cell) (cdr Cell)))))\n" << "(declare-const c1 Cell)\n" << "(declare-const c2 Cell)\n" << "(declare-const c3 Cell)\n" << "(declare-datatypes () ((Tuple (tuple (first Int) (second Bool) (third Real)))))\n" << "(declare-const t1 Tuple)\n" << "(declare-const t2 Tuple)\n" << "(declare-const t3 Tuple)\n" << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); SASSERT(ctx.begin_assertions() != ctx.end_assertions()); result = *ctx.begin_assertions(); return result; } static void parse_fml(char const* str, app_ref_vector& vars, expr_ref& fml) { ast_manager& m = fml.get_manager(); fml = parse_fml(m, str); if (is_exists(fml)) { quantifier* q = to_quantifier(fml); for (unsigned i = 0; i < q->get_num_decls(); ++i) { vars.push_back(m.mk_const(q->get_decl_name(i), q->get_decl_sort(i))); } fml = q->get_expr(); var_subst vs(m, true); vs(fml, vars.size(), (expr*const*)vars.c_ptr(), fml); } } static void test_quant_solver(ast_manager& m, app* x, char const* str, bool validate = true) { expr_ref fml = parse_fml(m, str); test_quant_solver(m, 1, &x, fml, validate); } static void test_quant_solver(ast_manager& m, unsigned sz, app*const* xs, char const* str, bool validate = true) { expr_ref fml = parse_fml(m, str); test_quant_solver(m, sz, xs, fml, validate); } static void test_quant_solver(ast_manager& m, char const* str, bool validate = true) { expr_ref fml(m); app_ref_vector vars(m); parse_fml(str, vars, fml); test_quant_solver(m, vars.size(), vars.c_ptr(), fml, validate); } static void test_quant_solve1() { ast_manager m; arith_util ar(m); reg_decl_plugins(m); sort* i = ar.mk_int(); app_ref xr(m.mk_const(symbol("x"),i), m); app_ref yr(m.mk_const(symbol("y"),i), m); app* x = xr.get(); app* y = yr.get(); app* xy[2] = { x, y }; test_quant_solver(m, x, "(and (<= (* 2 x) y) (>= x z) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= x y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= (* 2 x) y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (>= x y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (>= (* 2 x) y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= (* 2 x) y) (>= (* 3 x) z) (= (mod x 2) 0))"); test_quant_solver(m, x, "(>= (* 2 x) a)"); test_quant_solver(m, x, "(<= (* 2 x) a)"); test_quant_solver(m, x, "(< (* 2 x) a)"); test_quant_solver(m, x, "(= (* 2 x) a)"); test_quant_solver(m, x, "(< (* 2 x) a)"); test_quant_solver(m, x, "(> (* 2 x) a)"); test_quant_solver(m, x, "(and (<= a x) (<= (* 2 x) b))"); test_quant_solver(m, x, "(and (<= a x) (<= x b))"); test_quant_solver(m, x, "(and (<= (* 2 a) x) (<= x b))"); test_quant_solver(m, x, "(and (<= (* 2 a) x) (<= (* 2 x) b))"); test_quant_solver(m, x, "(and (<= a x) (<= (* 3 x) b))"); test_quant_solver(m, x, "(and (<= (* 3 a) x) (<= x b))"); test_quant_solver(m, x, "(and (<= (* 3 a) x) (<= (* 3 x) b))"); test_quant_solver(m, x, "(and (< a (* 3 x)) (< (* 3 x) b))"); test_quant_solver(m, x, "(< (* 3 x) a)"); test_quant_solver(m, x, "(= (* 3 x) a)"); test_quant_solver(m, x, "(< (* 3 x) a)"); test_quant_solver(m, x, "(> (* 3 x) a)"); test_quant_solver(m, x, "(<= (* 3 x) a)"); test_quant_solver(m, x, "(>= (* 3 x) a)"); test_quant_solver(m, x, "(<= (* 2 x) a)"); test_quant_solver(m, x, "(or (= (* 2 x) y) (= (+ (* 2 x) 1) y))"); test_quant_solver(m, x, "(= x a)"); test_quant_solver(m, x, "(< x a)"); test_quant_solver(m, x, "(> x a)"); test_quant_solver(m, x, "(and (> x a) (< x b))"); test_quant_solver(m, x, "(and (> x a) (< x b))"); test_quant_solver(m, x, "(<= x a)"); test_quant_solver(m, x, "(>= x a)"); test_quant_solver(m, x, "(and (<= (* 2 x) y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(= (* 2 x) y)"); test_quant_solver(m, x, "(or (< x 0) (> x 1))"); test_quant_solver(m, x, "(or (< x y) (> x y))"); test_quant_solver(m, x, "(= x y)"); test_quant_solver(m, x, "(<= x y)"); test_quant_solver(m, x, "(>= x y)"); test_quant_solver(m, x, "(and (<= (+ x y) 0) (<= (+ x z) 0))"); test_quant_solver(m, x, "(and (<= (+ x y) 0) (<= (+ (* 2 x) z) 0))"); test_quant_solver(m, x, "(and (<= (+ (* 3 x) y) 0) (<= (+ (* 2 x) z) 0))"); test_quant_solver(m, x, "(and (>= x y) (>= x z))"); test_quant_solver(m, x, "(< x y)"); test_quant_solver(m, x, "(> x y)"); test_quant_solver(m, 2, xy, "(and (<= (- (* 2 y) b) (+ (* 3 x) a)) (<= (- (* 2 x) a) (+ (* 4 y) b)))"); test_quant_solver(m, "(exists ((c Cell)) (= c null))"); test_quant_solver(m, "(exists ((c Cell)) (= c (cell null c1)))"); test_quant_solver(m, "(exists ((c Cell)) (not (= c null)))", false); test_quant_solver(m, "(exists ((c Cell)) (= (cell c c) c1))", false); test_quant_solver(m, "(exists ((c Cell)) (= (cell c (cdr c1)) c1))", false); test_quant_solver(m, "(exists ((t Tuple)) (= (tuple a P r1) t))"); test_quant_solver(m, "(exists ((t Tuple)) (= a (first t)))"); test_quant_solver(m, "(exists ((t Tuple)) (= P (second t)))"); test_quant_solver(m, "(exists ((t Tuple)) (= r2 (third t)))"); test_quant_solver(m, "(exists ((t Tuple)) (not (= a (first t))))"); test_quant_solver(m, "(exists ((t Tuple)) (not (= P (second t))))"); test_quant_solver(m, "(exists ((t Tuple)) (not (= r2 (third t))))"); } void tst_quant_solve() { disable_debug("heap"); test_quant_solve1(); #if 0 memory::finalize(); #ifdef _WINDOWS _CrtDumpMemoryLeaks(); #endif exit(0); #endif } z3-z3-4.4.1/src/test/random.cpp000066400000000000000000000006431260446376700161620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: random.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-08-24. Revision History: --*/ #include"util.h" #include"trace.h" static void tst1() { random_gen r(0); TRACE("random", for (unsigned i = 0; i < 1000; i++) { tout << r() << "\n"; }); } void tst_random() { tst1(); } z3-z3-4.4.1/src/test/rational.cpp000066400000000000000000000373261260446376700165230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_rational.cpp Abstract: Test rationals Author: Leonardo de Moura (leonardo) 2006-09-26. Revision History: --*/ #include #include"vector.h" #include"rational.h" #include"trace.h" #include"ext_gcd.h" #include"timeit.h" static void tst1() { rational r1(1); rational r2(1,2); rational r3(2,4); SASSERT(r2 == r3); SASSERT(r1 != r2); SASSERT(r2 + r3 == r1); SASSERT(r1.is_pos()); SASSERT((r2 - r1).is_neg()); SASSERT((r2 - r3).is_zero()); SASSERT(floor(r2).is_zero()); SASSERT(ceil(r2).is_one()); // std::cout << "-r2: " << (-r2) << ", floor(-r2):" << floor(-r2) << "\n"; SASSERT(floor(-r2).is_minus_one()); SASSERT(ceil(-r2).is_zero()); SASSERT(floor(r1) == r1); SASSERT(ceil(r1) == r1); rational r4(3,5); SASSERT(r3 * r4 == rational(3, 10)); SASSERT(r3 / r4 == rational(5, 6)); rational r5(2,3); SASSERT(r4 * r5 == rational(2, 5)); --r2; SASSERT(r2 == -r3); r2.neg(); SASSERT(r2 == r3); --r2; r2 = abs(r2); SASSERT(r2 == r3); --r2; ++r2; SASSERT(r2 == r3); SASSERT(r2 == abs(r2)); SASSERT(r4 * rational(1) == r4); SASSERT((r4 * rational(0)).is_zero()); SASSERT(r4 * rational(-1) == -r4); SASSERT(rational(1) * r4 == r4); SASSERT((rational(0) * r4).is_zero()); SASSERT(rational(-1) * r4 == -r4); SASSERT(r4 + rational(0) == r4); SASSERT(ceil(r4).is_one()); // std::cout << "r3: " << r3 << ", r4: " << r4 << ", -r4: " << -r4 << ", r3 / (-r4): " << (r3 / (-r4)) << "\n"; SASSERT(r3 / (-r4) == rational(5,-6)); SASSERT(div(rational(7), rational(2)) == rational(3)); SASSERT(rational(7) % rational(4) == rational(3)); SASSERT(div(rational(7), rational(-2)) == rational(-3)); SASSERT(rational(3) + rational(5) == rational(8)); SASSERT(rational("13/10") + rational("7/10") == rational(2)); SASSERT(rational("100/20") == rational(5)); SASSERT(gcd(rational(12), rational(8)) == rational(4)); SASSERT(ceil(rational(-3,2)) == rational(-1)); SASSERT(floor(rational(-3,2)) == rational(-2)); SASSERT(ceil(rational(3,2)) == rational(2)); SASSERT(floor(rational(3,2)) == rational(1)); SASSERT(rational(3).is_pos()); SASSERT(rational(0).is_nonneg()); SASSERT(rational(3).is_pos()); SASSERT(rational(3).is_nonneg()); SASSERT(rational(0).is_nonneg()); SASSERT(!rational(3).is_zero()); SASSERT(!rational(-3).is_zero()); SASSERT(rational(0).is_zero()); SASSERT(rational(1).is_one()); SASSERT(!rational(2).is_one()); SASSERT(rational(3,4) >= rational(2,8)); SASSERT(rational(3,4) <= rational(7,8)); SASSERT(rational(3,4) <= rational(3,4)); SASSERT(rational(3,4) >= rational(3,4)); SASSERT(rational(3,4) > rational(2,8)); SASSERT(rational(3,4) < rational(7,8)); TRACE("rational", tout << rational(3,4) << "\n";); TRACE("rational", tout << rational(7,9) << "\n";); TRACE("rational", tout << rational(-3,7) << "\n";); TRACE("rational", tout << rational(5,8) << "\n";); TRACE("rational", tout << rational(4,2) << "\n";); SASSERT(rational(3) + rational(2) == rational(5)); SASSERT(rational(3) - rational(2) == rational(1)); SASSERT(rational(3) * rational(2) == rational(6)); SASSERT(rational(6) / rational(2) == rational(3)); SASSERT(rational(6) % rational(4) == rational(2)); SASSERT(power(rational(2),0) == rational(1)); SASSERT(power(rational(2),1) == rational(2)); SASSERT(power(rational(2),3) == rational(8)); } static void tst2() { rational r1("10000000000000000000000000000000000"); rational r2("10000000000000000000000000000000000/3"); rational r3("20000000000000000000000000000000000/6"); TRACE("rational", tout << r1 << std::endl;); TRACE("rational", tout << r2 << std::endl;); TRACE("rational", tout << r3 << std::endl;); SASSERT(r2 == r3); SASSERT(r1 != r2); SASSERT(rational(2)*r2 + r3 == r1); SASSERT(r1.is_pos()); SASSERT((r2 - r1).is_neg()); SASSERT((r2 - r3).is_zero()); // std::cout << "===> " << floor(r2) << "\n"; { rational r0("1/3000000000000000000000000"); SASSERT(ceil(r0).is_one()); SASSERT(floor(-r0).is_minus_one()); SASSERT(ceil(-r0).is_zero()); } SASSERT(floor(r1) == r1); SASSERT(ceil(r1) == r1); rational r4("300000000/5"); SASSERT(rational(1,2) * r4 == rational("300000000/10")); SASSERT(rational(1,2) / r4 == rational("5/600000000")); rational r5(2,3); SASSERT(r4 * r5 == rational("200000000/5")); rational r6("10000000000000000000000000000000003/3"); --r6; SASSERT(r6 == r2); r6.neg(); SASSERT(r6 != r2); SASSERT(abs(r6) == r2); --r2; ++r2; r2.neg(); SASSERT(r2 == r6); SASSERT(r6 * rational(1) == r6); SASSERT((r6 * rational(0)).is_zero()); SASSERT(r6 * rational(-1) == -r6); SASSERT(rational(1) * r6 == r6); SASSERT((rational(0) * r6).is_zero()); SASSERT(rational(-1) * r6 == -r6); SASSERT(r6 + rational(0) == r6); SASSERT(rational("300000000000000").is_pos()); SASSERT(rational("0000000000000000000").is_nonneg()); SASSERT(rational("0000000000000000000").is_nonpos()); SASSERT(rational("3000000000000000000/2").is_pos()); SASSERT(rational("3000000000000000000/2").is_nonneg()); SASSERT((-rational("3000000000000000000/2")).is_neg()); SASSERT(!rational("3000000000000000000/2").is_neg()); SASSERT(!rational("3000000000000000000/2").is_zero()); SASSERT(!rational("3000000000000000000/2").is_one()); SASSERT(rational("99999999999/2") >= rational("23/2")); SASSERT(rational("99999999999/2") > rational("23/2")); SASSERT(rational("23/2") <= rational("99999999999/2")); SASSERT(rational("23/2") < rational("99999999999/2")); SASSERT(!(rational("99999999999/2") < rational("23/2"))); rational int64_max("9223372036854775807"); rational int64_min((-int64_max) - rational(1)); // is_int64 SASSERT(int64_max.is_int64()); SASSERT(int64_min.is_int64()); SASSERT(rational(0).is_int64()); SASSERT(rational(1).is_int64()); SASSERT(rational(-1).is_int64()); SASSERT(!(int64_max + rational(1)).is_int64()); SASSERT(!(int64_min - rational(1)).is_int64()); // is_uint64 SASSERT(int64_max.is_uint64()); SASSERT(!int64_min.is_uint64()); SASSERT(rational(0).is_uint64()); SASSERT(rational(1).is_uint64()); SASSERT(!rational(-1).is_uint64()); SASSERT((int64_max + rational(1)).is_uint64()); SASSERT(!(int64_min - rational(1)).is_uint64()); rational uint64_max(rational(1) + (rational(2) * int64_max)); SASSERT(uint64_max.is_uint64()); // get_int64, get_uint64 uint64 u1 = uint64_max.get_uint64(); uint64 u2 = UINT64_MAX; SASSERT(u1 == u2); std::cout << "int64_max: " << int64_max << ", INT64_MAX: " << INT64_MAX << ", int64_max.get_int64(): " << int64_max.get_int64() << ", int64_max.get_uint64(): " << int64_max.get_uint64() << "\n"; SASSERT(int64_max.get_int64() == INT64_MAX); SASSERT(int64_min.get_int64() == INT64_MIN); // extended Euclid: } void tst3() { rational n1 = power(rational(2), 32); TRACE("rational", tout << "n1: " << n1 << "\n";); rational n2 = div(n1, rational(2)); rational n3 = div(rational(2), n2); TRACE("rational", tout << "n1: " << n1 << "\n"; tout << "n2: " << n2 << "\n"; tout << "n3: " << n3 << "\n";); rational n4 = n1 - rational(3); rational n5 = div(n4, rational(2)); TRACE("rational", tout << "n4: " << n4 << "\n"; tout << "n5: " << n5 << "\n";); SASSERT(n5 == rational("2147483646")); } void tst4() { rational n1("4294967293"); TRACE("rational", tout << "n1: " << n1 << "\n";); rational n2 = div(n1, rational(2)); } void tst5() { rational n1(1); n1.neg(); rational n2("4294967295"); n1 /= n2; TRACE("rational", tout << n1 << " " << n2 << " " << n1.is_big() << " " << n2.is_big() << "\n";); n1 *= n2; TRACE("rational", tout << "after: " << n1 << " " << n2 << "\n";); SASSERT(n1.is_minus_one()); } void tst6() { rational t1(5); rational t2(3); rational a, b, g; g = gcd(t1, t2, a, b); t1 = rational(15); t2 = rational(25); g = gcd(t1, t2, a, b); t1.neg(); g = gcd(t1, t2, a, b); t2.neg(); g = gcd(t1, t2, a, b); t1.neg(); g = gcd(t1, t2, a, b); std::swap(t1, t2); g = gcd(t1, t2, a, b); t1.neg(); g = gcd(t1, t2, a, b); t2.neg(); g = gcd(t1, t2, a, b); t1.neg(); g = gcd(t1, t2, a, b); } class rational_tester { public: static void tst1() { rational n1(-1); rational n2(8); SASSERT((n1 % n2).is_minus_one()); SASSERT(mod(n1, n2) == rational(7)); } static void tst_hash(int val) { rational n1(val); rational n2("10203939394995449949494394932929"); rational n3(val); n2 = n3; SASSERT(n1.hash() == n2.hash()); } static void tst2() { tst_hash(0); for (int i = 0; i <= 10000; i++) { int r = rand() % INT_MAX; if (rand()%2 == 1) r = -r; tst_hash(r); } } }; static void tst7() { rational p; p = power(rational(2), 32); for (unsigned i = 1; i < 1000; i++) { rational n(i); rational x; rational y; rational gcd; extended_gcd(n, p, gcd, x, y); TRACE("gcd", tout << n << " " << p << ": " << gcd << " " << x << " " << y << "\n";); SASSERT(!mod(n, rational(2)).is_one() || mod(n * x, p).is_one()); } } static void tst8() { rational r; SASSERT(!rational(-4).is_int_perfect_square(r) && r.is_zero()); SASSERT(!rational(-3).is_int_perfect_square(r) && r.is_zero()); SASSERT(!rational(-2).is_int_perfect_square(r) && r.is_zero()); SASSERT(!rational(-1).is_int_perfect_square(r) && r.is_zero()); SASSERT(rational(0).is_int_perfect_square(r) && r.is_zero()); SASSERT(rational(1).is_int_perfect_square(r) && r.is_one()); SASSERT(!rational(2).is_int_perfect_square(r) && r == rational(2)); SASSERT(!rational(3).is_int_perfect_square(r) && r == rational(2)); SASSERT(rational(4).is_int_perfect_square(r) && r == rational(2)); SASSERT(!rational(5).is_int_perfect_square(r) && r == rational(3)); SASSERT(!rational(6).is_int_perfect_square(r) && r == rational(3)); SASSERT(!rational(7).is_int_perfect_square(r) && r == rational(3)); SASSERT(!rational(8).is_int_perfect_square(r) && r == rational(3)); SASSERT(rational(9).is_int_perfect_square(r) && r == rational(3)); SASSERT(!rational(10).is_int_perfect_square(r) && r == rational(4)); SASSERT(!rational(11).is_int_perfect_square(r) && r == rational(4)); SASSERT(!rational(12).is_int_perfect_square(r) && r == rational(4)); SASSERT(!rational(13).is_int_perfect_square(r) && r == rational(4)); SASSERT(!rational(14).is_int_perfect_square(r) && r == rational(4)); SASSERT(!rational(15).is_int_perfect_square(r) && r == rational(4)); SASSERT(rational(16).is_int_perfect_square(r) && r == rational(4)); SASSERT(!rational(17).is_int_perfect_square(r) && r == rational(5)); SASSERT(!rational(18).is_int_perfect_square(r) && r == rational(5)); SASSERT(!rational(19).is_int_perfect_square(r) && r == rational(5)); SASSERT(!rational(20).is_int_perfect_square(r) && r == rational(5)); SASSERT(!rational(21).is_int_perfect_square(r) && r == rational(5)); SASSERT(!rational(22).is_int_perfect_square(r) && r == rational(5)); SASSERT(!rational(23).is_int_perfect_square(r) && r == rational(5)); SASSERT(!rational(24).is_int_perfect_square(r) && r == rational(5)); SASSERT(rational(25).is_int_perfect_square(r) && r == rational(5)); SASSERT(!rational(26).is_int_perfect_square(r) && r == rational(6)); SASSERT(rational(36).is_int_perfect_square(r) && r == rational(6)); SASSERT(rational(1,9).is_perfect_square(r) && r == rational(1,3)); SASSERT(rational(4,9).is_perfect_square(r) && r == rational(2,3)); } static void tstmod(rational const& m, rational const& n) { // // (=> (distinct n 0) // (let ((q (div m n)) (r (mod m n))) // (and (= m (+ (* n q) r)) // (<= 0 r (- (abs n) 1)))))) // rational q = div(m,n); rational r = mod(m,n); std::cout << m << " " << n << " " << q << " " << r << "\n"; std::cout << m << " == " << n*q+r << "\n"; SASSERT(m == (n * q) + r); SASSERT(rational::zero() <= r); SASSERT(r < abs(n)); } static void tst9() { // record semantics of rational div/mod. tstmod(rational("41000000000000"),rational("-7000000000000")); tstmod(rational("-41000000000000"),rational("-7000000000000")); tstmod(rational("-41000000000000"),rational("7000000000000")); tstmod(rational("41000000000000"),rational("7000000000000")); tstmod(rational(41),rational(-7)); tstmod(rational(-41),rational(-7)); tstmod(rational(-41),rational(7)); tstmod(rational(41),rational(7)); } #define NUM_RATIONALS 1000000 #define MAGNITUDE 10000 static void tst10(bool use_ints) { if (use_ints) std::cout << "Testing multiplication performace using small ints\n"; else std::cout << "Testing multiplication performace using small rationals\n"; vector vals; vector vals2; vector fvals; vals.resize(NUM_RATIONALS); vals2.resize(NUM_RATIONALS); fvals.resize(NUM_RATIONALS); for (unsigned i = 0; i < NUM_RATIONALS; i++) { int r1 = rand() % MAGNITUDE; int r2 = use_ints ? 1 : rand() % MAGNITUDE; if (r2 == 0) r2 = 1; if (rand() % 2 == 0) r1 = -r1; vals[i] = rational(r1, r2); vals2[i] = rational(r1, r2); fvals[i] = ((float)r1) / ((float)r2); } { timeit t(true, "multiplication with rationals"); for (unsigned i = 0; i < NUM_RATIONALS - 1; i++) { vals[i] *= vals[i+1]; } } { timeit t(true, "multiplication with floats: "); for (unsigned i = 0; i < NUM_RATIONALS - 1; i++) { fvals[i] *= fvals[i+1]; } } std::cout << "\n"; } #define NUM_RATIONALS2 10000 #define MAGNITUDE2 100000000 static void tst11(bool use_ints) { vector vals; vector fvals; vals.resize(NUM_RATIONALS2); fvals.resize(NUM_RATIONALS2); for (unsigned i = 0; i < NUM_RATIONALS2; i++) { int r1 = rand() % MAGNITUDE2; int r2 = use_ints ? 1 : rand() % MAGNITUDE2; if (r2 == 0) r2 = 1; if (rand() % 2 == 0) r1 = -r1; vals[i] = rational(r1, r2); fvals[i] = ((float)r1) / ((float)r2); } { timeit t(true, "multiplication with big rationals"); for (unsigned j = 0; j < 10; j++) for (unsigned i = 0; i < NUM_RATIONALS2-1; i++) { vals[i] *= vals[i+1]; } } { timeit t(true, "multiplication with floats: "); for (unsigned j = 0; j < 10; j++) for (unsigned i = 0; i < NUM_RATIONALS2-1; i++) { fvals[i] *= fvals[i+1]; } } std::cout << "\n"; } void tst_rational() { TRACE("rational", tout << "starting rational test...\n";); std::cout << "sizeof(rational): " << sizeof(rational) << "\n"; rational r1("10000000000000000000000000000000001"); r1.hash(); tst1(); tst2(); tst3(); tst4(); tst5(); std::cout << "running tst6" << std::endl; tst6(); std::cout << "running tst7" << std::endl; tst7(); std::cout << "running tst8" << std::endl; tst8(); std::cout << "running tst9" << std::endl; tst9(); std::cout << "running rational_tester::tst1" << std::endl; rational_tester::tst1(); rational_tester::tst2(); tst11(true); tst10(true); tst10(false); } z3-z3-4.4.1/src/test/rcf.cpp000066400000000000000000000116221260446376700154530ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: rcf.cpp Abstract: Testing RCF module Author: Leonardo (leonardo) 2013-01-04 Notes: --*/ #include"realclosure.h" #include"mpz_matrix.h" static void tst1() { unsynch_mpq_manager qm; rcmanager m(qm); scoped_rcnumeral a(m); #if 0 a = 10; std::cout << sym_pp(a) << std::endl; std::cout << sym_pp(eps) << std::endl; std::cout << interval_pp(a) << std::endl; std::cout << interval_pp(eps) << std::endl; #endif scoped_rcnumeral eps(m); m.mk_infinitesimal(eps); mpq aux; qm.set(aux, 1, 3); m.set(a, aux); #if 0 std::cout << interval_pp(a) << std::endl; std::cout << decimal_pp(eps, 4) << std::endl; std::cout << decimal_pp(a) << std::endl; std::cout << a + eps << std::endl; std::cout << a * eps << std::endl; std::cout << (a + eps)*eps - eps << std::endl; #endif std::cout << interval_pp(a - eps*2) << std::endl; std::cout << interval_pp(eps + 1) << std::endl; scoped_rcnumeral t(m); t = (a - eps*2) / (eps + 1); std::cout << t << std::endl; std::cout << t * (eps + 1) << std::endl; a = 10; std::cout << (a + eps > a) << std::endl; scoped_rcnumeral pi(m); m.mk_pi(pi); std::cout << pi + 1 << std::endl; std::cout << decimal_pp(pi) << std::endl; std::cout << decimal_pp(pi + 1) << std::endl; scoped_rcnumeral e(m); m.mk_e(e); t = e + (pi + 1)*2; std::cout << t << std::endl; std::cout << decimal_pp(t, 10) << std::endl; std::cout << (eps + 1 > 1) << std::endl; std::cout << interval_pp((a + eps)/(a - eps)) << std::endl; } static void tst2() { enable_trace("mpz_matrix"); unsynch_mpq_manager nm; small_object_allocator allocator; mpz_matrix_manager mm(nm, allocator); scoped_mpz_matrix A(mm); mm.mk(3, 3, A); // Matrix // 1 1 1 // 0 1 -1 // 0 1 1 A.set(0, 0, 1); A.set(0, 1, 1); A.set(0, 2, 1); A.set(1, 0, 0); A.set(1, 1, 1); A.set(1, 2, -1); A.set(2, 0, 0); A.set(2, 1, 1); A.set(2, 2, 1); std::cout << A; { int b[3]; int c[3] = { 10, -2, 8 }; std::cout << "solve: " << mm.solve(A, b, c) << "\n"; for (unsigned i = 0; i < 3; i++) std::cout << b[i] << " "; std::cout << "\n"; } scoped_mpz_matrix A2(mm); mm.tensor_product(A, A, A2); std::cout << A2; scoped_mpz_matrix B(mm); unsigned cols[] = { 1, 3, 7, 8 }; mm.filter_cols(A2, 4, cols, B); std::cout << B; scoped_mpz_matrix C(mm); unsigned perm[] = { 8, 7, 6, 5, 4, 3, 2, 1, 0 }; mm.permute_rows(B, perm, C); std::cout << C; } static void tst_solve(unsigned n, int _A[], int _b[], int _c[], bool solved) { unsynch_mpq_manager nm; small_object_allocator allocator; mpz_matrix_manager mm(nm, allocator); scoped_mpz_matrix A(mm); mm.mk(n, n, A); for (unsigned i = 0; i < n; i++) for (unsigned j = 0; j < n; j++) A.set(i, j, _A[i*n + j]); svector b; b.resize(n, 0); if (mm.solve(A, b.c_ptr(), _c)) { SASSERT(solved); for (unsigned i = 0; i < n; i++) { SASSERT(b[i] == _b[i]); } } else { SASSERT(!solved); } } static void tst_lin_indep(unsigned m, unsigned n, int _A[], unsigned ex_sz, unsigned ex_r[]) { unsynch_mpq_manager nm; small_object_allocator allocator; mpz_matrix_manager mm(nm, allocator); scoped_mpz_matrix A(mm); mm.mk(m, n, A); for (unsigned i = 0; i < m; i++) for (unsigned j = 0; j < n; j++) A.set(i, j, _A[i*n + j]); unsigned_vector r; r.resize(A.n()); scoped_mpz_matrix B(mm); mm.linear_independent_rows(A, r.c_ptr(), B); for (unsigned i = 0; i < ex_sz; i++) { SASSERT(r[i] == ex_r[i]); } } static void tst_denominators() { unsynch_mpq_manager qm; rcmanager m(qm); scoped_rcnumeral a(m); scoped_rcnumeral t(m); scoped_rcnumeral eps(m); m.mk_pi(a); m.inv(a); m.mk_infinitesimal(eps); t = (a - eps*2) / (a*eps + 1); // t = t + a * 2; scoped_rcnumeral n(m), d(m); std::cout << t << "\n"; m.clean_denominators(t, n, d); std::cout << "---->\n" << n << "\n" << d << "\n"; } void tst_rcf() { enable_trace("rcf_clean"); enable_trace("rcf_clean_bug"); tst_denominators(); tst1(); tst2(); { int A[] = {0, 1, 1, 1, 0, 1, 1, 1, -1}; int c[] = {10, 4, -4}; int b[] = {-2, 4, 6}; tst_solve(3, A, b, c, true); } { int A[] = {1, 1, 1, 0, 1, 1, 0, 1, 1}; int c[] = {3, 2, 2}; int b[] = {1, 1, 1}; tst_solve(3, A, b, c, false); } { int A[] = {1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, -1}; unsigned r[] = {0, 1, 4}; tst_lin_indep(5, 3, A, 3, r); } { int A[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, -1}; unsigned r[] = {0, 4}; tst_lin_indep(5, 3, A, 2, r); } { int A[] = {1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 1, 2, 3, 1, 3}; unsigned r[] = {0, 2}; tst_lin_indep(5, 3, A, 2, r); } } z3-z3-4.4.1/src/test/region.cpp000066400000000000000000000004741260446376700161670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_region.cpp Abstract: Test region memory allocator. Author: Leonardo de Moura (leonardo) 2006-09-14. Revision History: --*/ #include #include"region.h" static void tst1() { // TODO } void tst_region() { tst1(); } z3-z3-4.4.1/src/test/sat_user_scope.cpp000066400000000000000000000054101260446376700177150ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "sat_solver.h" #include "util.h" typedef sat::literal_vector clause_t; typedef vector clauses_t; typedef vector trail_t; // [ [c1, c2, ..], [ ...] ] static unsigned s_num_vars = 6; static unsigned s_num_clauses_per_frame = 8; static unsigned s_num_frames = 7; static void add_literal(random_gen& r, clause_t& c) { c.push_back(sat::literal(r(s_num_vars) + 1, r(2) == 0)); } static clause_t& last_clause(trail_t& t) { return t.back().back(); } static void add_clause(sat::solver& s, random_gen& r, trail_t& t) { t.back().push_back(sat::literal_vector()); clause_t& cls = last_clause(t); for (unsigned i = 0; i < 3; ++i) { add_literal(r, cls); } s.mk_clause(cls.size(), cls.c_ptr()); } static void display_state(std::ostream& out, sat::solver& s, trail_t& t) { s.display(out); } static void pop_user_scope(sat::solver& s, trail_t& t) { std::cout << "pop\n"; s.user_pop(1); t.pop_back(); } static void push_user_scope(sat::solver& s, trail_t& t) { std::cout << "push\n"; s.user_push(); t.push_back(clauses_t()); } static void init_vars(sat::solver& s) { for (unsigned i = 0; i <= s_num_vars; ++i) { s.mk_var(); } } static void check_coherence(sat::solver& s1, trail_t& t) { params_ref p; reslimit rlim; sat::solver s2(p, rlim, 0); init_vars(s2); sat::literal_vector cls; for (unsigned i = 0; i < t.size(); ++i) { clauses_t& clss = t[i]; for (unsigned j = 0; j < clss.size(); ++j) { cls.reset(); cls.append(clss[j]); s2.mk_clause(cls.size(), cls.c_ptr()); } } lbool is_sat1 = s1.check(); lbool is_sat2 = s2.check(); if (is_sat1 != is_sat2) { s1.display(std::cout); s2.display(std::cout); } std::cout << is_sat1 << "\n"; SASSERT(is_sat1 == is_sat2); } void tst_sat_user_scope() { random_gen r(0); trail_t trail; params_ref p; reslimit rlim; sat::solver s(p, rlim, 0); // incremental solver init_vars(s); while (true) { for (unsigned i = 0; i < s_num_frames; ++i) { // push 3 frames, pop 2 for (unsigned k = 0; k < 3; ++k) { push_user_scope(s, trail); for (unsigned j = 0; j < s_num_clauses_per_frame; ++j) { add_clause(s, r, trail); } check_coherence(s, trail); } for (unsigned k = 0; k < 2; ++k) { pop_user_scope(s, trail); check_coherence(s, trail); } } for (unsigned i = 0; i < s_num_frames; ++i) { pop_user_scope(s, trail); check_coherence(s, trail); } } } z3-z3-4.4.1/src/test/simple_parser.cpp000066400000000000000000000032351260446376700175470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: simple_parser.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-14. Revision History: --*/ #include"cost_parser.h" #include"cost_evaluator.h" #include"arith_decl_plugin.h" #include"ast_pp.h" #include"well_sorted.h" #include"warning.h" #include"reg_decl_plugins.h" void tst_simple_parser() { ast_manager m; reg_decl_plugins(m); arith_util m_util(m); cost_parser p(m); var_ref_vector vs(m); cost_evaluator eval(m); p.add_var("x"); p.add_var("y"); expr_ref r(m); p.parse_string("(+ x (* y x))", r); TRACE("simple_parser", tout << mk_pp(r, m) << "\n";); p.parse_string("(+ x (* y x) x)", r); float vals[2] = { 2.0f, 3.0f }; TRACE("simple_parser", tout << mk_pp(r, m) << "\n"; tout << "val: " << eval(r, 2, vals) << "\n";); p.parse_string("(+ x (* y x) x", r); // << error p.parse_string("(x)", r); // << error p.parse_string("(+ x))", r); // <<< this is accepted TRACE("simple_parser", tout << mk_pp(r, m) << "\n";); p.parse_string(")x)", r); // error p.parse_string("(+ x (* 10 y) 2)", r); TRACE("simple_parser", tout << mk_pp(r, m) << "\n"; tout << "val: " << eval(r, 2, vals) << "\n";); p.parse_string("(ite (and (> x 3) (<= y 4)) 2 10)", r); TRACE("simple_parser", tout << mk_pp(r, m) << "\n"; tout << "val: " << eval(r, 2, vals) << "\n";); p.parse_string("(ite (or (> x 3) (<= y 4)) 2 10)", r); TRACE("simple_parser", tout << mk_pp(r, m) << "\n"; tout << "val: " << eval(r, 2, vals) << "\n";); } z3-z3-4.4.1/src/test/simplex.cpp000066400000000000000000000076161260446376700163720ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "sparse_matrix.h" #include "sparse_matrix_def.h" #include "simplex.h" #include "simplex_def.h" #include "mpq_inf.h" #include "vector.h" #include "rational.h" #define R rational typedef simplex::simplex Simplex; typedef simplex::sparse_matrix sparse_matrix; static vector vec(int i, int j) { vector nv; nv.resize(2); nv[0] = R(i); nv[1] = R(j); return nv; } // static vector vec(int i, int j, int k) { // vector nv = vec(i, j); // nv.push_back(R(k)); // return nv; // } // static vector vec(int i, int j, int k, int l) { // vector nv = vec(i, j, k); // nv.push_back(R(l)); // return nv; // } /// static vector vec(int i, int j, int k, int l, int x) { /// vector nv = vec(i, j, k, l); /// nv.push_back(R(x)); /// return nv; /// } // static vector vec(int i, int j, int k, int l, int x, int y) { // vector nv = vec(i, j, k, l, x); // nv.push_back(R(y)); // return nv; // } // static vector vec(int i, int j, int k, int l, int x, int y, int z) { // vector nv = vec(i, j, k, l, x, y); // nv.push_back(R(z)); // return nv; // } void add_row(Simplex& S, vector const& _v, R const& _b, bool is_eq = false) { unsynch_mpz_manager m; unsigned_vector vars; vector v(_v); R b(_b); R l(denominator(b)); scoped_mpz_vector coeffs(m); for (unsigned i = 0; i < v.size(); ++i) { l = lcm(l, denominator(v[i])); vars.push_back(i); S.ensure_var(i); } b *= l; b.neg(); for (unsigned i = 0; i < v.size(); ++i) { v[i] *= l; coeffs.push_back(v[i].to_mpq().numerator()); } unsigned nv = S.get_num_vars(); vars.push_back(nv); vars.push_back(nv+1); S.ensure_var(nv); S.ensure_var(nv+1); coeffs.push_back(mpz(-1)); coeffs.push_back(b.to_mpq().numerator()); mpq_inf one(mpq(1),mpq(0)); mpq_inf zero(mpq(0),mpq(0)); SASSERT(vars.size() == coeffs.size()); S.set_lower(nv, zero); if (is_eq) S.set_upper(nv, zero); S.set_lower(nv+1, one); S.set_upper(nv+1, one); S.add_row(nv, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); } static void feas(Simplex& S) { S.display(std::cout); lbool is_sat = S.make_feasible(); std::cout << "feasible: " << is_sat << "\n"; S.display(std::cout); } static void test1() { Simplex S; add_row(S, vec(1,0), R(1)); add_row(S, vec(0,1), R(1)); add_row(S, vec(1,1), R(1)); feas(S); } static void test2() { Simplex S; add_row(S, vec(1, 0), R(1)); add_row(S, vec(0, 1), R(1)); add_row(S, vec(1, 1), R(1), true); feas(S); } static void test3() { Simplex S; add_row(S, vec(-1, 0), R(-1)); add_row(S, vec(0, -1), R(-1)); add_row(S, vec(1, 1), R(1), true); feas(S); } static void test4() { Simplex S; add_row(S, vec(1, 0), R(1)); add_row(S, vec(0, -1), R(-1)); add_row(S, vec(1, 1), R(1), true); feas(S); } void tst_simplex() { Simplex S; std::cout << "simplex\n"; lbool is_sat = S.make_feasible(); std::cout << "feasible: " << is_sat << "\n"; unsynch_mpz_manager m; unsynch_mpq_inf_manager em; scoped_mpz_vector coeffs(m); svector vars; for (unsigned i = 0; i < 5; ++i) { S.ensure_var(i); vars.push_back(i); coeffs.push_back(mpz(i+1)); } // Simplex::row r = S.add_row(1, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); is_sat = S.make_feasible(); std::cout << "feasible: " << is_sat << "\n"; S.display(std::cout); _scoped_numeral num(em); num = std::make_pair(mpq(1), mpq(0)); S.set_lower(0, num); S.set_upper(0, num); is_sat = S.make_feasible(); std::cout << "feasible: " << is_sat << "\n"; S.display(std::cout); test1(); test2(); test3(); test4(); } z3-z3-4.4.1/src/test/simplifier.cpp000066400000000000000000000171201260446376700170430ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include "z3.h" #include "z3_private.h" #include #include "util.h" #include "trace.h" static void ev_const(Z3_context ctx, Z3_ast e) { Z3_ast r = Z3_simplify(ctx, e); TRACE("simplifier", tout << Z3_ast_to_string(ctx, e) << " -> "; tout << Z3_ast_to_string(ctx, r) << "\n";); Z3_ast_kind k = Z3_get_ast_kind(ctx, r); SASSERT(k == Z3_NUMERAL_AST || (k == Z3_APP_AST && (Z3_OP_TRUE == Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx, Z3_to_app(ctx, r))) || Z3_OP_FALSE == Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx, Z3_to_app(ctx, r)))))); } static void test_bv() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv1 = Z3_mk_bv_sort(ctx,1); Z3_sort bv2 = Z3_mk_bv_sort(ctx,2); Z3_sort bv72 = Z3_mk_bv_sort(ctx,72); Z3_ast bit1_1 = Z3_mk_numeral(ctx, "1", bv1); Z3_ast bit3_2 = Z3_mk_numeral(ctx, "3", bv2); Z3_ast e = Z3_mk_eq(ctx, bit3_2, Z3_mk_sign_ext(ctx, 1, bit1_1)); SASSERT(Z3_simplify(ctx, e) == Z3_mk_true(ctx)); TRACE("simplifier", tout << Z3_ast_to_string(ctx, e) << "\n";); Z3_ast b12 = Z3_mk_numeral(ctx, "12", bv72); Z3_ast b13 = Z3_mk_numeral(ctx, "13", bv72); ev_const(ctx, Z3_mk_bvnot(ctx,b12)); ev_const(ctx, Z3_mk_bvnot(ctx,Z3_simplify(ctx, Z3_mk_bvnot(ctx, b12)))); ev_const(ctx, Z3_mk_bvand(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvor(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvxor(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvnand(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvnor(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvxnor(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvneg(ctx,b12)); ev_const(ctx, Z3_mk_bvadd(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsub(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvmul(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvudiv(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsdiv(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsrem(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvuge(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsge(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvugt(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsgt(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvule(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvult(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsle(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvslt(ctx,b12,b13)); ev_const(ctx, Z3_mk_concat(ctx,b12,b13)); ev_const(ctx, Z3_mk_extract(ctx,43,1,b13)); ev_const(ctx, Z3_mk_sign_ext(ctx,33,b13)); ev_const(ctx, Z3_mk_zero_ext(ctx,33,b13)); ev_const(ctx, Z3_mk_bvshl(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvshl(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvlshr(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvashr(ctx,b12,b13)); ev_const(ctx, Z3_mk_rotate_left(ctx,21,b13)); ev_const(ctx, Z3_mk_rotate_right(ctx,21,b13)); Z3_del_config(cfg); Z3_del_context(ctx); } static void test_datatypes() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); 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; 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); Z3_ast a = Z3_simplify(ctx, Z3_mk_app(ctx, is_nil_decl, 1, &nil)); SASSERT(a == Z3_mk_true(ctx)); a = Z3_simplify(ctx, Z3_mk_app(ctx, is_cons_decl, 1, &nil)); SASSERT(a == Z3_mk_false(ctx)); Z3_ast one = Z3_mk_numeral(ctx, "1", int_ty); Z3_ast args[2] = { one, nil }; l1 = Z3_mk_app(ctx, cons_decl, 2, args); SASSERT(nil == Z3_simplify(ctx, Z3_mk_app(ctx, tail_decl, 1, &l1))); SASSERT(one == Z3_simplify(ctx, Z3_mk_app(ctx, head_decl, 1, &l1))); SASSERT(Z3_mk_false(ctx) == Z3_simplify(ctx, Z3_mk_eq(ctx, nil, l1))); Z3_del_config(cfg); Z3_del_context(ctx); } static void test_skolemize_bug() { Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg, "MODEL", "true"); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); Z3_sort Real = Z3_mk_real_sort(ctx); Z3_ast x = Z3_mk_bound(ctx, 0, Real); Z3_symbol x_name = Z3_mk_string_symbol(ctx, "x"); Z3_ast y = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, "y"), Real); Z3_ast xp = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, "xp"), Real); Z3_ast n0 = Z3_mk_numeral(ctx, "0", Real); Z3_ast n1 = Z3_mk_numeral(ctx, "1", Real); Z3_ast args1[2] = { x, n1 }; Z3_ast args2[2] = { x, y }; Z3_ast args[2] = { Z3_mk_eq(ctx, Z3_mk_add(ctx, 2, args1), xp), Z3_mk_ge(ctx, Z3_mk_add(ctx, 2, args2), n0) }; Z3_ast f = Z3_mk_and(ctx, 2, args); Z3_ast f2 = Z3_mk_exists(ctx, 0, 0, 0, 1, &Real, &x_name, f); std::cout << Z3_ast_to_string(ctx, f2) << "\n"; Z3_ast f3 = Z3_simplify(ctx, f2); std::cout << Z3_ast_to_string(ctx, f3) << "\n"; } static void test_bool() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_ast a = Z3_simplify(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, Z3_mk_false(ctx), Z3_mk_true(ctx)))); Z3_ast b = Z3_simplify(ctx, Z3_mk_not(ctx, Z3_mk_iff(ctx, Z3_mk_false(ctx), Z3_mk_true(ctx)))); SASSERT(Z3_mk_true(ctx) == a); SASSERT(Z3_mk_true(ctx) == b); TRACE("simplifier", tout << Z3_ast_to_string(ctx, a) << "\n";); TRACE("simplifier", tout << Z3_ast_to_string(ctx, b) << "\n";); Z3_del_config(cfg); Z3_del_context(ctx); } static void test_array() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort i = Z3_mk_int_sort(ctx); Z3_ast n1 = Z3_mk_numeral(ctx, "1", i); Z3_ast n2 = Z3_mk_numeral(ctx, "2", i); Z3_ast n3 = Z3_mk_numeral(ctx, "3", i); Z3_ast n4 = Z3_mk_numeral(ctx, "4", i); Z3_ast s1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"s1"), i); Z3_ast s2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"s2"), i); Z3_ast c1 = Z3_mk_const_array(ctx, i, n1); Z3_ast x1 = Z3_mk_store(ctx, Z3_mk_store(ctx, c1, n2, n3), n1, n4); Z3_ast x2 = Z3_mk_store(ctx, Z3_mk_store(ctx, c1, n1, n4), n2, n3); Z3_ast x3 = Z3_mk_store(ctx, Z3_mk_store(ctx, c1, s1, n1), n2, n3); Z3_ast x4 = Z3_mk_store(ctx, Z3_mk_store(ctx, Z3_mk_store(ctx, c1, n2, n3), n1, n4), n2, n3); Z3_ast xs[4] = { x1, x2, x3, x4}; Z3_ast exy = Z3_mk_eq(ctx, x2, x1); Z3_ast rxy = Z3_simplify(ctx, exy); TRACE("simplifier", tout << Z3_ast_to_string(ctx, rxy) << "\n";); TRACE("simplifier", tout << Z3_ast_to_string(ctx, Z3_simplify(ctx, Z3_mk_eq(ctx, x2, x3))) << "\n";); // SASSERT(rxy == Z3_mk_true(ctx)); // SASSERT(Z3_simplify(ctx, Z3_mk_eq(ctx, x2, x3)) == Z3_mk_false(ctx)); for (unsigned i = 0; i < 4; ++i) { for (unsigned j = 0; j < 4; ++j) { exy = Z3_mk_eq(ctx, xs[i], xs[j]); rxy = Z3_simplify(ctx, exy); TRACE("simplifier", tout << Z3_ast_to_string(ctx, exy); tout << " -> " << Z3_ast_to_string(ctx, rxy) << "\n"; ); } } Z3_ast sel1 = Z3_mk_select(ctx, x1, n1); Z3_ast sel2 = Z3_mk_select(ctx, x1, n4); TRACE("simplifier", tout << Z3_ast_to_string(ctx, Z3_simplify(ctx, sel1)) << "\n"; tout << Z3_ast_to_string(ctx, Z3_simplify(ctx, sel2)) << "\n"; ); Z3_del_config(cfg); Z3_del_context(ctx); } void tst_simplifier() { test_array(); test_bv(); test_datatypes(); test_bool(); test_skolemize_bug(); } #else void tst_simplifier() { } #endif z3-z3-4.4.1/src/test/small_object_allocator.cpp000066400000000000000000000023001260446376700213700ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include"util.h" #include"trace.h" #include"small_object_allocator.h" void tst_small_object_allocator() { small_object_allocator soa; char * p1 = new (soa) char[13]; char * q1 = new (soa) char[14]; char * p2 = new (soa) char[13]; TRACE("small_object_allocator", tout << "p1: " << (void*)p1 << " q1: " << (void*)q1 << " p2: " << (void*)p2 << "\n";); soa.deallocate(13,p1); char * p3 = new (soa) char[13]; TRACE("small_object_allocator", tout << "p3: " << (void*)p3 << "\n";); char * r1 = new (soa) char[1]; char * r2 = new (soa) char[1]; char * r3 = new (soa) char[1]; char * r4 = new (soa) char[1]; TRACE("small_object_allocator", tout << "r1: " << (void*)r1 << " r2: " << (void*)r2 << " r3: " << (void*)r3 << " r4: " << (void*)r4 << "\n";); soa.deallocate(1,r1); soa.deallocate(1,r3); r1 = new (soa) char[1]; soa.deallocate(1,r4); r4 = new (soa) char[1]; r3 = new (soa) char[1]; TRACE("small_object_allocator", tout << "r1: " << (void*)r1 << " r2: " << (void*)r2 << " r3: " << (void*)r3 << " r4: " << (void*)r4 << "\n";); } z3-z3-4.4.1/src/test/smt2print_parse.cpp000066400000000000000000000060031260446376700200320ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ // This is to test the print-parse facilities over the API // for SMT-LIB2. #include "z3.h" #include void test_print(Z3_context ctx, Z3_ast a) { Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT); char const* spec1 = Z3_benchmark_to_smtlib_string(ctx, "test", 0, 0, 0, 0, 0, a); std::cout << spec1 << "\n"; Z3_ast b = Z3_parse_smtlib2_string(ctx, spec1, 0, 0, 0, 0, 0, 0); char const* spec2 = Z3_ast_to_string(ctx, b); std::cout << spec2 << "\n"; } void test_parseprint(char const* spec) { Z3_context ctx = Z3_mk_context(0); std::cout << spec << "\n"; Z3_ast a = Z3_parse_smtlib2_string(ctx, spec, 0, 0, 0, 0, 0, 0); test_print(ctx, a); Z3_del_context(ctx); } void tst_smt2print_parse() { // test basic datatypes char const* spec1 = "(declare-datatypes (T) ((list (nil) (cons (car T) (cdr list)))))\n" "(declare-const x Int)\n" "(declare-const l (list Int))\n" "(declare-fun f ((list Int)) Bool)\n" "(assert (f (cons x l)))\n"; test_parseprint(spec1); // test basic arrays char const* spec2 = "(declare-const x Int)\n" "(declare-const a (Array Int Int))\n" "(declare-const b (Array (Array Int Int) Bool))\n" "(assert (select b a))\n" "(assert (= b ((as const (Array (Array Int Int) Bool)) true)))\n" "(assert (= b (store b a true)))\n" "(declare-const b1 (Array Bool Bool))\n" "(declare-const b2 (Array Bool Bool))\n" "(assert (= ((as const (Array Bool Bool)) false) ((_ map and) b1 b2)))\n"; // TBD: const, map, store test_parseprint(spec2); // Test mutually recursive datatypes char const* spec3 = "(declare-datatypes () ((list (nil) (cons (car tree) (cdr list))) (tree (leaf) (node (n list)))))\n" "(declare-const x tree)\n" "(declare-const l list)\n" "(declare-fun f (list) Bool)\n" "(assert (f (cons x l)))\n"; test_parseprint(spec3); // Test arithmetic char const* spec4 = "(declare-const x Real)\n" "(declare-const y Int)\n" "(assert (= x 0.0))\n" "(assert (= y 6))\n" "(assert (> (/ x 1.4) (to_real y)))"; test_parseprint(spec4); // Test bit-vectors char const* spec5 = "(declare-const x (_ BitVec 4))\n" "(declare-const y (_ BitVec 4))\n" "(assert (bvule x (bvmul y (concat ((_ extract 2 0) x) ((_ extract 3 3) #xf0)))))"; test_parseprint(spec5); // Test ? } z3-z3-4.4.1/src/test/smt_context.cpp000066400000000000000000000013501260446376700172450ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "smt_context.h" #include "reg_decl_plugins.h" void tst_smt_context() { smt_params params; ast_manager m; reg_decl_plugins(m); smt::context ctx(m, params); app_ref a1(m.mk_const(symbol("a"), m.mk_bool_sort()), m); app_ref b1(m.mk_const(symbol("b"), m.mk_bool_sort()), m); app_ref c1(m.mk_const(symbol("c"), m.mk_bool_sort()), m); app_ref na1(m.mk_not(a1), m); ctx.assert_expr(na1); ctx.assert_expr(m.mk_or(c1.get(), b1.get())); { app_ref nc(m.mk_not(c1), m); ptr_vector assumptions; assumptions.push_back(nc.get()); ctx.check(assumptions.size(), assumptions.c_ptr()); } ctx.check(); } z3-z3-4.4.1/src/test/sorting_network.cpp000066400000000000000000000207571260446376700201500ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "trace.h" #include "vector.h" #include "ast.h" #include "ast_pp.h" #include "reg_decl_plugins.h" #include "sorting_network.h" #include "smt_kernel.h" #include "model_smt2_pp.h" #include "smt_params.h" #include "ast_util.h" struct ast_ext { ast_manager& m; ast_ext(ast_manager& m):m(m) {} typedef expr* T; typedef expr_ref_vector vector; T mk_ite(T a, T b, T c) { return m.mk_ite(a, b, c); } T mk_le(T a, T b) { if (m.is_bool(a)) { return m.mk_implies(a, b); } UNREACHABLE(); return 0; } T mk_default() { return m.mk_false(); } }; struct unsigned_ext { unsigned_ext() {} typedef unsigned T; typedef svector vector; T mk_ite(T a, T b, T c) { return (a==1)?b:c; } T mk_le(T a, T b) { return (a <= b)?1:0; } T mk_default() { return 0; } }; static void is_sorted(svector const& v) { for (unsigned i = 0; i + 1 < v.size(); ++i) { SASSERT(v[i] <= v[i+1]); } } static void test_sorting1() { svector in, out; unsigned_ext uext; sorting_network sn(uext); in.push_back(0); in.push_back(1); in.push_back(0); in.push_back(1); in.push_back(1); in.push_back(0); sn(in, out); is_sorted(out); for (unsigned i = 0; i < out.size(); ++i) { std::cout << out[i]; } std::cout << "\n"; } static void test_sorting2() { svector in, out; unsigned_ext uext; sorting_network sn(uext); in.push_back(0); in.push_back(1); in.push_back(2); in.push_back(1); in.push_back(1); in.push_back(3); sn(in, out); is_sorted(out); for (unsigned i = 0; i < out.size(); ++i) { std::cout << out[i]; } std::cout << "\n"; } static void test_sorting4_r(unsigned i, svector& in) { if (i == in.size()) { svector out; unsigned_ext uext; sorting_network sn(uext); sn(in, out); is_sorted(out); std::cout << "sorted\n"; } else { in[i] = 0; test_sorting4_r(i+1, in); in[i] = 1; test_sorting4_r(i+1, in); } } static void test_sorting4() { svector in; in.resize(5); test_sorting4_r(0, in); in.resize(8); test_sorting4_r(0, in); } void test_sorting3() { ast_manager m; reg_decl_plugins(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < 7; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } for (unsigned i = 0; i < in.size(); ++i) { std::cout << mk_pp(in[i].get(), m) << "\n"; } ast_ext aext(m); sorting_network sn(aext); sn(in, out); std::cout << "size: " << out.size() << "\n"; for (unsigned i = 0; i < out.size(); ++i) { std::cout << mk_pp(out[i].get(), m) << "\n"; } } struct ast_ext2 { ast_manager& m; expr_ref_vector m_clauses; expr_ref_vector m_trail; ast_ext2(ast_manager& m):m(m), m_clauses(m), m_trail(m) {} typedef expr* literal; typedef ptr_vector literal_vector; expr* trail(expr* e) { m_trail.push_back(e); return e; } 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) { return out << mk_pp(lit, m); } literal fresh() { return trail(m.mk_fresh_const("x", m.mk_bool_sort())); } void mk_clause(unsigned n, literal const* lits) { m_clauses.push_back(mk_or(m, n, lits)); } }; static void test_sorting_eq(unsigned n, unsigned k) { SASSERT(k < n); ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < n; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); expr_ref result(m); // equality: std::cout << "eq " << k << "\n"; solver.push(); result = sn.eq(k, in.size(), in.c_ptr()); solver.assert_expr(result); for (unsigned i = 0; i < ext.m_clauses.size(); ++i) { solver.assert_expr(ext.m_clauses[i].get()); } lbool res = solver.check(); SASSERT(res == l_true); solver.push(); for (unsigned i = 0; i < k; ++i) { solver.assert_expr(in[i].get()); } res = solver.check(); SASSERT(res == l_true); solver.assert_expr(in[k].get()); res = solver.check(); if (res == l_true) { TRACE("pb", unsigned sz = solver.size(); for (unsigned i = 0; i < sz; ++i) { tout << mk_pp(solver.get_formulas()[i], m) << "\n"; }); model_ref model; solver.get_model(model); model_smt2_pp(std::cout, m, *model, 0); TRACE("pb", model_smt2_pp(tout, m, *model, 0);); } SASSERT(res == l_false); solver.pop(1); ext.m_clauses.reset(); } static void test_sorting_le(unsigned n, unsigned k) { ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < n; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); expr_ref result(m); // B <= k std::cout << "le " << k << "\n"; solver.push(); result = sn.le(false, k, in.size(), in.c_ptr()); solver.assert_expr(result); for (unsigned i = 0; i < ext.m_clauses.size(); ++i) { solver.assert_expr(ext.m_clauses[i].get()); } lbool res = solver.check(); SASSERT(res == l_true); for (unsigned i = 0; i < k; ++i) { solver.assert_expr(in[i].get()); } res = solver.check(); SASSERT(res == l_true); solver.assert_expr(in[k].get()); res = solver.check(); if (res == l_true) { TRACE("pb", unsigned sz = solver.size(); for (unsigned i = 0; i < sz; ++i) { tout << mk_pp(solver.get_formulas()[i], m) << "\n"; }); model_ref model; solver.get_model(model); model_smt2_pp(std::cout, m, *model, 0); TRACE("pb", model_smt2_pp(tout, m, *model, 0);); } SASSERT(res == l_false); solver.pop(1); ext.m_clauses.reset(); } void test_sorting_ge(unsigned n, unsigned k) { ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < n; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); expr_ref result(m); // k <= B std::cout << "ge " << k << "\n"; solver.push(); result = sn.ge(false, k, in.size(), in.c_ptr()); solver.assert_expr(result); for (unsigned i = 0; i < ext.m_clauses.size(); ++i) { solver.assert_expr(ext.m_clauses[i].get()); } lbool res = solver.check(); SASSERT(res == l_true); solver.push(); for (unsigned i = 0; i < n - k; ++i) { solver.assert_expr(m.mk_not(in[i].get())); } res = solver.check(); SASSERT(res == l_true); solver.assert_expr(m.mk_not(in[n - k].get())); res = solver.check(); if (res == l_true) { TRACE("pb", unsigned sz = solver.size(); for (unsigned i = 0; i < sz; ++i) { tout << mk_pp(solver.get_formulas()[i], m) << "\n"; }); model_ref model; solver.get_model(model); model_smt2_pp(std::cout, m, *model, 0); TRACE("pb", model_smt2_pp(tout, m, *model, 0);); } SASSERT(res == l_false); solver.pop(1); } void test_sorting5(unsigned n, unsigned k) { std::cout << "n: " << n << " k: " << k << "\n"; test_sorting_le(n, k); test_sorting_eq(n, k); test_sorting_ge(n, k); } void tst_sorting_network() { test_sorting_eq(11,7); for (unsigned n = 3; n < 20; n += 2) { for (unsigned k = 1; k < n; ++k) { test_sorting5(n, k); } } test_sorting1(); test_sorting2(); test_sorting3(); test_sorting4(); } z3-z3-4.4.1/src/test/stack.cpp000066400000000000000000000031521260446376700160050ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: stack.cpp Abstract: Low level stack (aka stack-based allocator). Author: Leonardo (leonardo) 2011-02-27 Notes: --*/ #include #include"stack.h" #include"vector.h" typedef std::pair point; static void tst1() { stack s; point * p1 = new (s) point(10, 20); point * p2 = new (s) point(30, 40); void * ptr = s.allocate(16000); SASSERT(p2->first == 30 && p2->second == 40); SASSERT(p1->first == 10 && p1->second == 20); s.deallocate(static_cast(ptr)); s.deallocate(p2); s.deallocate(p1); } static void tst2(unsigned num, unsigned del_rate) { ptr_vector ptrs; stack s; for (unsigned i = 0; i < num; i++) { SASSERT(ptrs.empty() == s.empty()); SASSERT(s.empty() || ptrs.back() == s.top()); if (!ptrs.empty() && rand() % del_rate == 0) { s.deallocate(); ptrs.pop_back(); } else { unsigned size; if (rand()%10 == 0) { size = 8192 + rand()%800; } else { size = rand()%100; } char * ptr = static_cast(s.allocate(size)); ptrs.push_back(ptr); } } while (s.empty()) { SASSERT(ptrs.empty() == s.empty()); SASSERT(s.empty() || ptrs.back() == s.top()); s.deallocate(); ptrs.pop_back(); } } void tst_stack() { tst1(); tst2(1000, 10); tst2(2000, 2); tst2(100000, 10); tst2(300000, 5); tst2(300000, 2); tst2(300000, 7); } z3-z3-4.4.1/src/test/string_buffer.cpp000066400000000000000000000015331260446376700175400ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_string_buffer.cpp Abstract: Test string buffer Author: Leonardo de Moura (leonardo) 2006-10-14. Revision History: --*/ #include #include"debug.h" #include"string_buffer.h" #include"trace.h" static void tst1() { string_buffer<> b; b << "Testing" << 10 << true; SASSERT(strcmp(b.c_str(), "Testing10true") == 0); } static void tst2() { string_buffer<> b; for (unsigned i = 0; i < 10000; i++) { int r = rand() % 10; b << r; } TRACE("string_buffer", tout << b.c_str() << "\n";); SASSERT(strlen(b.c_str()) == 10000); } static void tst3() { string_buffer<32> b; string_buffer<128> b2; b2 << "World"; b << "Hello" << " " << b2; SASSERT(strcmp(b.c_str(), "Hello World") == 0); } void tst_string_buffer() { tst1(); tst2(); tst3(); } z3-z3-4.4.1/src/test/substitution.cpp000066400000000000000000000027171260446376700174620ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "expr_substitution.h" #include "smt_params.h" #include "substitution.h" #include "unifier.h" #include "bv_decl_plugin.h" #include "ast_pp.h" #include "arith_decl_plugin.h" #include "reg_decl_plugins.h" void tst_substitution() { memory::initialize(0); smt_params params; params.m_model = true; enable_trace("subst_bug"); ast_manager m; reg_decl_plugins(m); var_ref v1(m.mk_var(0, m.mk_bool_sort()), m); var_ref v2(m.mk_var(1, m.mk_bool_sort()), m); var_ref v3(m.mk_var(2, m.mk_bool_sort()), m); var_ref v4(m.mk_var(3, m.mk_bool_sort()), m); substitution subst(m); subst.reserve(1,4); unifier unif(m); bool ok1 = unif(v1.get(), v2.get(), subst, false); bool ok2 = unif(v2.get(), v1.get(), subst, false); expr_ref res(m); TRACE("substitution", subst.display(tout);); TRACE("substitution", tout << ok1 << " " << ok2 << "\n";); subst.display(std::cout); subst.apply(v1.get(), res); TRACE("substitution", tout << mk_pp(res, m) << "\n";); expr_ref q(m), body(m); sort_ref_vector sorts(m); svector names; sorts.push_back(m.mk_bool_sort()); names.push_back(symbol("dude")); body = m.mk_and(m.mk_eq(v1,v2), m.mk_eq(v3,v4)); q = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), body); subst.apply(q, res); TRACE("substitution", tout << mk_pp(q, m) << "\n->\n" << mk_pp(res, m) << "\n";); } z3-z3-4.4.1/src/test/symbol.cpp000066400000000000000000000023511260446376700162050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_symbol.cpp Abstract: Test symbol class. Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #include #include"symbol.h" #include"debug.h" static void tst1() { symbol s1("foo"); symbol s2("boo"); symbol s3("foo"); SASSERT(s1 != s2); SASSERT(s1 == s3); std::cout << s1 << " " << s2 << " " << s3 << "\n"; SASSERT(s1 == "foo"); SASSERT(s1 != "boo"); SASSERT(s2 != "foo"); SASSERT(s3 == "foo"); SASSERT(s2 == "boo"); SASSERT(lt(s2, s1)); SASSERT(!lt(s1, s2)); SASSERT(!lt(s1, s3)); SASSERT(lt(symbol("abcc"), symbol("abcd"))); SASSERT(!lt(symbol("abcd"), symbol("abcc"))); SASSERT(lt(symbol("abc"), symbol("abcc"))); SASSERT(!lt(symbol("abcd"), symbol("abc"))); SASSERT(lt(symbol(10), s1)); SASSERT(!lt(s1, symbol(10))); SASSERT(lt(symbol(10), symbol(20))); SASSERT(!lt(symbol(20), symbol(10))); SASSERT(!lt(symbol(10), symbol(10))); SASSERT(lt(symbol("a"), symbol("b"))); SASSERT(!lt(symbol("z"), symbol("b"))); SASSERT(!lt(symbol("zzz"), symbol("b"))); SASSERT(lt(symbol("zzz"), symbol("zzzb"))); } void tst_symbol() { tst1(); } z3-z3-4.4.1/src/test/symbol_table.cpp000066400000000000000000000017201260446376700173530ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_symbol_table.cpp Abstract: Test symbol table module. Author: Leonardo de Moura (leonardo) 2006-09-19. Revision History: --*/ #include"symbol_table.h" static void tst1() { symbol_table t; t.insert(symbol("foo"), 35); SASSERT(t.contains(symbol("foo"))); SASSERT(!t.contains(symbol("boo"))); t.begin_scope(); t.insert(symbol("boo"), 20); SASSERT(t.contains(symbol("boo"))); #ifdef Z3DEBUG int tmp; #endif SASSERT(t.find(symbol("boo"), tmp) && tmp == 20); SASSERT(t.find(symbol("foo"), tmp) && tmp == 35); t.insert(symbol("foo"), 100); SASSERT(t.find(symbol("foo"), tmp) && tmp == 100); t.end_scope(); SASSERT(t.find(symbol("foo"), tmp) && tmp == 35); SASSERT(!t.contains(symbol("boo"))); t.reset(); SASSERT(!t.contains(symbol("boo"))); SASSERT(!t.contains(symbol("foo"))); } void tst_symbol_table() { tst1(); } z3-z3-4.4.1/src/test/tbv.cpp000066400000000000000000000040431260446376700154730ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "tbv.h" static void tst1(unsigned num_bits) { tbv_manager m(num_bits); tbv* b1 = m.allocate1(); tbv* b0 = m.allocate0(); tbv* bX = m.allocateX(); tbv* bN = m.allocate(31); m.display(std::cout, *b0) << "\n"; m.display(std::cout, *b1) << "\n"; m.display(std::cout, *bX) << "\n"; m.display(std::cout, *bN) << "\n"; SASSERT(!m.equals(*b1,*b0)); SASSERT(!m.equals(*b1,*bX)); SASSERT(!m.equals(*b0,*bX)); m.set_and(*bX,*b0); SASSERT(m.equals(*b0,*bX)); SASSERT(!m.equals(*b1,*bX)); m.copy(*bX,*b1); SASSERT(m.equals(*b1,*bX)); SASSERT(!m.equals(*b0,*bX)); m.fillX(*bX); VERIFY(m.intersect(*bX,*b0,*bN)); SASSERT(m.equals(*b0, *bN)); VERIFY(!m.intersect(*b0,*b1,*bN)); m.fill1(*b1); bit_vector to_delete; to_delete.reserve(num_bits, false); tbv_manager m2(num_bits-2); to_delete.set(1); to_delete.set(3); m.set(*b1, 2, BIT_0); m.set(*b1, 4, BIT_x); tbv_ref b2(m2, m2.project(to_delete, *b1)); m.display(std::cout, *b1) << " -> "; m2.display(std::cout, *b2) << "\n"; m.deallocate(b0); m.deallocate(b1); m.deallocate(bX); m.deallocate(bN); } static void tst0() { tbv_manager m(0); tbv_ref t1(m), t2(m), t3(m); t1 = m.allocate1(); t2 = m.allocate0(); t3 = m.allocateX(); m.display(std::cout, *t1) << "\n"; m.display(std::cout, *t2) << "\n"; m.display(std::cout, *t3) << "\n"; SASSERT(m.equals(*t1, *t2)); SASSERT(m.equals(*t1, *t3)); } static void tst2(unsigned num_bits) { tbv_manager m(num_bits); tbv_ref t(m), t2(m); for (unsigned i = 0; i < 55; ++i) { t = m.allocate(i); SASSERT(m.is_well_formed(*t)); t2 = m.allocate(i+1); VERIFY(!m.set_and(*t2, *t)); SASSERT(!m.is_well_formed(*t2)); } } void tst_tbv() { tst0(); tst1(31); tst1(11); tst1(15); tst1(16); tst1(17); tst2(31); tst2(11); tst2(15); tst2(16); tst2(17); } z3-z3-4.4.1/src/test/test_util.h000066400000000000000000000035121260446376700163610ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #pragma once #include "stopwatch.h" struct test_context { bool test_ok; unsigned test_fails; unsigned fails; double test_time; stopwatch test_timer; test_context() : fails(0) {} }; #undef min #undef max #define TEST_CLASS(context, CLASS_NAME, TYPE, CALL_DESTRUCTORS) \ context.test_fails = 0; \ cout << "" << #CLASS_NAME << "<" << #TYPE << ">" << endl; \ CLASS_NAME ## _test< TYPE, CALL_DESTRUCTORS >::run_tests(context); \ context.fails += context.test_fails; #define TEST_METHOD(context, METHOD) \ cout << "\t" << #METHOD << "... "; \ context.test_timer.reset(); \ context.test_ok = test_ ## METHOD; \ context.test_time = context.test_timer.get_seconds(); \ if (context.test_ok) { \ cout << "PASS"; \ if (context.test_time > 0) { \ cout << "(" << context.test_time << "s)"; \ } \ cout << endl; \ } \ else { \ cout << "FAIL"; \ if (context.test_time > 0) { \ cout << "(" << context.test_time << "s)"; \ } \ cout << endl; \ ++ context.test_fails; \ } \ z3-z3-4.4.1/src/test/theory_dl.cpp000066400000000000000000000015721260446376700166750ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "smt_context.h" #include "dl_decl_plugin.h" #include "ast_pp.h" #include "model_v2_pp.h" #include "reg_decl_plugins.h" void tst_theory_dl() { ast_manager m; smt_params params; params.m_model = true; datalog::dl_decl_util u(m); smt::context ctx(m, params); reg_decl_plugins(m); expr_ref a(m), b(m), c(m); sort_ref s(m); s = u.mk_sort(symbol("S"),111); a = m.mk_const(symbol("a"),s); b = m.mk_const(symbol("b"),s); ctx.assert_expr(u.mk_lt(a, b)); ctx.check(); ref md; ctx.get_model(md); model_v2_pp(std::cout, *md.get()); c = m.mk_const(symbol("c"),s); ctx.assert_expr(u.mk_lt(b, c)); ctx.check(); ctx.get_model(md); model_v2_pp(std::cout, *md.get()); ctx.assert_expr(u.mk_lt(c, a)); std::cout << ctx.check() << "\n"; } z3-z3-4.4.1/src/test/theory_pb.cpp000066400000000000000000000111531260446376700166730ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "smt_context.h" #include "ast_pp.h" #include "model_v2_pp.h" #include "reg_decl_plugins.h" #include "theory_pb.h" #include "th_rewriter.h" unsigned populate_literals(unsigned k, smt::literal_vector& lits) { SASSERT(k < (1u << lits.size())); unsigned t = 0; for (unsigned i = 0; i < lits.size(); ++i) { if (k & (1 << i)) { lits[i] = smt::true_literal; t++; } else { lits[i] = smt::false_literal; } } return t; } class pb_fuzzer { ast_manager& m; random_gen rand; smt_params params; smt::context ctx; expr_ref_vector vars; public: pb_fuzzer(ast_manager& m): m(m), rand(0), ctx(m, params), vars(m) { params.m_model = true; params.m_pb_enable_simplex = true; unsigned N = 3; for (unsigned i = 0; i < N; ++i) { std::stringstream strm; strm << "b" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort())); std::cout << "(declare-const " << strm.str() << " Bool)\n"; } } void fuzz() { enable_trace("pb"); enable_trace("simplex"); unsigned nr = 0; for (unsigned i = 0; i < 100000; ++i) { fuzz_round(nr, 2); } } private: void add_ineq() { pb_util pb(m); expr_ref fml(m), tmp(m); th_rewriter rw(m); vector coeffs(vars.size()); expr_ref_vector args(vars); while (true) { rational k(rand(6)); for (unsigned i = 0; i < coeffs.size(); ++i) { int v = 3 - rand(5); coeffs[i] = rational(v); if (coeffs[i].is_neg()) { args[i] = m.mk_not(args[i].get()); coeffs[i].neg(); k += coeffs[i]; } } fml = pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), k); rw(fml, tmp); rw(tmp, tmp); if (pb.is_ge(tmp)) { fml = tmp; break; } } std::cout << "(assert " << fml << ")\n"; ctx.assert_expr(fml); } void fuzz_round(unsigned& num_rounds, unsigned lvl) { unsigned num_rounds2 = 0; lbool is_sat = l_true; std::cout << "(push)\n"; ctx.push(); unsigned r = 0; while (is_sat == l_true && r <= num_rounds + 1) { add_ineq(); std::cout << "(check-sat)\n"; is_sat = ctx.check(); if (lvl > 0 && is_sat == l_true) { fuzz_round(num_rounds2, lvl-1); } ++r; } num_rounds = r; std::cout << "; number of rounds: " << num_rounds << " level: " << lvl << "\n"; ctx.pop(1); std::cout << "(pop)\n"; } }; static void fuzz_pb() { ast_manager m; reg_decl_plugins(m); pb_fuzzer fuzzer(m); fuzzer.fuzz(); } void tst_theory_pb() { fuzz_pb(); ast_manager m; smt_params params; params.m_model = true; reg_decl_plugins(m); expr_ref tmp(m); enable_trace("pb"); for (unsigned N = 4; N < 11; ++N) { for (unsigned i = 0; i < (1u << N); ++i) { smt::literal_vector lits(N, smt::false_literal); unsigned k = populate_literals(i, lits); std::cout << "k:" << k << " " << N << "\n"; std::cout.flush(); TRACE("pb", tout << "k " << k << ": "; for (unsigned j = 0; j < lits.size(); ++j) { tout << lits[j] << " "; } tout << "\n";); { smt::context ctx(m, params); ctx.push(); smt::literal l = smt::theory_pb::assert_ge(ctx, k+1, lits.size(), lits.c_ptr()); if (l != smt::false_literal) { ctx.assign(l, 0, false); TRACE("pb", tout << "assign: " << l << "\n"; ctx.display(tout);); VERIFY(l_false == ctx.check()); } ctx.pop(1); } { smt::context ctx(m, params); ctx.push(); smt::literal l = smt::theory_pb::assert_ge(ctx, k, lits.size(), lits.c_ptr()); SASSERT(l != smt::false_literal); ctx.assign(l, 0, false); TRACE("pb", ctx.display(tout);); VERIFY(l_true == ctx.check()); ctx.pop(1); } } } } z3-z3-4.4.1/src/test/timeout.cpp000066400000000000000000000003041260446376700163620ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "timeout.h" #include "trace.h" #ifdef _WINDOWS #include void tst_timeout() { } #else void tst_timeout() { } #endif z3-z3-4.4.1/src/test/total_order.cpp000066400000000000000000000073761260446376700172320ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: total_order.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-07-01. Revision History: --*/ #include"total_order.h" #include"timeit.h" static void tst1() { uint_total_order to; to.insert(1); to.insert_after(1, 2); to.insert_after(1, 3); SASSERT(to.lt(1, 2)); SASSERT(to.lt(3, 2)); SASSERT(to.lt(1, 3)); SASSERT(!to.lt(2, 3)); SASSERT(!to.lt(3, 1)); SASSERT(!to.lt(2, 2)); std::cout << to << "\n"; } static void tst2() { uint_total_order to; to.insert(1); to.insert_after(1, 2); to.insert_after(2, 3); for (unsigned i = 0; i < 1000; i++) { to.move_after(3, 1); to.move_after(1, 2); to.move_after(2, 3); SASSERT(to.lt(1,2)); SASSERT(to.lt(2,3)); } } static void tst3(unsigned sz, unsigned num_rounds) { uint_total_order to; to.insert(0); for (unsigned i = 0; i < sz; i++) { to.insert_after(i, i+1); } for (unsigned i = 0; i < num_rounds; i++) { unsigned v1 = rand() % sz; unsigned v2 = rand() % sz; if (v1 != v2) to.move_after(v1, v2); if (i % 1000 == 0) { std::cout << "*"; std::cout.flush(); } } std::cout << std::endl; } void move_after(unsigned_vector & v, unsigned_vector & inv_v, unsigned a, unsigned b) { if (a == b) return; // std::cout << std::endl; // display(std::cout, v.begin(), v.end()); std::cout << std::endl; // std::cout << "move_after(" << a << ", " << b << ")\n"; unsigned pos_a = inv_v[a]; unsigned pos_b = inv_v[b]; SASSERT(pos_a != pos_b); if (pos_b < pos_a) { for (unsigned i = pos_b; i < pos_a; i++) { v[i] = v[i+1]; inv_v[v[i+1]] = i; } v[pos_a] = b; inv_v[b] = pos_a; SASSERT(inv_v[b] == inv_v[a] + 1); } else { SASSERT(pos_b > pos_a); for (unsigned i = pos_b; i > pos_a + 1; i--) { v[i] = v[i-1]; inv_v[v[i-1]] = i; } v[pos_a+1] = b; inv_v[b] = pos_a+1; SASSERT(inv_v[b] == inv_v[a] + 1); } // display(std::cout, v.begin(), v.end()); std::cout << std::endl; } static void tst4(unsigned sz, unsigned num_rounds) { uint_total_order to; unsigned_vector v; unsigned_vector inv_v; to.insert(0); v.push_back(0); inv_v.push_back(0); for (unsigned i = 0; i < sz; i++) { to.insert_after(i, i+1); v.push_back(i+1); inv_v.push_back(i+1); } for (unsigned i = 0; i < num_rounds; i++) { unsigned v1 = rand() % sz; unsigned v2 = rand() % sz; if (v1 != v2) { to.move_after(v1, v2); move_after(v, inv_v, v1, v2); } for (unsigned k = 0; k < sz - 1; k++) { SASSERT(inv_v[v[k]] == k); SASSERT(to.lt(v[k], v[k+1])); } if (i % 1000 == 0) { std::cout << "*"; std::cout.flush(); } } std::cout << std::endl; } static void bad_case(unsigned sz, unsigned num_rounds) { uint_total_order to; to.insert(0); for (unsigned i = 0; i < sz; i++) { to.insert_after(i, i+1); } for (unsigned i = 0; i < num_rounds; i++) { to.move_after(sz, 0); for (unsigned j = 0; j < sz; j++) { to.move_after(j, j+1); } if (i % 10 == 0) { std::cout << "*"; std::cout.flush(); } } std::cout << std::endl; } void tst_total_order() { bad_case(100, 1000); tst1(); tst2(); tst4(3, 1000000); tst4(100, 100000); tst4(512, 100000); tst4(1000, 100000); tst3(100, 100000); tst3(1000, 100000); } z3-z3-4.4.1/src/test/trigo.cpp000066400000000000000000000125451260446376700160320ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: trigo.cpp Abstract: Test trigonometric primitives in the interval class. Author: Leonardo de Moura (leonardo) 2012-08-20 Revision History: --*/ #include #include"interval_def.h" #include"dependency.h" #include"mpq.h" #include"ast.h" #include"debug.h" #include"im_float_config.h" #define PREC 100000 static void tst_sine_core(std::ostream & out, unsynch_mpq_manager & nm, interval_manager & im, mpq & a, unsigned k) { scoped_mpq lo(nm), hi(nm); im.sine(a, k, lo, hi); nm.display(out, lo); out << " <= Sin["; nm.display(out, a); out << "]\n"; out << "Sin["; nm.display(out, a); out << "] <= "; nm.display(out, hi); out << "\n"; } static void tst_sine(std::ostream & out, unsigned N, unsigned k) { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); scoped_mpq a(nm); nm.set(a, 0); tst_sine_core(out, nm, im, a, 1); for (unsigned i = 0; i < N; i++) { nm.set(a, 4 * (rand() % PREC), PREC); if (rand() % 2 == 0) nm.neg(a); tst_sine_core(out, nm, im, a, k); } } static void tst_cosine_core(std::ostream & out, unsynch_mpq_manager & nm, interval_manager & im, mpq & a, unsigned k) { scoped_mpq lo(nm), hi(nm); im.cosine(a, k, lo, hi); nm.display(out, lo); out << " <= Cos["; nm.display(out, a); out << "]\n"; out << "Cos["; nm.display(out, a); out << "] <= "; nm.display(out, hi); out << "\n"; } static void tst_cosine(std::ostream & out, unsigned N, unsigned k) { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); scoped_mpq a(nm); nm.set(a, 0); tst_cosine_core(out, nm, im, a, 1); for (unsigned i = 0; i < N; i++) { nm.set(a, 4 * (rand() % PREC), PREC); if (rand() % 2 == 0) nm.neg(a); tst_cosine_core(out, nm, im, a, k); } } template static void tst_float_sine_core(std::ostream & out, fmanager & fm, interval_manager > & im, typename fmanager::numeral & a, unsigned k) { _scoped_numeral lo(fm), hi(fm); im.sine(a, k, lo, hi); out << fm.to_rational_string(lo) << " <= Sin[" << fm.to_rational_string(a) << "]\n"; out << "Sin[" << fm.to_rational_string(a) << "] <= " << fm.to_rational_string(hi) << "\n"; } const unsigned EBITS = 11; const unsigned SBITS = 53; template static void tst_float_sine(std::ostream & out, unsigned N, unsigned k) { fmanager fm; im_float_config ifc(fm, EBITS, SBITS); interval_manager > im(ifc); _scoped_numeral a(fm); fm.set(a, EBITS, SBITS, static_cast(0)); tst_float_sine_core(out, fm, im, a, 1); // fm.set(a, EBITS, SBITS, MPF_ROUND_TOWARD_POSITIVE, 25336, 100000); // tst_float_sine_core(out, fm, im, a, k); // return; for (unsigned i = 0; i < N; i++) { unsigned n = 4 * (rand() % PREC); unsigned d = PREC; TRACE("sine", tout << "next-val : " << n << "/" << d << "\n";); fm.set(a, EBITS, SBITS, MPF_ROUND_TOWARD_POSITIVE, n, d); if (rand() % 2 == 0) fm.neg(a); tst_float_sine_core(out, fm, im, a, k); } } #if 0 static void tst_mpf_bug() { mpf_manager fm; scoped_mpf a(fm), b(fm), c(fm); fm.set(a, EBITS, SBITS, 2); fm.set(b, EBITS, SBITS, 3); std::cout << "a: " << fm.to_double(a) << "\n"; std::cout << "b: " << fm.to_double(b) << "\n"; fm.mul(MPF_ROUND_TOWARD_NEGATIVE, a, b, c); std::cout << "c: " << fm.to_double(c) << "\n"; } #endif static void tst_e(std::ostream & out) { unsynch_mpq_manager nm; im_default_config imc(nm); interval_manager im(imc); im_default_config::interval r; for (unsigned i = 0; i < 64; i++) { im.e(i, r); out << nm.to_string(im.lower(r)) << " <= E\n"; out << "E <= " << nm.to_string(im.upper(r)) << "\n"; } im.del(r); } static void tst_e_float(std::ostream & out) { std::cout << "e float...\n"; unsynch_mpq_manager qm; mpf_manager fm; im_float_config ifc(fm); interval_manager > im(ifc); scoped_mpq q(qm); im_float_config::interval r; for (unsigned i = 0; i < 64; i++) { im.e(i, r); out << fm.to_rational_string(im.lower(r)) << " <= E\n"; out << "E <= " << fm.to_rational_string(im.upper(r)) << "\n"; } del_f_interval(ifc, r); } void tst_trigo() { // enable_trace("sine"); // enable_trace("sine_bug"); // enable_trace("mpf_mul_bug"); std::ofstream out("trigo-lemmas.math"); tst_e_float(out); tst_e(out); tst_float_sine(out, 100, 5); tst_float_sine(out, 100, 7); tst_sine(out, 200, 3); tst_sine(out, 200, 5); tst_sine(out, 200, 9); tst_cosine(out, 200, 3); tst_cosine(out, 200, 5); tst_cosine(out, 200, 9); } z3-z3-4.4.1/src/test/udoc_relation.cpp000066400000000000000000000725071260446376700175410ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "udoc_relation.h" #include "trace.h" #include "vector.h" #include "ast.h" #include "ast_pp.h" #include "reg_decl_plugins.h" #include "sorting_network.h" #include "smt_kernel.h" #include "model_smt2_pp.h" #include "smt_params.h" #include "ast_util.h" #include "expr_safe_replace.h" #include "th_rewriter.h" #include "dl_relation_manager.h" #include "dl_register_engine.h" #include "rel_context.h" #include "bv_decl_plugin.h" #include "check_relation.h" class udoc_tester { typedef datalog::relation_base relation_base; typedef datalog::udoc_relation udoc_relation; typedef datalog::udoc_plugin udoc_plugin; typedef datalog::relation_signature relation_signature; typedef datalog::relation_fact relation_fact; typedef scoped_ptr rel_mut; typedef scoped_ptr rel_union; struct init { init(ast_manager& m) { reg_decl_plugins(m); } }; random_gen m_rand; ast_manager m; init m_init; bv_util bv; expr_ref_vector m_vars; smt_params m_smt_params; datalog::register_engine m_reg; datalog::context m_ctx; datalog::rel_context rc; udoc_plugin& p; datalog::check_relation_plugin& cr; tbit choose_tbit() { switch (m_rand(3)) { case 0: return BIT_0; case 1: return BIT_1; default : return BIT_x; } } tbv* mk_rand_tbv(doc_manager& dm) { tbv* result = dm.tbvm().allocate(); for (unsigned i = 0; i < dm.num_tbits(); ++i) { dm.tbvm().set(*result, i, choose_tbit()); } return result; } tbv* mk_rand_tbv(doc_manager& dm, tbv const& pos) { tbv* result = dm.tbvm().allocate(); for (unsigned i = 0; i < dm.num_tbits(); ++i) { if (pos[i] == BIT_x) { dm.tbvm().set(*result, i, choose_tbit()); } else { dm.tbvm().set(*result, i, pos[i]); } } return result; } doc* mk_rand_doc(doc_manager& dm, unsigned num_diff) { tbv_ref t(dm.tbvm()); doc_ref result(dm); t = mk_rand_tbv(dm); result = dm.allocate(*t); SASSERT(dm.tbvm().equals(*t, result->pos())); for (unsigned i = 0; i < num_diff; ++i) { t = mk_rand_tbv(dm, result->pos()); if (dm.tbvm().equals(*t, result->pos())) { return 0; } if (!result->neg().is_empty() && dm.tbvm().equals(*t, result->neg()[0])) { continue; } result->neg().push_back(t.detach()); } SASSERT(dm.well_formed(*result)); return result.detach(); } void mk_rand_udoc(doc_manager& dm, unsigned num_elems, unsigned num_diff, udoc& result) { result.reset(dm); for (unsigned i = 0; i < num_elems; ++i) { doc* d = mk_rand_doc(dm, num_diff); if (d) { result.push_back(d); } } } public: udoc_tester(): m_init(m), bv(m), m_vars(m), m_ctx(m, m_reg, m_smt_params), rc(m_ctx), p(dynamic_cast(*rc.get_rmanager().get_relation_plugin(symbol("doc")))), cr(dynamic_cast(*rc.get_rmanager().get_relation_plugin(symbol("check_relation")))) { cr.set_plugin(&p); } udoc_relation* mk_empty(relation_signature const& sig) { SASSERT(p.can_handle_signature(sig)); relation_base* empty = p.mk_empty(sig); return dynamic_cast(empty); } udoc_relation* mk_full(relation_signature const& sig) { func_decl_ref fn(m); fn = m.mk_func_decl(symbol("full"), sig.size(), sig.c_ptr(), m.mk_bool_sort()); relation_base* full = p.mk_full(fn, sig); return dynamic_cast(full); } void test1() { datalog::relation_signature sig; sig.push_back(bv.mk_sort(12)); sig.push_back(bv.mk_sort(6)); sig.push_back(bv.mk_sort(12)); datalog::relation_fact fact1(m), fact2(m), fact3(m); fact1.push_back(bv.mk_numeral(rational(1), 12)); fact1.push_back(bv.mk_numeral(rational(6), 6)); fact1.push_back(bv.mk_numeral(rational(56), 12)); fact2.push_back(bv.mk_numeral(rational(8), 12)); fact2.push_back(bv.mk_numeral(rational(16), 6)); fact2.push_back(bv.mk_numeral(rational(32), 12)); fact3.push_back(bv.mk_numeral(rational(32), 12)); fact3.push_back(bv.mk_numeral(rational(16), 6)); fact3.push_back(bv.mk_numeral(rational(4), 12)); relation_signature sig2; sig2.push_back(bv.mk_sort(3)); sig2.push_back(bv.mk_sort(6)); sig2.push_back(bv.mk_sort(3)); sig2.push_back(bv.mk_sort(3)); sig2.push_back(bv.mk_sort(3)); relation_base* t; udoc_relation* t1, *t2, *t3; expr_ref fml(m); test_filter_neg2(); test_join_project(); test_join_project2(); test_join_project3(); test_filter_neg4(false); test_filter_neg4(true); test_filter_neg5(false); test_filter_neg5(true); test_filter_neg(); test_filter_neg3(); test_join(1000); test_rename(); // empty { std::cout << "empty\n"; t = mk_empty(sig); t->display(std::cout); std::cout << "\n"; t->to_formula(fml); std::cout << fml << "\n"; t->deallocate(); } // full { std::cout << "full\n"; t = mk_full(sig); t->display(std::cout); std::cout << "\n"; t->to_formula(fml); std::cout << fml << "\n"; t->deallocate(); } // join { t1 = mk_full(sig); t2 = mk_full(sig); t3 = mk_empty(sig); unsigned_vector jc1, jc2; jc1.push_back(1); jc2.push_back(1); datalog::relation_join_fn* join_fn = p.mk_join_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr()); SASSERT(join_fn); t = (*join_fn)(*t1, *t2); cr.verify_join(*t1, *t2, *t, jc1, jc2); t->display(std::cout); std::cout << "\n"; t->deallocate(); t = (*join_fn)(*t1, *t3); cr.verify_join(*t1, *t3, *t, jc1, jc2); SASSERT(t->empty()); t->display(std::cout); std::cout << "\n"; t->deallocate(); t = (*join_fn)(*t3, *t3); cr.verify_join(*t3, *t3, *t, jc1, jc2); SASSERT(t->empty()); t->display(std::cout); std::cout << "\n"; t->deallocate(); dealloc(join_fn); t1->deallocate(); t2->deallocate(); t3->deallocate(); } // project { std::cout << "project\n"; t1 = mk_full(sig); unsigned_vector pc; pc.push_back(0); datalog::relation_transformer_fn* proj_fn = p.mk_project_fn(*t1, pc.size(), pc.c_ptr()); t = (*proj_fn)(*t1); cr.verify_project(*t1, *t, pc); t->display(std::cout); std::cout << "\n"; t->deallocate(); t1->reset(); t = (*proj_fn)(*t1); cr.verify_project(*t1, *t, pc); t->display(std::cout); std::cout << "\n"; t->deallocate(); t1->add_fact(fact1); t1->add_fact(fact2); t1->add_fact(fact3); t = (*proj_fn)(*t1); cr.verify_project(*t1, *t, pc); t1->display(std::cout); std::cout << "\n"; t->display(std::cout); std::cout << "\n"; t->deallocate(); dealloc(proj_fn); t1->deallocate(); } // union { t1 = mk_empty(sig); t2 = mk_empty(sig); udoc_relation* delta = mk_full(sig); t2->add_fact(fact1); t2->add_fact(fact2); t1->add_fact(fact3); expr_ref t10(m); t1->to_formula(t10); expr_ref delta0(m); delta->to_formula(delta0); rel_union union_fn = p.mk_union_fn(*t1, *t2, 0); t1->display(std::cout << "t1 before:"); std::cout << "\n"; (*union_fn)(*t1, *t2, delta); cr.verify_union(t10, *t2, *t1, delta0, delta); t1->display(std::cout << "t1 after:"); std::cout << "\n"; delta->display(std::cout << "delta:"); std::cout << "\n"; t1->deallocate(); t2->deallocate(); delta->deallocate(); } // filter_identical { t1 = mk_empty(sig2); unsigned_vector id; id.push_back(0); id.push_back(2); id.push_back(4); rel_mut filter_id = p.mk_filter_identical_fn(*t1, id.size(), id.c_ptr()); relation_fact f1(m); f1.push_back(bv.mk_numeral(rational(1),3)); f1.push_back(bv.mk_numeral(rational(1),6)); f1.push_back(bv.mk_numeral(rational(1),3)); f1.push_back(bv.mk_numeral(rational(1),3)); f1.push_back(bv.mk_numeral(rational(1),3)); t1->add_fact(f1); f1[4] = bv.mk_numeral(rational(2),3); t1->add_fact(f1); t1->display(std::cout); std::cout << "\n"; (*filter_id)(*t1); t1->display(std::cout); std::cout << "\n"; t1->deallocate(); } // tbv_manager::debug_alloc(); { relation_signature sig3; sig3.push_back(m.mk_bool_sort()); sig3.push_back(m.mk_bool_sort()); sig3.push_back(m.mk_bool_sort()); var_ref v0(m.mk_var(0, m.mk_bool_sort()),m); var_ref v1(m.mk_var(1, m.mk_bool_sort()),m); var_ref v2(m.mk_var(2, m.mk_bool_sort()),m); app_ref cond1(m); t1 = mk_full(sig3); cond1 = m.mk_eq(v0,v1); apply_filter(*t1, cond1); t1->deallocate(); } { relation_signature sig3; sig3.push_back(m.mk_bool_sort()); sig3.push_back(m.mk_bool_sort()); sig3.push_back(m.mk_bool_sort()); var_ref v0(m.mk_var(0, m.mk_bool_sort()),m); var_ref v1(m.mk_var(1, m.mk_bool_sort()),m); var_ref v2(m.mk_var(2, m.mk_bool_sort()),m); app_ref cond1(m); t1 = mk_full(sig3); cond1 = m.mk_or(m.mk_eq(v0,v1),m.mk_eq(v0,v2)); apply_filter(*t1, cond1); t1->deallocate(); } { relation_signature sig3; sig3.push_back(bv.mk_sort(1)); sig3.push_back(bv.mk_sort(1)); sig3.push_back(bv.mk_sort(1)); var_ref v0(m.mk_var(0, bv.mk_sort(1)),m); var_ref v1(m.mk_var(1, bv.mk_sort(1)),m); var_ref v2(m.mk_var(2, bv.mk_sort(1)),m); app_ref cond1(m); cond1 = m.mk_or(m.mk_eq(v0,v1),m.mk_eq(v0,v2)); t1 = mk_full(sig3); apply_filter(*t1, cond1); t1->deallocate(); } app_ref_vector conds(m); app_ref cond1(m); var_ref v0(m.mk_var(0, bv.mk_sort(3)),m); var_ref v1(m.mk_var(1, bv.mk_sort(6)),m); var_ref v2(m.mk_var(2, bv.mk_sort(3)),m); var_ref v3(m.mk_var(3, bv.mk_sort(3)),m); var_ref v4(m.mk_var(4, bv.mk_sort(3)),m); conds.push_back(m.mk_true()); conds.push_back(m.mk_false()); conds.push_back(m.mk_eq(v0, v2)); conds.push_back(m.mk_not(m.mk_eq(v0, v2))); conds.push_back(m.mk_eq(v0, bv.mk_numeral(rational(2), 3))); cond1 = m.mk_eq(ex(2,1,v0),bv.mk_numeral(rational(3),2)); conds.push_back(cond1); conds.push_back(m.mk_or(cond1,m.mk_eq(v3,v4))); conds.push_back(m.mk_eq(ex(2,1,v3),ex(1,0,v4))); conds.push_back(m.mk_or(cond1,m.mk_eq(ex(2,1,v3),ex(1,0,v4)))); conds.push_back(m.mk_or(m.mk_eq(v0,v2),m.mk_eq(v0,v4))); conds.push_back(m.mk_or(m.mk_eq(v0,v2),m.mk_eq(v3,v4))); conds.push_back(m.mk_or(m.mk_eq(ex(2,1,v0),ex(1,0,v2)),m.mk_eq(v3,v4))); conds.push_back(m.mk_or(m.mk_eq(ex(2,1,v0),bv.mk_numeral(rational(3),2)), m.mk_eq(v3,v4))); conds.push_back(m.mk_or(m.mk_eq(ex(2,1,v0),bv.mk_numeral(rational(3),2)), m.mk_eq(v3,bv.mk_numeral(rational(3),3)))); conds.push_back(m.mk_or(m.mk_eq(v0,bv.mk_numeral(rational(5),3)), m.mk_eq(v3,bv.mk_numeral(rational(5),3)))); conds.push_back(m.mk_or(m.mk_eq(v0,bv.mk_numeral(rational(7),3)), m.mk_eq(v3,bv.mk_numeral(rational(7),3)))); conds.push_back(m.mk_not(m.mk_or(m.mk_eq(v0,v2),m.mk_eq(v3,v4)))); // filter_interpreted { std::cout << "filter interpreted\n"; t1 = mk_full(sig2); for (unsigned i = 0; i < conds.size(); ++i) { apply_filter(*t1, conds[i].get()); } t1->deallocate(); } // filter_interpreted_project { unsigned_vector remove; remove.push_back(0); remove.push_back(2); t1 = mk_full(sig2); apply_filter(*t1, conds[2].get()); apply_filter_project(*t1, remove, conds[2].get()); apply_filter_project(*t1, remove, conds[3].get()); t1->deallocate(); t1 = mk_full(sig2); apply_filter(*t1, conds[3].get()); apply_filter_project(*t1, remove, conds[2].get()); apply_filter_project(*t1, remove, conds[3].get()); t1->deallocate(); for (unsigned i = 0; i < conds.size(); ++i) { t1 = mk_full(sig2); apply_filter_project(*t1, remove, conds[i].get()); t1->deallocate(); } remove[1] = 1; for (unsigned i = 0; i < conds.size(); ++i) { t1 = mk_full(sig2); apply_filter_project(*t1, remove, conds[i].get()); t1->deallocate(); } } } // {11xx \ {111x}, x011 \ {x011}, 0111} // {xx11 \ {0011, 1111, x111}} // 0111 // {1x1x \ {1x1x}, 1111 \ {1111}, x1x1 \ {x1x1}} void test_join_project() { datalog::relation_signature sig; sig.push_back(bv.mk_sort(2)); sig.push_back(bv.mk_sort(2)); //sig.push_back(bv.mk_sort(3)); unsigned_vector jc1, jc2, pc; jc1.push_back(0); jc2.push_back(0); pc.push_back(1); pc.push_back(3); //pc.push_back(4); udoc_relation* t1, *t2; relation_base* t; scoped_ptr join_project_fn; for (unsigned i = 0; i < 200; ++i) { t1 = mk_rand(sig); t2 = mk_rand(sig); t1->display(std::cout); t2->display(std::cout); join_project_fn = p.mk_join_project_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr(), pc.size(), pc.c_ptr()); t = (*join_project_fn)(*t1, *t2); t->display(std::cout); cr.verify_join_project(*t1, *t2, *t, jc1, jc2, pc); t->deallocate(); t1->deallocate(); t2->deallocate(); } } void test_join_project2() { relation_signature sig3; sig3.push_back(bv.mk_sort(1)); sig3.push_back(bv.mk_sort(1)); sig3.push_back(bv.mk_sort(1)); /// xxx \ x11 udoc_relation *t1 = mk_full(sig3); { udoc_relation *neg = mk_full(sig3); doc& n = neg->get_udoc()[0]; neg->get_dm().set(n, 1, BIT_1); neg->get_dm().set(n, 2, BIT_1); unsigned_vector allcols; allcols.push_back(0); allcols.push_back(1); allcols.push_back(2); apply_filter_neg(*t1, *neg, allcols, allcols); neg->deallocate(); } // 11x udoc_relation *t2 = mk_full(sig3); { doc& n = t2->get_udoc()[0]; t2->get_dm().set(n, 0, BIT_1); t2->get_dm().set(n, 1, BIT_1); } unsigned_vector jc1, jc2, pc; jc1.push_back(1); jc1.push_back(2); jc2.push_back(0); jc2.push_back(1); pc.push_back(1); pc.push_back(2); scoped_ptr join_project_fn; join_project_fn = p.mk_join_project_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr(), pc.size(), pc.c_ptr()); relation_base *t = (*join_project_fn)(*t1, *t2); cr.verify_join_project(*t1, *t2, *t, jc1, jc2, pc); t->deallocate(); t1->deallocate(); t2->deallocate(); } void test_join_project3() { datalog::relation_signature sig; sig.push_back(bv.mk_sort(2)); sig.push_back(bv.mk_sort(2)); unsigned_vector jc1, jc2, pc; jc1.push_back(0); jc1.push_back(1); jc2.push_back(1); jc2.push_back(0); pc.push_back(0); pc.push_back(1); scoped_ptr join_project_fn; udoc_relation* t1 = mk_empty(sig); { datalog::relation_fact fact1(m); fact1.push_back(bv.mk_numeral(rational(3), 2)); fact1.push_back(bv.mk_numeral(rational(1), 2)); t1->add_fact(fact1); } udoc_relation *t2 = mk_empty(sig); { datalog::relation_fact fact1(m); fact1.push_back(bv.mk_numeral(rational(0), 2)); fact1.push_back(bv.mk_numeral(rational(3), 2)); t2->add_fact(fact1); fact1.reset(); fact1.push_back(bv.mk_numeral(rational(1), 2)); fact1.push_back(bv.mk_numeral(rational(3), 2)); t2->add_fact(fact1); } t1->display(std::cout << "t1:"); t2->display(std::cout << "t2:"); join_project_fn = p.mk_join_project_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr(), pc.size(), pc.c_ptr()); relation_base* t; t = (*join_project_fn)(*t1, *t2); t->display(std::cout << "t:"); cr.verify_join_project(*t1, *t2, *t, jc1, jc2, pc); t->deallocate(); t1->deallocate(); t2->deallocate(); } void test_rename() { udoc_relation* t1; // rename datalog::relation_signature sig; sig.push_back(bv.mk_sort(12)); sig.push_back(bv.mk_sort(6)); sig.push_back(bv.mk_sort(2)); datalog::relation_fact fact1(m); fact1.push_back(bv.mk_numeral(rational(1), 12)); fact1.push_back(bv.mk_numeral(rational(6), 6)); fact1.push_back(bv.mk_numeral(rational(3), 2)); t1 = mk_empty(sig); t1->add_fact(fact1); unsigned_vector cycle; cycle.push_back(0); cycle.push_back(2); check_permutation(t1, cycle); sig.reset(); sig.push_back(bv.mk_sort(2)); sig.push_back(bv.mk_sort(6)); sig.push_back(bv.mk_sort(12)); fact1.reset(); fact1.push_back(bv.mk_numeral(rational(3), 2)); fact1.push_back(bv.mk_numeral(rational(6), 6)); fact1.push_back(bv.mk_numeral(rational(1), 12)); t1 = mk_empty(sig); t1->add_fact(fact1); cycle.reset(); cycle.push_back(0); cycle.push_back(2); check_permutation(t1, cycle); t1 = mk_empty(sig); t1->add_fact(fact1); cycle.reset(); cycle.push_back(0); cycle.push_back(1); cycle.push_back(2); check_permutation(t1, cycle); } void test_join(unsigned num_rounds) { for (unsigned i = 0; i < num_rounds; ++i) { test_join(); } } void test_join() { relation_signature sig; sig.push_back(bv.mk_sort(2)); sig.push_back(bv.mk_sort(3)); udoc_relation* t1, *t2; relation_base* t; t1 = mk_rand(sig); t2 = mk_rand(sig); unsigned_vector jc1, jc2; jc1.push_back(0); jc2.push_back(0); scoped_ptr join_fn; join_fn = p.mk_join_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr()); t = (*join_fn)(*t1, *t2); cr.verify_join(*t1, *t2, *t, jc1, jc2); t1->display(std::cout); t2->display(std::cout); t->display(std::cout); std::cout << "\n"; t1->deallocate(); t2->deallocate(); t->deallocate(); } udoc_relation* mk_rand(relation_signature const& sig) { udoc_relation* t = mk_empty(sig); mk_rand_udoc(t->get_dm(), 3, 3, t->get_udoc()); return t; } void check_permutation(relation_base* t1, unsigned_vector const& cycle) { scoped_ptr rename; rename = p.mk_rename_fn(*t1, cycle.size(), cycle.c_ptr()); relation_base* t = (*rename)(*t1); cr.verify_permutation(*t1,*t, cycle); t1->display(std::cout); std::cout << "\n"; t->display(std::cout); std::cout << "\n"; t->deallocate(); t1->deallocate(); } /* 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 test_filter_neg() { // filter_by_negation relation_signature sig4; sig4.push_back(bv.mk_sort(1)); sig4.push_back(bv.mk_sort(1)); sig4.push_back(bv.mk_sort(1)); udoc_relation* t1 = mk_empty(sig4); udoc_relation* t2 = mk_empty(sig4); unsigned_vector cols1, cols2; unsigned num_bits = t1->get_dm().num_tbits(); cols1.push_back(0); cols2.push_back(1); for (unsigned i = 0; i < 100; ++i) { set_random(*t1, 2*num_bits/3); set_random(*t2, 2*num_bits/3); apply_filter_neg(*t1,*t2, cols1, cols2); } cols1.push_back(1); cols2.push_back(2); for (unsigned i = 0; i < 200; ++i) { set_random(*t1, 2*num_bits/3); set_random(*t2, 2*num_bits/3); apply_filter_neg(*t1,*t2, cols1, cols2); } t1->deallocate(); t2->deallocate(); } void test_filter_neg2() { // filter_by_negation relation_signature sig4; sig4.push_back(bv.mk_sort(1)); sig4.push_back(bv.mk_sort(1)); sig4.push_back(bv.mk_sort(1)); unsigned_vector cols, allcols; cols.push_back(0); cols.push_back(2); allcols.push_back(0); allcols.push_back(1); allcols.push_back(2); /// xxx \ 1x0 udoc_relation* t1 = mk_full(sig4); { udoc_relation* neg = mk_full(sig4); doc& n = neg->get_udoc()[0]; neg->get_dm().set(n, 0, BIT_1); neg->get_dm().set(n, 2, BIT_0); apply_filter_neg(*t1, *neg, allcols, allcols); neg->deallocate(); } /// xxx \ (1x1 u 0x0) udoc_relation* t2 = mk_full(sig4); { udoc_relation* neg = mk_full(sig4); doc& n = neg->get_udoc()[0]; neg->get_dm().set(n, 0, BIT_0); neg->get_dm().set(n, 2, BIT_0); apply_filter_neg(*t2, *neg, allcols, allcols); neg->deallocate(); } { udoc_relation* neg = mk_full(sig4); doc& n = neg->get_udoc()[0]; neg->get_dm().set(n, 0, BIT_1); neg->get_dm().set(n, 2, BIT_1); apply_filter_neg(*t2, *neg, allcols, allcols); neg->deallocate(); } t1->display(std::cout); t2->display(std::cout); apply_filter_neg(*t2, *t1, cols, cols); t1->deallocate(); t2->deallocate(); } void test_filter_neg3() { // filter_by_negation relation_signature sig; sig.push_back(bv.mk_sort(1)); sig.push_back(bv.mk_sort(1)); sig.push_back(bv.mk_sort(1)); unsigned_vector cols1, cols2; cols1.push_back(0); cols1.push_back(0); cols2.push_back(0); cols2.push_back(1); /// 1xx udoc_relation* t1 = mk_full(sig); { doc& d = t1->get_udoc()[0]; t1->get_dm().set(d, 0, BIT_1); } /// 10x udoc_relation* t2 = mk_full(sig); { doc& d = t2->get_udoc()[0]; t1->get_dm().set(d, 0, BIT_1); t1->get_dm().set(d, 1, BIT_0); } apply_filter_neg(*t1, *t2, cols1, cols2); t1->deallocate(); t2->deallocate(); } void test_filter_neg4(bool disable_fast) { relation_signature sig1, sig2; sig1.push_back(bv.mk_sort(2)); sig1.push_back(bv.mk_sort(2)); sig2.push_back(bv.mk_sort(2)); unsigned_vector cols1, cols2; cols1.push_back(0); cols1.push_back(1); cols2.push_back(0); cols2.push_back(0); udoc_relation* tgt = mk_full(sig1); udoc_relation* neg = mk_full(sig2); if (disable_fast) p.disable_fast_pass(); apply_filter_neg(*tgt, *neg, cols1, cols2); tgt->deallocate(); tgt = mk_full(sig1); apply_filter_neg(*neg, *tgt, cols2, cols1); tgt->deallocate(); neg->deallocate(); } void test_filter_neg5(bool disable_fast) { relation_signature sig1, sig2; sig1.push_back(bv.mk_sort(2)); sig1.push_back(bv.mk_sort(2)); sig2.push_back(bv.mk_sort(2)); sig2.push_back(bv.mk_sort(2)); sig2.push_back(bv.mk_sort(2)); unsigned_vector cols1, cols2, cols3; cols1.push_back(0); cols1.push_back(1); cols2.push_back(0); cols2.push_back(2); cols3.push_back(0); cols3.push_back(1); udoc_relation* tgt = mk_full(sig1); udoc_relation* neg = mk_full(sig2); rel_mut filter_id = p.mk_filter_identical_fn(*tgt, cols3.size(), cols3.c_ptr()); (*filter_id)(*tgt); if (disable_fast) p.disable_fast_pass(); apply_filter_neg(*tgt, *neg, cols1, cols2); tgt->deallocate(); neg->deallocate(); } void set_random(udoc_relation& r, unsigned num_vals) { unsigned num_bits = r.get_dm().num_tbits(); udoc_relation* full = mk_full(r.get_signature()); rel_union union_fn = p.mk_union_fn(r, r, 0); (*union_fn)(r, *full); doc_manager& dm = r.get_dm(); SASSERT(r.get_udoc().size() == 1); doc& d0 = r.get_udoc()[0]; SASSERT(dm.is_full(d0)); for (unsigned i = 0; i < num_vals; ++i) { unsigned idx = m_rand(num_bits); unsigned val = m_rand(2); tbit b = (val == 0)?BIT_0:BIT_1; dm.set(d0, idx, b); } full->deallocate(); } void apply_filter_neg(udoc_relation& dst, udoc_relation& neg, unsigned_vector const& cols1, unsigned_vector const& cols2) { scoped_ptr negf; negf = p.mk_filter_by_negation_fn(dst, neg, cols1.size(), cols1.c_ptr(), cols2.c_ptr()); expr_ref dst0(m); dst.to_formula(dst0); (*negf)(dst, neg); cr.verify_filter_by_negation(dst0, dst, neg, cols1, cols2); /* tgt_1:={ x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } */ } expr_ref ex(unsigned hi, unsigned lo, expr* e) { expr_ref result(m); result = bv.mk_extract(hi, lo, e); return result; } void apply_filter_project(udoc_relation& t, unsigned_vector const& rm, app* cond) { scoped_ptr rt; rt = p.mk_filter_interpreted_and_project_fn(t, cond, rm.size(), rm.c_ptr()); datalog::relation_base* result = (*rt)(t); cr.verify_filter_project(t, *result, cond, rm); result->deallocate(); } void project_var(unsigned i, sort* s, expr_ref& fml) { var_ref v(m); v = m.mk_var(i, s); unsigned num_bits = bv.get_bv_size(s); unsigned p = 1 << num_bits; expr_ref_vector disj(m); expr_ref tmp(m); for (unsigned i = 0; i < p; ++i) { expr_safe_replace repl(m); repl.insert(v, bv.mk_numeral(rational(i), s)); tmp = fml; repl(tmp); disj.push_back(tmp); } fml = mk_or(m, disj.size(), disj.c_ptr()); } void apply_filter(udoc_relation& t, app* cond) { udoc_relation* full = mk_full(t.get_signature()); rel_union union_fn = p.mk_union_fn(t, *full, 0); (*union_fn)(t, *full, 0); expr_ref fml0(m); t.to_formula(fml0); rel_mut fint = p.mk_filter_interpreted_fn(t, cond); (*fint)(t); t.display(std::cout << "filter: " << mk_pp(cond, m) << " "); std::cout << "\n"; cr.verify_filter(fml0, t, cond); full->deallocate(); } }; void tst_udoc_relation() { udoc_tester tester; try { tester.test1(); } catch (z3_exception& ex) { std::cout << ex.msg() << "\n"; } } z3-z3-4.4.1/src/test/uint_set.cpp000066400000000000000000000063311260446376700165340ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: uint_set.cpp Abstract: Test sets of unsigned integers. Author: Leonardo de Moura (leonardo) 2006-12-07. Revision History: --*/ #include"uint_set.h" #include"vector.h" static void tst1(unsigned n) { uint_set s1; svector s2(n, false); unsigned size = 0; unsigned num_op = rand()%1000; for (unsigned i = 0; i < num_op; i++) { unsigned op = rand()%3; if (op < 2) { unsigned idx = rand() % n; if (!s2[idx]) { size++; } s2[idx] = true; s1.insert(idx); } else { unsigned idx = rand() % n; if (s2[idx]) { size--; } s2[idx] = false; s1.remove(idx); } SASSERT(s1.num_elems() == size); SASSERT((size == 0) == s1.empty()); for (unsigned idx = 0; idx < n; idx++) { SASSERT(s2[idx] == s1.contains(idx)); } } } static void tst2(unsigned n) { uint_set s; SASSERT(s.empty()); unsigned val = rand()%n; s.insert(val); SASSERT(!s.empty()); SASSERT(s.num_elems() == 1); for (unsigned i = 0; i < 100; i++) { unsigned val2 = rand()%n; if (val != val2) { SASSERT(!s.contains(val2)); } } s.remove(val); SASSERT(s.num_elems() == 0); SASSERT(s.empty()); } static void tst3(unsigned n) { SASSERT(n > 10); uint_set s1; uint_set s2; SASSERT(s1 == s2); s1.insert(3); SASSERT(s1.num_elems() == 1); SASSERT(s2.num_elems() == 0); SASSERT(s1 != s2); s2.insert(5); SASSERT(s2.num_elems() == 1); SASSERT(s1 != s2); SASSERT(!s1.subset_of(s2)); s2 |= s1; SASSERT(s1.subset_of(s2)); SASSERT(s2.num_elems() == 2); SASSERT(s1 != s2); s1 |= s2; SASSERT(s1.subset_of(s2)); SASSERT(s2.subset_of(s1)); SASSERT(s1.num_elems() == 2); SASSERT(s2.num_elems() == 2); SASSERT(s1 == s2); s1.insert(9); SASSERT(s1.num_elems() == 3); SASSERT(s2.num_elems() == 2); s1.insert(9); SASSERT(s1.num_elems() == 3); SASSERT(s2.num_elems() == 2); SASSERT(s2.subset_of(s1)); SASSERT(!s1.subset_of(s2)); SASSERT(s1 != s2); uint_set s3(s1); SASSERT(s1 == s3); SASSERT(s1.subset_of(s3)); SASSERT(s3.subset_of(s1)); SASSERT(s2 != s3); uint_set s4(s2); SASSERT(s2 == s4); SASSERT(s2.subset_of(s4)); SASSERT(s4.subset_of(s2)); SASSERT(s2 != s3); for (unsigned i = 0; i < n; i++) { uint_set s5; s5.insert(i); SASSERT(s1.contains(i) == s5.subset_of(s1)); } } static void tst4() { uint_set s; s.insert(32); SASSERT(s.contains(32)); SASSERT(!s.contains(31)); SASSERT(!s.contains(0)); s.remove(32); SASSERT(!s.contains(32)); SASSERT(!s.contains(31)); SASSERT(!s.contains(0)); } void tst_uint_set() { for (unsigned i = 0; i < 100; i++) { tst1(1 + rand()%31); tst1(1 + rand()%100); } for (unsigned i = 0; i < 1000; i++) { tst2(1); tst2(10); tst2(31); tst2(32); tst2(100); } tst3(12); tst3(100); tst4(); } z3-z3-4.4.1/src/test/upolynomial.cpp000066400000000000000000001231421260446376700172520ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: upolynomial.cpp Abstract: Goodies for creating and handling univariate polynomials. Author: Leonardo (leonardo) 2011-12-01 Notes: --*/ #include"upolynomial.h" #include"timeit.h" static void tst1() { polynomial::numeral_manager nm; polynomial::manager m(nm); upolynomial::manager um(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x - 1)*(x + 1)*(x + 2)*(x + 3)*(x - 3); std::cout << "p: " << p << "\n"; // convert p into an univariate polynomial upolynomial::scoped_numeral_vector q(um); um.to_numeral_vector(p, q); std::cout << "q: "; um.display(std::cout, q); std::cout << "\n"; std::cout << "degree(q): " << um.degree(q) << "\n"; // display coefficients of q std::cout << "expanded q: "; for (unsigned i = 0; i < q.size(); i++) std::cout << nm.to_string(q[i]) << " "; std::cout << "\n"; // traverse coefficients of q adding 1 for (unsigned i = 0; i < q.size(); i++) { nm.add(q[i], mpz(1), q[i]); } // All operations in upolynomial::manager assume the leading coefficient of q is not zero. // So, if we perform destructive operations on these coefficients, we must execute the "trim" operation // before invoking another operation of upolynomial::manager um.trim(q); // q after adding 1 to all coefficients std::cout << "new q: "; um.display(std::cout, q); std::cout << "\n"; // q^2 um.mul(q.size(), q.c_ptr(), q.size(), q.c_ptr(), q); std::cout << "new q^2: "; um.display(std::cout, q); std::cout << "\n"; // using pw for (q^2)^3 um.pw(q.size(), q.c_ptr(), 3, q); std::cout << "new (q^2)^3: "; um.display(std::cout, q); std::cout << "\n"; } static void tst_isolate_roots(polynomial_ref const & p, unsigned prec, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { upolynomial::manager um(p.m().m()); upolynomial::scoped_numeral_vector q(um); um.to_numeral_vector(p, q); std::cout << "isolating roots of: "; um.display(std::cout, q); std::cout << "\n"; { timeit timer(true, "isolate time"); um.isolate_roots(q.size(), q.c_ptr(), bqm, roots, lowers, uppers); } upolynomial::scoped_upolynomial_sequence sseq(um), fseq(um); { timeit timer(true, "sturm time"); um.sturm_seq(q.size(), q.c_ptr(), sseq); // um.display(std::cout, sseq); std::cout << "\n"; } // Fourier sequence is sensitive to repeated roots... we remove them by taking the square free component. upolynomial::scoped_numeral_vector q_sqf(um); { timeit timer(true, "sqf time"); um.square_free(q.size(), q.c_ptr(), q_sqf); std::cout << "square free part: "; um.display(std::cout, q_sqf); std::cout << "\n"; } { timeit timer(true, "fourier time"); um.fourier_seq(q_sqf.size(), q_sqf.c_ptr(), fseq); } // um.display(std::cout, fseq); std::cout << "num. roots: " << roots.size() + lowers.size() << "\n"; std::cout << "sign var(-oo): " << um.sign_variations_at_minus_inf(sseq) << "\n"; std::cout << "sign var(+oo): " << um.sign_variations_at_plus_inf(sseq) << "\n"; SASSERT(roots.size() + lowers.size() == um.sign_variations_at_minus_inf(sseq) - um.sign_variations_at_plus_inf(sseq)); std::cout << "roots:"; for (unsigned i = 0; i < roots.size(); i++) { SASSERT(um.eval_sign_at(q.size(), q.c_ptr(), roots[i]) == 0); std::cout << " "; bqm.display_decimal(std::cout, roots[i], prec); } { timeit timer(true, "interval check"); std::cout << "\n"; std::cout << "intervals:"; for (unsigned i = 0; i < lowers.size(); i++) { std::cout << " ("; bqm.display_decimal(std::cout, lowers[i], prec); std::cout << ", "; bqm.display_decimal(std::cout, uppers[i], prec); std::cout << ")"; // Check interval with Sturm sequence. Small detail: Sturm sequence is for close intervals. SASSERT(um.eval_sign_at(q.size(), q.c_ptr(), lowers[i]) == 0 || um.eval_sign_at(q.size(), q.c_ptr(), uppers[i]) == 0 || um.sign_variations_at(sseq, lowers[i]) - um.sign_variations_at(sseq, uppers[i]) == 1); // Fourier sequence may also be used to check if the interval is isolating TRACE("upolynomial", tout << "lowers[i]: " << bqm.to_string(lowers[i]) << "\n"; tout << "uppers[i]: " << bqm.to_string(uppers[i]) << "\n"; tout << "fourier lower: " << um.sign_variations_at(fseq, lowers[i]) << "\n"; tout << "fourier upper: " << um.sign_variations_at(fseq, uppers[i]) << "\n";); unsigned fsv_lower = um.sign_variations_at(fseq, lowers[i]); unsigned fsv_upper = um.sign_variations_at(fseq, uppers[i]); SASSERT(um.eval_sign_at(q.size(), q.c_ptr(), lowers[i]) == 0 || um.eval_sign_at(q.size(), q.c_ptr(), uppers[i]) == 0 || // fsv_lower - fsv_upper is an upper bound for the number of roots in the interval // fsv_upper - fsv_upper - num_roots is even // Recall that num_roots == 1 in the interval. (fsv_lower - fsv_upper >= 1 && (fsv_lower - fsv_upper - 1) % 2 == 0)); // Double checking using Descartes bounds for the interval // Must use square free component. unsigned dab = um.descartes_bound_a_b(q_sqf.size(), q_sqf.c_ptr(), bqm, lowers[i], uppers[i]); TRACE("upolynomial", tout << "Descartes bound: " << dab << "\n";); SASSERT(dab == 1); } } std::cout << "\n"; } static void tst_isolate_roots(polynomial_ref const & p, unsigned prec = 5) { mpbq_manager bqm(p.m().m()); scoped_mpbq_vector roots(bqm); scoped_mpbq_vector lowers(bqm); scoped_mpbq_vector uppers(bqm); tst_isolate_roots(p, prec, bqm, roots, lowers, uppers); } static void check_roots(mpbq_vector const & roots, mpbq_vector const & lowers, mpbq_vector const & uppers, unsigned expected_sz, rational const * expected_roots) { SASSERT(expected_sz == roots.size() + lowers.size()); svector visited; visited.resize(expected_sz, false); for (unsigned i = 0; i < expected_sz; i++) { rational const & r = expected_roots[i]; bool found = false; for (unsigned j = 0; j < roots.size(); j++) { if (to_rational(roots[j]) == r) { SASSERT(!visited[j]); SASSERT(!found); found = true; visited[j] = true; } } for (unsigned j = 0; j < lowers.size(); j++) { unsigned j_prime = j + roots.size(); if (to_rational(lowers[j]) < r && r < to_rational(uppers[j])) { SASSERT(!found); SASSERT(!visited[j_prime]); found = true; visited[j_prime] = true; } } SASSERT(found); } } static void tst_isolate_roots(polynomial_ref const & p, unsigned expected_sz, rational const * expected_roots, unsigned prec = 5) { mpbq_manager bqm(p.m().m()); scoped_mpbq_vector roots(bqm); scoped_mpbq_vector lowers(bqm); scoped_mpbq_vector uppers(bqm); tst_isolate_roots(p, prec, bqm, roots, lowers, uppers); check_roots(roots, lowers, uppers, expected_sz, expected_roots); } static void tst_isolate_roots() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x-1)*(x-2); { rational ex[2] = { rational(1), rational(2) }; tst_isolate_roots(p, 2, ex); } p = (x-1)*(x-1)*x*x*x; { rational ex[2] = { rational(1), rational(0) }; tst_isolate_roots(p, 2, ex); } p = (x^5) - x - 1; { rational ex[1] = { rational(11673039, 10000000) }; // approximated root tst_isolate_roots(p, 1, ex); } p = (x - 1)*(x + 1)*(x + 2)*(x + 3)*((x - 3)^2); { rational ex[5] = { rational(1), rational(-1), rational(-2), rational(-3), rational(3) }; tst_isolate_roots(p, 5, ex); } p = (10000*x - 31)*(10000*x - 32); { rational ex[2] = { rational(31, 10000), rational(32, 10000) }; tst_isolate_roots(p, 2, ex, 10); } p = (10000*x - 31)*(10000*x - 32)*(10000*x - 33); { rational ex[3] = { rational(31, 10000), rational(32, 10000), rational(33, 10000) }; tst_isolate_roots(p, 3, ex, 10); } p = ((x^5) - x - 1)*((x^5) + x - 1)*(1000*x - 1167); { rational ex[3] = { rational(11673039, 10000000), // approximated rational(75487766, 100000000), // approximated rational(1167, 1000) }; tst_isolate_roots(p, 3, ex, 10); } p = (x - 2)*(x - 4)*(x - 8)*(x - 16)*(x - 32)*(x - 64)*(2*x - 1)*(4*x - 1)*(8*x - 1)*(16*x - 1)*(32*x - 1); { rational ex[11] = { rational(2), rational(4), rational(8), rational(16), rational(32), rational(64), rational(1, 2), rational(1, 4), rational(1, 8), rational(1, 16), rational(1, 32) }; tst_isolate_roots(p, 11, ex, 10); } p = ((x^5) - x - 1)*((x^5) + x - 1)*(1000*x - 1167)*((x^5) - x - 1)*((x^5) + x - 1)*(1000*x - 1167); { rational ex[3] = { rational(11673039, 10000000), // approximated rational(75487766, 100000000), // approximated rational(1167, 1000) }; tst_isolate_roots(p, 3, ex, 10); } p = (x^17) + 5*(x^16) + 3*(x^15) + 10*(x^13) + 13*(x^10) + (x^9) + 8*(x^5) + 3*(x^2) + 7; { rational ex[3] = { rational(-413582, 100000), // approximated rational(-170309, 100000), // approximated rational(-109968, 100000), // approximated }; tst_isolate_roots(p, 3, ex, 10); } p = ((x^17) + 5*(x^16) + 3*(x^15) + 10*(x^13) + 13*(x^10) + (x^9) + 8*(x^5) + 3*(x^2) + 7)*(((x^5) - x - 1)^2)*(((x^3) - 2)^2); { rational ex[5] = { rational(-413582, 100000), // approximated rational(-170309, 100000), // approximated rational(-109968, 100000), // approximated rational(11673039, 10000000), // approximated rational(125992, 100000) // approximated }; tst_isolate_roots(p, 5, ex, 10); } p = (((x^5) - 1000000000)^3)*((3*x - 10000000)^2)*((10*x - 632)^2); { rational ex[3] = { rational(630957, 10000), // approximated rational(10000000, 3), rational(632, 10) }; tst_isolate_roots(p, 3, ex, 10); } } static void tst_remove_one_half() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m), r(m); p = 4*(x^3) - 12*(x^2) - x + 3; r = 16*(x^2) - 40*x - 24; upolynomial::manager um(nm); upolynomial::scoped_numeral_vector _p(um), _q(um), _r(um); um.to_numeral_vector(p, _p); um.to_numeral_vector(r, _r); SASSERT(um.has_one_half_root(_p.size(), _p.c_ptr())); um.remove_one_half_root(_p.size(), _p.c_ptr(), _q); SASSERT(!um.has_one_half_root(_q.size(), _q.c_ptr())); std::cout << "_p: "; um.display(std::cout, _p); std::cout << "\n"; std::cout << "_r: "; um.display(std::cout, _r); std::cout << "\n"; std::cout << "_q: "; um.display(std::cout, _q); std::cout << "\n"; SASSERT(um.eq(_q, _r)); p = (((x^5) - 1000000000)^3)*((3*x - 10000000)^2)*((10*x - 632)^2); um.to_numeral_vector(p, _p); SASSERT(!um.has_one_half_root(_p.size(), _p.c_ptr())); p = (x - 2)*(x - 4)*(x - 8)*(x - 16)*(x - 32)*(x - 64)*(2*x - 1)*(4*x - 1)*(8*x - 1)*(16*x - 1)*(32*x - 1); um.to_numeral_vector(p, _p); SASSERT(um.has_one_half_root(_p.size(), _p.c_ptr())); } template static void tst_gcd(polynomial_ref const & p, polynomial_ref const & q, pmanager & um) { typename pmanager::scoped_numeral_vector _p(um.m()), _q(um.m()), _r(um.m()); um.to_numeral_vector(p, _p); um.to_numeral_vector(q, _q); std::cout << "_p: "; um.display(std::cout, _p); std::cout << "\n"; std::cout << "_q: "; um.display(std::cout, _q); std::cout << std::endl; um.gcd(_p.size(), _p.c_ptr(), _q.size(), _q.c_ptr(), _r); std::cout << "gcd: "; um.display(std::cout, _r); std::cout << "\n"; um.subresultant_gcd(_p.size(), _p.c_ptr(), _q.size(), _q.c_ptr(), _r); std::cout << "_p: "; um.display(std::cout, _p); std::cout << "\n"; std::cout << "_q: "; um.display(std::cout, _q); std::cout << "\n"; std::cout << "subresultant_gcd: "; um.display(std::cout, _r); std::cout << "\n"; } static void tst_gcd() { std::cout << "\n\nTesting GCD\n"; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); polynomial_ref q(m); upolynomial::manager um(nm); p = 13*((x - 3)^6)*((x - 5)^5)*((x - 11)^7); q = derivative(p, 0); tst_gcd(p, q, um); return; p = (x^8) + (x^6) - 3*(x^4) - 3*(x^3) + 8*(x^2) + 2*x - 5; q = 3*(x^6) + 5*(x^4) - 4*(x^2) - 9*x + 21; tst_gcd(p, q, um); p = ((x - 1)^2)*(x - 3)*(x + 2)*((x - 5)^3); q = (x + 1)*(x-1)*((x-3)^2)*(x + 3)*(x - 5); tst_gcd(p, q, um); std::cout << "expected: " << ((x - 1)*(x-3)*(x-5)) << "\n"; } static void tst_zp() { std::cout << "\n\nTesting Z_p\n"; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); polynomial_ref q(m); p = (x^4) + 2*(x^3) + 2*(x^2) + x; q = (x^3) + x + 1; // Computing GCD of p an q in Z[x] std::cout << "GCD in Z[x]\n"; upolynomial::manager um(nm); tst_gcd(p, q, um); // Computing GCD of p an q in Z_3[x] std::cout << "GCD in Z_3[x]\n"; upolynomial::zp_manager um3(nm); um3.set_zp(3); tst_gcd(p, q, um3); } static void tst_zp2() { std::cout << "\n\nTesting Z_p\n"; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref u(m); polynomial_ref v(m); v = (x^6) + 5*(x^5) + 9*(x^4) + 5*(x^2) + 5*x; u = (x^8) + (x^6) + 10*(x^4) + 10*(x^3) + 8*(x^2) + 2*x + 8; // Computing GCD of p an q in Z[x] std::cout << "GCD in Z[x]\n"; upolynomial::manager um(nm); tst_gcd(u, v, um); // Computing GCD of p an q in Z_3[x] std::cout << "GCD in Z_13[x]\n"; upolynomial::zp_manager um13(nm); um13.set_zp(13); tst_gcd(u, v, um13); } static void tst_ext_gcd() { std::cout << "\nExtended GCD\n"; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref a(m); polynomial_ref b(m); a = (x^6) + 5*(x^5) + 9*(x^4) + 5*(x^2) + 5*x; b = (x^8) + (x^6) + 10*(x^4) + 10*(x^3) + 8*(x^2) + 2*x + 8; // Computing GCD of p an q in Z_3[x] std::cout << "GCD in Z_13[x]\n"; upolynomial::zp_manager um(nm); um.set_zp(13); mpzzp_manager & z13 = um.m(); upolynomial::zp_manager::scoped_numeral_vector A(z13), B(z13), U(z13), V(z13), D(z13); um.to_numeral_vector(a, A); um.to_numeral_vector(b, B); um.ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D); std::cout << "A: "; um.display(std::cout, A); std::cout << "\n"; std::cout << "B: "; um.display(std::cout, B); std::cout << "\n"; std::cout << "U: "; um.display(std::cout, U); std::cout << "\n"; std::cout << "V: "; um.display(std::cout, V); std::cout << "\n"; std::cout << "D: "; um.display(std::cout, D); std::cout << "\n"; } static void tst_ext_gcd_z7() { std::cout << "\nExtended GCD in Z_7\n"; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref a(m); polynomial_ref b(m); a = (x^3) + 2; b = 6*(x^2) + 6; // Computing GCD of a and b in Z_3[x] // expecting: D = 1, U = 3*x + 6, V = 3*x^2 + 6*x + 4 std::cout << "GCD in Z_7[x]\n"; upolynomial::zp_manager um(nm); um.set_zp(7); mpzzp_manager & z7 = um.m(); upolynomial::zp_manager::scoped_numeral_vector A(z7), B(z7), U(z7), V(z7), D(z7); um.to_numeral_vector(a, A); um.to_numeral_vector(b, B); um.ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D); std::cout << "A: "; um.display(std::cout, A); std::cout << "\n"; std::cout << "B: "; um.display(std::cout, B); std::cout << "\n"; std::cout << "U: "; um.display(std::cout, U); std::cout << "\n"; std::cout << "V: "; um.display(std::cout, V); std::cout << "\n"; std::cout << "D: "; um.display(std::cout, D); std::cout << "\n"; } static void tst_sturm() { std::cout << "\nSturm Seq\n"; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = 7*(x^10) + 3*(x^9) + (x^8) + (x^6) + 10*(x^4) + 10*(x^3) + 8*(x^2) + 2*x + 8; // p = ((x^17) + 5*(x^16) + 3*(x^15) + 10*(x^13) + 13*(x^10) + (x^9) + 8*(x^5) + 3*(x^2) + 7)*(((x^5) - x - 1)^2)*(((x^3) - 2)^2); // p = ((x^17) + 5*(x^16) + 3*(x^15) + 10*(x^13) + 13*(x^10) + (x^9) + 8*(x^5) + 3*(x^2) + 7)*(((x^5) - x - 1))*(((x^3) - 2)); upolynomial::manager um(nm); upolynomial::scoped_numeral_vector _p(um); upolynomial::scoped_upolynomial_sequence seq2(um); um.to_numeral_vector(p, _p); um.sturm_seq(_p.size(), _p.c_ptr(), seq2); std::cout << "upolynomial sturm seq...\n"; um.display(std::cout, seq2); } static void tst_refinable(polynomial_ref const & p, mpbq_manager & bqm, mpbq & a, mpbq & b) { upolynomial::manager um(p.m().m()); upolynomial::scoped_numeral_vector _p(um); um.to_numeral_vector(p, _p); std::cout << "before (" << bqm.to_string(a) << ", " << bqm.to_string(b) << ")\n"; bool r = um.isolating2refinable(_p.size(), _p.c_ptr(), bqm, a, b); if (r) { std::cout << "new (" << bqm.to_string(a) << ", " << bqm.to_string(b) << ")\n"; int sign_a = um.eval_sign_at(_p.size(), _p.c_ptr(), a); int sign_b = um.eval_sign_at(_p.size(), _p.c_ptr(), b); SASSERT(sign_a != 0 && sign_b != 0 && sign_a == -sign_b); } else { std::cout << "new root: " << bqm.to_string(a) << "\n"; SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), a) == 0); } } static void tst_refinable() { std::cout << "\nRefinable intervals\n"; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x - 1)*(4*x - 11)*(x - 3); std::cout << "p: " << p << "\n"; mpbq_manager bqm(nm); mpbq a(1); mpbq b(3); tst_refinable(p, bqm, a, b); bqm.set(a, 2); bqm.set(b, 3); tst_refinable(p, bqm, a, b); bqm.set(a, 5, 1); bqm.set(b, 3); tst_refinable(p, bqm, a, b); p = (x - 1)*(5*x - 11)*(x - 3); std::cout << "p: " << p << "\n"; bqm.set(a, 1); bqm.set(b, 3); tst_refinable(p, bqm, a, b); bqm.set(a, 2); bqm.set(b, 3); tst_refinable(p, bqm, a, b); bqm.set(a, 3, 1); bqm.set(b, 3); tst_refinable(p, bqm, a, b); bqm.set(a, 1); bqm.set(b, 5, 1); tst_refinable(p, bqm, a, b); bqm.set(a, 3, 1); bqm.set(b, 5, 1); tst_refinable(p, bqm, a, b); p = (x - 1)*(x - 2)*(x - 3); std::cout << "p: " << p << "\n"; bqm.set(a, 1); bqm.set(b, 3); tst_refinable(p, bqm, a, b); bqm.del(a); bqm.del(b); } static void tst_refine(polynomial_ref const & p, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k=100) { upolynomial::manager um(p.m().m()); upolynomial::scoped_numeral_vector _p(um); um.to_numeral_vector(p, _p); std::cout << "before (" << bqm.to_string(a) << ", " << bqm.to_string(b) << ")\n"; bool r = um.refine(_p.size(), _p.c_ptr(), bqm, a, b, prec_k); if (r) { std::cout << "new (" << bqm.to_string(a) << ", " << bqm.to_string(b) << ")\n"; std::cout << "as decimal: "; bqm.display_decimal(std::cout, a, prec_k); std::cout << "\n"; } else { std::cout << "new root: " << bqm.to_string(a) << "\n"; std::cout << "as decimal: "; bqm.display_decimal(std::cout, a, prec_k); std::cout << "\n"; } } static void tst_refine() { std::cout << "\nRefining intervals\n"; polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x^5) - x - 1; std::cout << "p: " << p << "\n"; mpbq_manager bqm(nm); scoped_mpbq a(bqm), b(bqm); a = 1; b = 2; tst_refine(p, bqm, a, b, 20); p = (x^2) - 2; std::cout << "p: " << p << "\n"; a = 1; b = 2; tst_refine(p, bqm, a, b, 200); } static void tst_translate_q() { unsynch_mpq_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x-1)*(x-2)*(x-3)*(x-4); upolynomial::manager um(nm); upolynomial::scoped_numeral_vector _p(um), _q(um); um.to_numeral_vector(p, _p); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(1)) == 0); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(2)) == 0); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(3)) == 0); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(4)) == 0); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(-1)) != 0); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(5)) != 0); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(-2)) != 0); scoped_mpq c(nm); nm.set(c, 1, 3); scoped_mpq r1(nm); r1 = 1; r1 -= c; scoped_mpq r2(nm); r2 = 3; r2 -= c; SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), r1) != 0); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), r2) != 0); std::cout << "p: "; um.display(std::cout, _p); std::cout << "\n"; um.translate_q(_p.size(), _p.c_ptr(), c, _q); std::cout << "q: "; um.display(std::cout, _q); std::cout << "\n"; SASSERT(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(1)) != 0); SASSERT(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(2)) != 0); SASSERT(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(3)) != 0); SASSERT(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(4)) != 0); SASSERT(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(-1)) != 0); SASSERT(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(5)) != 0); SASSERT(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(-2)) != 0); SASSERT(um.eval_sign_at(_q.size(), _q.c_ptr(), r1) == 0); SASSERT(um.eval_sign_at(_q.size(), _q.c_ptr(), r2) == 0); um.p_1_div_x(_p.size(), _p.c_ptr()); std::cout << "p: "; um.display(std::cout, _p); std::cout << "\n"; SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(1)) == 0); nm.set(c, 1, 2); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), c) == 0); nm.set(c, 1, 3); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), c) == 0); nm.set(c, 1, 4); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), c) == 0); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(2)) != 0); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(3)) != 0); SASSERT(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(4)) != 0); } static void tst_convert_q2bq(unsynch_mpq_manager & m, polynomial_ref const & p, mpq const & a, mpq const & b) { upolynomial::manager um(m); upolynomial::scoped_numeral_vector _p(um); um.to_numeral_vector(p, _p); std::cout << "\np: "; um.display(std::cout, _p); std::cout << "\n"; std::cout << "before (" << m.to_string(a) << ", " << m.to_string(b) << ") ("; m.display_decimal(std::cout, a, 10); std::cout << ", "; m.display_decimal(std::cout, b, 10); std::cout << ")\n"; mpbq_manager bqm(m); scoped_mpbq c(bqm); scoped_mpbq d(bqm); if (!um.convert_q2bq_interval(_p.size(), _p.c_ptr(), a, b, bqm, c, d)) { std::cout << "found root: " << c << "\n"; } else { std::cout << "after (" << c << ", " << d << ")" << " ("; bqm.display_decimal(std::cout, c, 10); std::cout << ", "; bqm.display_decimal(std::cout, d, 10); std::cout << ")\n"; } } static void tst_convert_q2bq() { unsynch_mpq_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x-1)*(x-2)*(x-3)*(x-4); scoped_mpq a(nm), b(nm); nm.set(a, 1, 3); nm.set(b, 7, 5); tst_convert_q2bq(nm, p, a, b); nm.set(a, 1, 2); nm.set(b, 7, 5); tst_convert_q2bq(nm, p, a, b); nm.set(a, 3, 7); nm.set(b, 3, 2); tst_convert_q2bq(nm, p, a, b); nm.set(a, 0); nm.set(b, 3, 2); tst_convert_q2bq(nm, p, a, b); nm.set(a, 0); nm.set(b, 23, 21); tst_convert_q2bq(nm, p, a, b); nm.set(a, 7, 2); nm.set(b, 5); tst_convert_q2bq(nm, p, a, b); nm.set(a, 999, 1000); nm.set(b, 1001, 1000); tst_convert_q2bq(nm, p, a, b); nm.set(a, 9999, 10000); nm.set(b, 10001, 10000); tst_convert_q2bq(nm, p, a, b); nm.set(a, 39999, 10000); nm.set(b, 40001, 10000); tst_convert_q2bq(nm, p, a, b); } static void tst_sturm2() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); polynomial_ref q(m); p = (x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225; q = ((x^8) - 40*(x^6) + 352*(x^4) - 960*(x^2) + 576)^2; upolynomial::manager um(nm); upolynomial::scoped_numeral_vector _p(um), _q(um); upolynomial::scoped_upolynomial_sequence seq2(um); um.to_numeral_vector(p, _p); um.to_numeral_vector(q, _q); um.sturm_tarski_seq(_p.size(), _p.c_ptr(), _q.size(), _q.c_ptr(), seq2); std::cout << "upolynomial sturm seq...\n"; um.display(std::cout, seq2); } #if 0 static void tst_isolate_roots2() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (2*x - 1)*(x - 21)*(x + 12)*(x - 19)*(x + 11)*(x + 34)*(x - 9)*(x - 72)*(10000*x - 4999)*((x^5) - x - 1)*((x^2) - 2)*((x^2) - 3)*((x^7) - 3)*((x^101) - 3); { tst_isolate_roots(p, 10); } } static void tst_isolate_roots3() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x1(m), x2(m), x3(m), x4(m), x5(m), x6(m), x(m); x = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); x6 = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p1(m); polynomial_ref p2(m); polynomial_ref p3(m); polynomial_ref p4(m); polynomial_ref p5(m); polynomial_ref p6(m); polynomial_ref q(m); polynomial_ref r(m); p1 = ((x1^2) - 2); p2 = ((x2^2) - 3); p3 = ((x3^2) - 5); p4 = ((x4^2) - 7); p5 = ((x5^2) - 11); p6 = ((x6^2) - 13); q = (x - x1 - x2 - x3 - x4 - x5 - x6); r = resultant(resultant(resultant(resultant(resultant(resultant(q, p1, 1), p2, 2), p3, 3), p4, 4), p5, 5), p6, 6); std::cout << "r: " << r << "\n"; { timeit timer(true, "isolate"); tst_isolate_roots(r, 10); } } static void tst_gcd2() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = ((x^1000) - x + 1)^5; upolynomial::manager um(nm); upolynomial::scoped_numeral_vector _p(um); upolynomial::scoped_numeral_vector _p_sqf(um); um.to_numeral_vector(p, _p); { timeit timer(true, "gcd"); um.square_free(_p.size(), _p.c_ptr(), _p_sqf); } um.display(std::cout, _p_sqf.size(), _p_sqf.c_ptr()); std::cout << "\n"; } #endif static void tst_isolate_roots5() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x^70) - 6*(x^65) - (x^60) + 60*(x^55) - 54*(x^50) - 230*(x^45) + 274*(x^40) + 542*(x^35) - 615*(x^30) - 1120*(x^25) + 1500*(x^20) - 160*(x^15) - 395*(x^10) + 76*(x^5) + 34; { tst_isolate_roots(p, 10); } } static void tst_exact_div(polynomial_ref const & p1, polynomial_ref const & p2, bool expected, polynomial_ref const & expected_q) { upolynomial::manager um(p1.m().m()); upolynomial::scoped_numeral_vector _p1(um), _p2(um), _q(um), _r(um); um.to_numeral_vector(p1, _p1); um.to_numeral_vector(p2, _p2); if (expected) um.to_numeral_vector(expected_q, _q); std::cout << "------\n"; std::cout << "p1: "; um.display(std::cout, _p1); std::cout << "\n"; std::cout << "p2: "; um.display(std::cout, _p2); std::cout << std::endl; bool res = um.exact_div(_p1.size(), _p1.c_ptr(), _p2.size(), _p2.c_ptr(), _r); if (res) { std::cout << "r: "; um.display(std::cout, _r); std::cout << "\n"; } if (expected) { std::cout << "expected: "; um.display(std::cout, _q); std::cout << "\n"; } std::cout.flush(); SASSERT(res == expected); SASSERT(expected == um.divides(_p1.size(), _p1.c_ptr(), _p2.size(), _p2.c_ptr())); SASSERT(!expected || um.eq(_r, _q)); } static void tst_exact_div() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); tst_exact_div((x - 1)*(x - 2)*(x - 3), (x-2)*(x-1), true, (x-3)); tst_exact_div((x - 1)*(2*x - 4)*(x - 3), (x-2)*(x-1), true, (2*x-6)); tst_exact_div((x - 1)*(2*x - 4)*(x - 3), (x-2)*(x-1)*(x-4), false, x); tst_exact_div((x - 3), (x-1), false, x); polynomial_ref z(m); z = m.mk_const(rational(0)); tst_exact_div(z, (x-2)*(x-1)*(x-4), true, z); tst_exact_div((x-2)*(x-1)*(x-4), z, false, z); tst_exact_div(z, z, false, z); polynomial_ref two(m); two = m.mk_const(rational(2)); tst_exact_div((2*x - 2), (x-1), true, two); tst_exact_div((2*x - 2), (4*x-4), false, two); tst_exact_div((6*x - 4), two, true, (3*x - 2)); } static void tst_fact(polynomial_ref const & p, unsigned num_distinct_factors, upolynomial::factor_params const & params = upolynomial::factor_params()) { SASSERT(is_univariate(p)); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; upolynomial::manager um(p.m().m()); upolynomial::scoped_numeral_vector _p(um); upolynomial::factors fs(um); um.to_numeral_vector(p, _p); um.factor(_p, fs, params); std::cout << "factors:\n"; std::cout << um.m().to_string(fs.get_constant()) << "\n"; for (unsigned i = 0; i < fs.distinct_factors(); i++) { std::cout << "*("; um.display(std::cout, fs[i]); std::cout << ")^" << fs.get_degree(i) << std::endl; } SASSERT(fs.distinct_factors() == num_distinct_factors); upolynomial::scoped_numeral_vector _r(um); fs.multiply(_r); TRACE("upolynomial", tout << "_r: "; um.display(tout, _r); tout << "\n_p: "; um.display(tout, _p); tout << "\n";); SASSERT(um.eq(_p, _r)); } static void tst_fact() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m); x0 = m.mk_polynomial(m.mk_var()); tst_fact((x0^4) + (x0^2) - 20, 3); tst_fact((x0^4) + (x0^2) - 20, 1, upolynomial::factor_params(5, 1, 1000)); tst_fact((x0^4) + (x0^2) - 20, 3, upolynomial::factor_params(7, 1, 1000)); tst_fact((x0^70) - 6*(x0^65) - (x0^60) + 60*(x0^55) - 54*(x0^50) - 230*(x0^45) + 274*(x0^40) + 542*(x0^35) - 615*(x0^30) - 1120*(x0^25) + 1500*(x0^20) - 160*(x0^15) - 395*(x0^10) + 76*(x0^5) + 34, 1, upolynomial::factor_params(3, 1, 20)); tst_fact((x0^70) - 6*(x0^65) - (x0^60) + 60*(x0^55) - 54*(x0^50) - 230*(x0^45) + 274*(x0^40) + 542*(x0^35) - 615*(x0^30) - 1120*(x0^25) + 1500*(x0^20) - 160*(x0^15) - 395*(x0^10) + 76*(x0^5) + 34, 2, upolynomial::factor_params(3, 1, 72)); tst_fact((x0^70) - 6*(x0^65) - (x0^60) + 60*(x0^55) - 54*(x0^50) - 230*(x0^45) + 274*(x0^40) + 542*(x0^35) - 615*(x0^30) - 1120*(x0^25) + 1500*(x0^20) - 160*(x0^15) - 395*(x0^10) + 76*(x0^5) + 34, 3, upolynomial::factor_params(3, 1, 80)); tst_fact( (x0^10) - 10*(x0^8) + 38*(x0^6) - 2*(x0^5) - 100*(x0^4) - 40*(x0^3) + 121*(x0^2) - 38*x0 - 17, 1); tst_fact( (x0^4) - 404*(x0^2) + 39204, 2); tst_fact(((x0^5) - (x0^2) + 1)*((-1)*x0 + 1)*((x0^2) - 2*x0 + 3), 3); tst_fact((x0^4) + (x0^2) - 20, 3); tst_fact((-11)*((x0^5) - (x0^2) + 1)*((-1)*x0 + 1)*((x0^2) - 2*x0 + 3), 3); tst_fact(x0 - 2*(x0^2) + 1, 2); tst_fact(13*((x0 - 3)^6)*((x0 - 5)^5)*((x0 - 11)^7), 3); tst_fact((x0+1)^30, 1); tst_fact((x0^70) - 6*(x0^65) - (x0^60) + 60*(x0^55) - 54*(x0^50) - 230*(x0^45) + 274*(x0^40) + 542*(x0^35) - 615*(x0^30) - 1120*(x0^25) + 1500*(x0^20) - 160*(x0^15) - 395*(x0^10) + 76*(x0^5) + 34, 3); tst_fact(((x0^4) - 8*(x0^2)), 2); tst_fact((x0^5) - 2*(x0^3) + x0 - 1, 1); tst_fact( (x0^25) - 4*(x0^21) - 5*(x0^20) + 6*(x0^17) + 11*(x0^16) + 10*(x0^15) - 4*(x0^13) - 7*(x0^12) - 9*(x0^11) - 10*(x0^10) + (x0^9) + (x0^8) + (x0^7) + (x0^6) + 3*(x0^5) + x0 - 1, 2); tst_fact( (x0^25) - 10*(x0^21) - 10*(x0^20) - 95*(x0^17) - 470*(x0^16) - 585*(x0^15) - 40*(x0^13) - 1280*(x0^12) - 4190*(x0^11) - 3830*(x0^10) + 400*(x0^9)+ 1760*(x0^8) + 760*(x0^7) - 2280*(x0^6) + 449*(x0^5) + 640*(x0^3) - 640*(x0^2) + 240*x0 - 32, 2); tst_fact( x0^10, 1); tst_fact( (x0^2) - 1, 2); tst_fact( (-2)*(x0^2) + 2, 2); polynomial_ref zero(m); polynomial_ref three(m); zero = m.mk_zero(); three = m.mk_const(rational(3)); tst_fact(zero, 0); tst_fact(three, 0); tst_fact(x0 + 1, 1); tst_fact(x0 - 1, 1); tst_fact((-1)*x0 - 1, 1); tst_fact((-1)*x0 + 1, 1); tst_fact( (x0^10) - 10*(x0^8) + 38*(x0^6) - 2*(x0^5) - 100*(x0^4) - 40*(x0^3) + 121*(x0^2) - 38*x0 - 17, 1); tst_fact( (x0^50) - 10*(x0^40) + 38*(x0^30) - 2*(x0^25) - 100*(x0^20) - 40*(x0^15) + 121*(x0^10) - 38*(x0^5) - 17, 1); tst_fact( (((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^10) + 10*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^9) + 35*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^8) + 40*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^7) - 32*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^6) - 82*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^5) - 30*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^4) - 140*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^3) - 284*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^2) - 168*((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0) - 47, 1); tst_fact( (x0^4) - 404*(x0^2) + 39204, 2); tst_fact( ((x0^5) - 15552)* ((x0^20)- 15708*(x0^15) + rational("138771724")*(x0^10)- rational("432104148432")*(x0^5) + rational("614198284585616")), 2); tst_fact( (x0^25) - rational("3125")*(x0^21) - rational("15630")*(x0^20) + rational("3888750")*(x0^17) + rational("38684375")*(x0^16) + rational("95765635")*(x0^15) - rational("2489846500")*(x0^13) - rational("37650481875")*(x0^12) - rational("190548065625")*(x0^11) - rational("323785250010")*(x0^10) + rational("750249453025")*(x0^9) + rational("14962295699875")*(x0^8) + rational("111775113235000")*(x0^7) + rational("370399286731250")*(x0^6) + rational("362903064503129")*(x0^5) - rational("2387239013984400")*(x0^4) - rational("23872390139844000")*(x0^3) - rational("119361950699220000")*(x0^2) - rational("298404876748050000")*x0 - rational("298500366308609376"), 2); tst_fact( rational("54")*(x0^24) - (x0^27) - 324*(x0^21) + rational("17496")*(x0^18) - 34992*(x0^15)+ rational("1889568")*(x0^12)- 1259712*(x0^9) + rational("68024448")*(x0^6), 3); tst_fact( ((x0^3)- 432)*(((x0^3)+54)^2)*((x0^6)+108)*((x0^6)+6912)*((x0^6)- 324*(x0^3)+37044), 5); tst_fact( ((x0^6)- 6*(x0^4) - 864*(x0^3) + 12*(x0^2) - 5184*x0 + 186616)* (((x0^6) - 6*(x0^4) + 108*(x0^3) + 12*(x0^2) + 648*x0 + 2908)^2)* ((x0^12) - 12*(x0^10) + 60*(x0^8) + 56*(x0^6) + 6720*(x0^4) + 12768*(x0^2) + 13456)* ((x0^12) - 12*(x0^10) + 60*(x0^8) + 13664*(x0^6) + 414960*(x0^4) + 829248*(x0^2) + 47886400)* ((x0^12) - 12*(x0^10) - 648*(x0^9)+ 60*(x0^8) + 178904*(x0^6) + 15552*(x0^5) + 1593024*(x0^4) - 24045984*(x0^3) + 5704800*(x0^2) - 143995968*x0 + 1372010896), 5); } static void tst_rem(polynomial_ref const & p, polynomial_ref const & q, polynomial_ref const & expected) { SASSERT(is_univariate(p)); SASSERT(is_univariate(q)); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; std::cout << "q: " << q << std::endl; upolynomial::manager um(p.m().m()); upolynomial::scoped_numeral_vector _p(um), _q(um), _r(um); um.to_numeral_vector(p, _p); um.to_numeral_vector(q, _q); um.rem(_p.size(), _p.c_ptr(), _q.size(), _q.c_ptr(), _r); polynomial_ref r(p.m()); r = p.m().to_polynomial(_r.size(), _r.c_ptr(), 0); std::cout << "r: " << r << std::endl; SASSERT(eq(expected, r)); } static void tst_rem() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x0(m), zero(m), one(m); x0 = m.mk_polynomial(m.mk_var()); zero = m.mk_zero(); one = m.mk_const(rational(1)); tst_rem((x0^2) + x0, x0, zero); tst_rem((x0^2) + x0 + 1, x0, one); tst_rem((x0^2) + 2*x0 + 1, 2*x0 + 2, zero); } static void tst_lower_bound(polynomial_ref const & p) { SASSERT(is_univariate(p)); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; upolynomial::manager um(p.m().m()); upolynomial::scoped_numeral_vector _p(um); um.to_numeral_vector(p, _p); std::cout << "_p: "; um.display(std::cout, _p); std::cout << "\n"; unsigned k = um.nonzero_root_lower_bound(_p.size(), _p.c_ptr()); std::cout << "_p: "; um.display(std::cout, _p); std::cout << "\n"; std::cout << "k: " << k << "\n"; } static void tst_lower_bound() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m), zero(m), one(m); x = m.mk_polynomial(m.mk_var()); zero = m.mk_zero(); one = m.mk_const(rational(1)); tst_lower_bound((x^2) - 2); tst_lower_bound((x^5)); tst_lower_bound((x - 1)*(2*x - 1)*(4*x - 1)*(8*x - 1)); tst_lower_bound((x - 1)*(2*x - 1)*(4*x - 1)*(8*x - 1)*(16*x - 1)); tst_lower_bound((x - 1)*(2*x - 1)*(4*x - 1)*(8*x - 1)*(16*x - 1)*(x^3)); tst_lower_bound((x^5) - x - 1); tst_lower_bound((1000*x - 1)*(x - 1)); tst_lower_bound((x + 1)*(2*x - 1)*(4*x + 1)*(8*x - 1)*(16*x + 1)); tst_lower_bound((x + 1)*(2*x + 1)*(4*x + 1)*(8*x + 1)*(16*x + 1)); tst_lower_bound((x^10) - 10*(x^8) + 38*(x^6) - 2*(x^5) - 100*(x^4) - 40*(x^3) + 121*(x^2) - 38*x - 17); tst_lower_bound(((x^17) + 5*(x^16) + 3*(x^15) + 10*(x^13) + 13*(x^10) + (x^9) + 8*(x^5) + 3*(x^2) + 7)*(((x^5) - x - 1)^2)*(((x^3) - 2)^2)); tst_lower_bound((((x^5) - 1000000000)^3)*((3*x - 10000000)^2)*((10*x - 632)^2)); } void tst_upolynomial() { set_verbosity_level(1000); enable_trace("mpz_gcd"); enable_trace("normalize_bug"); enable_trace("factor_bug"); enable_trace("factor"); // enable_trace("mpzp_inv_bug"); // enable_trace("mpz"); tst_gcd(); tst_lower_bound(); tst_fact(); tst_rem(); tst_exact_div(); tst_isolate_roots5(); // tst_gcd2(); // tst_isolate_roots4(); // tst_isolate_roots3(); // tst_isolate_roots2(); // return; tst_isolate_roots(); tst_sturm2(); tst_convert_q2bq(); enable_trace("div_bug"); enable_trace("mpbq_bug"); tst_translate_q(); tst_refine(); tst_refinable(); tst_sturm(); tst_remove_one_half(); tst_isolate_roots(); tst1(); tst_zp(); tst_zp2(); tst_ext_gcd(); tst_ext_gcd_z7(); } z3-z3-4.4.1/src/test/var_subst.cpp000066400000000000000000000054421260446376700167140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: var_subst.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-12. Revision History: --*/ #include"var_subst.h" #include"ast_pp.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"array_decl_plugin.h" #include"for_each_expr.h" #include"reg_decl_plugins.h" namespace find_q { struct proc { quantifier * m_q; proc():m_q(0) {} void operator()(var * n) {} void operator()(app * n) {} void operator()(quantifier * n) { m_q = n; } }; }; quantifier * find_quantifier(expr * n) { find_q::proc p; for_each_expr(p, n); return p.m_q; } void tst_instantiate(ast_manager & m, expr * f) { if (is_quantifier(f)) { tst_instantiate(m, to_quantifier(f)->get_expr()); return; } quantifier * q = find_quantifier(f); if (q) { expr_ref_vector cnsts(m); for (unsigned i = 0; i < q->get_num_decls(); i++) cnsts.push_back(m.mk_fresh_const("a", q->get_decl_sort(i))); expr_ref r(m); instantiate(m, q, cnsts.c_ptr(), r); TRACE("var_subst", tout << "quantifier:\n" << mk_pp(q, m) << "\nresult:\n" << mk_pp(r, m) << "\n";); } } void tst_subst(ast_manager& m) { func_decl_ref p(m); sort_ref s(m); obj_ref x(m), y(m), z(m), u(m), v(m); expr_ref e1(m), e2(m), e3(m); expr_ref t1(m), t2(m), t3(m); s = m.mk_uninterpreted_sort(symbol("S")); sort* ss[2] = { s.get(), s.get() }; symbol names[2] = { symbol("y"), symbol("x") }; p = m.mk_func_decl(symbol("p"), 2, ss, m.mk_bool_sort()); x = m.mk_var(0, s); y = m.mk_var(1, s); z = m.mk_var(2, s); u = m.mk_var(3, s); v = m.mk_var(4, s); e1 = m.mk_and(m.mk_app(p, x.get(), y.get()), m.mk_app(p, z.get(), u.get())); e2 = m.mk_forall(1, ss, names, e1); t1 = m.mk_forall(1, ss, names, m.mk_and(m.mk_app(p, x.get(), z.get()), m.mk_app(p, y.get(), u.get()))); t2 = m.mk_forall(2, ss, names, m.mk_and(m.mk_app(p, x.get(), y.get()), m.mk_app(p, u.get(), z.get()))); var_subst subst(m); expr_ref_vector sub1(m); sub1.push_back(x); sub1.push_back(y); // replace #1 -> #2, #2 -> #1 subst(e2, 2, sub1.c_ptr(), e3); std::cout << mk_pp(e2, m) << "\n"; std::cout << mk_pp(e3, m) << "\n"; std::cout << mk_pp(t1, m) << "\n"; SASSERT(e3.get() == t1.get()); // replace #2 -> #3, #3 -> #2 e2 = m.mk_forall(2, ss, names, e1); subst(e2, 2, sub1.c_ptr(), e3); std::cout << mk_pp(e2, m) << "\n"; std::cout << mk_pp(e3, m) << "\n"; std::cout << mk_pp(t2, m) << "\n"; SASSERT(e3.get() == t2.get()); } void tst_var_subst() { ast_manager m; reg_decl_plugins(m); tst_subst(m); } z3-z3-4.4.1/src/test/vector.cpp000066400000000000000000000024531260446376700162050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_vector.cpp Abstract: Test my vector template. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #include"vector.h" static void tst1() { svector v1; SASSERT(v1.empty()); for (unsigned i = 0; i < 1000; i++) { v1.push_back(i + 3); SASSERT(static_cast(v1[i]) == i + 3); SASSERT(v1.capacity() >= v1.size()); SASSERT(!v1.empty()); } for (unsigned i = 0; i < 1000; i++) { SASSERT(static_cast(v1[i]) == i + 3); } svector::iterator it = v1.begin(); svector::iterator end = v1.end(); for (int i = 0; it != end; ++it, ++i) { SASSERT(*it == i + 3); } for (unsigned i = 0; i < 1000; i++) { SASSERT(static_cast(v1.back()) == 1000 - i - 1 + 3); SASSERT(v1.size() == 1000 - i); v1.pop_back(); } SASSERT(v1.empty()); SASSERT(v1.size() == 0); unsigned i = 1000000000; while (true) { std::cout << "resize " << i << "\n"; try { v1.resize(i); } catch (z3_exception& e) { std::cout << e.msg() << "\n"; break; } i *= 2; } } void tst_vector() { tst1(); } z3-z3-4.4.1/src/util/000077500000000000000000000000001260446376700141715ustar00rootroot00000000000000z3-z3-4.4.1/src/util/approx_nat.cpp000066400000000000000000000024331260446376700170520ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: approx_nat.cpp Abstract: Approximated natural numbers. It performs operations on the set [0, ..., 2^{n-2}, huge]. Where huge represents all numbers greater than 2^{n-2}. Author: Leonardo (leonardo) 2008-01-11 Notes: --*/ #include"approx_nat.h" approx_nat::approx_nat(unsigned val) { m_value = val > m_limit ? UINT_MAX : val; } approx_nat & approx_nat::operator=(unsigned val) { m_value = val > m_limit ? UINT_MAX : val; return *this; } approx_nat & approx_nat::operator+=(unsigned w) { if (is_huge()) return *this; if (w > m_limit) { m_value = UINT_MAX; return *this; } m_value += w; if (m_value > m_limit) m_value = UINT_MAX; return *this; } approx_nat & approx_nat::operator*=(unsigned w) { if (is_huge()) return *this; unsigned long long r = static_cast(m_value) * static_cast(w); if (r > m_limit) m_value = UINT_MAX; else m_value = static_cast(r); return *this; } std::ostream & operator<<(std::ostream & target, approx_nat const & w) { if (w.is_huge()) target << "[huge]"; else target << w.get_value(); return target; } z3-z3-4.4.1/src/util/approx_nat.h000066400000000000000000000023331260446376700165160ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: approx_nat.h Abstract: Approximated natural numbers. It performs operations on the set [0, ..., 2^{n-2}, huge]. Where huge represents all numbers greater than 2^{n-2}. Author: Leonardo (leonardo) 2008-01-11 Notes: --*/ #ifndef APPROX_NAT_H_ #define APPROX_NAT_H_ #include #include class approx_nat { unsigned m_value; static const unsigned m_limit = UINT_MAX >> 2; public: approx_nat():m_value(0) {} explicit approx_nat(unsigned val); bool is_huge() const { return m_value == UINT_MAX; } unsigned get_value() const { return m_value; } approx_nat & operator=(unsigned w); approx_nat & operator+=(unsigned w); approx_nat & operator+=(approx_nat const & w) { return operator+=(w.m_value); } approx_nat & operator*=(unsigned w); approx_nat & operator*=(approx_nat const & w) { return operator*=(w.m_value); } bool operator<(unsigned w) const { return !is_huge() && m_value < w; } bool operator<(approx_nat const & w) const { return !is_huge() && !w.is_huge() && m_value < w.m_value; } }; std::ostream & operator<<(std::ostream & target, approx_nat const & w); #endif /* APPROX_NAT_H_ */ z3-z3-4.4.1/src/util/approx_set.cpp000066400000000000000000000015671260446376700170720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: approx_set.cpp Abstract: Approximated sets. Author: Leonardo de Moura (leonardo) 2007-03-02. Revision History: --*/ #include"approx_set.h" void approx_set::display(std::ostream & out) const { out << "{"; bool first = true; unsigned long long s = m_set; for (unsigned i = 0; i < approx_set_traits::capacity; i++) { if ((s & 1) != 0) { if (first) { first = false; } else { out << ", "; } out << i; } s = s >> 1; } out << "}"; } unsigned approx_set::size() const { unsigned long long tmp = m_set; unsigned r = 0; while (tmp > 0) { if ((tmp & 1) != 0) { r++; } tmp = tmp >> 1; } return r; } z3-z3-4.4.1/src/util/approx_set.h000066400000000000000000000141001260446376700165220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: approx_set.h Abstract: Approximated sets. Author: Leonardo de Moura (leonardo) 2007-03-02. Revision History: --*/ #ifndef APPROX_SET_H_ #define APPROX_SET_H_ #include #include"debug.h" template class approx_set_traits; template <> class approx_set_traits { public: static const unsigned capacity = 64; static const unsigned long long zero = 0ull; static const unsigned long long one = 1ull; }; COMPILE_TIME_ASSERT(sizeof(unsigned long long) == 8); template <> class approx_set_traits { public: static const unsigned capacity = 32; static const unsigned zero = 0; static const unsigned one = 1; }; COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); template class approx_set_tpl : private T2U_Proc { protected: R m_set; unsigned e2u(T const & e) const { return T2U_Proc::operator()(e); } R u2s(unsigned u) const { return (approx_set_traits::one << (u & (approx_set_traits::capacity - 1))); } R e2s(T const & e) const { return u2s(e2u(e)); } static approx_set_tpl r2s(R const & s) { approx_set_tpl r; r.m_set = s; return r; } public: approx_set_tpl(): m_set(approx_set_traits::zero) { } explicit approx_set_tpl(T const & e): m_set(e2s(e)) { } approx_set_tpl(unsigned sz, T const * es): m_set(approx_set_traits::zero) { for (unsigned i = 0; i < sz; i++) insert(es[i]); } approx_set_tpl(approx_set_tpl const & s): m_set(s.m_set) { } void insert(T const & e) { m_set |= e2s(e); } bool may_contain(T const & e) const { return (m_set & e2s(e)) != approx_set_traits::zero; } bool must_not_contain(T const & e) const { return !may_contain(e); } friend inline approx_set_tpl mk_union(approx_set_tpl const & s1, approx_set_tpl const & s2) { return r2s(s1.m_set | s2.m_set); } friend inline approx_set_tpl mk_intersection(approx_set_tpl const & s1, approx_set_tpl const & s2) { return r2s(s1.m_set & s2.m_set); } void operator|=(approx_set_tpl const & other) { m_set |= other.m_set; } void operator&=(approx_set_tpl const & other) { m_set &= other.m_set; } void operator-=(approx_set_tpl const & other) { m_set &= ~(other.m_set); } bool empty() const { return m_set == approx_set_traits::zero; } friend inline bool empty(approx_set_tpl const & s) { return s.empty(); } bool must_not_subset(approx_set_tpl const & s2) const { return (m_set & ~(s2.m_set)) != approx_set_traits::zero; } friend inline bool must_not_subset(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.must_not_subset(s2); } bool must_not_subsume(approx_set_tpl const & s2) const { return must_not_subset(s2); } friend inline bool must_not_subsume(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.must_not_subset(s2); } friend inline bool must_not_eq(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.m_set != s2.m_set; } friend inline bool may_eq(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.m_set == s2.m_set; } /** \brief Return if s1 and s2 are the same approximated set. */ bool equiv(approx_set_tpl const & s2) const { return m_set == s2.m_set; } friend inline bool equiv(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.m_set == s2.m_set; } /** \brief Return true if the approximation of s1 is a subset of the approximation of s2. */ friend inline bool approx_subset(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s2.equiv(mk_union(s1, s2)); } void reset() { m_set = approx_set_traits::zero; } bool empty_intersection(approx_set_tpl const & other) const { return mk_intersection(*this, other).empty(); } }; struct u2u { unsigned operator()(unsigned u) const { return u; } }; typedef approx_set_tpl u_approx_set; #define APPROX_SET_CAPACITY (approx_set_traits::capacity) class approx_set : public u_approx_set { public: approx_set():u_approx_set() {} approx_set(unsigned e):u_approx_set(e) {} class iterator { unsigned long long m_set; unsigned m_val; void move_to_next() { // TODO: this code can be optimized in platforms with special // instructions to count leading (trailing) zeros in a word. while (m_set > 0) { if ((m_set & 1ull) != 0) { return; } m_val ++; m_set = m_set >> 1; } } public: iterator(unsigned long long s): m_set(s), m_val(0) { move_to_next(); } unsigned operator*() const { return m_val; } iterator & operator++() { m_val++; m_set = m_set >> 1; move_to_next(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const & it) const { return m_set == it.m_set; } bool operator!=(iterator const & it) const { return m_set != it.m_set; } }; iterator begin() const { return iterator(m_set); } static iterator end() { return iterator(0); } void display(std::ostream & out) const; unsigned size() const; // for backward compatibility friend inline bool operator==(approx_set const & s1, approx_set const & s2) { return may_eq(s1, s2); } }; inline std::ostream & operator<<(std::ostream & out, approx_set const & s) { s.display(out); return out; } #endif /* APPROX_SET_H_ */ z3-z3-4.4.1/src/util/array.h000066400000000000000000000126331260446376700154650ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array.h Abstract: Fixed size arrays Author: Leonardo de Moura (leonardo) 2011-01-26. Revision History: --*/ #ifndef ARRAY_H_ #define ARRAY_H_ template class array { public: // Return the space needed to store an array of size sz. static size_t space(size_t sz) { return sizeof(T)*sz + sizeof(size_t); } private: #define ARRAY_SIZE_IDX -1 T * m_data; void destroy_elements() { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { it->~T(); } } char * raw_ptr() const { return reinterpret_cast(reinterpret_cast(m_data) - 1); } array & operator=(array const & source); void set_data(void * mem, unsigned sz) { size_t * _mem = static_cast(mem); *_mem = sz; _mem ++; m_data = reinterpret_cast(_mem); } template void allocate(Allocator & a, unsigned sz) { size_t * mem = reinterpret_cast(a.allocate(space(sz))); set_data(mem, sz); } void init(T const & v) { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { new (it) T(v); } } void init(T const * vs) { iterator it = begin(); iterator e = end(); for (; it != e; ++it, ++vs) { new (it) T(*vs); } } public: typedef T data; typedef T * iterator; typedef const T * const_iterator; array():m_data(0) {} /** \brief Store the array in the given chunk of memory (mem). This chunck should be big enough to store space(sz) bytes. */ array(void * mem, unsigned sz, T const * vs) { DEBUG_CODE(m_data = 0;); set(mem, sz, vs); } // WARNING: the memory allocated will not be automatically freed. array(void * mem, unsigned sz, bool init_mem) { DEBUG_CODE(m_data = 0;); set_data(mem, sz); if (init_mem) init(); } // WARNING: the memory allocated will not be automatically freed. template array(Allocator & a, unsigned sz, T const * vs) { DEBUG_CODE(m_data = 0;); set(a, sz, vs); } // WARNING: the memory allocated will not be automatically freed. template array(Allocator & a, unsigned sz, bool init_mem) { DEBUG_CODE(m_data = 0;); allocate(a, sz); if (init_mem) init(); } // WARNING: this does not free the memory used to store the array. // You must free it yourself, or use finalize. ~array() { if (m_data && CallDestructors) destroy_elements(); } // Free the memory used to store the array. template void finalize(Allocator & a) { if (m_data) { if (CallDestructors) destroy_elements(); a.deallocate(space(size()), raw_ptr()); m_data = 0; } } void set(void * mem, unsigned sz, T const * vs) { SASSERT(m_data == 0); set_data(mem, sz); init(vs); } template void set(Allocator & a, unsigned sz, T const * vs) { SASSERT(m_data == 0); allocate(a, sz); init(vs); } template void set(Allocator & a, unsigned sz, T const & v = T()) { SASSERT(m_data == 0); allocate(a, sz); init(v); } unsigned size() const { if (m_data == 0) { return 0; } return static_cast(reinterpret_cast(m_data)[SIZE_IDX]); } bool empty() const { return m_data == 0; } T & operator[](unsigned idx) { SASSERT(idx < size()); return m_data[idx]; } T const & operator[](unsigned idx) const { SASSERT(idx < size()); return m_data[idx]; } iterator begin() { return m_data; } iterator end() { return m_data + size(); } const_iterator begin() const { return m_data; } const_iterator end() const { return m_data + size(); } T const * c_ptr() const { return m_data; } T * c_ptr() { return m_data; } void swap(array & other) { std::swap(m_data, other.m_data); } }; template class ptr_array : public array { public: ptr_array() {} ptr_array(void * mem, unsigned sz, T * const * vs):array(mem, sz, vs) {} template ptr_array(Allocator & a, unsigned sz, T * const * vs):array(a, sz, vs) {} ptr_array(void * mem, unsigned sz, bool init_mem):array(mem, sz, init_mem) {} template ptr_array(Allocator & a, unsigned sz, bool init_mem):array(a, sz, init_mem) {} }; template class sarray : public array { public: sarray() {} sarray(void * mem, unsigned sz, T const * vs):array(mem, sz, vs) {} template sarray(Allocator & a, unsigned sz, T const * vs):array(a, sz, vs) {} sarray(void * mem, unsigned sz, bool init_mem):array(mem, sz, init_mem) {} template sarray(Allocator & a, unsigned sz, bool init_mem):array(a, sz, init_mem) {} }; #endif z3-z3-4.4.1/src/util/array_map.h000066400000000000000000000076041260446376700163240ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array_map.h Abstract: A mapping for keys that can be mapped to unsigned integers. Author: Leonardo de Moura (leonardo) 2008-01-03. Revision History: --*/ #ifndef ARRAY_MAP_H_ #define ARRAY_MAP_H_ #include"vector.h" #include"optional.h" /** \brief Implements a mapping from Key to Data. Plugin must provide the following functions: - void ins_eh(Key const & k, Data const & d); - void del_eh(Key const & k, Data const & d); - unsigned to_int(Key const & k); */ template class array_map { struct entry { Key m_key; Data m_data; unsigned m_timestamp; entry(Key const & k, Data const & d, unsigned t): m_key(k), m_data(d), m_timestamp(t) {} }; unsigned m_timestamp; unsigned m_garbage; unsigned m_non_garbage; static const unsigned m_gc_threshold = 10000; vector, CallDestructors > m_map; Plugin m_plugin; bool is_current(optional const& e) const { return e->m_timestamp == m_timestamp; } optional const & get_core(Key const & k) const { unsigned id = m_plugin.to_int(k); if (id < m_map.size()) { optional const & e = m_map[id]; if (e && is_current(e)) { return e; } } return optional::undef(); } void really_flush() { typename vector >::iterator it = m_map.begin(); typename vector >::iterator end = m_map.end(); for (; it != end; ++it) { optional & e = *it; if (e) { m_plugin.del_eh(e->m_key, e->m_data); e.set_invalid(); } } m_garbage = 0; m_non_garbage = 0; } public: array_map(Plugin const & p = Plugin()):m_timestamp(0), m_garbage(0), m_non_garbage(0), m_plugin(p) {} ~array_map() { really_flush(); } bool contains(Key const & k) const { return get_core(k); } Data const & get(Key const & k) const { optional const & e = get_core(k); SASSERT(e); return e->m_data; } void reset() { if (m_timestamp < UINT_MAX) { m_timestamp++; } else { really_flush(); m_timestamp = 0; } } void insert(Key const & k, Data const & d) { unsigned id = m_plugin.to_int(k); if (id >= m_map.size()) { m_map.resize(id + 1, optional::undef()); } m_plugin.ins_eh(k, d); optional & e = m_map[id]; if (e) { if (!is_current(e)) { --m_garbage; ++m_non_garbage; } m_plugin.del_eh(e->m_key, e->m_data); } else { ++m_non_garbage; } e = entry(k, d, m_timestamp); } void erase(Key const & k) { unsigned id = m_plugin.to_int(k); if (id < m_map.size()) { optional & e = m_map[id]; if (e) { m_plugin.del_eh(e->m_key, e->m_data); if (is_current(e)) { SASSERT(m_non_garbage > 0); --m_non_garbage; } else { SASSERT(m_garbage > 0); --m_garbage; } e.set_invalid(); } } } void flush() { m_garbage += m_non_garbage; m_non_garbage = 0; if (m_garbage > m_gc_threshold) { really_flush(); } else { reset(); } } void finalize() { really_flush(); } }; #endif /* ARRAY_MAP_H_ */ z3-z3-4.4.1/src/util/backtrackable_set.h000066400000000000000000000046141260446376700177730ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: backtrackable_set.h Abstract: Quick hack for support backtrackable sets. Author: Leonardo de Moura (leonardo) 2011-01-08. Revision History: --*/ #ifndef BACKTRACKABLE_SET_H_ #define BACKTRACKABLE_SET_H_ #include"vector.h" template struct default_eh { void operator()(T const & e, bool ins) {} }; // quick hack for having backtrackable sets. // // EV is a big hack, it should be used with care. // template > class backtrackable_set : private EV { enum trail_kind { DEL, INS }; typedef std::pair trail_obj; Set m_set; svector m_trail; svector m_scopes; public: typedef typename Set::iterator iterator; backtrackable_set(EV const & ev = EV()): EV(ev) { } void insert(T const & e) { if (m_scopes.empty()) { m_set.insert(e); } else if (!m_set.contains(e)) { m_set.insert(e); m_trail.push_back(std::make_pair(INS, e)); } } void erase(T const & e) { if (m_scopes.empty()) { m_set.insert(e); } else if (m_set.contains(e)) { m_set.erase(e); m_trail.push_back(std::make_pair(DEL, e)); } } bool contains(T const & e) const { return m_set.contains(e); } bool empty() const { return m_set.empty(); } void push_scope() { m_scopes.push_back(m_trail.size()); } void pop_scope() { unsigned old_sz = m_scopes.back(); m_scopes.pop_back(); SASSERT(old_sz <= m_trail.size()); while (m_trail.size() > old_sz) { trail_obj & t = m_trail.back(); if (t.first == INS) { this->operator()(t.second, true); m_set.erase(t.second); } else { SASSERT(t.first == DEL); this->operator()(t.second, false); m_set.insert(t.second); } m_trail.pop_back(); } } void reset() { m_scopes.reset(); m_trail.reset(); m_set.reset(); } iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } }; #endif z3-z3-4.4.1/src/util/basic_interval.h000066400000000000000000000245411260446376700173350ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: basic_interval.h Abstract: Basic interval arithmetic template for precise numerals: mpz, mpq, mpbq. Only basic support is provided. There is no support for: - minus and plus infinity bounds. - mixed open/closed intervals such as (2, 3] The main customer of this package is the algebraic_number module. Author: Leonardo de Moura (leonardo) 2012-12-04. Revision History: --*/ #ifndef BASIC_INTERVAL_H_ #define BASIC_INTERVAL_H_ template class basic_interval_manager { public: typedef typename numeral_manager::numeral bound; class interval { friend class basic_interval_manager; bound m_lower; bound m_upper; public: interval() {} bound const & lower() const { return m_lower; } bound const & upper() const { return m_upper; } bound & lower() { return m_lower; } bound & upper() { return m_upper; } }; class scoped_interval { basic_interval_manager & m_manager; interval m_interval; public: scoped_interval(basic_interval_manager & m):m_manager(m) {} ~scoped_interval() { m_manager.del(m_interval); } basic_interval_manager & m() const { return m_manager; } operator interval const &() const { return m_interval; } operator interval&() { return m_interval; } interval const & get() const { return m_interval; } interval & get() { return m_interval; } void reset() { m().reset(m_interval); } void swap(scoped_interval & a) { m().swap(m_interval, a.m_interval); } void swap(interval & a) { m().swap(m_interval, a); } bound const & lower() const { return m_interval.lower(); } bound const & upper() const { return m_interval.upper(); } bound & lower() { return m_interval.lower(); } bound & upper() { return m_interval.upper(); } friend std::ostream & operator<<(std::ostream & out, scoped_interval const & a) { a.m().display(out, a.get()); return out; } }; protected: numeral_manager & m_manager; bound m_mul_curr; bound m_mul_max; bound m_mul_min; public: typedef interval numeral; // allow intervals to be used by algorithms parameterized by numeral_manager basic_interval_manager(numeral_manager & m): m_manager(m) { } ~basic_interval_manager() { m().del(m_mul_curr); m().del(m_mul_max); m().del(m_mul_min); } numeral_manager & m() const { return m_manager; } /** \brief Delete interval */ void del(interval & a) { m().del(a.m_lower); m().del(a.m_upper); } /** \brief Delete and reset lower and upper bounds to 0 */ void reset(interval & a) { m().reset(a.m_lower); m().reset(a.m_upper); } bound const & lower(interval const & a) { return a.lower(); } bound const & upper(interval const & a) { return a.upper(); } /** \brief a <- (lower, upper) */ void set(interval & a, bound const & lower, bound const & upper) { SASSERT(m().le(lower, upper)); m().set(a.m_lower, lower); m().set(a.m_upper, upper); } /** \brief a <- b */ void set(interval & a, interval const & b) { set(a, b.m_lower, b.m_upper); } /** \brief a <- (n, n) Manager must be configured for closed intervals. */ void set(interval & a, bound const & n) { m().set(a.m_lower, n); m().set(a.m_upper, n); } void set_lower(interval & a, bound const & n) { SASSERT(m().le(n, a.m_upper)); m().set(a.m_lower, n); } void set_upper(interval & a, bound const & n) { SASSERT(m().le(a.m_lower, n)); m().set(a.m_upper, n); } void swap(interval & a, interval & b) { m().swap(a.m_lower, b.m_lower); m().swap(a.m_upper, b.m_upper); } /** \brief a <- -a */ void neg(interval & a) { m().neg(a.m_lower); m().neg(a.m_upper); m().swap(a.m_lower, a.m_upper); } /** \brief Return true if a does not contain any value. We can only have empty intervals if the manager is configured to used open intervals. */ bool is_empty(interval const & a) { return !closed && m().eq(a.m_lower, a.m_upper); } /** \brief Return true if all values in the given interval are positive. */ bool is_pos(interval const & a) { return (closed && m().is_pos(a.m_lower)) || (!closed && m().is_nonneg(a.m_lower)); } /** \brief Return true if all values in the given interval are negative. */ bool is_neg(interval const & a) { return (closed && m().is_neg(a.m_upper)) || (!closed && m().is_nonpos(a.m_upper)); } /** \brief Return true if 0 is in the interval. */ bool contains_zero(interval const & a) { return (closed && m().is_nonpos(a.m_lower) && m().is_nonneg(a.m_upper)) || (!closed && m().is_neg(a.m_lower) && m().is_pos(a.m_upper)); } /** \brief Return true if all values in interval a are in interval b. */ bool is_subset(interval const & a, interval const & b) { return m().le(b.m_lower, a.m_lower) && m().le(a.m_upper, b.m_upper); } /** \brief Return true if there is no value v s.t. v \in a and v \in b. */ bool disjoint(interval const & a, interval const & b) { return (closed && (m().lt(a.m_upper, b.m_lower) || m().lt(b.m_upper, a.m_lower))) || (!closed && (m().le(a.m_upper, b.m_upper) || m().le(b.m_upper, a.m_lower))); } /** \brief Return true if all elements in a are smaller than all elements in b. */ bool precedes(interval const & a, interval const & b) { return (closed && (m().lt(a.m_upper, b.m_lower))) || (!closed && (m().le(a.m_upper, b.m_lower))); } /** \brief Return true if all elements in a are smaller than b. */ bool precedes(interval const & a, bound const & b) { return (closed && (m().lt(a.m_upper, b))) || (!closed && (m().le(a.m_upper, b))); } /** \brief Return true if a is smaller than all elements in b. */ bool precedes(bound const & a, interval const & b) { return (closed && (m().lt(a, b.m_lower))) || (!closed && (m().le(a, b.m_lower))); } /** \brief a <- 1/a \pre a.m_lower and m_upper must not be 0. \pre bound must be a field. */ void inv(interval & a) { SASSERT(numeral_manager::field()); SASSERT(!contains_zero(a)); SASSERT(!m().is_zero(a.m_lower) && !m().is_zero(a.m_upper)); m().inv(a.m_lower); m().inv(a.m_upper); m().swap(a.m_lower, a.m_upper); } /** \brief c <- a + b */ void add(interval const & a, interval const & b, interval & c) { m().add(a.m_lower, b.m_lower, c.m_lower); m().add(a.m_upper, b.m_upper, c.m_upper); } /** \brief c <- a - b */ void sub(interval const & a, interval const & b, interval & c) { m().sub(a.m_lower, b.m_upper, c.m_lower); m().sub(a.m_upper, b.m_lower, c.m_upper); } private: /** \brief Init the value of m_mul_max and m_mul_min using m_mul_curr */ void init_mul_max_min() { m().set(m_mul_min, m_mul_curr); m().swap(m_mul_max, m_mul_curr); } /** \brief Update the value of m_mul_max and m_mul_min using m_mul_curr */ void update_mul_max_min() { if (m().lt(m_mul_curr, m_mul_min)) m().set(m_mul_min, m_mul_curr); if (m().gt(m_mul_curr, m_mul_max)) m().swap(m_mul_max, m_mul_curr); } public: /** \brief c <- a * b */ void mul(interval const & a, interval const & b, interval & c) { m().mul(a.m_lower, b.m_lower, m_mul_curr); init_mul_max_min(); m().mul(a.m_lower, b.m_upper, m_mul_curr); update_mul_max_min(); m().mul(a.m_upper, b.m_lower, m_mul_curr); update_mul_max_min(); m().mul(a.m_upper, b.m_upper, m_mul_curr); update_mul_max_min(); m().swap(c.m_lower, m_mul_min); m().swap(c.m_upper, m_mul_max); } /** \brief c <- a/b \pre b m_lower and m_upper must not be 0 \pre bound must be a field. */ void div(interval const & a, interval const & b, interval & c) { SASSERT(numeral_manager::field()); SASSERT(!contains_zero(b)); SASSERT(!m().is_zero(b.m_lower) && !m().is_zero(b.m_upper)); m().div(a.m_lower, b.m_lower, m_mul_curr); init_mul_max_min(); m().div(a.m_lower, b.m_upper, m_mul_curr); update_mul_max_min(); m().div(a.m_upper, b.m_lower, m_mul_curr); update_mul_max_min(); m().div(a.m_upper, b.m_upper, m_mul_curr); update_mul_max_min(); m().swap(c.m_lower, m_mul_min); m().swap(c.m_upper, m_mul_max); } /** \brief c <- a^n */ void power(interval const & a, unsigned n, interval & c) { // Let a be of the form (l, u) if (n % 2 == 1) { // n is odd // c <- (l^n, u^n) m().power(a.m_lower, n, c.m_lower); m().power(a.m_upper, n, c.m_upper); } else { SASSERT(n % 2 == 0); m().power(a.m_lower, n, c.m_lower); m().power(a.m_upper, n, c.m_upper); if (m().is_nonneg(a.m_lower)) { // n is even and l >= 0 // c <- (l^n, u^n) return; } if (m().is_neg(a.m_upper)) { // n is even and u < 0 // c <- (u^n, l^n) m().swap(c.m_lower, c.m_upper); return; } // c <- (0, max(l^n, u^n)) if (m().gt(c.m_lower, c.m_upper)) m().swap(c.m_lower, c.m_upper); m().reset(c.m_lower); } } void display(std::ostream & out, interval const & a) { out << (closed ? "[" : "(") << m().to_string(a.m_lower) << ", " << m().to_string(a.m_upper) << (closed ? "]" : ")"); } }; #endif z3-z3-4.4.1/src/util/bit_util.cpp000066400000000000000000000234151260446376700165150ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bit_util.cpp Abstract: Bit hacking utilities. Author: Leonardo de Moura (leonardo) 2012-09-11. Revision History: --*/ #include"bit_util.h" #include"util.h" #include"debug.h" #include /** \brief (Debugging version) Return the position of the most significant (set) bit of a nonzero unsigned integer. */ #ifdef Z3DEBUG unsigned slow_msb_pos(unsigned v) { SASSERT(v != 0); unsigned r = 0; while (v != 1) { v = v >> 1; r++; } return r; } #endif /** \brief Return the position of the most significant (set) bit of a nonzero unsigned integer. */ unsigned msb_pos(unsigned v) { SASSERT(v != 0); #ifdef Z3DEBUG unsigned expected = slow_msb_pos(v); #endif unsigned r, shift; r = (v > 0xFFFF) << 4; v >>= r; shift = (v > 0xFF) << 3; v >>= shift; r |= shift; shift = (v > 0xF) << 2; v >>= shift; r |= shift; shift = (v > 0x3) << 1; v >>= shift; r |= shift; r |= (v >> 1); SASSERT(r == expected); return r; } /** \brief Return the number of leading zeros bits in a nonzero unsigned integer. */ unsigned nlz_core(unsigned x) { SASSERT(x != 0); #ifdef __GNUC__ return __builtin_clz(x); #else return 31 - msb_pos(x); #endif } /** \brief Return the number of leading zero bits in data (a number of sz words). */ unsigned nlz(unsigned sz, unsigned const * data) { unsigned r = 0; unsigned i = sz; while (i > 0) { --i; unsigned d = data[i]; if (d == 0) r += 32; else return r + nlz_core(d); } return r; } /** \brief Return the number of trailing zeros in a nonzero unsigned number. */ unsigned ntz_core(unsigned x) { SASSERT(x != 0); #ifdef __GNUC__ return __builtin_ctz(x); #else float f = static_cast(x & static_cast(-static_cast(x))); unsigned u; SASSERT(sizeof(u) == sizeof(f)); memcpy(&u, &f, sizeof(u)); return (u >> 23) - 0x7f; #endif } /** \brief Return the number of trailing zero bits in data (a number of sz words). */ unsigned ntz(unsigned sz, unsigned const * data) { unsigned r = 0; for (unsigned i = 0; i < sz; i++) { unsigned d = data[i]; if (d == 0) r += 32; else return r + ntz_core(d); } return r; } /** \brief dst <- src Trucate if src_sz > dst_sz. Fill range [src_sz, dst_sz) of dst with zeros if dst_sz > src_sz. */ void copy(unsigned src_sz, unsigned const * src, unsigned dst_sz, unsigned * dst) { if (dst_sz >= src_sz) { unsigned i; for (i = 0; i < src_sz; i++) dst[i] = src[i]; for (; i < dst_sz; i++) dst[i] = 0; } else { SASSERT(dst_sz < src_sz); for (unsigned i = 0; i < dst_sz; i++) dst[i] = src[i]; } } /** \brief Return true if all words of data are zero. */ bool is_zero(unsigned sz, unsigned const * data) { for (unsigned i = 0; i < sz; i++) if (data[i]) return false; return true; } /** \brief Set all words of data to zero. */ void reset(unsigned sz, unsigned * data) { for (unsigned i = 0; i < sz; i++) data[i] = 0; } /** \brief dst <- src << k Store in dst the result of shifting src k bits to the left. The result is truncated by dst_sz. \pre src_sz != 0 \pre dst_sz != 0 */ void shl(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst) { SASSERT(src_sz != 0); SASSERT(dst_sz != 0); SASSERT(k != 0); unsigned word_shift = k / (8 * sizeof(unsigned)); unsigned bit_shift = k % (8 * sizeof(unsigned)); if (word_shift > 0) { unsigned j = src_sz; unsigned i = src_sz + word_shift; if (i > dst_sz) { if (j >= i - dst_sz) j -= (i - dst_sz); else j = 0; i = dst_sz; } else if (i < dst_sz) { for (unsigned r = i; r < dst_sz; r++) dst[r] = 0; } while (j > 0) { --j; --i; dst[i] = src[j]; } while (i > 0) { --i; dst[i] = 0; } if (bit_shift > 0) { unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift; unsigned prev = 0; for (unsigned i = word_shift; i < dst_sz; i++) { unsigned new_prev = (dst[i] >> comp_shift); dst[i] <<= bit_shift; dst[i] |= prev; prev = new_prev; } } } else { unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift; unsigned prev = 0; if (src_sz > dst_sz) src_sz = dst_sz; for (unsigned i = 0; i < src_sz; i++) { unsigned new_prev = (src[i] >> comp_shift); dst[i] = src[i]; dst[i] <<= bit_shift; dst[i] |= prev; prev = new_prev; } if (dst_sz > src_sz) { dst[src_sz] = prev; for (unsigned i = src_sz+1; i < dst_sz; i++) dst[i] = 0; } } } /** \brief dst <- src >> k Store in dst the result of shifting src k bits to the right. \pre dst must have size sz. \pre src_sz != 0 \pre dst_sz != 0 */ void shr(unsigned sz, unsigned const * src, unsigned k, unsigned * dst) { unsigned digit_shift = k / (8 * sizeof(unsigned)); if (digit_shift >= sz) { reset(sz, dst); return; } unsigned bit_shift = k % (8 * sizeof(unsigned)); unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift; unsigned new_sz = sz - digit_shift; if (new_sz < sz) { unsigned i = 0; unsigned j = digit_shift; if (bit_shift != 0) { for (; i < new_sz - 1; i++, j++) { dst[i] = src[j]; dst[i] >>= bit_shift; dst[i] |= (src[j+1] << comp_shift); } dst[i] = src[j]; dst[i] >>= bit_shift; } else { for (; i < new_sz; i++, j++) { dst[i] = src[j]; } } for (unsigned i = new_sz; i < sz; i++) dst[i] = 0; } else { SASSERT(new_sz == sz); SASSERT(bit_shift != 0); unsigned i = 0; for (; i < new_sz - 1; i++) { dst[i] = src[i]; dst[i] >>= bit_shift; dst[i] |= (src[i+1] << comp_shift); } dst[i] = src[i]; dst[i] >>= bit_shift; } } void shr(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst) { unsigned digit_shift = k / (8 * sizeof(unsigned)); if (digit_shift >= src_sz) { reset(dst_sz, dst); return; } unsigned bit_shift = k % (8 * sizeof(unsigned)); unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift; unsigned new_sz = src_sz - digit_shift; if (digit_shift > 0) { unsigned i = 0; unsigned j = digit_shift; if (bit_shift != 0) { unsigned sz = new_sz; if (new_sz > dst_sz) sz = dst_sz; for (; i < sz - 1; i++, j++) { dst[i] = src[j]; dst[i] >>= bit_shift; dst[i] |= (src[j+1] << comp_shift); } dst[i] = src[j]; dst[i] >>= bit_shift; if (new_sz > dst_sz) dst[i] |= (src[j+1] << comp_shift); } else { if (new_sz > dst_sz) new_sz = dst_sz; for (; i < new_sz; i++, j++) { dst[i] = src[j]; } } } else { SASSERT(new_sz == src_sz); SASSERT(bit_shift != 0); unsigned sz = new_sz; if (new_sz > dst_sz) sz = dst_sz; unsigned i = 0; for (; i < sz - 1; i++) { dst[i] = src[i]; dst[i] >>= bit_shift; dst[i] |= (src[i+1] << comp_shift); } dst[i] = src[i]; dst[i] >>= bit_shift; if (new_sz > dst_sz) dst[i] |= (src[i+1] << comp_shift); } for (unsigned i = new_sz; i < dst_sz; i++) dst[i] = 0; } /** \brief Return true if one of the first k bits of src is not zero. */ bool has_one_at_first_k_bits(unsigned sz, unsigned const * data, unsigned k) { SASSERT(sz != 0); unsigned word_sz = k / (8 * sizeof(unsigned)); if (word_sz > sz) word_sz = sz; for (unsigned i = 0; i < word_sz; i++) { if (data[i] != 0) return true; } if (word_sz < sz) { unsigned bit_sz = k % (8 * sizeof(unsigned)); unsigned mask = (1u << bit_sz) - 1; return (data[word_sz] & mask) != 0; } return false; } bool inc(unsigned sz, unsigned * data) { for (unsigned i = 0; i < sz; i++) { data[i]++; if (data[i] != 0) return true; // no overflow } return false; // overflow } bool dec(unsigned sz, unsigned * data) { for (unsigned i = 0; i < sz; i++) { data[i]--; if (data[i] != UINT_MAX) return true; // no underflow } return false; // underflow } bool lt(unsigned sz, unsigned * data1, unsigned * data2) { unsigned i = sz; while (i > 0) { --i; if (data1[i] < data2[i]) return true; if (data1[i] > data2[i]) return false; } return false; } bool add(unsigned sz, unsigned const * a, unsigned const * b, unsigned * c) { unsigned k = 0; for (unsigned j = 0; j < sz; j++) { unsigned r = a[j] + b[j]; bool c1 = r < a[j]; c[j] = r + k; bool c2 = c[j] < r; k = c1 | c2; } return k == 0; } z3-z3-4.4.1/src/util/bit_util.h000066400000000000000000000055261260446376700161650ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bit_util.h Abstract: Bit hacking utilities. Author: Leonardo de Moura (leonardo) 2012-09-11. Revision History: --*/ #ifndef BIT_UTIL_H_ #define BIT_UTIL_H_ /** \brief Return the position of the most significant (set) bit of a nonzero unsigned integer. */ unsigned msb_pos(unsigned v); /** \brief Return the number of leading zeros bits in a nonzero unsigned integer. */ unsigned nlz_core(unsigned x); /** \brief Return the number of leading zero bits in data (a number of sz words). */ unsigned nlz(unsigned sz, unsigned const * data); /** \brief Return the number of trailing zeros in a nonzero unsigned number. */ unsigned ntz_core(unsigned x); /** \brief Return the number of trailing zero bits in data (a number of sz words). */ unsigned ntz(unsigned sz, unsigned const * data); /** \brief dst <- src Trucate if src_sz > dst_sz. Fill range [src_sz, dst_sz) of dst with zeros if dst_sz > src_sz. */ void copy(unsigned src_sz, unsigned const * src, unsigned dst_sz, unsigned * dst); /** \brief Return true if all words of data are zero. */ bool is_zero(unsigned sz, unsigned const * data); /** \brief Set all words of data to zero. */ void reset(unsigned sz, unsigned * data); /** \brief dst <- src << k Store in dst the result of shifting src k bits to the left. The result is truncated by dst_sz. \pre src_sz != 0 \pre dst_sz != 0 */ void shl(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst); /** \brief dst <- src >> k Store in dst the result of shifting src k bits to the right. \pre dst must have size sz. \pre src_sz != 0 \pre dst_sz != 0 */ void shr(unsigned sz, unsigned const * src, unsigned k, unsigned * dst); /** \brief dst <- src >> k Store in dst the result of shifting src k bits to the right. Trucate if src_sz > dst_sz. Fill range [src_sz, dst_sz) of dst with zeros if dst_sz > src_sz. \pre src_sz != 0 \pre dst_sz != 0 */ void shr(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst); /** \brief Return true if one of the first k bits of src is not zero. */ bool has_one_at_first_k_bits(unsigned sz, unsigned const * data, unsigned k); /** \brief data <- data + 1 Return true if no overflow occurred. */ bool inc(unsigned sz, unsigned * data); /** \brief data <- data - 1 Return true if no underflow occurred. */ bool dec(unsigned sz, unsigned * data); /** \brief Return true if data1 < data2. Both must have the same size. */ bool lt(unsigned sz, unsigned * data1, unsigned * data2); /** \brief Store in c the a+b. This procedure assumes that a,b,c are vectors of size sz. Return false if a+b overflows. */ bool add(unsigned sz, unsigned const * a, unsigned const * b, unsigned * c); #endif z3-z3-4.4.1/src/util/bit_vector.cpp000066400000000000000000000146211260446376700170410ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bitvector.cpp Abstract: Simple bitvector implementation Author: Leonardo de Moura (leonardo) 2006-10-03. Revision History: --*/ #include #include"bit_vector.h" #include"trace.h" #define DEFAULT_CAPACITY 2 #define MK_MASK(_num_bits_) ((1U << _num_bits_) - 1) void bit_vector::expand_to(unsigned new_capacity) { if (m_data) { m_data = (unsigned*)memory::reallocate(m_data, new_capacity * sizeof(unsigned)); } else { m_data = alloc_svect(unsigned, new_capacity); } memset(m_data + m_capacity, 0, (new_capacity - m_capacity) * sizeof(unsigned)); m_capacity = new_capacity; } void bit_vector::resize(unsigned new_size, bool val) { if (new_size <= m_num_bits) { m_num_bits = new_size; return; } TRACE("bit_vector", tout << "expanding: " << new_size << " capacity: " << m_capacity << " num words: " << num_words(new_size) << "\n";); if (num_words(new_size) > m_capacity) { expand_to((num_words(new_size) * 3 + 1) >> 1); } unsigned bwidx = m_num_bits/32; unsigned ewidx = num_words(new_size); unsigned * begin = m_data + bwidx; unsigned pos = m_num_bits % 32; unsigned mask = MK_MASK(pos); int cval; if (val) { *begin |= ~mask; cval = ~0; } else { *begin &= mask; cval = 0; } TRACE("bit_vector", tout << "num_bits: " << m_num_bits << "\n"; tout << "bwidx: " << bwidx << "\n"; tout << "ewidx: " << ewidx << "\n"; tout << "pos: " << pos << "\n"; tout << "mask: " << std::hex << mask << "\n" << std::dec; tout << "cval: " << cval << "\n";); if (bwidx < ewidx) { memset(begin + 1, cval, (ewidx - bwidx - 1) * sizeof(unsigned)); } m_num_bits = new_size; } void bit_vector::shift_right(unsigned k) { if (k == 0) return; unsigned new_num_bits = m_num_bits + k; unsigned old_num_words = num_words(m_num_bits); unsigned new_num_words = num_words(new_num_bits); resize(m_num_bits + k, false); unsigned bit_shift = k % (8 * sizeof(unsigned)); unsigned word_shift = k / (8 * sizeof(unsigned)); if (word_shift > 0) { unsigned j = old_num_words; unsigned i = old_num_words + word_shift; while (j > 0) { --j; --i; m_data[i] = m_data[j]; } while (i > 0) { --i; m_data[i] = 0; } } if (bit_shift > 0) { DEBUG_CODE({ for (unsigned i = 0; i < word_shift; i++) { SASSERT(m_data[i] == 0); } }); unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift; unsigned prev = 0; for (unsigned i = word_shift; i < new_num_words; i++) { unsigned new_prev = (m_data[i] >> comp_shift); m_data[i] <<= bit_shift; m_data[i] |= prev; prev = new_prev; } } } bool bit_vector::operator==(bit_vector const & source) const { if (m_num_bits != source.m_num_bits) return false; unsigned n = num_words(); if (n == 0) return true; unsigned i; for (i = 0; i < n - 1; i++) { if (m_data[i] != source.m_data[i]) return false; } unsigned bit_rest = source.m_num_bits % 32; unsigned mask = MK_MASK(bit_rest); if (mask == 0) mask = UINT_MAX; return (m_data[i] & mask) == (source.m_data[i] & mask); } bit_vector & bit_vector::operator|=(bit_vector const & source) { if (size() < source.size()) resize(source.size(), false); unsigned n2 = source.num_words(); SASSERT(n2 <= num_words()); unsigned bit_rest = source.m_num_bits % 32; if (bit_rest == 0) { unsigned i = 0; for (i = 0; i < n2; i++) m_data[i] |= source.m_data[i]; } else { unsigned i = 0; for (i = 0; i < n2 - 1; i++) m_data[i] |= source.m_data[i]; unsigned mask = MK_MASK(bit_rest); m_data[i] |= source.m_data[i] & mask; } return *this; } bit_vector & bit_vector::operator&=(bit_vector const & source) { unsigned n1 = num_words(); unsigned n2 = source.num_words(); if (n1 == 0) return *this; if (n2 > n1) { for (unsigned i = 0; i < n1; i++) m_data[i] &= source.m_data[i]; } else { SASSERT(n2 <= n1); unsigned bit_rest = source.m_num_bits % 32; unsigned i = 0; if (bit_rest == 0) { for (i = 0; i < n2; i++) m_data[i] &= source.m_data[i]; } else { for (i = 0; i < n2 - 1; i++) m_data[i] &= source.m_data[i]; unsigned mask = MK_MASK(bit_rest); m_data[i] &= (source.m_data[i] & mask); } for (i = n2; i < n1; i++) m_data[i] = 0; } return *this; } void bit_vector::display(std::ostream & out) const { #if 1 unsigned i = m_num_bits; while (i > 0) { --i; if (get(i)) out << "1"; else out << "0"; } #else for (unsigned i = 0; i < m_num_bits; i++) { if (get(i)) out << "1"; else out << "0"; if ((i + 1) % 32 == 0) out << "\n"; } #endif } bool bit_vector::contains(bit_vector const& other) const { unsigned n = num_words(); if (n == 0) return true; for (unsigned i = 0; i < n - 1; ++i) { if ((m_data[i] & other.m_data[i]) != other.m_data[i]) return false; } unsigned bit_rest = m_num_bits % 32; unsigned mask = (1U << bit_rest) - 1; if (mask == 0) mask = UINT_MAX; unsigned other_data = other.m_data[n-1] & mask; return (m_data[n-1] & other_data) == other_data; } unsigned bit_vector::get_hash() const { return string_hash(reinterpret_cast(m_data), size()/8, 0); } bit_vector& bit_vector::neg() { unsigned n = num_words(); for (unsigned i = 0; i < n; ++i) { m_data[i] = ~m_data[i]; } return *this; } void fr_bit_vector::reset() { unsigned sz = size(); unsigned_vector::const_iterator it = m_one_idxs.begin(); unsigned_vector::const_iterator end = m_one_idxs.end(); for (; it != end; ++it) { unsigned idx = *it; if (idx < sz) unset(idx); } m_one_idxs.reset(); } z3-z3-4.4.1/src/util/bit_vector.h000066400000000000000000000136021260446376700165040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_vector.h Abstract: Simple bitvector implementation. Author: Leonardo de Moura (leonardo) 2006-10-03. Revision History: --*/ #ifndef BIT_VECTOR_H_ #define BIT_VECTOR_H_ #include #include"debug.h" #include"vector.h" #include"memory_manager.h" COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); #define BV_DEFAULT_CAPACITY 2 class bit_vector { protected: unsigned m_num_bits; unsigned m_capacity; //!< in words unsigned * m_data; static unsigned get_pos_mask(unsigned bit_idx) { return 1 << (bit_idx % 32); } static unsigned num_words(unsigned num_bits) { // return (num_bits % 32) == 0 ? (num_bits / 32) : ((num_bits / 32) + 1); return (num_bits + 31) / 32; } void expand_to(unsigned new_capacity); void expand() { expand_to(m_capacity == 0 ? BV_DEFAULT_CAPACITY : ((m_capacity * 3 + 1) >> 1)); } unsigned get_bit_word(unsigned bit_idx) const { SASSERT(bit_idx < size()); return m_data[bit_idx / 32]; } unsigned & get_bit_word(unsigned bit_idx) { SASSERT(bit_idx < size()); return m_data[bit_idx / 32]; } public: bit_vector(): m_num_bits(0), m_capacity(0), m_data(0) { } bit_vector(unsigned reserve_num_bits) : m_num_bits(0), m_capacity(num_words(reserve_num_bits)), m_data(alloc_svect(unsigned, m_capacity)) { memset(m_data, 0, m_capacity * sizeof(unsigned)); } bit_vector(bit_vector const & source): m_num_bits(source.m_num_bits), m_capacity(source.m_capacity), m_data(0) { if (source.m_data) { m_data = alloc_svect(unsigned, m_capacity); memcpy(m_data, source.m_data, m_capacity * sizeof(unsigned)); } } bit_vector(unsigned const * source, int num_bits): m_num_bits(num_bits), m_capacity(num_words(num_bits)), m_data(alloc_svect(unsigned, m_capacity)) { memcpy(m_data, source, m_capacity * sizeof(unsigned)); } ~bit_vector() { dealloc_svect(m_data); } void reset() { if (m_data) memset(m_data, 0, m_capacity * sizeof(unsigned)); m_num_bits = 0; } void swap(bit_vector & other) { std::swap(m_data, other.m_data); std::swap(m_num_bits, other.m_num_bits); std::swap(m_capacity, other.m_capacity); } // Increase the size of the bit_vector by k 0-bits. void shift_right(unsigned k); void fill0() { memset(m_data, 0, m_capacity * sizeof(unsigned)); } unsigned size() const { return m_num_bits; } bool empty() const { return m_num_bits == 0; } unsigned num_words() const { return num_words(m_num_bits); } unsigned get_word(unsigned word_idx) const { return m_data[word_idx]; } unsigned get_hash() const; bool get(unsigned bit_idx) const { SASSERT(bit_idx < size()); bool r = (get_bit_word(bit_idx) & get_pos_mask(bit_idx)) != 0; return r; } void set(unsigned bit_idx) { SASSERT(bit_idx < size()); get_bit_word(bit_idx) |= get_pos_mask(bit_idx); } void unset(unsigned bit_idx) { SASSERT(bit_idx < size()); get_bit_word(bit_idx) &= ~get_pos_mask(bit_idx); } void set(unsigned bit_idx, bool val) { SASSERT(bit_idx < size()); int _val = static_cast(val); get_bit_word(bit_idx) ^= (-_val ^ get_bit_word(bit_idx)) & get_pos_mask(bit_idx); } void push_back(bool val) { unsigned idx = m_num_bits; m_num_bits++; if (num_words(m_num_bits) > m_capacity) { expand(); } set(idx, val); } void pop_back() { SASSERT(m_num_bits > 0); m_num_bits--; } bool back() const { SASSERT(!empty()); bool r = get(m_num_bits - 1); return r; } void shrink(unsigned new_size) { SASSERT(new_size <= m_num_bits); m_num_bits = new_size; } void resize(unsigned new_size, bool val = false); void reserve(unsigned sz, bool val = false) { if (sz > size()) resize(sz, val); } bool operator==(bit_vector const & other) const; bool operator!=(bit_vector const & other) const { return !operator==(other); } bit_vector & operator=(bit_vector const & source) { m_num_bits = source.m_num_bits; if (!source.m_data) return *this; if (m_capacity < source.m_capacity) { dealloc_svect(m_data); m_data = alloc_svect(unsigned, source.m_capacity); m_capacity = source.m_capacity; } memcpy(m_data, source.m_data, source.m_capacity * sizeof(unsigned)); return *this; } bit_vector & operator|=(bit_vector const & source); bit_vector & operator&=(bit_vector const & source); bit_vector & neg(); void display(std::ostream & out) const; bool contains(const bit_vector & other) const; }; inline std::ostream & operator<<(std::ostream & out, bit_vector const & b) { b.display(out); return out; } /** \brief Bitvector class with fast reset. This class should be used if the reset is frequently called. */ class fr_bit_vector : private bit_vector { unsigned_vector m_one_idxs; public: void reset(); void fill0() { bit_vector::fill0(); m_one_idxs.reset(); } void set(unsigned idx) { m_one_idxs.push_back(idx); bit_vector::set(idx); } void set(unsigned idx, bool val) { if (val) m_one_idxs.push_back(idx); bit_vector::set(idx, val); } void push_back(bool val) { if (val) m_one_idxs.push_back(size()); bit_vector::push_back(val); } }; #endif /* BIT_VECTOR_H_ */ z3-z3-4.4.1/src/util/buffer.h000066400000000000000000000130101260446376700156060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: buffer.h Abstract: Author: Leonardo de Moura (leonardo) 2006-10-16. Revision History: --*/ #ifndef BUFFER_H_ #define BUFFER_H_ #include #include"memory_manager.h" template class buffer { protected: T * m_buffer; unsigned m_pos; unsigned m_capacity; char m_initial_buffer[INITIAL_SIZE * sizeof(T)]; void free_memory() { if (m_buffer != reinterpret_cast(m_initial_buffer)) { memory::deallocate(m_buffer); } } void expand() { unsigned new_capacity = m_capacity << 1; T * new_buffer = reinterpret_cast(memory::allocate(sizeof(T) * new_capacity)); memcpy(new_buffer, m_buffer, m_pos * sizeof(T)); free_memory(); m_buffer = new_buffer; m_capacity = new_capacity; } void destroy_elements() { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { it->~T(); } } void destroy() { if (CallDestructors) { destroy_elements(); } free_memory(); } public: typedef T data; typedef T * iterator; typedef const T * const_iterator; buffer(): m_buffer(reinterpret_cast(m_initial_buffer)), m_pos(0), m_capacity(INITIAL_SIZE) { } buffer(const buffer & source): m_buffer(reinterpret_cast(m_initial_buffer)), m_pos(0), m_capacity(INITIAL_SIZE) { unsigned sz = source.size(); for(unsigned i = 0; i < sz; i++) { push_back(source.m_buffer[i]); } } buffer(unsigned sz, const T & elem): m_buffer(reinterpret_cast(m_initial_buffer)), m_pos(0), m_capacity(INITIAL_SIZE) { for (unsigned i = 0; i < sz; i++) { push_back(elem); } SASSERT(size() == sz); } ~buffer() { destroy(); } void reset() { if (CallDestructors) { destroy_elements(); } m_pos = 0; } void finalize() { destroy(); m_buffer = reinterpret_cast(m_initial_buffer); m_pos = 0; m_capacity = INITIAL_SIZE; } unsigned size() const { return m_pos; } bool empty() const { return m_pos == 0; } iterator begin() { return m_buffer; } iterator end() { return m_buffer + size(); } void set_end(iterator it) { m_pos = static_cast(it - m_buffer); if (CallDestructors) { iterator e = end(); for (; it != e; ++it) { it->~T(); } } } const_iterator begin() const { return m_buffer; } const_iterator end() const { return m_buffer + size(); } void push_back(const T & elem) { if (m_pos >= m_capacity) expand(); new (m_buffer + m_pos) T(elem); m_pos++; } void pop_back() { if (CallDestructors) { back().~T(); } m_pos--; } const T & back() const { SASSERT(!empty()); SASSERT(m_pos > 0); return m_buffer[m_pos - 1]; } T & back() { SASSERT(!empty()); SASSERT(m_pos > 0); return m_buffer[m_pos - 1]; } T * c_ptr() const { return m_buffer; } void append(unsigned n, T const * elems) { for (unsigned i = 0; i < n; i++) { push_back(elems[i]); } } void append(const buffer& source) { append(source.size(), source.c_ptr()); } T & operator[](unsigned idx) { SASSERT(idx < size()); return m_buffer[idx]; } const T & operator[](unsigned idx) const { SASSERT(idx < size()); return m_buffer[idx]; } T & get(unsigned idx) { SASSERT(idx < size()); return m_buffer[idx]; } const T & get(unsigned idx) const { SASSERT(idx < size()); return m_buffer[idx]; } void set(unsigned idx, T const & val) { SASSERT(idx < size()); m_buffer[idx] = val; } void resize(unsigned nsz, const T & elem=T()) { unsigned sz = size(); if (nsz > sz) { for (unsigned i = sz; i < nsz; i++) { push_back(elem); } } else if (nsz < sz) { for (unsigned i = nsz; i < sz; i++) { pop_back(); } } SASSERT(size() == nsz); } void shrink(unsigned nsz) { unsigned sz = size(); SASSERT(nsz <= sz); for (unsigned i = nsz; i < sz; i++) pop_back(); SASSERT(size() == nsz); } buffer & operator=(buffer const & other) { if (this == &other) return *this; reset(); append(other); return *this; } }; template class ptr_buffer : public buffer { public: void append(unsigned n, T * const * elems) { for (unsigned i = 0; i < n; i++) { this->push_back(elems[i]); } } }; template class sbuffer : public buffer { public: sbuffer(): buffer() {} sbuffer(unsigned sz, const T& elem) : buffer(sz,elem) {} }; #endif /* BUFFER_H_ */ z3-z3-4.4.1/src/util/cancel_eh.h000066400000000000000000000011621260446376700162430ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cancel_eh.h Abstract: Template for implementing simple event handler that just invokes cancel method. Author: Leonardo de Moura (leonardo) 2011-04-27. Revision History: --*/ #ifndef CANCEL_EH_H_ #define CANCEL_EH_H_ #include"event_handler.h" /** \brief Generic event handler for invoking cancel method. */ template class cancel_eh : public event_handler { T & m_obj; public: cancel_eh(T & o):m_obj(o) {} ~cancel_eh() { m_obj.reset_cancel(); } virtual void operator()() { m_obj.cancel(); } }; #endif z3-z3-4.4.1/src/util/chashtable.h000066400000000000000000000474721260446376700164560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: chashtable.h Abstract: Hashtable with chaining. The performance of the hashtable in hashtable.h deteriorates if there is a huge number of deletions. In this case, the hashtable starts to contain many cells marked as deleted, and insertion/deletion start to suffer. The hashtable defined in this class addresses this problem by using chaining. Of course, there is the cost of storing the link to the next cell. Author: Leonardo de Moura (leonardo) 2011-04-14. Revision History: --*/ #ifndef CHASHTABLE_H_ #define CHASHTABLE_H_ #include"memory_manager.h" #include"debug.h" #include"trace.h" #ifdef Z3DEBUG #include"hashtable.h" #endif #define CH_STATISTICS #ifdef CH_STATISTICS #define CHS_CODE(CODE) { CODE } #else #define CHS_CODE(CODE) #endif template class chashtable : private HashProc, private EqProc { public: static const unsigned default_init_slots = 8; static const unsigned default_init_cellar = 2; protected: struct cell { cell * m_next; T m_data; cell():m_next(reinterpret_cast(1)) {} bool is_free() const { return m_next == reinterpret_cast(1); } void mark_free() { m_next = reinterpret_cast(1); } }; cell * m_table; // array of cells. unsigned m_capacity; // size of the array of cells. unsigned m_init_slots; unsigned m_init_cellar; unsigned m_slots; // m_slots < m_capacity, and m_slots is a power of two, the cells [m_slots, m_capacity) are used for chaining. unsigned m_used_slots; // m_used_slots <= m_slots (number of used slots). unsigned m_size; // number of occupied cells. #ifdef CH_STATISTICS unsigned m_collisions; #endif cell * m_next_cell; cell * m_free_cell; unsigned get_hash(T const & d) const { return HashProc::operator()(d); } bool equals(T const & e1, T const & e2) const { return EqProc::operator()(e1, e2); } static cell * alloc_table(unsigned sz) { return alloc_vect(sz); } void delete_table() { dealloc_vect(m_table, m_capacity); } // Return the next free cell in the cellar, and the number of used slots // Return 0 if the cellar is too small (unlikely but it might happen with a bad hash) cell * copy_table(cell * source, unsigned source_slots, unsigned source_capacity, cell * target, unsigned target_slots, unsigned target_capacity, unsigned & used_slots) { TRACE("chashtable", tout << "copy_table...\n";); SASSERT(target_slots >= source_slots); SASSERT(target_capacity >= source_capacity); unsigned target_mask = target_slots - 1; used_slots = 0; cell * source_end = source + source_slots; cell * target_cellar = target + target_slots; cell * target_end = target + target_capacity; for (cell * source_it = source; source_it != source_end; ++source_it) { if (!source_it->is_free()) { cell * list_it = source_it; do { unsigned h = get_hash(list_it->m_data); unsigned idx = h & target_mask; cell * target_it = target + idx; SASSERT(target_it >= target); SASSERT(target_it < target + target_slots); if (target_it->is_free()) { target_it->m_data = list_it->m_data; target_it->m_next = 0; used_slots++; } else { SASSERT((get_hash(target_it->m_data) & target_mask) == idx); if (target_cellar == target_end) return 0; // the cellar is too small... SASSERT(target_cellar >= target + target_slots); SASSERT(target_cellar < target_end); *target_cellar = *target_it; target_it->m_data = list_it->m_data; target_it->m_next = target_cellar; target_cellar++; } SASSERT(!target_it->is_free()); list_it = list_it->m_next; } while (list_it != 0); } } #if 0 TRACE("chashtable", for (unsigned i = 0; i < source_capacity; i++) { tout << i << ":["; if (source[i].m_next == 0) tout << "null"; else if (source[i].m_next == reinterpret_cast(1)) tout << "X"; else tout << (source[i].m_next - source); tout << ", " << source[i].m_data << "]\n"; } tout << "\n"; for (unsigned i = 0; i < target_capacity; i++) { tout << i << ":["; if (target[i].m_next == 0) tout << "null"; else if (target[i].m_next == reinterpret_cast(1)) tout << "X"; else tout << (target[i].m_next - target); tout << ", " << target[i].m_data << "]\n"; } tout << "\n";); #endif return target_cellar; } void expand_table() { unsigned curr_cellar = (m_capacity - m_slots); unsigned new_slots = m_slots * 2; unsigned new_cellar = curr_cellar * 2; while (true) { unsigned new_capacity = new_slots + new_cellar; cell * new_table = alloc_table(new_capacity); cell * next_cell = copy_table(m_table, m_slots, m_capacity, new_table, new_slots, new_capacity, m_used_slots); if (next_cell != 0) { delete_table(); m_table = new_table; m_capacity = new_capacity; m_slots = new_slots; m_next_cell = next_cell; m_free_cell = 0; CASSERT("chashtable", check_invariant()); return; } dealloc_vect(new_table, new_capacity); new_cellar *= 2; } } bool has_free_cells() const { return m_free_cell != 0 || m_next_cell < m_table + m_capacity; } cell * get_free_cell() { if (m_free_cell != 0) { cell * c = m_free_cell; m_free_cell = c->m_next; return c; } else { cell * c = m_next_cell; m_next_cell++; return c; } } void recycle_cell(cell * c) { // c is in the cellar SASSERT(c >= m_table + m_slots); SASSERT(c < m_table + m_capacity); c->m_next = m_free_cell; m_free_cell = c; } void init(unsigned slots, unsigned cellar) { m_capacity = slots + cellar; m_table = alloc_table(m_capacity); m_slots = slots; m_used_slots = 0; m_size = 0; m_next_cell = m_table + slots; m_free_cell = 0; } public: chashtable(HashProc const & h = HashProc(), EqProc const & e = EqProc(), unsigned init_slots = default_init_slots, unsigned init_cellar = default_init_cellar): HashProc(h), EqProc(e) { SASSERT(is_power_of_two(init_slots)); SASSERT(init_cellar > 0); m_init_slots = init_slots; m_init_cellar = init_cellar; init(m_init_slots, m_init_cellar); CHS_CODE(m_collisions = 0;); } ~chashtable() { #if 0 cell * it = m_table; cell * end = m_table + m_slots; verbose_stream() << "[chashtable] free slots: "; for (; it != end; ++it) { if (it->is_free()) verbose_stream() << (it - m_table) << " "; } verbose_stream() << "\n"; #endif delete_table(); } void reset() { if (m_size == 0) return; finalize(); } void finalize() { delete_table(); init(m_init_slots, m_init_cellar); } bool empty() const { return m_size == 0; } unsigned size() const { return m_size; } unsigned capacity() const { return m_capacity; } unsigned used_slots() const { return m_used_slots; } void insert(T const & d) { if (!has_free_cells()) expand_table(); unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) { m_size++; m_used_slots++; c->m_data = d; c->m_next = 0; CASSERT("chashtable_bug", check_invariant()); return; } else { cell * it = c; do { if (equals(it->m_data, d)) { // already there it->m_data = d; CASSERT("chashtable_bug", check_invariant()); return; } CHS_CODE(m_collisions++;); it = it->m_next; } while (it != 0); // d is not in the table. m_size++; cell * new_c = get_free_cell(); *new_c = *c; c->m_data = d; c->m_next = new_c; CASSERT("chashtable_bug", check_invariant()); return; } } T & insert_if_not_there(T const & d) { if (!has_free_cells()) expand_table(); unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) { m_size++; m_used_slots++; c->m_data = d; c->m_next = 0; CASSERT("chashtable_bug", check_invariant()); return c->m_data; } else { cell * it = c; do { if (equals(it->m_data, d)) { // already there CASSERT("chashtable_bug", check_invariant()); return it->m_data; } CHS_CODE(m_collisions++;); it = it->m_next; } while (it != 0); // d is not in the table. m_size++; cell * new_c = get_free_cell(); *new_c = *c; c->m_data = d; c->m_next = new_c; CASSERT("chashtable_bug", check_invariant()); return c->m_data; } } bool insert_if_not_there2(T const & d) { if (!has_free_cells()) expand_table(); unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) { m_size++; m_used_slots++; c->m_data = d; c->m_next = 0; CASSERT("chashtable_bug", check_invariant()); return true; } else { cell * it = c; do { if (equals(it->m_data, d)) { // already there CASSERT("chashtable_bug", check_invariant()); return false; } CHS_CODE(m_collisions++;); it = it->m_next; } while (it != 0); // d is not in the table. m_size++; cell * new_c = get_free_cell(); *new_c = *c; c->m_data = d; c->m_next = new_c; CASSERT("chashtable_bug", check_invariant()); return true; } } bool contains(T const & d) const { unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) return false; do { if (equals(c->m_data, d)) { return true; } CHS_CODE(const_cast(this)->m_collisions++;); c = c->m_next; } while (c != 0); return false; } T * find_core(T const & d) const { unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) return 0; do { if (equals(c->m_data, d)) { return &(c->m_data); } CHS_CODE(const_cast(this)->m_collisions++;); c = c->m_next; } while (c != 0); return 0; } bool find(T const & d, T & r) { unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) return false; do { if (equals(c->m_data, d)) { r = c->m_data; return true; } CHS_CODE(const_cast(this)->m_collisions++;); c = c->m_next; } while (c != 0); return false; } void erase(T const & d) { unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) return; cell * prev = 0; do { if (equals(c->m_data, d)) { 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); } CASSERT("chashtable_bug", check_invariant()); return; } CHS_CODE(m_collisions++;); prev = c; c = c->m_next; } while (c != 0); } class iterator { cell * m_it; cell * m_end; cell * m_list_it; void move_to_used() { while (m_it != m_end) { if (!m_it->is_free()) { m_list_it = m_it; return; } m_it++; } m_list_it = 0; } public: iterator(cell * start, cell * end): m_it(start), m_end(end) { move_to_used(); } iterator():m_it(0), m_end(0), m_list_it(0) {} T & operator*() { return m_list_it->m_data; } T const & operator*() const { return m_list_it->m_data; } T const * operator->() const { return &(operator*()); } T * operator->() { return &(operator*()); } iterator & operator++() { m_list_it = m_list_it->m_next; if (m_list_it == 0) { m_it++; move_to_used(); } return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const & it) const { return m_list_it == it.m_list_it; } bool operator!=(iterator const & it) const { return m_list_it != it.m_list_it; } }; iterator begin() const { return iterator(m_table, m_table + m_slots); } iterator end() const { return iterator(); } void swap(chashtable & other) { std::swap(m_table, other.m_table); std::swap(m_capacity, other.m_capacity); std::swap(m_init_slots, other.m_init_slots); std::swap(m_init_cellar, other.m_init_cellar); std::swap(m_slots, other.m_slots); std::swap(m_used_slots, other.m_used_slots); std::swap(m_size, other.m_size); #ifdef CH_STATISTICS std::swap(m_collisions, other.m_collisions); #endif std::swap(m_next_cell, other.m_next_cell); std::swap(m_free_cell, other.m_free_cell); } unsigned collisions() const { #ifdef CH_STATISTICS return m_collisions; #else return 0; #endif } #ifdef Z3DEBUG bool check_invariant() const { ptr_addr_hashtable visited; unsigned sz = 0; cell * _end = m_table + m_slots; for (cell * it = m_table; it != _end; ++it) { if (!it->is_free()) { cell * list_it = it; while (list_it != 0) { sz++; SASSERT(!visited.contains(list_it)); visited.insert(list_it); list_it = list_it->m_next; } } } SASSERT(m_size == sz); return true; } #endif }; template class cmap { public: struct key_value { Key m_key; Value m_value; key_value() {} key_value(Key const & k):m_key(k) {} key_value(Key const & k, Value const & v):m_key(k), m_value(v) {} }; protected: struct key_value_hash_proc : private HashProc { key_value_hash_proc(HashProc const & p):HashProc(p) {} unsigned operator()(key_value const & d) const { return HashProc::operator()(d.m_key); } }; struct key_value_eq_proc : private EqProc { key_value_eq_proc(EqProc const & p):EqProc(p) {} bool operator()(key_value const & d1, key_value const & d2) const { return EqProc::operator()(d1.m_key, d2.m_key); } }; typedef chashtable table; table m_table; public: cmap(HashProc const & h = HashProc(), EqProc const & e = EqProc(), unsigned init_slots = table::default_init_slots, unsigned init_cellar = table::default_init_cellar): m_table(key_value_hash_proc(h), key_value_eq_proc(e), init_slots, init_cellar) { } typedef typename table::iterator iterator; void reset() { m_table.reset(); } void finalize() { m_table.finalize(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } unsigned used_slots() const { return m_table.used_slots(); } unsigned collisions() const { return m_table.collisions(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(Key const & k, Value const & v) { return m_table.insert(key_value(k, v)); } key_value & insert_if_not_there(Key const & k, Value const & v) { return m_table.insert_if_not_there(key_value(k, v)); } bool contains(Key const & k) const { return m_table.contains(key_value(k)); } key_value * find_core(Key const & k) const { return m_table.find_core(key_value(k)); } bool find(Key const & k, Value & v) const { key_value * e = m_table.find_core(key_value(k)); if (e == 0) return false; v = e->m_value; return true; } void erase(Key const & k) { m_table.erase(key_value(k)); } }; #endif z3-z3-4.4.1/src/util/checked_int64.h000066400000000000000000000152351260446376700167620ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: checked_int64.h Abstract: A class for wrapping checked (and unchecked) int64 operations. Note: the mpfx class defines a more general class of fixed-point operations. A tradeoff is that it relies on a manager. This class several of the most common operations from rational, so it can be swapped for rational. Author: Nikolaj Bjorner (nbjorner) 2013-03-25. Revision History: --*/ #ifndef CHECKED_INT64_H_ #define CHECKED_INT64_H_ #include"z3_exception.h" #include"rational.h" template class checked_int64 { int64 m_value; typedef checked_int64 ci; rational r64(int64 i) { return rational(i, rational::i64()); } public: checked_int64(): m_value(0) {} checked_int64(int64 v): m_value(v) {} checked_int64(checked_int64 const& other) { m_value = other.m_value; } class overflow_exception : public z3_exception { virtual char const * msg() const { return "checked_int64 overflow/underflow";} }; bool is_zero() const { return m_value == 0; } bool is_pos() const { return m_value > 0; } bool is_neg() const { return m_value < 0; } bool is_one() const { return m_value == 1; } bool is_minus_one() const { return m_value == -1; } bool is_nonneg() const { return m_value >= 0; } bool is_nonpos() const { return m_value <= 0; } bool is_even() const { return 0 == (m_value ^ 0x1); } static checked_int64 zero() { return ci(0); } static checked_int64 one() { return ci(1); } static checked_int64 minus_one() { return ci(-1);} int64 get_int64() const { return m_value; } checked_int64 abs() const { if (m_value >= 0) { return *this; } if (CHECK && m_value == INT64_MIN) { throw overflow_exception(); } return ci(-m_value); } checked_int64& neg() { if (CHECK && m_value == INT64_MIN) { throw overflow_exception(); } m_value = -m_value; return *this; } unsigned hash() const { return static_cast(m_value); } struct hash_proc { unsigned operator()(checked_int64 const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(checked_int64 const& r1, checked_int64 const& r2) const { return r1 == r2; } }; friend inline std::ostream& operator<<(std::ostream& out, checked_int64 const& i) { return out << i.m_value; } friend inline bool operator==(checked_int64 const& a, checked_int64 const& b) { return a.m_value == b.m_value; } friend inline bool operator<(checked_int64 const& a, checked_int64 const& b) { return a.m_value < b.m_value; } checked_int64 & operator++() { if (CHECK && INT64_MAX == m_value) { throw overflow_exception(); } ++m_value; return *this; } const checked_int64 operator++(int) { checked_int64 tmp(*this); ++(*this); return tmp; } checked_int64 & operator--() { if (CHECK && m_value == INT64_MIN) { throw overflow_exception(); } --m_value; return *this; } const checked_int64 operator--(int) { checked_int64 tmp(*this); --(*this); return tmp; } checked_int64& operator+=(checked_int64 const& other) { if (CHECK && m_value > 0 && other.m_value > 0 && (m_value > INT_MAX || other.m_value > INT_MAX)) { rational r(r64(m_value) + r64(other.m_value)); if (!r.is_int64()) { throw overflow_exception(); } m_value = r.get_int64(); return *this; } if (CHECK && m_value < 0 && other.m_value < 0 && (m_value < INT_MIN || other.m_value < INT_MIN)) { rational r(r64(m_value) + r64(other.m_value)); if (!r.is_int64()) { throw overflow_exception(); } m_value = r.get_int64(); return *this; } m_value += other.m_value; return *this; } checked_int64& operator-=(checked_int64 const& other) { if (CHECK && m_value > 0 && other.m_value < 0 && (m_value > INT_MAX || other.m_value < INT_MIN)) { rational r(r64(m_value) - r64(other.m_value)); if (!r.is_int64()) { throw overflow_exception(); } m_value = r.get_int64(); return *this; } if (CHECK && m_value < 0 && other.m_value > 0 && (m_value < INT_MIN || other.m_value > INT_MAX)) { rational r(r64(m_value) - r64(other.m_value)); if (!r.is_int64()) { throw overflow_exception(); } m_value = r.get_int64(); return *this; } m_value -= other.m_value; return *this; } checked_int64& operator*=(checked_int64 const& other) { if (CHECK) { rational r(r64(m_value) * r64(other.m_value)); if (!r.is_int64()) { throw overflow_exception(); } m_value = r.get_int64(); } else { m_value *= other.m_value; } return *this; } friend inline checked_int64 abs(checked_int64 const& i) { return i.abs(); } }; template inline bool operator!=(checked_int64 const & i1, checked_int64 const & i2) { return !operator==(i1, i2); } template inline bool operator>(checked_int64 const & i1, checked_int64 const & i2) { return operator<(i2, i1); } template inline bool operator<=(checked_int64 const & i1, checked_int64 const & i2) { return !operator>(i1, i2); } template inline bool operator>=(checked_int64 const & i1, checked_int64 const & i2) { return !operator<(i1, i2); } template inline checked_int64 operator-(checked_int64 const& i) { checked_int64 result(i); return result.neg(); } template inline checked_int64 operator+(checked_int64 const& a, checked_int64 const& b) { checked_int64 result(a); result += b; return result; } template inline checked_int64 operator-(checked_int64 const& a, checked_int64 const& b) { checked_int64 result(a); result -= b; return result; } template inline checked_int64 operator*(checked_int64 const& a, checked_int64 const& b) { checked_int64 result(a); result *= b; return result; } #endif z3-z3-4.4.1/src/util/cmd_context_types.cpp000066400000000000000000000024471260446376700204370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_context_types.h Abstract: Author: Leonardo (leonardo) 2011-04-22 Notes: --*/ #include #include"cmd_context_types.h" std::ostream & operator<<(std::ostream & out, cmd_arg_kind k) { switch (k) { case CPK_UINT: out << "unsigned int"; break; case CPK_BOOL: out << "bool"; break; case CPK_DOUBLE: out << "double"; break; case CPK_NUMERAL: out << "rational"; break; case CPK_DECIMAL: out << "rational"; break; case CPK_STRING: out << "string"; break; case CPK_OPTION_VALUE: out << "optional-value"; break; case CPK_KEYWORD: out << "keyword"; break; case CPK_SYMBOL: out << "symbol"; break; case CPK_SYMBOL_LIST: out << "symbol-list"; break; case CPK_SORT: out << "sort"; break; case CPK_SORT_LIST: out << "sort-list"; break; case CPK_EXPR: out << "expression"; break; case CPK_EXPR_LIST: out << "expression-list"; break; case CPK_FUNC_DECL: out << "declaration"; break; case CPK_FUNC_DECL_LIST: out << "declaration-list"; break; case CPK_SORTED_VAR: out << "sorted-variable"; break; case CPK_SORTED_VAR_LIST: out << "sorted-variable-list"; break; case CPK_SEXPR: out << "s-expression"; break; default: out << "unknown"; break; } return out; } z3-z3-4.4.1/src/util/cmd_context_types.h000066400000000000000000000101651260446376700201000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_context_types.h Abstract: Author: Leonardo (leonardo) 2011-04-22 Notes: --*/ #ifndef CMD_CONTEXT_TYPES_H_ #define CMD_CONTEXT_TYPES_H_ #include"symbol.h" #include"z3_exception.h" #include class rational; class expr; class sort; class func_decl; class sexpr; class cmd_context; enum cmd_arg_kind { CPK_UINT, CPK_BOOL, CPK_DOUBLE, CPK_NUMERAL, CPK_DECIMAL, CPK_STRING, CPK_OPTION_VALUE, CPK_KEYWORD, CPK_SYMBOL, CPK_SYMBOL_LIST, CPK_SORT, CPK_SORT_LIST, CPK_EXPR, CPK_EXPR_LIST, CPK_FUNC_DECL, CPK_FUNC_DECL_LIST, CPK_SORTED_VAR, CPK_SORTED_VAR_LIST, CPK_SEXPR, CPK_INVALID }; std::ostream & operator<<(std::ostream & out, cmd_arg_kind k); typedef cmd_arg_kind param_kind; class cmd_exception : public default_exception { int m_line; int m_pos; std::string compose(char const* msg, symbol const& s) { std::stringstream stm; stm << msg << s; return stm.str(); } public: cmd_exception(char const * msg):default_exception(msg), m_line(-1), m_pos(-1) {} cmd_exception(std::string const & msg):default_exception(msg), m_line(-1), m_pos(-1) {} cmd_exception(std::string const & msg, int line, int pos):default_exception(msg), m_line(line), m_pos(pos) {} cmd_exception(char const * msg, symbol const & s): default_exception(compose(msg,s)),m_line(-1),m_pos(-1) {} cmd_exception(char const * msg, symbol const & s, int line, int pos): default_exception(compose(msg,s)),m_line(line),m_pos(pos) {} bool has_pos() const { return m_line >= 0; } int line() const { SASSERT(has_pos()); return m_line; } int pos() const { SASSERT(has_pos()); return m_pos; } }; class stop_parser_exception { }; typedef std::pair sorted_var; // A command may have a variable number of arguments. #define VAR_ARITY UINT_MAX /** \brief Command abstract class. Commands may have variable number of argumets. */ class cmd { symbol m_name; public: cmd(char const * n):m_name(n) {} virtual ~cmd() {} virtual void reset(cmd_context & ctx) {} virtual void finalize(cmd_context & ctx) {} virtual symbol get_name() const { return m_name; } virtual char const * get_usage() const { return 0; } virtual char const * get_descr(cmd_context & ctx) const { return 0; } virtual unsigned get_arity() const { return 0; } // command invocation virtual void prepare(cmd_context & ctx) {} virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { UNREACHABLE(); return CPK_UINT; } virtual void set_next_arg(cmd_context & ctx, unsigned val) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, bool val) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, rational const & val) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, double val) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, char const * val) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, symbol const & s) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, unsigned num, symbol const * slist) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, sort * s) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, expr * t) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, unsigned num, expr * const * tlist) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, sorted_var const & sv) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, unsigned num, sorted_var const * svlist) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, func_decl * f) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * flist) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, sexpr * n) { UNREACHABLE(); } virtual void failure_cleanup(cmd_context & ctx) {} virtual void execute(cmd_context & ctx) {} }; #endif z3-z3-4.4.1/src/util/common_msgs.cpp000066400000000000000000000013101260446376700172110ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: common_msgs.cpp Abstract: Common messages used in Z3. Author: Leonardo (leonardo) 2012-10-25 Notes: --*/ #include"common_msgs.h" char const * common_msgs::g_canceled_msg = "canceled"; char const * common_msgs::g_max_memory_msg = "max. memory exceeded"; char const * common_msgs::g_max_scopes_msg = "max. scopes exceeded"; char const * common_msgs::g_max_steps_msg = "max. steps exceeded"; char const * common_msgs::g_max_frames_msg = "max. frames exceeded"; char const * common_msgs::g_no_proofs_msg = "component does not support proof generation"; char const * common_msgs::g_max_resource_msg = "max. resource limit exceeded"; z3-z3-4.4.1/src/util/common_msgs.h000066400000000000000000000017141260446376700166660ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: common_msgs.h Abstract: Common messages used in Z3. Author: Leonardo (leonardo) 2012-10-25 Notes: --*/ #ifndef COMMON_MSGS_H_ #define COMMON_MSGS_H_ class common_msgs { public: static char const * g_canceled_msg; static char const * g_max_memory_msg; static char const * g_max_scopes_msg; static char const * g_max_steps_msg; static char const * g_max_frames_msg; static char const * g_no_proofs_msg; static char const * g_max_resource_msg; }; #define Z3_CANCELED_MSG common_msgs::g_canceled_msg #define Z3_MAX_MEMORY_MSG common_msgs::g_max_memory_msg #define Z3_MAX_SCOPES_MSG common_msgs::g_max_scopes_msg #define Z3_MAX_STEPS_MSG common_msgs::g_max_steps_msg #define Z3_MAX_FRAMES_MSG common_msgs::g_max_frames_msg #define Z3_NO_PROOF_GEN_MSG common_msgs::g_no_proofs_msg #define Z3_MAX_RESOURCE_MSG common_msgs::g_max_resource_msg #endif z3-z3-4.4.1/src/util/cooperate.cpp000066400000000000000000000037421260446376700166640ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cooperate.cpp Abstract: Cooperation support Author: Leonardo (leonardo) 2011-05-17 Notes: --*/ #include"cooperate.h" #include"trace.h" #include"debug.h" #include"z3_omp.h" struct cooperation_lock { omp_nest_lock_t m_lock; char const * m_task; volatile int m_owner_thread; cooperation_lock() { omp_set_nested(1); omp_init_nest_lock(&m_lock); m_task = 0; m_owner_thread = -1; } ~cooperation_lock() { omp_destroy_nest_lock(&m_lock); } }; cooperation_lock g_lock; bool cooperation_ctx::g_cooperate = false; void cooperation_ctx::checkpoint(char const * task) { SASSERT(cooperation_ctx::enabled()); int tid = omp_get_thread_num(); if (g_lock.m_owner_thread == tid) { g_lock.m_owner_thread = -1; omp_unset_nest_lock(&(g_lock.m_lock)); } // this critical section is used to force the owner thread to give a chance to // another thread to get the lock #pragma omp critical (z3_cooperate) { omp_set_nest_lock(&(g_lock.m_lock)); TRACE("cooperate_detail", tout << task << ", tid: " << tid << "\n";); CTRACE("cooperate", g_lock.m_task != task, tout << "moving to task: " << task << "\n";); g_lock.m_owner_thread = tid; } } cooperation_section::cooperation_section() { SASSERT(!cooperation_ctx::enabled()); SASSERT(!omp_in_parallel()); cooperation_ctx::g_cooperate = true; } cooperation_section::~cooperation_section() { SASSERT(cooperation_ctx::enabled()); cooperation_ctx::g_cooperate = false; } init_task::init_task(char const * task) { SASSERT(cooperation_ctx::enabled()); SASSERT(omp_in_parallel()); cooperation_ctx::checkpoint(task); } init_task::~init_task() { int tid = omp_get_thread_num(); if (g_lock.m_owner_thread == tid) { g_lock.m_owner_thread = -1; omp_unset_nest_lock(&(g_lock.m_lock)); } } z3-z3-4.4.1/src/util/cooperate.h000066400000000000000000000015501260446376700163240ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cooperate.h Abstract: Cooperation support Author: Leonardo (leonardo) 2011-05-17 Notes: --*/ #ifndef COOPERATE_H_ #define COOPERATE_H_ class cooperation_section; class cooperation_ctx { friend class cooperation_section; static bool g_cooperate; public: static bool enabled() { return g_cooperate; } static void checkpoint(char const * task); }; inline void cooperate(char const * task) { if (cooperation_ctx::enabled()) cooperation_ctx::checkpoint(task); } // must be declared before "#pragma parallel" to enable cooperation class cooperation_section { public: cooperation_section(); ~cooperation_section(); }; // must be first declaration inside "#pragma parallel for" class init_task { public: init_task(char const * task); ~init_task(); }; #endif z3-z3-4.4.1/src/util/critical_flet.h000066400000000000000000000013731260446376700171520ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: critical flet.cpp Abstract: Version of flet using "omp critical" directive. Warning: it uses omp critical section "critical_flet" Author: Leonardo de Moura (leonardo) 2011-05-12 Revision History: --*/ #ifndef CRITICAL_FLET_H_ #define CRITICAL_FLET_H_ template class critical_flet { T & m_ref; T m_old_value; public: critical_flet(T & ref, const T & new_value): m_ref(ref), m_old_value(ref) { #pragma omp critical (critical_flet) { m_ref = new_value; } } ~critical_flet() { #pragma omp critical (critical_flet) { m_ref = m_old_value; } } }; #endif z3-z3-4.4.1/src/util/debug.cpp000066400000000000000000000051361260446376700157700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: debug.cpp Abstract: Basic debugging support. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #include #ifndef _WINDOWS #include #endif #include #include"str_hashtable.h" #include"z3_exception.h" static volatile bool g_enable_assertions = true; void enable_assertions(bool f) { g_enable_assertions = f; } bool assertions_enabled() { return g_enable_assertions; } void notify_assertion_violation(const char * fileName, int line, const char * condition) { std::cerr << "ASSERTION VIOLATION\n"; std::cerr << "File: " << fileName << "\n"; std::cerr << "Line: " << line << "\n"; std::cerr << condition << "\n"; } static str_hashtable* g_enabled_debug_tags = 0; static void init_debug_table() { if (!g_enabled_debug_tags) { g_enabled_debug_tags = alloc(str_hashtable); } } void finalize_debug() { dealloc(g_enabled_debug_tags); g_enabled_debug_tags = 0; } void enable_debug(const char * tag) { init_debug_table(); g_enabled_debug_tags->insert(const_cast(tag)); } void disable_debug(const char * tag) { init_debug_table(); g_enabled_debug_tags->erase(const_cast(tag)); } bool is_debug_enabled(const char * tag) { init_debug_table(); return g_enabled_debug_tags->contains(const_cast(tag)); } #ifndef _WINDOWS void invoke_gdb() { char buffer[1024]; int * x = 0; for (;;) { std::cerr << "(C)ontinue, (A)bort, (S)top, (T)hrow exception, Invoke (G)DB\n"; char result; bool ok = (std::cin >> result); if (!ok) exit(ERR_INTERNAL_FATAL); // happens if std::cin is eof or unattached. switch(result) { case 'C': case 'c': return; case 'A': case 'a': exit(1); case 'S': case 's': // force seg fault... *x = 0; return; case 't': case 'T': throw default_exception("assertion violation"); case 'G': case 'g': sprintf(buffer, "gdb -nw /proc/%d/exe %d", getpid(), getpid()); std::cerr << "invoking GDB...\n"; if (system(buffer) == 0) { std::cerr << "continuing the execution...\n"; } else { std::cerr << "error starting GDB...\n"; // forcing seg fault. int * x = 0; *x = 0; } return; default: std::cerr << "INVALID COMMAND\n"; } } } #endif z3-z3-4.4.1/src/util/debug.h000066400000000000000000000055071260446376700154370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: debug.h Abstract: Basic debugging support. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #ifndef DEBUG_H_ #define DEBUG_H_ void enable_assertions(bool f); bool assertions_enabled(); #if 0 #define _CRTDBG_MAP_ALLOC #include #include #include #endif #ifndef __has_builtin # define __has_builtin(x) 0 #endif #include"error_codes.h" #include"warning.h" #ifdef Z3DEBUG #define DEBUG_CODE(CODE) { CODE } ((void) 0) #else #define DEBUG_CODE(CODE) ((void) 0) #endif #ifdef _WINDOWS #define INVOKE_DEBUGGER() __debugbreak() #else void invoke_gdb(); #define INVOKE_DEBUGGER() invoke_gdb() #endif void notify_assertion_violation(const char * file_name, int line, const char * condition); void enable_debug(const char * tag); void disable_debug(const char * tag); bool is_debug_enabled(const char * tag); #define SASSERT(COND) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define CASSERT(TAG, COND) DEBUG_CODE(if (assertions_enabled() && is_debug_enabled(TAG) && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define XASSERT(COND, EXTRA_CODE) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); { EXTRA_CODE } INVOKE_DEBUGGER(); }) #ifdef Z3DEBUG # define UNREACHABLE() DEBUG_CODE(notify_assertion_violation(__FILE__, __LINE__, "UNREACHABLE CODE WAS REACHED."); INVOKE_DEBUGGER();) #else #if (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 405)) || __has_builtin(__builtin_unreachable) // only available in gcc >= 4.5 and in newer versions of clang # define UNREACHABLE() __builtin_unreachable() #elif defined(_MSC_VER) # define UNREACHABLE() __assume(0) #else #define UNREACHABLE() DEBUG_CODE(notify_assertion_violation(__FILE__, __LINE__, "UNREACHABLE CODE WAS REACHED."); INVOKE_DEBUGGER();) #endif #endif #define NOT_IMPLEMENTED_YET() { std::cerr << "NOT IMPLEMENTED YET!\n"; UNREACHABLE(); exit(ERR_NOT_IMPLEMENTED_YET); } ((void) 0) #ifdef Z3DEBUG #define VERIFY(_x_) if (!(_x_)) { \ std::cerr << "Failed to verify: " << #_x_ << "\n"; \ UNREACHABLE(); \ } #else #define VERIFY(_x_) (void)(_x_) #endif #define MAKE_NAME2(LINE) zofty_ ## LINE #define MAKE_NAME(LINE) MAKE_NAME2(LINE) #define DBG_UNIQUE_NAME MAKE_NAME(__LINE__) #ifdef __GNUC__ #define COMPILE_TIME_ASSERT(expr) extern __attribute__((unused)) char DBG_UNIQUE_NAME[expr] #else #define COMPILE_TIME_ASSERT(expr) extern char DBG_UNIQUE_NAME[expr] #endif void finalize_debug(); /* ADD_FINALIZER('finalize_debug();') */ #endif /* DEBUG_H_ */ z3-z3-4.4.1/src/util/dec_ref_util.h000066400000000000000000000025551260446376700167750ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: map_util.h Abstract: Some goodies for managing reference counters stored in maps. Author: Leonardo (leonardo) 2011-06-07 Notes: --*/ #ifndef MAP_UTIL_H_ #define MAP_UTIL_H_ /** \brief Decrement the reference counter of the keys and values stored in the map, then reset the map. */ template void dec_ref_key_values(Mng & m, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_key); m.dec_ref(it->m_value); } map.reset(); } /** \brief Decrement the reference counter of the keys stored in the map, then reset the map. */ template void dec_ref_keys(Mng & m, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_key); } map.reset(); } /** \brief Decrement the reference counter of the values stored in the map, then reset the map. */ template void dec_ref_values(Mng & m, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_value); } map.reset(); } #endif z3-z3-4.4.1/src/util/dependency.h000066400000000000000000000203751260446376700164670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dependency.h Abstract: Author: Leonardo de Moura (leonardo) 2008-12-10. Revision History: --*/ #ifndef DEPENDENCY_H_ #define DEPENDENCY_H_ #include"vector.h" #include"region.h" template class dependency_manager { public: typedef typename C::value value; typedef typename C::value_manager value_manager; typedef typename C::allocator allocator; class dependency { unsigned m_ref_count:30; unsigned m_mark:1; unsigned m_leaf:1; friend class dependency_manager; dependency(bool leaf): m_ref_count(0), m_mark(false), m_leaf(leaf) { } bool is_marked() const { return m_mark == 1; } void mark() { m_mark = true; } void unmark() { m_mark = false; } public: unsigned get_ref_count() const { return m_ref_count; } bool is_leaf() const { return m_leaf == 1; } }; private: struct join : public dependency { dependency * m_children[2]; join(dependency * d1, dependency * d2): dependency(false) { m_children[0] = d1; m_children[1] = d2; } }; struct leaf : public dependency { value m_value; leaf(value const & v): dependency(true), m_value(v) { } }; static join * to_join(dependency * d) { SASSERT(!d->is_leaf()); return static_cast(d); } static leaf * to_leaf(dependency * d) { SASSERT(d->is_leaf()); return static_cast(d); } value_manager & m_vmanager; allocator & m_allocator; ptr_vector m_todo; void inc_ref(value const & v) { if (C::ref_count) m_vmanager.inc_ref(v); } void dec_ref(value const & v) { if (C::ref_count) m_vmanager.dec_ref(v); } void del(dependency * d) { SASSERT(d); m_todo.push_back(d); while (!m_todo.empty()) { d = m_todo.back(); m_todo.pop_back(); if (d->is_leaf()) { dec_ref(to_leaf(d)->m_value); to_leaf(d)->~leaf(); m_allocator.deallocate(sizeof(leaf), to_leaf(d)); } else { for (unsigned i = 0; i < 2; i++) { dependency * c = to_join(d)->m_children[i]; SASSERT(c->m_ref_count > 0); c->m_ref_count--; if (c->m_ref_count == 0) m_todo.push_back(c); } to_join(d)->~join(); m_allocator.deallocate(sizeof(join), to_join(d)); } } } void unmark_todo() { typename ptr_vector::iterator it = m_todo.begin(); typename ptr_vector::iterator end = m_todo.end(); for (; it != end; ++it) { (*it)->unmark(); } m_todo.reset(); } public: dependency_manager(value_manager & m, allocator & a): m_vmanager(m), m_allocator(a) { } void inc_ref(dependency * d) { if (d) d->m_ref_count++; } void dec_ref(dependency * d) { if (d) { SASSERT(d->m_ref_count > 0); d->m_ref_count--; if (d->m_ref_count == 0) del(d); } } dependency * mk_empty() { return 0; } dependency * mk_leaf(value const & v) { void * mem = m_allocator.allocate(sizeof(leaf)); inc_ref(v); return new (mem) leaf(v); } dependency * mk_join(dependency * d1, dependency * d2) { if (d1 == 0) { return d2; } else if (d2 == 0) { return d1; } else if (d1 == d2) { return d1; } else { void * mem = m_allocator.allocate(sizeof(join)); inc_ref(d1); inc_ref(d2); return new (mem) join(d1, d2); } } bool contains(dependency * d, value const & v) { if (d) { m_todo.reset(); d->mark(); m_todo.push_back(d); unsigned qhead = 0; while (qhead < m_todo.size()) { d = m_todo[qhead]; qhead++; if (d->is_leaf()) { if (to_leaf(d)->m_value == v) { unmark_todo(); return true; } } else { for (unsigned i = 0; i < 2; i++) { dependency * child = to_join(d)->m_children[i]; if (!child->is_marked()) { m_todo.push_back(child); child->mark(); } } } } unmark_todo(); } return false; } void linearize(dependency * d, vector & vs) { if (d) { m_todo.reset(); d->mark(); m_todo.push_back(d); unsigned qhead = 0; while (qhead < m_todo.size()) { d = m_todo[qhead]; qhead++; if (d->is_leaf()) { vs.push_back(to_leaf(d)->m_value); } else { for (unsigned i = 0; i < 2; i++) { dependency * child = to_join(d)->m_children[i]; if (!child->is_marked()) { m_todo.push_back(child); child->mark(); } } } } unmark_todo(); } } }; /** \brief Version of the dependency_manager where memory management is scoped (i.e., reference counting is ignored), and push_scope/pop_scope are used instead. Value must be a primitive type such as an integer or pointer. */ template class scoped_dependency_manager { class config { public: static const bool ref_count = true; typedef Value value; class value_manager { public: void inc_ref(value const & v) { } void dec_ref(value const & v) { } }; class allocator { region m_region; public: void * allocate(size_t sz) { return m_region.allocate(sz); } void deallocate(size_t sz, void * mem) { } void push_scope() { m_region.push_scope(); } void pop_scope(unsigned num) { m_region.pop_scope(num); } void reset() { m_region.reset(); } }; }; typedef dependency_manager dep_manager; public: typedef typename dep_manager::dependency dependency; typedef Value value; private: typename config::value_manager m_vmanager; typename config::allocator m_allocator; dep_manager m_dep_manager; public: scoped_dependency_manager(): m_dep_manager(m_vmanager, m_allocator) { } dependency * mk_empty() { return m_dep_manager.mk_empty(); } dependency * mk_leaf(value const & v) { return m_dep_manager.mk_leaf(v); } dependency * mk_join(dependency * d1, dependency * d2) { return m_dep_manager.mk_join(d1, d2); } bool contains(dependency * d, value const & v) { return m_dep_manager.contains(d, v); } void linearize(dependency * d, vector & vs) { return m_dep_manager.linearize(d, vs); } void reset() { m_allocator.reset(); } void push_scope() { m_allocator.push_scope(); } void pop_scope(unsigned num_scopes) { m_allocator.pop_scope(num_scopes); } }; // Implement old dependency manager used by interval and groebner typedef scoped_dependency_manager v_dependency_manager; typedef scoped_dependency_manager::dependency v_dependency; #endif /* DEPENDENCY_H_ */ z3-z3-4.4.1/src/util/dictionary.h000066400000000000000000000005561260446376700165150ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dictionary.h Abstract: Author: Leonardo (leonardo) 2011-03-01 Notes: --*/ #ifndef DICTIONARY_H_ #define DICTIONARY_H_ #include"map.h" #include"symbol.h" template class dictionary : public map { public: dictionary() {} }; #endif z3-z3-4.4.1/src/util/dlist.h000066400000000000000000000064261260446376700154710ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dlist.h Abstract: Templates for manipulating doubly linked lists. Author: Leonardo de Moura (leonardo) 2011-01-25. Revision History: --*/ #ifndef DLIST_H_ #define DLIST_H_ /** Add element \c elem to the list headed by \c head. NextProc and PrevProc must have the methods: T * & operator()(T *); T * & operator()(T *); They should return the next and prev fields of the given object. */ template void dlist_add(T * & head, T * elem, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) { SASSERT(prev(elem) == 0); SASSERT(next(elem) == 0); if (head == 0) { head = elem; } else { next(elem) = head; prev(head) = elem; head = elem; } } template void dlist_del(T * & head, T * elem, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) { T * & prev_elem = prev(elem); T * & next_elem = next(elem); if (head == elem) { SASSERT(prev_elem == 0); if (next_elem != 0) prev(next_elem) = 0; head = next_elem; } else { SASSERT(prev_elem != 0); next(prev_elem) = next_elem; if (next_elem != 0) prev(next_elem) = prev_elem; } prev_elem = 0; next_elem = 0; } /** \brief Remove the head of the list. Return the old head. */ template T * dlist_pop(T * & head, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) { if (head == 0) { return 0; } else { SASSERT(prev(head) == 0); T * r = head; head = next(head); if (head) prev(head) = 0; next(r) = 0; return r; } } /** \brief Insert new element after elem. */ template void dlist_insert_after(T * elem, T * new_elem, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) { SASSERT(elem); SASSERT(new_elem); T * & old_next_elem = next(elem); prev(new_elem) = elem; next(new_elem) = old_next_elem; if (old_next_elem) prev(old_next_elem) = new_elem; // next(elem) = new_elem; old_next_elem = new_elem; } template bool dlist_contains(T * head, T const * elem, NextProc const & next = NextProc()) { T * curr = head; while (curr != 0) { if (curr == elem) return true; curr = next(curr); } return false; } template unsigned dlist_length(T * head, NextProc const & next = NextProc()) { unsigned r = 0; T * curr = head; while (curr != 0) { r++; curr = next(curr); } return r; } template bool dlist_check_invariant(T * head, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) { if (head == 0) return true; SASSERT(prev(head) == 0); T * old = head; T * curr = next(head); while (curr != 0) { SASSERT(prev(curr) == old); old = curr; curr = next(curr); } return true; } #endif z3-z3-4.4.1/src/util/double_manager.h000066400000000000000000000072071260446376700173140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: double_manager.h Abstract: Simulates mpq interface using doubles Author: Leonardo de Moura (leonardo) 2011-06-09. Revision History: --*/ #ifndef DOUBLE_MANAGER_H_ #define DOUBLE_MANAGER_H_ #include #include #include #include #include"util.h" #include"debug.h" #include"hash.h" #include"params.h" /** \brief Create an interface for manipulating double numbers compatible with the one for mpq. */ class double_manager { double m_zero_tolerance; public: typedef double numeral; static bool precise() { return false; } double_manager(params_ref const & p = params_ref()) { updt_params(p); } void updt_params(params_ref const & p) { m_zero_tolerance = p.get_double("zero_tolerance", 0.00000001); } static void reset(double & a) { a = 0.0; } static void del(double & a) { /* do nothing */ } static void add(double a, double b, double & c) { c = a + b; } // d <- a + b*c static void addmul(double a, double b, double c, double & d) { d = a + b*c; } // d <- a - b*c static void submul(double a, double b, double c, double & d) { d = a - b*c; } static void sub(double a, double b, double & c) { c = a - b; } static void mul(double a, double b, double & c) { c = a * b; } static void div(double a, double b, double & c) { c = a / b; } static void inv(double a, double & b) { b = 1 / a; } static void inv(double & a) { a = 1 / a; } static void neg(double & a) { a = -a; } static void abs(double & a) { if (a < 0.0) neg(a); } static void power(double a, unsigned p, double & b) { SASSERT(p <= INT_MAX); b = ::pow(a, static_cast(p)); } static void floor(double a, double & b) { b = ::floor(a); } static void ceil(double a, double & b) { b = ::ceil(a); } bool eq(double a, double b) const { return is_zero(a - b); } bool neq(double a, double b) const { return !eq(a, b); } static bool lt(double a, double b) { return a < b; } static bool le(double a, double b) { return a <= b; } static bool gt(double a, double b) { return a > b; } static bool ge(double a, double b) { return a >= b; } static void set(double & a, int n, int d) { a = static_cast(n)/static_cast(d); } static void set(double & a, double val) { a = val; } static void set(double & a, char const * val) { a = atof(val); } static void set(double & a, int val) { a = static_cast(val); } static void set(double & a, unsigned val) { a = static_cast(val); } static void set(double & a, int64 val) { a = static_cast(val); } static void set(double & a, uint64 val) { a = static_cast(val); } static void swap(double & a, double & b) { std::swap(a, b); } bool is_pos(double a) const { return a > m_zero_tolerance; } bool is_neg(double a) const { return a < m_zero_tolerance; } bool is_zero(double a) const { return -m_zero_tolerance <= a && a <= m_zero_tolerance; } bool is_nonpos(double a) const { return !is_pos(a); } bool is_nonneg(double a) const { return !is_neg(a); } static bool is_one(double a) { return a == 1.0; } static bool is_minus_one(double a) { return a == -1.0; } static bool is_int(double a) { return a == ::floor(a); } static std::string to_string(double a) { std::ostringstream sstream; sstream << std::setprecision(12) << a; return sstream.str(); } static unsigned hash(double a) { return hash_ull(static_cast(a)); } }; COMPILE_TIME_ASSERT(sizeof(uint64) == sizeof(double)); #endif /* DOUBLE_MANAGER_H_ */ z3-z3-4.4.1/src/util/env_params.cpp000066400000000000000000000025421260446376700170330ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: env_params.cpp Abstract: Goodies for updating environment parameters. Author: Leonardo (leonardo) 2012-12-01 Notes: --*/ #include"env_params.h" #include"params.h" #include"gparams.h" #include"util.h" #include"memory_manager.h" void env_params::updt_params() { params_ref p = gparams::get(); set_verbosity_level(p.get_uint("verbose", get_verbosity_level())); enable_warning_messages(p.get_bool("warning", true)); memory::set_max_size(megabytes_to_bytes(p.get_uint("memory_max_size", 0))); memory::set_max_alloc_count(p.get_uint("memory_max_alloc_count", 0)); memory::set_high_watermark(p.get_uint("memory_high_watermark", 0)); } void env_params::collect_param_descrs(param_descrs & d) { d.insert("verbose", CPK_UINT, "be verbose, where the value is the verbosity level", "0"); d.insert("warning", CPK_BOOL, "enable/disable warning messages", "true"); d.insert("memory_max_size", CPK_UINT, "set hard upper limit for memory consumption (in megabytes), if 0 then there is no limit", "0"); d.insert("memory_max_alloc_count", CPK_UINT, "set hard upper limit for memory allocations, if 0 then there is no limit", "0"); d.insert("memory_high_watermark", CPK_UINT, "set high watermark for memory consumption (in megabytes), if 0 then there is no limit", "0"); } z3-z3-4.4.1/src/util/env_params.h000066400000000000000000000007011260446376700164730ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: env_params.h Abstract: Goodies for updating environment parameters. Author: Leonardo (leonardo) 2012-12-01 Notes: --*/ #ifndef ENV_PARAMS_H_ #define ENV_PARAMS_H_ class param_descrs; struct env_params { static void updt_params(); static void collect_param_descrs(param_descrs & p); /* REG_PARAMS('env_params::collect_param_descrs') */ }; #endif z3-z3-4.4.1/src/util/error_codes.h000066400000000000000000000014201260446376700166450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: error_codes.h Abstract: Error codes produced by Z3. Author: Leonardo de Moura (leonardo) 2007-09-04. Revision History: --*/ #ifndef ERROR_CODES_H_ #define ERROR_CODES_H_ #define ERR_OK 0 #define ERR_MEMOUT 101 #define ERR_TIMEOUT 102 #define ERR_PARSER 103 #define ERR_UNSOUNDNESS 104 #define ERR_INCOMPLETENESS 105 #define ERR_INI_FILE 106 #define ERR_NOT_IMPLEMENTED_YET 107 #define ERR_OPEN_FILE 108 #define ERR_CMD_LINE 109 #define ERR_INTERNAL_FATAL 110 #define ERR_TYPE_CHECK 111 #define ERR_UNKNOWN_RESULT 112 #define ERR_ALLOC_EXCEEDED 113 #endif /* ERROR_CODES_H_ */ z3-z3-4.4.1/src/util/event_handler.h000066400000000000000000000005501260446376700171600ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: event_handler.h Abstract: Abstract event handler. Author: Leonardo de Moura (leonardo) 2011-04-26. Revision History: --*/ #ifndef EVENT_HANDLER_H_ #define EVENT_HANDLER_H_ class event_handler { public: virtual ~event_handler() {} virtual void operator()() = 0; }; #endif z3-z3-4.4.1/src/util/ext_gcd.h000066400000000000000000000016441260446376700157640ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ext_gcd.h Abstract: Author: Leonardo de Moura (leonardo) 2007-06-09. Revision History: --*/ #ifndef EXT_GCD_H_ #define EXT_GCD_H_ template void extended_gcd(const numeral & in_a, const numeral & in_b, numeral & gcd, numeral & x, numeral & y) { numeral a = in_a; numeral b = in_b; x = numeral(0); y = numeral(1); numeral lastx(1); numeral lasty(0); numeral tmp; numeral quotient; while (!b.is_zero()) { tmp = b; quotient = div(a, b); b = mod(a, b); a = tmp; tmp = x; x = lastx - (quotient * x); lastx = tmp; tmp = y; y = lasty - (quotient * y); lasty = tmp; } gcd = a; x = lastx; y = lasty; } #endif /* EXT_GCD_H_ */ z3-z3-4.4.1/src/util/ext_numeral.h000066400000000000000000000217631260446376700166760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ext_numeral.h Abstract: Goodies for handling extended numerals such as R union { -oo, +oo }. We can have extended sets of mpq, mpz, mpbq, mpf, etc. Author: Leonardo de Moura (leonardo) 2011-12-04. Revision History: --*/ #ifndef EXT_NUMERAL_H_ #define EXT_NUMERAL_H_ #include #include"debug.h" enum ext_numeral_kind { EN_MINUS_INFINITY, EN_NUMERAL, EN_PLUS_INFINITY }; template bool is_zero(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak) { return ak == EN_NUMERAL && m.is_zero(a); } template bool is_pos(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak) { return ak == EN_PLUS_INFINITY || (ak == EN_NUMERAL && m.is_pos(a)); } template bool is_neg(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak) { return ak == EN_MINUS_INFINITY || (ak == EN_NUMERAL && m.is_neg(a)); } inline bool is_infinite(ext_numeral_kind ak) { return ak != EN_NUMERAL; } template void set(numeral_manager & m, typename numeral_manager::numeral & a, ext_numeral_kind & ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { m.set(a, b); ak = bk; } template void reset(numeral_manager & m, typename numeral_manager::numeral & a, ext_numeral_kind & ak) { m.reset(a); ak = EN_NUMERAL; } template void neg(numeral_manager & m, typename numeral_manager::numeral & a, ext_numeral_kind & ak) { switch (ak) { case EN_MINUS_INFINITY: ak = EN_PLUS_INFINITY; break; case EN_NUMERAL: m.neg(a); break; case EN_PLUS_INFINITY: ak = EN_MINUS_INFINITY; break; } } template void inv(numeral_manager & m, typename numeral_manager::numeral & a, ext_numeral_kind & ak) { SASSERT(numeral_manager::field()); switch (ak) { case EN_MINUS_INFINITY: ak = EN_NUMERAL; m.reset(a); break; case EN_NUMERAL: SASSERT(!m.is_zero(a)); m.inv(a); break; case EN_PLUS_INFINITY: ak = EN_NUMERAL; m.reset(a); break; } } template void add(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk, typename numeral_manager::numeral & c, ext_numeral_kind & ck) { SASSERT(!(ak == EN_MINUS_INFINITY && bk == EN_PLUS_INFINITY)); // result is undefined SASSERT(!(ak == EN_PLUS_INFINITY && bk == EN_MINUS_INFINITY)); // result is undefined if (ak != EN_NUMERAL) { m.reset(c); ck = ak; } else if (bk != EN_NUMERAL) { m.reset(c); ck = bk; } else { m.add(a, b, c); ck = EN_NUMERAL; } } template void sub(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk, typename numeral_manager::numeral & c, ext_numeral_kind & ck) { SASSERT(!(ak == EN_MINUS_INFINITY && bk == EN_MINUS_INFINITY)); // result is undefined SASSERT(!(ak == EN_PLUS_INFINITY && bk == EN_PLUS_INFINITY)); // result is undefined if (ak != EN_NUMERAL) { SASSERT(bk != ak); m.reset(c); ck = ak; } else { switch (bk) { case EN_MINUS_INFINITY: m.reset(c); ck = EN_PLUS_INFINITY; break; case EN_NUMERAL: m.sub(a, b, c); ck = EN_NUMERAL; break; case EN_PLUS_INFINITY: m.reset(c); ck = EN_MINUS_INFINITY; break; } } } template void mul(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk, typename numeral_manager::numeral & c, ext_numeral_kind & ck) { if (is_zero(m, a, ak) || is_zero(m, b, bk)) { m.reset(c); ck = EN_NUMERAL; } else if (is_infinite(ak) || is_infinite(bk)) { if (is_pos(m, a, ak) == is_pos(m, b, bk)) ck = EN_PLUS_INFINITY; else ck = EN_MINUS_INFINITY; m.reset(c); } else { ck = EN_NUMERAL; m.mul(a, b, c); } } template void div(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk, typename numeral_manager::numeral & c, ext_numeral_kind & ck) { SASSERT(!is_zero(m, b, bk)); if (is_zero(m, a, ak)) { SASSERT(!is_zero(m, b, bk)); m.reset(c); ck = EN_NUMERAL; } else if (is_infinite(ak)) { SASSERT(!is_infinite(bk)); if (is_pos(m, a, ak) == is_pos(m, b, bk)) ck = EN_PLUS_INFINITY; else ck = EN_MINUS_INFINITY; m.reset(c); } else if (is_infinite(bk)) { SASSERT(!is_infinite(ak)); m.reset(c); ck = EN_NUMERAL; } else { ck = EN_NUMERAL; m.div(a, b, c); } } template void power(numeral_manager & m, typename numeral_manager::numeral & a, ext_numeral_kind & ak, unsigned n) { switch (ak) { case EN_MINUS_INFINITY: if (n % 2 == 0) ak = EN_PLUS_INFINITY; break; case EN_NUMERAL: m.power(a, n, a); break; case EN_PLUS_INFINITY: break; // do nothing } } /** \brief Return true if (a,ak) == (b,bk). */ template bool eq(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { if (ak == EN_NUMERAL) { return bk == EN_NUMERAL && m.eq(a, b); } else { return ak == bk; } } template bool neq(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { return !eq(m, a, ak, b, bk); } template bool lt(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { switch (ak) { case EN_MINUS_INFINITY: return bk != EN_MINUS_INFINITY; case EN_NUMERAL: switch (bk) { case EN_MINUS_INFINITY: return false; case EN_NUMERAL: return m.lt(a, b); case EN_PLUS_INFINITY: return true; default: UNREACHABLE(); return false; } case EN_PLUS_INFINITY: return false; default: UNREACHABLE(); return false; } } template bool gt(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { return lt(m, b, bk, a, ak); } template bool le(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { return !gt(m, a, ak, b, bk); } template bool ge(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { return !lt(m, a, ak, b, bk); } template void display(std::ostream & out, numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak) { switch (ak) { case EN_MINUS_INFINITY: out << "-oo"; break; case EN_NUMERAL: m.display(out, a); break; case EN_PLUS_INFINITY: out << "+oo"; break; } } template void display_pp(std::ostream & out, numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak) { switch (ak) { case EN_MINUS_INFINITY: out << "-∞"; break; case EN_NUMERAL: m.display_pp(out, a); break; case EN_PLUS_INFINITY: out << "+∞"; break; } } #endif z3-z3-4.4.1/src/util/f2n.h000066400000000000000000000152241260446376700150330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: f2n.h Abstract: Template for wrapping a float-like API as a numeral-like API. The basic idea is to have the rounding mode as an implicit argument. Author: Leonardo de Moura (leonardo) 2012-07-30. Revision History: --*/ #ifndef F2N_H_ #define F2N_H_ #include"mpf.h" template class f2n { public: typedef typename fmanager::numeral numeral; struct exception {}; private: fmanager & m_manager; mpf_rounding_mode m_mode; unsigned m_ebits; unsigned m_sbits; numeral m_tmp1; numeral m_one; void check(numeral const & n) { if (!m().is_regular(n)) throw exception(); } public: static bool field() { return true; } static bool precise() { return false; } f2n(fmanager & m, unsigned ebits = 11, unsigned sbits = 53):m_manager(m), m_mode(MPF_ROUND_TOWARD_POSITIVE), m_ebits(ebits), m_sbits(sbits) { m_manager.set(m_one, ebits, sbits, 1); } ~f2n() { m().del(m_tmp1); m().del(m_one); } void set_rounding_mode(mpf_rounding_mode m) { m_mode = m; } mpf_rounding_mode rounding_mode() const { return m_mode; } void round_to_plus_inf() { m_mode = MPF_ROUND_TOWARD_POSITIVE; } void round_to_minus_inf() { m_mode = MPF_ROUND_TOWARD_NEGATIVE; } void set_rounding(bool to_plus_inf) { if (to_plus_inf) round_to_plus_inf(); else round_to_minus_inf(); } unsigned ebits() const { return m_ebits; } unsigned sbits() const { return m_sbits; } fmanager & m() const { return m_manager; } double to_double(numeral & x) const { return m().to_double(x); } void del(numeral & x) { m().del(x); } void abs(numeral & o) { m().abs(o); } void abs(numeral const & x, numeral & o) { m().abs(x, o); } void neg(numeral & o) { m().neg(o); } void neg(numeral const & x, numeral & o) { m().neg(x, o); } bool is_zero(numeral const & x) { return m().is_zero(x); } bool is_neg(numeral const & x) { return m().is_neg(x) && !m().is_zero(x); /* it is not clear whether actual hardware returns true for is_neg(0-) */ } bool is_pos(numeral const & x) { return m().is_pos(x) && !m().is_zero(x); } bool is_nonneg(numeral const & x) { return !is_neg(x); } bool is_nonpos(numeral const & x) { return !is_pos(x); } void set(numeral & o, int value) { m().set(o, m_ebits, m_sbits, value); check(o); } void set(numeral & o, int n, int d) { m().set(o, m_ebits, m_sbits, m_mode, n, d); check(o); } void set(numeral & o, double x) { m().set(o, m_ebits, m_sbits, x); check(o); } void set(numeral & o, unsigned value) { m().set(o, m_ebits, m_sbits, (double)value); check(o); } void set(numeral & o, numeral const & x) { m().set(o, x); check(o); } void set(numeral & o, mpq const & x) { m().set(o, m_ebits, m_sbits, m_mode, x); check(o); } void reset(numeral & o) { m().reset(o, m_ebits, m_sbits); } static void swap(numeral & x, numeral & y) { x.swap(y); } void add(numeral const & x, numeral const & y, numeral & o) { m().add(m_mode, x, y, o); check(o); } void sub(numeral const & x, numeral const & y, numeral & o) { m().sub(m_mode, x, y, o); check(o); } void mul(numeral const & x, numeral const & y, numeral & o) { m().mul(m_mode, x, y, o); check(o); } void div(numeral const & x, numeral const & y, numeral & o) { m().div(m_mode, x, y, o); check(o); } void inv(numeral & o) { numeral a; set(a, 1); div(a, o, o); del(a); check(o); } void inv(numeral const & x, numeral & o) { set(o, x); inv(o); } void inc(numeral & x) { add(x, m_one, x); } void dec(numeral & x) { sub(x, m_one, x); } void power(numeral const & a, unsigned p, numeral & b) { unsigned mask = 1; numeral power; set(power, a); set(b, 1); while (mask <= p) { if (mask & p) mul(b, power, b); mul(power, power, power); mask = mask << 1; } del(power); check(b); } // Store the floor of a into b. Return true if a is an integer. // Throws an exception if the result cannot be computed precisely. void floor(numeral const & a, numeral & b) { SASSERT(m().is_regular(a)); // Claim: If a is a regular float, then floor(a) is an integer that can be precisely represented. // Justification: (for the case a is nonnegative) // If 0 <= a > 2^sbits(), then a is an integer, and floor(a) == a // If 0 <= a <= 2^sbits(), then floor(a) is representable since every integer less than 2^sbit m().round_to_integral(MPF_ROUND_TOWARD_NEGATIVE, a, m_tmp1); SASSERT(m().is_regular(m_tmp1)); if (m().le(m_tmp1, a)) { m().set(b, m_tmp1); } else { // the rounding mode doesn't matter for the following operation. m().sub(MPF_ROUND_TOWARD_NEGATIVE, m_tmp1, m_one, b); } SASSERT(m().is_regular(b)); } void ceil(numeral const & a, numeral & b) { SASSERT(m().is_regular(a)); // See comment in floor m().round_to_integral(MPF_ROUND_TOWARD_POSITIVE, a, m_tmp1); SASSERT(m().is_regular(m_tmp1)); if (m().ge(m_tmp1, a)) { m().set(b, m_tmp1); } else { // the rounding mode doesn't matter for the following operation. m().add(MPF_ROUND_TOWARD_NEGATIVE, m_tmp1, m_one, b); } SASSERT(m().is_regular(b)); } unsigned prev_power_of_two(numeral const & a) { return m().prev_power_of_two(a); } bool eq(numeral const & x, numeral const & y) { return m().eq(x, y); } bool lt(numeral const & x, numeral const & y) { return m().lt(x, y); } bool le(numeral const & x, numeral const & y) { return m().le(x, y); } bool gt(numeral const & x, numeral const & y) { return m().gt(x, y); } bool ge(numeral const & x, numeral const & y) { return m().ge(x, y); } bool is_int(numeral const & x) { return m().is_int(x); } bool is_one(numeral const & x) { return m().is_one(x); } bool is_minus_one(numeral const & x) { numeral & _x = const_cast(x); m().neg(_x); bool r = m().is_one(_x); m().neg(_x); return r; } std::string to_string(numeral const & a) { return m().to_string(a); } std::string to_rational_string(numeral const & a) { return m().to_rational_string(a); } void display(std::ostream & out, numeral const & a) { out << to_string(a); } void display_decimal(std::ostream & out, numeral const & a, unsigned k) { m().display_decimal(out, a, k); } void display_smt2(std::ostream & out, numeral const & a, bool decimal) { m().display_smt2(out, a, decimal); } }; #endif z3-z3-4.4.1/src/util/fixed_bit_vector.cpp000066400000000000000000000105321260446376700202150ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: fixed_bit_vector.cpp Abstract: Simple bitvector implementation for fixed size bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2014-9-15. Leonardo de Moura (leonardo) 2006-10-03. Revision History: Based on bit_vector.cpp --*/ #include #include"fixed_bit_vector.h" #include"trace.h" #include"hash.h" void fixed_bit_vector::set(fixed_bit_vector const& other, unsigned hi, unsigned lo) { if ((lo % 32) == 0) { unsigned sz32 = (hi-lo+1)/32; unsigned lo32 = lo/32; for (unsigned i = 0; i < sz32; ++i) { m_data[lo32 + i] = other.m_data[i]; } for (unsigned i = sz32*32; i < hi - lo + 1; ++i) { set(lo + i, other.get(i)); } return; } for (unsigned i = 0; i < hi - lo + 1; ++i) { set(lo + i, other.get(i)); } } fixed_bit_vector_manager::fixed_bit_vector_manager(unsigned num_bits): m_alloc("fixed_bit_vector") { m_num_bits = num_bits; m_num_words = num_words(num_bits); m_num_bytes = m_num_words * sizeof(unsigned); unsigned bit_rest = m_num_bits % 32; m_mask = (1U << bit_rest) - 1; if (m_mask == 0) m_mask = UINT_MAX; } fixed_bit_vector* fixed_bit_vector_manager::allocate() { if (m_num_bytes == 0) return &m_0; return static_cast(m_alloc.allocate(m_num_bytes)); } fixed_bit_vector* fixed_bit_vector_manager::allocate0() { fixed_bit_vector* result = allocate(); fill0(*result); return result; } fixed_bit_vector* fixed_bit_vector_manager::allocate1() { fixed_bit_vector* result = allocate(); fill1(*result); return result; } fixed_bit_vector* fixed_bit_vector_manager::allocate(fixed_bit_vector const& bv) { fixed_bit_vector* result = allocate(); copy(*result, bv); return result; } void fixed_bit_vector_manager::deallocate(fixed_bit_vector* bv) { if (m_num_bytes > 0) m_alloc.deallocate(m_num_bytes, bv); } void fixed_bit_vector_manager::copy(fixed_bit_vector& dst, fixed_bit_vector const& src) const { memcpy(dst.m_data, src.m_data, num_bytes()); } fixed_bit_vector& fixed_bit_vector_manager::fill0(fixed_bit_vector& bv) const { memset(bv.m_data, 0, num_bytes()); return bv; } fixed_bit_vector& fixed_bit_vector_manager::fill1(fixed_bit_vector& bv) const { memset(bv.m_data, 0xFF, num_bytes()); return bv; } fixed_bit_vector& fixed_bit_vector_manager::set_and(fixed_bit_vector& dst, fixed_bit_vector const& src) const { for (unsigned i = 0; i < m_num_words; i++) dst.m_data[i] &= src.m_data[i]; return dst; } fixed_bit_vector& fixed_bit_vector_manager::set_or(fixed_bit_vector& dst, fixed_bit_vector const& src) const { for (unsigned i = 0; i < m_num_words; i++) dst.m_data[i] |= src.m_data[i]; return dst; } fixed_bit_vector& fixed_bit_vector_manager::set_neg(fixed_bit_vector& dst) const { for (unsigned i = 0; i < m_num_words; i++) dst.m_data[i] = ~dst.m_data[i]; return dst; } unsigned fixed_bit_vector_manager::last_word(fixed_bit_vector const& bv) const { unsigned n = num_words(); if (n == 0) return 0; return bv.m_data[n-1] & m_mask; } bool fixed_bit_vector_manager::equals(fixed_bit_vector const& a, fixed_bit_vector const& b) const { if (&a == &b) return true; unsigned n = num_words(); if (n == 0) return true; for (unsigned i = 0; i < n - 1; i++) { if (a.m_data[i] != b.m_data[i]) return false; } return last_word(a) == last_word(b); } unsigned fixed_bit_vector_manager::hash(fixed_bit_vector const& src) const { return string_hash(reinterpret_cast(src.m_data), num_bits()/8, num_bits()); } bool fixed_bit_vector_manager::contains(fixed_bit_vector const& a, fixed_bit_vector const& b) const { unsigned n = num_words(); if (n == 0) return true; for (unsigned i = 0; i < n - 1; ++i) { if ((a.m_data[i] & b.m_data[i]) != b.m_data[i]) return false; } unsigned b_data = last_word(b); return (last_word(a) & b_data) == b_data; } std::ostream& fixed_bit_vector_manager::display(std::ostream& out, fixed_bit_vector const& b) const { unsigned i = num_bits(); while (i > 0) { --i; if (b.get(i)) out << "1"; else out << "0"; } return out; } z3-z3-4.4.1/src/util/fixed_bit_vector.h000066400000000000000000000100571260446376700176640ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: fixed_bit_vector.h Abstract: Simple bitvector implementation for fixed size bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2014-9-15. Revision History: Related to bit_vector, but is based on a manager. --*/ #ifndef FIXED_BIT_VECTOR_H_ #define FIXED_BIT_VECTOR_H_ #include #include"debug.h" #include"small_object_allocator.h" class fixed_bit_vector { friend class fixed_bit_vector_manager; friend class tbv_manager; unsigned m_data[1]; static unsigned get_pos_mask(unsigned bit_idx) { return 1 << (bit_idx % 32); } unsigned get_bit_word(unsigned bit_idx) const { return m_data[bit_idx / 32]; } unsigned & get_bit_word(unsigned bit_idx) { return m_data[bit_idx / 32]; } public: fixed_bit_vector() {} ~fixed_bit_vector() {} unsigned get_word(unsigned word_idx) const { return m_data[word_idx]; } bool operator[](unsigned bit_idx) const { return get(bit_idx); } bool get(unsigned bit_idx) const { return (get_bit_word(bit_idx) & get_pos_mask(bit_idx)) != 0; } private: void set(unsigned bit_idx) { get_bit_word(bit_idx) |= get_pos_mask(bit_idx); } void unset(unsigned bit_idx) { get_bit_word(bit_idx) &= ~get_pos_mask(bit_idx); } void set(unsigned bit_idx, bool val) { int _val = static_cast(val); get_bit_word(bit_idx) ^= (-_val ^ get_bit_word(bit_idx)) & get_pos_mask(bit_idx); } // assign bits this[lo:hi] := other[0:hi-lo+1] void set(fixed_bit_vector const& other, unsigned hi, unsigned lo); }; class fixed_bit_vector_manager { friend class fixed_bit_vector; small_object_allocator m_alloc; unsigned m_num_bits; unsigned m_num_bytes; unsigned m_num_words; unsigned m_mask; fixed_bit_vector m_0; static unsigned num_words(unsigned num_bits) { return (num_bits + 31) / 32; } public: fixed_bit_vector_manager(unsigned num_bits); void reset() { m_alloc.reset(); } fixed_bit_vector* allocate(); fixed_bit_vector* allocate1(); fixed_bit_vector* allocate0(); fixed_bit_vector* allocate(fixed_bit_vector const& bv); void deallocate(fixed_bit_vector* bv); void copy(fixed_bit_vector& dst, fixed_bit_vector const& src) const; unsigned num_words() const { return m_num_words; } unsigned num_bytes() const { return m_num_bytes; } unsigned num_bits() const { return m_num_bits; } fixed_bit_vector& reset(fixed_bit_vector& bv) const { return fill0(bv); } fixed_bit_vector& fill0(fixed_bit_vector& bv) const; fixed_bit_vector& fill1(fixed_bit_vector& bv) const; fixed_bit_vector& set_and(fixed_bit_vector& dst, fixed_bit_vector const& src) const; fixed_bit_vector& set_or(fixed_bit_vector& dst, fixed_bit_vector const& src) const; fixed_bit_vector& set_neg(fixed_bit_vector& dst) const; unsigned last_word(fixed_bit_vector const& bv) const; unsigned get_mask() const { return m_mask; } bool equals(fixed_bit_vector const& a, fixed_bit_vector const& b) const; unsigned hash(fixed_bit_vector const& src) const; bool contains(fixed_bit_vector const& a, fixed_bit_vector const& b) const; std::ostream& display(std::ostream& out, fixed_bit_vector const& b) const; void set(fixed_bit_vector& dst, unsigned bit_idx) { SASSERT(bit_idx < num_bits()); dst.set(bit_idx); } void unset(fixed_bit_vector& dst, unsigned bit_idx) { SASSERT(bit_idx < num_bits()); dst.unset(bit_idx); } void set(fixed_bit_vector& dst, unsigned bit_idx, bool val) { SASSERT(bit_idx < num_bits()); dst.set(bit_idx, val); } // assign bits this[lo:hi] := other[0:hi-lo+1] void set(fixed_bit_vector& dst, fixed_bit_vector const& other, unsigned hi, unsigned lo) { SASSERT(lo <= hi && hi < num_bits()); dst.set(other, hi, lo); } }; #endif /* FIXED_BIT_VECTOR_H_ */ z3-z3-4.4.1/src/util/gparams.cpp000066400000000000000000000643661260446376700163460ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: gparams.cpp Abstract: Global parameter management. Author: Leonardo (leonardo) 2012-11-29 Notes: --*/ #include"gparams.h" #include"dictionary.h" #include"trace.h" extern void gparams_register_modules(); static char const * g_old_params_names[] = { "arith_adaptive","arith_adaptive_assertion_threshold","arith_adaptive_gcd","arith_adaptive_propagation_threshold","arith_add_binary_bounds","arith_blands_rule_threshold","arith_branch_cut_ratio","arith_dump_lemmas","arith_eager_eq_axioms","arith_eager_gcd","arith_eq_bounds","arith_euclidean_solver","arith_expand_eqs","arith_force_simplex","arith_gcd_test","arith_ignore_int","arith_lazy_adapter","arith_lazy_pivoting","arith_max_lemma_size","arith_process_all_eqs","arith_propagate_eqs","arith_propagation_mode","arith_propagation_threshold","arith_prop_strategy","arith_random_initial_value","arith_random_lower","arith_random_seed","arith_random_upper","arith_reflect","arith_skip_big_coeffs","arith_small_lemma_size","arith_solver","arith_stronger_lemmas","array_always_prop_upward","array_canonize","array_cg","array_delay_exp_axiom","array_extensional","array_laziness","array_lazy_ieq","array_lazy_ieq_delay","array_solver","array_weak","async_commands","at_labels_cex","auto_config","bb_eager","bb_ext_gates","bb_quantifiers","bin_clauses","bit2int","bv2int_distribute","bv_blast_max_size","bv_cc","bv_enable_int2bv_propagation","bv_lazy_le","bv_max_sharing","bv_reflect","bv_solver","case_split","check_at_labels","check_proof","cnf_factor","cnf_mode","context_simplifier","dack","dack_eq","dack_factor","dack_gc","dack_gc_inv_decay","dack_threshold","default_qid","default_table","default_table_checked","delay_units","delay_units_threshold","der","display_config","display_dot_proof","display_error_for_visual_studio","display_features","display_proof","display_unsat_core","distribute_forall","dt_lazy_splits","dump_goal_as_smt","elim_and","elim_bounds","elim_nlarith_quantifiers","elim_quantifiers","elim_term_ite","ematching","engine","eq_propagation","hi_div0","ignore_bad_patterns","ignore_setparameter","instruction_max","inst_gen","interactive","internalizer_nnf","lemma_gc_factor","lemma_gc_half","lemma_gc_initial","lemma_gc_new_clause_activity","lemma_gc_new_clause_relevancy","lemma_gc_new_old_ratio","lemma_gc_old_clause_activity","lemma_gc_old_clause_relevancy","lemma_gc_strategy","lift_ite","lookahead_diseq","macro_finder","max_conflicts","max_counterexamples","mbqi","mbqi_force_template","mbqi_max_cexs","mbqi_max_cexs_incr","mbqi_max_iterations","mbqi_trace","minimize_lemmas","model","model_compact","model_completion","model_display_arg_sort","model_hide_unused_partitions","model_on_final_check","model_on_timeout","model_partial","model_v1","model_v2","model_validate","new_core2th_eq","ng_lift_ite","nl_arith","nl_arith_branching","nl_arith_gb","nl_arith_gb_eqs","nl_arith_gb_perturbate","nl_arith_gb_threshold","nl_arith_max_degree","nl_arith_rounds","nnf_factor","nnf_ignore_labels","nnf_mode","nnf_sk_hack","order","order_var_weight","order_weights","phase_selection","pi_arith","pi_arith_weight","pi_avoid_skolems","pi_block_looop_patterns","pi_max_multi_patterns","pi_non_nested_arith_weight","pi_nopat_weight","pi_pull_quantifiers","pi_use_database","pi_warnings","pp_bounded","pp_bv_literals","pp_bv_neg","pp_decimal","pp_decimal_precision","pp_fixed_indent","pp_flat_assoc","pp_max_depth","pp_max_indent","pp_max_num_lines","pp_max_ribbon","pp_max_width","pp_min_alias_size","pp_simplify_implies","pp_single_line","precedence","precedence_gen","pre_demodulator","pre_simplifier","pre_simplify_expr","profile_res_sub","progress_sampling_freq","proof_mode","propagate_booleans","propagate_values","pull_cheap_ite_trees","pull_nested_quantifiers","qi_conservative_final_check","qi_cost","qi_eager_threshold","qi_lazy_instantiation","qi_lazy_quick_checker","qi_lazy_threshold","qi_max_eager_multi_patterns","qi_max_instances","qi_max_lazy_multi_pattern_matching","qi_new_gen","qi_profile","qi_profile_freq","qi_promote_unsat","qi_quick_checker","quasi_macros","random_case_split_freq","random_initial_activity","random_seed","recent_lemma_threshold","reduce_args","refine_inj_axiom","relevancy","relevancy_lemma","rel_case_split_order","restart_adaptive","restart_agility_threshold","restart_factor","restart_initial","restart_strategy","restricted_quasi_macros","simplify_clauses","smtlib2_compliant","smtlib_category","smtlib_dump_lemmas","smtlib_logic","smtlib_source_info","smtlib_trace_path","soft_timeout","solver","spc_bs","spc_es","spc_factor_subsumption_index_opt","spc_initial_subsumption_index_opt","spc_max_subsumption_index_features","spc_min_func_freq_subsumption_index","spc_num_iterations","spc_trace","statistics","strong_context_simplifier","tick","trace","trace_file_name","type_check","user_theory_persist_axioms","user_theory_preprocess_axioms","verbose","warning","well_sorted_check","z3_solver_ll_pp","z3_solver_smt_pp", 0 }; bool is_old_param_name(symbol const & name) { char const * const * it = g_old_params_names; while (*it) { if (name == *it) return true; it++; } return false; } static char const * g_params_renames[] = { "proof_mode", "proof", "soft_timeout", "timeout", "mbqi", "smt.mbqi", "relevancy", "smt.relevancy", "ematching", "smt.ematching", "macro_finder", "smt.macro_finder", "delay_units", "smt.delay_units", "case_split", "smt.case_split", "phase_selection", "smt.phase_selection", "restart_strategy", "smt.restart_strategy", "restart_factor", "smt.restart_factor", "arith_random_initial_value", "smt.arith.random_initial_value", "bv_reflect", "smt.bv.reflect", "bv_enable_int2bv_propagation", "smt.bv.enable_int2bv", "qi_cost", "smt.qi.cost", "qi_eager_threshold", "smt.qi.eager_threshold", "nl_arith", "smt.arith.nl", "pull_nested_quantifiers", "smt.pull_nested_quantifiers", "nnf_sk_hack", "nnf.sk_hack", "model_v2", "model.v2", "pi_non_nested_arith_weight", "pi.non_nested_arith_weight", "pi_warnings", "pi.warnings", "pp_decimal", "pp.decimal", "pp_decimal", "pp.decimal_precision", "pp_bv_literals", "pp.bv_literals", "pp_bv_neg", "pp.bv_neg", "pp_max_depth", "pp.max_depth", "pp_min_alias_size", "pp.min_alias_size", 0 }; char const * get_new_param_name(symbol const & p) { char const * const * it = g_params_renames; while (*it) { if (p == *it) { it++; return *it; } it += 2; } return 0; } struct gparams::imp { bool m_modules_registered; dictionary m_module_param_descrs; dictionary m_module_descrs; param_descrs m_param_descrs; dictionary m_module_params; params_ref m_params; void check_registered() { if (m_modules_registered) return; m_modules_registered = true; gparams_register_modules(); } dictionary & get_module_param_descrs() { check_registered(); return m_module_param_descrs; } dictionary & get_module_descrs() { check_registered(); return m_module_descrs; } param_descrs & get_param_descrs() { check_registered(); return m_param_descrs; } public: imp(): m_modules_registered(false) { } ~imp() { reset(); dictionary::iterator it = m_module_param_descrs.begin(); dictionary::iterator end = m_module_param_descrs.end(); for (; it != end; ++it) { dealloc(it->m_value); } } void reset() { #pragma omp critical (gparams) { m_params.reset(); dictionary::iterator it = m_module_params.begin(); dictionary::iterator end = m_module_params.end(); for (; it != end; ++it) { dealloc(it->m_value); } m_module_params.reset(); } } // ----------------------------------------------- // // Module registration routines. // They are invoked when descriptions are initialized // // ----------------------------------------------- void register_global(param_descrs & d) { // Don't need synchronization here, this method // is invoked from check_registered that is already protected. m_param_descrs.copy(d); } void register_module(char const * module_name, param_descrs * d) { // Don't need synchronization here, this method // is invoked from check_registered that is already protected. symbol s(module_name); param_descrs * old_d; if (m_module_param_descrs.find(s, old_d)) { old_d->copy(*d); dealloc(d); } else { m_module_param_descrs.insert(s, d); } } void register_module_descr(char const * module_name, char const * descr) { // Don't need synchronization here, this method // is invoked from check_registered that is already protected. m_module_descrs.insert(symbol(module_name), descr); } // ----------------------------------------------- // // Parameter setting & retrieval // // ----------------------------------------------- void normalize(char const * name, /* out */ symbol & mod_name, /* out */ symbol & param_name) { if (*name == ':') name++; std::string tmp = name; unsigned n = static_cast(tmp.size()); for (unsigned i = 0; i < n; i++) { if (tmp[i] >= 'A' && tmp[i] <= 'Z') tmp[i] = tmp[i] - 'A' + 'a'; else if (tmp[i] == '-') tmp[i] = '_'; } for (unsigned i = 0; i < n; i++) { if (tmp[i] == '.') { param_name = tmp.substr(i+1).c_str(); tmp.resize(i); mod_name = tmp.c_str(); return; } } param_name = tmp.c_str(); mod_name = symbol::null; } params_ref & get_params(symbol const & mod_name) { if (mod_name == symbol::null) { return m_params; } else { params_ref * p = 0; if (!m_module_params.find(mod_name, p)) { p = alloc(params_ref); m_module_params.insert(mod_name, p); } SASSERT(p != 0); return *p; } } void throw_unknown_parameter(symbol const & param_name, param_descrs const& d, symbol const & mod_name) { if (mod_name == symbol::null) { char const * new_name = get_new_param_name(param_name); if (new_name) { std::stringstream strm; strm << "the parameter '" << param_name << "', invoke 'z3 -p' to obtain the new parameter list, and 'z3 -pp:" << new_name << "' for the full description of the parameter"; throw exception(strm.str()); } else if (is_old_param_name(param_name)) { std::stringstream strm; strm << "unknown parameter '" << param_name << "', this is an old parameter name, invoke 'z3 -p' to obtain the new parameter list"; throw default_exception(strm.str()); } else { std::stringstream strm; strm << "unknown parameter '" << param_name << "'\n"; strm << "Legal parameters are:\n"; d.display(strm, 2, false, false); throw default_exception(strm.str()); } } else { std::stringstream strm; strm << "unknown parameter '" << param_name << "' "; strm << "at module '" << mod_name << "'\n"; strm << "Legal parameters are:\n"; d.display(strm, 2, false, false); throw default_exception(strm.str()); } } void validate_type(symbol const& name, char const* value, param_descrs const& d) { param_kind k = d.get_kind(name); std::stringstream strm; char const* _value = value; switch (k) { case CPK_UINT: for (; *value; ++value) { if (!('0' <= *value && *value <= '9')) { strm << "Expected values for parameter " << name << " is an unsigned integer. It was given argument '" << _value << "'"; throw default_exception(strm.str()); } } break; case CPK_DOUBLE: for (; *value; ++value) { if (!('0' <= *value && *value <= '9') && *value != '.' && *value != '-' && *value != '/') { strm << "Expected values for parameter " << name << " is a double. It was given argument '" << _value << "'"; throw default_exception(strm.str()); } } break; case CPK_BOOL: if (strcmp(value, "true") != 0 && strcmp(value, "false") != 0) { strm << "Expected values for parameter " << name << " are 'true' or 'false'. It was given argument '" << value << "'"; throw default_exception(strm.str()); } break; default: break; } } void set(param_descrs const & d, symbol const & param_name, char const * value, symbol const & mod_name) { param_kind k = d.get_kind(param_name); params_ref & ps = get_params(mod_name); if (k == CPK_INVALID) { throw_unknown_parameter(param_name, d, mod_name); } else if (k == CPK_UINT) { long val = strtol(value, 0, 10); ps.set_uint(param_name, static_cast(val)); } else if (k == CPK_DOUBLE) { char * aux; double val = strtod(value, &aux); ps.set_double(param_name, val); } else if (k == CPK_BOOL) { if (strcmp(value, "true") == 0) { ps.set_bool(param_name, true); } else if (strcmp(value, "false") == 0) { ps.set_bool(param_name, false); } else { std::stringstream strm; strm << "invalid value '" << value << "' for Boolean parameter '" << param_name << "'"; if (mod_name == symbol::null) { strm << " at module '" << mod_name << "'"; } throw default_exception(strm.str()); } } else if (k == CPK_SYMBOL) { ps.set_sym(param_name, symbol(value)); } else if (k == CPK_STRING) { // There is no guarantee that (external) callers will not delete value after invoking gparams::set. // I see two solutions: // 1) Modify params_ref to create a copy of set_str parameters. // This solution is not nice since we create copies and move the params_ref around. // We would have to keep copying the strings. // Moreover, when we use params_ref internally, the value is usually a static value. // So, we would be paying this price for nothing. // 2) "Copy" value by transforming it into a symbol. // I'm using this solution for now. ps.set_str(param_name, symbol(value).bare_str()); } else { std::stringstream strm; strm << "unsupported parameter type '" << param_name << "'"; if (mod_name == symbol::null) { strm << " at module '" << mod_name << "'"; } throw exception(strm.str()); } } void set(char const * name, char const * value) { bool error = false; std::string error_msg; #pragma omp critical (gparams) { try { symbol m, p; normalize(name, m, p); if (m == symbol::null) { validate_type(p, value, get_param_descrs()); set(get_param_descrs(), p, value, m); } else { param_descrs * d; if (get_module_param_descrs().find(m, d)) { validate_type(p, value, *d); set(*d, p, value, m); } else { std::stringstream strm; strm << "invalid parameter, unknown module '" << m << "'"; throw exception(strm.str()); } } } catch (z3_exception & ex) { // Exception cannot cross critical section boundaries. error = true; error_msg = ex.msg(); } } if (error) throw exception(error_msg); } std::string get_value(params_ref const & ps, symbol const & p) { std::ostringstream buffer; ps.display(buffer, p); return buffer.str(); } std::string get_default(param_descrs const & d, symbol const & p, symbol const & m) { if (!d.contains(p)) { throw_unknown_parameter(p, d, m); } char const * r = d.get_default(p); if (r == 0) return "default"; return r; } std::string get_value(char const * name) { std::string r; bool error = false; std::string error_msg; #pragma omp critical (gparams) { try { symbol m, p; normalize(name, m, p); if (m == symbol::null) { if (m_params.contains(p)) { r = get_value(m_params, p); } else { r = get_default(get_param_descrs(), p, m); } } else { params_ref * ps = 0; if (m_module_params.find(m, ps) && ps->contains(p)) { r = get_value(*ps, p); } else { param_descrs * d; if (get_module_param_descrs().find(m, d)) { r = get_default(*d, p, m); } else { std::stringstream strm; strm << "unknown module '" << m << "'"; throw exception(strm.str()); } } } } catch (z3_exception & ex) { // Exception cannot cross critical section boundaries. error = true; error_msg = ex.msg(); } } if (error) throw exception(error_msg); return r; } params_ref get_module(symbol const & module_name) { params_ref result; params_ref * ps = 0; #pragma omp critical (gparams) { if (m_module_params.find(module_name, ps)) { result = *ps; } } return result; } params_ref get() { params_ref result; TRACE("gparams", tout << "get() m_params: " << m_params << "\n";); #pragma omp critical (gparams) { result = m_params; } return result; } // ----------------------------------------------- // // Pretty printing // // ----------------------------------------------- void display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) { #pragma omp critical (gparams) { out << "Global parameters\n"; get_param_descrs().display(out, indent + 4, smt2_style, include_descr); out << "\n"; if (!smt2_style) { out << "To set a module parameter, use .=value\n"; out << "Example: pp.decimal=true\n"; out << "\n"; } dictionary::iterator it = get_module_param_descrs().begin(); dictionary::iterator end = get_module_param_descrs().end(); for (; it != end; ++it) { out << "[module] " << it->m_key; char const * descr = 0; if (get_module_descrs().find(it->m_key, descr)) { out << ", description: " << descr; } out << "\n"; it->m_value->display(out, indent + 4, smt2_style, include_descr); } } } void display_modules(std::ostream & out) { #pragma omp critical (gparams) { dictionary::iterator it = get_module_param_descrs().begin(); dictionary::iterator end = get_module_param_descrs().end(); for (; it != end; ++it) { out << "[module] " << it->m_key; char const * descr = 0; if (get_module_descrs().find(it->m_key, descr)) { out << ", description: " << descr; } out << "\n"; } } } void display_module(std::ostream & out, symbol const & module_name) { bool error = false; std::string error_msg; #pragma omp critical (gparams) { try { param_descrs * d = 0; if (!get_module_param_descrs().find(module_name, d)) { std::stringstream strm; strm << "unknown module '" << module_name << "'"; throw exception(strm.str()); } out << "[module] " << module_name; char const * descr = 0; if (get_module_descrs().find(module_name, descr)) { out << ", description: " << descr; } out << "\n"; d->display(out, 4, false); } catch (z3_exception & ex) { // Exception cannot cross critical section boundaries. error = true; error_msg = ex.msg(); } } if (error) throw exception(error_msg); } void display_parameter(std::ostream & out, char const * name) { bool error = false; std::string error_msg; #pragma omp critical (gparams) { try { symbol m, p; normalize(name, m, p); std::cout << name << " " << m << " " << p << "\n"; param_descrs * d; if (m == symbol::null) { d = &get_param_descrs(); } else { if (!get_module_param_descrs().find(m, d)) { std::stringstream strm; strm << "unknown module '" << m << "'"; throw exception(strm.str()); } } if (!d->contains(p)) throw_unknown_parameter(p, *d, m); out << " name: " << p << "\n"; if (m != symbol::null) { out << " module: " << m << "\n"; out << " qualified name: " << m << "." << p << "\n"; } out << " type: " << d->get_kind(p) << "\n"; out << " description: " << d->get_descr(p) << "\n"; out << " default value: " << d->get_default(p) << "\n"; } catch (z3_exception & ex) { // Exception cannot cross critical section boundaries. error = true; error_msg = ex.msg(); } } if (error) throw exception(error_msg); } }; gparams::imp * gparams::g_imp = 0; void gparams::reset() { SASSERT(g_imp != 0); g_imp->reset(); } void gparams::set(char const * name, char const * value) { TRACE("gparams", tout << "setting [" << name << "] <- '" << value << "'\n";); SASSERT(g_imp != 0); g_imp->set(name, value); } void gparams::set(symbol const & name, char const * value) { SASSERT(g_imp != 0); g_imp->set(name.bare_str(), value); } std::string gparams::get_value(char const * name) { SASSERT(g_imp != 0); return g_imp->get_value(name); } std::string gparams::get_value(symbol const & name) { SASSERT(g_imp != 0); return g_imp->get_value(name.bare_str()); } void gparams::register_global(param_descrs & d) { SASSERT(g_imp != 0); g_imp->register_global(d); } void gparams::register_module(char const * module_name, param_descrs * d) { SASSERT(g_imp != 0); g_imp->register_module(module_name, d); } void gparams::register_module_descr(char const * module_name, char const * descr) { SASSERT(g_imp != 0); g_imp->register_module_descr(module_name, descr); } params_ref gparams::get_module(char const * module_name) { return get_module(symbol(module_name)); } params_ref gparams::get_module(symbol const & module_name) { SASSERT(g_imp != 0); return g_imp->get_module(module_name); } params_ref gparams::get() { TRACE("gparams", tout << "gparams::get()\n";); SASSERT(g_imp != 0); return g_imp->get(); } void gparams::display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) { SASSERT(g_imp != 0); g_imp->display(out, indent, smt2_style, include_descr); } void gparams::display_modules(std::ostream & out) { SASSERT(g_imp != 0); g_imp->display_modules(out); } void gparams::display_module(std::ostream & out, char const * module_name) { SASSERT(g_imp != 0); g_imp->display_module(out, symbol(module_name)); } void gparams::display_parameter(std::ostream & out, char const * name) { SASSERT(g_imp != 0); g_imp->display_parameter(out, name); } void gparams::init() { TRACE("gparams", tout << "gparams::init()\n";); g_imp = alloc(imp); } void gparams::finalize() { TRACE("gparams", tout << "gparams::finalize()\n";); if (g_imp != 0) { dealloc(g_imp); g_imp = 0; } } z3-z3-4.4.1/src/util/gparams.h000066400000000000000000000111231260446376700157720ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: gparams.h Abstract: Global parameter management. Author: Leonardo (leonardo) 2012-11-29 Notes: --*/ #ifndef GPARAMS_H_ #define GPARAMS_H_ #include"params.h" class gparams { struct imp; static imp * g_imp; public: typedef default_exception exception; /** \brief Reset all global and module parameters. */ static void reset(); /** \brief Set a global parameter \c name with \c value. 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: "auto-config" and "AUTO_CONFIG". 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. An exception is thrown if the the parameter name is unknown, or if the value is incorrect. */ static void set(char const * name, char const * value); static void set(symbol const & name, char const * value); /** \brief Auxiliary method used to implement get-option in SMT 2.0 front-end. If the parameter is not set, then it just returns 'default'. An exception is thrown if the the parameter name is unknown. */ static std::string get_value(char const * name); static std::string get_value(symbol const & name); /** \brief Register additional global parameters This is an auxiliary function used by our automatic code generator. Example: the directive REG_PARAMS('collect_param_descrs') "tells" the automatic code generator how to register the additional global parameters. */ static void register_global(param_descrs & d); /** \brief Register parameter descriptions for a Z3 module. The parameters of a given Z3 module can only be set using #set_global_param if they are registered in this module using this function. This is an auxiliary function used by our automatic code generator. Each module will contain directives (in comments) such as Example: the directive REG_MODULE_PARAMS('nlsat', 'nlsat::solver::collect_param_descrs') "tells" the automatic code generator how to register the parameters for the given module. */ static void register_module(char const * module_name, param_descrs * d); /** \brief Add a (small) description to the given module. */ static void register_module_descr(char const * module_name, char const * descr); /** \brief Retrieves the parameters associated with the given module. Example: // The following command sets the parameter "decimal" in the module "pp" to true. set_global_param("pp.decimal", "true"); ... // The following command will return the global parameters that were set for the module "pp". // In this example "p" will contain "decimal" -> true after executing this function. params_ref const & p = get_module_params("pp") */ static params_ref get_module(char const * module_name); static params_ref get_module(symbol const & module_name); /** \brief Return the global parameter set (i.e., parameters that are not associated with any particular module). */ static params_ref get(); /** \brief Dump information about available parameters in the given output stream. */ static void display(std::ostream & out, unsigned indent = 0, bool smt2_style=false, bool include_descr=true); // Auxiliary APIs for better command line support static void display_modules(std::ostream & out); static void display_module(std::ostream & out, char const * module_name); static void display_parameter(std::ostream & out, char const * name); /** \brief Initialize the global parameter management module. Remark: I set a priority in the initialization, because this module must be initialized after the core modules such as symbol. ADD_INITIALIZER('gparams::init();', 1) */ static void init(); /** \brief Finalize the global parameter management module. ADD_FINALIZER('gparams::finalize();'); */ static void finalize(); }; #endif z3-z3-4.4.1/src/util/hash.cpp000066400000000000000000000041651260446376700156260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: hash.cpp Abstract: Basic hash computation support. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #include"debug.h" #include"hash.h" #include static unsigned read_unsigned(const char *s) { unsigned n; memcpy(&n, s, sizeof(unsigned)); return n; } // I'm using Bob Jenkin's hash function. // http://burtleburtle.net/bob/hash/doobs.html unsigned string_hash(const char * str, unsigned length, unsigned init_value) { unsigned a, b, c, len; /* Set up the internal state */ len = length; a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ c = init_value; /* the previous hash value */ /*---------------------------------------- handle most of the key */ SASSERT(sizeof(unsigned) == 4); while (len >= 12) { a += read_unsigned(str); b += read_unsigned(str+4); c += read_unsigned(str+8); mix(a,b,c); str += 12; len -= 12; } /*------------------------------------- handle the last 11 bytes */ c += length; switch(len) { /* all the case statements fall through */ case 11: c+=((unsigned)str[10]<<24); __fallthrough; case 10: c+=((unsigned)str[9]<<16); __fallthrough; case 9 : c+=((unsigned)str[8]<<8); __fallthrough; /* the first byte of c is reserved for the length */ case 8 : b+=((unsigned)str[7]<<24); __fallthrough; case 7 : b+=((unsigned)str[6]<<16); __fallthrough; case 6 : b+=((unsigned)str[5]<<8); __fallthrough; case 5 : b+=str[4]; __fallthrough; case 4 : a+=((unsigned)str[3]<<24); __fallthrough; case 3 : a+=((unsigned)str[2]<<16); __fallthrough; case 2 : a+=((unsigned)str[1]<<8); __fallthrough; case 1 : a+=str[0]; __fallthrough; /* case 0: nothing left to add */ } mix(a,b,c); /*-------------------------------------------- report the result */ return c; } z3-z3-4.4.1/src/util/hash.h000066400000000000000000000140701260446376700152670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: hash.h Abstract: Basic hash computation support. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #ifndef HASH_H_ #define HASH_H_ #include #include"util.h" #ifndef __fallthrough #define __fallthrough #endif #define mix(a,b,c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<<8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>>5); \ a -= b; a -= c; a ^= (c>>3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } inline unsigned hash_u(unsigned a) { a = (a+0x7ed55d16) + (a<<12); a = (a^0xc761c23c) ^ (a>>19); a = (a+0x165667b1) + (a<<5); a = (a+0xd3a2646c) ^ (a<<9); a = (a+0xfd7046c5) + (a<<3); a = (a^0xb55a4f09) ^ (a>>16); return a; } inline unsigned hash_ull(unsigned long long a) { a = (~a) + (a << 18); a ^= (a >> 31); a += (a << 2) + (a << 4); a ^= (a >> 11); a += (a << 6); a ^= (a >> 22); return static_cast(a); } inline unsigned combine_hash(unsigned h1, unsigned h2) { h2 -= h1; h2 ^= (h1 << 8); h1 -= h2; h2 ^= (h1 << 16); h2 -= h1; h2 ^= (h1 << 10); return h2; } inline unsigned hash_u_u(unsigned a, unsigned b) { return combine_hash(hash_u(a), hash_u(b)); } unsigned string_hash(const char * str, unsigned len, unsigned init_value); template unsigned get_composite_hash(Composite app, unsigned n, GetKindHashProc const & khasher = GetKindHashProc(), GetChildHashProc const & chasher = GetChildHashProc()) { unsigned a, b, c; SASSERT(n > 0); unsigned kind_hash = khasher(app); a = b = 0x9e3779b9; c = 11; switch (n) { case 1: a += kind_hash; b = chasher(app, 0); mix(a, b, c); return c; case 2: a += kind_hash; b += chasher(app, 0); c += chasher(app, 1); mix(a, b, c); return c; case 3: a += chasher(app, 0); b += chasher(app, 1); c += chasher(app, 2); mix(a, b, c); a += kind_hash; mix(a, b, c); return c; default: while (n >= 3) { n--; a += chasher(app, n); n--; b += chasher(app, n); n--; c += chasher(app, n); mix(a, b, c); } a += kind_hash; switch (n) { case 2: b += chasher(app, 1); __fallthrough; case 1: c += chasher(app, 0); } mix(a, b, c); return c; } } template struct default_kind_hash_proc { unsigned operator()(Composite const & c) const { return 17; } }; struct int_hash { typedef int data; unsigned operator()(int x) const { return static_cast(x); } }; struct unsigned_hash { typedef unsigned data; unsigned operator()(unsigned x) const { return x; } }; struct size_t_hash { typedef size_t data; unsigned operator()(size_t x) const { return static_cast(x); } }; struct uint64_hash { typedef uint64 data; unsigned operator()(uint64 x) const { return static_cast(x); } }; struct bool_hash { typedef bool data; unsigned operator()(bool x) const { return static_cast(x); } }; template struct obj_hash { typedef T data; unsigned operator()(const T & e) const { return e.hash(); } }; template struct obj_ptr_hash { typedef T * data; unsigned operator()(T * a) const { return a->hash(); } }; template struct obj_ptr_pair_hash { typedef std::pair data; unsigned operator()(data const & d) const { return combine_hash(d.first->hash(), d.second->hash()); } }; template struct triple { T1 first; T2 second; T3 third; triple(): first(T1()), second(T2()), third(T3()) {} triple(T1 f, T2 s, T3 t): first(f), second(s), third(t) {} bool operator==(triple const& other) const { return first == other.first && second == other.second && third == other.third; } }; template struct triple_hash : private Hash1 { Hash2 m_hash2; Hash3 m_hash3; typedef triple data; triple_hash(Hash1 const & h1 = Hash1(), Hash2 const & h2 = Hash2(), Hash3 const & h3 = Hash3()): Hash1(h1), m_hash2(h2), m_hash3(h3) { } unsigned operator()(std::pair const & p) const { return combine_hash(combine_hash(Hash1::operator()(p.first), m_hash2.operator()(p.second)), m_hash3.operator()(p.third)); } }; template struct obj_ptr_triple_hash { typedef triple data; unsigned operator()(data const & d) const { return combine_hash(combine_hash(d.first->hash(), d.second->hash()), d.third->hash()); } }; template struct pair_hash : private Hash1 { Hash2 m_hash2; typedef std::pair data; pair_hash(Hash1 const & h1 = Hash1(), Hash2 const & h2 = Hash2()): Hash1(h1), m_hash2(h2) { } unsigned operator()(std::pair const & p) const { return combine_hash(Hash1::operator()(p.first), m_hash2.operator()(p.second)); } }; template inline unsigned get_ptr_hash(T * ptr) { return static_cast(reinterpret_cast(ptr)); } template struct ptr_hash { typedef T * data; unsigned operator()(T * ptr) const { return get_ptr_hash(ptr); } }; inline unsigned mk_mix(unsigned a, unsigned b, unsigned c) { mix(a, b, c); return c; } #endif /* HASH_H_ */ z3-z3-4.4.1/src/util/hashtable.h000066400000000000000000000574371260446376700163150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: hashtable.h Abstract: Hashtable without buckets. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #ifndef HASHTABLE_H_ #define HASHTABLE_H_ #include"debug.h" #include #include"util.h" #include #include"memory_manager.h" #include"hash.h" #define DEFAULT_HASHTABLE_INITIAL_CAPACITY 8 #define SMALL_TABLE_CAPACITY 64 // #define HASHTABLE_STATISTICS #ifdef HASHTABLE_STATISTICS #define HS_CODE(CODE) { CODE } #else #define HS_CODE(CODE) #endif typedef enum { HT_FREE, HT_DELETED, HT_USED } hash_entry_state; template class default_hash_entry { unsigned m_hash; //!< cached hash code hash_entry_state m_state; T m_data; public: typedef T data; default_hash_entry():m_state(HT_FREE) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_state == HT_FREE; } bool is_deleted() const { return m_state == HT_DELETED; } bool is_used() const { return m_state == HT_USED; } T & get_data() { return m_data; } const T & get_data() const { return m_data; } void set_data(const T & d) { m_data = d; m_state = HT_USED; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_state = HT_DELETED; } void mark_as_free() { m_state = HT_FREE; } }; /** \brief Special entry for a hashtable of integers. This entry "steals" two values for representing HT_FREE and HT_DELETED. */ template class int_hash_entry { unsigned m_hash; //!< cached hash code int m_data; public: typedef int data; int_hash_entry():m_data(Free) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_data == Free; } bool is_deleted() const { return m_data == Deleted; } bool is_used() const { return m_data != Free && m_data != Deleted; } int get_data() const { return m_data; } int & get_data() { return m_data; } void set_data(int d) { m_data = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_data = Deleted; } void mark_as_free() { m_data = Free; } }; /** \brief Special entry for a hashtable of pointers. This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED. */ template class ptr_hash_entry { unsigned m_hash; //!< cached hash code T * m_ptr; public: typedef T * data; ptr_hash_entry():m_ptr(0) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_ptr == 0; } bool is_deleted() const { return m_ptr == reinterpret_cast(1); } bool is_used() const { return m_ptr != reinterpret_cast(0) && m_ptr != reinterpret_cast(1); } T * get_data() const { return m_ptr; } T * & get_data() { return m_ptr; } void set_data(T * d) { m_ptr = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_ptr = reinterpret_cast(1); } void mark_as_free() { m_ptr = 0; } }; /** \brief Special entry for a hashtable of pointers which uses the pointer itself as the hashcode. This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED. */ template class ptr_addr_hash_entry : public ptr_hash_entry { T * m_ptr; public: typedef T * data; ptr_addr_hash_entry():m_ptr(0) {} unsigned get_hash() const { return get_ptr_hash(m_ptr); } bool is_free() const { return m_ptr == 0; } bool is_deleted() const { return m_ptr == reinterpret_cast(1); } bool is_used() const { return m_ptr != reinterpret_cast(0) && m_ptr != reinterpret_cast(1); } T * get_data() const { return m_ptr; } T * & get_data() { return m_ptr; } void set_data(T * d) { m_ptr = d; } void set_hash(unsigned h) { SASSERT(h == get_ptr_hash(m_ptr)); /* do nothing */ } void mark_as_deleted() { m_ptr = reinterpret_cast(1); } void mark_as_free() { m_ptr = 0; } }; template class core_hashtable : private HashProc, private EqProc { protected: Entry * m_table; unsigned m_capacity; unsigned m_size; unsigned m_num_deleted; #ifdef HASHTABLE_STATISTICS unsigned long long m_st_collision; #endif Entry* alloc_table(unsigned size) { Entry* entries = alloc_vect(size); return entries; } void delete_table() { dealloc_vect(m_table, m_capacity); m_table = 0; } public: typedef typename Entry::data data; typedef Entry entry; protected: unsigned get_hash(data const & e) const { return HashProc::operator()(e); } bool equals(data const & e1, data const & e2) const { return EqProc::operator()(e1, e2); } static void copy_table(entry * source, unsigned source_capacity, entry * target, unsigned target_capacity) { SASSERT(target_capacity >= source_capacity); unsigned target_mask = target_capacity - 1; entry * source_end = source + source_capacity; entry * target_end = target + target_capacity; for (entry * source_curr = source; source_curr != source_end; ++source_curr) { if (source_curr->is_used()) { unsigned hash = source_curr->get_hash(); unsigned idx = hash & target_mask; entry * target_begin = target + idx; entry * target_curr = target_begin; for (; target_curr != target_end; ++target_curr) { SASSERT(!target_curr->is_deleted()); if (target_curr->is_free()) { *target_curr = *source_curr; goto end; } } for (target_curr = target; target_curr != target_begin; ++target_curr) { SASSERT(!target_curr->is_deleted()); if (target_curr->is_free()) { *target_curr = *source_curr; goto end; } } UNREACHABLE(); end: ; } } } void expand_table() { unsigned new_capacity = m_capacity << 1; entry * new_table = alloc_table(new_capacity); copy_table(m_table, m_capacity, new_table, new_capacity); delete_table(); m_table = new_table; m_capacity = new_capacity; m_num_deleted = 0; } void remove_deleted_entries() { if (memory::is_out_of_memory()) return; entry * new_table = alloc_table(m_capacity); copy_table(m_table, m_capacity, new_table, m_capacity); delete_table(); m_table = new_table; m_num_deleted = 0; } public: core_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY, HashProc const & h = HashProc(), EqProc const & e = EqProc()): HashProc(h), EqProc(e) { SASSERT(is_power_of_two(initial_capacity)); m_table = alloc_table(initial_capacity); m_capacity = initial_capacity; m_size = 0; m_num_deleted = 0; HS_CODE({ m_st_collision = 0; }); } core_hashtable(const core_hashtable & source): HashProc(source), EqProc(source) { m_capacity = source.m_capacity; m_table = alloc_table(m_capacity); copy_table(source.m_table, m_capacity, m_table, m_capacity); m_size = source.m_size; m_num_deleted = 0; HS_CODE({ m_st_collision = 0; }); } ~core_hashtable() { delete_table(); } void swap(core_hashtable & source) { std::swap(m_table, source.m_table); std::swap(m_capacity, source.m_capacity); std::swap(m_size, source.m_size); std::swap(m_num_deleted, source.m_num_deleted); HS_CODE({ std::swap(m_st_collision, source.m_st_collision); }); } void reset() { if (m_size == 0 && m_num_deleted == 0) return; unsigned overhead = 0; entry * curr = m_table; entry * end = m_table + m_capacity; for (; curr != end; ++curr) { if (!curr->is_free()) curr->mark_as_free(); else overhead++; } if (m_capacity > 16 && overhead << 2 > (m_capacity * 3)) { delete_table(); SASSERT(m_capacity > 16); SASSERT(is_power_of_two(m_capacity)); m_capacity = (m_capacity >> 1); SASSERT(is_power_of_two(m_capacity)); m_table = alloc_table(m_capacity); } m_size = 0; m_num_deleted = 0; } void finalize() { if (m_capacity > SMALL_TABLE_CAPACITY) { delete_table(); m_table = alloc_table(SMALL_TABLE_CAPACITY); m_capacity = SMALL_TABLE_CAPACITY; m_size = 0; m_num_deleted = 0; } else { reset(); } } class iterator { entry * m_curr; entry * m_end; void move_to_used() { while (m_curr != m_end && !m_curr->is_used()) { m_curr++; } } public: iterator(entry * start, entry * end): m_curr(start), m_end(end) { move_to_used(); } data & operator*() { return m_curr->get_data(); } data const & operator*() const { return m_curr->get_data(); } data const * operator->() const { return &(operator*()); } data * operator->() { return &(operator*()); } iterator & operator++() { ++m_curr; move_to_used(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const & it) const { return m_curr == it.m_curr; } bool operator!=(iterator const & it) const { return m_curr != it.m_curr; } }; bool empty() const { return m_size == 0; } unsigned size() const { return m_size; } unsigned capacity() const { return m_capacity; } iterator begin() const { return iterator(m_table, m_table + m_capacity); } iterator end() const { return iterator(m_table + m_capacity, m_table + m_capacity); } #define INSERT_LOOP_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ curr->set_data(e); \ return; \ } \ HS_CODE(m_st_collision++;); \ } \ else if (curr->is_free()) { \ entry * new_entry; \ if (del_entry) { new_entry = del_entry; m_num_deleted--; } \ else { new_entry = curr; } \ new_entry->set_data(e); \ new_entry->set_hash(hash); \ m_size++; \ return; \ } \ else { \ SASSERT(curr->is_deleted()); \ del_entry = curr; \ HS_CODE(m_st_collision++;); \ } \ } ((void) 0) void insert(data const & e) { if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) { // if ((m_size + m_num_deleted) * 2 > (m_capacity)) { expand_table(); } unsigned hash = get_hash(e); unsigned mask = m_capacity - 1; unsigned idx = hash & mask; entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; entry * del_entry = 0; for (; curr != end; ++curr) { INSERT_LOOP_BODY(); } for (curr = m_table; curr != begin; ++curr) { INSERT_LOOP_BODY(); } UNREACHABLE(); } #define INSERT_LOOP_CORE_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ et = curr; \ return false; \ } \ HS_CODE(m_st_collision++;); \ } \ else if (curr->is_free()) { \ entry * new_entry; \ if (del_entry) { new_entry = del_entry; m_num_deleted--; } \ else { new_entry = curr; } \ new_entry->set_data(e); \ new_entry->set_hash(hash); \ m_size++; \ et = new_entry; \ return true; \ } \ else { \ SASSERT(curr->is_deleted()); \ del_entry = curr; \ HS_CODE(m_st_collision++;); \ } \ } ((void) 0) /** \brief Insert the element e if it is not in the table. Return true if it is a new element, and false otherwise. Store the entry/slot of the table in et. */ bool insert_if_not_there_core(data const & e, entry * & et) { if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) { // if ((m_size + m_num_deleted) * 2 > (m_capacity)) { expand_table(); } unsigned hash = get_hash(e); unsigned mask = m_capacity - 1; unsigned idx = hash & mask; entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; entry * del_entry = 0; for (; curr != end; ++curr) { INSERT_LOOP_CORE_BODY(); } for (curr = m_table; curr != begin; ++curr) { INSERT_LOOP_CORE_BODY(); } UNREACHABLE(); return 0; } /** \brief Insert the element e if it is not in the table. Return a reference to e or to an object identical to e that was already in the table. */ data const & insert_if_not_there(data const & e) { entry * et; insert_if_not_there_core(e, et); return et->get_data(); } /** \brief Insert the element e if it is not in the table. Return the entry that contains e. */ entry * insert_if_not_there2(data const & e) { entry * et; insert_if_not_there_core(e, et); return et; } #define FIND_LOOP_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ return curr; \ } \ HS_CODE(const_cast(this)->m_st_collision++;); \ } \ else if (curr->is_free()) { \ return 0; \ } \ HS_CODE(const_cast(this)->m_st_collision++;); \ } ((void) 0) entry * find_core(data const & e) const { unsigned hash = get_hash(e); unsigned mask = m_capacity - 1; unsigned idx = hash & mask; entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; for (; curr != end; ++curr) { FIND_LOOP_BODY(); } for (curr = m_table; curr != begin; ++curr) { FIND_LOOP_BODY(); } return 0; } bool find(data const & k, data & r) const { entry * e = find_core(k); if (e != 0) { r = e->get_data(); return true; } return false; } bool contains(data const & e) const { return find_core(e) != 0; } iterator find(data const & e) const { entry * r = find_core(e); if (r) { return iterator(r, m_table + m_capacity); } else { return end(); } } #define REMOVE_LOOP_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ goto end_remove; \ } \ HS_CODE(m_st_collision++;); \ } \ else if (curr->is_free()) { \ return; \ } \ HS_CODE(m_st_collision++;); \ } ((void) 0) void remove(data const & e) { unsigned hash = get_hash(e); unsigned mask = m_capacity - 1; unsigned idx = hash & mask; entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; for (; curr != end; ++curr) { REMOVE_LOOP_BODY(); } for (curr = m_table; curr != begin; ++curr) { REMOVE_LOOP_BODY(); } SASSERT(!contains(e)); return; // node is not in the table end_remove: entry * next = curr + 1; if (next == end) { next = m_table; } if (next->is_free()) { curr->mark_as_free(); m_size--; } else { curr->mark_as_deleted(); m_num_deleted++; m_size--; if (m_num_deleted > m_size && m_num_deleted > SMALL_TABLE_CAPACITY) { remove_deleted_entries(); } } } void erase(data const & e) { remove(e); } void dump(std::ostream & out) { entry * curr = m_table; entry * end = m_table + m_capacity; out << "["; bool first = true; for (; curr != end; ++curr) { if (curr->is_used()) { if (first) { first = false; } else { out << " "; } out << curr->get_data(); } } out << "]"; } #ifdef Z3DEBUG bool check_invariant() { entry * curr = m_table; entry * end = m_table + m_capacity; unsigned num_deleted = 0; unsigned num_used = 0; for (; curr != end; ++curr) { if (curr->is_deleted()) { num_deleted ++; } if (curr->is_used()) { num_used++; } } SASSERT(num_deleted == m_num_deleted); SASSERT(num_used == m_size); return true; } #endif #ifdef HASHTABLE_STATISTICS unsigned long long get_num_collision() const { return m_st_collision; } #else unsigned long long get_num_collision() const { return 0; } #endif private: core_hashtable& operator=(core_hashtable const&); }; template class hashtable : public core_hashtable, HashProc, EqProc> { public: hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY, HashProc const & h = HashProc(), EqProc const & e = EqProc()): core_hashtable, HashProc, EqProc>(initial_capacity, h, e) {} }; template class ptr_hashtable : public core_hashtable, HashProc, EqProc> { public: ptr_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY, HashProc const & h = HashProc(), EqProc const & e = EqProc()): core_hashtable, HashProc, EqProc>(initial_capacity, h, e) {} }; /** \brief Hashtable of pointers which use the pointer as the hash-code. */ template class ptr_addr_hashtable : public core_hashtable, ptr_hash, ptr_eq > { public: typedef typename core_hashtable, ptr_hash, ptr_eq >::iterator iterator; ptr_addr_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY): core_hashtable, ptr_hash, ptr_eq >(initial_capacity) {} // Using iterators to traverse the elements of this kind of hashtable will produce non-determinism. iterator begin() const { UNREACHABLE(); } iterator end() const { UNREACHABLE(); } }; /** \brief Simple int_hashtable. The values INT_MIN and INT_MIN + 1 are used to mark deleted and free slots. So, these values cannot be stored in the table. Use core_hashtable template to avoid this limitation. */ template class int_hashtable : public core_hashtable, HashProc, EqProc> { public: int_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY, HashProc const & h = HashProc(), EqProc const & e = EqProc()): core_hashtable, HashProc, EqProc>(initial_capacity, h, e) {} }; #endif /* HASHTABLE_H_ */ z3-z3-4.4.1/src/util/heap.h000066400000000000000000000166021260446376700152640ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: heap.h Abstract: A heap of integers. Author: Leonardo de Moura (leonardo) 2006-09-14. Revision History: --*/ #ifndef HEAP_H_ #define HEAP_H_ #include"vector.h" #include"debug.h" template class heap : private LT { int_vector m_values; int_vector m_value2indices; bool less_than(int v1, int v2) const { return LT::operator()(v1, v2); } static int left(int i) { return i << 1; } static int right(int i) { return (i << 1) + 1; } static int parent(int i) { return i >> 1; } #ifdef Z3DEBUG // Return true if the value can be inserted in the heap. That is, the vector m_value2indices is big enough to store this value. bool is_valid_value(int v) const { SASSERT(v >= 0 && v < static_cast(m_value2indices.size())); return true; } bool check_invariant_core(int idx) const { if (idx < static_cast(m_values.size())) { SASSERT(m_value2indices[m_values[idx]] == idx); SASSERT(parent(idx) == 0 || !less_than(m_values[idx], m_values[parent(idx)])); SASSERT(check_invariant_core(left(idx))); SASSERT(check_invariant_core(right(idx))); } return true; } public: bool check_invariant() const { return check_invariant_core(1); } #endif private: void move_up(int idx) { int val = m_values[idx]; while (true) { int parent_idx = parent(idx); if (parent_idx == 0 || !less_than(val, m_values[parent_idx])) { break; } m_values[idx] = m_values[parent_idx]; m_value2indices[m_values[idx]] = idx; idx = parent_idx; } m_values[idx] = val; m_value2indices[val] = idx; CASSERT("heap", check_invariant()); } void move_down(int idx) { int val = m_values[idx]; int sz = static_cast(m_values.size()); while (true) { int left_idx = left(idx); if (left_idx >= sz) { break; } int right_idx = right(idx); int min_idx = right_idx < sz && less_than(m_values[right_idx], m_values[left_idx]) ? right_idx : left_idx; SASSERT(parent(min_idx) == idx); int min_value = m_values[min_idx]; if (!less_than(min_value, val)) { break; } m_values[idx] = min_value; m_value2indices[min_value] = idx; idx = min_idx; } m_values[idx] = val; m_value2indices[val] = idx; CASSERT("heap", check_invariant()); } public: typedef int * iterator; typedef const int * const_iterator; heap(int s, const LT & lt = LT()):LT(lt) { m_values.push_back(-1); set_bounds(s); CASSERT("heap", check_invariant()); } bool empty() const { return m_values.size() == 1; } bool contains(int val) const { return val < static_cast(m_value2indices.size()) && m_value2indices[val] != 0; } void reset() { CASSERT("heap", check_invariant()); if (empty()) { return; } memset(m_value2indices.begin(), 0, sizeof(int) * m_value2indices.size()); m_values.reset(); m_values.push_back(-1); CASSERT("heap", check_invariant()); } void clear() { reset(); } void set_bounds(int s) { m_value2indices.resize(s, 0); CASSERT("heap", check_invariant()); } unsigned get_bounds() const { return m_value2indices.size(); } void reserve(int s) { CASSERT("heap", check_invariant()); if (s > static_cast(m_value2indices.size())) set_bounds(s); CASSERT("heap", check_invariant()); } int min_value() const { SASSERT(!empty()); return m_values[1]; } int erase_min() { CASSERT("heap", check_invariant()); SASSERT(!empty()); SASSERT(m_values.size() >= 2); int result = m_values[1]; if (m_values.size() == 2) { m_value2indices[result] = 0; m_values.pop_back(); SASSERT(empty()); } else { int last_val = m_values.back(); m_values[1] = last_val; m_value2indices[last_val] = 1; m_value2indices[result] = 0; m_values.pop_back(); move_down(1); } CASSERT("heap", check_invariant()); return result; } void erase(int val) { CASSERT("heap", check_invariant()); SASSERT(contains(val)); int idx = m_value2indices[val]; if (idx == static_cast(m_values.size()) - 1) { m_value2indices[val] = 0; m_values.pop_back(); } else { int last_val = m_values.back(); m_values[idx] = last_val; m_value2indices[last_val] = idx; m_value2indices[val] = 0; m_values.pop_back(); int parent_idx = parent(idx); if (parent_idx != 0 && less_than(last_val, m_values[parent(idx)])) { move_up(idx); } else { move_down(idx); } } CASSERT("heap", check_invariant()); } void decreased(int val) { SASSERT(contains(val)); move_up(m_value2indices[val]); } void increased(int val) { SASSERT(contains(val)); move_down(m_value2indices[val]); } void insert(int val) { CASSERT("heap", check_invariant()); SASSERT(is_valid_value(val)); int idx = static_cast(m_values.size()); m_value2indices[val] = idx; m_values.push_back(val); SASSERT(idx == static_cast(m_values.size()) - 1); move_up(idx); CASSERT("heap", check_invariant()); } iterator begin() { return m_values.begin() + 1; } iterator end() { return m_values.end(); } const_iterator begin() const { return m_values.begin() + 1; } const_iterator end() const { return m_values.end(); } void swap(heap & other) { if (this != &other) { CASSERT("heap", other.check_invariant()); CASSERT("heap", check_invariant()); m_values.swap(other.m_values); m_value2indices.swap(other.m_value2indices); CASSERT("heap", other.check_invariant()); CASSERT("heap", check_invariant()); } } /** \brief return set of values in heap that are less or equal to val. */ void find_le(int val, int_vector& result) { int_vector todo; todo.push_back(1); while (!todo.empty()) { int index = todo.back(); todo.pop_back(); if (index < static_cast(m_values.size()) && !less_than(val, m_values[index])) { result.push_back(m_values[index]); todo.push_back(left(index)); todo.push_back(right(index)); } } } }; #endif /* HEAP_H_ */ z3-z3-4.4.1/src/util/hwf.cpp000066400000000000000000000463151260446376700154720ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: hwf.cpp Abstract: Hardware Floating Point Numbers Author: Christoph Wintersteiger (cwinter) 2012-07-30. Revision History: --*/ #include #include #include #ifdef _WINDOWS #pragma float_control( except, on ) // exception semantics; this does _not_ mean that exceptions are enabled (we want them off!) #pragma float_control( precise, on ) // precise semantics (no guessing!) #pragma fp_contract(off) // contractions off (`contraction' means x*y+z is turned into a fused-mul-add). #pragma fenv_access(on) // fpu environment sensitivity (needed to be allowed to make FPU mode changes). #else #include #endif #ifndef _M_IA64 #define USE_INTRINSICS #endif #include"hwf.h" // Note: // Which FPU will be used is determined by compiler settings. On x64 it's always SSE2, // on x86 we have to chose SSE2 by enabling /arch:SSE2 (otherwise the x87 FPU will be used). // Christoph has decided that we don't want to use the x87; this makes everything a lot easier. // For SSE2, it is best to use compiler intrinsics because this makes it completely // clear to the compiler what instructions should be used. E.g., for sqrt(), the Windows compiler selects // the x87 FPU, even when /arch:SSE2 is on. // Luckily, these are kind of standardized, at least for Windows/Linux/OSX. #ifdef __clang__ #undef USE_INTRINSICS #else #include #endif hwf_manager::hwf_manager() : m_mpz_manager(m_mpq_manager) { #ifdef _WINDOWS #if defined(_AMD64_) || defined(_M_IA64) // Precision control is not supported on x64. // See: http://msdn.microsoft.com/en-us/library/e9b52ceh(VS.110).aspx // CMW: I think this is okay though, the compiler will chose the right instructions // (the x64/SSE2 FPU has separate instructions for different precisions). #else // Setting the precision should only be required on the x87, but it won't hurt to do it anyways. // _PC_53 means double precision (53 significand bits). For extended precision use _PC_64. #ifndef USE_INTRINSICS __control87_2(_PC_53, _MCW_PC, &x86_state, &sse2_state); #endif #endif #else // OSX/Linux: Nothing. #endif // We only set the precision of the FPU here in the constructor. At the moment, there are no // other parts of the code that could overwrite this, and Windows takes care of context switches. // CMW: I'm not sure what happens on CPUs with hyper-threading (since the FPU is shared). // I have yet to discover whether Linux and OSX save the FPU state when switching context. // As long as we stick to using the SSE2 FPU though, there shouldn't be any problems with respect // to the precision (not sure about the rounding modes though). } hwf_manager::~hwf_manager() { } uint64 RAW(double X) { uint64 tmp; memcpy(&tmp, &(X), sizeof(uint64)); return tmp; } double DBL(uint64 X) { double tmp; memcpy(&tmp, &(X), sizeof(double)); return tmp; } void hwf_manager::set(hwf & o, int value) { o.value = (double) value; } void hwf_manager::set(hwf & o, mpf_rounding_mode rm, int n, int d) { set_rounding_mode(rm); o.value = ((double) n)/((double) d); } void hwf_manager::set(hwf & o, double value) { o.value = value; } void hwf_manager::set(hwf & o, float value) { o.value = (double)value; } void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & value) { set_rounding_mode(rm); o.value = m_mpq_manager.get_double(value); } void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) { // We expect [i].[f]P[e], where P means that the exponent is interpreted as 2^e instead of 10^e. std::string v(value); size_t e_pos = v.find('p'); if (e_pos == std::string::npos) e_pos = v.find('P'); std::string f, e; f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v; e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;); mpq q; m_mpq_manager.set(q, f.c_str()); mpz ex; m_mpz_manager.set(ex, e.c_str()); set(o, rm, q, ex); TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent) { // Assumption: this represents significand * 2^exponent. set_rounding_mode(rm); mpq sig; m_mpq_manager.set(sig, significand); int64 exp = m_mpz_manager.get_int64(exponent); if (m_mpq_manager.is_zero(significand)) o.value = 0.0; else { while (m_mpq_manager.lt(sig, 1)) { m_mpq_manager.mul(sig, 2, sig); exp--; } hwf s; s.value = m_mpq_manager.get_double(sig); uint64 r = (RAW(s.value) & 0x800FFFFFFFFFFFFFull) | ((exp + 1023) << 52); o.value = DBL(r); } } void hwf_manager::set(hwf & o, bool sign, uint64 significand, int exponent) { // Assumption: this represents (sign * -1) * (significand/2^sbits) * 2^exponent. SASSERT(significand <= 0x000FFFFFFFFFFFFFull); SASSERT(-1022 <= exponent && exponent <= 1023); uint64 raw = (sign?0x8000000000000000ull:0); raw |= (((uint64)exponent) + 1023) << 52; raw |= significand; memcpy(&o.value, &raw, sizeof(double)); } void hwf_manager::set(hwf & o, hwf const & x) { o.value = x.value; } void hwf_manager::abs(hwf & o) { o.value = fabs(o.value); } void hwf_manager::abs(hwf const & x, hwf & o) { o.value = fabs(x.value); } void hwf_manager::neg(hwf & o) { o.value = -o.value; } void hwf_manager::neg(hwf const & x, hwf & o) { o.value = -x.value; } bool hwf_manager::eq(hwf const & x, hwf const & y) { return (x.value == y.value); } bool hwf_manager::lt(hwf const & x, hwf const & y) { return (x.value < y.value); } bool hwf_manager::lte(hwf const & x, hwf const & y) { return (x.value <= y.value); } bool hwf_manager::gt(hwf const & x, hwf const & y) { return (x.value > y.value); } bool hwf_manager::gte(hwf const & x, hwf const & y) { return (x.value >= y.value); } void hwf_manager::add(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_add_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else o.value = x.value + y.value; #endif } void hwf_manager::sub(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_sub_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else o.value = x.value - y.value; #endif } #define DBL_SCALE 15360 void hwf_manager::mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_mul_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else o.value = x.value * y.value; #endif #if 0 // On the x86 FPU (x87), we use custom assembly routines because // the code generated for x*y and x/y suffers from the double // rounding on underflow problem. The scaling trick is described // in Roger Golliver: `Efficiently producing default orthogonal IEEE // double results using extended IEEE hardware', see // http://www.open-std.org/JTC1/SC22/JSG/docs/m3/docs/jsgn326.pdf // CMW: Tthis is not really needed if we use only the SSE2 FPU, // it shouldn't hurt the performance too much though. static const int const1 = -DBL_SCALE; static const int const2 = +DBL_SCALE; double xv = x.value; double yv = y.value; double & ov = o.value; __asm { fild const1; fld xv; fscale; fstp st(1); fmul yv; fild const2; fxch st(1); fscale; fstp ov; } #endif } void hwf_manager::div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_div_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else o.value = x.value / y.value; #endif #if 0 // see mul(...) static const int const1 = -DBL_SCALE; static const int const2 = +DBL_SCALE; double xv = x.value; double yv = y.value; double & ov = o.value; __asm { fild const1; fld xv; fscale; fstp st(1); fdiv yv; fild const2; fxch st(1); fscale; fstp ov; } #endif } #ifdef _M_IA64 #pragma fp_contract(on) #endif void hwf_manager::fma(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o) { // CMW: fused_mul_add is not available on most CPUs. As of 2012, only Itanium, // Intel Sandybridge and AMD Bulldozers support that (via AVX). set_rounding_mode(rm); #ifdef _M_IA64 // IA64 (Itanium) will do it, if contractions are on. o.value = x.value * y.value + z.value; #else #if defined(_WINDOWS) #if _MSC_VER >= 1800 o.value = ::fma(x.value, y.value, z.value); #else // Windows, older than VS 2013 #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_fmadd_sd(_mm_set_sd(x.value), _mm_set_sd(y.value), _mm_set_sd(z.value))); #else // If all else fails, we are imprecise. o.value = (x.value * y.value) + z; #endif #endif #else // Linux, OSX o.value = ::fma(x.value, y.value, z.value); #endif #endif } #ifdef _M_IA64 #pragma fp_contract(off) #endif void hwf_manager::sqrt(mpf_rounding_mode rm, hwf const & x, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_sqrt_pd(_mm_set_sd(x.value))); #else o.value = ::sqrt(x.value); #endif } void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o) { set_rounding_mode(rm); // CMW: modf is not the right function here. // modf(x.value, &o.value); // According to the Intel Architecture manual, the x87-instrunction FRNDINT is the // same in 32-bit and 64-bit mode. The _mm_round_* intrinsics are SSE4 extensions. #ifdef _WINDOWS #ifdef USE_INTRINSICS switch (rm) { case 0: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_NEAREST_INT)); break; case 2: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_POS_INF)); break; case 3: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_NEG_INF)); break; case 4: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_ZERO)); break; case 1: UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! break; default: UNREACHABLE(); // Unknown rounding mode. } #else double xv = x.value; double & ov = o.value; __asm { fld xv frndint fstp ov // Store result away. } #endif #else // Linux, OSX. o.value = nearbyint(x.value); #endif } void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { // The built-in fmod() works, except for the special numbers. if (is_inf(x) && is_inf(y)) o.value = x.value/y.value; // NaN else if (is_inf(y)) o.value = x.value; else o.value = fmod(x.value, y.value); // Here is an x87 alternative if the above makes problems; this may also be faster. #if 0 double xv = x.value; double yv = y.value; double & ov = o.value; // This is from: http://webster.cs.ucr.edu/AoA/DOS/ch14/CH14-4.html#HEADING4-173 __asm { fld yv fld xv L: fprem1 fstsw ax // Get condition bits in AX. test ah, 100b // See if C2 is set. jnz L // Repeat if not done yet. fstp ov // Store remainder away. fstp st(0) // Pop old y value. } #endif } void hwf_manager::maximum(hwf const & x, hwf const & y, hwf & o) { #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_max_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else // use __max ? if (is_nan(x)) o.value = y.value; else if (is_nan(y)) o.value = x.value; else if (lt(x, y)) o.value = y.value; else o.value = x.value; #endif } void hwf_manager::minimum(hwf const & x, hwf const & y, hwf & o) { #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_min_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else // use __min ? if (is_nan(x) || is_nan(x)) o.value = y.value; else if (is_nan(y)) o.value = x.value; else if (lt(x, y)) o.value = x.value; else o.value = y.value; #endif } std::string hwf_manager::to_string(hwf const & x) { std::stringstream ss(""); ss << std::scientific << x.value; return ss.str(); } std::string hwf_manager::to_rational_string(hwf const & a) { // temporary hack unsynch_mpq_manager qm; scoped_mpq q(qm); to_rational(a, q); return qm.to_string(q); } void hwf_manager::display_decimal(std::ostream & out, hwf const & a, unsigned k) { // temporary hack unsynch_mpq_manager qm; scoped_mpq q(qm); to_rational(a, q); qm.display_decimal(out, q, k); } void hwf_manager::display_smt2(std::ostream & out, hwf const & a, bool decimal) { // temporary hack unsynch_mpq_manager qm; scoped_mpq q(qm); to_rational(a, q); qm.display_smt2(out, q, decimal); } void hwf_manager::to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o) { SASSERT(is_normal(x) || is_denormal(x) || is_zero(x)); scoped_mpz n(qm), d(qm); if (is_normal(x)) qm.set(n, sig(x) | 0x0010000000000000ull); else qm.set(n, sig(x)); if (sgn(x)) qm.neg(n); qm.set(d, 0x0010000000000000ull); int e = exp(x); if (e >= 0) qm.mul2k(n, (unsigned)e); else qm.mul2k(d, (unsigned)-e); qm.set(o, n, d); } bool hwf_manager::is_zero(hwf const & x) { uint64 t = RAW(x.value) & 0x7FFFFFFFFFFFFFFFull; return (t == 0x0ull); // CMW: I tried, and these are slower: // return (t != 0x0ull) ? false : true; // return (x.value == 0.0 || x.value == -0.0); // [uses SSE2]. } bool hwf_manager::is_neg(hwf const & x) { // [Leo]: I added !is_nan(x) return sgn(x) && !is_nan(x); } bool hwf_manager::is_pos(hwf const & x) { return !sgn(x) && !is_nan(x); } bool hwf_manager::is_nzero(hwf const & x) { return RAW(x.value) == 0x8000000000000000ull; } bool hwf_manager::is_pzero(hwf const & x) { return RAW(x.value) == 0x0000000000000000ull; } bool hwf_manager::is_one(hwf const & x) { return RAW(x.value) == 0x3FF0000000000000ull; } bool hwf_manager::is_nan(hwf const & x) { bool r = ((RAW(x.value) & 0x7FF0000000000000ull) == 0x7FF0000000000000ull) && ((RAW(x.value) & 0x000FFFFFFFFFFFFFull) != 0x0); #ifdef _WINDOWS SASSERT( !r || (_fpclass(x.value) == _FPCLASS_SNAN || _fpclass(x.value) == _FPCLASS_QNAN)); #endif return r; } bool hwf_manager::is_inf(hwf const & x) { bool r = ((RAW(x.value) & 0x7FF0000000000000ull) == 0x7FF0000000000000ull) && ((RAW(x.value) & 0x000FFFFFFFFFFFFFull) == 0x0); #ifdef _WINDOWS SASSERT( !r || (_fpclass(x.value) == _FPCLASS_NINF || _fpclass(x.value) == _FPCLASS_PINF)); #endif return r; } bool hwf_manager::is_pinf(hwf const & x) { return !sgn(x) && is_inf(x); } bool hwf_manager::is_ninf(hwf const & x) { return sgn(x) && is_inf(x); } bool hwf_manager::is_normal(hwf const & x) { uint64 t = RAW(x.value) & 0x7FF0000000000000ull; return (t != 0x0ull && t != 0x7FF0000000000000ull); } bool hwf_manager::is_denormal(hwf const & x) { uint64 t = RAW(x.value); return ((t & 0x7FF0000000000000ull) == 0x0 && (t & 0x000FFFFFFFFFFFFFull) != 0x0); } bool hwf_manager::is_regular(hwf const & x) { // Everything that doesn't have the top-exponent is considered regular. // Note that +-0.0 and denormal numbers have exponent==0; these are regular. // All normal numbers are also regular. What remains is +-Inf and NaN, they are // not regular and they are the only numbers that have exponent 7FF. uint64 e = RAW(x.value) & 0x7FF0000000000000ull; // the exponent return (e != 0x7FF0000000000000ull); } bool hwf_manager::is_int(hwf const & x) { if (!is_normal(x)) return false; const int e = exp(x); if (e >= 52) return true; else if (e < 0) return false; else { uint64 t = sig(x); unsigned shift = 52 - ((unsigned)e); uint64 mask = (0x1ull << shift) - 1; return (t & mask) == 0; } } void hwf_manager::mk_nzero(hwf & o) { uint64 raw = 0x8000000000000000ull; o.value = DBL(raw); } void hwf_manager::mk_pzero(hwf & o) { o.value = 0; } void hwf_manager::mk_zero(bool sign, hwf & o) { if (sign) mk_nzero(o); else mk_pzero(o); } void hwf_manager::mk_nan(hwf & o) { uint64 raw = 0x7FF0000000000001ull; o.value = DBL(raw); } void hwf_manager::mk_inf(bool sign, hwf & o) { uint64 raw = (sign) ? 0xFFF0000000000000ull : 0x7FF0000000000000ull; o.value = DBL(raw); } void hwf_manager::mk_pinf(hwf & o) { uint64 raw = 0x7FF0000000000000ull; o.value = DBL(raw); } void hwf_manager::mk_ninf(hwf & o) { uint64 raw = 0xFFF0000000000000ull; o.value = DBL(raw); } #ifdef _WINDOWS #if defined(_AMD64_) || defined(_M_IA64) #ifdef USE_INTRINSICS #define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) #else #define SETRM(RM) _controlfp_s(&sse2_state, RM, _MCW_RC); #endif #else #ifdef USE_INTRINSICS #define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) #else #define SETRM(RM) __control87_2(RM, _MCW_RC, &x86_state, &sse2_state) #endif #endif #else #define SETRM(RM) fesetround(RM) #endif unsigned hwf_manager::prev_power_of_two(hwf const & a) { SASSERT(!is_nan(a) && !is_pinf(a) && !is_ninf(a)); if (!is_pos(a)) return 0; if (exp(a) <= -52) return 0; return 51 + exp(a); } void hwf_manager::set_rounding_mode(mpf_rounding_mode rm) { #ifdef _WINDOWS #ifdef USE_INTRINSICS switch (rm) { case MPF_ROUND_NEAREST_TEVEN: SETRM(_MM_ROUND_NEAREST); break; case MPF_ROUND_TOWARD_POSITIVE: SETRM(_MM_ROUND_UP); break; case MPF_ROUND_TOWARD_NEGATIVE: SETRM(_MM_ROUND_DOWN); break; case MPF_ROUND_TOWARD_ZERO: SETRM(_MM_ROUND_TOWARD_ZERO); break; case MPF_ROUND_NEAREST_TAWAY: default: UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! } #else switch (rm) { case MPF_ROUND_NEAREST_TEVEN: SETRM(_RC_NEAR); break; case MPF_ROUND_TOWARD_POSITIVE: SETRM(_RC_UP); break; case MPF_ROUND_TOWARD_NEGATIVE: SETRM(_RC_DOWN); break; case MPF_ROUND_TOWARD_ZERO: SETRM(_RC_CHOP); break; case MPF_ROUND_NEAREST_TAWAY: default: UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! } #endif #else // OSX/Linux switch (rm) { case MPF_ROUND_NEAREST_TEVEN: SETRM(FE_TONEAREST); break; case MPF_ROUND_TOWARD_POSITIVE: SETRM(FE_UPWARD); break; case MPF_ROUND_TOWARD_NEGATIVE: SETRM(FE_DOWNWARD); break; case MPF_ROUND_TOWARD_ZERO: SETRM(FE_TOWARDZERO); break; case MPF_ROUND_NEAREST_TAWAY: default: UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! } #endif } z3-z3-4.4.1/src/util/hwf.h000066400000000000000000000125751260446376700151400ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: hwf.h Abstract: Hardware Floating Point Numbers Author: Christoph Wintersteiger (cwinter) 2012-07-30. Revision History: --*/ #ifndef HWF_H_ #define HWF_H_ #include #include"mpz.h" #include"mpq.h" #include"mpf.h" // we use the same rounding modes as mpf's class hwf { friend class hwf_manager; double value; hwf & operator=(hwf const & other) { UNREACHABLE(); return *this; } uint64 get_raw() const { uint64 n; SASSERT(sizeof(n) == sizeof(value)); memcpy(&n, &value, sizeof(value)); return n; } public: hwf() {} hwf(hwf const & other) { this->value = other.value; } ~hwf() {} void swap(hwf & other) { double t = value; value = other.value; other.value = t; } }; class hwf_manager { unsynch_mpq_manager m_mpq_manager; unsynch_mpz_manager & m_mpz_manager; // A mpq_manager is a mpz_manager, reusing it. public: typedef hwf numeral; hwf_manager(); ~hwf_manager(); void reset(hwf & o) { set(o, 0); } void set(hwf & o, int value); void set(hwf & o, mpf_rounding_mode rm, int n, int d); void set(hwf & o, float value); void set(hwf & o, double value); void set(hwf & o, mpf_rounding_mode rm, mpq const & value); void set(hwf & o, mpf_rounding_mode rm, char const * value); void set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent); void set(hwf & o, bool sign, uint64 significand, int exponent); void set(hwf & o, hwf const & x); // auxiliary methods to make the interface compatible with mpf void reset(hwf & o, unsigned ebits, unsigned sbits) { set(o, 0); } void set(hwf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & value) { set(o, rm, value); } void set(hwf & o, unsigned ebits, unsigned sbits, int value) { set(o, value); } void set(hwf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, int n, int d) { set(o, rm, n, d); } void set(hwf & o, unsigned ebits, unsigned sbits, float value) { set(o, value); } void set(hwf & o, unsigned ebits, unsigned sbits, double value) { set(o, value); } void del(hwf & x) {} void abs(hwf & o); void abs(hwf const & x, hwf & o); void neg(hwf & o); void neg(hwf const & x, hwf & o); bool is_zero(hwf const & x); bool is_neg(hwf const & x); bool is_pos(hwf const & x); bool is_nzero(hwf const & x); bool is_pzero(hwf const & x); bool is_one(hwf const & x); // structural eq bool eq_core(hwf const & x, hwf const & y); bool eq(hwf const & x, hwf const & y); bool lt(hwf const & x, hwf const & y); bool lte(hwf const & x, hwf const & y); bool le(hwf const & x, hwf const & y) { return lte(x, y); } bool gt(hwf const & x, hwf const & y); bool gte(hwf const & x, hwf const & y); bool ge(hwf const & x, hwf const & y) { return gte(x, y); } void add(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o); void sub(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o); void mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o); void div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o); void fma(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o); void sqrt(mpf_rounding_mode rm, hwf const & x, hwf & o); void round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o); void rem(hwf const & x, hwf const & y, hwf & o); void maximum(hwf const & x, hwf const & y, hwf & o); void minimum(hwf const & x, hwf const & y, hwf & o); std::string to_string(hwf const & a); std::string to_rational_string(hwf const & a); void display_decimal(std::ostream & out, hwf const & a, unsigned k); void display_smt2(std::ostream & out, hwf const & a, bool decimal); double to_double(hwf const & x) { return x.value; } float to_float(hwf const & x) { return (float) x.value; } void to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o); void to_rational(hwf const & x, scoped_mpq & o) { to_rational(x, o.m(), o); } bool sgn(hwf const & x) const { return (x.get_raw() & 0x8000000000000000ull) != 0; } uint64 sig(hwf const & x) const { return x.get_raw() & 0x000FFFFFFFFFFFFFull; } int exp(hwf const & x) const { return ((x.get_raw() & 0x7FF0000000000000ull) >> 52) - 1023; } bool is_nan(hwf const & x); bool is_inf(hwf const & x); bool is_pinf(hwf const & x); bool is_ninf(hwf const & x); bool is_normal(hwf const & x); bool is_denormal(hwf const & x); bool is_regular(hwf const & x); bool is_int(hwf const & x); void mk_zero(bool sign, hwf & o); void mk_nzero(hwf & o); void mk_pzero(hwf & o); void mk_nan(hwf & o); void mk_inf(bool sign, hwf & o); void mk_pinf(hwf & o); void mk_ninf(hwf & o); unsigned hash(hwf const & a) { return hash_ull(a.get_raw()); } inline void set_rounding_mode(mpf_rounding_mode rm); /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(hwf const & a); protected: #ifdef _WINDOWS unsigned x86_state, sse2_state; #endif }; typedef _scoped_numeral scoped_hwf; typedef _scoped_numeral_vector scoped_hwf_vector; #endif z3-z3-4.4.1/src/util/id_gen.h000066400000000000000000000036251260446376700155750ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: id_gen.h Abstract: Basic support for generating & recycling ids. Author: Leonardo de Moura (leonardo) 2008-02-02. Revision History: --*/ #ifndef ID_GEN_H_ #define ID_GEN_H_ #include"vector.h" #include"util.h" class id_gen { unsigned m_next_id; unsigned_vector m_free_ids; public: id_gen(unsigned start = 0):m_next_id(start) {} unsigned mk() { unsigned r; if (m_free_ids.empty()) { r = m_next_id; m_next_id++; } else { r = m_free_ids.back(); m_free_ids.pop_back(); } return r; } void recycle(unsigned id) { if (memory::is_out_of_memory()) return; m_free_ids.push_back(id); } void reset(unsigned start = 0) { m_next_id = start; m_free_ids.reset(); } void cleanup(unsigned start = 0) { m_next_id = start; m_free_ids.finalize(); } unsigned show_hash(){ unsigned h = string_hash((char *)&m_free_ids[0],m_free_ids.size()*sizeof(unsigned),17); return hash_u_u(h,m_next_id); } /** \brief Return N if the range of ids generated by this module is in the set [0..N) */ unsigned get_id_range() const { return m_next_id; } /** \brief Debugging support method: set m_next_id to the least value id' s.t. id' >= id and id' is not in m_free_ids. This method is only used to create small repros that exposes bugs in Z3. */ unsigned set_next_id(unsigned id) { m_next_id = id; while (std::find(m_free_ids.begin(), m_free_ids.end(), m_next_id) != m_free_ids.end()) m_next_id++; return m_next_id; } void display_free_ids(std::ostream & out) { ::display(out, m_free_ids.begin(), m_free_ids.end()); } }; #endif /* ID_GEN_H_ */ z3-z3-4.4.1/src/util/inf_eps_rational.h000066400000000000000000000245371260446376700176710ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: inf_eps_rational.h Abstract: Rational numbers with infinity and epsilon. Author: Nikolaj Bjorner (nbjorner) 2013-4-23. Revision History: --*/ #ifndef INF_EPS_RATIONAL_H_ #define INF_EPS_RATIONAL_H_ #include #include #include"debug.h" #include"vector.h" #include"rational.h" template class inf_eps_rational { rational m_infty; Numeral m_r; public: unsigned hash() const { return m_infty.hash() ^ m_r.hash(); } struct hash_proc { unsigned operator()(inf_eps_rational const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(inf_eps_rational const& r1, inf_eps_rational const& r2) const { return r1 == r2; } }; void swap(inf_eps_rational & n) { m_infty.swap(n.m_infty); m_r.swap(n.m_r); } std::string to_string() const { if (m_infty.is_zero()) { return m_r.to_string(); } std::string si; if (m_infty.is_one()) { si = "oo"; } else if (m_infty.is_minus_one()) { si = "-oo"; } else { si = m_infty.to_string() += "*oo"; } if (m_r.is_zero()) { return si; } std::string s = "("; s += si; s += " + "; s += m_r.to_string(); s += ")"; return s; } inf_eps_rational(): m_infty(), m_r() {} inf_eps_rational(const inf_eps_rational & r): m_infty(r.m_infty), m_r(r.m_r) {} explicit inf_eps_rational(int n): m_infty(), m_r(n) {} explicit inf_eps_rational(Numeral const& r): m_infty(), m_r(r) {} explicit inf_eps_rational(rational const& i, Numeral const& r): m_infty(i), m_r(r) { } ~inf_eps_rational() {} /** \brief Set inf_eps_rational to 0. */ void reset() { m_infty.reset(); m_r.reset(); } bool is_int() const { return m_infty.is_zero() && m_r.is_int(); } bool is_int64() const { return m_infty.is_zero() && m_r.is_int64(); } bool is_uint64() const { return m_infty.is_zero() && m_r.is_uint64(); } bool is_rational() const { return m_infty.is_zero() && m_r.is_rational(); } int64 get_int64() const { SASSERT(is_int64()); return m_r.get_int64(); } uint64 get_uint64() const { SASSERT(is_uint64()); return m_r.get_uint64(); } Numeral const& get_numeral() const { return m_r; } rational const& get_rational() const { return m_r.get_rational(); } rational const& get_infinitesimal() const { return m_r.get_infinitesimal(); } rational const& get_infinity() const { return m_infty; } bool is_finite() const { return m_infty.is_zero(); } static inf_eps_rational zero() { return inf_eps_rational(Numeral::zero()); } static inf_eps_rational one() { return inf_eps_rational(Numeral::one()); } static inf_eps_rational minus_one() { return inf_eps_rational(Numeral::minus_one()); } static inf_eps_rational infinity() { return inf_eps_rational(rational::one(), Numeral::zero()); } inf_eps_rational & operator=(const inf_eps_rational & r) { m_infty = r.m_infty; m_r = r.m_r; return *this; } inf_eps_rational & operator=(const Numeral & r) { m_infty.reset(); m_r = r; return *this; } inf_eps_rational & operator+=(const inf_eps_rational & r) { m_infty += r.m_infty; m_r += r.m_r; return *this; } inf_eps_rational & operator-=(const inf_eps_rational & r) { m_infty -= r.m_infty; m_r -= r.m_r; return *this; } inf_eps_rational & operator-=(const inf_rational & r) { m_r -= r; return *this; } inf_eps_rational & operator+=(const inf_rational & r) { m_r += r; return *this; } inf_eps_rational & operator+=(const rational & r) { m_r += r; return *this; } inf_eps_rational & operator-=(const rational & r) { m_r -= r; return *this; } inf_eps_rational & operator*=(const rational & r1) { m_infty *= r1; m_r *= r1; return *this; } inf_eps_rational & operator/=(const rational & r) { m_infty /= r; m_r /= r; return *this; } inf_eps_rational & operator++() { ++m_r; return *this; } const inf_eps_rational operator++(int) { inf_eps_rational tmp(*this); ++(*this); return tmp; } inf_eps_rational & operator--() { --m_r; return *this; } const inf_eps_rational operator--(int) { inf_eps_rational tmp(*this); --(*this); return tmp; } friend inline bool operator==(const inf_eps_rational & r1, const inf_eps_rational & r2) { return r1.m_infty == r2.m_infty && r1.m_r == r2.m_r; } friend inline bool operator==(const rational & r1, const inf_eps_rational & r2) { return r1 == r2.m_infty && r2.m_r.is_zero(); } friend inline bool operator==(const inf_eps_rational & r1, const rational & r2) { return r1.m_infty == r2 && r1.m_r.is_zero(); } friend inline bool operator<(const inf_eps_rational & r1, const inf_eps_rational & r2) { return (r1.m_infty < r2.m_infty) || (r1.m_infty == r2.m_infty && r1.m_r < r2.m_r); } friend inline bool operator<(const rational & r1, const inf_eps_rational & r2) { return r2.m_infty.is_pos() || (r2.m_infty.is_zero() && r1 < r2.m_r); } friend inline bool operator<(const inf_eps_rational & r1, const rational & r2) { return r1.m_infty.is_neg() || (r1.m_infty.is_zero() && r1.m_r < r2); } void neg() { m_infty.neg(); m_r.neg(); } bool is_zero() const { return m_infty.is_zero() && m_r.is_zero(); } bool is_one() const { return m_infty.is_zero() && m_r.is_one(); } bool is_minus_one() const { return m_infty.is_zero() && m_r.is_minus_one(); } bool is_neg() const { return m_infty.is_neg() || (m_infty.is_zero() && m_r.is_neg()); } bool is_pos() const { return m_infty.is_pos() || (m_infty.is_zero() && m_r.is_pos()); } bool is_nonneg() const { return m_infty.is_pos() || (m_infty.is_zero() && m_r.is_nonneg()); } bool is_nonpos() const { return m_infty.is_neg() || (m_infty.is_zero() && m_r.is_nonpos()); } friend inline rational floor(const inf_eps_rational & r) { // SASSERT(r.m_infty.is_zero()); return floor(r.m_r); } friend inline rational ceil(const inf_eps_rational & r) { // SASSERT(r.m_infty.is_zero()); return ceil(r.m_r); } // Perform: this += c * k void addmul(const rational & c, const inf_eps_rational & k) { m_infty.addmul(c, k.m_infty); m_r.addmul(c, k.m_r); } // Perform: this += c * k void submul(const rational & c, const inf_eps_rational & k) { m_infty.submul(c, k.m_infty); m_r.submul(c, k.m_r); } }; template inline bool operator!=(const inf_eps_rational & r1, const inf_eps_rational & r2) { return !operator==(r1, r2); } template inline bool operator!=(const rational & r1, const inf_eps_rational & r2) { return !operator==(r1, r2); } template inline bool operator!=(const inf_eps_rational & r1, const rational & r2) { return !operator==(r1, r2); } template inline bool operator>(const inf_eps_rational & r1, const inf_eps_rational & r2) { return operator<(r2, r1); } template inline bool operator>(const inf_eps_rational & r1, const rational & r2) { return operator<(r2, r1); } template inline bool operator>(const rational & r1, const inf_eps_rational & r2) { return operator<(r2, r1); } template inline bool operator<=(const inf_eps_rational & r1, const inf_eps_rational & r2) { return !operator>(r1, r2); } template inline bool operator<=(const rational & r1, const inf_eps_rational & r2) { return !operator>(r1, r2); } template inline bool operator<=(const inf_eps_rational & r1, const rational & r2) { return !operator>(r1, r2); } template inline bool operator>=(const inf_eps_rational & r1, const inf_eps_rational & r2) { return !operator<(r1, r2); } template inline bool operator>=(const rational & r1, const inf_eps_rational & r2) { return !operator<(r1, r2); } template inline bool operator>=(const inf_eps_rational & r1, const rational & r2) { return !operator<(r1, r2); } template inline inf_eps_rational operator+(const inf_eps_rational & r1, const inf_eps_rational & r2) { return inf_eps_rational(r1) += r2; } template inline inf_eps_rational operator-(const inf_eps_rational & r1, const inf_eps_rational & r2) { return inf_eps_rational(r1) -= r2; } template inline inf_eps_rational operator-(const inf_eps_rational & r) { inf_eps_rational result(r); result.neg(); return result; } template inline inf_eps_rational operator*(const rational & r1, const inf_eps_rational & r2) { inf_eps_rational result(r2); result *= r1; return result; } template inline inf_eps_rational operator*(const inf_eps_rational & r1, const rational & r2) { return r2 * r1; } template inline inf_eps_rational operator/(const inf_eps_rational & r1, const rational & r2) { inf_eps_rational result(r1); result /= r2; return result; } template inline std::ostream & operator<<(std::ostream & target, const inf_eps_rational & r) { target << r.to_string(); return target; } template inline inf_eps_rational abs(const inf_eps_rational & r) { inf_eps_rational result(r); if (result.is_neg()) { result.neg(); } return result; } #endif /* INF_EPS_RATIONAL_H_ */ z3-z3-4.4.1/src/util/inf_int_rational.cpp000066400000000000000000000022111260446376700202100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_int_rational.cpp Abstract: Rational numbers with infenitesimals Author: Nikolaj Bjorner (nbjorner) 2006-12-05. Revision History: --*/ #include #include"inf_int_rational.h" inf_int_rational inf_int_rational::m_zero; inf_int_rational inf_int_rational::m_one; inf_int_rational inf_int_rational::m_minus_one; std::string inf_int_rational::to_string() const { if (m_second == 0) { return m_first.to_string(); } std::ostringstream s; s << "(" << m_first.to_string(); if (m_second < 0) { s << " -e*" << (-m_second) << ")"; } else { s << " +e*" << m_second << ")"; } return s.str(); } void initialize_inf_int_rational() { inf_int_rational::init(); } void inf_int_rational::init() { m_zero.m_first = rational::zero(); m_one.m_first = rational::one(); m_minus_one.m_first = rational::minus_one(); } void finalize_inf_int_rational() { inf_int_rational::finalize(); } void inf_int_rational::finalize() { m_zero.~inf_int_rational(); m_one.~inf_int_rational(); m_minus_one.~inf_int_rational(); } z3-z3-4.4.1/src/util/inf_int_rational.h000066400000000000000000000226051260446376700176660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_int_rational.h Abstract: Rational numbers with infenitesimals Author: Leonardo de Moura (leonardo) 2006-09-18. Nikolaj Bjorner (nbjorner) 2006-10-24. Revision History: --*/ #ifndef INF_INT_RATIONAL_H_ #define INF_INT_RATIONAL_H_ #include #include #include"debug.h" #include"vector.h" #include"rational.h" class inf_int_rational { static inf_int_rational m_zero; static inf_int_rational m_one; static inf_int_rational m_minus_one; rational m_first; int m_second; public: static void init(); // called from rational::initialize() only static void finalize(); // called from rational::finalize() only unsigned hash() const { return m_first.hash() ^ (static_cast(m_second) + 1); } struct hash_proc { unsigned operator()(inf_int_rational const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(inf_int_rational const& r1, inf_int_rational const& r2) const { return r1 == r2; } }; void swap(inf_int_rational & n) { m_first.swap(n.m_first); std::swap(m_second, n.m_second); } std::string to_string() const; inf_int_rational(): m_first(rational()), m_second(0) {} inf_int_rational(const inf_int_rational & r): m_first(r.m_first), m_second(r.m_second) {} explicit inf_int_rational(int n): m_first(rational(n)), m_second(0) {} explicit inf_int_rational(int n, int d): m_first(rational(n,d)), m_second(0) {} explicit inf_int_rational(rational const& r, bool pos_inf): m_first(r), m_second(pos_inf?1:-1) {} explicit inf_int_rational(rational const& r): m_first(r), m_second(0) {} inf_int_rational(rational const& r, int i): m_first(r), m_second(i) { } ~inf_int_rational() {} /** \brief Set inf_int_rational to 0. */ void reset() { m_first.reset(); m_second = 0; } bool is_int() const { return m_first.is_int() && m_second == 0; } bool is_int64() const { return m_first.is_int64() && m_second == 0; } bool is_uint64() const { return m_first.is_uint64() && m_second == 0; } bool is_rational() const { return m_second == 0; } int64 get_int64() const { SASSERT(is_int64()); return m_first.get_int64(); } uint64 get_uint64() const { SASSERT(is_uint64()); return m_first.get_uint64(); } rational const& get_rational() const { return m_first; } rational get_infinitesimal() const { return rational(m_second); } rational const & get_first() const { return m_first; } inf_int_rational & operator=(const inf_int_rational & r) { m_first = r.m_first; m_second = r.m_second; return *this; } inf_int_rational & operator=(const rational & r) { m_first = r; m_second = 0; return *this; } friend inline inf_int_rational numerator(const inf_int_rational & r) { SASSERT(r.m_second == 0); return inf_int_rational(numerator(r.m_first)); } friend inline inf_int_rational denominator(const inf_int_rational & r) { SASSERT(r.m_second == 0); return inf_int_rational(denominator(r.m_first)); } inf_int_rational & operator+=(const inf_int_rational & r) { m_first += r.m_first; m_second += r.m_second; return *this; } inf_int_rational & operator*=(const rational & r) { if (!r.is_int32()) { throw default_exception("multiplication with large rational is not possible"); } m_first *= r; m_second *= r.get_int32(); return *this; } inf_int_rational & operator-=(const inf_int_rational & r) { m_first -= r.m_first; m_second -= r.m_second; return *this; } inf_int_rational & operator+=(const rational & r) { m_first += r; return *this; } inf_int_rational & operator-=(const rational & r) { m_first -= r; return *this; } inf_int_rational & operator++() { ++m_first; return *this; } const inf_int_rational operator++(int) { inf_int_rational tmp(*this); ++(*this); return tmp; } inf_int_rational & operator--() { --m_first; return *this; } const inf_int_rational operator--(int) { inf_int_rational tmp(*this); --(*this); return tmp; } friend inline bool operator==(const inf_int_rational & r1, const inf_int_rational & r2) { return r1.m_first == r2.m_first && r1.m_second == r2.m_second; } friend inline bool operator==(const rational & r1, const inf_int_rational & r2) { return r1 == r2.m_first && r2.m_second == 0; } friend inline bool operator==(const inf_int_rational & r1, const rational & r2) { return r1.m_first == r2 && r1.m_second == 0; } friend inline bool operator<(const inf_int_rational & r1, const inf_int_rational & r2) { return (r1.m_first < r2.m_first) || (r1.m_first == r2.m_first && r1.m_second < r2.m_second); } friend inline bool operator<(const rational & r1, const inf_int_rational & r2) { return (r1 < r2.m_first) || (r1 == r2.m_first && r2.m_second > 0); } friend inline bool operator<(const inf_int_rational & r1, const rational & r2) { return (r1.m_first < r2) || (r1.m_first == r2 && r1.m_second < 0); } void neg() { m_first.neg(); m_second = -m_second; } bool is_zero() const { return m_first.is_zero() && m_second == 0; } bool is_one() const { return m_first.is_one() && m_second == 0; } bool is_minus_one() const { return m_first.is_minus_one() && m_second == 0; } bool is_neg() const { return m_first.is_neg() || (m_first.is_zero() && m_second < 0); } bool is_pos() const { return m_first.is_pos() || (m_first.is_zero() && m_second > 0); } bool is_nonneg() const { return m_first.is_pos() || (m_first.is_zero() && m_second >= 0); } bool is_nonpos() const { return m_first.is_neg() || (m_first.is_zero() && m_second <= 0); } friend inline rational floor(const inf_int_rational & r) { if (r.m_first.is_int()) { if (r.m_second >= 0) { return r.m_first; } return r.m_first - rational::one(); } return floor(r.m_first); } friend inline rational ceil(const inf_int_rational & r) { if (r.m_first.is_int()) { if (r.m_second <= 0) { return r.m_first; } return r.m_first + rational::one(); } return ceil(r.m_first); } static const inf_int_rational & zero() { return m_zero; } static const inf_int_rational & one() { return m_one; } static const inf_int_rational & minus_one() { return m_minus_one; } }; inline bool operator!=(const inf_int_rational & r1, const inf_int_rational & r2) { return !operator==(r1, r2); } inline bool operator!=(const rational & r1, const inf_int_rational & r2) { return !operator==(r1, r2); } inline bool operator!=(const inf_int_rational & r1, const rational & r2) { return !operator==(r1, r2); } inline bool operator>(const inf_int_rational & r1, const inf_int_rational & r2) { return operator<(r2, r1); } inline bool operator>(const inf_int_rational & r1, const rational & r2) { return operator<(r2, r1); } inline bool operator>(const rational & r1, const inf_int_rational & r2) { return operator<(r2, r1); } inline bool operator<=(const inf_int_rational & r1, const inf_int_rational & r2) { return !operator>(r1, r2); } inline bool operator<=(const rational & r1, const inf_int_rational & r2) { return !operator>(r1, r2); } inline bool operator<=(const inf_int_rational & r1, const rational & r2) { return !operator>(r1, r2); } inline bool operator>=(const inf_int_rational & r1, const inf_int_rational & r2) { return !operator<(r1, r2); } inline bool operator>=(const rational & r1, const inf_int_rational & r2) { return !operator<(r1, r2); } inline bool operator>=(const inf_int_rational & r1, const rational & r2) { return !operator<(r1, r2); } inline inf_int_rational operator+(const inf_int_rational & r1, const inf_int_rational & r2) { return inf_int_rational(r1) += r2; } inline inf_int_rational operator*(const rational & r1, const inf_int_rational & r2) { return inf_int_rational(r2) *= r1; } inline inf_int_rational operator-(const inf_int_rational & r1, const inf_int_rational & r2) { return inf_int_rational(r1) -= r2; } inline inf_int_rational operator-(const inf_int_rational & r) { inf_int_rational result(r); result.neg(); return result; } inline std::ostream & operator<<(std::ostream & target, const inf_int_rational & r) { target << r.to_string(); return target; } inline inf_int_rational abs(const inf_int_rational & r) { inf_int_rational result(r); if (result.is_neg()) { result.neg(); } return result; } #endif /* INF_INT_RATIONAL_H_ */ z3-z3-4.4.1/src/util/inf_rational.cpp000066400000000000000000000115231260446376700173440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_rational.cpp Abstract: Rational numbers with infenitesimals Author: Nikolaj Bjorner (nbjorner) 2006-12-05. Revision History: --*/ #include"inf_rational.h" inf_rational inf_rational::m_zero; inf_rational inf_rational::m_one; inf_rational inf_rational::m_minus_one; inf_rational inf_mult(inf_rational const& r1, inf_rational const& r2) { inf_rational result; result.m_first = r1.m_first * r2.m_first; result.m_second = (r1.m_first * r2.m_second) + (r1.m_second * r2.m_first); if (r1.m_second.is_pos() && r2.m_second.is_neg()) { --result.m_second; } else if (r1.m_second.is_neg() && r2.m_second.is_pos()) { --result.m_second; } return result; } inf_rational sup_mult(inf_rational const& r1, inf_rational const& r2) { inf_rational result; result.m_first = r1.m_first * r2.m_first; result.m_second = (r1.m_first * r2.m_second) + (r1.m_second * r2.m_first); if (r1.m_second.is_pos() && r2.m_second.is_pos()) { ++result.m_second; } else if (r1.m_second.is_neg() && r2.m_second.is_neg()) { ++result.m_second; } return result; } // // Find rationals c, x, such that c + epsilon*x <= r1/r2 // // let r1 = a + d_1 // let r2 = b + d_2 // // suppose b != 0: // // r1/b <= r1/r2 // <=> { if b > 0, then r2 > 0, and cross multiplication does not change the sign } // { if b < 0, then r2 < 0, and cross multiplication changes sign twice } // r1 * r2 <= b * r1 // <=> // r1 * (b + d_2) <= r1 * b // <=> // r1 * d_2 <= 0 // // if r1 * d_2 > 0, then r1/(b + sign_of(r1)*1/2*|b|) <= r1/r2 // // Not handled here: // if b = 0, then d_2 != 0 // if r1 * d_2 = 0 then it's 0. // if r1 * d_2 > 0, then result is +oo // if r1 * d_2 < 0, then result is -oo // inf_rational inf_div(inf_rational const& r1, inf_rational const& r2) { SASSERT(!r2.m_first.is_zero()); inf_rational result; if (r2.m_second.is_neg() && r1.is_neg()) { result = r1 / (r2.m_first - (abs(r2.m_first)/rational(2))); } else if (r2.m_second.is_pos() && r1.is_pos()) { result = r1 / (r2.m_first + (abs(r2.m_first)/rational(2))); } else { result = r1 / r2.m_first; } return result; } inf_rational sup_div(inf_rational const& r1, inf_rational const& r2) { SASSERT(!r2.m_first.is_zero()); inf_rational result; if (r2.m_second.is_pos() && r1.is_neg()) { result = r1 / (r2.m_first + (abs(r2.m_first)/rational(2))); } else if (r2.m_second.is_neg() && r1.is_pos()) { result = r1 / (r2.m_first - (abs(r2.m_first)/rational(2))); } else { result = r1 / r2.m_first; } return result; } inf_rational inf_power(inf_rational const& r, unsigned n) { bool is_even = (0 == (n & 0x1)); inf_rational result; if (n == 1) { result = r; } else if ((r.m_second.is_zero()) || (r.m_first.is_pos() && r.m_second.is_pos()) || (r.m_first.is_neg() && r.m_second.is_neg() && is_even)) { result.m_first = r.m_first.expt(n); } else if (is_even) { // 0 will work. } else if (r.m_first.is_zero()) { result.m_first = rational::minus_one(); } else if (r.m_first.is_pos()) { result.m_first = rational(r.m_first - r.m_first/rational(2)).expt(n); } else { result.m_first = rational(r.m_first + r.m_first/rational(2)).expt(n); } return result; } inf_rational sup_power(inf_rational const& r, unsigned n) { bool is_even = (0 == (n & 0x1)); inf_rational result; if (n == 1) { result = r; } else if (r.m_second.is_zero() || (r.m_first.is_pos() && r.m_second.is_neg()) || (r.m_first.is_neg() && r.m_second.is_pos() && is_even)) { result.m_first = r.m_first.expt(n); } else if (r.m_first.is_zero() || (n == 0)) { result.m_first = rational::one(); } else if (r.m_first.is_pos() || is_even) { result.m_first = rational(r.m_first + r.m_first/rational(2)).expt(n); } else { // r (r.m_first) is negative, n is odd. result.m_first = rational(r.m_first - r.m_first/rational(2)).expt(n); } return result; } inf_rational inf_root(inf_rational const& r, unsigned n) { SASSERT(!r.is_neg()); // use 0 return inf_rational(); } inf_rational sup_root(inf_rational const& r, unsigned n) { SASSERT(!r.is_neg()); // use r. return r; } void initialize_inf_rational() { inf_rational::init(); } void inf_rational::init() { m_zero.m_first = rational::zero(); m_one.m_first = rational::one(); m_minus_one.m_first = rational::minus_one(); } void finalize_inf_rational() { inf_rational::finalize(); } void inf_rational::finalize() { m_zero.~inf_rational(); m_one.~inf_rational(); m_minus_one.~inf_rational(); } z3-z3-4.4.1/src/util/inf_rational.h000066400000000000000000000305351260446376700170150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_rational.h Abstract: Rational numbers with infenitesimals Author: Leonardo de Moura (leonardo) 2006-09-18. Nikolaj Bjorner (nbjorner) 2006-10-24. Revision History: --*/ #ifndef INF_RATIONAL_H_ #define INF_RATIONAL_H_ #include #include #include"debug.h" #include"vector.h" #include"rational.h" class inf_rational { static inf_rational m_zero; static inf_rational m_one; static inf_rational m_minus_one; rational m_first; rational m_second; public: static void init(); // called from rational::initialize() only static void finalize(); // called from rational::finalize() only unsigned hash() const { return m_first.hash() ^ (m_second.hash()+1); } struct hash_proc { unsigned operator()(inf_rational const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(inf_rational const& r1, inf_rational const& r2) const { return r1 == r2; } }; void swap(inf_rational & n) { m_first.swap(n.m_first); m_second.swap(n.m_second); } std::string to_string() const { if (m_second.is_zero()) { return m_first.to_string(); } std::string s = "("; s += m_first.to_string(); if (m_second.is_neg()) { s += " -e*"; } else { s += " +e*"; } s += abs(m_second).to_string(); s += ")"; return s; } inf_rational() {} inf_rational(const inf_rational & r): m_first(r.m_first), m_second(r.m_second) {} explicit inf_rational(int n): m_first(rational(n)), m_second(rational()) {} explicit inf_rational(int n, int d): m_first(rational(n,d)), m_second(rational()) {} explicit inf_rational(rational const& r, bool pos_inf): m_first(r), m_second(pos_inf ? rational::one() : rational::minus_one()) {} inf_rational(rational const& r): m_first(r) { m_second.reset(); } inf_rational(rational const& r, rational const& i): m_first(r), m_second(i) { } ~inf_rational() {} /** \brief Set inf_rational to 0. */ void reset() { m_first.reset(); m_second.reset(); } bool is_int() const { return m_first.is_int() && m_second.is_zero(); } bool is_int64() const { return m_first.is_int64() && m_second.is_zero(); } bool is_uint64() const { return m_first.is_uint64() && m_second.is_zero(); } bool is_rational() const { return m_second.is_zero(); } int64 get_int64() const { SASSERT(is_int64()); return m_first.get_int64(); } uint64 get_uint64() const { SASSERT(is_uint64()); return m_first.get_uint64(); } rational const& get_rational() const { return m_first; } rational const& get_infinitesimal() const { return m_second; } rational const & get_first() const { return m_first; } inf_rational & operator=(const inf_rational & r) { m_first = r.m_first; m_second = r.m_second; return *this; } inf_rational & operator=(const rational & r) { m_first = r; m_second.reset(); return *this; } friend inline inf_rational numerator(const inf_rational & r) { SASSERT(r.m_second.is_zero()); return inf_rational(numerator(r.m_first)); } friend inline inf_rational denominator(const inf_rational & r) { SASSERT(r.m_second.is_zero()); return inf_rational(denominator(r.m_first)); } inf_rational & operator+=(const inf_rational & r) { m_first += r.m_first; m_second += r.m_second; return *this; } inf_rational & operator-=(const inf_rational & r) { m_first -= r.m_first; m_second -= r.m_second; return *this; } inf_rational & operator+=(const rational & r) { m_first += r; return *this; } inf_rational & operator-=(const rational & r) { m_first -= r; return *this; } inf_rational & operator*=(const rational & r1) { m_first *= r1; m_second *= r1; return *this; } // // These operations get us out of the realm of inf_rational: // (r1 + e*k1)*(r2 + e*k2) = (r1*r2 + (r1*k2 + r2*k1)*e) // // inf_rational & operator*=(const inf_rational & r) // inf_rational & operator/=(const inf_rational & r) // inf_rational & operator%=(const inf_rational & r) // friend inline inf_rational div(const inf_rational & r1, const inf_rational & r2) // inf_rational expt(int n) // instead, we define operators that approximate some of these operations from above and below. friend inf_rational inf_mult(inf_rational const& r1, inf_rational const& r2); friend inf_rational sup_mult(inf_rational const& r1, inf_rational const& r2); friend inf_rational inf_div(inf_rational const& r1, inf_rational const& r2); friend inf_rational sup_div(inf_rational const& r1, inf_rational const& r2); friend inf_rational inf_power(inf_rational const& r1, unsigned n); friend inf_rational sup_power(inf_rational const& r1, unsigned n); friend inf_rational inf_root(inf_rational const& r1, unsigned n); friend inf_rational sup_root(inf_rational const& r1, unsigned n); inf_rational & operator/=(const rational & r) { m_first /= r; m_second /= r; return *this; } friend inline inf_rational operator*(const rational & r1, const inf_rational & r2); friend inline inf_rational operator*(const inf_rational & r1, const rational & r2); friend inline inf_rational operator/(const inf_rational & r1, const rational & r2); inf_rational & operator++() { ++m_first; return *this; } const inf_rational operator++(int) { inf_rational tmp(*this); ++(*this); return tmp; } inf_rational & operator--() { --m_first; return *this; } const inf_rational operator--(int) { inf_rational tmp(*this); --(*this); return tmp; } friend inline bool operator==(const inf_rational & r1, const inf_rational & r2) { return r1.m_first == r2.m_first && r1.m_second == r2.m_second; } friend inline bool operator==(const rational & r1, const inf_rational & r2) { return r1 == r2.m_first && r2.m_second.is_zero(); } friend inline bool operator==(const inf_rational & r1, const rational & r2) { return r1.m_first == r2 && r1.m_second.is_zero(); } friend inline bool operator<(const inf_rational & r1, const inf_rational & r2) { return (r1.m_first < r2.m_first) || (r1.m_first == r2.m_first && r1.m_second < r2.m_second); } friend inline bool operator<(const rational & r1, const inf_rational & r2) { return (r1 < r2.m_first) || (r1 == r2.m_first && r2.m_second.is_pos()); } friend inline bool operator<(const inf_rational & r1, const rational & r2) { return (r1.m_first < r2) || (r1.m_first == r2 && r1.m_second.is_neg()); } void neg() { m_first.neg(); m_second.neg(); } bool is_zero() const { return m_first.is_zero() && m_second.is_zero(); } bool is_one() const { return m_first.is_one() && m_second.is_zero(); } bool is_minus_one() const { return m_first.is_minus_one() && m_second.is_zero(); } bool is_neg() const { return m_first.is_neg() || (m_first.is_zero() && m_second.is_neg()); } bool is_pos() const { return m_first.is_pos() || (m_first.is_zero() && m_second.is_pos()); } bool is_nonneg() const { return m_first.is_pos() || (m_first.is_zero() && m_second.is_nonneg()); } bool is_nonpos() const { return m_first.is_neg() || (m_first.is_zero() && m_second.is_nonpos()); } friend inline rational floor(const inf_rational & r) { if (r.m_first.is_int()) { if (r.m_second.is_nonneg()) { return r.m_first; } return r.m_first - rational::one(); } return floor(r.m_first); } friend inline rational ceil(const inf_rational & r) { if (r.m_first.is_int()) { if (r.m_second.is_nonpos()) { return r.m_first; } return r.m_first + rational::one(); } return ceil(r.m_first); } static const inf_rational & zero() { return m_zero; } static const inf_rational & one() { return m_one; } static const inf_rational & minus_one() { return m_minus_one; } // Perform: this += c * k void addmul(const rational & c, const inf_rational & k) { m_first.addmul(c, k.m_first); m_second.addmul(c, k.m_second); } // Perform: this += c * k void submul(const rational & c, const inf_rational & k) { m_first.submul(c, k.m_first); m_second.submul(c, k.m_second); } }; inline bool operator!=(const inf_rational & r1, const inf_rational & r2) { return !operator==(r1, r2); } inline bool operator!=(const rational & r1, const inf_rational & r2) { return !operator==(r1, r2); } inline bool operator!=(const inf_rational & r1, const rational & r2) { return !operator==(r1, r2); } inline bool operator>(const inf_rational & r1, const inf_rational & r2) { return operator<(r2, r1); } inline bool operator>(const inf_rational & r1, const rational & r2) { return operator<(r2, r1); } inline bool operator>(const rational & r1, const inf_rational & r2) { return operator<(r2, r1); } inline bool operator<=(const inf_rational & r1, const inf_rational & r2) { return !operator>(r1, r2); } inline bool operator<=(const rational & r1, const inf_rational & r2) { return !operator>(r1, r2); } inline bool operator<=(const inf_rational & r1, const rational & r2) { return !operator>(r1, r2); } inline bool operator>=(const inf_rational & r1, const inf_rational & r2) { return !operator<(r1, r2); } inline bool operator>=(const rational & r1, const inf_rational & r2) { return !operator<(r1, r2); } inline bool operator>=(const inf_rational & r1, const rational & r2) { return !operator<(r1, r2); } inline inf_rational operator+(const inf_rational & r1, const inf_rational & r2) { return inf_rational(r1) += r2; } inline inf_rational operator-(const inf_rational & r1, const inf_rational & r2) { return inf_rational(r1) -= r2; } inline inf_rational operator-(const inf_rational & r) { inf_rational result(r); result.neg(); return result; } inline inf_rational operator*(const rational & r1, const inf_rational & r2) { inf_rational result(r2); result.m_first *= r1; result.m_second *= r1; return result; } inline inf_rational operator*(const inf_rational & r1, const rational & r2) { return r2 * r1; } inline inf_rational operator/(const inf_rational & r1, const rational & r2) { inf_rational result(r1); result.m_first /= r2; result.m_second /= r2; return result; } #if 0 inf_rational inf_mult(inf_rational const& r1, inf_rational const& r2); inf_rational sup_mult(inf_rational const& r1, inf_rational const& r2); inf_rational inf_div(inf_rational const& r1, inf_rational const& r2); inf_rational sup_div(inf_rational const& r1, inf_rational const& r2); inf_rational inf_power(inf_rational const& r1, unsigned n); inf_rational sup_power(inf_rational const& r1, unsigned n); inf_rational inf_root(inf_rational const& r1, unsigned n); inf_rational sup_root(inf_rational const& r1, unsigned n); #endif // // inline inf_rational operator/(const inf_rational & r1, const inf_rational & r2) // inline inf_rational operator%(const inf_rational & r1, const inf_rational & r2) // inf_rational gcd(const inf_rational & r1, const inf_rational & r2); // inf_rational lcm(const inf_rational & r1, const inf_rational & r2); inline std::ostream & operator<<(std::ostream & target, const inf_rational & r) { target << r.to_string(); return target; } inline inf_rational abs(const inf_rational & r) { inf_rational result(r); if (result.is_neg()) { result.neg(); } return result; } #endif /* INF_RATIONAL_H_ */ z3-z3-4.4.1/src/util/inf_s_integer.cpp000066400000000000000000000005301260446376700175060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_s_integer.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-06-16. Revision History: --*/ #include"inf_s_integer.h" inf_s_integer inf_s_integer::m_zero(0); inf_s_integer inf_s_integer::m_one(1); inf_s_integer inf_s_integer::m_minus_one(-1); z3-z3-4.4.1/src/util/inf_s_integer.h000066400000000000000000000242021260446376700171550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_s_integer.h Abstract: Author: Leonardo de Moura (leonardo) 2007-06-11. Revision History: --*/ #ifndef INF_S_INTEGER_H_ #define INF_S_INTEGER_H_ #include"s_integer.h" #include"rational.h" class inf_s_integer { static inf_s_integer m_zero; static inf_s_integer m_one; static inf_s_integer m_minus_one; int m_first; int m_second; public: unsigned hash() const { return m_first ^ (m_second + 1); } struct hash_proc { unsigned operator()(inf_s_integer const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(inf_s_integer const& r1, inf_s_integer const& r2) const { return r1 == r2; } }; void swap(inf_s_integer & n) { std::swap(m_first, n.m_first); std::swap(m_second, n.m_second); } std::string to_string() const; inf_s_integer():m_first(0), m_second(0) {} inf_s_integer(const inf_s_integer & r):m_first(r.m_first), m_second(r.m_second) {} explicit inf_s_integer(int n):m_first(n), m_second(0) {} explicit inf_s_integer(int n, int d): m_first(n), m_second(0) { SASSERT(d == 1); } explicit inf_s_integer(s_integer const& r, bool pos_inf):m_first(r.get_int()), m_second(pos_inf ? 1 : -1) {} explicit inf_s_integer(s_integer const& r):m_first(r.get_int()), m_second(0) {} explicit inf_s_integer(rational const& r):m_first(static_cast(r.get_int64())), m_second(0) {} inf_s_integer(s_integer const& r, s_integer const& i):m_first(r.get_int()), m_second(i.get_int()) {} void reset() { m_first = 0; m_second = 0; } bool is_int() const { return m_second == 0; } bool is_int64() const { return m_second == 0; } bool is_uint64() const { return m_second == 0; } bool is_rational() const { return m_second == 0; } int64 get_int64() const { return m_first; } uint64 get_uint64() const { return m_first; } s_integer get_rational() const { return s_integer(m_first); } s_integer get_infinitesimal() const { return s_integer(m_second); } inf_s_integer & operator=(const inf_s_integer & r) { m_first = r.m_first; m_second = r.m_second; return *this; } inf_s_integer & operator=(const rational & r) { m_first = static_cast(r.get_int64()); m_second = 0; return *this; } inf_s_integer & operator=(const s_integer & r) { m_first = r.get_int(); m_second = 0; return *this; } friend inline inf_s_integer numerator(const inf_s_integer & r) { SASSERT(r.m_second == 0); return inf_s_integer(r.m_first); } friend inline inf_s_integer denominator(const inf_s_integer & r) { SASSERT(r.m_second == 0); return inf_s_integer(1); } inf_s_integer & operator+=(const inf_s_integer & r) { m_first += r.m_first; m_second += r.m_second; return *this; } inf_s_integer & operator-=(const inf_s_integer & r) { m_first -= r.m_first; m_second -= r.m_second; return *this; } inf_s_integer & operator+=(const s_integer & r) { m_first += r.get_int(); return *this; } inf_s_integer & operator-=(const s_integer & r) { m_first -= r.get_int(); return *this; } inf_s_integer & operator*=(const s_integer & r1) { m_first *= r1.get_int(); m_second *= r1.get_int(); return *this; } // friend inf_s_integer inf_mult(inf_s_integer const& r1, inf_s_integer const& r2); // friend inf_s_integer sup_mult(inf_s_integer const& r1, inf_s_integer const& r2); // friend inf_s_integer inf_div(inf_s_integer const& r1, inf_s_integer const& r2); // friend inf_s_integer sup_div(inf_s_integer const& r1, inf_s_integer const& r2); // friend inf_s_integer inf_power(inf_s_integer const& r1, unsigned n); // friend inf_s_integer sup_power(inf_s_integer const& r1, unsigned n); // friend inf_s_integer inf_root(inf_s_integer const& r1, unsigned n); // friend inf_s_integer sup_root(inf_s_integer const& r1, unsigned n); inf_s_integer & operator/=(const s_integer & r) { m_first /= r.get_int(); m_second /= r.get_int(); return *this; } friend inline inf_s_integer operator*(const s_integer & r1, const inf_s_integer & r2); friend inline inf_s_integer operator/(const inf_s_integer & r1, const s_integer & r2); inf_s_integer & operator++() { ++m_first; return *this; } const inf_s_integer operator++(int) { inf_s_integer tmp(*this); ++(*this); return tmp; } inf_s_integer & operator--() { --m_first; return *this; } const inf_s_integer operator--(int) { inf_s_integer tmp(*this); --(*this); return tmp; } friend inline bool operator==(const inf_s_integer & r1, const inf_s_integer & r2) { return r1.m_first == r2.m_first && r1.m_second == r2.m_second; } friend inline bool operator==(const s_integer & r1, const inf_s_integer & r2) { return r1.get_int() == r2.m_first && r2.m_second == 0; } friend inline bool operator==(const inf_s_integer & r1, const s_integer & r2) { return r1.m_first == r2.get_int() && r1.m_second == 0; } friend inline bool operator<(const inf_s_integer & r1, const inf_s_integer & r2) { return (r1.m_first < r2.m_first) || (r1.m_first == r2.m_first && r1.m_second < r2.m_second); } friend inline bool operator<(const s_integer & r1, const inf_s_integer & r2) { return (r1.get_int() < r2.m_first) || (r1.get_int() == r2.m_first && r2.m_second > 0); } friend inline bool operator<(const inf_s_integer & r1, const s_integer & r2) { return (r1.m_first < r2.get_int()) || (r1.m_first == r2.get_int() && r1.m_second < 0); } void neg() { m_first = -m_first; m_second = -m_second; } bool is_zero() const { return m_first == 0 && m_second == 0; } bool is_one() const { return m_first == 1 && m_second == 0; } bool is_minus_one() const { return m_first == -1 && m_second == 0; } bool is_neg() const { return m_first < 0 || (m_first == 0 && m_second < 0); } bool is_pos() const { return m_first > 0 || (m_first == 0 && m_second > 0); } bool is_nonneg() const { return m_first > 0 || (m_first == 0 && m_second >= 0); } bool is_nonpos() const { return m_first < 0 || (m_first == 0 && m_second <= 0); } friend inline s_integer floor(const inf_s_integer & r) { if (r.m_second >= 0) { return s_integer(r.m_first); } return s_integer(r.m_first - 1); } friend inline s_integer ceil(const inf_s_integer & r) { if (r.m_second <= 0) { return s_integer(r.m_first); } return s_integer(r.m_first + 1); } static const inf_s_integer & zero() { return m_zero; } static const inf_s_integer & one() { return m_one; } static const inf_s_integer & minus_one() { return m_minus_one; } // Perform: this += c * k void addmul(const s_integer & c, const inf_s_integer & k) { m_first += c.get_int() * k.m_first; m_second += c.get_int() * k.m_second; } // Perform: this += c * k void submul(const s_integer & c, const inf_s_integer & k) { m_first -= c.get_int() * k.m_first; m_second -= c.get_int() * k.m_second; } friend inline std::ostream & operator<<(std::ostream & target, const inf_s_integer & r) { if (r.m_second == 0) { target << r.m_first; } else if (r.m_second < 0) { target << "(" << r.m_first << " -e*" << r.m_second << ")"; } else { target << "(" << r.m_first << " +e*" << r.m_second << ")"; } return target; } }; inline bool operator!=(const inf_s_integer & r1, const inf_s_integer & r2) { return !operator==(r1, r2); } inline bool operator!=(const s_integer & r1, const inf_s_integer & r2) { return !operator==(r1, r2); } inline bool operator!=(const inf_s_integer & r1, const s_integer & r2) { return !operator==(r1, r2); } inline bool operator>(const inf_s_integer & r1, const inf_s_integer & r2) { return operator<(r2, r1); } inline bool operator>(const inf_s_integer & r1, const s_integer & r2) { return operator<(r2, r1); } inline bool operator>(const s_integer & r1, const inf_s_integer & r2) { return operator<(r2, r1); } inline bool operator<=(const inf_s_integer & r1, const inf_s_integer & r2) { return !operator>(r1, r2); } inline bool operator<=(const s_integer & r1, const inf_s_integer & r2) { return !operator>(r1, r2); } inline bool operator<=(const inf_s_integer & r1, const s_integer & r2) { return !operator>(r1, r2); } inline bool operator>=(const inf_s_integer & r1, const inf_s_integer & r2) { return !operator<(r1, r2); } inline bool operator>=(const s_integer & r1, const inf_s_integer & r2) { return !operator<(r1, r2); } inline bool operator>=(const inf_s_integer & r1, const s_integer & r2) { return !operator<(r1, r2); } inline inf_s_integer operator+(const inf_s_integer & r1, const inf_s_integer & r2) { return inf_s_integer(r1) += r2; } inline inf_s_integer operator-(const inf_s_integer & r1, const inf_s_integer & r2) { return inf_s_integer(r1) -= r2; } inline inf_s_integer operator-(const inf_s_integer & r) { inf_s_integer result(r); result.neg(); return result; } inline inf_s_integer operator*(const s_integer & r1, const inf_s_integer & r2) { inf_s_integer result(r2); result.m_first *= r1.get_int(); result.m_second *= r1.get_int(); return result; } inline inf_s_integer operator/(const inf_s_integer & r1, const s_integer & r2) { inf_s_integer result(r1); result.m_first /= r2.get_int(); result.m_second /= r2.get_int(); return result; } inline inf_s_integer abs(const inf_s_integer & r) { inf_s_integer result(r); if (result.is_neg()) { result.neg(); } return result; } #endif /* INF_S_INTEGER_H_ */ z3-z3-4.4.1/src/util/lbool.cpp000066400000000000000000000011161260446376700160030ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: lbool.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-08. Revision History: --*/ #include"lbool.h" std::ostream & operator<<(std::ostream & out, lbool b) { switch(b) { case l_false: return out << "l_false"; case l_true: return out << "l_true"; default: return out << "l_undef"; } } char const * to_sat_str(lbool l) { switch (l) { case l_false: return "unsatisfiable"; case l_true: return "satisfiable"; default: return "unknown"; } } z3-z3-4.4.1/src/util/lbool.h000066400000000000000000000012601260446376700154500ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: lbool.h Abstract: Lifted boolean Author: Leonardo de Moura (leonardo) 2008-02-08. Revision History: --*/ #ifndef LBOOL_H_ #define LBOOL_H_ #include"util.h" typedef enum { l_false = -1, l_undef, l_true } lbool; inline lbool operator~(lbool lb) { return static_cast(-static_cast(lb)); } inline lbool to_lbool(bool b) { return static_cast(static_cast(b)*2-1); } std::ostream & operator<<(std::ostream & out, lbool b); /** \brief Convert l_true -> satisfiable, l_false -> unsatisfiable, and l_undef -> unknown. */ char const * to_sat_str(lbool l); #endif /* LBOOL_H_ */ z3-z3-4.4.1/src/util/list.h000066400000000000000000000041771260446376700153260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: list.h Abstract: Simple list data structure. It is meant to be used with region allocators. Author: Leonardo de Moura (leonardo) 2007-07-10. Revision History: --*/ #ifndef LIST_H_ #define LIST_H_ #include"buffer.h" #include"region.h" template class list { T m_head; list * m_tail; public: list(T const & h, list * t = 0): m_head(h), m_tail(t) { } T const & head() const { return m_head; } list * tail() const { return m_tail; } T & head() { return m_head; } list * & tail() { return m_tail; } class iterator { list const * m_curr; public: iterator(list const * c = 0):m_curr(c) {} T const & operator*() const { return m_curr->head(); } iterator & operator++() { m_curr = m_curr->tail(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const & it) { return m_curr == it.m_curr; } bool operator!=(iterator const & it) { return m_curr != it.m_curr; } }; typedef iterator const_iterator; iterator begin() const { return iterator(this); } iterator end() const { return iterator(0); } }; /** \brief Return the list length. */ template unsigned length(list * l) { unsigned r = 0; while(l) { l = l->tail(); r++; } return r; } /** \brief Non destructive apppend operation. The new nodes are allocated using the given region allocator. */ template list * append(region & r, list * l1, list * l2) { if (l2 == 0) { return l1; } ptr_buffer > buffer; while (l1) { buffer.push_back(l1); l1 = l1->tail(); } list * result = l2; typename ptr_buffer >::const_iterator it = buffer.end(); typename ptr_buffer >::const_iterator begin = buffer.begin(); while (it != begin) { --it; list * curr = *it; result = new (r) list(curr->head(), result); } return result; } #endif /* LIST_H_ */ z3-z3-4.4.1/src/util/luby.cpp000066400000000000000000000010531260446376700156470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: luby.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-03-04. Revision History: --*/ #include unsigned get_luby(unsigned i) { if (i == 1) return 1; double k = log(static_cast(i+1))/log(static_cast(2)); if (k == floor(k + 0.5)) return static_cast(pow(2,k-1)); else { k = static_cast(floor(k)); return get_luby(i - static_cast(pow(2, k)) + 1); } } z3-z3-4.4.1/src/util/luby.h000066400000000000000000000007601260446376700153200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: luby.h Abstract: Author: Leonardo de Moura (leonardo) 2008-03-04. Revision History: --*/ #ifndef LUBY_H_ #define LUBY_H_ /** \brief Return the i-th element of the Luby sequence: 1,1,2,1,1,2,4,1,1,2,1,1,2,4,8,... get_luby(i) = 2^{i-1} if i = 2^k -1 get_luby(i) = get_luby(i - 2^{k-1} + 1) if 2^{k-1} <= i < 2^k - 1 */ unsigned get_luby(unsigned i); #endif /* LUBY_H_ */ z3-z3-4.4.1/src/util/machine.h000066400000000000000000000005341260446376700157500ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: machine.h Abstract: Machine/OS dependent configuration Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #ifndef MACHINE_H_ #define MACHINE_H_ #ifdef _AMD64_ #define PTR_ALIGNMENT 3 #else #define PTR_ALIGNMENT 2 #endif #endif /* MACHINE_H_ */ z3-z3-4.4.1/src/util/map.h000066400000000000000000000166671260446376700151370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: map.h Abstract: Simple mapping based on the hashtable. Author: Leonardo de Moura (leonardo) 2006-10-14. Revision History: --*/ #ifndef MAP_H_ #define MAP_H_ #include"hashtable.h" template struct _key_data { Key m_key; Value m_value; _key_data() { } _key_data(Key const & k): m_key(k) { } _key_data(Key const & k, Value const & v): m_key(k), m_value(v) { } }; template class table2map { public: typedef Entry entry; typedef typename Entry::key key; typedef typename Entry::value value; typedef typename Entry::key_data key_data; struct entry_hash_proc : private HashProc { entry_hash_proc(HashProc const & p): HashProc(p) { } unsigned operator()(key_data const & d) const { return HashProc::operator()(d.m_key); } }; struct entry_eq_proc : private EqProc { entry_eq_proc(EqProc const & p): EqProc(p) { } bool operator()(key_data const & d1, key_data const & d2) const { return EqProc::operator()(d1.m_key, d2.m_key); } }; typedef core_hashtable table; table m_table; public: table2map(HashProc const & h = HashProc(), EqProc const & e = EqProc()): m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, entry_hash_proc(h), entry_eq_proc(e)) { } typedef typename table::iterator iterator; void reset() { m_table.reset(); } void finalize() { m_table.finalize(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(key const & k, value const & v) { m_table.insert(key_data(k, v)); } bool insert_if_not_there_core(key const & k, value const & v, entry *& et) { return m_table.insert_if_not_there_core(key_data(k,v), et); } key_data const & insert_if_not_there(key const & k, value const & v) { return m_table.insert_if_not_there(key_data(k, v)); } entry * insert_if_not_there2(key const & k, value const & v) { return m_table.insert_if_not_there2(key_data(k, v)); } entry * find_core(key const & k) const { return m_table.find_core(key_data(k)); } bool find(key const & k, value & v) const { entry * e = find_core(k); if (e) { v = e->get_data().m_value; } return (0 != e); } value const& get(key const& k, value const& default_value) const { entry* e = find_core(k); if (e) { return e->get_data().m_value; } else { return default_value; } } iterator find_iterator(key const & k) const { return m_table.find(key_data(k)); } value const & find(key const& k) const { entry * e = find_core(k); SASSERT(e); return e->get_data().m_value; } value & find(key const& k) { entry * e = find_core(k); SASSERT(e); return e->get_data().m_value; } value const& operator[](key const& k) const { return find(k); } value& operator[](key const& k) { return find(k); } bool contains(key const & k) const { return find_core(k) != 0; } void remove(key const & k) { m_table.remove(key_data(k)); } void erase(key const & k) { remove(k); } unsigned long long get_num_collision() const { return m_table.get_num_collision(); } void swap(table2map & other) { m_table.swap(other.m_table); } #ifdef Z3DEBUG bool check_invariant() { return m_table.check_invariant(); } #endif }; template class default_map_entry : public default_hash_entry<_key_data > { public: typedef Key key; typedef Value value; typedef _key_data key_data; }; template class map : public table2map, HashProc, EqProc> { public: map(HashProc const & h = HashProc(), EqProc const & e = EqProc()): table2map, HashProc, EqProc>(h, e) { } }; template struct _key_ptr_data { Key * m_key; Value m_value; _key_ptr_data(): m_key(0) { } _key_ptr_data(Key * k): m_key(k) { } _key_ptr_data(Key * k, Value const & v): m_key(k), m_value(v) { } }; template class ptr_map_entry { public: typedef _key_ptr_data key_data; typedef _key_ptr_data data; private: unsigned m_hash; //!< cached hash code data m_data; public: typedef Key * key; typedef Value value; unsigned get_hash() const { return m_hash; } bool is_free() const { return m_data.m_key == 0; } bool is_deleted() const { return m_data.m_key == reinterpret_cast(1); } bool is_used() const { return m_data.m_key != reinterpret_cast(0) && m_data.m_key != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_data.m_key = reinterpret_cast(1); } void mark_as_free() { m_data.m_key = 0; } }; template class ptr_addr_map_entry { public: typedef _key_ptr_data key_data; typedef _key_ptr_data data; private: data m_data; public: typedef Key * key; typedef Value value; unsigned get_hash() const { return get_ptr_hash(m_data.m_key); } bool is_free() const { return m_data.m_key == 0; } bool is_deleted() const { return m_data.m_key == reinterpret_cast(1); } bool is_used() const { return m_data.m_key != reinterpret_cast(0) && m_data.m_key != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { SASSERT(h == get_ptr_hash(m_data.m_key)); /* do nothing */ } void mark_as_deleted() { m_data.m_key = reinterpret_cast(1); } void mark_as_free() { m_data.m_key = 0; } }; template class ptr_addr_map : public table2map, ptr_hash, ptr_eq > { public: }; struct u_hash { unsigned operator()(unsigned u) const { return u; } }; struct u_eq { bool operator()(unsigned u1, unsigned u2) const { return u1 == u2; } }; struct size_t_eq { bool operator()(size_t u1, size_t u2) const { return u1 == u2; } }; struct int_eq { bool operator()(int u1, int u2) const { return u1 == u2; } }; template class u_map : public map {}; template class size_t_map : public map {}; #endif z3-z3-4.4.1/src/util/memory_manager.cpp000066400000000000000000000270431260446376700177050ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include #include #include"trace.h" #include"memory_manager.h" #include"error_codes.h" #include"z3_omp.h" // The following two function are automatically generated by the mk_make.py script. // The script collects ADD_INITIALIZER and ADD_FINALIZER commands in the .h files. // For example, rational.h contains // ADD_INITIALIZER('rational::initialize();') // ADD_FINALIZER('rational::finalize();') // Thus, any executable or shared object (DLL) that depends on rational.h // will have an automalically generated file mem_initializer.cpp containing // mem_initialize() // mem_finalize() // and these functions will include the statements: // rational::initialize(); // // rational::finalize(); void mem_initialize(); void mem_finalize(); // If PROFILE_MEMORY is defined, Z3 will display the amount of memory used, and the number of synchronization steps during finalization // #define PROFILE_MEMORY out_of_memory_error::out_of_memory_error():z3_error(ERR_MEMOUT) { } static volatile bool g_memory_out_of_memory = false; static bool g_memory_initialized = false; static long long g_memory_alloc_size = 0; static long long g_memory_max_size = 0; static long long g_memory_max_used_size = 0; static long long g_memory_watermark = 0; static long long g_memory_alloc_count = 0; static long long g_memory_max_alloc_count = 0; static bool g_exit_when_out_of_memory = false; static char const * g_out_of_memory_msg = "ERROR: out of memory"; static volatile bool g_memory_fully_initialized = false; void memory::exit_when_out_of_memory(bool flag, char const * msg) { g_exit_when_out_of_memory = flag; if (flag && msg) g_out_of_memory_msg = msg; } static void throw_out_of_memory() { #pragma omp critical (z3_memory_manager) { g_memory_out_of_memory = true; } if (g_exit_when_out_of_memory) { std::cerr << g_out_of_memory_msg << "\n"; exit(ERR_MEMOUT); } else { throw out_of_memory_error(); } } static void throw_alloc_counts_exceeded() { std::cout << "Maximal allocation counts " << g_memory_max_alloc_count << " have been exceeded\n"; exit(ERR_ALLOC_EXCEEDED); } #ifdef PROFILE_MEMORY static unsigned g_synch_counter = 0; class mem_usage_report { public: ~mem_usage_report() { std::cerr << "(memory :max " << g_memory_max_used_size << " :allocs " << g_memory_alloc_count << " :final " << g_memory_alloc_size << " :synch " << g_synch_counter << ")" << std::endl; } }; mem_usage_report g_info; #endif void memory::initialize(size_t max_size) { bool initialize = false; #pragma omp critical (z3_memory_manager) { // only update the maximum size if max_size != UINT_MAX if (max_size != UINT_MAX) g_memory_max_size = max_size; if (!g_memory_initialized) { g_memory_initialized = true; initialize = true; } } if (initialize) { g_memory_out_of_memory = false; mem_initialize(); g_memory_fully_initialized = true; } else { // Delay the current thread until the DLL is fully initialized // Without this, multiple threads can start to call API functions // before memory::initialize(...) finishes. while (!g_memory_fully_initialized) /* wait */ ; } } bool memory::is_out_of_memory() { bool r = false; #pragma omp critical (z3_memory_manager) { r = g_memory_out_of_memory; } return r; } void memory::set_high_watermark(size_t watermark) { // This method is only safe to invoke at initialization time, that is, before the threads are created. g_memory_watermark = watermark; } bool memory::above_high_watermark() { if (g_memory_watermark == 0) return false; bool r; #pragma omp critical (z3_memory_manager) { r = g_memory_watermark < g_memory_alloc_size; } return r; } // The following methods are only safe to invoke at // initialization time, that is, before threads are created. void memory::set_max_size(size_t max_size) { g_memory_max_size = max_size; } void memory::set_max_alloc_count(size_t max_count) { g_memory_max_alloc_count = max_count; } static bool g_finalizing = false; void memory::finalize() { if (g_memory_initialized) { g_finalizing = true; mem_finalize(); g_memory_initialized = false; g_finalizing = false; } } unsigned long long memory::get_allocation_size() { long long r; #pragma omp critical (z3_memory_manager) { r = g_memory_alloc_size; } if (r < 0) r = 0; return r; } unsigned long long memory::get_max_used_memory() { unsigned long long r; #pragma omp critical (z3_memory_manager) { r = g_memory_max_used_size; } return r; } unsigned long long memory::get_allocation_count() { return g_memory_alloc_count; } void memory::display_max_usage(std::ostream & os) { unsigned long long mem = get_max_used_memory(); os << "max. heap size: " << static_cast(mem)/static_cast(1024*1024) << " Mbytes\n"; } void memory::display_i_max_usage(std::ostream & os) { unsigned long long mem = get_max_used_memory(); std::cout << "MEMORY " << static_cast(mem)/static_cast(1024*1024) << "\n"; } #if _DEBUG void memory::deallocate(char const * file, int line, void * p) { deallocate(p); TRACE_CODE(if (!g_finalizing) TRACE("memory", tout << "dealloc " << std::hex << p << std::dec << " " << file << ":" << line << "\n";);); } void * memory::allocate(char const* file, int line, char const* obj, size_t s) { void * r = allocate(s); TRACE("memory", tout << "alloc " << std::hex << r << std::dec << " " << file << ":" << line << " " << obj << " " << s << "\n";); return r; } #endif #if defined(_WINDOWS) || defined(_USE_THREAD_LOCAL) // ================================== // ================================== // THREAD LOCAL VERSION // ================================== // ================================== // We only integrate the local thread counters with the global one // when the local counter > SYNCH_THRESHOLD #define SYNCH_THRESHOLD 100000 #ifdef _WINDOWS // Actually this is VS specific instead of Windows specific. __declspec(thread) long long g_memory_thread_alloc_size = 0; __declspec(thread) long long g_memory_thread_alloc_count = 0; #else // GCC style __thread long long g_memory_thread_alloc_size = 0; __thread long long g_memory_thread_alloc_count = 0; #endif static void synchronize_counters(bool allocating) { #ifdef PROFILE_MEMORY g_synch_counter++; #endif bool out_of_mem = false; bool counts_exceeded = false; #pragma omp critical (z3_memory_manager) { g_memory_alloc_size += g_memory_thread_alloc_size; g_memory_alloc_count += g_memory_thread_alloc_count; if (g_memory_alloc_size > g_memory_max_used_size) g_memory_max_used_size = g_memory_alloc_size; if (g_memory_max_size != 0 && g_memory_alloc_size > g_memory_max_size) out_of_mem = true; if (g_memory_max_alloc_count != 0 && g_memory_alloc_count > g_memory_max_alloc_count) counts_exceeded = true; } g_memory_thread_alloc_size = 0; if (out_of_mem && allocating) { throw_out_of_memory(); } if (counts_exceeded && allocating) { throw_alloc_counts_exceeded(); } } void memory::deallocate(void * p) { size_t * sz_p = reinterpret_cast(p) - 1; size_t sz = *sz_p; void * real_p = reinterpret_cast(sz_p); g_memory_thread_alloc_size -= sz; free(real_p); if (g_memory_thread_alloc_size < -SYNCH_THRESHOLD) { synchronize_counters(false); } } void * memory::allocate(size_t s) { s = s + sizeof(size_t); // we allocate an extra field! void * r = malloc(s); if (r == 0) throw_out_of_memory(); *(static_cast(r)) = s; g_memory_thread_alloc_size += s; g_memory_thread_alloc_count += 1; if (g_memory_thread_alloc_size > SYNCH_THRESHOLD) { synchronize_counters(true); } return static_cast(r) + 1; // we return a pointer to the location after the extra field } void* memory::reallocate(void *p, size_t s) { size_t *sz_p = reinterpret_cast(p)-1; size_t sz = *sz_p; void *real_p = reinterpret_cast(sz_p); s = s + sizeof(size_t); // we allocate an extra field! g_memory_thread_alloc_size += s - sz; g_memory_thread_alloc_count += 1; if (g_memory_thread_alloc_size > SYNCH_THRESHOLD) { synchronize_counters(true); } void *r = realloc(real_p, s); if (r == 0) throw_out_of_memory(); *(static_cast(r)) = s; return static_cast(r) + 1; // we return a pointer to the location after the extra field } #else // ================================== // ================================== // NO THREAD LOCAL VERSION // ================================== // ================================== // allocate & deallocate without using thread local storage void memory::deallocate(void * p) { size_t * sz_p = reinterpret_cast(p) - 1; size_t sz = *sz_p; void * real_p = reinterpret_cast(sz_p); #pragma omp critical (z3_memory_manager) { g_memory_alloc_size -= sz; } free(real_p); } void * memory::allocate(size_t s) { s = s + sizeof(size_t); // we allocate an extra field! bool out_of_mem = false, counts_exceeded = false; #pragma omp critical (z3_memory_manager) { g_memory_alloc_size += s; g_memory_alloc_count += 1; if (g_memory_alloc_size > g_memory_max_used_size) g_memory_max_used_size = g_memory_alloc_size; if (g_memory_max_size != 0 && g_memory_alloc_size > g_memory_max_size) out_of_mem = true; if (g_memory_max_alloc_count != 0 && g_memory_alloc_count > g_memory_max_alloc_count) counts_exceeded = true; } if (out_of_mem) throw_out_of_memory(); if (counts_exceeded) throw_alloc_counts_exceeded(); void * r = malloc(s); if (r == 0) throw_out_of_memory(); *(static_cast(r)) = s; return static_cast(r) + 1; // we return a pointer to the location after the extra field } void* memory::reallocate(void *p, size_t s) { size_t * sz_p = reinterpret_cast(p) - 1; size_t sz = *sz_p; void * real_p = reinterpret_cast(sz_p); s = s + sizeof(size_t); // we allocate an extra field! bool out_of_mem = false, counts_exceeded = false; #pragma omp critical (z3_memory_manager) { g_memory_alloc_size += s - sz; g_memory_alloc_count += 1; if (g_memory_alloc_size > g_memory_max_used_size) g_memory_max_used_size = g_memory_alloc_size; if (g_memory_max_size != 0 && g_memory_alloc_size > g_memory_max_size) out_of_mem = true; if (g_memory_max_alloc_count != 0 && g_memory_alloc_count > g_memory_max_alloc_count) counts_exceeded = true; } if (out_of_mem) throw_out_of_memory(); if (counts_exceeded) throw_alloc_counts_exceeded(); void *r = realloc(real_p, s); if (r == 0) throw_out_of_memory(); *(static_cast(r)) = s; return static_cast(r) + 1; // we return a pointer to the location after the extra field } #endif z3-z3-4.4.1/src/util/memory_manager.h000066400000000000000000000061501260446376700173460ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: memory_manager.h Abstract: Custom memory layer. Author: Nikolaj Bjorner (nbjorner) 2007-07-24 Revision History: --*/ #ifndef MEMORY_H_ #define MEMORY_H_ #include #include #include"z3_exception.h" #ifndef __has_builtin # define __has_builtin(x) 0 #endif #ifdef __GNUC__ # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 || __has_builtin(returns_nonnull) # define GCC_RET_NON_NULL __attribute__((returns_nonnull)) # else # define GCC_RET_NON_NULL # endif # define ALLOC_ATTR __attribute__((malloc)) GCC_RET_NON_NULL #elif defined(_WINDOWS) # define ALLOC_ATTR __declspec(restrict) #else # define ALLOC_ATTR #endif class out_of_memory_error : public z3_error { public: out_of_memory_error(); }; class exceeded_memory_allocations : public z3_error { public: exceeded_memory_allocations(); }; class memory { public: static bool is_out_of_memory(); static void initialize(size_t max_size); static void set_high_watermark(size_t watermak); static bool above_high_watermark(); static void set_max_size(size_t max_size); static void set_max_alloc_count(size_t max_count); static void finalize(); static void display_max_usage(std::ostream& os); static void display_i_max_usage(std::ostream& os); static void deallocate(void* p); static ALLOC_ATTR void* allocate(size_t s); static ALLOC_ATTR void* reallocate(void *p, size_t s); #if _DEBUG static void deallocate(char const* file, int line, void* p); static ALLOC_ATTR void* allocate(char const* file, int line, char const* obj, size_t s); #endif static unsigned long long get_allocation_size(); static unsigned long long get_max_used_memory(); static unsigned long long get_allocation_count(); // temporary hack to avoid out-of-memory crash in z3.exe static void exit_when_out_of_memory(bool flag, char const * msg); }; #if _DEBUG #define alloc(T,...) new (memory::allocate(__FILE__,__LINE__,#T, sizeof(T))) T(__VA_ARGS__) #define dealloc(_ptr_) deallocf(__FILE__,__LINE__,_ptr_) template void deallocf(char const* file, int line, T * ptr) { if (ptr == 0) return; ptr->~T(); memory::deallocate(file, line, ptr); } #else #define alloc(T,...) new (memory::allocate(sizeof(T))) T(__VA_ARGS__) template void dealloc(T * ptr) { if (ptr == 0) return; ptr->~T(); memory::deallocate(ptr); } #endif template ALLOC_ATTR T * alloc_vect(unsigned sz); template T * alloc_vect(unsigned sz) { T * r = static_cast(memory::allocate(sizeof(T) * sz)); T * curr = r; for (unsigned i = 0; i < sz; i++, curr++) new (curr) T(); return r; } template void dealloc_vect(T * ptr, unsigned sz) { if (ptr == 0) return; T * curr = ptr; for (unsigned i = 0; i < sz; i++, curr++) curr->~T(); memory::deallocate(ptr); } #define alloc_svect(T, sz) static_cast(memory::allocate(sizeof(T) * sz)) template void dealloc_svect(T * ptr) { if (ptr == 0) return; memory::deallocate(ptr); } #endif /* MEMORY_H_ */ z3-z3-4.4.1/src/util/mpbq.cpp000066400000000000000000000607071260446376700156460ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpbq.cpp Abstract: Binary Rational Numbers A binary rational is a number of the form a/2^k. All integers are binary rationals. Binary rational numbers can be implemented more efficiently than rationals. Binary rationals form a Ring. They are not closed under division. In Z3, they are used to implement algebraic numbers. The root isolation operations only use division by 2. Author: Leonardo de Moura (leonardo) 2011-11-24. Revision History: --*/ #include #include"mpbq.h" #ifdef Z3DEBUG #define MPBQ_DEBUG #endif rational to_rational(mpbq const & v) { rational r(v.numerator()); rational twok; twok = power(rational(2), v.k()); return r/twok; } mpbq_manager::mpbq_manager(unsynch_mpz_manager & m): m_manager(m) { } mpbq_manager::~mpbq_manager() { del(m_addmul_tmp); m_manager.del(m_tmp); m_manager.del(m_tmp2); m_manager.del(m_select_int_tmp1); m_manager.del(m_select_int_tmp2); m_manager.del(m_select_small_tmp); del(m_select_small_tmp1); del(m_select_small_tmp2); m_manager.del(m_div_tmp1); m_manager.del(m_div_tmp2); m_manager.del(m_div_tmp3); } void mpbq_manager::reset(mpbq_vector & v) { unsigned sz = v.size(); for (unsigned i = 0; i < sz; i++) reset(v[i]); v.reset(); } void mpbq_manager::normalize(mpbq & a) { if (a.m_k == 0) return; if (m_manager.is_zero(a.m_num)) { a.m_k = 0; return; } #ifdef MPBQ_DEBUG rational r = to_rational(a); #endif unsigned k = m_manager.power_of_two_multiple(a.m_num); if (k > a.m_k) k = a.m_k; m_manager.machine_div2k(a.m_num, k); a.m_k -= k; #ifdef MPBQ_DEBUG rational new_r = to_rational(a); SASSERT(r == new_r); #endif } int mpbq_manager::magnitude_lb(mpbq const & a) { if (m_manager.is_zero(a.m_num)) return 0; if (m_manager.is_pos(a.m_num)) return m_manager.log2(a.m_num) - a.m_k; return m_manager.mlog2(a.m_num) - a.m_k + 1; } int mpbq_manager::magnitude_ub(mpbq const & a) { if (m_manager.is_zero(a.m_num)) return 0; if (m_manager.is_pos(a.m_num)) return m_manager.log2(a.m_num) - a.m_k + 1; return m_manager.mlog2(a.m_num) - a.m_k; } void mpbq_manager::mul2(mpbq & a) { if (a.m_k == 0) m_manager.mul2k(a.m_num, 1); else a.m_k--; } void mpbq_manager::mul2k(mpbq & a, unsigned k) { if (k == 0) return; if (a.m_k < k) { m_manager.mul2k(a.m_num, k - a.m_k); a.m_k = 0; } else { SASSERT(a.m_k >= k); a.m_k -= k; } } void mpbq_manager::add(mpbq const & a, mpbq const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b = to_rational(b); #endif if (a.m_k == b.m_k) { m_manager.add(a.m_num, b.m_num, r.m_num); r.m_k = a.m_k; } else if (a.m_k < b.m_k) { m_manager.mul2k(a.m_num, b.m_k - a.m_k, m_tmp); m_manager.add(b.m_num, m_tmp, r.m_num); r.m_k = b.m_k; } else { SASSERT(a.m_k > b.m_k); m_manager.mul2k(b.m_num, a.m_k - b.m_k, m_tmp); m_manager.add(a.m_num, m_tmp, r.m_num); r.m_k = a.m_k; } normalize(r); #ifdef MPBQ_DEBUG rational _r = to_rational(r); SASSERT(_a + _b == _r); #endif } void mpbq_manager::add(mpbq const & a, mpz const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b(b); #endif if (a.m_k == 0) { m_manager.add(a.m_num, b, r.m_num); r.m_k = a.m_k; } else { m_manager.mul2k(b, a.m_k, m_tmp); m_manager.add(a.m_num, m_tmp, r.m_num); r.m_k = a.m_k; } normalize(r); #ifdef MPBQ_DEBUG rational _r = to_rational(r); TRACE("mpbq_bug", tout << "add a: " << _a << ", b: " << _b << ", r: " << _r << ", expected: " << (_a + _b) << "\n";); SASSERT(_a + _b == _r); #endif } void mpbq_manager::sub(mpbq const & a, mpbq const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b = to_rational(b); #endif if (a.m_k == b.m_k) { m_manager.sub(a.m_num, b.m_num, r.m_num); r.m_k = a.m_k; } else if (a.m_k < b.m_k) { m_manager.mul2k(a.m_num, b.m_k - a.m_k, m_tmp); m_manager.sub(m_tmp, b.m_num, r.m_num); r.m_k = b.m_k; } else { SASSERT(a.m_k > b.m_k); m_manager.mul2k(b.m_num, a.m_k - b.m_k, m_tmp); m_manager.sub(a.m_num, m_tmp, r.m_num); r.m_k = a.m_k; } normalize(r); #ifdef MPBQ_DEBUG rational _r = to_rational(r); TRACE("mpbq_bug", tout << "sub a: " << _a << ", b: " << _b << ", r: " << _r << ", expected: " << (_a - _b) << "\n";); SASSERT(_a - _b == _r); #endif } void mpbq_manager::sub(mpbq const & a, mpz const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b(b); #endif if (a.m_k == 0) { m_manager.sub(a.m_num, b, r.m_num); r.m_k = a.m_k; } else { m_manager.mul2k(b, a.m_k, m_tmp); m_manager.sub(a.m_num, m_tmp, r.m_num); r.m_k = a.m_k; } normalize(r); #ifdef MPBQ_DEBUG rational _r = to_rational(r); SASSERT(_a - _b == _r); #endif } void mpbq_manager::mul(mpbq const & a, mpbq const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b = to_rational(b); #endif m_manager.mul(a.m_num, b.m_num, r.m_num); r.m_k = a.m_k + b.m_k; if (a.m_k == 0 || b.m_k == 0) { // if a.m_k and b.m_k are greater than 0, then there is no point in normalizing r. normalize(r); } #ifdef MPBQ_DEBUG rational _r = to_rational(r); SASSERT(_a * _b == _r); #endif } void mpbq_manager::mul(mpbq const & a, mpz const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b(b); #endif m_manager.mul(a.m_num, b, r.m_num); r.m_k = a.m_k; normalize(r); #ifdef MPBQ_DEBUG rational _r = to_rational(r); SASSERT(_a * _b == _r); #endif } void mpbq_manager::power(mpbq & a, unsigned k) { SASSERT(static_cast(k) * static_cast(a.k()) <= static_cast(UINT_MAX)); // We don't need to normalize because: // If a.m_k == 0, then a is an integer, and the result be an integer // If a.m_k > 0, then a.m_num must be odd, and the (a.m_num)^k will also be odd a.m_k *= k; m_manager.power(a.m_num, k, a.m_num); } bool mpbq_manager::root_lower(mpbq & a, unsigned n) { bool r = m_manager.root(a.m_num, n); if (!r) m_manager.dec(a.m_num); if (a.m_k % n == 0) { a.m_k /= n; normalize(a); return r; } else if (m_manager.is_neg(a.m_num)) { a.m_k /= n; normalize(a); return false; } else { a.m_k /= n; a.m_k++; normalize(a); return false; } } bool mpbq_manager::root_upper(mpbq & a, unsigned n) { bool r = m_manager.root(a.m_num, n); if (a.m_k % n == 0) { a.m_k /= n; normalize(a); return r; } else if (m_manager.is_neg(a.m_num)) { a.m_k /= n; a.m_k++; normalize(a); return false; } else { a.m_k /= n; normalize(a); return false; } } bool mpbq_manager::lt(mpbq const & a, mpbq const & b) { // TODO: try the following trick when k1 != k2 // Given, a = n1/2^k1 b = n2/2^k2 // Suppose n1 > 0 and n2 > 0, // Then, we have, n1 <= 2^{log2(n1) - k1} 2^{log2(n2) - 1 - k2} <= n2 // Thus, log2(n1) - k1 < log2(n2) - 1 - k2 implies a < b // Similarly: log2(n2) - k2 < log2(n1) - 1 - k1 implies b < a // That is we compare the "magnitude" of the numbers before performing mul2k // // If n1 < 0 and n2 < 0, a similar trick can be implemented using mlog2 instead log2. // // It seems the trick is not useful when n1 and n2 are small // numbers, and k1 and k2 very small < 8. Since, no bignumber // computation is needed for mul2k. if (a.m_k == b.m_k) { return m_manager.lt(a.m_num, b.m_num); } else if (a.m_k < b.m_k) { m_manager.mul2k(a.m_num, b.m_k - a.m_k, m_tmp); return m_manager.lt(m_tmp, b.m_num); } else { SASSERT(a.m_k > b.m_k); m_manager.mul2k(b.m_num, a.m_k - b.m_k, m_tmp); return m_manager.lt(a.m_num, m_tmp); } } bool mpbq_manager::lt_1div2k(mpbq const & a, unsigned k) { if (m_manager.is_nonpos(a.m_num)) return true; if (a.m_k <= k) { // since a.m_num >= 1 return false; } else { SASSERT(a.m_k > k); m_manager.mul2k(mpz(1), a.m_k - k, m_tmp); return m_manager.lt(a.m_num, m_tmp); } } bool mpbq_manager::eq(mpbq const & a, mpq const & b) { if (is_int(a) && m_manager.is_one(b.denominator())) return m_manager.eq(a.m_num, b.numerator()); m_manager.mul2k(b.numerator(), a.m_k, m_tmp); m_manager.mul(a.m_num, b.denominator(), m_tmp2); return m_manager.eq(m_tmp, m_tmp2); } bool mpbq_manager::lt(mpbq const & a, mpq const & b) { if (is_int(a) && m_manager.is_one(b.denominator())) return m_manager.lt(a.m_num, b.numerator()); m_manager.mul(a.m_num, b.denominator(), m_tmp); m_manager.mul2k(b.numerator(), a.m_k, m_tmp2); return m_manager.lt(m_tmp, m_tmp2); } bool mpbq_manager::le(mpbq const & a, mpq const & b) { if (is_int(a) && m_manager.is_one(b.denominator())) return m_manager.le(a.m_num, b.numerator()); m_manager.mul(a.m_num, b.denominator(), m_tmp); m_manager.mul2k(b.numerator(), a.m_k, m_tmp2); return m_manager.le(m_tmp, m_tmp2); } bool mpbq_manager::lt(mpbq const & a, mpz const & b) { if (is_int(a)) return m_manager.lt(a.m_num, b); m_manager.mul2k(b, a.m_k, m_tmp); return m_manager.lt(a.m_num, m_tmp); } bool mpbq_manager::le(mpbq const & a, mpz const & b) { if (is_int(a)) return m_manager.le(a.m_num, b); m_manager.mul2k(b, a.m_k, m_tmp); return m_manager.le(a.m_num, m_tmp); } std::string mpbq_manager::to_string(mpbq const & a) { std::ostringstream buffer; buffer << m_manager.to_string(a.m_num); if (a.m_k == 1) buffer << "/2"; else if (a.m_k > 1) buffer << "/2^" << a.m_k; return buffer.str(); } void mpbq_manager::display(std::ostream & out, mpbq const & a) { out << m_manager.to_string(a.m_num); if (a.m_k > 0) out << "/2"; if (a.m_k > 1) out << "^" << a.m_k; } void mpbq_manager::display_pp(std::ostream & out, mpbq const & a) { out << m_manager.to_string(a.m_num); if (a.m_k > 0) out << "/2"; if (a.m_k > 1) out << "" << a.m_k << ""; } void mpbq_manager::display_smt2(std::ostream & out, mpbq const & a, bool decimal) { if (a.m_k == 0) { m_manager.display_smt2(out, a.m_num, decimal); } else { out << "(/ "; m_manager.display_smt2(out, a.m_num, decimal); out << " "; out << "(^ 2"; if (decimal) out << ".0"; out << " " << a.m_k; if (decimal) out << ".0"; out << "))"; } } void mpbq_manager::display_decimal(std::ostream & out, mpbq const & a, unsigned prec) { if (is_int(a)) { out << m_manager.to_string(a.m_num); return; } mpz two(2); mpz ten(10); mpz two_k; mpz n1, v1; if (m_manager.is_neg(a.m_num)) out << "-"; m_manager.set(v1, a.m_num); m_manager.abs(v1); m_manager.power(two, a.m_k, two_k); m_manager.rem(v1, two_k, n1); m_manager.div(v1, two_k, v1); SASSERT(!m_manager.is_zero(n1)); out << m_manager.to_string(v1); out << "."; for (unsigned i = 0; i < prec; i++) { m_manager.mul(n1, ten, n1); m_manager.div(n1, two_k, v1); m_manager.rem(n1, two_k, n1); out << m_manager.to_string(v1); if (m_manager.is_zero(n1)) goto end; } out << "?"; end: m_manager.del(n1); m_manager.del(v1); m_manager.del(two_k); } void mpbq_manager::display_decimal(std::ostream & out, mpbq const & a, mpbq const & b, unsigned prec) { mpz two(2); mpz ten(10); mpz two_k1, two_k2; mpz n1, v1, n2, v2; if (m_manager.is_neg(a.m_num) != m_manager.is_neg(b.m_num)) { out << "?"; return; } if (m_manager.is_neg(a.m_num)) out << "-"; m_manager.set(v1, a.m_num); m_manager.abs(v1); m_manager.set(v2, b.m_num); m_manager.abs(v2); m_manager.power(two, a.m_k, two_k1); m_manager.power(two, b.m_k, two_k2); m_manager.rem(v1, two_k1, n1); m_manager.rem(v2, two_k2, n2); m_manager.div(v1, two_k1, v1); m_manager.div(v2, two_k2, v2); if (!m_manager.eq(v1, v2)) { out << "?"; goto end; } out << m_manager.to_string(v1); if (m_manager.is_zero(n1) && m_manager.is_zero(n2)) goto end; // number is an integer out << "."; for (unsigned i = 0; i < prec; i++) { m_manager.mul(n1, ten, n1); m_manager.mul(n2, ten, n2); m_manager.div(n1, two_k1, v1); m_manager.div(n2, two_k2, v2); if (m_manager.eq(v1, v2)) { out << m_manager.to_string(v1); } else { out << "?"; goto end; } m_manager.rem(n1, two_k1, n1); m_manager.rem(n2, two_k2, n2); if (m_manager.is_zero(n1) && m_manager.is_zero(n2)) goto end; // number is precise } out << "?"; end: m_manager.del(n1); m_manager.del(v1); m_manager.del(n2); m_manager.del(v2); m_manager.del(two_k1); m_manager.del(two_k2); } bool mpbq_manager::to_mpbq(mpq const & q, mpbq & bq) { mpz const & n = q.numerator(); mpz const & d = q.denominator(); unsigned shift; if (m_manager.is_one(d)) { set(bq, n); SASSERT(eq(bq, q)); return true; } else if (m_manager.is_power_of_two(d, shift)) { SASSERT(shift>=1); unsigned k = shift; set(bq, n, k); SASSERT(eq(bq, q)); return true; } else { unsigned k = m_manager.log2(d); set(bq, n, k+1); return false; } } void mpbq_manager::refine_upper(mpq const & q, mpbq & l, mpbq & u) { SASSERT(lt(l, q) && gt(u, q)); SASSERT(!m_manager.is_power_of_two(q.denominator())); // l < q < u mpbq mid; while (true) { add(l, u, mid); div2(mid); if (gt(mid, q)) { swap(u, mid); del(mid); SASSERT(lt(l, q) && gt(u, q)); return; } swap(l, mid); } } void mpbq_manager::refine_lower(mpq const & q, mpbq & l, mpbq & u) { SASSERT(lt(l, q) && gt(u, q)); SASSERT(!m_manager.is_power_of_two(q.denominator())); // l < q < u mpbq mid; while (true) { add(l, u, mid); div2(mid); if (lt(mid, q)) { swap(l, mid); del(mid); SASSERT(lt(l, q) && gt(u, q)); return; } swap(u, mid); } } // sectect integer in [lower, upper] bool mpbq_manager::select_integer(mpbq const & lower, mpbq const & upper, mpz & r) { if (is_int(lower)) { m_manager.set(r, lower.m_num); return true; } if (is_int(upper)) { m_manager.set(r, upper.m_num); return true; } mpz & ceil_lower = m_select_int_tmp1; mpz & floor_upper = m_select_int_tmp2; ceil(m_manager, lower, ceil_lower); floor(m_manager, upper, floor_upper); if (m_manager.le(ceil_lower, floor_upper)) { m_manager.set(r, ceil_lower); return true; } return false; } // select integer in (lower, upper] bool mpbq_manager::select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpz & r) { if (is_int(upper)) { m_manager.set(r, upper.m_num); return true; } mpz & ceil_lower = m_select_int_tmp1; mpz & floor_upper = m_select_int_tmp2; if (qm.is_int(lower)) { m_manager.set(ceil_lower, lower.numerator()); m_manager.inc(ceil_lower); } else { scoped_mpz tmp(qm); qm.ceil(lower, tmp); m_manager.set(ceil_lower, tmp); } floor(m_manager, upper, floor_upper); if (m_manager.le(ceil_lower, floor_upper)) { m_manager.set(r, ceil_lower); return true; } return false; } // sectect integer in [lower, upper) bool mpbq_manager::select_integer(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpz & r) { if (is_int(lower)) { m_manager.set(r, lower.m_num); return true; } mpz & ceil_lower = m_select_int_tmp1; mpz & floor_upper = m_select_int_tmp2; ceil(m_manager, lower, ceil_lower); if (qm.is_int(upper)) { m_manager.set(floor_upper, upper.numerator()); m_manager.dec(floor_upper); } else { scoped_mpz tmp(qm); qm.floor(upper, tmp); m_manager.set(floor_upper, tmp); } if (m_manager.le(ceil_lower, floor_upper)) { m_manager.set(r, ceil_lower); return true; } return false; } // sectect integer in (lower, upper) bool mpbq_manager::select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpz & r) { mpz & ceil_lower = m_select_int_tmp1; mpz & floor_upper = m_select_int_tmp2; if (qm.is_int(lower)) { m_manager.set(ceil_lower, lower.numerator()); m_manager.inc(ceil_lower); } else { scoped_mpz tmp(qm); qm.ceil(lower, tmp); m_manager.set(ceil_lower, tmp); } if (qm.is_int(upper)) { m_manager.set(floor_upper, upper.numerator()); m_manager.dec(floor_upper); } else { scoped_mpz tmp(qm); qm.floor(upper, tmp); m_manager.set(floor_upper, tmp); } if (m_manager.le(ceil_lower, floor_upper)) { m_manager.set(r, ceil_lower); return true; } return false; } #define LINEAR_SEARCH_THRESHOLD 8 void mpbq_manager::select_small_core(mpbq const & lower, mpbq const & upper, mpbq & r) { SASSERT(le(lower, upper)); mpz & aux = m_select_small_tmp; if (select_integer(lower, upper, aux)) { set(r, aux); return; } // At this point we know that k=0 does not work, since there is no integer // in the interval [lower, upper] unsigned min_k = 0; unsigned max_k = std::min(lower.m_k, upper.m_k); if (max_k <= LINEAR_SEARCH_THRESHOLD) { unsigned k = 0; mpbq & l2k = m_select_small_tmp1; mpbq & u2k = m_select_small_tmp2; set(l2k, lower); set(u2k, upper); while (true) { k++; mul2(l2k); mul2(u2k); if (select_integer(l2k, u2k, aux)) { set(r, aux, k); break; } } } else { mpbq & l2k = m_select_small_tmp1; mpbq & u2k = m_select_small_tmp2; while (true) { unsigned mid_k = min_k + (max_k - min_k)/2; set(l2k, lower); set(u2k, upper); mul2k(l2k, mid_k); mul2k(u2k, mid_k); if (select_integer(l2k, u2k, aux)) max_k = mid_k; else min_k = mid_k + 1; if (min_k == max_k) { if (max_k == mid_k) { set(r, aux, max_k); } else { set(l2k, lower); set(u2k, upper); mul2k(l2k, max_k); mul2k(u2k, max_k); VERIFY(select_integer(l2k, u2k, aux)); set(r, aux, max_k); } break; } } } SASSERT(le(lower, r)); SASSERT(le(r, upper)); } bool mpbq_manager::select_small(mpbq const & lower, mpbq const & upper, mpbq & r) { if (gt(lower, upper)) return false; select_small_core(lower, upper, r); return true; } void mpbq_manager::select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpbq & r) { TRACE("select_small", tout << "lower (q): " << qm.to_string(lower) << ", upper (bq): " << to_string(upper) << "\n";); SASSERT(gt(upper, lower)); mpz & aux = m_select_small_tmp; if (select_integer(qm, lower, upper, aux)) { set(r, aux); return; } // At this point we know that k=0 does not work, since there is no integer // in the interval [lower, upper] unsigned k = 0; scoped_mpq l2k(qm); mpq two(2); mpbq & u2k = m_select_small_tmp2; qm.set(l2k, lower); set(u2k, upper); while (true) { k++; qm.mul(l2k, two, l2k); mul2(u2k); if (select_integer(qm, l2k, u2k, aux)) { set(r, aux, k); break; } } } void mpbq_manager::select_small_core(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpbq & r) { SASSERT(lt(lower, upper)); mpz & aux = m_select_small_tmp; if (select_integer(qm, lower, upper, aux)) { set(r, aux); return; } // At this point we know that k=0 does not work, since there is no integer // in the interval [lower, upper] unsigned k = 0; mpbq & l2k = m_select_small_tmp2; scoped_mpq u2k(qm); mpq two(2); set(l2k, lower); qm.set(u2k, upper); while (true) { k++; mul2(l2k); qm.mul(u2k, two, u2k); if (select_integer(qm, l2k, u2k, aux)) { set(r, aux, k); break; } } } void mpbq_manager::select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpbq & r) { SASSERT(qm.lt(lower, upper)); mpz & aux = m_select_small_tmp; if (select_integer(qm, lower, upper, aux)) { set(r, aux); return; } // At this point we know that k=0 does not work, since there is no integer // in the interval [lower, upper] unsigned k = 0; scoped_mpq l2k(qm); scoped_mpq u2k(qm); mpq two(2); qm.set(l2k, lower); qm.set(u2k, upper); while (true) { k++; qm.mul(l2k, two, l2k); qm.mul(u2k, two, u2k); if (select_integer(qm, l2k, u2k, aux)) { set(r, aux, k); break; } } } void mpbq_manager::approx(mpbq & a, unsigned k, bool to_plus_inf) { if (a.m_k <= k) return; #ifdef MPBQ_DEBUG scoped_mpbq old_a(*this); old_a = a; #endif bool sgn = m_manager.is_neg(a.m_num); bool _inc = (sgn != to_plus_inf); unsigned shift = a.m_k - k; m_manager.abs(a.m_num); m_manager.machine_div2k(a.m_num, shift); if (_inc) m_manager.inc(a.m_num); if (sgn) m_manager.neg(a.m_num); a.m_k = k; normalize(a); #ifdef MPBQ_DEBUG if (to_plus_inf) { SASSERT(lt(old_a, a)); } else { SASSERT(lt(a, old_a)); } #endif } void mpbq_manager::approx_div(mpbq const & a, mpbq const & b, mpbq & c, unsigned k, bool to_plus_inf) { SASSERT(!is_zero(b)); unsigned k_prime; if (m_manager.is_power_of_two(b.m_num, k_prime)) { // The division is precise, so we ignore k and to_plus_inf SASSERT(b.m_k == 0 || k_prime == 0); // remark: b.m_num is odd when b.m_k > 0, since b.m_num is a power of two we have that b.m_k == 0 or b.m_num == 1. m_manager.set(c.m_num, a.m_num); if (b.m_k > 0) { SASSERT(k_prime == 0); mpz & pw2 = m_div_tmp1; m_manager.power(mpz(2), b.m_k, pw2); m_manager.mul(c.m_num, pw2, c.m_num); } c.m_k = a.m_k + k_prime; normalize(c); } else if (m_manager.divides(b.m_num, a.m_num)) { // result is also precise m_manager.div(a.m_num, b.m_num, c.m_num); if (a.m_k >= b.m_k) { c.m_k = a.m_k - b.m_k; } else { m_manager.mul2k(c.m_num, b.m_k - a.m_k); c.m_k = 0; } normalize(c); } else { bool sgn = is_neg(a) != is_neg(b); mpz & abs_a = m_div_tmp1; mpz & norm_a = m_div_tmp2; mpz & abs_b = m_div_tmp3; m_manager.set(abs_a, a.m_num); m_manager.abs(abs_a); m_manager.set(abs_b, b.m_num); m_manager.abs(abs_b); if (a.m_k > b.m_k) { if (k >= a.m_k - b.m_k) m_manager.mul2k(abs_a, k - (a.m_k - b.m_k), norm_a); else m_manager.machine_div2k(abs_a, (a.m_k - b.m_k) - k, norm_a); } else { m_manager.mul2k(abs_a, k + b.m_k - a.m_k, norm_a); } c.m_k = k; m_manager.div(norm_a, abs_b, c.m_num); if (sgn != to_plus_inf) m_manager.inc(c.m_num); if (sgn) m_manager.neg(c.m_num); normalize(c); } } z3-z3-4.4.1/src/util/mpbq.h000066400000000000000000000330411260446376700153020ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpbq.h Abstract: Binary Rational Numbers A binary rational is a number of the form a/2^k. All integers are binary rationals. Binary rational numbers can be implemented more efficiently than rationals. Binary rationals form a Ring. They are not closed under division. In Z3, they are used to implement algebraic numbers. The root isolation operations only use division by 2. Author: Leonardo de Moura (leonardo) 2011-11-24. Revision History: --*/ #ifndef MPBQ_H_ #define MPBQ_H_ #include"mpq.h" #include"rational.h" #include"vector.h" class mpbq { mpz m_num; unsigned m_k; // we don't need mpz here. 2^(2^32-1) is a huge number, we will not even be able to convert the mpbq into an mpq friend class mpbq_manager; public: mpbq():m_num(0), m_k(0) {} mpbq(int v):m_num(v), m_k(0) {} mpbq(int v, unsigned k):m_num(v), m_k(k) {} mpz const & numerator() const { return m_num; } unsigned k() const { return m_k; } void swap(mpbq & other) { m_num.swap(other.m_num); std::swap(m_k, other.m_k); } }; inline void swap(mpbq & m1, mpbq & m2) { m1.swap(m2); } typedef svector mpbq_vector; class mpbq_manager { unsynch_mpz_manager & m_manager; mpz m_tmp; mpz m_tmp2; mpbq m_addmul_tmp; mpz m_select_int_tmp1; mpz m_select_int_tmp2; mpz m_select_small_tmp; mpbq m_select_small_tmp1; mpbq m_select_small_tmp2; mpz m_div_tmp1, m_div_tmp2, m_div_tmp3; void normalize(mpbq & a); bool select_integer(mpbq const & lower, mpbq const & upper, mpz & r); bool select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpz & r); bool select_integer(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpz & r); bool select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpz & r); public: static bool precise() { return true; } static bool field() { return false; } typedef mpbq numeral; mpbq_manager(unsynch_mpz_manager & m); ~mpbq_manager(); static void swap(mpbq & a, mpbq & b) { a.swap(b); } void del(mpbq & a) { m_manager.del(a.m_num); } void reset(mpbq & a) { m_manager.reset(a.m_num); a.m_k = 0; } void reset(mpbq_vector & v); void set(mpbq & a, int n) { m_manager.set(a.m_num, n); a.m_k = 0; } void set(mpbq & a, unsigned n) { m_manager.set(a.m_num, n); a.m_k = 0; } void set(mpbq & a, int n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); } void set(mpbq & a, mpz const & n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); } void set(mpbq & a, mpz const & n) { m_manager.set(a.m_num, n); a.m_k = 0; } void set(mpbq & a, mpbq const & b) { m_manager.set(a.m_num, b.m_num); a.m_k = b.m_k; } void set(mpbq & a, int64 n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); } bool is_int(mpbq const & a) const { return a.m_k == 0; } void get_numerator(mpbq const & a, mpz & n) { m_manager.set(n, a.m_num); } unsigned get_denominator_power(mpbq const & a) { return a.m_k; } bool is_zero(mpbq const & a) const { return m_manager.is_zero(a.m_num); } bool is_nonzero(mpbq const & a) const { return !is_zero(a); } bool is_one(mpbq const & a) const { return a.m_k == 0 && m_manager.is_one(a.m_num); } bool is_pos(mpbq const & a) const { return m_manager.is_pos(a.m_num); } bool is_neg(mpbq const & a) const { return m_manager.is_neg(a.m_num); } bool is_nonpos(mpbq const & a) const { return m_manager.is_nonpos(a.m_num); } bool is_nonneg(mpbq const & a) const { return m_manager.is_nonneg(a.m_num); } void add(mpbq const & a, mpbq const & b, mpbq & r); void add(mpbq const & a, mpz const & b, mpbq & r); void sub(mpbq const & a, mpbq const & b, mpbq & r); void sub(mpbq const & a, mpz const & b, mpbq & r); void mul(mpbq const & a, mpbq const & b, mpbq & r); void mul(mpbq const & a, mpz const & b, mpbq & r); // r <- a + b*c void addmul(mpbq const & a, mpbq const & b, mpbq const & c, mpbq & r) { mul(b, c, m_addmul_tmp); add(a, m_addmul_tmp, r); } void addmul(mpbq const & a, mpz const & b, mpbq const & c, mpbq & r) { mul(c, b, m_addmul_tmp); add(a, m_addmul_tmp, r); } void neg(mpbq & a) { m_manager.neg(a.m_num); } // when dividing by 2, we only need to normalize if m_k was zero. void div2(mpbq & a) { bool old_k_zero = (a.m_k == 0); a.m_k++; if (old_k_zero) normalize(a); } void div2k(mpbq & a, unsigned k) { bool old_k_zero = (a.m_k == 0); a.m_k += k; if (old_k_zero) normalize(a); } void mul2(mpbq & a); void mul2k(mpbq & a, unsigned k); void power(mpbq & a, unsigned k); void power(mpbq const & a, unsigned k, mpbq & b) { set(b, a); power(b, k); } /** \brief Return true if a^{1/n} is a binary rational, and store the result in a. Otherwise, return false and return an lower bound based on the integer root of the numerator and denominator/n */ bool root_lower(mpbq & a, unsigned n); bool root_lower(mpbq const & a, unsigned n, mpbq & r) { set(r, a); return root_lower(r, n); } /** \brief Return true if a^{1/n} is a binary rational, and store the result in a. Otherwise, return false and return an upper bound based on the integer root of the numerator and denominator/n */ bool root_upper(mpbq & a, unsigned n); bool root_upper(mpbq const & a, unsigned n, mpbq & r) { set(r, a); return root_upper(r, n); } bool eq(mpbq const & a, mpbq const & b) { return a.m_k == b.m_k && m_manager.eq(a.m_num, b.m_num); } bool lt(mpbq const & a, mpbq const & b); bool neq(mpbq const & a, mpbq const & b) { return !eq(a, b); } bool gt(mpbq const & a, mpbq const & b) { return lt(b, a); } bool ge(mpbq const & a, mpbq const & b) { return le(b, a); } bool le(mpbq const & a, mpbq const & b) { return !gt(a, b); } bool eq(mpbq const & a, mpq const & b); bool lt(mpbq const & a, mpq const & b); bool le(mpbq const & a, mpq const & b); bool neq(mpbq const & a, mpq const & b) { return !eq(a, b); } bool gt(mpbq const & a, mpq const & b) { return !le(a, b); } bool ge(mpbq const & a, mpq const & b) { return !lt(a, b); } bool eq(mpbq const & a, mpz const & b) { return m_manager.eq(a.m_num, b) && a.m_k == 0; } bool lt(mpbq const & a, mpz const & b); bool le(mpbq const & a, mpz const & b); bool neq(mpbq const & a, mpz const & b) { return !eq(a, b); } bool gt(mpbq const & a, mpz const & b) { return !le(a, b); } bool ge(mpbq const & a, mpz const & b) { return !lt(a, b); } /** \brief Return the magnitude of a = b/2^k. It is defined as: a == 0 -> 0 a > 0 -> log2(b) - k Note that 2^{log2(b) - k} <= a <= 2^{log2(b) - k + 1} a < 0 -> mlog2(b) - k + 1 Note that -2^{mlog2(b) - k + 1} <= a <= -2^{mlog2(b) - k} Remark: mlog2(b) = log2(-b) Examples: 5/2^3 log2(5) - 3 = -1 21/2^2 log2(21) - 2 = 2 -3/2^4 log2(3) - 4 + 1 = -2 */ int magnitude_lb(mpbq const & a); /** \brief Similar to magnitude_lb a == 0 -> 0 a > 0 -> log2(b) - k + 1 a <= 2^{log2(b) - k + 1} a < 0 -> mlog2(b) - k a <= -2^{mlog2(b) - k} */ int magnitude_ub(mpbq const & a); /** \brief Return true if a < 1/2^k */ bool lt_1div2k(mpbq const & a, unsigned k); std::string to_string(mpbq const & a); /** \brief Return true if q (= c/d) is a binary rational, and store it in bq (as a binary rational). Otherwise return false, and set bq to c/2^{k+1} where k = log2(d) */ bool to_mpbq(mpq const & q, mpbq & bq); /** \brief Given a rational q which cannot be represented as a binary rational, and an interval (l, u) s.t. l < q < u. This method stores in u, a u' s.t. q < u' < u. In the refinement process, the lower bound l may be also refined to l' s.t. l < l' < q */ void refine_upper(mpq const & q, mpbq & l, mpbq & u); /** \brief Similar to refine_upper. */ void refine_lower(mpq const & q, mpbq & l, mpbq & u); template void floor(mpz_manager & m, mpbq const & a, mpz & f) { if (is_int(a)) { m.set(f, a.m_num); return; } bool is_neg_num = is_neg(a); m.machine_div2k(a.m_num, a.m_k, f); if (is_neg_num) m.sub(f, mpz(1), f); } template void ceil(mpz_manager & m, mpbq const & a, mpz & c) { if (is_int(a)) { m.set(c, a.m_num); return; } bool is_pos_num = is_pos(a); m.machine_div2k(a.m_num, a.m_k, c); if (is_pos_num) m.add(c, mpz(1), c); } /** \brief Select some number in the interval [lower, upper]. Return true if succeeded, and false if lower > upper. This method tries to minimize the size (in bits) of r. For example, it will select an integer in [lower, upper] if the interval contains one. */ bool select_small(mpbq const & lower, mpbq const & upper, mpbq & r); /** \brief Similar to select_small, but assumes lower <= upper */ void select_small_core(mpbq const & lower, mpbq const & upper, mpbq & r); // Select some number in the interval (lower, upper] void select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpbq & r); // Select some number in the interval [lower, upper) void select_small_core(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpbq & r); // Select some number in the interval (lower, upper) void select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpbq & r); void display(std::ostream & out, mpbq const & a); void display_pp(std::ostream & out, mpbq const & a); void display_decimal(std::ostream & out, mpbq const & a, unsigned prec = 8); /** \brief Display a in decimal while its digits match b digits. This function is useful when a and b are representing an interval [a,b] which contains an algebraic number */ void display_decimal(std::ostream & out, mpbq const & a, mpbq const & b, unsigned prec); void display_smt2(std::ostream & out, mpbq const & a, bool decimal); /** \brief Approximate n as b/2^k' s.t. k' <= k. if get_denominator_power(n) <= k, then n is not modified. if get_denominator_power(n) > k, then if to_plus_inf, old(n) < b/2^k' otherwise, b/2^k' < old(n) */ void approx(mpbq & n, unsigned k, bool to_plus_inf); /** \brief Approximated division c <- a/b The result is precise when: 1) b is a power of two 2) get_numerator(b) divides get_numerator(a) When the result is not precise, |c - a/b| <= 1/2^k Actually, we have that to_plus_inf => c - a/b <= 1/2^k not to_plus_inf => a/b - c <= 1/2^k */ void approx_div(mpbq const & a, mpbq const & b, mpbq & c, unsigned k=32, bool to_plus_inf=false); }; /** \brief Convert a binary rational into a rational */ template void to_mpq(mpq_manager & m, mpbq const & source, mpq & target) { mpq two(2); m.power(two, source.k(), target); m.inv(target); m.mul(source.numerator(), target, target); } /** \brief Convert a binary rational into a rational. */ rational to_rational(mpbq const & m); typedef _scoped_numeral scoped_mpbq; typedef _scoped_numeral_vector scoped_mpbq_vector; #define MPBQ_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \ inline bool EXTERNAL(scoped_mpbq const & a, TYPE const & b) { \ mpbq_manager & m = a.m(); \ scoped_mpbq _b(m); \ m.set(_b, b); \ return m.INTERNAL(a, _b); \ } #define MPBQ_MK_COMPARISON(EXTERNAL, INTERNAL) \ MPBQ_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \ MPBQ_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \ MPBQ_MK_COMPARISON(operator==, eq); MPBQ_MK_COMPARISON(operator!=, neq); MPBQ_MK_COMPARISON(operator<, lt); MPBQ_MK_COMPARISON(operator<=, le); MPBQ_MK_COMPARISON(operator>, gt); MPBQ_MK_COMPARISON(operator>=, ge); #undef MPBQ_MK_COMPARISON #undef MPBQ_MK_COMPARISON_CORE #define MPBQ_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \ inline scoped_mpbq EXTERNAL(scoped_mpbq const & a, TYPE const & b) { \ mpbq_manager & m = a.m(); \ scoped_mpbq _b(m); \ m.set(_b, b); \ scoped_mpbq r(m); \ m.INTERNAL(a, _b, r); \ return r; \ } #define MPBQ_MK_BINARY(EXTERNAL, INTERNAL) \ MPBQ_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \ MPBQ_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \ MPBQ_MK_BINARY(operator+, add) MPBQ_MK_BINARY(operator-, sub) MPBQ_MK_BINARY(operator*, mul) #undef MPBQ_MK_BINARY #undef MPBQ_MK_BINARY_CORE #endif z3-z3-4.4.1/src/util/mpbqi.h000066400000000000000000000022371260446376700154560ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpbqi.h Abstract: Binary Rational Number Intervals Author: Leonardo de Moura (leonardo) 2012-01-04 Revision History: --*/ #ifndef MPBQI_H_ #define MPBQI_H_ #include"mpbq.h" #include"basic_interval.h" class mpbqi_manager : public basic_interval_manager { typedef basic_interval_manager super; public: mpbqi_manager(mpbq_manager & m):super(m) {} void set(interval & a, interval const & b) { super::set(a, b); } void set(interval & a, bound const & lower, bound const & upper) { super::set(a, lower, upper); } void set(interval & a, bound const & n) { super::set(a, n); } void set(interval & a, mpz const & n) { m().set(a.lower(), n); m().set(a.upper(), n); } void add(interval const & a, interval const & b, interval & c) { super::add(a, b, c); } void add(interval const & a, mpz const & b, interval & c) { m().add(a.lower(), b, c.lower()); m().add(a.upper(), b, c.upper()); } }; typedef mpbqi_manager::interval mpbqi; typedef mpbqi_manager::scoped_interval scoped_mpbqi; #endif z3-z3-4.4.1/src/util/mpf.cpp000066400000000000000000001710111260446376700154600ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpf.cpp Abstract: Multi Precision Floating Point Numbers Author: Christoph Wintersteiger (cwinter) 2011-12-01. Revision History: --*/ #include #include"mpf.h" mpf::mpf() : ebits(0), sbits(0), sign(false), significand(0), exponent(0) { } void mpf::set(unsigned _ebits, unsigned _sbits) { ebits = _ebits; sbits = _sbits; sign = false; exponent = 0; } mpf::mpf(unsigned _ebits, unsigned _sbits): significand(0) { set(ebits, sbits); } mpf::mpf(mpf const & other) { // It is safe if the mpz numbers are small. // I need it for resize method in vector. // UNREACHABLE(); } mpf::~mpf() { } void mpf::swap(mpf & other) { unsigned tmp = ebits; ebits = other.ebits; other.ebits = tmp; tmp = sbits; sbits = other.sbits; other.sbits = tmp; tmp = sign; sign = other.sign; other.sign = tmp; significand.swap(other.significand); std::swap(exponent, other.exponent); } mpf_manager::mpf_manager() : m_mpz_manager(m_mpq_manager), m_powers2(m_mpz_manager) { } mpf_manager::~mpf_manager() { } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, int value) { COMPILE_TIME_ASSERT(sizeof(int) == 4); o.sign = false; o.ebits = ebits; o.sbits = sbits; TRACE("mpf_dbg", tout << "set: value = " << value << std::endl;); if (value == 0) { mk_pzero(ebits, sbits, o); } else { unsigned uval=value; if (value < 0) { o.sign = true; if (value == INT_MIN) uval = 0x80000000; else uval = -value; } o.exponent = 31; while ((uval & 0x80000000) == 0) { uval <<= 1; o.exponent--; } m_mpz_manager.set(o.significand, uval & 0x7FFFFFFF); // remove the "1." part // align with sbits. if (sbits > 31) m_mpz_manager.mul2k(o.significand, sbits-32); else m_mpz_manager.machine_div2k(o.significand, 32-sbits); } TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, int n, int d) { scoped_mpq tmp(m_mpq_manager); m_mpq_manager.set(tmp, n, d); set(o, ebits, sbits, rm, tmp); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, double value) { // double === mpf(11, 53) COMPILE_TIME_ASSERT(sizeof(double) == 8); uint64 raw; memcpy(&raw, &value, sizeof(double)); bool sign = (raw >> 63) != 0; int64 e = ((raw & 0x7FF0000000000000ull) >> 52) - 1023; uint64 s = raw & 0x000FFFFFFFFFFFFFull; TRACE("mpf_dbg", tout << "set: " << value << " is: raw=" << raw << " (double)" << " sign=" << sign << " s=" << s << " e=" << e << std::endl;); SASSERT(-1023 <= e && e <= +1024); o.ebits = ebits; o.sbits = sbits; o.sign = sign; if (e <= -((0x01ll<<(ebits-1))-1)) o.exponent = mk_bot_exp(ebits); else if (e >= (0x01ll<<(ebits-1))) o.exponent = mk_top_exp(ebits); else o.exponent = e; m_mpz_manager.set(o.significand, s); if (sbits < 53) m_mpz_manager.machine_div2k(o.significand, 53-sbits); else if (sbits > 53) m_mpz_manager.mul2k(o.significand, sbits-53); TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, float value) { // single === mpf(8, 24) COMPILE_TIME_ASSERT(sizeof(float) == 4); unsigned int raw; memcpy(&raw, &value, sizeof(float)); bool sign = (raw >> 31) != 0; signed int e = ((raw & 0x7F800000) >> 23) - 127; unsigned int s = raw & 0x007FFFFF; TRACE("mpf_dbg", tout << "set: " << value << " is: raw=" << raw << " (float)" << " sign=" << sign << " s=" << s << " e=" << e << std::endl;); SASSERT(-127 <= e && e <= +128); o.ebits = ebits; o.sbits = sbits; o.sign = sign; if (e <= -((0x01ll<<(ebits-1))-1)) o.exponent = mk_bot_exp(ebits); else if (e >= (0x01ll<<(ebits-1))) o.exponent = mk_top_exp(ebits); else o.exponent = e; m_mpz_manager.set(o.significand, s); if (sbits < 24) m_mpz_manager.machine_div2k(o.significand, 24-sbits); else if (sbits > 24) m_mpz_manager.mul2k(o.significand, sbits-24); TRACE("mpf_dbg", tout << "set: res = " << to_string_raw(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & value) { TRACE("mpf_dbg", tout << "set: " << m_mpq_manager.to_string(value) << " [" << ebits << "/" << sbits << "]"<< std::endl;); scoped_mpz exp(m_mpz_manager); m_mpz_manager.set(exp, 0); set(o, ebits, sbits, rm, value, exp); TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, char const * value) { TRACE("mpf_dbg", tout << "set: " << value << " [" << ebits << "/" << sbits << "]"<< std::endl;); o.ebits = ebits; o.sbits = sbits; // We expect [i].[f]P[e], where P means that the exponent is interpreted as 2^e instead of 10^e. std::string v(value); size_t e_pos = v.find('p'); if (e_pos == std::string::npos) e_pos = v.find('P'); std::string f, e; f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v; e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;); scoped_mpq q(m_mpq_manager); m_mpq_manager.set(q, f.c_str()); scoped_mpz ex(m_mpq_manager); m_mpz_manager.set(ex, e.c_str()); set(o, ebits, sbits, rm, q, ex); TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent) { // Assumption: this represents significand * 2^exponent. TRACE("mpf_dbg", tout << "set: sig = " << m_mpq_manager.to_string(significand) << " exp = " << m_mpz_manager.to_string(exponent) << std::endl;); o.ebits = ebits; o.sbits = sbits; o.sign = m_mpq_manager.is_neg(significand); if (m_mpq_manager.is_zero(significand)) mk_zero(ebits, sbits, o.sign, o); else { scoped_mpq sig(m_mpq_manager); scoped_mpz exp(m_mpq_manager); m_mpq_manager.set(sig, significand); m_mpq_manager.abs(sig); m_mpz_manager.set(exp, exponent); // Normalize while (m_mpq_manager.ge(sig, 2)) { m_mpq_manager.div(sig, mpq(2), sig); m_mpz_manager.inc(exp); } while (m_mpq_manager.lt(sig, 1)) { m_mpq_manager.mul(sig, 2, sig); m_mpz_manager.dec(exp); } // 1.0 <= sig < 2.0 SASSERT((m_mpq_manager.le(1, sig) && m_mpq_manager.lt(sig, 2))); TRACE("mpf_dbg", tout << "sig = " << m_mpq_manager.to_string(sig) << " exp = " << m_mpz_manager.to_string(exp) << std::endl;); m_mpz_manager.set(o.significand, 0); for (unsigned i = 0; i < (sbits+3); i++) { m_mpz_manager.mul2k(o.significand, 1); if (m_mpq_manager.ge(sig, 1)) { m_mpz_manager.inc(o.significand); m_mpq_manager.dec(sig); } m_mpq_manager.mul(sig, mpq(2), sig); } // sticky if (!m_mpq_manager.is_zero(sig) && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); TRACE("mpf_dbg", tout << "sig = " << m_mpz_manager.to_string(o.significand) << " exp = " << o.exponent << std::endl;); if (m_mpz_manager.is_small(exp)) { o.exponent = m_mpz_manager.get_int64(exp); round(rm, o); } else mk_inf(ebits, sbits, o.sign, o); } TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, bool sign, uint64 significand, mpf_exp_t exponent) { // Assumption: this represents (sign * -1) * (significand/2^sbits) * 2^exponent. o.ebits = ebits; o.sbits = sbits; o.sign = sign; m_mpz_manager.set(o.significand, significand); o.exponent = exponent; DEBUG_CODE({ SASSERT(m_mpz_manager.lt(o.significand, m_powers2(sbits-1))); SASSERT(o.exponent <= mk_top_exp(ebits)); SASSERT(o.exponent >= mk_bot_exp(ebits)); }); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpz const & significand, mpf_exp_t exponent) { // Assumption: this represents (sign * -1) * (significand/2^sbits) * 2^exponent. o.ebits = ebits; o.sbits = sbits; o.sign = sign; m_mpz_manager.set(o.significand, significand); o.exponent = exponent; } void mpf_manager::set(mpf & o, mpf const & x) { o.ebits = x.ebits; o.sbits = x.sbits; o.sign = x.sign; o.exponent = x.exponent; m_mpz_manager.set(o.significand, x.significand); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpf const & x) { if (is_nan(x)) mk_nan(ebits, sbits, o); else if (is_inf(x)) mk_inf(ebits, sbits, x.sign, o); else if (is_zero(x)) mk_zero(ebits, sbits, x.sign, o); else if (x.ebits == ebits && x.sbits == sbits) set(o, x); else { set(o, x); unpack(o, true); o.ebits = ebits; o.sbits = sbits; signed ds = sbits - x.sbits + 3; // plus rounding bits if (ds > 0) { m_mpz_manager.mul2k(o.significand, ds); round(rm, o); } else if (ds < 0) { bool sticky = false; while (ds < 0) { sticky |= m_mpz_manager.is_odd(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); ds++; } if (sticky && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); round(rm, o); } } } void mpf_manager::abs(mpf & o) { o.sign = false; } void mpf_manager::abs(mpf const & x, mpf & o) { set(o, x); abs(o); } void mpf_manager::neg(mpf & o) { if (!is_nan(o)) o.sign = !o.sign; } void mpf_manager::neg(mpf const & x, mpf & o) { set(o, x); neg(o); } bool mpf_manager::is_zero(mpf const & x) { return has_bot_exp(x) && m_mpz_manager.is_zero(sig(x)); } bool mpf_manager::is_one(mpf const & x) { return m_mpz_manager.is_zero(sig(x)) && exp(x) == 0; } bool mpf_manager::is_neg(mpf const & x) { return x.sign && !is_nan(x); } bool mpf_manager::is_pos(mpf const & x) { return !x.sign && !is_nan(x); } bool mpf_manager::is_nzero(mpf const & x) { return x.sign && is_zero(x); } bool mpf_manager::is_pzero(mpf const & x) { return !x.sign && is_zero(x); } bool mpf_manager::eq(mpf const & x, mpf const & y) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); if (is_nan(x) || is_nan(y)) return false; else if (is_zero(x) && is_zero(y)) return true; else if (sgn(x) != sgn(y)) return false; else return exp(x)==exp(y) && m_mpz_manager.eq(sig(x), sig(y)); } bool mpf_manager::lt(mpf const & x, mpf const & y) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); if (is_nan(x) || is_nan(y)) return false; else if (is_zero(x) && is_zero(y)) return false; else if (sgn(x)) { if (!sgn(y)) return true; else return exp(y) < exp(x) || (exp(y) == exp(x) && m_mpz_manager.lt(sig(y), sig(x))); } else { // !sgn(x) if (sgn(y)) return false; else return exp(x) < exp(y) || (exp(x)==exp(y) && m_mpz_manager.lt(sig(x), sig(y))); } } bool mpf_manager::lte(mpf const & x, mpf const & y) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); return lt(x, y) || eq(x, y); } bool mpf_manager::gt(mpf const & x, mpf const & y) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); if (is_nan(x) || is_nan(y)) return false; else if (is_zero(x) && is_zero(y)) return false; else return !lte(x, y); } bool mpf_manager::gte(mpf const & x, mpf const & y) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); return gt(x, y) || eq(x, y); } void mpf_manager::add(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o) { add_sub(rm, x, y, o, false); } void mpf_manager::sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o) { add_sub(rm, x, y, o, true); } void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o, bool sub) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); bool sgn_y = sgn(y) ^ sub; if (is_nan(x)) mk_nan(x.ebits, x.sbits, o); else if (is_nan(y)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(x)) { if (is_inf(y) && (sgn(x) ^ sgn_y)) mk_nan(x.ebits, x.sbits, o); else set(o, x); } else if (is_inf(y)) { if (is_inf(x) && (sgn(x) ^ sgn_y)) mk_nan(x.ebits, x.sbits, o); else { set(o, y); o.sign = sgn_y; } } else if (is_zero(x) && is_zero(y)) { if ((x.sign && sgn_y) || ((rm == MPF_ROUND_TOWARD_NEGATIVE) && (x.sign != sgn_y))) mk_nzero(x.ebits, x.sbits, o); else mk_pzero(x.ebits, x.sbits, o); } else if (is_zero(x)) { set(o, y); o.sign = sgn_y; } else if (is_zero(y)) set(o, x); else { o.ebits = x.ebits; o.sbits = x.sbits; SASSERT(is_normal(x) || is_denormal(x)); SASSERT(is_normal(y) || is_denormal(y)); scoped_mpf a(*this), b(*this); set(a, x); set(b, y); b.get().sign = sgn_y; // Unpack a/b, this inserts the hidden bit and adjusts the exponent. unpack(a, false); unpack(b, false); if (exp(b) > exp(a)) a.swap(b); mpf_exp_t exp_delta = exp(a) - exp(b); SASSERT(exp(a) >= exp(b)); SASSERT(exp_delta >= 0); if (exp_delta > x.sbits+2) exp_delta = x.sbits+2; TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); TRACE("mpf_dbg", tout << "d = " << exp_delta << std::endl;); // Introduce 3 extra bits into both numbers. m_mpz_manager.mul2k(a.significand(), 3, a.significand()); m_mpz_manager.mul2k(b.significand(), 3, b.significand()); // Alignment shift with sticky bit computation. SASSERT(exp_delta <= INT_MAX); scoped_mpz sticky_rem(m_mpz_manager); m_mpz_manager.machine_div_rem(b.significand(), m_powers2((int)exp_delta), b.significand(), sticky_rem); if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(b.significand())) m_mpz_manager.inc(b.significand()); TRACE("mpf_dbg", tout << "A' = " << m_mpz_manager.to_string(a.significand()) << std::endl;); TRACE("mpf_dbg", tout << "B' = " << m_mpz_manager.to_string(b.significand()) << std::endl;); // Significand addition if (sgn(a) != sgn(b)) { TRACE("mpf_dbg", tout << "SUBTRACTING" << std::endl;); m_mpz_manager.sub(a.significand(), b.significand(), o.significand); } else { TRACE("mpf_dbg", tout << "ADDING" << std::endl;); m_mpz_manager.add(a.significand(), b.significand(), o.significand); } TRACE("mpf_dbg", tout << "sum[-2:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); if (m_mpz_manager.is_zero(o.significand)) mk_zero(o.ebits, o.sbits, rm == MPF_ROUND_TOWARD_NEGATIVE, o); else { bool neg = m_mpz_manager.is_neg(o.significand); TRACE("mpf_dbg", tout << "NEG=" << neg << std::endl;); m_mpz_manager.abs(o.significand); TRACE("mpf_dbg", tout << "fs[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); o.sign = ((!a.sign() && b.sign() && neg) || ( a.sign() && !b.sign() && !neg) || ( a.sign() && b.sign())); o.exponent = a.exponent(); round(rm, o); } } } void mpf_manager::mul(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o) { TRACE("mpf_mul_bug", tout << "rm: " << rm << "\n"; tout << "X: " << to_string(x) << "\n"; tout << "Y: " << to_string(y) << "\n";); SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); TRACE("mpf_dbg", tout << "Y = " << to_string(y) << std::endl;); if (is_nan(x)) mk_nan(x.ebits, x.sbits, o); else if (is_nan(y)) mk_nan(x.ebits, x.sbits, o); else if (is_pinf(x)) { if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, y.sign, o); } else if (is_pinf(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, x.sign, o); } else if (is_ninf(x)) { if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, !y.sign, o); } else if (is_ninf(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, !x.sign, o); } else if (is_zero(x) || is_zero(y)) { mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else { o.ebits = x.ebits; o.sbits = x.sbits; o.sign = x.sign ^ y.sign; scoped_mpf a(*this, x.ebits, x.sbits), b(*this, x.ebits, x.sbits); set(a, x); set(b, y); unpack(a, true); unpack(b, true); TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); o.exponent = a.exponent() + b.exponent(); TRACE("mpf_dbg", tout << "A' = " << m_mpz_manager.to_string(a.significand()) << std::endl;); TRACE("mpf_dbg", tout << "B' = " << m_mpz_manager.to_string(b.significand()) << std::endl;); m_mpz_manager.mul(a.significand(), b.significand(), o.significand); TRACE("mpf_dbg", tout << "PRODUCT = " << to_string(o) << std::endl;); // Remove the extra bits, keeping a sticky bit. scoped_mpz sticky_rem(m_mpz_manager); if (o.sbits >= 4) m_mpz_manager.machine_div_rem(o.significand, m_powers2(o.sbits - 4), o.significand, sticky_rem); else m_mpz_manager.mul2k(o.significand, 4-o.sbits, o.significand); if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); round(rm, o); } TRACE("mpf_mul_bug", tout << "result: " << to_string(o) << "\n";); } void mpf_manager::div(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); TRACE("mpf_dbg", tout << "Y = " << to_string(y) << std::endl;); if (is_nan(x)) mk_nan(x.ebits, x.sbits, o); else if (is_nan(y)) mk_nan(x.ebits, x.sbits, o); else if (is_pinf(x)) { if (is_inf(y)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, y.sign, o); } else if (is_pinf(y)) { if (is_inf(x)) mk_nan(x.ebits, x.sbits, o); else mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else if (is_ninf(x)) { if (is_inf(y)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, !y.sign, o); } else if (is_ninf(y)) { if (is_inf(x)) mk_nan(x.ebits, x.sbits, o); else mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else if (is_zero(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, x.sign != y.sign, o); } else if (is_zero(x)) { // Special case to avoid problems with unpacking of zeros. mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else { o.ebits = x.ebits; o.sbits = x.sbits; o.sign = x.sign ^ y.sign; scoped_mpf a(*this), b(*this); set(a, x); set(b, y); unpack(a, true); unpack(b, true); TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); o.exponent = a.exponent() - b.exponent(); TRACE("mpf_dbg", tout << "A' = " << m_mpz_manager.to_string(a.significand()) << std::endl;); TRACE("mpf_dbg", tout << "B' = " << m_mpz_manager.to_string(b.significand()) << std::endl;); unsigned extra_bits = x.sbits + 2; m_mpz_manager.mul2k(a.significand(), x.sbits + extra_bits); m_mpz_manager.machine_div(a.significand(), b.significand(), o.significand); TRACE("mpf_dbg", tout << "QUOTIENT = " << to_string(o) << std::endl;); // Remove the extra bits, keeping a sticky bit. scoped_mpz sticky_rem(m_mpz_manager); m_mpz_manager.machine_div_rem(o.significand, m_powers2(extra_bits-2), o.significand, sticky_rem); if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); TRACE("mpf_dbg", tout << "QUOTIENT' = " << to_string(o) << std::endl;); round(rm, o); } } void mpf_manager::fma(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf const &z, mpf & o) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits && x.sbits == y.sbits && z.ebits == z.ebits); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); TRACE("mpf_dbg", tout << "Y = " << to_string(y) << std::endl;); TRACE("mpf_dbg", tout << "Z = " << to_string(z) << std::endl;); if (is_nan(x) || is_nan(y) || is_nan(z)) mk_nan(x.ebits, x.sbits, o); else if (is_pinf(x)) { if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, y.sign, o); } else if (is_pinf(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, x.sign, o); } else if (is_ninf(x)) { if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, !y.sign, o); } else if (is_ninf(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, !x.sign, o); } else if (is_inf(z)) { set(o, z); } else if (is_zero(x) || is_zero(y)) { if (is_zero(z) && rm != MPF_ROUND_TOWARD_NEGATIVE) mk_pzero(x.ebits, x.sbits, o); else set(o, z); } else { o.ebits = x.ebits; o.sbits = x.sbits; scoped_mpf mul_res(*this, x.ebits+2, 2*x.sbits); scoped_mpf a(*this, x.ebits, x.sbits), b(*this, x.ebits, x.sbits), c(*this, x.ebits, x.sbits); set(a, x); set(b, y); set(c, z); unpack(a, true); unpack(b, true); unpack(c, true); TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); TRACE("mpf_dbg", tout << "C = " << to_string(c) << std::endl;); SASSERT(m_mpz_manager.lt(a.significand(), m_powers2(x.sbits))); SASSERT(m_mpz_manager.lt(b.significand(), m_powers2(x.sbits))); SASSERT(m_mpz_manager.lt(c.significand(), m_powers2(x.sbits))); SASSERT(m_mpz_manager.ge(a.significand(), m_powers2(x.sbits-1))); SASSERT(m_mpz_manager.ge(b.significand(), m_powers2(x.sbits-1))); mul_res.get().sign = (a.sign() != b.sign()); mul_res.get().exponent = a.exponent() + b.exponent(); m_mpz_manager.mul(a.significand(), b.significand(), mul_res.get().significand); TRACE("mpf_dbg", tout << "PRODUCT = " << to_string(mul_res) << std::endl;); // mul_res is [-1][0].[2*sbits - 2], i.e., between 2*sbits-1 and 2*sbits. SASSERT(m_mpz_manager.lt(mul_res.significand(), m_powers2(2*x.sbits))); SASSERT(m_mpz_manager.ge(mul_res.significand(), m_powers2(2*x.sbits - 2))); // Introduce extra bits into c. m_mpz_manager.mul2k(c.significand(), x.sbits-1, c.significand()); SASSERT(m_mpz_manager.lt(c.significand(), m_powers2(2 * x.sbits - 1))); SASSERT(m_mpz_manager.is_zero(c.significand()) || m_mpz_manager.ge(c.significand(), m_powers2(2 * x.sbits - 2))); TRACE("mpf_dbg", tout << "C = " << to_string(c) << std::endl;); if (exp(c) > exp(mul_res)) mul_res.swap(c); mpf_exp_t exp_delta = exp(mul_res) - exp(c); SASSERT(exp(mul_res) >= exp(c) && exp_delta >= 0); if (exp_delta > 2 * x.sbits) exp_delta = 2 * x.sbits; TRACE("mpf_dbg", tout << "exp_delta = " << exp_delta << std::endl;); // Alignment shift with sticky bit computation. scoped_mpz sticky_rem(m_mpz_manager); m_mpz_manager.machine_div_rem(c.significand(), m_powers2((int)exp_delta), c.significand(), sticky_rem); TRACE("mpf_dbg", tout << "alignment shift -> sig = " << m_mpz_manager.to_string(c.significand()) << " sticky_rem = " << m_mpz_manager.to_string(sticky_rem) << std::endl;); if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(c.significand())) m_mpz_manager.inc(c.significand()); TRACE("mpf_dbg", tout << "M' = " << m_mpz_manager.to_string(mul_res.significand()) << std::endl;); TRACE("mpf_dbg", tout << "C' = " << m_mpz_manager.to_string(c.significand()) << std::endl;); // Significand addition if (sgn(mul_res) != sgn(c)) { TRACE("mpf_dbg", tout << "SUBTRACTING" << std::endl;); m_mpz_manager.sub(mul_res.significand(), c.significand(), o.significand); } else { TRACE("mpf_dbg", tout << "ADDING" << std::endl;); m_mpz_manager.add(mul_res.significand(), c.significand(), o.significand); } TRACE("mpf_dbg", tout << "sum[-1:] = " << m_mpz_manager.to_string(o.significand) << std::endl;); bool neg = m_mpz_manager.is_neg(o.significand); TRACE("mpf_dbg", tout << "NEG=" << neg << std::endl;); if (neg) m_mpz_manager.abs(o.significand); o.exponent = mul_res.exponent(); unsigned extra = 0; // Result could overflow into 4.xxx ... SASSERT(m_mpz_manager.lt(o.significand, m_powers2(2 * x.sbits + 2))); if(m_mpz_manager.ge(o.significand, m_powers2(2 * x.sbits + 1))) { extra++; o.exponent++; TRACE("mpf_dbg", tout << "Addition overflew!" << std::endl;); } // Remove the extra bits, keeping a sticky bit. m_mpz_manager.set(sticky_rem, 0); unsigned minbits = (4 + extra); if (o.sbits >= minbits) m_mpz_manager.machine_div_rem(o.significand, m_powers2(o.sbits - minbits), o.significand, sticky_rem); else m_mpz_manager.mul2k(o.significand, minbits - o.sbits, o.significand); if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); TRACE("mpf_dbg", tout << "sum[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); if (m_mpz_manager.is_zero(o.significand)) mk_zero(x.ebits, x.sbits, rm == MPF_ROUND_TOWARD_NEGATIVE, o); else { o.sign = ((!mul_res.sign() && c.sign() && neg) || ( mul_res.sign() && !c.sign() && !neg) || ( mul_res.sign() && c.sign())); TRACE("mpf_dbg", tout << "before round = " << to_string(o) << std::endl << "fs[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); round(rm, o); } } } void my_mpz_sqrt(unsynch_mpz_manager & m, unsigned sbits, bool odd_exp, mpz & in, mpz & o) { scoped_mpz lower(m), upper(m); scoped_mpz mid(m), product(m), diff(m); // we have lower <= a.significand <= upper and we need 1.[52+3 bits] in the bounds. // since we comapre upper*upper to a.significand further down, we need a.significand // to be of twice the size. m.set(lower, 1); m.mul2k(lower, sbits+2-1); if (odd_exp) m.mul2k(in, 4, upper); else m.mul2k(in, 3, upper); if (m.eq(lower, upper)) m.set(o, lower); while (m.neq(lower, upper)) { STRACE("mpf_dbg", tout << "SIG = " << m.to_string(in) << " LOWER = " << m.to_string(lower) << " UPPER = " << m.to_string(upper) << std::endl;); m.sub(upper, lower, diff); if (m.is_one(diff)) { m.mul(lower, lower, product); if (m.eq(product, in)) { STRACE("mpf_dbg", tout << "choosing lower" << std::endl;); m.set(o, lower); } else { STRACE("mpf_dbg", tout << "choosing upper" << std::endl;); m.set(o, upper); // chosing upper is like a sticky bit here. } break; } m.add(lower, upper, mid); m.machine_div2k(mid, 1); m.mul(mid, mid, product); STRACE("mpf_dbg", tout << "MID = " << m.to_string(mid) << " PROD = " << m.to_string(product) << std::endl;); if (m.lt(product, in)) m.set(lower, mid); else if (m.gt(product, in)) m.set(upper, mid); else { SASSERT(m.eq(product, in)); m.set(o, mid); break; } } } void mpf_manager::sqrt(mpf_rounding_mode rm, mpf const & x, mpf & o) { SASSERT(x.ebits > 0 && x.sbits > 0); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); if (is_nan(x)) mk_nan(x.ebits, x.sbits, o); else if (is_pinf(x)) set(o, x); else if (is_zero(x)) set(o, x); else if (x.sign) mk_nan(x.ebits, x.sbits, o); else { o.ebits = x.ebits; o.sbits = x.sbits; o.sign = false; scoped_mpf a(*this); set(a, x); unpack(a, true); TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); m_mpz_manager.mul2k(a.significand(), x.sbits + ((a.exponent() % 2)?6:7)); if (!m_mpz_manager.root(a.significand(), 2, o.significand)) { // If the result is inexact, it is 1 too large. // We need a sticky bit in the last position here, so we fix that. if (m_mpz_manager.is_even(o.significand)) m_mpz_manager.dec(o.significand); TRACE("mpf_dbg", tout << "dec'ed " << m_mpz_manager.to_string(o.significand) << std::endl;); } o.exponent = a.exponent() >> 1; if (a.exponent() % 2 == 0) o.exponent--; round(rm, o); } TRACE("mpf_dbg", tout << "SQRT = " << to_string(o) << std::endl;); } void mpf_manager::round_to_integral(mpf_rounding_mode rm, mpf const & x, mpf & o) { SASSERT(x.ebits > 0 && x.sbits > 0); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); if (is_nan(x)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(x)) set(o, x); else if (is_zero(x)) mk_zero(x.ebits, x.sbits, x.sign, o); // -0.0 -> -0.0, says IEEE754, Sec 5.9. else if (x.exponent < 0) { if (rm == MPF_ROUND_TOWARD_ZERO) mk_zero(x.ebits, x.sbits, x.sign, o); else if (rm == MPF_ROUND_TOWARD_NEGATIVE) { if (x.sign) mk_one(x.ebits, x.sbits, true, o); else mk_zero(x.ebits, x.sbits, false, o); } else if (rm == MPF_ROUND_TOWARD_POSITIVE) { if (x.sign) mk_zero(x.ebits, x.sbits, true, o); else mk_one(x.ebits, x.sbits, false, o); } else { SASSERT(rm == MPF_ROUND_NEAREST_TEVEN || rm == MPF_ROUND_NEAREST_TAWAY); bool tie = m_mpz_manager.is_zero(x.significand) && x.exponent == -1; TRACE("mpf_dbg", tout << "tie = " << tie << std::endl;); if (tie && rm == MPF_ROUND_NEAREST_TEVEN) mk_zero(x.ebits, x.sbits, x.sign, o); else if (tie && rm == MPF_ROUND_NEAREST_TAWAY) mk_one(x.ebits, x.sbits, x.sign, o); else if (x.exponent < -1) mk_zero(x.ebits, x.sbits, x.sign, o); else mk_one(x.ebits, x.sbits, x.sign, o); } } else if (x.exponent >= x.sbits - 1) set(o, x); else { SASSERT(x.exponent >= 0 && x.exponent < x.sbits-1); o.ebits = x.ebits; o.sbits = x.sbits; o.sign = x.sign; scoped_mpf a(*this); set(a, x); unpack(a, true); // A includes hidden bit TRACE("mpf_dbg", tout << "A = " << to_string_raw(a) << std::endl;); SASSERT(m_mpz_manager.lt(a.significand(), m_powers2(x.sbits))); SASSERT(m_mpz_manager.ge(a.significand(), m_powers2(x.sbits - 1))); o.exponent = a.exponent(); m_mpz_manager.set(o.significand, a.significand()); unsigned shift = (o.sbits - 1) - ((unsigned)o.exponent); const mpz & shift_p = m_powers2(shift); TRACE("mpf_dbg", tout << "shift=" << shift << std::endl;); scoped_mpz div(m_mpz_manager), rem(m_mpz_manager); m_mpz_manager.machine_div_rem(o.significand, shift_p, div, rem); TRACE("mpf_dbg", tout << "div=" << m_mpz_manager.to_string(div) << " rem=" << m_mpz_manager.to_string(rem) << std::endl;); const mpz & shift_p1 = m_powers2(shift-1); TRACE("mpf_dbg", tout << "shift_p1=" << m_mpz_manager.to_string(shift_p1) << std::endl;); switch (rm) { case MPF_ROUND_NEAREST_TEVEN: case MPF_ROUND_NEAREST_TAWAY: { bool tie = m_mpz_manager.eq(rem, shift_p1); bool less_than_tie = m_mpz_manager.lt(rem, shift_p1); bool more_than_tie = m_mpz_manager.gt(rem, shift_p1); TRACE("mpf_dbg", tout << "tie= " << tie << "; tie = " << more_than_tie << std::endl;); if (tie) { if ((rm == MPF_ROUND_NEAREST_TEVEN && m_mpz_manager.is_odd(div)) || (rm == MPF_ROUND_NEAREST_TAWAY && m_mpz_manager.is_even(div))) { TRACE("mpf_dbg", tout << "div++ (1)" << std::endl;); m_mpz_manager.inc(div); } } else { SASSERT(less_than_tie || more_than_tie); if (more_than_tie) { m_mpz_manager.inc(div); TRACE("mpf_dbg", tout << "div++ (2)" << std::endl;); } } break; } case MPF_ROUND_TOWARD_POSITIVE: if (!m_mpz_manager.is_zero(rem) && !o.sign) m_mpz_manager.inc(div); break; case MPF_ROUND_TOWARD_NEGATIVE: if (!m_mpz_manager.is_zero(rem) && o.sign) m_mpz_manager.inc(div); break; case MPF_ROUND_TOWARD_ZERO: default: /* nothing */; } m_mpz_manager.mul2k(div, shift, o.significand); SASSERT(m_mpz_manager.ge(o.significand, m_powers2(o.sbits - 1))); // re-normalize while (m_mpz_manager.ge(o.significand, m_powers2(o.sbits))) { m_mpz_manager.machine_div2k(o.significand, 1); o.exponent++; } m_mpz_manager.sub(o.significand, m_powers2(o.sbits - 1), o.significand); // strip hidden bit } TRACE("mpf_dbg", tout << "INTEGRAL = " << to_string(o) << std::endl;); } void mpf_manager::to_mpz(mpf const & x, unsynch_mpz_manager & zm, mpz & o) { // x is assumed to be unpacked. SASSERT(x.exponent < INT_MAX); zm.set(o, x.significand); if (x.sign) zm.neg(o); int e = (int)x.exponent - x.sbits + 1; if (e < 0) zm.machine_div2k(o, -e); else zm.mul2k(o, e); } void mpf_manager::to_sbv_mpq(mpf_rounding_mode rm, const mpf & x, scoped_mpq & o) { SASSERT(!is_nan(x) && !is_inf(x)); scoped_mpf t(*this); scoped_mpz z(m_mpz_manager); set(t, x); unpack(t, true); SASSERT(t.exponent() < INT_MAX); m_mpz_manager.set(z, t.significand()); mpf_exp_t e = (mpf_exp_t)t.exponent() - t.sbits() + 1; if (e < 0) { bool last = false, round = false, sticky = m_mpz_manager.is_odd(z); for (; e != 0; e++) { m_mpz_manager.machine_div2k(z, 1); sticky |= round; round = last; last = m_mpz_manager.is_odd(z); } bool inc = false; switch (rm) { case MPF_ROUND_NEAREST_TEVEN: inc = round && (last || sticky); break; case MPF_ROUND_NEAREST_TAWAY: inc = round && (!last || sticky); break; // CMW: Check! case MPF_ROUND_TOWARD_POSITIVE: inc = (!x.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_NEGATIVE: inc = (x.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_ZERO: inc = false; break; default: UNREACHABLE(); } if (inc) m_mpz_manager.inc(z); } else m_mpz_manager.mul2k(z, (unsigned) e); m_mpq_manager.set(o, z); if (x.sign) m_mpq_manager.neg(o); } void mpf_manager::to_ieee_bv_mpz(const mpf & x, scoped_mpz & o) { SASSERT(!is_nan(x) && !is_inf(x)); SASSERT(exp(x) < INT_MAX); unsigned sbits = x.get_sbits(); unsigned ebits = x.get_ebits(); m_mpz_manager.set(o, sgn(x)); m_mpz_manager.mul2k(o, ebits); m_mpz_manager.add(o, (int)exp(x), o); m_mpz_manager.mul2k(o, sbits - 1); m_mpz_manager.add(o, sig(x), o); } void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); TRACE("mpf_dbg", tout << "Y = " << to_string(y) << std::endl;); if (is_nan(x) || is_nan(y)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(x)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(y)) set(o, x); else if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else if (is_zero(x)) set(o, x); else { o.ebits = x.ebits; o.sbits = x.sbits; o.sign = x.sign; scoped_mpf a(*this), b(*this); set(a, x); set(b, y); unpack(a, true); unpack(b, true); TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); if (a.exponent() < b.exponent()) set(o, x); else { mpf_exp_t exp_diff = a.exponent() - b.exponent(); SASSERT(exp_diff >= 0); TRACE("mpf_dbg", tout << "exp_diff = " << exp_diff << std::endl;); SASSERT(exp_diff < INT_MAX); // CMW: This requires rather a lot of memory. There are algorithms that trade space for time by // computing only a small chunk of the remainder bits at a time. unsigned extra_bits = (unsigned) exp_diff; m_mpz_manager.mul2k(a.significand(), extra_bits); m_mpz_manager.rem(a.significand(), b.significand(), o.significand); TRACE("mpf_dbg", tout << "REM' = " << to_string(o) << std::endl;); if (m_mpz_manager.is_zero(o.significand)) mk_zero(o.ebits, o.sbits, o.sign, o); else { o.exponent = b.exponent(); m_mpz_manager.mul2k(o.significand, 3); // rounding bits round(MPF_ROUND_NEAREST_TEVEN, o); } } } TRACE("mpf_dbg", tout << "REMAINDER = " << to_string(o) << std::endl;); } void mpf_manager::maximum(mpf const & x, mpf const & y, mpf & o) { if (is_nan(x)) set(o, y); else if (is_zero(x) && is_zero(y) && sgn(x) != sgn(y)) mk_pzero(x.ebits, x.sbits, o); else if (is_zero(x) && is_zero(y)) set(o, y); else if (is_nan(y)) set(o, x); else if (gt(x, y)) set(o, x); else set(o, y); } void mpf_manager::minimum(mpf const & x, mpf const & y, mpf & o) { if (is_nan(x)) set(o, y); else if (is_zero(x) && is_zero(y) && sgn(x) != sgn(y)) mk_pzero(x.ebits, x.sbits, o); else if (is_zero(x) && is_zero(y)) set(o, y); else if (is_nan(y)) set(o, x); else if (lt(x, y)) set(o, x); else set(o, y); } std::string mpf_manager::to_string(mpf const & x) { std::string res; if (is_nan(x)) res = "NaN"; else { if (is_inf(x)) res = sgn(x) ? "-oo" : "+oo"; else if (is_zero(x)) res = sgn(x) ? "-zero" : "+zero"; else { res = sgn(x) ? "-" : ""; scoped_mpz num(m_mpq_manager), denom(m_mpq_manager); num = 0; denom = 1; mpf_exp_t exponent; if (is_denormal(x)) exponent = mk_min_exp(x.ebits); else { m_mpz_manager.set(num, 1); m_mpz_manager.mul2k(num, x.sbits-1, num); exponent = exp(x); } m_mpz_manager.add(num, sig(x), num); m_mpz_manager.mul2k(denom, x.sbits-1, denom); //TRACE("mpf_dbg", tout << "SIG=" << m_mpq_manager.to_string(sig(x)) << std::endl; ); //TRACE("mpf_dbg", tout << "NUM=" << m_mpq_manager.to_string(num) << std::endl;); //TRACE("mpf_dbg", tout << "DEN=" << m_mpq_manager.to_string(denom) << std::endl;); //TRACE("mpf_dbg", tout << "EXP=" << exponent << std::endl;); scoped_mpq r(m_mpq_manager); m_mpq_manager.set(r, num); m_mpq_manager.div(r, denom, r); std::stringstream ss; m_mpq_manager.display_decimal(ss, r, x.sbits); if (m_mpq_manager.is_int(r)) ss << ".0"; ss << " " << exponent; res += ss.str(); } } //DEBUG_CODE( // res += " " + to_string_raw(x); //); return res; } std::string mpf_manager::to_rational_string(mpf const & x) { scoped_mpq q(m_mpq_manager); to_rational(x, q); return m_mpq_manager.to_string(q); } void mpf_manager::display_decimal(std::ostream & out, mpf const & a, unsigned k) { scoped_mpq q(m_mpq_manager); to_rational(a, q); m_mpq_manager.display_decimal(out, q, k); } void mpf_manager::display_smt2(std::ostream & out, mpf const & a, bool decimal) { scoped_mpq q(m_mpq_manager); to_rational(a, q); m_mpq_manager.display_smt2(out, q, decimal); } std::string mpf_manager::to_string_raw(mpf const & x) { std::string res; res += "["; res += (x.sign?"-":"+"); res += " "; res += m_mpz_manager.to_string(sig(x)); res += " "; std::stringstream ss(""); ss << exp(x); res += ss.str(); if (is_normal(x)) res += " N"; else res += " D"; res += "]"; return res; } void mpf_manager::to_rational(mpf const & x, unsynch_mpq_manager & qm, mpq & o) { scoped_mpf a(*this); scoped_mpz n(m_mpq_manager), d(m_mpq_manager); set(a, x); unpack(a, true); m_mpz_manager.set(n, a.significand()); if (a.sign()) m_mpz_manager.neg(n); m_mpz_manager.power(2, a.sbits() - 1, d); if (a.exponent() >= 0) m_mpz_manager.mul2k(n, (unsigned)a.exponent()); else m_mpz_manager.mul2k(d, (unsigned)-a.exponent()); qm.set(o, n, d); } double mpf_manager::to_double(mpf const & x) { SASSERT(x.ebits <= 11 && x.sbits <= 53); uint64 raw = 0; int64 sig = 0, exp = 0; sig = m_mpz_manager.get_uint64(x.significand); sig <<= 53 - x.sbits; if (has_top_exp(x)) exp = 1024; else if (has_bot_exp(x)) exp = -1023; else exp = x.exponent; exp += 1023; raw = (exp << 52) | sig; if (x.sign) raw = raw | 0x8000000000000000ull; double ret; memcpy(&ret, &raw, sizeof(double)); return ret; } float mpf_manager::to_float(mpf const & x) { SASSERT(x.ebits <= 8 && x.sbits <= 24); unsigned int raw = 0; unsigned int sig = 0, exp = 0; uint64 q = m_mpz_manager.get_uint64(x.significand); SASSERT(q < 4294967296ull); sig = q & 0x00000000FFFFFFFF; sig <<= 24 - x.sbits; if (has_top_exp(x)) exp = +128; else if (has_bot_exp(x)) exp = -127; else { int64 q = x.exponent; SASSERT(q < 4294967296ll); exp = q & 0x00000000FFFFFFFF; } exp += 127; raw = (exp << 23) | sig; if (x.sign) raw = raw | 0x80000000; float ret; memcpy(&ret, &raw, sizeof(float)); return ret; } bool mpf_manager::is_nan(mpf const & x) { return has_top_exp(x) && !m_mpz_manager.is_zero(sig(x)); } bool mpf_manager::is_inf(mpf const & x) { return has_top_exp(x) && m_mpz_manager.is_zero(sig(x)); } bool mpf_manager::is_pinf(mpf const & x) { return !x.sign && is_inf(x); } bool mpf_manager::is_ninf(mpf const & x) { return x.sign && is_inf(x); } bool mpf_manager::is_normal(mpf const & x) { return !(has_top_exp(x) || is_denormal(x) || is_zero(x)); } bool mpf_manager::is_denormal(mpf const & x) { return !is_zero(x) && has_bot_exp(x); } bool mpf_manager::is_int(mpf const & x) { if (!is_normal(x)) return false; if (exp(x) >= x.sbits-1) return true; else if (exp(x) < 0) return false; else { SASSERT(x.exponent >= 0 && x.exponent < x.sbits-1); scoped_mpz t(m_mpz_manager); m_mpz_manager.set(t, sig(x)); unsigned shift = x.sbits - ((unsigned)exp(x)) - 1; do { if (m_mpz_manager.is_odd(t)) return false; m_mpz_manager.machine_div2k(t, 1); } while (--shift != 0); return true; } } bool mpf_manager::has_bot_exp(mpf const & x) { return exp(x) == mk_bot_exp(x.ebits); } bool mpf_manager::has_top_exp(mpf const & x) { return exp(x) == mk_top_exp(x.ebits); } mpf_exp_t mpf_manager::mk_bot_exp(unsigned ebits) { SASSERT(ebits >= 2); return m_mpz_manager.get_int64(m_powers2.m1(ebits-1, true)); } mpf_exp_t mpf_manager::mk_top_exp(unsigned ebits) { SASSERT(ebits >= 2); return m_mpz_manager.get_int64(m_powers2(ebits-1)); } mpf_exp_t mpf_manager::mk_min_exp(unsigned ebits) { SASSERT(ebits > 0); mpf_exp_t r = m_mpz_manager.get_int64(m_powers2.m1(ebits-1, true)); return r+1; } mpf_exp_t mpf_manager::mk_max_exp(unsigned ebits) { SASSERT(ebits > 0); return m_mpz_manager.get_int64(m_powers2.m1(ebits-1, false)); } mpf_exp_t mpf_manager::unbias_exp(unsigned ebits, mpf_exp_t biased_exponent) { return biased_exponent - m_mpz_manager.get_int64(m_powers2.m1(ebits - 1, false)); } void mpf_manager::mk_nzero(unsigned ebits, unsigned sbits, mpf & o) { o.sbits = sbits; o.ebits = ebits; o.exponent = mk_bot_exp(ebits); m_mpz_manager.set(o.significand, 0); o.sign = true; } void mpf_manager::mk_pzero(unsigned ebits, unsigned sbits, mpf & o) { o.sbits = sbits; o.ebits = ebits; o.exponent = mk_bot_exp(ebits); m_mpz_manager.set(o.significand, 0); o.sign = false; } void mpf_manager::mk_zero(unsigned ebits, unsigned sbits, bool sign, mpf & o) { if (sign) mk_nzero(ebits, sbits, o); else mk_pzero(ebits, sbits, o); } void mpf_manager::mk_nan(unsigned ebits, unsigned sbits, mpf & o) { o.sbits = sbits; o.ebits = ebits; o.exponent = mk_top_exp(ebits); // This is a quiet NaN, i.e., the first bit should be 1. m_mpz_manager.set(o.significand, m_powers2(sbits-1)); m_mpz_manager.dec(o.significand); o.sign = false; } void mpf_manager::mk_one(unsigned ebits, unsigned sbits, bool sign, mpf & o) const { o.sbits = sbits; o.ebits = ebits; o.sign = sign; m_mpz_manager.set(o.significand, 0); o.exponent = 0; } void mpf_manager::mk_max_value(unsigned ebits, unsigned sbits, bool sign, mpf & o) { o.sbits = sbits; o.ebits = ebits; o.sign = sign; o.exponent = mk_top_exp(ebits) - 1; m_mpz_manager.set(o.significand, m_powers2.m1(sbits-1, false)); } void mpf_manager::mk_inf(unsigned ebits, unsigned sbits, bool sign, mpf & o) { o.sbits = sbits; o.ebits = ebits; o.sign = sign; o.exponent = mk_top_exp(ebits); m_mpz_manager.set(o.significand, 0); } void mpf_manager::mk_pinf(unsigned ebits, unsigned sbits, mpf & o) { mk_inf(ebits, sbits, false, o); } void mpf_manager::mk_ninf(unsigned ebits, unsigned sbits, mpf & o) { mk_inf(ebits, sbits, true, o); } void mpf_manager::unpack(mpf & o, bool normalize) { TRACE("mpf_dbg", tout << "unpack " << to_string(o) << ": ebits=" << o.ebits << " sbits=" << o.sbits << " normalize=" << normalize << " has_top_exp=" << has_top_exp(o) << " (" << mk_top_exp(o.ebits) << ")" << " has_bot_exp=" << has_bot_exp(o) << " (" << mk_bot_exp(o.ebits) << ")" << " is_zero=" << is_zero(o) << std::endl;); // Insert the hidden bit or adjust the exponent of denormal numbers. if (is_zero(o)) return; if (is_normal(o)) m_mpz_manager.add(o.significand, m_powers2(o.sbits-1), o.significand); else { o.exponent = mk_bot_exp(o.ebits) + 1; if (normalize && !m_mpz_manager.is_zero(o.significand)) { const mpz & p = m_powers2(o.sbits-1); while (m_mpz_manager.gt(p, o.significand)) { o.exponent--; m_mpz_manager.mul2k(o.significand, 1, o.significand); } } } } void mpf_manager::mk_round_inf(mpf_rounding_mode rm, mpf & o) { if (!o.sign) { if (rm == MPF_ROUND_TOWARD_ZERO || rm == MPF_ROUND_TOWARD_NEGATIVE) mk_max_value(o.ebits, o.sbits, o.sign, o); else mk_inf(o.ebits, o.sbits, o.sign, o); } else { if (rm == MPF_ROUND_TOWARD_ZERO || rm == MPF_ROUND_TOWARD_POSITIVE) mk_max_value(o.ebits, o.sbits, o.sign, o); else mk_inf(o.ebits, o.sbits, o.sign, o); } } void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { // Assumptions: o.significand 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. TRACE("mpf_dbg", tout << "RND: " << to_string(o) << std::endl;); DEBUG_CODE({ const mpz & p_m3 = m_powers2(o.sbits+5); SASSERT(m_mpz_manager.lt(o.significand, p_m3)); }); // Structure of the rounder: // (s, e_out, f_out) == (s, exprd(s, post(e, sigrd(s, f)))). bool UNFen = false; // Are these supposed to be persistent flags accross calls? bool OVFen = false; mpf_exp_t e_max_norm = mk_max_exp(o.ebits); mpf_exp_t e_min_norm = mk_min_exp(o.ebits); scoped_mpz temporary(m_mpq_manager); TRACE("mpf_dbg", tout << "e_min_norm = " << e_min_norm << std::endl << "e_max_norm = " << e_max_norm << std::endl;); const mpz & p_m1 = m_powers2(o.sbits+2); const mpz & p_m2 = m_powers2(o.sbits+3); TRACE("mpf_dbg", tout << "p_m1 = " << m_mpz_manager.to_string(p_m1) << std::endl << "p_m2 = " << m_mpz_manager.to_string(p_m2) << std::endl;); bool OVF1 = o.exponent > e_max_norm || // Exponent OVF (o.exponent == e_max_norm && m_mpz_manager.ge(o.significand, p_m2)); TRACE("mpf_dbg", tout << "OVF1 = " << OVF1 << std::endl;); int lz = 0; scoped_mpz t(m_mpq_manager); m_mpz_manager.set(t, p_m2); while (m_mpz_manager.gt(t, o.significand)) { m_mpz_manager.machine_div2k(t, 1); lz++; } TRACE("mpf_dbg", tout << "LZ = " << lz << std::endl;); m_mpz_manager.set(t, o.exponent); m_mpz_manager.inc(t); m_mpz_manager.sub(t, lz, t); m_mpz_manager.set(temporary, e_min_norm); m_mpz_manager.sub(t, temporary, t); bool TINY = m_mpz_manager.is_neg(t); TRACE("mpf_dbg", tout << "TINY = " << TINY << std::endl;); mpf_exp_t alpha = 3 << (o.ebits-2); mpf_exp_t beta = o.exponent - lz + 1; TRACE("mpf_dbg", tout << "alpha = " << alpha << std::endl << "beta = " << beta << std::endl; ); scoped_mpz sigma(m_mpq_manager); sigma = 0; if (TINY && !UNFen) { m_mpz_manager.set(sigma, o.exponent); m_mpz_manager.sub(sigma, temporary, sigma); m_mpz_manager.inc(sigma); } else m_mpz_manager.set(sigma, lz); scoped_mpz limit(m_mpq_manager); limit = o.sbits + 2; m_mpz_manager.neg(limit); if (m_mpz_manager.lt(sigma, limit)) { m_mpz_manager.set(sigma, limit); } // Normalization shift TRACE("mpf_dbg", tout << "Shift distance: " << m_mpz_manager.to_string(sigma) << " " << ((m_mpz_manager.is_nonneg(sigma))?"(LEFT)":"(RIGHT)") << std::endl;); bool sticky = !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); // Let f' = f_r/2 if (!m_mpz_manager.is_zero(sigma)) { if (m_mpz_manager.is_neg(sigma)) { // Right shift unsigned sigma_uint = (unsigned) -m_mpz_manager.get_int64(sigma); // sigma is capped, this is safe. if (sticky) m_mpz_manager.machine_div2k(o.significand, sigma_uint); else { scoped_mpz sticky_rem(m_mpz_manager); m_mpz_manager.machine_div_rem(o.significand, m_powers2(sigma_uint), o.significand, sticky_rem); sticky = !m_mpz_manager.is_zero(sticky_rem); } } else { // Left shift unsigned sh_m = static_cast(m_mpz_manager.get_int64(sigma)); m_mpz_manager.mul2k(o.significand, sh_m, o.significand); m_mpz_manager.set(sigma, 0); } } TRACE("mpf_dbg", tout << "Before sticky: " << to_string(o) << std::endl;); // Make sure o.significand is a [sbits+2] bit number (i.e. f1[0:sbits+1] == f1[0:sbits-1][round][sticky]) sticky = sticky || !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); if (sticky && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); if (OVF1 && OVFen) { o.exponent = beta; o.exponent -= alpha; } else if (TINY && UNFen) { o.exponent = beta; o.exponent += alpha; } else if (TINY && !UNFen) o.exponent = e_min_norm; else o.exponent = beta; TRACE("mpf_dbg", tout << "Shifted: " << to_string(o) << std::endl;); const mpz & p_sig = m_powers2(o.sbits); SASSERT(TINY || (m_mpz_manager.ge(o.significand, p_sig))); // Significand rounding (sigrd) sticky = !m_mpz_manager.is_even(o.significand); // new sticky bit! m_mpz_manager.machine_div2k(o.significand, 1); bool round = !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); bool last = !m_mpz_manager.is_even(o.significand); TRACE("mpf_dbg", tout << "sign=" << o.sign << " last=" << last << " round=" << round << " sticky=" << sticky << std::endl;); TRACE("mpf_dbg", tout << "before rounding decision: " << to_string(o) << std::endl;); // The significand has the right size now, but we might have to increment it // depending on the sign, the last/round/sticky bits, and the rounding mode. bool inc = false; switch (rm) { case MPF_ROUND_NEAREST_TEVEN: inc = round && (last || sticky); break; // case MPF_ROUND_NEAREST_TAWAY: inc = round; break; // CMW: Check case MPF_ROUND_NEAREST_TAWAY: inc = round && (!last || sticky); break; // CMW: Fix ok? case MPF_ROUND_TOWARD_POSITIVE: inc = (!o.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_NEGATIVE: inc = (o.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_ZERO: inc = false; break; default: UNREACHABLE(); } if (inc) { TRACE("mpf_dbg", tout << "Rounding increment -> significand +1" << std::endl;); m_mpz_manager.inc(o.significand); } else TRACE("mpf_dbg", tout << "Rounding increment -> significand +0" << std::endl;); TRACE("mpf_dbg", tout << "Rounded significand: " << to_string(o) << std::endl;); bool SIGovf = false; // Post normalization (post) if (m_mpz_manager.ge(o.significand, p_sig)) { m_mpz_manager.machine_div2k(o.significand, 1); o.exponent++; } if (o.exponent > e_max_norm) SIGovf = true; TRACE("mpf_dbg", tout << "Post-normalized: " << to_string(o) << std::endl;); TRACE("mpf_dbg", tout << "SIGovf = " << SIGovf << std::endl;); // Exponent rounding (exprd) bool o_has_max_exp = (o.exponent > e_max_norm); bool OVF2 = SIGovf && o_has_max_exp; TRACE("mpf_dbg", tout << "OVF2 = " << OVF2 << std::endl;); TRACE("mpf_dbg", tout << "o_has_max_exp = " << o_has_max_exp << std::endl;); if (!OVFen && OVF2) mk_round_inf(rm, o); else { const mpz & p = m_powers2(o.sbits-1); TRACE("mpf_dbg", tout << "P: " << m_mpz_manager.to_string(p_m1) << std::endl;); if (m_mpz_manager.ge(o.significand, p)) { TRACE("mpf_dbg", tout << "NORMAL: " << m_mpz_manager.to_string(o.significand) << std::endl;); m_mpz_manager.sub(o.significand, p, o.significand); } else { TRACE("mpf_dbg", tout << "DENORMAL: " << m_mpz_manager.to_string(o.significand) << std::endl;); o.exponent = mk_bot_exp(o.ebits); } } TRACE("mpf_dbg", tout << "ROUNDED = " << to_string(o) << std::endl;); } void mpf_manager::round_sqrt(mpf_rounding_mode rm, mpf & o) { TRACE("mpf_dbg", tout << "RND-SQRT: " << to_string(o) << std::endl;); bool sticky = !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); sticky = sticky || !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); bool round = !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); bool last = !m_mpz_manager.is_even(o.significand); bool inc = false; // Specialized rounding for sqrt, as there are no negative cases (or half-way cases?) switch(rm) { case MPF_ROUND_NEAREST_TEVEN: case MPF_ROUND_NEAREST_TAWAY: inc = (round && sticky); break; case MPF_ROUND_TOWARD_NEGATIVE: break; case MPF_ROUND_TOWARD_ZERO: break; case MPF_ROUND_TOWARD_POSITIVE: inc = round || sticky; break; default: UNREACHABLE(); } TRACE("mpf_dbg", tout << "last=" << last << " round=" << round << " sticky=" << sticky << " --> inc=" << inc << std::endl;); if (inc) m_mpz_manager.inc(o.significand); m_mpz_manager.sub(o.significand, m_powers2(o.sbits-1), o.significand); TRACE("mpf_dbg", tout << "ROUNDED = " << to_string(o) << std::endl;); } unsigned mpf_manager::prev_power_of_two(mpf const & a) { SASSERT(!is_nan(a) && !is_pinf(a) && !is_ninf(a)); if (!is_pos(a)) return 0; if (a.exponent <= -static_cast(a.sbits)) return 0; // Number is smaller than 1 return static_cast(a.sbits + a.exponent - 1); } z3-z3-4.4.1/src/util/mpf.h000066400000000000000000000236421260446376700151330ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpf.h Abstract: Multi Precision Floating Point Numbers Author: Christoph Wintersteiger (cwinter) 2011-12-01. Revision History: --*/ #ifndef MPF_H_ #define MPF_H_ #include #include"mpz.h" #include"mpq.h" #include"map.h" #include"scoped_numeral.h" #include"scoped_numeral_vector.h" #include"hash.h" typedef enum { MPF_ROUND_NEAREST_TEVEN, MPF_ROUND_NEAREST_TAWAY, MPF_ROUND_TOWARD_POSITIVE, MPF_ROUND_TOWARD_NEGATIVE, MPF_ROUND_TOWARD_ZERO } mpf_rounding_mode; typedef int64 mpf_exp_t; class mpf { friend class mpf_manager; friend class scoped_mpf; unsigned ebits:15; unsigned sbits:16; unsigned sign:1; // counts as one sbit. mpz significand; mpf_exp_t exponent; mpf & operator=(mpf const & other) { UNREACHABLE(); return *this; } void set(unsigned _ebits, unsigned _sbits); public: mpf(); mpf(unsigned ebits, unsigned sbits); mpf(mpf const & other); ~mpf(); unsigned get_ebits() const { return ebits; } unsigned get_sbits() const { return sbits; } void swap(mpf & other); }; class mpf_manager { unsynch_mpq_manager m_mpq_manager; unsynch_mpz_manager & m_mpz_manager; // A mpq_manager is a mpz_manager, reusing it. public: typedef mpf numeral; mpf_manager(); ~mpf_manager(); void reset(mpf & o, unsigned ebits, unsigned sbits) { set(o, ebits, sbits, 0); } void set(mpf & o, unsigned ebits, unsigned sbits, int value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, int n, int d); void set(mpf & o, unsigned ebits, unsigned sbits, float value); void set(mpf & o, unsigned ebits, unsigned sbits, double value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, char const * value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent); void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, uint64 significand, mpf_exp_t exponent); void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpz const & significand, mpf_exp_t exponent); void set(mpf & o, mpf const & x); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpf const & x); void del(mpf & x) { m_mpz_manager.del(x.significand); } void abs(mpf & o); void abs(mpf const & x, mpf & o); void neg(mpf & o); void neg(mpf const & x, mpf & o); bool is_zero(mpf const & x); bool is_neg(mpf const & x); bool is_pos(mpf const & x); bool is_one(mpf const & x); bool is_nzero(mpf const & x); bool is_pzero(mpf const & x); // structural eq bool eq_core(mpf const & x, mpf const & y) { return x.ebits == y.ebits && x.sbits == y.sbits && x.sign == y.sign && m_mpz_manager.eq(x.significand, y.significand) && x.exponent == y.exponent; } bool eq(mpf const & x, mpf const & y); bool lt(mpf const & x, mpf const & y); bool lte(mpf const & x, mpf const & y); bool le(mpf const & x, mpf const & y) { return lte(x, y); } bool gt(mpf const & x, mpf const & y); bool gte(mpf const & x, mpf const & y); bool ge(mpf const & x, mpf const & y) { return gte(x, y); } void add(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void mul(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void div(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void fma(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf const &z, mpf & o); void sqrt(mpf_rounding_mode rm, mpf const & x, mpf & o); void round_to_integral(mpf_rounding_mode rm, mpf const & x, mpf & o); void rem(mpf const & x, mpf const & y, mpf & o); void maximum(mpf const & x, mpf const & y, mpf & o); void minimum(mpf const & x, mpf const & y, mpf & o); std::string to_string(mpf const & a); std::string to_rational_string(mpf const & a); void display_decimal(std::ostream & out, mpf const & a, unsigned k); void display_smt2(std::ostream & out, mpf const & a, bool decimal); // Convert x into a mpq numeral. zm is the manager that owns o. void to_rational(mpf const & x, unsynch_mpq_manager & qm, mpq & o); void to_rational(mpf const & x, scoped_mpq & o) { to_rational(x, o.m(), o); } double to_double(mpf const & x); float to_float(mpf const & x); bool sgn(mpf const & x) const { return x.sign; } const mpz & sig(mpf const & x) const { return x.significand; } void sig_normalized(mpf const & x, mpz & res) { mpf t; set(t, x); unpack(t, true); mpz_manager().set(res, t.significand); del(t); } const mpf_exp_t & exp(mpf const & x) const { return x.exponent; } mpf_exp_t exp_normalized(mpf const & x) { mpf t; set(t, x); unpack(t, true); mpf_exp_t r = t.exponent; del(t); return r; } bool is_nan(mpf const & x); bool is_inf(mpf const & x); bool is_pinf(mpf const & x); bool is_ninf(mpf const & x); bool is_normal(mpf const & x); bool is_denormal(mpf const & x); bool is_regular(mpf const & x) { return x.sbits == 0 || is_normal(x) || is_denormal(x); } bool is_int(mpf const & x); void mk_zero(unsigned ebits, unsigned sbits, bool sign, mpf & o); void mk_nzero(unsigned ebits, unsigned sbits, mpf & o); void mk_pzero(unsigned ebits, unsigned sbits, mpf & o); void mk_nan(unsigned ebits, unsigned sbits, mpf & o); void mk_inf(unsigned ebits, unsigned sbits, bool sign, mpf & o); void mk_pinf(unsigned ebits, unsigned sbits, mpf & o); void mk_ninf(unsigned ebits, unsigned sbits, mpf & o); std::string to_string_raw(mpf const & a); unsynch_mpz_manager & mpz_manager(void) { return m_mpz_manager; } unsynch_mpq_manager & mpq_manager(void) { return m_mpq_manager; } unsigned hash(mpf const & a) { return hash_u_u(m_mpz_manager.hash(a.significand), m_mpz_manager.hash(hash_ull(a.exponent))); } void mk_max_value(unsigned ebits, unsigned sbits, bool sign, mpf & o); mpf_exp_t mk_bot_exp(unsigned ebits); mpf_exp_t mk_top_exp(unsigned ebits); mpf_exp_t mk_max_exp(unsigned ebits); mpf_exp_t mk_min_exp(unsigned ebits); mpf_exp_t unbias_exp(unsigned ebits, mpf_exp_t biased_exponent); /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpf const & a); void to_sbv_mpq(mpf_rounding_mode rm, const mpf & x, scoped_mpq & o); void to_ieee_bv_mpz(const mpf & x, scoped_mpz & o); protected: void mk_one(unsigned ebits, unsigned sbits, bool sign, mpf & o) const; bool has_bot_exp(mpf const & x); bool has_top_exp(mpf const & x); void unpack(mpf & o, bool normalize); void add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o, bool sub); void round(mpf_rounding_mode rm, mpf & o); void round_sqrt(mpf_rounding_mode rm, mpf & o); void mk_round_inf(mpf_rounding_mode rm, mpf & o); // Convert x into a mpz numeral. zm is the manager that owns o. void to_mpz(mpf const & x, unsynch_mpz_manager & zm, mpz & o); void to_mpz(mpf const & x, scoped_mpz & o) { to_mpz(x, o.m(), o); } class powers2 { unsynch_mpz_manager & m; u_map m_p; u_map m_pn; u_map m_pm1; u_map m_pm1n; public: powers2(unsynch_mpz_manager & m) : m(m) {} ~powers2() { dispose(m_p); dispose(m_pn); dispose(m_pm1); dispose(m_pm1n); } void dispose(u_map & map) { for (u_map::iterator it = map.begin(); it != map.end(); it++) { m.del(*it->m_value); dealloc(it->m_value); } } const mpz & operator()(unsigned n, bool negated = false) { u_map & map = (negated) ? m_pn : m_p; u_map::iterator it = map.find_iterator(n); if (it != map.end()) return *it->m_value; else { mpz * new_obj = alloc(mpz); map.insert(n, new_obj); m.power(unsynch_mpz_manager::mk_z(2), n, *new_obj); if (negated) m.neg(*new_obj); return *new_obj; } } const mpz & m1(unsigned n, bool negated=false) { // (2 ^ n) - 1 u_map & map = (negated) ? m_pm1n : m_pm1; u_map::iterator it = map.find_iterator(n); if (it != map.end()) return *it->m_value; else { mpz * new_obj = alloc(mpz); map.insert(n, new_obj); m.power(unsynch_mpz_manager::mk_z(2), n, *new_obj); m.dec(*new_obj); if (negated) m.neg(*new_obj); return *new_obj; } } }; public: powers2 m_powers2; }; class scoped_mpf : public _scoped_numeral { friend class mpf_manager; mpz & significand() { return get().significand; } bool sign() const { return get().sign; } mpf_exp_t exponent() const { return get().exponent; } unsigned sbits() const { return get().sbits; } void set(unsigned ebits, unsigned sbits) { get().set(ebits, sbits); } public: scoped_mpf(mpf_manager & m):_scoped_numeral(m) {} scoped_mpf(scoped_mpf const & n):_scoped_numeral(n) {} scoped_mpf(mpf_manager & m, unsigned ebits, unsigned sbits):_scoped_numeral(m) { set(ebits, sbits); } }; typedef _scoped_numeral_vector scoped_mpf_vector; #endif z3-z3-4.4.1/src/util/mpff.cpp000066400000000000000000001313451260446376700156340ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpff.cpp Abstract: Multi precision fast floating point numbers. The implementation is not compliant with the IEEE standard. For a compliant implementation, see mpf.h Author: Leonardo de Moura (leonardo) 2012-09-12 Revision History: --*/ #include #include #include"mpff.h" #include"mpn.h" #include"mpz.h" #include"mpq.h" #include"bit_util.h" #include"trace.h" COMPILE_TIME_ASSERT(sizeof(mpn_digit) == sizeof(unsigned)); COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); // MIN_MSW is an shorthand for 0x8000..00, i.e., the minimal most significand word. #define MIN_MSW (1u << (sizeof(unsigned) * 8 - 1)) mpff_manager::mpff_manager(unsigned prec, unsigned initial_capacity) { SASSERT(initial_capacity > 0); m_precision = prec; m_precision_bits = prec * 8 * sizeof(unsigned); m_capacity = initial_capacity; m_to_plus_inf = false; m_significands.resize(initial_capacity * prec, 0); for (unsigned i = 0; i < MPFF_NUM_BUFFERS; i++) m_buffers[i].resize(2 * prec, 0); // Reserve space for zero VERIFY(m_id_gen.mk() == 0); set(m_one, 1); } mpff_manager::~mpff_manager() { del(m_one); } void mpff_manager::expand() { m_capacity = 2*m_capacity; m_significands.resize(m_capacity * m_precision, 0); } void mpff_manager::allocate(mpff & n) { SASSERT(n.m_sig_idx == 0); unsigned sig_idx = m_id_gen.mk(); ensure_capacity(sig_idx); n.m_sig_idx = sig_idx; DEBUG_CODE({ unsigned * s = sig(n); for (unsigned i = 0; i < m_precision; i++) { SASSERT(s[i] == 0); } }); } void mpff_manager::to_buffer(unsigned idx, mpff const & n) const { SASSERT(idx < MPFF_NUM_BUFFERS); svector & b = const_cast(this)->m_buffers[idx]; unsigned * s = sig(n); for (unsigned i = 0; i < m_precision; i++) b[i] = s[i]; } void mpff_manager::to_buffer_ext(unsigned idx, mpff const & n) const { SASSERT(idx < MPFF_NUM_BUFFERS); svector & b = const_cast(this)->m_buffers[idx]; unsigned * s = sig(n); unsigned j = m_precision; for (unsigned i = 0; i < m_precision; i++, j++) { b[i] = s[i]; b[j] = 0; } } void mpff_manager::to_buffer_shifting(unsigned idx, mpff const & n) const { SASSERT(idx < MPFF_NUM_BUFFERS); svector & b = const_cast(this)->m_buffers[idx]; unsigned * s = sig(n); unsigned j = m_precision; for (unsigned i = 0; i < m_precision; i++, j++) { b[i] = 0; b[j] = s[i]; } } void mpff_manager::del(mpff & n) { unsigned sig_idx = n.m_sig_idx; if (sig_idx != 0) { m_id_gen.recycle(sig_idx); unsigned * s = sig(n); for (unsigned i = 0; i < m_precision; i++) s[i] = 0; } } void mpff_manager::reset(mpff & n) { del(n); n.m_sign = false; n.m_sig_idx = 0; n.m_exponent = 0; SASSERT(check(n)); } bool mpff_manager::is_int(mpff const & n) const { if (n.m_exponent >= 0) return true; // cheap case if (n.m_exponent <= -static_cast(m_precision_bits)) return false; return !has_one_at_first_k_bits(m_precision, sig(n), -n.m_exponent); } bool mpff_manager::is_int64(mpff const & n) const { SASSERT(m_precision >= 2); if (is_zero(n)) return true; int max_exp = -static_cast(sizeof(unsigned) * 8 * (m_precision - 2)); if (n.m_exponent < max_exp) { return n.m_exponent > -static_cast(m_precision_bits) && !has_one_at_first_k_bits(m_precision, sig(n), -n.m_exponent); } else if (n.m_exponent == max_exp) { // handle INT64_MIN case unsigned * s = sig(n); return is_neg(n) && s[m_precision-1] == 0x80000000u && ::is_zero(m_precision-1, s); } else { return false; } } bool mpff_manager::is_uint64(mpff const & n) const { SASSERT(m_precision >= 2); if (is_zero(n)) return true; return n.m_sign == 0 && n.m_exponent <= -static_cast(sizeof(unsigned) * 8 * (m_precision - 2)) && n.m_exponent > -static_cast(m_precision_bits) && !has_one_at_first_k_bits(m_precision, sig(n), -n.m_exponent); } uint64 mpff_manager::get_uint64(mpff const & a) const { SASSERT(is_uint64(a)); if (is_zero(a)) return 0; int exp = -a.m_exponent - sizeof(unsigned) * 8 * (m_precision - 2); SASSERT(exp >= 0); uint64 * s = reinterpret_cast(sig(a) + (m_precision - 2)); return *s >> exp; } int64 mpff_manager::get_int64(mpff const & a) const { SASSERT(is_int64(a)); if (is_zero(a)) return 0; int exp = -a.m_exponent - sizeof(unsigned) * 8 * (m_precision - 2); SASSERT(exp >= 0); uint64 * s = reinterpret_cast(sig(a) + (m_precision - 2)); // INT64_MIN case if (exp == 0 && *s == 0x8000000000000000ull && is_neg(a)) { return INT64_MIN; } else { int64 r = *s >> exp; if (is_neg(a)) r = -r; return r; } } // Return true if n is 1 or -1 bool mpff_manager::is_abs_one(mpff const & n) const { // That is, check whether // n.exponent == 1 - m_precision_bits // n.significand == 0b10000...0 (that is, only the highest bit is set in the significand). if (n.m_exponent != 1 - static_cast(m_precision_bits)) return false; unsigned * s = sig(n); if (s[m_precision - 1] != 0x80000000u) return false; for (unsigned i = 0; i < m_precision - 1; i++) if (s[i] != 0) return false; return true; } bool mpff_manager::is_two(mpff const & n) const { // That is, check whether // n.exponent = 2 - m_precision_bits // n.significand == 0b10000...0 (that is, only the highest bit is set in the significand). if (is_neg(n)) return false; if (n.m_exponent != 2 - static_cast(m_precision_bits)) return false; unsigned * s = sig(n); if (s[m_precision - 1] != 0x80000000u) return false; for (unsigned i = 0; i < m_precision - 1; i++) if (s[i] != 0) return false; return true; } void mpff_manager::set(mpff & n, int v) { if (v == 0) { reset(n); } else { if (v < 0) { set(n, static_cast(-v)); n.m_sign = 1; } else { set(n, static_cast(v)); } } SASSERT(check(n)); } void mpff_manager::set(mpff & n, unsigned v) { if (v == 0) { reset(n); } else { allocate_if_needed(n); n.m_sign = 0; int num_leading_zeros = nlz_core(v); n.m_exponent = static_cast(8 * sizeof(unsigned)) - num_leading_zeros - static_cast(m_precision_bits); v <<= num_leading_zeros; unsigned * s = sig(n); s[m_precision - 1] = v; for (unsigned i = 0; i < m_precision - 1; i++) s[i] = 0; } SASSERT(check(n)); } void mpff_manager::set(mpff & n, int64 v) { if (v == 0) { reset(n); } else { if (v < 0) { set(n, 1 + static_cast(-(1+v))); n.m_sign = 1; } else { set(n, static_cast(v)); } } SASSERT(check(n)); SASSERT(get_int64(n) == v); } void mpff_manager::set(mpff & n, uint64 v) { #ifdef Z3DEBUG uint64 old_v = v; #endif if (v == 0) { reset(n); } else { allocate_if_needed(n); n.m_sign = 0; unsigned * _v = reinterpret_cast(&v); int num_leading_zeros = nlz(2, _v); n.m_exponent = static_cast(8 * sizeof(uint64)) - num_leading_zeros - static_cast(m_precision_bits); v <<= num_leading_zeros; SASSERT(m_precision >= 2); unsigned * s = sig(n); s[m_precision-1] = _v[1]; s[m_precision-2] = _v[0]; for (unsigned i = 0; i < m_precision - 2; i++) s[i] = 0; } SASSERT(check(n)); SASSERT(get_uint64(n) == old_v); } void mpff_manager::set(mpff & n, int num, unsigned den) { scoped_mpff a(*this), b(*this); set(a, num); set(b, den); div(a, b, n); SASSERT(check(n)); } void mpff_manager::set(mpff & n, int64 num, uint64 den) { scoped_mpff a(*this), b(*this); set(a, num); set(b, den); div(a, b, n); SASSERT(check(n)); } void mpff_manager::set(mpff & n, mpff const & v) { if (is_zero(v)) { reset(n); return; } if (&n == &v) return; allocate_if_needed(n); n.m_sign = v.m_sign; n.m_exponent = v.m_exponent; unsigned * s1 = sig(n); unsigned * s2 = sig(v); for (unsigned i = 0; i < m_precision; i++) s1[i] = s2[i]; SASSERT(check(n)); } template void mpff_manager::set_core(mpff & n, mpz_manager & m, mpz const & v) { TRACE("mpff", tout << "mpz->mpff\n"; m.display(tout, v); tout << "\n";); if (m.is_int64(v)) { TRACE("mpff", tout << "is_int64 " << m.get_int64(v) << "\n";); set(n, m.get_int64(v)); } else if (m.is_uint64(v)) { TRACE("mpff", tout << "is_uint64\n";); set(n, m.get_uint64(v)); } else { allocate_if_needed(n); svector & w = m_set_buffer; n.m_sign = m.decompose(v, w); while (w.size() < m_precision) { w.push_back(0); } TRACE("mpff", tout << "w words: "; for (unsigned i = 0; i < w.size(); i++) tout << w[i] << " "; tout << "\n";); unsigned w_sz = w.size(); SASSERT(w_sz >= m_precision); unsigned num_leading_zeros = nlz(w_sz, w.c_ptr()); shl(w_sz, w.c_ptr(), num_leading_zeros, w_sz, w.c_ptr()); unsigned * s = sig(n); unsigned i = m_precision; unsigned j = w_sz; while (i > 0) { --i; --j; s[i] = w[j]; } n.m_exponent = j * 8 * sizeof(unsigned) - static_cast(num_leading_zeros); if ((n.m_sign == 1) != m_to_plus_inf) { // must check whether it is precise or not while (j > 0) { --j; if (w[j] != 0) { // imprecision detected. inc_significand(n); } } // it is precise } } TRACE("mpff", tout << "mpz->mpff result:\n"; display_raw(tout, n); tout << "\n";); SASSERT(check(n)); } void mpff_manager::set(mpff & n, unsynch_mpz_manager & m, mpz const & v) { set_core(n, m, v); } void mpff_manager::set(mpff & n, synch_mpz_manager & m, mpz const & v) { set_core(n, m, v); } template void mpff_manager::set_core(mpff & n, mpq_manager & m, mpq const & v) { // TODO: improve precision? scoped_mpff num(*this), den(*this); set_core(num, m, v.numerator()); { flet l(m_to_plus_inf, !m_to_plus_inf); set_core(den, m, v.denominator()); } div(num, den, n); SASSERT(check(n)); } void mpff_manager::set(mpff & n, unsynch_mpq_manager & m, mpq const & v) { set_core(n, m, v); } void mpff_manager::set(mpff & n, synch_mpq_manager & m, mpq const & v) { set_core(n, m, v); } bool mpff_manager::eq(mpff const & a, mpff const & b) const { if (is_zero(a) && is_zero(b)) return true; if (is_zero(a) || is_zero(b)) return false; if (a.m_sign != b.m_sign || a.m_exponent != b.m_exponent) return false; unsigned * s1 = sig(a); unsigned * s2 = sig(b); for (unsigned i = 0; i < m_precision; i++) if (s1[i] != s2[i]) return false; return true; } bool mpff_manager::lt(mpff const & a, mpff const & b) const { STRACE("mpff_trace", tout << "[mpff] ("; display(tout, a); tout << " < "; display(tout, b); tout << ") == ";); if (is_zero(a)) { if (is_zero(b) || is_neg(b)) { STRACE("mpff_trace", tout << "(1 == 0)\n";); return false; } else { STRACE("mpff_trace", tout << "(1 == 1)\n";); SASSERT(is_pos(b)); return true; } } if (is_zero(b)) { SASSERT(!is_zero(a)); if (is_neg(a)) { STRACE("mpff_trace", tout << "(1 == 1)\n";); return true; } else { SASSERT(is_pos(a)); STRACE("mpff_trace", tout << "(1 == 0)\n";); return false; } } SASSERT(!is_zero(a)); SASSERT(!is_zero(b)); if (a.m_sign == 1) { if (b.m_sign == 0) { STRACE("mpff_trace", tout << "(1 == 1)\n";); return true; // neg < pos } // case: neg neg bool r = b.m_exponent < a.m_exponent || (a.m_exponent == b.m_exponent && ::lt(m_precision, sig(b), sig(a))); STRACE("mpff_trace", tout << "(" << r << " == 1)\n";); return r; } else { if (b.m_sign == 1) { STRACE("mpff_trace", tout << "(1 == 0)\n";); return false; // pos < neg } // case: pos pos bool r = a.m_exponent < b.m_exponent || (a.m_exponent == b.m_exponent && ::lt(m_precision, sig(a), sig(b))); STRACE("mpff_trace", tout << "(" << r << " == 1)\n";); return r; } } void mpff_manager::inc_significand(unsigned * s, int64 & exp) { if (!::inc(m_precision, s)) { SASSERT(::is_zero(m_precision, s)); s[m_precision - 1] = MIN_MSW; SASSERT(exp != INT64_MAX); exp++; } } void mpff_manager::inc_significand(mpff & a) { unsigned * s = sig(a); if (!::inc(m_precision, s)) { // Overflow happened, a was of the form 0xFFFF...FF // Now, it must be 0x000...000 SASSERT(::is_zero(m_precision, s)); // Set it to 0x80000...000, and increment exponent by 1. s[m_precision - 1] = MIN_MSW; if (a.m_exponent == INT_MAX) throw overflow_exception(); a.m_exponent++; } } void mpff_manager::dec_significand(mpff & a) { SASSERT(!is_minus_epsilon(a) && !is_zero(a) && !is_plus_epsilon(a)); unsigned * s = sig(a); for (unsigned i = 0; i < m_precision - 1; i++) { s[i]--; if (s[i] != UINT_MAX) return; } s[m_precision - 1]--; if (s[m_precision - 1] < MIN_MSW) { s[m_precision - 1] = UINT_MAX; a.m_exponent--; } } bool mpff_manager::min_significand(mpff const & a) const { unsigned * s = sig(a); return s[m_precision - 1] == MIN_MSW && ::is_zero(m_precision - 1, s); } bool mpff_manager::is_minus_epsilon(mpff const & a) const { return a.m_sign == 1 && a.m_exponent == INT_MIN && min_significand(a); } bool mpff_manager::is_plus_epsilon(mpff const & a) const { return a.m_sign == 0 && a.m_exponent == INT_MIN && min_significand(a); } void mpff_manager::set_min_significand(mpff & a) { // Since the most significand bit of the most significand word must be 1 in our representation, // we have that 0x8000..00 is the minimal significand unsigned * s = sig(a); s[m_precision - 1] = MIN_MSW; for (unsigned i = 0; i < m_precision - 1; i++) s[i] = 0; } void mpff_manager::set_max_significand(mpff & a) { SASSERT(!is_zero(a)); unsigned * s = sig(a); for (unsigned i = 0; i < m_precision; i++) s[i] = UINT_MAX; } void mpff_manager::set_plus_epsilon(mpff & n) { allocate_if_needed(n); n.m_sign = 0; n.m_exponent = INT_MIN; set_min_significand(n); SASSERT(check(n)); } void mpff_manager::set_minus_epsilon(mpff & n) { set_plus_epsilon(n); n.m_sign = 1; SASSERT(check(n)); } void mpff_manager::set_max(mpff & n) { allocate_if_needed(n); n.m_sign = 0; n.m_exponent = INT_MAX; set_max_significand(n); SASSERT(check(n)); } void mpff_manager::set_min(mpff & n) { set_max(n); n.m_sign = 1; SASSERT(check(n)); } void mpff_manager::next(mpff & a) { if (is_zero(a)) { set_plus_epsilon(a); } else if (is_minus_epsilon(a)) { reset(a); } else if (a.m_sign == 0) { inc_significand(a); } else { dec_significand(a); } SASSERT(check(a)); } void mpff_manager::prev(mpff & a) { if (is_zero(a)) { set_minus_epsilon(a); } else if (is_plus_epsilon(a)) { reset(a); } else if (a.m_sign == 0) { dec_significand(a); } else { inc_significand(a); } SASSERT(check(a)); } void mpff_manager::set_big_exponent(mpff & a, int64 e) { SASSERT(e > INT_MAX || e < INT_MIN); if (e > INT_MAX) { if (a.m_sign == 1) { if (m_to_plus_inf) set_min(a); else throw overflow_exception(); } else { if (m_to_plus_inf) throw overflow_exception(); else set_max(a); } } else { SASSERT(e < INT_MIN); if (a.m_sign == 1) { if (m_to_plus_inf) reset(a); else set_minus_epsilon(a); } else { if (m_to_plus_inf) set_plus_epsilon(a); else reset(a); } } } void mpff_manager::add_sub(bool is_sub, mpff const & a, mpff const & b, mpff & c) { if (is_zero(a)) { set(c, b); if (is_sub) neg(c); return; } if (is_zero(b)) { set(c, a); return; } TRACE("mpff", tout << (is_sub ? "sub" : "add") << "("; display(tout, a); tout << ", "; display(tout, b); tout << ")\n";); // Remark: any result returned by sig(...) may be invalid after a call to allocate_if_needed() // So, we must invoke allocate_if_needed(c) before we invoke sig(a) and sig(b). allocate_if_needed(c); bool sgn_a, sgn_b; int exp_a, exp_b; unsigned * sig_a, * sig_b; if (a.m_exponent >= b.m_exponent) { sgn_a = a.m_sign != 0; sgn_b = b.m_sign != 0; exp_a = a.m_exponent; exp_b = b.m_exponent; sig_a = sig(a); sig_b = sig(b); if (is_sub) sgn_b = !sgn_b; } else { sgn_a = b.m_sign != 0; sgn_b = a.m_sign != 0; exp_a = b.m_exponent; exp_b = a.m_exponent; sig_a = sig(b); sig_b = sig(a); if (is_sub) sgn_a = !sgn_a; } SASSERT(exp_a >= exp_b); unsigned * n_sig_b; // normalized sig_b // Make sure that a and b have the same exponent. if (exp_a > exp_b) { unsigned shift = (unsigned)exp_a - (unsigned)exp_b; n_sig_b = m_buffers[0].c_ptr(); shr(m_precision, sig_b, shift, m_precision, n_sig_b); if (sgn_b != m_to_plus_inf && has_one_at_first_k_bits(m_precision, sig_b, shift)) { // Precision was lost when normalizing the significand. // The current rounding mode forces us to bump the significand. // Remark: an overflow cannot happen when incrementing the significand. VERIFY(::inc(m_precision, n_sig_b)); } } else { SASSERT(exp_a == exp_b); n_sig_b = sig_b; } // Compute c if (sgn_a == sgn_b) { c.m_sign = sgn_a; unsigned * sig_r = m_buffers[1].c_ptr(); size_t r_sz; m_mpn_manager.add(sig_a, m_precision, n_sig_b, m_precision, sig_r, m_precision + 1, &r_sz); SASSERT(r_sz <= m_precision + 1); unsigned num_leading_zeros = nlz(m_precision + 1, sig_r); SASSERT(num_leading_zeros >= sizeof(unsigned) * 8 - 1); // num_leading_zeros >= 31 SASSERT(num_leading_zeros < sizeof(unsigned) * 8 * (m_precision + 1)); // result is not zero. unsigned * sig_c = sig(c); if (num_leading_zeros == sizeof(unsigned) * 8) { // no shift is needed c.m_exponent = exp_a; for (unsigned i = 0; i < m_precision; i++) sig_c[i] = sig_r[i]; } else if (num_leading_zeros == sizeof(unsigned) * 8 - 1) { // shift 1 right bool _inc_significand = ((c.m_sign == 1) != m_to_plus_inf) && has_one_at_first_k_bits(m_precision*2, sig_r, 1); int64 exp_c = exp_a; exp_c++; shr(m_precision + 1, sig_r, 1, m_precision, sig_c); if (_inc_significand) inc_significand(sig_c, exp_c); set_exponent(c, exp_c); } else { SASSERT(num_leading_zeros > sizeof(unsigned) * 8); num_leading_zeros -= sizeof(unsigned) * 8; // remove 1 word bits. // Now, we can assume sig_r has size m_precision SASSERT(num_leading_zeros > 0); // shift left num_leading_zeros int64 exp_c = exp_a; exp_c -= num_leading_zeros; shl(m_precision, sig_r, num_leading_zeros, m_precision, sig_c); set_exponent(c, exp_c); } } else { unsigned borrow; SASSERT(sgn_a != sgn_b); unsigned * sig_c = sig(c); if (::lt(m_precision, sig_a, n_sig_b)) { c.m_sign = sgn_b; m_mpn_manager.sub(n_sig_b, m_precision, sig_a, m_precision, sig_c, &borrow); } else { c.m_sign = sgn_a; m_mpn_manager.sub(sig_a, m_precision, n_sig_b, m_precision, sig_c, &borrow); } SASSERT(borrow == 0); unsigned num_leading_zeros = nlz(m_precision, sig_c); if (num_leading_zeros == m_precision_bits) { reset(c); } else if (num_leading_zeros > 0) { int64 exp_c = exp_a; exp_c -= num_leading_zeros; shl(m_precision, sig_c, num_leading_zeros, m_precision, sig_c); set_exponent(c, exp_c); } else { SASSERT(num_leading_zeros == 0); c.m_exponent = exp_a; } } TRACE("mpff", tout << "result: "; display(tout, c); tout << "\n";); SASSERT(check(c)); } void mpff_manager::add(mpff const & a, mpff const & b, mpff & c) { STRACE("mpff_trace", tout << "[mpff] "; display(tout, a); tout << " + "; display(tout, b); tout << " " << (m_to_plus_inf ? "<=" : ">=") << " ";); add_sub(false, a, b, c); STRACE("mpff_trace", display(tout, c); tout << "\n";); } void mpff_manager::sub(mpff const & a, mpff const & b, mpff & c) { STRACE("mpff_trace", tout << "[mpff] "; display(tout, a); tout << " - "; display(tout, b); tout << " " << (m_to_plus_inf ? "<=" : ">=") << " ";); add_sub(true, a, b, c); STRACE("mpff_trace", display(tout, c); tout << "\n";); } void mpff_manager::mul(mpff const & a, mpff const & b, mpff & c) { STRACE("mpff_trace", tout << "[mpff] ("; display(tout, a); tout << ") * ("; display(tout, b); tout << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";); if (is_zero(a) || is_zero(b)) { reset(c); } else { allocate_if_needed(c); TRACE("mpff", tout << "mul("; display(tout, a); tout << ", "; display(tout, b); tout << ")\n";); c.m_sign = a.m_sign ^ b.m_sign; // use int64 to make sure we do not have overflows int64 exp_a = a.m_exponent; int64 exp_b = b.m_exponent; int64 exp_c = exp_a + exp_b; // store result in m_buffers[0] unsigned * r = m_buffers[0].c_ptr(); m_mpn_manager.mul(sig(a), m_precision, sig(b), m_precision, r); // r has 2*m_precision_bits bits unsigned num_leading_zeros = nlz(m_precision*2, r); SASSERT(num_leading_zeros <= m_precision_bits); TRACE("mpff", tout << "num_leading_zeros: " << num_leading_zeros << "\n";); // We must shift right (m_precision_bits - num_leading_zeros) // If r does not have a 1 bit in the first (m_precision_bits - num_leading_zeros), then the result is precise. unsigned shift = m_precision_bits - num_leading_zeros; // Imprecision == "lost digits" // If c.m_sign --> result became bigger (e.g., -3.1 --> -3) // If !c.m_sign --> result became smaller (e.g., 3.1 --> 3) // Thus, when we are imprecise, we only need to bump the significand when: // 1) !c.m_sign && m_to_plus_inf // 2) c.m_sign && !m_to_plus_inf bool _inc_significand = ((c.m_sign == 1) != m_to_plus_inf) && has_one_at_first_k_bits(m_precision*2, r, shift); TRACE("mpff", tout << "c.m_sign: " << c.m_sign << ", m_to_plus_inf: " << m_to_plus_inf << "\nhas_one_at_first_k_bits: " << has_one_at_first_k_bits(m_precision*2, r, shift) << "\n"; tout << "_inc_significand: " << _inc_significand << "\n";); exp_c += shift; unsigned * s_c = sig(c); shr(m_precision*2, r, shift, m_precision, s_c); if (_inc_significand) inc_significand(s_c, exp_c); set_exponent(c, exp_c); TRACE("mpff", tout << "result: "; display(tout, c); tout << "\n";); SASSERT(check(c)); } STRACE("mpff_trace", display(tout, c); tout << "\n";); } void mpff_manager::div(mpff const & a, mpff const & b, mpff & c) { if (is_zero(b)) throw div0_exception(); STRACE("mpff_trace", tout << "[mpff] ("; display(tout, a); tout << ") / ("; display(tout, b); tout << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";); if (is_zero(a)) { reset(c); } #if 1 else if (is_two(b)) { set(c, a); int64 exp_c = a.m_exponent; exp_c--; set_exponent(c, exp_c); } #endif else { allocate_if_needed(c); c.m_sign = a.m_sign ^ b.m_sign; // use int64 to make sure we do not have overflows int64 exp_a = a.m_exponent; int64 exp_b = b.m_exponent; int64 exp_c = exp_a - exp_b; exp_c -= m_precision_bits; // we will multiplying (shifting) a by 2^m_precision_bits. // copy a to buffer 0, and shift by m_precision_bits to_buffer_shifting(0, a); unsigned * _a = m_buffers[0].c_ptr(); unsigned * q = m_buffers[1].c_ptr(); unsigned q_sz = m_precision + 1; // 2*m_precision - m_precision + 1 unsigned * r = m_buffers[2].c_ptr(); unsigned r_sz = m_precision; SASSERT(!::is_zero(2*m_precision, _a)); SASSERT(!::is_zero(m_precision, sig(b))); SASSERT(nlz(2*m_precision, _a) == 0); // Thus it is always the case that _a > b since size(a) = 2*size(b) // Actually, a is much bigger than b. // b is at most 2^m_precision_bits - 1 // a is at least 2^(2*m_precision_bits - 1) // Thus the quotient of a/b cannot be zero // Actually, quotient of a/b must be >= 2^(2*m_precision_bits - 1)/(2^m_precision_bits - 1) m_mpn_manager.div(_a, 2 * m_precision, sig(b), m_precision, q, r); TRACE("mpff_div", unsigned j = q_sz; while (j > 0) { --j; tout << std::hex << std::setfill('0') << std::setw(2*sizeof(unsigned)) << q[j]; tout << " "; } tout << std::dec << "\n";); SASSERT(!::is_zero(q_sz, q)); unsigned num_leading_zeros = nlz(q_sz, q); SASSERT(num_leading_zeros < q_sz * 8 * sizeof(unsigned)); unsigned q_bits = q_sz * 8 * sizeof(unsigned); SASSERT(num_leading_zeros < q_bits); unsigned actual_q_bits = q_bits - num_leading_zeros; bool _inc_significand; unsigned * s_c = sig(c); if (actual_q_bits > m_precision_bits) { unsigned shift = actual_q_bits - m_precision_bits; // We are imprecise if the remainder is != 0 or if we lost a bit when shifting. // See comment in mul(...) _inc_significand = ((c.m_sign == 1) != m_to_plus_inf) && (has_one_at_first_k_bits(q_sz, q, shift) || !::is_zero(r_sz, r)); exp_c += shift; shr(q_sz, q, shift, m_precision, s_c); } else { // We are imprecise only if the remainder is != 0 _inc_significand = ((c.m_sign == 1) != m_to_plus_inf) && !::is_zero(r_sz, r); if (actual_q_bits < m_precision_bits) { unsigned shift = m_precision_bits - actual_q_bits; exp_c -= shift; shl(q_sz, q, shift, m_precision, s_c); } else { SASSERT(actual_q_bits == m_precision_bits); ::copy(q_sz, q, m_precision, s_c); } } if (_inc_significand) inc_significand(s_c, exp_c); set_exponent(c, exp_c); SASSERT(check(c)); } STRACE("mpff_trace", display(tout, c); tout << "\n";); } void mpff_manager::floor(mpff & n) { if (n.m_exponent >= 0) return; STRACE("mpff_trace", tout << "[mpff] Floor["; display(tout, n); tout << "] == ";); if (n.m_exponent <= -static_cast(m_precision_bits)) { // number is between (-1, 1) if (n.m_sign == 0) reset(n); else set(n, -1); } else { unsigned * s = sig(n); if (n.m_sign == 1 && has_one_at_first_k_bits(m_precision, s, -n.m_exponent)) { shr(m_precision, s, -n.m_exponent, m_precision, s); VERIFY(::inc(m_precision, s)); int num_leading_zeros = nlz(m_precision, s); SASSERT(num_leading_zeros == -n.m_exponent || num_leading_zeros == -n.m_exponent - 1); if (num_leading_zeros != -n.m_exponent) { shl(m_precision, s, -n.m_exponent - 1, m_precision, s); n.m_exponent++; } else { shl(m_precision, s, -n.m_exponent, m_precision, s) ; } } else { // reset first n.m_exponent bits shr(m_precision, s, -n.m_exponent, m_precision, s); shl(m_precision, s, -n.m_exponent, m_precision, s); } } SASSERT(check(n)); STRACE("mpff_trace", display(tout, n); tout << "\n";); } void mpff_manager::ceil(mpff & n) { if (n.m_exponent >= 0) return; STRACE("mpff_trace", tout << "[mpff] Ceiling["; display(tout, n); tout << "] == ";); if (n.m_exponent <= -static_cast(m_precision_bits)) { // number is between (-1, 1) if (n.m_sign == 0) set(n, 1); else reset(n); } else { unsigned * s = sig(n); if (n.m_sign == 0 && has_one_at_first_k_bits(m_precision, s, -n.m_exponent)) { shr(m_precision, s, -n.m_exponent, m_precision, s); VERIFY(::inc(m_precision, s)); int num_leading_zeros = nlz(m_precision, s); SASSERT(num_leading_zeros == -n.m_exponent || num_leading_zeros == -n.m_exponent - 1); if (num_leading_zeros != -n.m_exponent) { shl(m_precision, s, -n.m_exponent - 1, m_precision, s); n.m_exponent++; } else { shl(m_precision, s, -n.m_exponent, m_precision, s) ; } } else { // reset first n.m_exponent bits shr(m_precision, s, -n.m_exponent, m_precision, s); shl(m_precision, s, -n.m_exponent, m_precision, s); } } SASSERT(check(n)); STRACE("mpff_trace", display(tout, n); tout << "\n";); } void mpff_manager::power(mpff const & a, unsigned p, mpff & b) { #ifdef _TRACE scoped_mpff _a(*this); _a = a; unsigned _p = p; #endif #define SMALL_POWER 8 SASSERT(check(a)); if (is_zero(a)) { SASSERT(p != 0); reset(b); } else if (p == 0) { set(b, 1); } else if (p == 1) { set(b, a); } else if (p == 2) { mul(a, a, b); } else if (p <= SMALL_POWER && &a != &b) { SASSERT(p > 2); --p; set(b, a); while (p > 0) { --p; mul(a, b, b); } } else { unsigned * s = sig(a); if (s[m_precision - 1] == 0x80000000u && ::is_zero(m_precision - 1, s)) { allocate_if_needed(b); if (p % 2 == 0) b.m_sign = 0; else b.m_sign = a.m_sign; int64 exp = a.m_exponent; exp *= p; if (exp > INT_MAX || exp < INT_MIN) throw overflow_exception(); exp += (m_precision_bits - 1)*(p - 1); if (exp > INT_MAX || exp < INT_MIN) throw overflow_exception(); unsigned * r = sig(b); r[m_precision - 1] = 0x80000000u; for (unsigned i = 0; i < m_precision - 1; i++) r[i] = 0; b.m_exponent = static_cast(exp); } else { unsigned mask = 1; scoped_mpff pw(*this); set(pw, a); set(b, 1); while (mask <= p) { if (mask & p) mul(b, pw, b); mul(pw, pw, pw); mask = mask << 1; } } } STRACE("mpff_trace", tout << "[mpff] ("; display(tout, _a); tout << ") ^ " << _p << (m_to_plus_inf ? "<=" : ">="); display(tout, b); tout << "\n";); TRACE("mpff_power", display_raw(tout, b); tout << "\n";); SASSERT(check(b)); } bool mpff_manager::is_power_of_two(mpff const & a, unsigned & k) const { if (!is_power_of_two(a)) return false; int64 exp = a.m_exponent + m_precision_bits - 1; SASSERT(exp >= 0); k = static_cast(exp); return true; } bool mpff_manager::is_power_of_two(mpff const & a) const { unsigned * s = sig(a); return is_pos(a) && a.m_exponent > -static_cast(m_precision_bits) && s[m_precision - 1] == 0x80000000u && ::is_zero(m_precision - 1, s); } template void mpff_manager::significand_core(mpff const & n, mpz_manager & m, mpz & t) { m.set(t, m_precision, sig(n)); } void mpff_manager::significand(mpff const & n, unsynch_mpz_manager & m, mpz & t) { significand_core(n, m, t); } void mpff_manager::significand(mpff const & n, synch_mpz_manager & m, mpz & t) { significand_core(n, m, t); } template void mpff_manager::to_mpz_core(mpff const & n, mpz_manager & m, mpz & t) { SASSERT(is_int(n)); int exp = n.m_exponent; if (exp < 0) { SASSERT(exp > -static_cast(m_precision_bits)); to_buffer(0, n); unsigned * b = m_buffers[0].c_ptr(); shr(m_precision, b, -exp, m_precision, b); m.set(t, m_precision, b); } else { m.set(t, m_precision, sig(n)); if (exp > 0) { _scoped_numeral > p(m); m.set(p, 2); m.power(p, exp, p); m.mul(t, p, t); } } if (is_neg(n)) m.neg(t); } void mpff_manager::to_mpz(mpff const & n, unsynch_mpz_manager & m, mpz & t) { to_mpz_core(n, m, t); } void mpff_manager::to_mpz(mpff const & n, synch_mpz_manager & m, mpz & t) { to_mpz_core(n, m, t); } template void mpff_manager::to_mpq_core(mpff const & n, mpq_manager & m, mpq & t) { int exp = n.m_exponent; TRACE("mpff_to_mpq", tout << "to_mpq: "; display(tout, n); tout << "\nexp: " << exp << "\n";); if (exp < 0 && exp > -static_cast(m_precision_bits) && !has_one_at_first_k_bits(m_precision, sig(n), -n.m_exponent)) { to_buffer(0, n); unsigned * b = m_buffers[0].c_ptr(); shr(m_precision, b, -exp, m_precision, b); m.set(t, m_precision, b); } else { m.set(t, m_precision, sig(n)); if (exp != 0) { _scoped_numeral > p(m); m.set(p, 2); unsigned abs_exp; if (exp < 0) { // Avoid -INT_MIN == INT_MIN issue. It is not really useful, since we will run out of memory anyway. if (exp == INT_MIN) abs_exp = static_cast(-static_cast(INT_MIN)); else abs_exp = -exp; } else { abs_exp = exp; } m.power(p, abs_exp, p); if (exp < 0) m.div(t, p, t); else m.mul(t, p, t); } } if (is_neg(n)) m.neg(t); } void mpff_manager::to_mpq(mpff const & n, unsynch_mpq_manager & m, mpq & t) { to_mpq_core(n, m, t); } void mpff_manager::to_mpq(mpff const & n, synch_mpq_manager & m, mpq & t) { to_mpq_core(n, m, t); } void mpff_manager::display_raw(std::ostream & out, mpff const & n) const { if (is_neg(n)) out << "-"; unsigned * s = sig(n); unsigned i = m_precision; while(i > 0) { --i; out << std::hex << std::setfill('0') << std::setw(2 * sizeof(unsigned)) << s[i]; } out << "*2^" << std::dec << n.m_exponent; } void mpff_manager::display(std::ostream & out, mpff const & n) const { if (is_neg(n)) out << "-"; to_buffer_ext(0, n); svector & u_buffer = const_cast(this)->m_buffers[0]; int num_trailing_zeros = ntz(m_precision, u_buffer.c_ptr()); int shift = 0; int64 exp = n.m_exponent; // use int64 to avoid -INT_MIN == INT_MIN issue if (exp < 0) { if (num_trailing_zeros >= -exp) { shift = static_cast(-exp); exp = 0; } else { shift = num_trailing_zeros; exp += num_trailing_zeros; } } if (shift > 0) shr(m_precision, u_buffer.c_ptr(), shift, u_buffer.c_ptr()); sbuffer str_buffer(11*m_precision, 0); out << m_mpn_manager.to_string(u_buffer.c_ptr(), m_precision, str_buffer.begin(), str_buffer.size()); if (exp > 0) { if (exp <= 63) { uint64 _exp = 1; _exp <<= exp; out << "*" << _exp; } else { out << "*2"; if (exp > 1) { out << "^"; out << exp; } } } else if (exp < 0) { exp = -exp; if (exp <= 63) { uint64 _exp = 1; _exp <<= exp; out << "/" << _exp; } else { out << "/2"; if (exp > 1) { out << "^"; out << exp; } } } } void mpff_manager::display_decimal(std::ostream & out, mpff const & n, unsigned prec, unsigned min_exponent) { // The result in scientific notation when n.m_exponent >= min_exponent or n.m_exponent <= - min_exponent - m_precision_bits int64 exp = n.m_exponent; if (exp >= min_exponent || exp <= -static_cast(min_exponent) - m_precision_bits || is_int(n)) { display(out, n); return; } if (is_neg(n)) out << "-"; unsigned word_sz = 8 * sizeof(unsigned); if (exp >= 0) { sbuffer buffer; unsigned num_extra_words = 1 + static_cast(exp/word_sz); unsigned shift = word_sz - exp%word_sz; for (unsigned i = 0; i < num_extra_words; i++) buffer.push_back(0); unsigned * s = sig(n); for (unsigned i = 0; i < m_precision; i++) buffer.push_back(s[i]); shr(buffer.size(), buffer.c_ptr(), shift, buffer.size(), buffer.c_ptr()); sbuffer str_buffer(11*buffer.size(), 0); out << m_mpn_manager.to_string(buffer.c_ptr(), buffer.size(), str_buffer.begin(), str_buffer.size()); } else { sbuffer buffer1, buffer2; sbuffer buffer3; exp = -exp; unsigned num_words = 1 + static_cast(exp/word_sz); unsigned num_extra_words = m_precision < num_words ? num_words - m_precision : 0; num_extra_words++; unsigned * s = sig(n); for (unsigned i = 0; i < m_precision; i++) { buffer1.push_back(s[i]); buffer2.push_back(0); buffer3.push_back(0); } for (unsigned i = 0; i < num_extra_words; i++) { buffer1.push_back(0); buffer2.push_back(0); } unsigned ten = 10; sbuffer pw_buffer; pw_buffer.resize(num_words, 0); pw_buffer[0] = 1; shl(num_words, pw_buffer.c_ptr(), static_cast(exp), num_words, pw_buffer.c_ptr()); if (num_words > m_precision) { out << "0"; } else { m_mpn_manager.div(buffer1.c_ptr(), m_precision, pw_buffer.c_ptr(), num_words, buffer3.c_ptr(), buffer2.c_ptr()); sbuffer str_buffer(11*buffer3.size(), 0); out << m_mpn_manager.to_string(buffer3.c_ptr(), buffer3.size(), str_buffer.begin(), str_buffer.size()); SASSERT(!::is_zero(buffer2.size(), buffer2.c_ptr())); // otherwise n is an integer ::copy(buffer2.size(), buffer2.c_ptr(), buffer1.size(), buffer1.c_ptr()); } out << "."; // buffer1 contain the fractional part unsigned i = 0; unsigned sz1 = buffer1.size(); while (sz1 > 0 && buffer1[sz1-1] == 0) --sz1; SASSERT(sz1 > 0); // otherwise the number is an integer while (sz1 > 0) { if (i >= prec) { out << "?"; return; } i = i + 1; m_mpn_manager.mul(buffer1.c_ptr(), sz1, &ten, 1, buffer2.c_ptr()); unsigned sz2 = sz1 + 1; while (sz2 > 0 && buffer2[sz2-1] == 0) --sz2; SASSERT(sz2 > 0); if (num_words > sz2) { out << "0"; sz1 = sz2; ::copy(sz2, buffer2.c_ptr(), sz1, buffer1.c_ptr()); SASSERT(sz1 == 0 || !::is_zero(sz1, buffer1.c_ptr())); } else { m_mpn_manager.div(buffer2.c_ptr(), sz2, pw_buffer.c_ptr(), num_words, buffer3.c_ptr(), buffer1.c_ptr()); out << buffer3[0]; sz1 = num_words; while (sz1 > 0 && buffer1[sz1-1] == 0) --sz1; SASSERT(sz1 == 0 || !::is_zero(sz1, buffer1.c_ptr())); } } } } void mpff_manager::display_smt2(std::ostream & out, mpff const & n, bool decimal) const { if (is_neg(n)) out << "(- "; to_buffer_ext(0, n); svector & u_buffer = const_cast(this)->m_buffers[0]; int num_trailing_zeros = ntz(m_precision, u_buffer.c_ptr()); int shift = 0; int64 exp = n.m_exponent; if (exp < 0) { if (num_trailing_zeros >= -exp) { shift = static_cast(-exp); exp = 0; } else { shift = num_trailing_zeros; exp += num_trailing_zeros; } } if (shift > 0) shr(m_precision, u_buffer.c_ptr(), shift, u_buffer.c_ptr()); if (exp > 0) out << "(* "; else if (exp < 0) out << "(/ "; sbuffer str_buffer(11*m_precision, 0); out << m_mpn_manager.to_string(u_buffer.c_ptr(), m_precision, str_buffer.begin(), str_buffer.size()); if (decimal) out << ".0"; if (exp != 0) { if (exp < 0) exp = -exp; if (exp <= 63) { uint64 _exp = 1; _exp <<= exp; out << _exp; if (decimal) out << ".0"; } else { out << " (^ 2"; if (decimal) out << ".0"; out << " " << exp; if (decimal) out << ".0"; out << ")"; } out << ")"; } if (is_neg(n)) out << ")"; } std::string mpff_manager::to_string(mpff const & a) const { std::ostringstream buffer; display(buffer, a); return buffer.str(); } std::string mpff_manager::to_rational_string(mpff const & a) const { // The exponent may be too big to encode without using 2^ return to_string(a); } unsigned mpff_manager::prev_power_of_two(mpff const & a) { if (!is_pos(a)) return 0; if (a.m_exponent <= -static_cast(m_precision_bits)) return 0; // Number is smaller than 1 SASSERT(static_cast(a.m_exponent) + static_cast(m_precision_bits) - 1 >= 0); SASSERT(static_cast(a.m_exponent) + static_cast(m_precision_bits) - 1 <= static_cast(static_cast(UINT_MAX))); return m_precision_bits + a.m_exponent - 1; } bool mpff_manager::check(mpff const & n) const { // n is zero or the most significand bit of the most significand word is 1. unsigned * s = sig(n); SASSERT(is_zero(n) || (s[m_precision - 1] & MIN_MSW) != 0); // if n is zero, then the sign must be 0 SASSERT(!is_zero(n) || n.m_sign == 0); // if n is zero, then all bits must be 0. SASSERT(!is_zero(n) || ::is_zero(m_precision, sig(n))); // if n is zero, then exponent must be 0. SASSERT(!is_zero(n) || n.m_exponent == 0); return true; } z3-z3-4.4.1/src/util/mpff.h000066400000000000000000000352131260446376700152760ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpff.h Abstract: Multi precision fast floating point numbers. The implementation is not compliant with the IEEE standard. For an IEEE compliant implementation, see mpf.h There are only two rounding modes: towards plus or minus inf. Author: Leonardo de Moura (leonardo) 2012-09-12 Revision History: --*/ #ifndef MPFF_H_ #define MPFF_H_ #include"id_gen.h" #include"util.h" #include"vector.h" #include"z3_exception.h" #include"scoped_numeral.h" #include"scoped_numeral_vector.h" #include"mpn.h" class mpff_manager; class mpff { friend class mpff_manager; unsigned m_sign:1; unsigned m_sig_idx:31; // position where the significand is stored in the mpff_manager. int m_exponent; public: mpff(): m_sign(0), m_sig_idx(0), m_exponent(0) { } void swap(mpff & other) { unsigned sign = m_sign; m_sign = other.m_sign; other.m_sign = sign; unsigned sig_idx = m_sig_idx; m_sig_idx = other.m_sig_idx; other.m_sig_idx = sig_idx; std::swap(m_exponent, other.m_exponent); } }; inline void swap(mpff & m1, mpff & m2) { m1.swap(m2); } class mpz; class mpq; template class mpz_manager; template class mpq_manager; typedef mpz_manager synch_mpz_manager; typedef mpz_manager unsynch_mpz_manager; typedef mpq_manager synch_mpq_manager; typedef mpq_manager unsynch_mpq_manager; class mpff_manager { // Some restrictions on mpff numbers // // - The exponent is always a machine integer. The main point is that 2^(2^31) is a huge number, // we will not even be able to convert the mpff into mpq. Formulas that need this kind of huge number // are usually out-of-reach for Z3. // // - The significand size is measured in words of 32-bit. The number of words is always even. // This decision makes sure that the size (in bits) of mpff numbers is always a multiple of 64. // Thus mpff objs can be easily packed in 64-bit machines. // // - The smallest mpff numeral has 128-bits total. mpff structure has always 64-bits. // The minimal size for the significand is 64-bits. // // - All mpff numerals in a given manager use the same number of words for storing the significand. // This is different from the mpf_manager where the same manager can be used to manipulate floating point numbers // of different precision. // // - In the encoding used for mpff numbers, the most significand bit of the most significand word is always 1. // The only exception is the number zero. // For example, assuming we are using 64-bits for the significand, the number 1 is encoded as // (sign = 0, significand = 0x800..0, exponent = -63) // Note that, in this representation, the smallest positive integer is: // (sign = 0, significand = 0x800..0, exponent = INT_MIN) // instead of // (sign = 0, significand = 0x000..1, exponent = INT_MIN) // // Remarks: // // - All values of type int, unsigned, int64 and uint64 can be precisely represented as mpff numerals. // // - Hardware float and double values (corresponding to rationals) can also be precisely represented as mpff numberals. // That is, NaN, +oo and -oo are not supported by this module. // // - An exception (mpff_manager::exception) is thrown if overflow occurs. This can happen because the exponent is // represented as a machine integer. // // - There are only two rounding modes: towards plus infinity and towards minus infinity. // The rounding mode can be dynamically modified. // // - The mpff numerals are stored in a dynamic array. // Type mpff is just an index (unsigned) into this array. unsigned m_precision; //!< Number of words in the significand. Must be an even number. unsigned m_precision_bits; //!< Number of bits in the significand. Must be 32*m_precision. unsigned_vector m_significands; //!< Array containing all significands. unsigned m_capacity; //!< Number of significands that can be stored in m_significands. bool m_to_plus_inf; //!< If True, then round to plus infinity, otherwise to minus infinity id_gen m_id_gen; static const unsigned MPFF_NUM_BUFFERS = 4; svector m_buffers[MPFF_NUM_BUFFERS]; svector m_set_buffer; mpff m_one; mpn_manager m_mpn_manager; unsigned * sig(mpff const & n) const { return m_significands.c_ptr() + (n.m_sig_idx * m_precision); } void ensure_capacity(unsigned sig_idx) { while (sig_idx >= m_capacity) expand(); } void expand(); void allocate_if_needed(mpff & n) { if (n.m_sig_idx == 0) allocate(n); } void allocate(mpff & n); // copy n to buffer idx. void to_buffer(unsigned idx, mpff const & n) const; // copy n to buffer idx and add m_precision zeros. void to_buffer_ext(unsigned idx, mpff const & n) const; // copy (and shift by m_precision_bits) n to buffer idx void to_buffer_shifting(unsigned idx, mpff const & n) const; void inc_significand(unsigned * s, int64 & exp); void inc_significand(mpff & a); void dec_significand(mpff & a); bool min_significand(mpff const & a) const; void set_min_significand(mpff & a); void set_max_significand(mpff & a); void set_big_exponent(mpff & a, int64 e); void set_exponent(mpff & a, int64 e) { if (e > INT_MAX || e < INT_MIN) set_big_exponent(a, e); else a.m_exponent = static_cast(e); } template void set_core(mpff & n, mpz_manager & m, mpz const & v); template void set_core(mpff & n, mpq_manager & m, mpq const & v); template void to_mpz_core(mpff const & n, mpz_manager & m, mpz & t); template void to_mpq_core(mpff const & n, mpq_manager & m, mpq & t); template void significand_core(mpff const & n, mpz_manager & m, mpz & r); void add_sub(bool is_sub, mpff const & a, mpff const & b, mpff & c); public: typedef mpff numeral; static bool precise() { return false; } static bool field() { return true; } class exception : public z3_exception { virtual char const * msg() const { return "multi-precision floating point (mpff) exception"; } }; class overflow_exception : public exception { virtual char const * msg() const { return "multi-precision floating point (mpff) overflow"; } }; class div0_exception : public exception { virtual char const * msg() const { return "multi-precision floating point (mpff) division by zero"; } }; mpff_manager(unsigned prec = 2, unsigned initial_capacity = 1024); ~mpff_manager(); void round_to_plus_inf() { m_to_plus_inf = true; } void round_to_minus_inf() { m_to_plus_inf = false; } void set_rounding(bool to_plus_inf) { m_to_plus_inf = to_plus_inf; } bool rounding_to_plus_inf() const { return m_to_plus_inf; } /** \brief Return the exponent of n. */ static int exponent(mpff const & n) { return n.m_exponent; } /** \brief Update the exponent of n. \remark It is a NOOP if n is zero. */ void set_exponent(mpff & n, int exp) { if (is_zero(n)) return; n.m_exponent = exp; SASSERT(check(n)); } /** \brief Return the significand as a mpz numeral. */ void significand(mpff const & n, unsynch_mpz_manager & m, mpz & r); void significand(mpff const & n, synch_mpz_manager & m, mpz & r); /** \brief Return true if n is negative */ static bool sign(mpff const & n) { return is_neg(n); } /** \brief Set n to zero. */ void reset(mpff & n); /** \brief Return true if n is an integer. */ bool is_int(mpff const & n) const; /** \brief Return true if n is zero. */ static bool is_zero(mpff const & n) { return n.m_sig_idx == 0; } /** \brief Return true if n is positive. */ static bool is_pos(mpff const & n) { return n.m_sign == 0 && !is_zero(n); } /** \brief Return true if n is negative. */ static bool is_neg(mpff const & n) { return n.m_sign != 0; } /** \brief Return true if n is non positive. */ static bool is_nonpos(mpff const & n) { return !is_pos(n); } /** \brief Return true if n is non negative. */ static bool is_nonneg(mpff const & n) { return !is_neg(n); } /** \brief Return true if the absolute value of n is 1. */ bool is_abs_one(mpff const & n) const; /** \brief Return true if n is one. */ bool is_one(mpff const & n) const { return is_pos(n) && is_abs_one(n); } /** \brief Return true if n is minus one. */ bool is_minus_one(mpff const & n) const { return is_neg(n) && is_abs_one(n); } /** \brief Return true if n is two. */ bool is_two(mpff const & n) const; /** \brief Return true if \c a is the smallest representable negative number. */ bool is_minus_epsilon(mpff const & a) const; /** \brief Return true if \c a is the smallest representable positive number. */ bool is_plus_epsilon(mpff const & a) const; /** \brief Return true if \c a is an integer and fits in an int64 machine integer. */ bool is_int64(mpff const & a) const; /** \brief Return true if \c a is a non-negative integer and fits in an int64 machine integer. */ bool is_uint64(mpff const & a) const; /** \brief Delete the resources associated with n. */ void del(mpff & n); /** \brief a <- -a */ static void neg(mpff & a) { if (!is_zero(a)) a.m_sign = !a.m_sign; } /** \brief a <- |a| */ static void abs(mpff & a) { a.m_sign = 0; } static void swap(mpff & a, mpff & b) { a.swap(b); } /** \brief c <- a + b */ void add(mpff const & a, mpff const & b, mpff & c); /** \brief c <- a - b */ void sub(mpff const & a, mpff const & b, mpff & c); /** \brief a <- a + 1 */ void inc(mpff & a) { add(a, m_one, a); } /** \brief a <- a - 1 */ void dec(mpff & a) { sub(a, m_one, a); } /** \brief c <- a * b */ void mul(mpff const & a, mpff const & b, mpff & c); /** \brief c <- a / b \pre !is_zero(b) */ void div(mpff const & a, mpff const & b, mpff & c); /** \brief a <- 1/a \pre !is_zero(a); */ void inv(mpff & a) { div(m_one, a, a); } void inv(mpff const & a, mpff & b) { set(b, a); inv(b); } /** \brief b <- a^k */ void power(mpff const & a, unsigned k, mpff & b); /** \brief Return true if \c a is a power of 2. That is, a is equal to 2^k for some k >= 0. */ bool is_power_of_two(mpff const & a, unsigned & k) const; bool is_power_of_two(mpff const & a) const; bool eq(mpff const & a, mpff const & b) const; bool neq(mpff const & a, mpff const & b) const { return !eq(a, b); } bool lt(mpff const & a, mpff const & b) const; bool gt(mpff const & a, mpff const & b) const { return lt(b, a); } bool le(mpff const & a, mpff const & b) const { return !lt(b, a); } bool ge(mpff const & a, mpff const & b) const { return !lt(a, b); } void set(mpff & n, int v); void set(mpff & n, unsigned v); void set(mpff & n, int64 v); void set(mpff & n, uint64 v); void set(mpff & n, int num, unsigned den); void set(mpff & n, int64 num, uint64 den); void set(mpff & n, mpff const & v); void set(mpff & n, unsynch_mpz_manager & m, mpz const & v); void set(mpff & n, synch_mpz_manager & m, mpz const & v); void set(mpff & n, unsynch_mpq_manager & m, mpq const & v); void set(mpff & n, synch_mpq_manager & m, mpq const & v); void set_plus_epsilon(mpff & n); void set_minus_epsilon(mpff & n); void set_max(mpff & n); void set_min(mpff & n); /** \brief n <- floor(n) */ void floor(mpff & n); void floor(mpff const & n, mpff & o) { set(o, n); floor(o); } /** \brief n <- ceil(n) */ void ceil(mpff & n); void ceil(mpff const & n, mpff & o) { set(o, n); ceil(o); } /** \brief Update \c a to the next representable float. Throws an exception if \c a is the maximal representable float. */ void next(mpff & a); /** \brief Update \c a to the previous representable float. Throws an exception if \c a is the minimal representable float. */ void prev(mpff & a); /** \brief Convert n into a mpz numeral. \pre is_int(n) \remark if exponent(n) is too big, we may run out of memory. */ void to_mpz(mpff const & n, unsynch_mpz_manager & m, mpz & t); /** \brief Convert n into a mpz numeral. \pre is_int(n) \remark if exponent(n) is too big, we may run out of memory. */ void to_mpz(mpff const & n, synch_mpz_manager & m, mpz & t); /** \brief Convert n into a mpq numeral. \remark if exponent(n) is too big, we may run out of memory. */ void to_mpq(mpff const & n, unsynch_mpq_manager & m, mpq & t); /** \brief Convert n into a mpq numeral. \remark if exponent(n) is too big, we may run out of memory. */ void to_mpq(mpff const & n, synch_mpq_manager & m, mpq & t); /** \brief Return n as an int64. \pre is_int64(n) */ int64 get_int64(mpff const & n) const; /** \brief Return n as an uint64. \pre is_uint64(n) */ uint64 get_uint64(mpff const & n) const; /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpff const & a); void display_raw(std::ostream & out, mpff const & n) const; void display(std::ostream & out, mpff const & n) const; void display_pp(std::ostream & out, mpff const & n) const { display(out, n); } void display_decimal(std::ostream & out, mpff const & n, unsigned prec=32, unsigned max_power=128); void display_smt2(std::ostream & out, mpff const & n, bool decimal=true) const; std::string to_string(mpff const & a) const; std::string to_rational_string(mpff const & a) const; bool check(mpff const & n) const; }; typedef _scoped_numeral scoped_mpff; typedef _scoped_numeral_vector scoped_mpff_vector; #endif z3-z3-4.4.1/src/util/mpfx.cpp000066400000000000000000000576031260446376700156620ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpfx.h Abstract: Multi precision fixed point numbers. Author: Leonardo de Moura (leonardo) 2012-09-19 Revision History: --*/ #include #include #include"mpfx.h" #include"mpn.h" #include"mpz.h" #include"mpq.h" #include"bit_util.h" #include"trace.h" mpfx_manager::mpfx_manager(unsigned int_sz, unsigned frac_sz, unsigned initial_capacity) { SASSERT(initial_capacity > 0); SASSERT(int_sz > 0); SASSERT(frac_sz > 0); m_int_part_sz = int_sz; m_frac_part_sz = frac_sz; m_total_sz = m_int_part_sz + m_frac_part_sz; m_words.resize(initial_capacity * m_total_sz, 0); m_capacity = initial_capacity; m_to_plus_inf = false; m_buffer0.resize(2*m_total_sz, 0); m_buffer1.resize(2*m_total_sz, 0); m_buffer2.resize(2*m_total_sz, 0); VERIFY(m_id_gen.mk() == 0); set(m_one, 1); } mpfx_manager::~mpfx_manager() { del(m_one); } void mpfx_manager::expand() { m_capacity = 2*m_capacity; m_words.resize(m_capacity * m_total_sz, 0); } void mpfx_manager::allocate(mpfx & n) { SASSERT(n.m_sig_idx == 0); unsigned sig_idx = m_id_gen.mk(); ensure_capacity(sig_idx); n.m_sig_idx = sig_idx; SASSERT(::is_zero(m_total_sz, words(n))); } unsigned mpfx_manager::sz(unsigned * ws) const { SASSERT(!::is_zero(m_total_sz, ws)); unsigned r = m_total_sz; while (true) { SASSERT(r > 0); --r; if (ws[r] != 0) return r + 1; } } void mpfx_manager::del(mpfx & n) { unsigned sig_idx = n.m_sig_idx; if (sig_idx != 0) { m_id_gen.recycle(sig_idx); unsigned * w = words(n); for (unsigned i = 0; i < m_total_sz; i++) w[i] = 0; } } void mpfx_manager::reset(mpfx & n) { del(n); n.m_sign = false; n.m_sig_idx = 0; SASSERT(check(n)); } bool mpfx_manager::is_int(mpfx const & n) const { unsigned * w = words(n); for (unsigned i = 0; i < m_frac_part_sz; i++) if (w[i] != 0) return false; return true; } bool mpfx_manager::is_abs_one(mpfx const & n) const { unsigned * w = words(n); return is_int(n) && w[m_frac_part_sz] == 1 && ::is_zero(m_int_part_sz - 1, w + m_frac_part_sz + 1); } bool mpfx_manager::is_int64(mpfx const & a) const { if (!is_int(a)) return false; if (is_zero(a) || m_int_part_sz <= 1) return true; unsigned * w = words(a); w += m_frac_part_sz; if (w[1] < 0x80000000u || (w[1] == 0x80000000u && is_neg(a))) { for (unsigned i = 2; i < m_int_part_sz; i++) if (w[i] != 0) return false; return true; } else { return false; } } bool mpfx_manager::is_uint64(mpfx const & a) const { if (!is_int(a) || is_neg(a)) return false; if (is_zero(a) || m_int_part_sz <= 2) return true; unsigned * w = words(a); for (unsigned i = m_frac_part_sz + 2; i < m_total_sz; i++) if (w[i] != 0) return false; return true; } void mpfx_manager::set(mpfx & n, int v) { if (v == 0) { reset(n); } else { if (v < 0) { set(n, static_cast(-v)); n.m_sign = 1; } else { set(n, static_cast(v)); } } SASSERT(get_int64(n) == v); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, unsigned v) { if (v == 0) { reset(n); } else { allocate_if_needed(n); n.m_sign = 0; unsigned * w = words(n); for (unsigned i = 0; i < m_total_sz; i++) w[i] = 0; w[m_frac_part_sz] = v; } SASSERT(is_int(n)); SASSERT(get_uint64(n) == v); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, int64 v) { if (m_int_part_sz == 1) { if (v < -static_cast(static_cast(UINT_MAX)) || v > static_cast(static_cast(UINT_MAX))) throw overflow_exception(); } if (v == 0) { reset(n); } else { if (v < 0) { set(n, static_cast(-v)); n.m_sign = 1; } else { set(n, static_cast(v)); } } SASSERT(is_int(n)); SASSERT(get_int64(n) == v); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, uint64 v) { if (m_int_part_sz == 1) { if (v > static_cast(UINT_MAX)) throw overflow_exception(); } if (v == 0) { reset(n); } else { allocate_if_needed(n); n.m_sign = 0; unsigned * w = words(n); uint64 * _vp = &v; unsigned * _v = 0; memcpy(&_v, &_vp, sizeof(unsigned*)); for (unsigned i = 0; i < m_total_sz; i++) w[i] = 0; w[m_frac_part_sz] = _v[0]; if (m_int_part_sz == 1) { SASSERT(_v[1] == 0); } else { w[m_frac_part_sz+1] = _v[1]; } } SASSERT(is_int(n)); SASSERT(get_uint64(n) == v); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, int num, unsigned den) { scoped_mpfx a(*this), b(*this); set(a, num); set(b, den); div(a, b, n); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, int64 num, uint64 den) { scoped_mpfx a(*this), b(*this); set(a, num); set(b, den); div(a, b, n); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, mpfx const & v) { if (is_zero(v)) { reset(n); return; } allocate_if_needed(n); n.m_sign = v.m_sign; unsigned * w1 = words(n); unsigned * w2 = words(v); for (unsigned i = 0; i < m_total_sz; i++) w1[i] = w2[i]; SASSERT(check(n)); } template void mpfx_manager::set_core(mpfx & n, mpz_manager & m, mpz const & v) { if (m.is_zero(v)) { reset(n); } else { m_tmp_digits.reset(); allocate_if_needed(n); n.m_sign = m.decompose(v, m_tmp_digits); unsigned sz = m_tmp_digits.size(); if (sz > m_int_part_sz) throw overflow_exception(); unsigned * w = words(n); for (unsigned i = 0; i < m_frac_part_sz; i++) w[i] = 0; ::copy(sz, m_tmp_digits.c_ptr(), m_int_part_sz, w + m_frac_part_sz); } SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, unsynch_mpz_manager & m, mpz const & v) { set_core(n, m, v); } void mpfx_manager::set(mpfx & n, synch_mpz_manager & m, mpz const & v) { set_core(n, m, v); } template void mpfx_manager::set_core(mpfx & n, mpq_manager & m, mpq const & v) { if (m.is_int(v)) { set_core(n, m, v.numerator()); } else { allocate_if_needed(n); _scoped_numeral > tmp(m); n.m_sign = is_neg(n); m.mul2k(v.numerator(), 8 * sizeof(unsigned) * m_frac_part_sz, tmp); m.abs(tmp); if ((n.m_sign == 1) != m_to_plus_inf && !m.divides(v.denominator(), tmp)) { m.div(tmp, v.denominator(), tmp); m.inc(tmp); } else { m.div(tmp, v.denominator(), tmp); } m_tmp_digits.reset(); m.decompose(tmp, m_tmp_digits); unsigned sz = m_tmp_digits.size(); if (sz > m_total_sz) throw overflow_exception(); unsigned * w = words(n); ::copy(sz, m_tmp_digits.c_ptr(), m_total_sz, w); } SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, unsynch_mpq_manager & m, mpq const & v) { set_core(n, m, v); } void mpfx_manager::set(mpfx & n, synch_mpq_manager & m, mpq const & v) { set_core(n, m, v); } bool mpfx_manager::eq(mpfx const & a, mpfx const & b) const { if (is_zero(a) && is_zero(b)) return true; if (is_zero(a) || is_zero(b)) return false; if (a.m_sign != b.m_sign) return false; unsigned * w1 = words(a); unsigned * w2 = words(b); for (unsigned i = 0; i < m_total_sz; i++) if (w1[i] != w2[i]) return false; return true; } bool mpfx_manager::lt(mpfx const & a, mpfx const & b) const { STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << " < "; display(tout, b); tout << ") == ";); bool r; if (is_zero(a)) { r = !is_zero(b) && !is_neg(b); } else if (is_zero(b)) { r = is_neg(a); } else { SASSERT(!is_zero(a)); SASSERT(!is_zero(b)); if (is_neg(a)) { r = is_pos(b) || ::lt(m_total_sz, words(b), words(a)); } else { SASSERT(is_pos(a)); r = is_pos(b) && ::lt(m_total_sz, words(a), words(b)); } } STRACE("mpfx_trace", tout << "(" << r << " == 1)\n";); return r; } void mpfx_manager::add_sub(bool is_sub, mpfx const & a, mpfx const & b, mpfx & c) { if (is_zero(a)) { set(c, b); if (is_sub) neg(c); return; } if (is_zero(b)) { set(c, a); return; } TRACE("mpfx", tout << (is_sub ? "sub" : "add") << "("; display(tout, a); tout << ", "; display(tout, b); tout << ")\n";); allocate_if_needed(c); bool sgn_a = a.m_sign; bool sgn_b = b.m_sign; unsigned * w_a = words(a); unsigned * w_b = words(b); if (is_sub) sgn_b = !sgn_b; // Compute c unsigned * w_c = words(c); if (sgn_a == sgn_b) { c.m_sign = sgn_a; if (!::add(m_total_sz, w_a, w_b, w_c)) throw overflow_exception(); } else { unsigned borrow; SASSERT(sgn_a != sgn_b); if (::lt(m_total_sz, w_a, w_b)) { c.m_sign = sgn_b; m_mpn_manager.sub(w_b, m_total_sz, w_a, m_total_sz, w_c, &borrow); SASSERT(!::is_zero(m_total_sz, w_c)); } else { c.m_sign = sgn_a; m_mpn_manager.sub(w_a, m_total_sz, w_b, m_total_sz, w_c, &borrow); if (::is_zero(m_total_sz, w_c)) reset(c); } SASSERT(borrow == 0); } TRACE("mpfx", tout << "result: "; display(tout, c); tout << "\n";); SASSERT(check(c)); } void mpfx_manager::add(mpfx const & a, mpfx const & b, mpfx & c) { STRACE("mpfx_trace", tout << "[mpfx] "; display(tout, a); tout << " + "; display(tout, b); tout << " == ";); add_sub(false, a, b, c); STRACE("mpfx_trace", display(tout, c); tout << "\n";); } void mpfx_manager::sub(mpfx const & a, mpfx const & b, mpfx & c) { STRACE("mpfx_trace", tout << "[mpfx] "; display(tout, a); tout << " - "; display(tout, b); tout << " == ";); add_sub(true, a, b, c); STRACE("mpfx_trace", display(tout, c); tout << "\n";); } void mpfx_manager::mul(mpfx const & a, mpfx const & b, mpfx & c) { STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << ") * ("; display(tout, b); tout << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";); if (is_zero(a) || is_zero(b)) { reset(c); } else { allocate_if_needed(c); c.m_sign = a.m_sign ^ b.m_sign; unsigned * r = m_buffer0.c_ptr(); m_mpn_manager.mul(words(a), m_total_sz, words(b), m_total_sz, r); // round result unsigned * _r = r + m_frac_part_sz; if ((c.m_sign == 1) != m_to_plus_inf && !::is_zero(m_frac_part_sz, r)) { if (!::inc(m_total_sz, _r)) throw overflow_exception(); } // check for overflows if (!::is_zero(m_int_part_sz, _r + m_total_sz)) throw overflow_exception(); // copy result to c unsigned * w_c = words(c); for (unsigned i = 0; i < m_total_sz; i++) w_c[i] = _r[i]; } STRACE("mpfx_trace", display(tout, c); tout << "\n";); SASSERT(check(c)); } void mpfx_manager::div(mpfx const & a, mpfx const & b, mpfx & c) { if (is_zero(b)) throw div0_exception(); STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << ") / ("; display(tout, b); tout << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";); if (is_zero(a)) { reset(c); } else { allocate_if_needed(c); c.m_sign = a.m_sign ^ b.m_sign; unsigned * w_a = words(a); unsigned * w_a_shft = m_buffer0.c_ptr(); unsigned a_shft_sz = sz(w_a) + m_frac_part_sz; // copy a to buffer 0, and shift by m_frac_part_sz for (unsigned i = 0; i < m_frac_part_sz; i++) w_a_shft[i] = 0; for (unsigned i = 0; i < m_total_sz; i++) w_a_shft[i+m_frac_part_sz] = w_a[i]; unsigned * w_b = words(b); unsigned b_sz = sz(w_b); unsigned * w_q = m_buffer1.c_ptr(); if (b_sz > a_shft_sz) { if ((c.m_sign == 1) != m_to_plus_inf) set_epsilon(c); else reset(c); } else { unsigned q_sz = a_shft_sz - b_sz + 1; unsigned * w_r = m_buffer2.c_ptr(); unsigned r_sz = b_sz; m_mpn_manager.div(w_a_shft, a_shft_sz, w_b, b_sz, w_q, w_r); for (unsigned i = m_total_sz; i < q_sz; i++) if (w_q[i] != 0) throw overflow_exception(); if (((c.m_sign == 1) != m_to_plus_inf) && !::is_zero(r_sz, w_r)) { // round the result if (!::inc(m_total_sz, w_q)) throw overflow_exception(); } unsigned * w_c = words(c); bool zero_q = true; if (m_total_sz >= q_sz) { unsigned i; for (i = 0; i < q_sz; i++) { if (w_q[i] != 0) zero_q = false; w_c[i] = w_q[i]; } for (; i < m_total_sz; i++) w_c[i] = 0; } else { for (unsigned i = 0; i < m_total_sz; i++) { if (w_q[i] != 0) zero_q = false; w_c[i] = w_q[i]; } } if (zero_q) { if ((c.m_sign == 1) != m_to_plus_inf) set_epsilon(c); else reset(c); } } } STRACE("mpfx_trace", display(tout, c); tout << "\n";); SASSERT(check(c)); } void mpfx_manager::div2k(mpfx & a, unsigned k) { STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << ") / (2^" << k << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";); if (!is_zero(a) && k > 0) { unsigned * w = words(a); bool _inc = ((a.m_sign == 1) != m_to_plus_inf) && has_one_at_first_k_bits(m_total_sz, w, k); shr(m_total_sz, w, k, m_total_sz, w); if (_inc) { VERIFY(::inc(m_total_sz, w)); SASSERT(!::is_zero(m_total_sz, w)); } else if (::is_zero(m_total_sz, w)) { reset(a); } } STRACE("mpfx_trace", display(tout, a); tout << "\n";); SASSERT(check(a)); } void mpfx_manager::set_epsilon(mpfx & n) { unsigned * w = words(n); w[0] = 1; for (unsigned i = 1; i < m_total_sz; i++) w[i] = 0; } void mpfx_manager::set_minus_epsilon(mpfx & n) { set_epsilon(n); n.m_sign = true; SASSERT(check(n)); } void mpfx_manager::set_plus_epsilon(mpfx & n) { set_epsilon(n); n.m_sign = 0; SASSERT(check(n)); } void mpfx_manager::floor(mpfx & n) { STRACE("mpfx_trace", tout << "[mpfx] Floor["; display(tout, n); tout << "] == ";); unsigned * w = words(n); if (is_neg(n)) { bool is_int = true; for (unsigned i = 0; i < m_frac_part_sz; i++) { if (w[i] != 0) { is_int = false; w[i] = 0; } } if (!is_int && !::inc(m_int_part_sz, w + m_frac_part_sz)) throw overflow_exception(); } else { for (unsigned i = 0; i < m_frac_part_sz; i++) w[i] = 0; } if (::is_zero(m_int_part_sz, w + m_frac_part_sz)) reset(n); SASSERT(check(n)); STRACE("mpfx_trace", display(tout, n); tout << "\n";); } void mpfx_manager::ceil(mpfx & n) { STRACE("mpfx_trace", tout << "[mpfx] Ceiling["; display(tout, n); tout << "] == ";); unsigned * w = words(n); if (is_pos(n)) { bool is_int = true; for (unsigned i = 0; i < m_frac_part_sz; i++) { if (w[i] != 0) { is_int = false; w[i] = 0; } } if (!is_int && !::inc(m_int_part_sz, w + m_frac_part_sz)) throw overflow_exception(); } else { for (unsigned i = 0; i < m_frac_part_sz; i++) w[i] = 0; } if (::is_zero(m_int_part_sz, w + m_frac_part_sz)) reset(n); SASSERT(check(n)); STRACE("mpfx_trace", display(tout, n); tout << "\n";); } void mpfx_manager::power(mpfx const & a, unsigned p, mpfx & b) { #ifdef _TRACE scoped_mpfx _a(*this); _a = a; unsigned _p = p; #endif #define SMALL_POWER 8 SASSERT(check(a)); if (is_zero(a)) { SASSERT(p != 0); reset(b); } else if (p == 0) { set(b, 1); } else if (p == 1) { set(b, a); } else if (p == 2) { mul(a, a, b); } else if (p <= SMALL_POWER && &a != &b) { SASSERT(p > 2); --p; set(b, a); while (p > 0) { --p; mul(a, b, b); } } else { unsigned mask = 1; scoped_mpfx pw(*this); set(pw, a); set(b, 1); while (mask <= p) { if (mask & p) mul(b, pw, b); mul(pw, pw, pw); mask = mask << 1; } } STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, _a); tout << ") ^ " << _p << (m_to_plus_inf ? "<=" : ">="); display(tout, b); tout << "\n";); TRACE("mpfx_power", display_raw(tout, b); tout << "\n";); SASSERT(check(b)); } bool mpfx_manager::is_power_of_two(mpfx const & a, unsigned & k) const { if (!is_int(a) || is_zero(a)) return false; unsigned * w = words(a); unsigned i = m_total_sz; while (true) { SASSERT (i > m_frac_part_sz); --i; if (w[i] != 0) { if (!::is_power_of_two(w[i])) return false; k = (i - m_frac_part_sz) * 8 * sizeof(unsigned) + log2(w[i]); while (i > m_frac_part_sz) { --i; if (w[i] != 0) return false; } return true; } } } bool mpfx_manager::is_power_of_two(mpfx const & a) const { unsigned k; return is_power_of_two(a, k); } int64 mpfx_manager::get_int64(mpfx const & n) const { SASSERT(is_int64(n)); unsigned * w = words(n); w += m_frac_part_sz; uint64 r = 0; memcpy(&r, w, sizeof(uint64)); if (r == 0x8000000000000000ull) { SASSERT(is_neg(n)); return INT64_MIN; } else { return is_neg(n) ? -static_cast(r) : r; } } uint64 mpfx_manager::get_uint64(mpfx const & n) const { SASSERT(is_uint64(n)); unsigned * w = words(n); w += m_frac_part_sz; uint64 r = 0; memcpy(&r, w, sizeof(uint64)); return r; } template void mpfx_manager::to_mpz_core(mpfx const & n, mpz_manager & m, mpz & t) { SASSERT(is_int(n)); unsigned * w = words(n); m.set(t, m_int_part_sz, w+m_frac_part_sz); if (is_neg(n)) m.neg(t); } void mpfx_manager::to_mpz(mpfx const & n, unsynch_mpz_manager & m, mpz & t) { to_mpz_core(n, m, t); } void mpfx_manager::to_mpz(mpfx const & n, synch_mpz_manager & m, mpz & t) { to_mpz_core(n, m, t); } template void mpfx_manager::to_mpq_core(mpfx const & n, mpq_manager & m, mpq & t) { _scoped_numeral > a(m), b(m); unsigned * w = words(n); m.set(a, m_total_sz, w); m.set(b, 1); m.mul2k(b, sizeof(unsigned)*8*m_frac_part_sz); m.rat_div(a, b, t); if (is_neg(n)) m.neg(t); } void mpfx_manager::to_mpq(mpfx const & n, unsynch_mpq_manager & m, mpq & t) { to_mpq_core(n, m, t); } void mpfx_manager::to_mpq(mpfx const & n, synch_mpq_manager & m, mpq & t) { to_mpq_core(n, m, t); } void mpfx_manager::display_raw(std::ostream & out, mpfx const & n) const { if (is_neg(n)) out << "-"; unsigned * w = words(n); unsigned i = m_total_sz; while(i > 0) { if (i == m_frac_part_sz) out << "."; --i; out << std::hex << std::setfill('0') << std::setw(2 * sizeof(unsigned)) << w[i]; } } void mpfx_manager::display(std::ostream & out, mpfx const & n) const { if (is_neg(n)) out << "-"; unsigned * w = words(n); unsigned sz = m_total_sz; unsigned shift = UINT_MAX; if (is_int(n)) { w += m_frac_part_sz; sz -= m_frac_part_sz; } else { shift = ntz(m_total_sz, w); if (shift > 0) shr(m_total_sz, w, shift, m_total_sz, w); } sbuffer str_buffer(11*sz, 0); out << m_mpn_manager.to_string(w, sz, str_buffer.begin(), str_buffer.size()); if (!is_int(n)) { SASSERT(shift != UINT_MAX); // reverse effect of shr if (shift > 0) shl(m_total_sz, w, shift, m_total_sz, w); // display denominator as a power of 2 unsigned k = sizeof(unsigned)*8*m_frac_part_sz - shift; out << "/2"; if (k > 1) out << "^" << k; } } void mpfx_manager::display_smt2(std::ostream & out, mpfx const & n) const { if (is_neg(n)) out << "(- "; unsigned * w = words(n); unsigned sz = m_total_sz; if (is_int(n)) { w += m_frac_part_sz; sz -= m_frac_part_sz; } else { out << "(/ "; } sbuffer str_buffer(11*sz, 0); out << m_mpn_manager.to_string(w, sz, str_buffer.begin(), str_buffer.size()); if (!is_int(n)) { out << " "; unsigned * w = m_buffer0.c_ptr(); for (unsigned i = 0; i < m_frac_part_sz; i++) w[i] = 0; w[m_frac_part_sz] = 1; sbuffer str_buffer2(11*(m_frac_part_sz+1), 0); out << m_mpn_manager.to_string(w, m_frac_part_sz + 1, str_buffer2.begin(), str_buffer2.size()); out << ")"; } if (is_neg(n)) out << ")"; } void mpfx_manager::display_decimal(std::ostream & out, mpfx const & n, unsigned prec) const { if (is_neg(n)) out << "-"; unsigned * w = words(n); sbuffer str_buffer(11*m_int_part_sz, 0); out << m_mpn_manager.to_string(w + m_frac_part_sz, m_int_part_sz, str_buffer.begin(), str_buffer.size()); if (!is_int(n)) { out << "."; unsigned * frac = m_buffer0.c_ptr(); ::copy(m_frac_part_sz, w, m_frac_part_sz, frac); unsigned ten = 10; unsigned * n_frac = m_buffer1.c_ptr(); bool frac_is_zero = false; unsigned i = 0; while (!frac_is_zero) { if (i >= prec) { out << "?"; return; } m_mpn_manager.mul(frac, m_frac_part_sz, &ten, 1, n_frac); frac_is_zero = ::is_zero(m_frac_part_sz, n_frac); SASSERT(n_frac[m_frac_part_sz] <= 9); if (!frac_is_zero || n_frac[m_frac_part_sz] != 0) out << n_frac[m_frac_part_sz]; n_frac[m_frac_part_sz] = 0; std::swap(frac, n_frac); i++; } } } std::string mpfx_manager::to_string(mpfx const & a) const { std::ostringstream buffer; display(buffer, a); return buffer.str(); } std::string mpfx_manager::to_rational_string(mpfx const & a) const { return to_string(a); } bool mpfx_manager::check(mpfx const & a) const { SASSERT(!is_zero(a) || a.m_sign == 0); SASSERT(is_zero(a) == ::is_zero(m_total_sz, words(a))); return true; } unsigned mpfx_manager::prev_power_of_two(mpfx const & a) { if (!is_pos(a)) return 0; return m_int_part_sz * sizeof(unsigned) * 8 - nlz(m_int_part_sz, words(a) + m_frac_part_sz) - 1; } z3-z3-4.4.1/src/util/mpfx.h000066400000000000000000000256341260446376700153260ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpfx.h Abstract: Multi precision fixed point numbers. Author: Leonardo de Moura (leonardo) 2012-09-19 Revision History: --*/ #ifndef MPFX_H_ #define MPFX_H_ #include"id_gen.h" #include"util.h" #include"vector.h" #include"z3_exception.h" #include"scoped_numeral.h" #include"scoped_numeral_vector.h" #include"mpn.h" class mpfx_manager; class mpfx { friend class mpfx_manager; unsigned m_sign:1; unsigned m_sig_idx:31; // position where the data is stored in the mpfx_manager. public: mpfx(): m_sign(0), m_sig_idx(0) { } void swap(mpfx & other) { unsigned sign = m_sign; m_sign = other.m_sign; other.m_sign = sign; unsigned sig_idx = m_sig_idx; m_sig_idx = other.m_sig_idx; other.m_sig_idx = sig_idx; } }; inline void swap(mpfx & m1, mpfx & m2) { m1.swap(m2); } class mpz; class mpq; template class mpz_manager; template class mpq_manager; typedef mpz_manager synch_mpz_manager; typedef mpz_manager unsynch_mpz_manager; typedef mpq_manager synch_mpq_manager; typedef mpq_manager unsynch_mpq_manager; class mpfx_manager { // Every mpfx numeral from a given mpfx_manager uses the same number of words // to encode the integer and fractional parts. // // The number of words used to encode the integer part may be different from the number of words // used to encode the fractional part. // // There are two rounding modes: towards plus infinity, and towards minus infinity. // // If the result of an operation does not fit in the integer part, then an overflow exception is thrown. // // If the fractional part uses n words, then the error of every operation is less than 1/2^(32*n). // // Machine integer values (int, unsigned, int64, uint64) can be easily converted into mpfx numerals. // // The result of addition and subtraction operations are always precise. Note that overflows will trigger // an exception instead of an incorrect result. // unsigned m_int_part_sz; unsigned m_frac_part_sz; unsigned m_total_sz; //!< == m_int_part_sz + m_frac_part_sz unsigned_vector m_words; //!< Array containing all words unsigned m_capacity; //!< Number of mpfx numerals that can be stored in m_words. bool m_to_plus_inf; //!< If True, then round to plus infinity, otherwise to minus infinity id_gen m_id_gen; unsigned_vector m_buffer0, m_buffer1, m_buffer2; unsigned_vector m_tmp_digits; mpfx m_one; mpn_manager m_mpn_manager; unsigned * words(mpfx const & n) const { return m_words.c_ptr() + (n.m_sig_idx * m_total_sz); } unsigned sz(unsigned * ws) const; void ensure_capacity(unsigned sig_idx) { while (sig_idx >= m_capacity) expand(); } void expand(); void allocate_if_needed(mpfx & n) { if (n.m_sig_idx == 0) allocate(n); } void allocate(mpfx & n); void set_epsilon(mpfx & n); void add_sub(bool is_sub, mpfx const & a, mpfx const & b, mpfx & c); template void set_core(mpfx & n, mpz_manager & m, mpz const & v); template void set_core(mpfx & n, mpq_manager & m, mpq const & v); template void to_mpz_core(mpfx const & n, mpz_manager & m, mpz & t); template void to_mpq_core(mpfx const & n, mpq_manager & m, mpq & t); public: typedef mpfx numeral; static bool precise() { return false; } static bool field() { return true; } class exception : public z3_exception { virtual char const * msg() const { return "multi-precision fixed point (mpfx) exception"; } }; class overflow_exception : public exception { virtual char const * msg() const { return "multi-precision fixed point (mpfx) overflow"; } }; class div0_exception : public exception { virtual char const * msg() const { return "multi-precision fixed point (mpfx) division by zero"; } }; mpfx_manager(unsigned int_sz = 2, unsigned frac_sz = 1, unsigned initial_capacity = 1024); ~mpfx_manager(); void round_to_plus_inf() { m_to_plus_inf = true; } void round_to_minus_inf() { m_to_plus_inf = false; } void set_rounding(bool to_plus_inf) { m_to_plus_inf = to_plus_inf; } bool rounding_to_plus_inf() const { return m_to_plus_inf; } /** \brief Return true if n is negative */ static bool sign(mpfx const & n) { return is_neg(n); } /** \brief Set n to zero. */ void reset(mpfx & n); /** \brief Return true if n is an integer. */ bool is_int(mpfx const & n) const; /** \brief Return true if n is zero. */ static bool is_zero(mpfx const & n) { return n.m_sig_idx == 0; } /** \brief Return true if n is positive. */ static bool is_pos(mpfx const & n) { return n.m_sign == 0 && !is_zero(n); } /** \brief Return true if n is negative. */ static bool is_neg(mpfx const & n) { return n.m_sign != 0; } /** \brief Return true if n is non positive. */ static bool is_nonpos(mpfx const & n) { return !is_pos(n); } /** \brief Return true if n is non negative. */ static bool is_nonneg(mpfx const & n) { return !is_neg(n); } /** \brief Return true if the absolute value of n is 1. */ bool is_abs_one(mpfx const & n) const; /** \brief Return true if n is one. */ bool is_one(mpfx const & n) const { return is_pos(n) && is_abs_one(n); } /** \brief Return true if n is minus one. */ bool is_minus_one(mpfx const & n) const { return is_neg(n) && is_abs_one(n); } /** \brief Return true if \c a is an integer and fits in an int64 machine integer. */ bool is_int64(mpfx const & a) const; /** \brief Return true if \c a is a non-negative integer and fits in an int64 machine integer. */ bool is_uint64(mpfx const & a) const; /** \brief Delete the resources associated with n. */ void del(mpfx & n); /** \brief a <- -a */ static void neg(mpfx & a) { if (!is_zero(a)) a.m_sign = !a.m_sign; } /** \brief a <- |a| */ static void abs(mpfx & a) { a.m_sign = 0; } static void swap(mpfx & a, mpfx & b) { a.swap(b); } /** \brief c <- a + b */ void add(mpfx const & a, mpfx const & b, mpfx & c); /** \brief c <- a - b */ void sub(mpfx const & a, mpfx const & b, mpfx & c); /** \brief a <- a + 1 */ void inc(mpfx & a) { add(a, m_one, a); } /** \brief a <- a - 1 */ void dec(mpfx & a) { sub(a, m_one, a); } /** \brief c <- a * b */ void mul(mpfx const & a, mpfx const & b, mpfx & c); /** \brief c <- a / b \pre !is_zero(b) */ void div(mpfx const & a, mpfx const & b, mpfx & c); /** \brief a <- 1/a \pre !is_zero(a); */ void inv(mpfx & a) { div(m_one, a, a); } void inv(mpfx const & a, mpfx & b) { set(b, a); inv(b); } /** \brief a <- a/2^k */ void div2k(mpfx & a, unsigned k); /** \brief b <- a/2^k */ void div2k(mpfx const & a, unsigned k, mpfx & b) { set(b, a); div2k(b, k); } /** \brief a <- a/2 */ void div2(mpfx & a) { div2k(a, 1); } /** \brief b <- a/2 */ void div2(mpfx const & a, mpfx & b) { div2k(a, 1, b); } /** \brief b <- a^k */ void power(mpfx const & a, unsigned k, mpfx & b); /** \brief Return true if \c a is a power of 2. That is, a is equal to 2^k for some k >= 0. */ bool is_power_of_two(mpfx const & a, unsigned & k) const; bool is_power_of_two(mpfx const & a) const; bool eq(mpfx const & a, mpfx const & b) const; bool neq(mpfx const & a, mpfx const & b) const { return !eq(a, b); } bool lt(mpfx const & a, mpfx const & b) const; bool gt(mpfx const & a, mpfx const & b) const { return lt(b, a); } bool le(mpfx const & a, mpfx const & b) const { return !lt(b, a); } bool ge(mpfx const & a, mpfx const & b) const { return !lt(a, b); } void set(mpfx & n, int v); void set(mpfx & n, unsigned v); void set(mpfx & n, int64 v); void set(mpfx & n, uint64 v); void set(mpfx & n, int num, unsigned den); void set(mpfx & n, int64 num, uint64 den); void set(mpfx & n, mpfx const & v); void set(mpfx & n, unsynch_mpz_manager & m, mpz const & v); void set(mpfx & n, synch_mpz_manager & m, mpz const & v); void set(mpfx & n, unsynch_mpq_manager & m, mpq const & v); void set(mpfx & n, synch_mpq_manager & m, mpq const & v); /** \brief Set n to the smallest representable numeral greater than zero. */ void set_plus_epsilon(mpfx & n); /** \brief Set n to the greatest representable numeral less than zero. */ void set_minus_epsilon(mpfx & n); /** \brief n <- floor(n) */ void floor(mpfx & n); void floor(mpfx const & n, mpfx & o) { set(o, n); floor(o); } /** \brief n <- ceil(n) */ void ceil(mpfx & n); void ceil(mpfx const & n, mpfx & o) { set(o, n); ceil(o); } /** \brief Return n as an int64. \pre is_int64(n) */ int64 get_int64(mpfx const & n) const; /** \brief Return n as an uint64. \pre is_uint64(n) */ uint64 get_uint64(mpfx const & n) const; /** \brief Convert n into a mpz numeral. \pre is_int(n) */ void to_mpz(mpfx const & n, unsynch_mpz_manager & m, mpz & t); /** \brief Convert n into a mpz numeral. \pre is_int(n) */ void to_mpz(mpfx const & n, synch_mpz_manager & m, mpz & t); /** \brief Convert n into a mpq numeral. */ void to_mpq(mpfx const & n, unsynch_mpq_manager & m, mpq & t); /** \brief Convert n into a mpq numeral. */ void to_mpq(mpfx const & n, synch_mpq_manager & m, mpq & t); /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpfx const & a); void display(std::ostream & out, mpfx const & n) const; void display_pp(std::ostream & out, mpfx const & n) const { display(out, n); } void display_smt2(std::ostream & out, mpfx const & n) const; void display_decimal(std::ostream & out, mpfx const & n, unsigned prec = UINT_MAX) const; void display_raw(std::ostream & out, mpfx const & n) const; std::string to_string(mpfx const & a) const; std::string to_rational_string(mpfx const & a) const; bool check(mpfx const & a) const; }; typedef _scoped_numeral scoped_mpfx; typedef _scoped_numeral_vector scoped_mpfx_vector; #endif z3-z3-4.4.1/src/util/mpn.cpp000066400000000000000000000330311260446376700154670ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpn.cpp Abstract: Multi Precision Natural Numbers Author: Christoph Wintersteiger (cwinter) 2011-11-16. Revision History: --*/ #include"debug.h" #include"trace.h" #include"buffer.h" #include"mpn.h" #define max(a,b) (((a) > (b)) ? (a) : (b)) typedef uint64 mpn_double_digit; COMPILE_TIME_ASSERT(sizeof(mpn_double_digit) == 2 * sizeof(mpn_digit)); const mpn_digit mpn_manager::zero = 0; mpn_manager::mpn_manager() { omp_init_nest_lock(&m_lock); } mpn_manager::~mpn_manager() { omp_destroy_nest_lock(&m_lock); } int mpn_manager::compare(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb) const { int res = 0; trace(a, lnga); size_t j = max(lnga, lngb) - 1; for (; j != (size_t)-1 && res == 0; j--) { mpn_digit const & u_j = (j < lnga) ? a[j] : zero; mpn_digit const & v_j = (j < lngb) ? b[j] : zero; if (u_j > v_j) res = 1; else if (u_j < v_j) res = -1; } TRACE("mpn", tout << ((res == 1) ? " > " : (res == -1) ? " < " : " == "); ); trace_nl(b, lngb); return res; } bool mpn_manager::add(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, mpn_digit * c, size_t const lngc_alloc, size_t * plngc) const { trace(a, lnga, b, lngb, "+"); // Essentially Knuth's Algorithm A size_t len = max(lnga, lngb); SASSERT(lngc_alloc == len+1 && len > 0); mpn_digit k = 0; mpn_digit r; bool c1, c2; for (size_t j = 0; j < len; j++) { mpn_digit const & u_j = (j < lnga) ? a[j] : zero; mpn_digit const & v_j = (j < lngb) ? b[j] : zero; r = u_j + v_j; c1 = r < u_j; c[j] = r + k; c2 = c[j] < r; k = c1 | c2; } c[len] = k; size_t &os = *plngc; for (os = len+1; os > 1 && c[os-1] == 0; ) os--; SASSERT(os > 0 && os <= len+1); trace_nl(c, os); return true; // return k != 0? } bool mpn_manager::sub(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, mpn_digit * c, mpn_digit * pborrow) const { trace(a, lnga, b, lngb, "-"); // Essentially Knuth's Algorithm S size_t len = max(lnga, lngb); mpn_digit & k = *pborrow; k = 0; mpn_digit r; bool c1, c2; for (size_t j = 0; j < len; j++) { mpn_digit const & u_j = (j < lnga) ? a[j] : zero; mpn_digit const & v_j = (j < lngb) ? b[j] : zero; r = u_j - v_j; c1 = r > u_j; c[j] = r - k; c2 = c[j] > r; k = c1 | c2; } trace_nl(c, lnga); return true; // return k != 0? } bool mpn_manager::mul(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, mpn_digit * c) const { trace(a, lnga, b, lngb, "*"); // Essentially Knuth's Algorithm M. // Perhaps implement a more efficient version, see e.g., Knuth, Section 4.3.3. size_t i; mpn_digit k; #define DIGIT_BITS (sizeof(mpn_digit)*8) #define HALF_BITS (sizeof(mpn_digit)*4) for (unsigned i = 0; i < lnga; i++) c[i] = 0; for (size_t j = 0; j < lngb; j++) { mpn_digit const & v_j = b[j]; if (v_j == 0) { // This branch may be omitted according to Knuth. c[j+lnga] = 0; } else { k = 0; for (i = 0; i < lnga; i++) { mpn_digit const & u_i = a[i]; mpn_double_digit t; t = ((mpn_double_digit)u_i * (mpn_double_digit)v_j) + (mpn_double_digit) c[i+j] + (mpn_double_digit) k; c[i+j] = (t << DIGIT_BITS) >> DIGIT_BITS; k = t >> DIGIT_BITS; } c[j+lnga] = k; } } trace_nl(c, lnga+lngb); return true; } #define MASK_FIRST (~((mpn_digit)(-1) >> 1)) #define FIRST_BITS(N, X) ((X) >> (DIGIT_BITS-(N))) #define LAST_BITS(N, X) (((X) << (DIGIT_BITS-(N))) >> (DIGIT_BITS-(N))) #define BASE ((mpn_double_digit)0x01 << DIGIT_BITS) bool mpn_manager::div(mpn_digit const * numer, size_t const lnum, mpn_digit const * denom, size_t const lden, mpn_digit * quot, mpn_digit * rem) { MPN_BEGIN_CRITICAL(); trace(numer, lnum, denom, lden, "/"); bool res = false; if (lnum < lden) { for (size_t i = 0; i < (lnum-lden+1); i++) quot[i] = 0; for (size_t i = 0; i < lden; i++) rem[i] = (i < lnum) ? numer[i] : 0; MPN_END_CRITICAL(); return false; } bool all_zero = true; for (size_t i = 0; i < lden && all_zero; i++) if (denom[i] != zero) all_zero = false; if (all_zero) { UNREACHABLE(); MPN_END_CRITICAL(); return res; } SASSERT(denom[lden-1] != 0); if (lnum == 1 && lden == 1) { *quot = numer[0] / denom[0]; *rem = numer[0] % denom[0]; } else if (lnum < lden || (lnum == lden && numer[lnum-1] < denom[lden-1])) { *quot = 0; for (size_t i = 0; i < lden; i++) rem[i] = (i < lnum) ? numer[i] : 0; } else { size_t d = div_normalize(numer, lnum, denom, lden, u, v); if (lden == 1) res = div_1(u, v[0], quot); else res = div_n(u, v, quot, rem, t_ms, t_ab); div_unnormalize(u, v, d, rem); } // TRACE("mpn_dbg", display_raw(tout, quot, lnum - lden + 1); tout << ", "; // display_raw(tout, rem, lden); tout << std::endl; ); trace_nl(quot, lnum-lden+1); trace(numer, lnum, denom, lden, "%"); trace_nl(rem, lden); #ifdef Z3DEBUG mpn_sbuffer temp(lnum+1, 0); mul(quot, lnum-lden+1, denom, lden, temp.c_ptr()); size_t real_size; add(temp.c_ptr(), lnum, rem, lden, temp.c_ptr(), lnum+1, &real_size); bool ok = true; for (size_t i = 0; i < lnum && ok; i++) if (temp[i] != numer[i]) ok = false; if (temp[lnum] != 0) ok = false; CTRACE("mpn_dbg", !ok, tout << "DIV BUG: quot * denom + rem = "; display_raw(tout, temp.c_ptr(), lnum+1); tout << std::endl; ); SASSERT(ok); #endif MPN_END_CRITICAL(); return res; } size_t mpn_manager::div_normalize(mpn_digit const * numer, size_t const lnum, mpn_digit const * denom, size_t const lden, mpn_sbuffer & n_numer, mpn_sbuffer & n_denom) const { size_t d = 0; while (((denom[lden-1] << d) & MASK_FIRST) == 0) d++; SASSERT(d < DIGIT_BITS); n_numer.resize(lnum+1); n_denom.resize(lden); if (d == 0) { n_numer[lnum] = 0; for (size_t i = 0; i < lnum; i++) n_numer[i] = numer[i]; for (size_t i = 0; i < lden; i++) n_denom[i] = denom[i]; } else { mpn_digit q = FIRST_BITS(d, numer[lnum-1]); n_numer[lnum] = q; for (size_t i = lnum-1; i > 0; i--) n_numer[i] = (numer[i] << d) | FIRST_BITS(d, numer[i-1]); n_numer[0] = numer[0] << d; for (size_t i = lden-1; i > 0; i--) n_denom[i] = denom[i] << d | FIRST_BITS(d, denom[i-1]); n_denom[0] = denom[0] << d; } TRACE("mpn_norm", tout << "Normalized: n_numer="; display_raw(tout, n_numer.c_ptr(), n_numer.size()); tout << " n_denom="; display_raw(tout, n_denom.c_ptr(), n_denom.size()); tout << std::endl; ); return d; } void mpn_manager::div_unnormalize(mpn_sbuffer & numer, mpn_sbuffer & denom, size_t const d, mpn_digit * rem) const { if (d == 0) { for (size_t i = 0; i < denom.size(); i++) rem[i] = numer[i]; } else { for (size_t i = 0; i < denom.size()-1; i++) rem[i] = numer[i] >> d | (LAST_BITS(d, numer[i+1]) << (DIGIT_BITS-d)); rem[denom.size()-1] = numer[denom.size()-1] >> d; } } bool mpn_manager::div_1(mpn_sbuffer & numer, mpn_digit const denom, mpn_digit * quot) const { mpn_double_digit q_hat, temp, r_hat, ms; mpn_digit borrow; for (size_t j = numer.size()-1; j > 0; j--) { temp = (((mpn_double_digit)numer[j]) << DIGIT_BITS) | ((mpn_double_digit)numer[j-1]); q_hat = temp / (mpn_double_digit) denom; r_hat = temp % (mpn_double_digit) denom; if (q_hat >= BASE) { UNREACHABLE(); // is this reachable with normalized v? } SASSERT(q_hat < BASE); ms = temp - (q_hat * (mpn_double_digit) denom); borrow = ms > temp; numer[j-1] = (mpn_digit) ms; numer[j] = ms >> DIGIT_BITS; quot[j-1] = (mpn_digit) q_hat; if (borrow) { quot[j-1]--; numer[j] = numer[j-1] + denom; } TRACE("mpn_div1", tout << "j=" << j << " q_hat=" << q_hat << " r_hat=" << r_hat; tout << " ms=" << ms; tout << " new numer="; display_raw(tout, numer.c_ptr(), numer.size()); tout << " borrow=" << borrow; tout << std::endl; ); } return true; // return rem != 0? } bool mpn_manager::div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom, mpn_digit * quot, mpn_digit * rem, mpn_sbuffer & ms, mpn_sbuffer & ab) const { SASSERT(denom.size() > 1); // This is essentially Knuth's Algorithm D. size_t m = numer.size() - denom.size(); size_t n = denom.size(); SASSERT(numer.size() == m+n); ms.resize(n+1); mpn_double_digit q_hat, temp, r_hat; mpn_digit borrow; for (size_t j = m-1; j != (size_t)-1; j--) { temp = (((mpn_double_digit)numer[j+n]) << DIGIT_BITS) | ((mpn_double_digit)numer[j+n-1]); q_hat = temp / (mpn_double_digit) denom[n-1]; r_hat = temp % (mpn_double_digit) denom[n-1]; recheck: if (q_hat >= BASE || ((q_hat * denom[n-2]) > ((r_hat << DIGIT_BITS) + numer[j+n-2]))) { q_hat--; r_hat += denom[n-1]; if (r_hat < BASE) goto recheck; } SASSERT(q_hat < BASE); // Replace numer[j+n]...numer[j] with // numer[j+n]...numer[j] - q * (denom[n-1]...denom[0]) mpn_digit q_hat_small = (mpn_digit)q_hat; mul(&q_hat_small, 1, denom.c_ptr(), n, ms.c_ptr()); sub(&numer[j], n+1, ms.c_ptr(), n+1, &numer[j], &borrow); quot[j] = q_hat_small; if (borrow) { quot[j]--; ab.resize(n+2); size_t real_size; add(denom.c_ptr(), n, &numer[j], n+1, ab.c_ptr(), n+2, &real_size); for (size_t i = 0; i < n+1; i++) numer[j+i] = ab[i]; } TRACE("mpn_div", tout << "q_hat=" << q_hat << " r_hat=" << r_hat; tout << " ms="; display_raw(tout, ms.c_ptr(), n); tout << " new numer="; display_raw(tout, numer.c_ptr(), m+n+1); tout << " borrow=" << borrow; tout << std::endl; ); } return true; // return rem != 0? } char * mpn_manager::to_string(mpn_digit const * a, size_t const lng, char * buf, size_t const lbuf) const { SASSERT(buf && lbuf > 0); TRACE("mpn_to_string", tout << "[mpn] to_string "; display_raw(tout, a, lng); tout << " == "; ); if (lng == 1) { #ifdef _WINDOWS sprintf_s(buf, lbuf, "%u", *a); #else snprintf(buf, lbuf, "%u", *a); #endif } else { mpn_sbuffer temp(lng, 0), t_numer(lng+1, 0), t_denom(1, 0); for (unsigned i = 0; i < lng; i++) temp[i] = a[i]; size_t j = 0; mpn_digit rem; mpn_digit ten = 10; while (!temp.empty() && (temp.size() > 1 || temp[0] != 0)) { size_t d = div_normalize(&temp[0], temp.size(), &ten, 1, t_numer, t_denom); div_1(t_numer, t_denom[0], &temp[0]); div_unnormalize(t_numer, t_denom, d, &rem); buf[j++] = '0' + rem; while (temp.size() > 0 && temp.back() == 0) temp.pop_back(); } buf[j] = 0; j--; size_t mid = (j/2) + ((j % 2) ? 1 : 0); for (size_t i = 0; i < mid; i++) std::swap(buf[i], buf[j-i]); } TRACE("mpn_to_string", tout << buf << std::endl; ); return buf; } void mpn_manager::display_raw(std::ostream & out, mpn_digit const * a, size_t const lng) const { out << "["; for (size_t i = lng-1; i != (size_t)-1; i-- ) { out << a[i]; if (i != 0) out << "|"; } out << "]"; } void mpn_manager::trace(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, const char * op) const { #ifdef Z3DEBUG char char_buf[4096]; TRACE("mpn", tout << "[mpn] " << to_string(a, lnga, char_buf, sizeof(char_buf)); tout << " " << op << " " << to_string(b, lngb, char_buf, sizeof(char_buf)); tout << " == "; ); #endif } void mpn_manager::trace(mpn_digit const * a, size_t const lnga) const { #ifdef Z3DEBUG char char_buf[4096]; TRACE("mpn", tout << to_string(a, lnga, char_buf, sizeof(char_buf)); ); #endif } void mpn_manager::trace_nl(mpn_digit const * a, size_t const lnga) const { #ifdef Z3DEBUG char char_buf[4096]; TRACE("mpn", tout << to_string(a, lnga, char_buf, sizeof(char_buf)) << std::endl; ); #endif } z3-z3-4.4.1/src/util/mpn.h000066400000000000000000000064551260446376700151460ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpn.h Abstract: Multi Precision Natural Numbers Author: Christoph Wintersteiger (cwinter) 2011-11-16. Revision History: --*/ #ifndef MPN_H_ #define MPN_H_ #include #include"util.h" #include"buffer.h" #include"z3_omp.h" typedef unsigned int mpn_digit; class mpn_manager { #ifndef _NO_OMP_ omp_nest_lock_t m_lock; #endif #define MPN_BEGIN_CRITICAL() omp_set_nest_lock(&m_lock); #define MPN_END_CRITICAL() omp_unset_nest_lock(&m_lock); public: mpn_manager(); ~mpn_manager(); int compare(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb) const; bool add(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, mpn_digit *c, size_t const lngc_alloc, size_t * plngc) const; bool sub(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, mpn_digit * c, mpn_digit * pborrow) const; bool mul(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, mpn_digit * c) const; bool div(mpn_digit const * numer, size_t const lnum, mpn_digit const * denom, size_t const lden, mpn_digit * quot, mpn_digit * rem); char * to_string(mpn_digit const * a, size_t const lng, char * buf, size_t const lbuf) const; private: #ifdef _AMD64_ class mpn_sbuffer : public sbuffer { public: mpn_sbuffer() : sbuffer() {} mpn_sbuffer(size_t nsz, const mpn_digit & elem = 0) : sbuffer(static_cast(nsz), elem) { } void resize(size_t nsz, const mpn_digit & elem = 0) { sbuffer::resize(static_cast(nsz), elem); } mpn_digit & operator[](size_t idx) { return sbuffer::operator[](static_cast(idx)); } const mpn_digit & operator[](size_t idx) const { return sbuffer::operator[](static_cast(idx)); } }; #else typedef sbuffer mpn_sbuffer; #endif static const mpn_digit zero; mpn_sbuffer u, v, t_ms, t_ab; void display_raw(std::ostream & out, mpn_digit const * a, size_t const lng) const; size_t div_normalize(mpn_digit const * numer, size_t const lnum, mpn_digit const * denom, size_t const lden, mpn_sbuffer & n_numer, mpn_sbuffer & n_denom) const; void div_unnormalize(mpn_sbuffer & numer, mpn_sbuffer & denom, size_t const d, mpn_digit * rem) const; bool div_1(mpn_sbuffer & numer, mpn_digit const denom, mpn_digit * quot) const; bool div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom, mpn_digit * quot, mpn_digit * rem, mpn_sbuffer & ms, mpn_sbuffer & ab) const; void trace(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, const char * op) const; void trace(mpn_digit const * a, size_t const lnga) const; void trace_nl(mpn_digit const * a, size_t const lnga) const; }; #endif z3-z3-4.4.1/src/util/mpq.cpp000066400000000000000000000203011260446376700154660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpq.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-21. Revision History: --*/ #include"mpq.h" #include"warning.h" #include"z3_exception.h" template mpq_manager::mpq_manager() { } template mpq_manager::~mpq_manager() { del(m_n_tmp); del(m_add_tmp1); del(m_add_tmp2); del(m_lt_tmp1); del(m_lt_tmp2); del(m_addmul_tmp); } template bool mpq_manager::rat_lt(mpq const & a, mpq const & b) { mpz const & na = a.numerator(); mpz const & nb = b.numerator(); int sign_a = this->sign(na); int sign_b = this->sign(nb); if (sign_a < 0) { if (sign_b >= 0) return true; } else if (sign_a == 0) { if (sign_b > 0) return true; SASSERT(sign_b <= 0); return false; } else { SASSERT(sign_a > 0); if (sign_b <= 0) return false; } SASSERT((sign_a > 0 && sign_b > 0) || (sign_a < 0 && sign_b < 0)); mpz const & da = a.denominator(); mpz const & db = b.denominator(); if (SYNCH) { mpq tmp1; mpq tmp2; mul(na, db, tmp1); mul(nb, da, tmp2); bool r = lt(tmp1, tmp2); del(tmp1); del(tmp2); return r; } else { mul(na, db, m_lt_tmp1); mul(nb, da, m_lt_tmp2); return lt(m_lt_tmp1, m_lt_tmp2); } } template void mpq_manager::floor(mpq const & a, mpz & f) { if (is_int(a)) { set(f, a.m_num); return; } bool is_neg_num = is_neg(a.m_num); machine_div(a.m_num, a.m_den, f); if (is_neg_num) sub(f, this->mk_z(1), f); } template void mpq_manager::ceil(mpq const & a, mpz & c) { if (is_int(a)) { set(c, a.m_num); return; } bool is_pos_num = is_pos(a.m_num); machine_div(a.m_num, a.m_den, c); if (is_pos_num) add(c, this->mk_z(1), c); } template void mpq_manager::gcd(unsigned sz, mpq const * as, mpq & g) { switch (sz) { case 0: reset(g); return; case 1: set(g, as[0]); abs(g); return; default: break; } gcd(as[0], as[1], g); for (unsigned i = 2; i < sz; i++) { if (is_one(g)) return; gcd(g, as[i], g); } } template std::string mpq_manager::to_string(mpq const & a) const { if (is_int(a)) return to_string(a.m_num); return to_string(a.m_num) + "/" + to_string(a.m_den); } template void mpq_manager::display(std::ostream & out, mpq const & a) const { if (is_int(a)) { display(out, a.m_num); } else { display(out, a.m_num); out << "/"; display(out, a.m_den); } } template void mpq_manager::display_smt2(std::ostream & out, mpq const & a, bool decimal) const { if (is_int(a)) { display_smt2(out, a.m_num, decimal); } else { out << "(/ "; display_smt2(out, a.m_num, decimal); out << " "; display_smt2(out, a.m_den, decimal); out << ")"; } } template void mpq_manager::display_decimal(std::ostream & out, mpq const & a, unsigned prec) { mpz n1, d1, v1; get_numerator(a, n1); get_denominator(a, d1); if (is_neg(a)) { out << "-"; neg(n1); } mpz ten(10); div(n1, d1, v1); display(out, v1); rem(n1, d1, n1); if (is_zero(n1)) goto end; // number is an integer out << "."; for (unsigned i = 0; i < prec; i++) { mul(n1, ten, n1); div(n1, d1, v1); SASSERT(lt(v1, ten)); display(out, v1); rem(n1, d1, n1); if (is_zero(n1)) goto end; // number is precise } out << "?"; end: del(ten); del(n1); del(d1); del(v1); } template void mpq_manager::set(mpq & a, char const * val) { reset(a.m_num); mpz ten(10); _scoped_numeral > tmp(*this); char const * str = val; bool sign = false; while (str[0] == ' ') ++str; if (str[0] == '-') sign = true; while (str[0] && (str[0] != '/') && (str[0] != '.') && (str[0] != 'e') && (str[0] != 'E')) { if ('0' <= str[0] && str[0] <= '9') { SASSERT(str[0] - '0' <= 9); mul(a.m_num, ten, tmp); add(tmp, this->mk_z(str[0] - '0'), a.m_num); } ++str; } TRACE("mpq_set", tout << "[before] a: " << to_string(a) << "\n";); if (str[0] == '/' || str[0] == '.' || str[0] == 'e' || str[0] == 'E') { bool is_rat = str[0] == '/'; _scoped_numeral > tmp2(*this); set(tmp2, 1); bool has_den = false; if (str[0] == '/' || str[0] == '.') { has_den = true; ++str; reset(a.m_den); while (str[0] && (str[0] != 'e') && (str[0] != 'E')) { if ('0' <= str[0] && str[0] <= '9') { mul(a.m_den, ten, tmp); add(tmp, this->mk_z(str[0] - '0'), a.m_den); if (!is_rat) mul(tmp2, ten, tmp2); } ++str; } } unsigned long long exp = 0; bool exp_sign = false; if (str[0] == 'e' || str[0] == 'E') { if (is_rat) throw default_exception("mixing rational/scientific notation"); ++str; if (str[0] == '-') { exp_sign = true; ++str; } while (str[0]) { if ('0' <= str[0] && str[0] <= '9') { SASSERT(str[0] - '0' <= 9); exp = (10*exp) + (str[0] - '0'); } else if ('/' == str[0]) { throw default_exception("mixing rational/scientific notation"); } TRACE("mpq_set", tout << "[exp]: " << exp << ", str[0]: " << (str[0] - '0') << std::endl;); ++str; } } if (!is_rat) { // a <- a.m_num + a.m_den/tmp2 if (exp > static_cast(UINT_MAX)) throw default_exception("exponent is too big"); _scoped_numeral > b(*this); if (has_den) { set(b, a.m_den, tmp2); set(a.m_den, 1); add(a, b, a); } if (exp > 0) { _scoped_numeral > _exp(*this); _scoped_numeral > _ten(*this); set(_ten, 10); power(_ten, static_cast(exp), _exp); TRACE("mpq_set", tout << "a: " << to_string(a) << ", exp_sign:" << exp_sign << ", exp: " << exp << " " << to_string(_exp) << std::endl;); if (exp_sign) div(a, _exp, a); else mul(a, _exp, a); } } else { // rational case if (is_zero(a.m_den)) throw default_exception("division by zero"); } } else { reset_denominator(a); } if (sign) neg(a.m_num); normalize(a); } template void mpq_manager::power(mpq const & a, unsigned p, mpq & b) { unsigned mask = 1; mpq power; set(power, a); set(b, 1); while (mask <= p) { if (mask & p) mul(b, power, b); mul(power, power, power); mask = mask << 1; } del(power); } template double mpq_manager::get_double(mpq const & a) const { double n; double d; n = get_double(a.m_num); d = get_double(a.m_den); SASSERT(d > 0.0); return n/d; } template bool mpq_manager::root(mpq const & a, unsigned n, mpq & r) { return root(a.m_num, n, r.m_num) && root(a.m_den, n, r.m_den); } template unsigned mpq_manager::prev_power_of_two(mpq const & a) { _scoped_numeral > _tmp(*this); floor(a, _tmp); return prev_power_of_two(_tmp); } template class mpq_manager; template class mpq_manager; z3-z3-4.4.1/src/util/mpq.h000066400000000000000000000631771260446376700151550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpq.h Abstract: Author: Leonardo de Moura (leonardo) 2010-06-21. Revision History: --*/ #ifndef MPQ_H_ #define MPQ_H_ #include"mpz.h" #include"trace.h" class mpq { mpz m_num; mpz m_den; friend class mpq_manager; friend class mpq_manager; mpq & operator=(mpq const & other) { UNREACHABLE(); return *this; } public: mpq(int v):m_num(v), m_den(1) {} mpq():m_den(1) {} void swap(mpq & other) { m_num.swap(other.m_num); m_den.swap(other.m_den); } mpz const & numerator() const { return m_num; } mpz const & denominator() const { return m_den; } }; inline void swap(mpq & m1, mpq & m2) { m1.swap(m2); } template class mpq_manager : public mpz_manager { mpz m_n_tmp; mpz m_add_tmp1; mpz m_add_tmp2; mpq m_addmul_tmp; mpq m_lt_tmp1; mpq m_lt_tmp2; void reset_denominator(mpq & a) { del(a.m_den); a.m_den.m_val = 1; } void normalize(mpq & a) { if (SYNCH) { mpz tmp; gcd(a.m_num, a.m_den, tmp); if (is_one(tmp)) { del(tmp); return; } div(a.m_num, tmp, a.m_num); div(a.m_den, tmp, a.m_den); del(tmp); } else { gcd(a.m_num, a.m_den, m_n_tmp); if (is_one(m_n_tmp)) return; div(a.m_num, m_n_tmp, a.m_num); div(a.m_den, m_n_tmp, a.m_den); } } void rat_add(mpq const & a, mpq const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); if (SYNCH) { mpz tmp1, tmp2; mul(a.m_num, b.m_den, tmp1); mul(b.m_num, a.m_den, tmp2); mul(a.m_den, b.m_den, c.m_den); add(tmp1, tmp2, c.m_num); normalize(c); del(tmp1); del(tmp2); } else { mul(a.m_num, b.m_den, m_add_tmp1); mul(b.m_num, a.m_den, m_add_tmp2); mul(a.m_den, b.m_den, c.m_den); add(m_add_tmp1, m_add_tmp2, c.m_num); normalize(c); } STRACE("rat_mpq", tout << to_string(c) << "\n";); } void rat_add(mpq const & a, mpz const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); if (SYNCH) { mpz tmp1; mul(b, a.m_den, tmp1); set(c.m_den, a.m_den); add(a.m_num, tmp1, c.m_num); normalize(c); del(tmp1); } else { mul(b, a.m_den, m_add_tmp1); set(c.m_den, a.m_den); add(a.m_num, m_add_tmp1, c.m_num); normalize(c); } STRACE("rat_mpq", tout << to_string(c) << "\n";); } void rat_sub(mpq const & a, mpq const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " - " << to_string(b) << " == ";); if (SYNCH) { mpz tmp1, tmp2; mul(a.m_num, b.m_den, tmp1); mul(b.m_num, a.m_den, tmp2); mul(a.m_den, b.m_den, c.m_den); sub(tmp1, tmp2, c.m_num); normalize(c); del(tmp1); del(tmp2); } else { mul(a.m_num, b.m_den, m_add_tmp1); mul(b.m_num, a.m_den, m_add_tmp2); mul(a.m_den, b.m_den, c.m_den); sub(m_add_tmp1, m_add_tmp2, c.m_num); normalize(c); } STRACE("rat_mpq", tout << to_string(c) << "\n";); } void rat_mul(mpq const & a, mpq const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); mul(a.m_num, b.m_num, c.m_num); mul(a.m_den, b.m_den, c.m_den); normalize(c); STRACE("rat_mpq", tout << to_string(c) << "\n";); } void rat_mul(mpz const & a, mpq const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); mul(a, b.m_num, c.m_num); set(c.m_den, b.m_den); normalize(c); STRACE("rat_mpq", tout << to_string(c) << "\n";); } bool rat_lt(mpq const & a, mpq const & b); public: typedef mpq numeral; typedef mpq rational; typedef mpz integer; static bool precise() { return true; } static bool field() { return true; } mpq_manager(); ~mpq_manager(); void reset(mpz & a) { mpz_manager::reset(a); } void reset(mpq & a) { reset(a.m_num); reset_denominator(a); } static bool is_small(mpz const & a) { return mpz_manager::is_small(a); } static bool is_small(mpq const & a) { return is_small(a.m_num) && is_small(a.m_den); } static mpq mk_q(int v) { return mpq(v); } mpq mk_q(int n, int d) { mpq r; set(r, n, d); return r; } void del(mpz & a) { mpz_manager::del(a); } void del(mpq & a) { del(a.m_num); del(a.m_den); } void get_numerator(mpq const & a, mpz & n) { set(n, a.m_num); } void get_denominator(mpq const & a, mpz & d) { set(d, a.m_den); } void get_numerator(mpq const & a, mpq & n) { get_numerator(a, n.m_num); reset_denominator(n); } void get_denominator(mpq const & a, mpq & d) { get_denominator(a, d.m_num); reset_denominator(d); } void neg(mpz & a) { mpz_manager::neg(a); } void neg(mpq & a) { mpz_manager::neg(a.m_num); } void abs(mpz & a) { mpz_manager::abs(a); } void abs(mpq & a) { mpz_manager::abs(a.m_num); } static int sign(mpz const & a) { return mpz_manager::sign(a); } static int sign(mpq const & a) { return mpz_manager::sign(a.m_num); } static bool is_pos(mpz const & a) { return mpz_manager::is_pos(a); } static bool is_neg(mpz const & a) { return mpz_manager::is_neg(a); } static bool is_zero(mpz const & a) { return mpz_manager::is_zero(a); } static bool is_nonpos(mpz const & a) { return mpz_manager::is_nonpos(a); } static bool is_nonneg(mpz const & a) { return mpz_manager::is_nonneg(a); } static bool is_pos(mpq const & a) { return is_pos(a.m_num); } static bool is_neg(mpq const & a) { return is_neg(a.m_num); } static bool is_zero(mpq const & a) { return is_zero(a.m_num); } static bool is_nonpos(mpq const & a) { return is_nonpos(a.m_num); } static bool is_nonneg(mpq const & a) { return is_nonneg(a.m_num); } static bool is_one(mpz const & a) { return mpz_manager::is_one(a); } static bool is_one(mpq const & a) { return is_one(a.m_num) && is_one(a.m_den); } static bool is_minus_one(mpz const & a) { return mpz_manager::is_minus_one(a); } static bool is_minus_one(mpq const & a) { return is_minus_one(a.m_num) && is_one(a.m_den); } void floor(mpq const & a, mpz & f); void floor(mpq const & a, mpq & f) { floor(a, f.m_num); reset_denominator(f); } void ceil(mpq const & a, mpz & f); void ceil(mpq const & a, mpq & f) { ceil(a, f.m_num); reset_denominator(f); } static bool is_int(mpq const & a) { return is_one(a.m_den); } std::string to_string(mpq const & a) const; std::string to_rational_string(numeral const & a) { return to_string(a); } std::string to_string(mpz const & a) const { return mpz_manager::to_string(a); } void display(std::ostream & out, mpz const & a) const { return mpz_manager::display(out, a); } void display(std::ostream & out, mpq const & a) const; void display_pp(std::ostream & out, mpq const & a) const { display(out, a); } void display_smt2(std::ostream & out, mpz const & a, bool decimal) const { return mpz_manager::display_smt2(out, a, decimal); } void display_smt2(std::ostream & out, mpq const & a, bool decimal) const; void display_decimal(std::ostream & out, mpq const & a, unsigned prec); void add(mpz const & a, mpz const & b, mpz & c) { mpz_manager::add(a, b, c); } void add(mpq const & a, mpq const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); if (is_int(a) && is_int(b)) { mpz_manager::add(a.m_num, b.m_num, c.m_num); reset_denominator(c); } else rat_add(a, b, c); STRACE("mpq", tout << to_string(c) << "\n";); } void add(mpq const & a, mpz const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); if (is_int(a)) { mpz_manager::add(a.m_num, b, c.m_num); reset_denominator(c); } else { rat_add(a, b, c); } STRACE("mpq", tout << to_string(c) << "\n";); } void sub(mpz const & a, mpz const & b, mpz & c) { mpz_manager::sub(a, b, c); } void sub(mpq const & a, mpq const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " - " << to_string(b) << " == ";); if (is_int(a) && is_int(b)) { mpz_manager::sub(a.m_num, b.m_num, c.m_num); reset_denominator(c); } else rat_sub(a, b, c); STRACE("mpq", tout << to_string(c) << "\n";); } void inc(mpz & a) { mpz_manager::inc(a); } void dec(mpz & a) { mpz_manager::dec(a); } void inc(mpq & a) { add(a, mpz(1), a); } void dec(mpq & a) { add(a, mpz(-1), a); } void mul(mpz const & a, mpz const & b, mpz & c) { mpz_manager::mul(a, b, c); } void mul(mpz const & a, mpz const & b, mpq & c) { mpz_manager::mul(a, b, c.m_num); reset_denominator(c); } void mul(mpq const & a, mpq const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); if (is_int(a) && is_int(b)) { mpz_manager::mul(a.m_num, b.m_num, c.m_num); reset_denominator(c); } else rat_mul(a, b, c); STRACE("mpq", tout << to_string(c) << "\n";); } void mul(mpz const & a, mpq const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); if (is_int(b)) { mpz_manager::mul(a, b.m_num, c.m_num); reset_denominator(c); } else rat_mul(a, b, c); STRACE("mpq", tout << to_string(c) << "\n";); } void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { return mpz_manager::addmul(a, b, c, d); } void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { return mpz_manager::submul(a, b, c, d); } // d <- a + b*c void addmul(mpq const & a, mpq const & b, mpq const & c, mpq & d) { if (is_one(b)) { add(a, c, d); } else if (is_minus_one(b)) { sub(a, c, d); } else { if (SYNCH) { mpq tmp; mul(b,c,tmp); add(a,tmp,d); del(tmp); } else { mul(b,c,m_addmul_tmp); add(a, m_addmul_tmp, d); } } } // d <- a + b*c void addmul(mpq const & a, mpz const & b, mpq const & c, mpq & d) { if (is_one(b)) { add(a, c, d); } else if (is_minus_one(b)) { sub(a, c, d); } else { if (SYNCH) { mpq tmp; mul(b,c,tmp); add(a,tmp,d); del(tmp); } else { mul(b,c,m_addmul_tmp); add(a, m_addmul_tmp, d); } } } // d <- a - b*c void submul(mpq const & a, mpq const & b, mpq const & c, mpq & d) { if (is_one(b)) { sub(a, c, d); } else if (is_minus_one(b)) { add(a, c, d); } else { if (SYNCH) { mpq tmp; mul(b,c,tmp); sub(a,tmp,d); del(tmp); } else { mul(b,c,m_addmul_tmp); sub(a, m_addmul_tmp, d); } } } // d <- a - b*c void submul(mpq const & a, mpz const & b, mpq const & c, mpq & d) { if (is_one(b)) { sub(a, c, d); } else if (is_minus_one(b)) { add(a, c, d); } else { if (SYNCH) { mpq tmp; mul(b,c,tmp); sub(a,tmp,d); del(tmp); } else { mul(b,c,m_addmul_tmp); sub(a, m_addmul_tmp, d); } } } void inv(mpq & a) { SASSERT(!is_zero(a)); if (is_neg(a)) { neg(a.m_num); neg(a.m_den); } mpz_manager::swap(a.m_num, a.m_den); } void inv(mpq const & a, mpq & b) { set(b, a); inv(b); } void div(mpq const & a, mpq const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " / " << to_string(b) << " == ";); if (&b == &c) { mpz tmp; // it is not safe to use c.m_num at this point. mul(a.m_num, b.m_den, tmp); mul(a.m_den, b.m_num, c.m_den); set(c.m_num, tmp); del(tmp); } else { mul(a.m_num, b.m_den, c.m_num); mul(a.m_den, b.m_num, c.m_den); } if (mpz_manager::is_neg(c.m_den)) { neg(c.m_num); neg(c.m_den); } normalize(c); STRACE("mpq", tout << to_string(c) << "\n";); } void div(mpq const & a, mpz const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " / " << to_string(b) << " == ";); set(c.m_num, a.m_num); mul(a.m_den, b, c.m_den); if (mpz_manager::is_neg(b)) { neg(c.m_num); neg(c.m_den); } normalize(c); STRACE("mpq", tout << to_string(c) << "\n";); } void acc_div(mpq & a, mpz const & b) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " / " << to_string(b) << " == ";); mul(a.m_den, b, a.m_den); if (mpz_manager::is_neg(b)) { neg(a.m_num); neg(a.m_den); } normalize(a); STRACE("mpq", tout << to_string(a) << "\n";); } void machine_div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::machine_div(a, b, c); } void div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::div(a, b, c); } void rat_div(mpz const & a, mpz const & b, mpq & c) { set(c.m_num, a); set(c.m_den, b); normalize(c); } void machine_idiv(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); machine_div(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void machine_idiv(mpq const & a, mpq const & b, mpz & c) { SASSERT(is_int(a) && is_int(b)); machine_div(a.m_num, b.m_num, c); } void idiv(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); div(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void idiv(mpq const & a, mpq const & b, mpz & c) { SASSERT(is_int(a) && is_int(b)); div(a.m_num, b.m_num, c); } void rem(mpz const & a, mpz const & b, mpz & c) { mpz_manager::rem(a, b, c); } void rem(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); rem(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void rem(mpq const & a, mpq const & b, mpz & c) { SASSERT(is_int(a) && is_int(b)); rem(a.m_num, b.m_num, c); } void mod(mpz const & a, mpz const & b, mpz & c) { mpz_manager::mod(a, b, c); } void mod(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); mod(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void mod(mpq const & a, mpq const & b, mpz & c) { SASSERT(is_int(a) && is_int(b)); mod(a.m_num, b.m_num, c); } static unsigned hash(mpz const & a) { return mpz_manager::hash(a); } static unsigned hash(mpq const & a) { return hash(a.m_num); } bool eq(mpz const & a, mpz const & b) { return mpz_manager::eq(a, b); } bool eq(mpq const & a, mpq const & b) { return eq(a.m_num, b.m_num) && eq(a.m_den, b.m_den); } bool lt(mpz const & a, mpz const & b) { return mpz_manager::lt(a, b); } bool lt(mpq const & a, mpq const & b) { if (is_int(a) && is_int(b)) return lt(a.m_num, b.m_num); else return rat_lt(a, b); } bool neq(mpz const & a, mpz const & b) { return mpz_manager::neq(a, b); } bool gt(mpz const & a, mpz const & b) { return mpz_manager::gt(a, b); } bool ge(mpz const & a, mpz const & b) { return mpz_manager::ge(a, b); } bool le(mpz const & a, mpz const & b) { return mpz_manager::le(a, b); } bool neq(mpq const & a, mpq const & b) { return !eq(a, b); } bool gt(mpq const & a, mpq const & b) { return lt(b, a); } bool ge(mpq const & a, mpq const & b) { return !lt(a, b); } bool le(mpq const & a, mpq const & b) { return !lt(b, a); } void gcd(mpz const & a, mpz const & b, mpz & c) { mpz_manager::gcd(a, b, c); } void gcd(unsigned sz, mpz const * as, mpz & g) { mpz_manager::gcd(sz, as, g); } void gcd(unsigned sz, mpq const * as, mpq & g); void gcd(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); gcd(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & g) { mpz_manager::gcd(r1, r2, a, b, g); } void gcd(mpq const & r1, mpq const & r2, mpq & a, mpq & b, mpq & g) { SASSERT(is_int(r1) && is_int(r2)); reset_denominator(a); reset_denominator(b); reset_denominator(g); gcd(r1.m_num, r2.m_num, a.m_num, b.m_num, g.m_num); } void lcm(mpz const & a, mpz const & b, mpz & c) { mpz_manager::lcm(a, b, c); } void lcm(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); lcm(a.m_num, b.m_num, c.m_num); reset_denominator(c); } bool divides(mpz const & a, mpz const & b) { return mpz_manager::divides(a, b); } bool divides(mpq const & a, mpq const & b) { SASSERT(is_int(a) && is_int(b)); return divides(a.m_num, b.m_num); } void bitwise_or(mpz const & a, mpz const & b, mpz & c) { return mpz_manager::bitwise_or(a, b, c); } void bitwise_or(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); bitwise_or(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void bitwise_and(mpz const & a, mpz const & b, mpz & c) { return mpz_manager::bitwise_and(a, b, c); } void bitwise_and(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); bitwise_and(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void bitwise_xor(mpz const & a, mpz const & b, mpz & c) { return mpz_manager::bitwise_xor(a, b, c); } void bitwise_xor(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); bitwise_xor(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void bitwise_not(unsigned sz, mpz const & a, mpz & c) { return mpz_manager::bitwise_not(sz, a, c); } void bitwise_not(unsigned sz, mpq const & a, mpq & c) { SASSERT(is_int(a)); bitwise_not(sz, a.m_num, c.m_num); reset_denominator(c); } void set(mpz & target, mpz const & source) { mpz_manager::set(target, source); } void set(mpq & target, mpq const & source) { set(target.m_num, source.m_num); set(target.m_den, source.m_den); } void set(mpz & a, int val) { mpz_manager::set(a, val); } void set(mpq & a, int val) { set(a.m_num, val); reset_denominator(a); } void set(mpq & a, int n, int d) { SASSERT(d != 0); if (d < 0) { n = -n; d = -d; } set(a.m_num, n); set(a.m_den, d); normalize(a); } void set(mpq & a, int64 n, uint64 d) { SASSERT(d != 0); set(a.m_num, n); set(a.m_den, d); normalize(a); } void set(mpq & a, mpz const & n, mpz const & d) { if (is_neg(d)) { set(a.m_num, n); set(a.m_den, d); neg(a.m_num); neg(a.m_den); } else { set(a.m_num, n); set(a.m_den, d); } normalize(a); } void set(mpz & a, unsigned val) { mpz_manager::set(a, val); } void set(mpq & a, unsigned val) { set(a.m_num, val); reset_denominator(a); } void set(mpz & a, char const * val) { mpz_manager::set(a, val); } void set(mpq & a, char const * val); void set(mpz & a, int64 val) { mpz_manager::set(a, val); } void set(mpq & a, int64 val) { set(a.m_num, val); reset_denominator(a); } void set(mpz & a, uint64 val) { mpz_manager::set(a, val); } void set(mpq & a, uint64 val) { set(a.m_num, val); reset_denominator(a); } void set(mpq & a, mpz const & val) { mpz_manager::set(a.m_num, val); reset_denominator(a); } void set(mpz & a, unsigned sz, digit_t const * digits) { mpz_manager::set(a, sz, digits); } void set(mpq & a, unsigned sz, digit_t const * digits) { mpz_manager::set(a.m_num, sz, digits); reset_denominator(a); } void swap(mpz & a, mpz & b) { mpz_manager::swap(a, b); } void swap(mpq & a, mpq & b) { swap(a.m_num, b.m_num); swap(a.m_den, b.m_den); } void swap_numerator(mpz & a, mpq & b) { swap(a, b.m_num); } bool is_uint64(mpz const & a) const { return mpz_manager::is_uint64(a); } bool is_int64(mpz const & a) const { return mpz_manager::is_int64(a); } uint64 get_uint64(mpz const & a) const { return mpz_manager::get_uint64(a); } int64 get_int64(mpz const & a) const { return mpz_manager::get_int64(a); } bool is_uint64(mpq const & a) const { return is_int(a) && is_uint64(a.m_num); } bool is_int64(mpq const & a) const { return is_int(a) && is_int64(a.m_num); } uint64 get_uint64(mpq const & a) const { SASSERT(is_uint64(a)); return get_uint64(a.m_num); } int64 get_int64(mpq const & a) const { SASSERT(is_int64(a)); return get_int64(a.m_num); } double get_double(mpz const & a) const { return mpz_manager::get_double(a); } double get_double(mpq const & a) const; void power(mpz const & a, unsigned p, mpz & b) { mpz_manager::power(a, p, b); } void power(mpq const & a, unsigned p, mpq & b); bool is_power_of_two(mpz const & a, unsigned & shift) { return mpz_manager::is_power_of_two(a, shift); } bool is_power_of_two(mpq const & a, unsigned & shift) { return is_int(a) && is_power_of_two(a.m_num, shift); } unsigned bitsize(mpz const & a) { return mpz_manager::bitsize(a); } unsigned bitsize(mpq const & a) { return is_int(a) ? bitsize(a.m_num) : bitsize(a.m_num) + bitsize(a.m_den); } /** \brief Return true if the number is a perfect square, and store the square root in 'root'. If the number n is positive and the result is false, then root will contain the smallest integer r such that r*r > n. */ bool is_perfect_square(mpz const & a, mpz & r) { return mpz_manager::is_perfect_square(a, r); } /** \brief Return true if the numerator and denominators are perfect squares. Store the square root in root. If the result is false, then the value of root should be ignored. */ bool is_perfect_square(mpq const & a, mpq & r) { if (is_int(a)) { reset_denominator(r); return is_perfect_square(a.m_num, r.m_num); } if (is_perfect_square(a.m_num, r.m_num) && is_perfect_square(a.m_den, r.m_den)) { normalize(r); return true; } return false; } bool root(mpz & a, unsigned n) { return mpz_manager::root(a, n); } bool root(mpz const & a, unsigned n, mpz & r) { return mpz_manager::root(a, n, r); } /** \brief Return true if n-th root of a is rational, and store result in r. */ bool root(mpq const & a, unsigned n, mpq & r); /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpz const & a) { return mpz_manager::prev_power_of_two(a); } unsigned prev_power_of_two(mpq const & a); bool is_int_perfect_square(mpq const & a, mpq & r) { SASSERT(is_int(a)); reset_denominator(r); return is_perfect_square(a.m_num, r.m_num); } bool is_even(mpz const & a) { return mpz_manager::is_even(a); } bool is_even(mpq const & a) { return is_int(a) && is_even(a.m_num); } }; typedef mpq_manager synch_mpq_manager; typedef mpq_manager unsynch_mpq_manager; typedef _scoped_numeral scoped_mpq; typedef _scoped_numeral scoped_synch_mpq; typedef _scoped_numeral_vector scoped_mpq_vector; #endif /* MPQ_H_ */ z3-z3-4.4.1/src/util/mpq_inf.cpp000066400000000000000000000013261260446376700163300ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpq_inf.cpp Abstract: MPQ numbers with infinitesimals Author: Leonardo de Moura (leonardo) 2011-06-28 Revision History: --*/ #include"mpq_inf.h" template std::string mpq_inf_manager::to_string(mpq_inf const & a) { if (m.is_zero(a.second)) return m.to_string(a.first); std::string s = "("; s += m.to_string(a.first); if (m.is_neg(a.second)) s += " -e*"; else s += " +e*"; mpq tmp; m.set(tmp, a.second); m.abs(tmp); s += m.to_string(tmp); m.del(tmp); s += ")"; return s; } template class mpq_inf_manager; template class mpq_inf_manager; z3-z3-4.4.1/src/util/mpq_inf.h000066400000000000000000000164451260446376700160050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpq_inf.h Abstract: MPQ numbers with infinitesimals Author: Leonardo de Moura (leonardo) 2011-06-28 Revision History: --*/ #ifndef MPQ_INF_H_ #define MPQ_INF_H_ #include"mpq.h" #include"hash.h" typedef std::pair mpq_inf; template class mpq_inf_manager { mpq_manager m; double m_inf; public: typedef mpq_inf numeral; mpq_inf_manager(double inf = 0.0001) { set_inf(inf); } void set_inf(double inf) { m_inf = inf; } enum inf_kind { NEG=-1, ZERO, POS }; void reset(mpq_inf & a) { m.reset(a.first); m.reset(a.second); } unsigned hash(mpq_inf const & a) const { return hash_u_u(m.hash(a.first), m.hash(a.second)); } void del(mpq_inf & a) { m.del(a.first); m.del(a.second); } void swap(mpq_inf & a, mpq_inf & b) { m.swap(a.first, b.first); m.swap(a.second, b.second); } void set(mpq_inf & a, mpq_inf const & b) { m.set(a.first, b.first); m.set(a.second, b.second); } void set(mpq_inf & a, mpq const & r) { m.set(a.first, r); m.reset(a.second); } void set(mpq_inf & a, mpq const & r, inf_kind k) { m.set(a.first, r); switch (k) { case NEG: m.set(a.second, -1); break; case ZERO: m.reset(a.second); break; case POS: m.set(a.second, 1); break; } } void set(mpq_inf & a, mpq const & r, mpq const & i) { m.set(a.first, r); m.set(a.second, i); } bool is_int(mpq_inf const & a) const { return m.is_int(a.first) && m.is_zero(a.second); } bool is_pos(mpq_inf const & a) const { return m.is_pos(a.first) || (m.is_zero(a.first) && m.is_pos(a.second)); } bool is_neg(mpq_inf const & a) const { return m.is_neg(a.first) || (m.is_zero(a.first) && m.is_neg(a.second)); } bool is_rational(mpq_inf const & a) const { return m.is_zero(a.second); } void get_rational(mpq_inf const & a, mpq & r) { m.set(r, a.first); } void get_infinitesimal(mpq_inf const & a, mpq & r) { m.set(r, a.second); } double get_double(mpq_inf const & a) { double r = m.get_double(a.first); if (m.is_pos(a.second)) return r + m_inf; else if (m.is_neg(a.second)) return r - m_inf; else return r; } bool is_zero(mpq_inf const & a) const { return m.is_zero(a.first) && m.is_zero(a.second); } bool eq(mpq_inf const & a, mpq_inf const & b) { return m.eq(a.first, b.first) && m.eq(a.second, b.second); } bool eq(mpq_inf const & a, mpq const & b) { return m.eq(a.first, b) && m.is_zero(a.second); } bool eq(mpq_inf const & a, mpq const & b, inf_kind k) { if (!m.eq(a.first, b)) return false; switch (k) { case NEG: return m.is_minus_one(a.second); case ZERO: return m.is_zero(a.second); case POS: return m.is_one(a.second); } UNREACHABLE(); return false; } bool lt(mpq_inf const & a, mpq_inf const & b) { return m.lt(a.first, b.first) || (m.lt(a.second, b.second) && m.eq(a.first, b.first)); } bool lt(mpq_inf const & a, mpq const & b) { return m.lt(a.first, b) || (m.is_neg(a.second) && m.eq(a.first, b)); } bool lt(mpq_inf const & a, mpq const & b, inf_kind k) { if (m.lt(a.first, b)) return true; if (m.eq(a.first, b)) { switch (k) { case NEG: return m.lt(a.second, mpq(-1)); case ZERO: return m.is_neg(a.second); case POS: return m.lt(a.second, mpq(1)); } UNREACHABLE(); } return false; } bool gt(mpq_inf const & a, mpq_inf const & b) { return lt(b, a); } bool gt(mpq_inf const & a, mpq const & b) { return m.gt(a.first, b) || (m.is_pos(a.second) && m.eq(a.first, b)); } bool gt(mpq_inf const & a, mpq const & b, inf_kind k) { if (m.gt(a.first, b)) return true; if (m.eq(a.first, b)) { switch (k) { case NEG: return m.gt(a.second, mpq(-1)); case ZERO: return m.is_pos(a.second); case POS: return m.gt(a.second, mpq(1)); } UNREACHABLE(); } return false; } bool le(mpq_inf const & a, mpq_inf const & b) { return !gt(a, b); } bool le(mpq_inf const & a, mpq const & b) { return !gt(a, b); } bool le(mpq_inf const & a, mpq const & b, inf_kind k) { return !gt(a, b, k); } bool ge(mpq_inf const & a, mpq_inf const & b) { return !lt(a, b); } bool ge(mpq_inf const & a, mpq const & b) { return !lt(a, b); } bool ge(mpq_inf const & a, mpq const & b, inf_kind k) { return !lt(a, b, k); } void add(mpq_inf const & a, mpq_inf const & b, mpq_inf & c) { m.add(a.first, b.first, c.first); m.add(a.second, b.second, c.second); } void sub(mpq_inf const & a, mpq_inf const & b, mpq_inf & c) { m.sub(a.first, b.first, c.first); m.sub(a.second, b.second, c.second); } void add(mpq_inf const & a, mpq const & b, mpq_inf & c) { m.add(a.first, b, c.first); m.set(c.second, a.second); } void sub(mpq_inf const & a, mpq const & b, mpq_inf & c) { m.sub(a.first, b, c.first); m.set(c.second, a.second); } void mul(mpq_inf const & a, mpq const & b, mpq_inf & c) { m.mul(a.first, b, c.first); m.mul(a.second, b, c.second); } void mul(mpq_inf const & a, mpz const & b, mpq_inf & c) { m.mul(b, a.first, c.first); m.mul(b, a.second, c.second); } void div(mpq_inf const & a, mpq const & b, mpq_inf & c) { m.div(a.first, b, c.first); m.div(a.second, b, c.second); } void div(mpq_inf const & a, mpz const & b, mpq_inf & c) { m.div(a.first, b, c.first); m.div(a.second, b, c.second); } void inc(mpq_inf & a) { m.inc(a.first); } void dec(mpq_inf & a) { m.dec(a.first); } void neg(mpq_inf & a) { m.neg(a.first); m.neg(a.second); } void abs(mpq_inf & a) { if (is_neg(a)) { neg(a); } } void ceil(mpq_inf const & a, mpq & b) { if (m.is_int(a.first)) { // special cases for k - delta*epsilon where k is an integer if (m.is_pos(a.second)) m.add(a.first, mpq(1), b); // ceil(k + delta*epsilon) --> k+1 else m.set(b, a.first); } else { m.ceil(a.first, b); } } void floor(mpq_inf const & a, mpq & b) { if (m.is_int(a.first)) { if (m.is_neg(a.first)) m.sub(a.first, mpq(1), b); // floor(k - delta*epsilon) --> k-1 else m.set(b, a.first); } else { m.floor(a.first, b); } } std::string to_string(mpq_inf const & a); void display(std::ostream & out, mpq_inf const & a) { out << to_string(a); } mpq_manager& get_mpq_manager() { return m; } }; typedef mpq_inf_manager synch_mpq_inf_manager; typedef mpq_inf_manager unsynch_mpq_inf_manager; #endif z3-z3-4.4.1/src/util/mpz.cpp000066400000000000000000001567371260446376700155260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpz.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-17. Revision History: --*/ #include #include"mpz.h" #include"buffer.h" #include"trace.h" #include"hash.h" #include"bit_util.h" #if defined(_MP_INTERNAL) #include"mpn.h" #elif defined(_MP_GMP) #include #else #error No multi-precision library selected. #endif // Available GCD algorithms // #define EUCLID_GCD // #define BINARY_GCD // #define LS_BINARY_GCD // #define LEHMER_GCD #if defined(_MP_GMP) // Use LEHMER only if not using GMP // LEHMER assumes 32-bit digits, so it cannot be used with MSBIGNUM library + 64-bit binary #define EUCLID_GCD #else #define LEHMER_GCD #endif template static T gcd_core(T u, T v) { if (u == 0) return v; if (v == 0) return u; int k; for (k = 0; ((u | v) & 1) == 0; ++k) { u >>= 1; v >>= 1; } while ((u & 1) == 0) u >>= 1; do { while ((v & 1) == 0) v >>= 1; if (u < v) { v -= u; } else { T diff = u - v; u = v; v = diff; } v >>= 1; } while (v != 0); return u << k; } unsigned u_gcd(unsigned u, unsigned v) { return gcd_core(u, v); } uint64 u64_gcd(uint64 u, uint64 v) { return gcd_core(u, v); } template mpz_manager::mpz_manager(): m_allocator("mpz_manager") { if (SYNCH) omp_init_nest_lock(&m_lock); #ifndef _MP_GMP if (sizeof(digit_t) == sizeof(uint64)) { // 64-bit machine m_init_cell_capacity = 4; } else { m_init_cell_capacity = 6; } for (unsigned i = 0; i < 2; i++) { m_tmp[i] = allocate(m_init_cell_capacity); m_arg[i] = allocate(m_init_cell_capacity); m_arg[i]->m_size = 1; } set(m_int_min, -static_cast(INT_MIN)); #else // GMP mpz_init(m_tmp); mpz_init(m_tmp2); mpz_init(m_two32); mpz_set_ui(m_two32, UINT_MAX); mpz_set_ui(m_tmp, 1); mpz_add(m_two32, m_two32, m_tmp); m_arg[0] = allocate(); m_arg[1] = allocate(); mpz_init(m_uint64_max); unsigned max_l = static_cast(UINT64_MAX); unsigned max_h = static_cast(UINT64_MAX >> 32); mpz_set_ui(m_uint64_max, max_h); mpz_mul(m_uint64_max, m_two32, m_uint64_max); mpz_set_ui(m_tmp, max_l); mpz_add(m_uint64_max, m_uint64_max, m_tmp); mpz_init(m_int64_max); mpz_init(m_int64_min); max_l = static_cast(INT64_MAX % static_cast(UINT_MAX)); max_h = static_cast(INT64_MAX / static_cast(UINT_MAX)); mpz_set_ui(m_int64_max, max_h); mpz_set_ui(m_tmp, UINT_MAX); mpz_mul(m_int64_max, m_tmp, m_int64_max); mpz_set_ui(m_tmp, max_l); mpz_add(m_int64_max, m_tmp, m_int64_max); mpz_neg(m_int64_min, m_int64_max); mpz_sub_ui(m_int64_min, m_int64_min, 1); #endif mpz one(1); set(m_two64, UINT64_MAX); add(m_two64, one, m_two64); } template mpz_manager::~mpz_manager() { del(m_two64); #ifndef _MP_GMP del(m_int_min); for (unsigned i = 0; i < 2; i++) { deallocate(m_tmp[i]); deallocate(m_arg[i]); } #else mpz_clear(m_tmp); mpz_clear(m_tmp2); mpz_clear(m_two32); deallocate(m_arg[0]); deallocate(m_arg[1]); mpz_clear(m_uint64_max); mpz_clear(m_int64_max); mpz_clear(m_int64_min); #endif if (SYNCH) omp_destroy_nest_lock(&m_lock); } template void mpz_manager::set_big_i64(mpz & c, int64 v) { #ifndef _MP_GMP if (is_small(c)) { c.m_ptr = allocate(m_init_cell_capacity); } SASSERT(capacity(c) >= m_init_cell_capacity); uint64 _v; if (v < 0) { _v = -v; c.m_val = -1; } else { _v = v; c.m_val = 1; } if (sizeof(digit_t) == sizeof(uint64)) { // 64-bit machine digits(c)[0] = static_cast(_v); c.m_ptr->m_size = 1; } else { // 32-bit machine digits(c)[0] = static_cast(_v); digits(c)[1] = static_cast(_v >> 32); c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2; } #else if (is_small(c)) { c.m_ptr = allocate(); } uint64 _v; bool sign; if (v < 0) { _v = -v; sign = true; } else { _v = v; sign = false; } mpz_set_ui(*c.m_ptr, static_cast(_v)); mpz_set_ui(m_tmp, static_cast(_v >> 32)); mpz_mul(m_tmp, m_tmp, m_two32); mpz_add(*c.m_ptr, *c.m_ptr, m_tmp); if (sign) mpz_neg(*c.m_ptr, *c.m_ptr); #endif } template void mpz_manager::set_big_ui64(mpz & c, uint64 v) { #ifndef _MP_GMP if (is_small(c)) { c.m_ptr = allocate(m_init_cell_capacity); } SASSERT(capacity(c) >= m_init_cell_capacity); c.m_val = 1; if (sizeof(digit_t) == sizeof(uint64)) { // 64-bit machine digits(c)[0] = static_cast(v); c.m_ptr->m_size = 1; } else { // 32-bit machine digits(c)[0] = static_cast(v); digits(c)[1] = static_cast(v >> 32); c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2; } #else if (is_small(c)) { c.m_ptr = allocate(); } mpz_set_ui(*c.m_ptr, static_cast(v)); mpz_set_ui(m_tmp, static_cast(v >> 32)); mpz_mul(m_tmp, m_tmp, m_two32); mpz_add(*c.m_ptr, *c.m_ptr, m_tmp); #endif } #ifndef _MP_GMP template template void mpz_manager::set(mpz & a, int sign, unsigned sz) { #if 0 static unsigned max_sz = 0; if (sz > max_sz) { max_sz = sz; verbose_stream() << "max_sz: " << max_sz << "\n"; } #endif unsigned i = sz; for (; i > 0; --i) { if (m_tmp[IDX]->m_digits[i-1] != 0) break; } if (i == 0) { // m_tmp[IDX] is zero reset(a); return; } if (i == 1 && m_tmp[IDX]->m_digits[0] <= INT_MAX) { // m_tmp[IDX] fits is a fixnum del(a); a.m_val = sign < 0 ? -static_cast(m_tmp[IDX]->m_digits[0]) : static_cast(m_tmp[IDX]->m_digits[0]); return; } a.m_val = sign; std::swap(a.m_ptr, m_tmp[IDX]); a.m_ptr->m_size = i; if (!m_tmp[IDX]) // 'a' was a small number m_tmp[IDX] = allocate(m_init_cell_capacity); } #endif template void mpz_manager::set(mpz & a, char const * val) { reset(a); mpz ten(10); mpz tmp; char const * str = val; bool sign = false; while (str[0] == ' ') ++str; if (str[0] == '-') sign = true; while (str[0]) { if ('0' <= str[0] && str[0] <= '9') { SASSERT(str[0] - '0' <= 9); mul(a, ten, tmp); add(tmp, mk_z(str[0] - '0'), a); } ++str; } del(tmp); if (sign) neg(a); } template void mpz_manager::set(mpz & target, unsigned sz, digit_t const * digits) { // remove zero digits while (sz > 0 && digits[sz - 1] == 0) sz--; if (sz == 0) reset(target); else if (sz == 1) set(target, digits[0]); else { #ifndef _MP_GMP target.m_val = 1; // number is positive. if (is_small(target)) { unsigned c = sz < m_init_cell_capacity ? m_init_cell_capacity : sz; target.m_ptr = allocate(c); target.m_ptr->m_size = sz; target.m_ptr->m_capacity = c; memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); } else { if (capacity(target) < sz) { SASSERT(sz > m_init_cell_capacity); deallocate(target.m_ptr); target.m_ptr = allocate(sz); target.m_ptr->m_size = sz; target.m_ptr->m_capacity = sz; memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); } else { target.m_ptr->m_size = sz; memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); } } #else mk_big(target); // reset mpz_set_ui(*target.m_ptr, digits[sz - 1]); SASSERT(sz > 0); unsigned i = sz - 1; while (i > 0) { --i; mpz_mul_2exp(*target.m_ptr, *target.m_ptr, 32); mpz_set_ui(m_tmp, digits[i]); mpz_add(*target.m_ptr, *target.m_ptr, m_tmp); } #endif } } #ifndef _MP_GMP template template void mpz_manager::big_add_sub(mpz const & a, mpz const & b, mpz & c) { int sign_a; int sign_b; mpz_cell * cell_a; mpz_cell * cell_b; get_sign_cell<0>(a, sign_a, cell_a); get_sign_cell<1>(b, sign_b, cell_b); if (SUB) sign_b = -sign_b; size_t real_sz; if (sign_a == sign_b) { unsigned sz = std::max(cell_a->m_size, cell_b->m_size)+1; ensure_tmp_capacity<0>(sz); m_mpn_manager.add(cell_a->m_digits, cell_a->m_size, cell_b->m_digits, cell_b->m_size, m_tmp[0]->m_digits, sz, &real_sz); SASSERT(real_sz <= sz); set<0>(c, sign_a, static_cast(real_sz)); } else { digit_t borrow; int r = m_mpn_manager.compare(cell_a->m_digits, cell_a->m_size, cell_b->m_digits, cell_b->m_size); if (r == 0) { reset(c); } else if (r < 0) { // a < b unsigned sz = cell_b->m_size; ensure_tmp_capacity<0>(sz); m_mpn_manager.sub(cell_b->m_digits, cell_b->m_size, cell_a->m_digits, cell_a->m_size, m_tmp[0]->m_digits, &borrow); SASSERT(borrow == 0); set<0>(c, sign_b, sz); } else { // a > b unsigned sz = cell_a->m_size; ensure_tmp_capacity<0>(sz); m_mpn_manager.sub(cell_a->m_digits, cell_a->m_size, cell_b->m_digits, cell_b->m_size, m_tmp[0]->m_digits, &borrow); SASSERT(borrow == 0); set<0>(c, sign_a, sz); } } } #endif template void mpz_manager::big_add(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP big_add_sub(a, b, c); #else // GMP version mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); mk_big(c); mpz_add(*c.m_ptr, *arg0, *arg1); #endif } template void mpz_manager::big_sub(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP big_add_sub(a, b, c); #else // GMP version mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); mk_big(c); mpz_sub(*c.m_ptr, *arg0, *arg1); #endif } template void mpz_manager::big_mul(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP int sign_a; int sign_b; mpz_cell * cell_a; mpz_cell * cell_b; get_sign_cell<0>(a, sign_a, cell_a); get_sign_cell<1>(b, sign_b, cell_b); unsigned sz = cell_a->m_size + cell_b->m_size; ensure_tmp_capacity<0>(sz); m_mpn_manager.mul(cell_a->m_digits, cell_a->m_size, cell_b->m_digits, cell_b->m_size, m_tmp[0]->m_digits); set<0>(c, sign_a == sign_b ? 1 : -1, sz); #else // GMP version mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); mk_big(c); mpz_mul(*c.m_ptr, *arg0, *arg1); #endif } #ifndef _MP_GMP template template void mpz_manager::quot_rem_core(mpz const & a, mpz const & b, mpz & q, mpz & r) { /* +26 / +7 = +3, remainder is +5 -26 / +7 = -3, remainder is -5 +26 / -7 = -3, remainder is +5 -26 / -7 = +3, remainder is -5 */ int sign_a; int sign_b; mpz_cell * cell_a; mpz_cell * cell_b; get_sign_cell<0>(a, sign_a, cell_a); get_sign_cell<1>(b, sign_b, cell_b); if (cell_b->m_size > cell_a->m_size) { if (MODE == REM_ONLY || MODE == QUOT_AND_REM) set(r, a); if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) reset(q); return; } unsigned q_sz = cell_a->m_size - cell_b->m_size + 1; unsigned r_sz = cell_b->m_size; ensure_tmp_capacity<0>(q_sz); ensure_tmp_capacity<1>(r_sz); m_mpn_manager.div(cell_a->m_digits, cell_a->m_size, cell_b->m_digits, cell_b->m_size, m_tmp[0]->m_digits, m_tmp[1]->m_digits); if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) set<0>(q, sign_a == sign_b ? 1 : -1, q_sz); if (MODE == REM_ONLY || MODE == QUOT_AND_REM) set<1>(r, sign_a, r_sz); } #endif template void mpz_manager::big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { #ifndef _MP_GMP quot_rem_core(a, b, q, r); #else // GMP version mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); mk_big(q); mk_big(r); mpz_tdiv_qr(*q.m_ptr, *r.m_ptr, *arg0, *arg1); #endif } template void mpz_manager::big_div(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP mpz dummy; quot_rem_core(a, b, c, dummy); SASSERT(is_zero(dummy)); #else // GMP version mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); mk_big(c); mpz_tdiv_q(*c.m_ptr, *arg0, *arg1); #endif } template void mpz_manager::big_rem(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP mpz dummy; quot_rem_core(a, b, dummy, c); SASSERT(is_zero(dummy)); #else // GMP version mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); mk_big(c); mpz_tdiv_r(*c.m_ptr, *arg0, *arg1); #endif } template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { if (is_small(a) && is_small(b)) { int _a = a.m_val; int _b = b.m_val; if (_a < 0) _a = -_a; if (_b < 0) _b = -_b; unsigned r = u_gcd(_a, _b); // Remark: r is (INT_MAX + 1) // If a == b == INT_MIN set(c, r); } else { #ifdef _MP_GMP mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); mk_big(c); mpz_gcd(*c.m_ptr, *arg0, *arg1); return; #endif if (is_zero(a)) { set(c, b); abs(c); return; } if (is_zero(b)) { set(c, a); abs(c); return; } #ifdef BINARY_GCD // Binary GCD for big numbers // - It doesn't use division // - The initial experiments, don't show any performance improvement // - It only works with _MP_INTERNAL mpz u, v, diff; set(u, a); set(v, b); abs(u); abs(v); unsigned k_u = power_of_two_multiple(u); unsigned k_v = power_of_two_multiple(v); unsigned k = k_u < k_v ? k_u : k_v; machine_div2k(u, k_u); while (true) { machine_div2k(v, k_v); if (lt(u, v)) { sub(v, u, v); } else { sub(u, v, diff); swap(u, v); swap(v, diff); } if (is_zero(v) || is_one(v)) break; // reset least significant bit if (is_small(v)) v.m_val &= ~1; else v.m_ptr->m_digits[0] &= ~static_cast(1); k_v = power_of_two_multiple(v); } mul2k(u, k, c); del(u); del(v); del(diff); #endif // BINARY_GCD #ifdef EUCLID_GCD mpz tmp1; mpz tmp2; mpz aux; set(tmp1, a); set(tmp2, b); abs(tmp1); abs(tmp2); if (lt(tmp1, tmp2)) swap(tmp1, tmp2); if (is_zero(tmp2)) { swap(c, tmp1); } else { while (true) { if (is_uint64(tmp1) && is_uint64(tmp2)) { set(c, u64_gcd(get_uint64(tmp1), get_uint64(tmp2))); break; } rem(tmp1, tmp2, aux); if (is_zero(aux)) { swap(c, tmp2); break; } swap(tmp1, tmp2); swap(tmp2, aux); } } del(tmp1); del(tmp2); del(aux); #endif // EUCLID_GCD #ifdef LS_BINARY_GCD mpz u, v, t, u1, u2; set(u, a); set(v, b); abs(u); abs(v); if (lt(u, v)) swap(u, v); while (!is_zero(v)) { // Basic idea: // compute t = 2^e*v such that t <= u < 2t // u := min{u - t, 2t - u} // // The assignment u := min{u - t, 2t - u} // can be replaced with u := u - t // // Since u and v are positive, we have: // 2^{log2(u)} <= u < 2^{(log2(u) + 1)} // 2^{log2(v)} <= v < 2^{(log2(v) + 1)} // --> // 2^{log2(v)}*2^{log2(u)-log2(v)} <= v*2^{log2(u)-log2(v)} < 2^{log2(v) + 1}*2^{log2(u)-log2(v)} // --> // 2^{log2(u)} <= v*2^{log2(u)-log2(v)} < 2^{log2(u) + 1} // // Now, let t be v*2^{log2(u)-log2(v)} // If t <= u, then we found t // Otherwise t = t div 2 unsigned k_u = log2(u); unsigned k_v = log2(v); SASSERT(k_v <= k_u); unsigned e = k_u - k_v; mul2k(v, e, t); sub(u, t, u1); if (is_neg(u1)) { // t is too big machine_div2k(t, 1); // Now, u1 contains u - 2t neg(u1); // Now, u1 contains 2t - u sub(u, t, u2); // u2 := u - t } else { // u1 contains u - t mul2k(t, 1); sub(t, u, u2); // u2 contains 2t - u } SASSERT(is_nonneg(u1)); SASSERT(is_nonneg(u2)); if (lt(u1, u2)) swap(u, u1); else swap(u, u2); if (lt(u, v)) swap(u,v); } swap(u, c); del(u); del(v); del(t); del(u1); del(u2); #endif // LS_BINARY_GCD #ifdef LEHMER_GCD // For now, it only works if sizeof(digit_t) == sizeof(unsigned) COMPILE_TIME_ASSERT(sizeof(digit_t) == sizeof(unsigned)); int64 a_hat, b_hat, A, B, C, D, T, q, a_sz, b_sz; mpz a1, b1, t, r, tmp; set(a1, a); set(b1, b); abs(a1); abs(b1); if (lt(a1, b1)) swap(a1, b1); while (true) { SASSERT(ge(a1, b1)); if (is_small(b1)) { if (is_small(a1)) { unsigned r = u_gcd(a1.m_val, b1.m_val); set(c, r); break; } else { while (!is_zero(b1)) { SASSERT(ge(a1, b1)); rem(a1, b1, tmp); swap(a1, b1); swap(b1, tmp); } swap(c, a1); break; } } SASSERT(!is_small(a1)); SASSERT(!is_small(b1)); a_sz = a1.m_ptr->m_size; b_sz = b1.m_ptr->m_size; SASSERT(b_sz <= a_sz); a_hat = a1.m_ptr->m_digits[a_sz - 1]; b_hat = (b_sz == a_sz) ? b1.m_ptr->m_digits[b_sz - 1] : 0; A = 1; B = 0; C = 0; D = 1; while (true) { // Loop invariants SASSERT(a_hat + A <= static_cast(UINT_MAX) + 1); SASSERT(a_hat + B < static_cast(UINT_MAX) + 1); SASSERT(b_hat + C < static_cast(UINT_MAX) + 1); SASSERT(b_hat + D <= static_cast(UINT_MAX) + 1); // overflows can't happen since I'm using int64 if (b_hat + C == 0 || b_hat + D == 0) break; q = (a_hat + A)/(b_hat + C); if (q != (a_hat + B)/(b_hat + D)) break; T = A - q*C; A = C; C = T; T = B - q*D; B = D; D = T; T = a_hat - q*b_hat; a_hat = b_hat; b_hat = T; } SASSERT(ge(a1, b1)); if (B == 0) { rem(a1, b1, t); swap(a1, b1); swap(b1, t); SASSERT(ge(a1, b1)); } else { // t <- A*a1 set(tmp, A); mul(a1, tmp, t); // t <- t + B*b1 set(tmp, B); addmul(t, tmp, b1, t); // r <- C*a1 set(tmp, C); mul(a1, tmp, r); // r <- r + D*b1 set(tmp, D); addmul(r, tmp, b1, r); // a <- t swap(a1, t); // b <- r swap(b1, r); SASSERT(ge(a1, b1)); } } del(a1); del(b1); del(r); del(t); del(tmp); #endif // LEHMER_GCD } } template unsigned mpz_manager::size_info(mpz const & a) { if (is_small(a)) return 1; #ifndef _MP_GMP return a.m_ptr->m_size + 1; #else return mpz_size(*a.m_ptr); #endif } template struct mpz_manager::sz_lt { mpz_manager & m; mpz const * m_as; sz_lt(mpz_manager & _m, mpz const * as):m(_m), m_as(as) {} bool operator()(unsigned p1, unsigned p2) { return m.size_info(m_as[p1]) < m.size_info(m_as[p2]); } }; template void mpz_manager::gcd(unsigned sz, mpz const * as, mpz & g) { #if 0 // Optimization: sort numbers by size. Motivation: compute the gcd of the small ones first. // The optimization did not really help. switch (sz) { case 0: reset(g); return; case 1: set(g, as[0]); abs(g); return; case 2: gcd(as[0], as[1], g); return; default: break; } unsigned i; for (i = 0; i < sz; i++) { if (!is_small(as[i])) break; } if (i != sz) { // array has big numbers sbuffer p; for (i = 0; i < sz; i++) p.push_back(i); sz_lt lt(*this, as); std::sort(p.begin(), p.end(), lt); TRACE("mpz_gcd", for (unsigned i = 0; i < sz; i++) tout << p[i] << ":" << size_info(as[p[i]]) << " "; tout << "\n";); gcd(as[p[0]], as[p[1]], g); for (i = 2; i < sz; i++) { if (is_one(g)) return; gcd(g, as[p[i]], g); } return; } else { gcd(as[0], as[1], g); for (unsigned i = 2; i < sz; i++) { if (is_one(g)) return; gcd(g, as[i], g); } } #else // Vanilla implementation switch (sz) { case 0: reset(g); return; case 1: set(g, as[0]); abs(g); return; default: break; } gcd(as[0], as[1], g); for (unsigned i = 2; i < sz; i++) { if (is_one(g)) return; gcd(g, as[i], g); } #endif } template void mpz_manager::gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & r) { mpz tmp1, tmp2; mpz aux, quot; set(tmp1, r1); set(tmp2, r2); set(a, 1); set(b, 0); mpz nexta, nextb; set(nexta, 0); set(nextb, 1); abs(tmp1); abs(tmp2); if (lt(tmp1, tmp2)) { swap(tmp1, tmp2); swap(nexta, nextb); swap(a, b); } // tmp1 >= tmp2 >= 0 // quot_rem in one function would be faster. while (is_pos(tmp2)) { SASSERT(ge(tmp1, tmp2)); // aux = tmp2 set(aux, tmp2); // quot = div(tmp1, tmp2); machine_div(tmp1, tmp2, quot); // tmp2 = tmp1 % tmp2 rem(tmp1, tmp2, tmp2); // tmp1 = aux set(tmp1, aux); // aux = nexta set(aux, nexta); // nexta = a - (quot*nexta) mul(quot, nexta, nexta); sub(a, nexta, nexta); // a = axu set(a, aux); // aux = nextb set(aux, nextb); // nextb = b - (quot*nextb) mul(nextb, quot, nextb); sub(b, nextb, nextb); // b = aux set(b, aux); } if (is_neg(r1)) neg(a); if (is_neg(r2)) neg(b); // SASSERT((a*r1) + (b*r2) == tmp1); #ifdef Z3DEBUG mul(a, r1, nexta); mul(b, r2, nextb); add(nexta, nextb, nexta); SASSERT(eq(nexta, tmp1)); #endif set(r, tmp1); del(tmp1); del(tmp2); del(aux); del(quot); del(nexta); del(nextb); } template void mpz_manager::lcm(mpz const & a, mpz const & b, mpz & c) { if (is_one(b)) { set(c, a); TRACE("lcm_bug", tout << "1. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); } else if (is_one(a) || eq(a, b)) { set(c, b); TRACE("lcm_bug", tout << "2. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); } else { mpz r; gcd(a, b, r); TRACE("lcm_bug", tout << "gcd(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(r) << "\n";); if (eq(r, a)) { set(c, b); TRACE("lcm_bug", tout << "3. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); } else if (eq(r, b)) { set(c, a); TRACE("lcm_bug", tout << "4. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); } else { // c contains gcd(a, b) // so c divides a, and machine_div(a, c) is equal to div(a, c) machine_div(a, r, r); mul(r, b, c); TRACE("lcm_bug", tout << "5. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); } del(r); } } template void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); TRACE("mpz", tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";); if (is_small(a) && is_small(b)) { del(c); c.m_val = a.m_val | b.m_val; } else { #ifndef _MP_GMP mpz a1, b1, a2, b2, m, tmp; set(a1, a); set(b1, b); set(m, 1); reset(c); while (!is_zero(a1) && !is_zero(b1)) { TRACE("mpz", tout << "a1: " << to_string(a1) << ", b1: " << to_string(b1) << "\n";); mod(a1, m_two64, a2); mod(b1, m_two64, b2); TRACE("mpz", tout << "a2: " << to_string(a2) << ", b2: " << to_string(b2) << "\n";); uint64 v = get_uint64(a2) | get_uint64(b2); TRACE("mpz", tout << "uint(a2): " << get_uint64(a2) << ", uint(b2): " << get_uint64(b2) << "\n";); set(tmp, v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v mul(m, m_two64, m); div(a1, m_two64, a1); div(b1, m_two64, b1); } if (!is_zero(a1)) { mul(a1, m, a1); add(c, a1, c); } if (!is_zero(b1)) { mul(b1, m, b1); add(c, b1, c); } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); mk_big(c); mpz_ior(*c.m_ptr, *arg0, *arg1); #endif } } template void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { del(c); c.m_val = a.m_val & b.m_val; } else { #ifndef _MP_GMP mpz a1, b1, a2, b2, m, tmp; set(a1, a); set(b1, b); set(m, 1); reset(c); while (!is_zero(a1) && !is_zero(b1)) { mod(a1, m_two64, a2); mod(b1, m_two64, b2); uint64 v = get_uint64(a2) & get_uint64(b2); set(tmp, v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v mul(m, m_two64, m); div(a1, m_two64, a1); div(b1, m_two64, b1); } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); mk_big(c); mpz_and(*c.m_ptr, *arg0, *arg1); #endif } } template void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) ^ i64(b)); } else { #ifndef _MP_GMP mpz a1, b1, a2, b2, m, tmp; set(a1, a); set(b1, b); set(m, 1); reset(c); while (!is_zero(a1) && !is_zero(b1)) { mod(a1, m_two64, a2); mod(b1, m_two64, b2); uint64 v = get_uint64(a2) ^ get_uint64(b2); set(tmp, v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v mul(m, m_two64, m); div(a1, m_two64, a1); div(b1, m_two64, b1); } if (!is_zero(a1)) { mul(a1, m, a1); add(c, a1, c); } if (!is_zero(b1)) { mul(b1, m, b1); add(c, b1, c); } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); mk_big(c); mpz_xor(*c.m_ptr, *arg0, *arg1); #endif } } template void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { SASSERT(is_nonneg(a)); if (is_small(a) && sz <= 63) { int64 mask = (static_cast(1) << sz) - static_cast(1); set_i64(c, (~ i64(a)) & mask); } else { mpz a1, a2, m, tmp; set(a1, a); set(m, 1); set(c, 0); while (sz > 0) { mod(a1, m_two64, a2); uint64 n = get_uint64(a2); uint64 v = ~n; SASSERT(~v == n); if (sz < 64) { uint64 mask = (1ull << static_cast(sz)) - 1ull; v = mask & v; } TRACE("bitwise_not", tout << "sz: " << sz << ", v: " << v << ", n: " << n << "\n";); set(tmp, v); SASSERT(get_uint64(tmp) == v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v mul(m, m_two64, m); div(a1, m_two64, a1); sz -= (sz<64) ? sz : 64; } del(a1); del(a2); del(m); del(tmp); TRACE("bitwise_not", tout << "sz: " << sz << " a: " << to_string(a) << " c: " << to_string(c) << "\n";); } } template void mpz_manager::big_set(mpz & target, mpz const & source) { #ifndef _MP_GMP if (&target == &source) return; target.m_val = source.m_val; if (is_small(target)) { target.m_ptr = allocate(capacity(source)); target.m_ptr->m_size = size(source); target.m_ptr->m_capacity = capacity(source); memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); } else { if (capacity(target) < size(source)) { deallocate(target.m_ptr); target.m_ptr = allocate(capacity(source)); target.m_ptr->m_size = size(source); target.m_ptr->m_capacity = capacity(source); memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); } else { target.m_ptr->m_size = size(source); memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); } } #else // GMP version mk_big(target); mpz_set(*target.m_ptr, *source.m_ptr); #endif } template int mpz_manager::big_compare(mpz const & a, mpz const & b) { #ifndef _MP_GMP int sign_a; int sign_b; mpz_cell * cell_a; mpz_cell * cell_b; get_sign_cell<0>(a, sign_a, cell_a); get_sign_cell<1>(b, sign_b, cell_b); if (sign_a > 0) { // a is positive if (sign_b > 0) { // a & b are positive return m_mpn_manager.compare(cell_a->m_digits, cell_a->m_size, cell_b->m_digits, cell_b->m_size); } else { // b is negative return 1; // a > b } } else { // a is negative if (sign_b > 0) { // b is positive return -1; // a < b } else { // a & b are negative return m_mpn_manager.compare(cell_b->m_digits, cell_b->m_size, cell_a->m_digits, cell_a->m_size); } } #else // GMP version mpz_t * arg0; mpz_t * arg1; get_arg<0>(a, arg0); get_arg<1>(b, arg1); return mpz_cmp(*arg0, *arg1); #endif } template bool mpz_manager::is_uint64(mpz const & a) const { #ifndef _MP_GMP if (a.m_val < 0) return false; if (is_small(a)) return true; if (sizeof(digit_t) == sizeof(uint64)) { return size(a) <= 1; } else { return size(a) <= 2; } #else // GMP version if (is_small(a)) return a.m_val >= 0; return is_nonneg(a) && mpz_cmp(*a.m_ptr, m_uint64_max) <= 0; #endif } template bool mpz_manager::is_int64(mpz const & a) const { if (is_small(a)) return true; #ifndef _MP_GMP if (!is_abs_uint64(a)) return false; uint64 num = big_abs_to_uint64(a); uint64 msb = static_cast(1) << 63; uint64 msb_val = msb & num; if (a.m_val >= 0) { // non-negative number. return (0 == msb_val); } else { // negative number. // either the high bit is 0, or // the number is 2^64 which can be represented. // return 0 == msb_val || (msb_val == num); } #else // GMP version return mpz_cmp(m_int64_min, *a.m_ptr) <= 0 && mpz_cmp(*a.m_ptr, m_int64_max) <= 0; #endif } template uint64 mpz_manager::get_uint64(mpz const & a) const { if (is_small(a)) return static_cast(a.m_val); #ifndef _MP_GMP SASSERT(a.m_ptr->m_size > 0); return big_abs_to_uint64(a); #else // GMP version if (sizeof(uint64) == sizeof(unsigned long)) { return mpz_get_ui(*a.m_ptr); } else { mpz_manager * _this = const_cast(this); mpz_set(_this->m_tmp, *a.m_ptr); mpz_mod(_this->m_tmp, m_tmp, m_two32); uint64 r = static_cast(mpz_get_ui(m_tmp)); mpz_set(_this->m_tmp, *a.m_ptr); mpz_div(_this->m_tmp, m_tmp, m_two32); r += static_cast(mpz_get_ui(m_tmp)) << static_cast(32); return r; } #endif } template int64 mpz_manager::get_int64(mpz const & a) const { if (is_small(a)) return static_cast(a.m_val); #ifndef _MP_GMP SASSERT(is_int64(a)); uint64 num = big_abs_to_uint64(a); if (a.m_val < 0) { if (num != 0 && (num << 1) == 0) return INT64_MIN; return -static_cast(num); } return static_cast(num); #else // GMP if (sizeof(int64) == sizeof(long) || mpz_fits_slong_p(*a.m_ptr)) { return mpz_get_si(*a.m_ptr); } else { mpz_manager * _this = const_cast(this); mpz_mod(_this->m_tmp, *a.m_ptr, m_two32); int64 r = static_cast(mpz_get_ui(m_tmp)); mpz_div(_this->m_tmp, *a.m_ptr, m_two32); r += static_cast(mpz_get_si(m_tmp)) << static_cast(32); return r; } #endif } template double mpz_manager::get_double(mpz const & a) const { if (is_small(a)) return static_cast(a.m_val); #ifndef _MP_GMP double r = 0.0; double d = 1.0; unsigned sz = size(a); for (unsigned i = 0; i < sz; i++) { r += d * static_cast(digits(a)[i]); if (sizeof(digit_t) == sizeof(uint64)) d *= static_cast(UINT64_MAX); // 64-bit version else d *= static_cast(UINT_MAX); // 32-bit version } return a.m_val < 0 ? -r : r; #else return mpz_get_d(*a.m_ptr); #endif } template void mpz_manager::display(std::ostream & out, mpz const & a) const { if (is_small(a)) { out << a.m_val; } else { #ifndef _MP_GMP if (a.m_val < 0) out << "-"; if (sizeof(digit_t) == 4) { sbuffer buffer(11*size(a), 0); out << m_mpn_manager.to_string(digits(a), size(a), buffer.begin(), buffer.size()); } else { sbuffer buffer(21*size(a), 0); out << m_mpn_manager.to_string(digits(a), size(a), buffer.begin(), buffer.size()); } #else // GMP version size_t sz = mpz_sizeinbase(*a.m_ptr, 10) + 2; sbuffer buffer(sz, 0); mpz_get_str(buffer.c_ptr(), 10, *a.m_ptr); out << buffer.c_ptr(); #endif } } template void mpz_manager::display_smt2(std::ostream & out, mpz const & a, bool decimal) const { if (is_neg(a)) { mpz_manager * _this = const_cast*>(this); _scoped_numeral > tmp(*_this); _this->set(tmp, a); _this->neg(tmp); out << "(- "; display(out, tmp); if (decimal) out << ".0"; out << ")"; } else { display(out, a); if (decimal) out << ".0"; } } template std::string mpz_manager::to_string(mpz const & a) const { std::ostringstream buffer; display(buffer, a); return buffer.str(); } template unsigned mpz_manager::hash(mpz const & a) { if (is_small(a)) return a.m_val; #ifndef _MP_GMP unsigned sz = size(a); if (sz == 1) return static_cast(digits(a)[0]); return string_hash(reinterpret_cast(digits(a)), sz * sizeof(digit_t), 17); #else return mpz_get_si(*a.m_ptr); #endif } template void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { #ifdef _MP_GMP if (!is_small(a)) { mk_big(b); mpz_pow_ui(*b.m_ptr, *a.m_ptr, p); return; } #endif #ifndef _MP_GMP if (is_small(a)) { if (a.m_val == 2) { if (p < 8 * sizeof(int) - 1) { del(b); b.m_val = 1 << p; } else { unsigned sz = p/(8 * sizeof(digit_t)) + 1; unsigned shift = p%(8 * sizeof(digit_t)); SASSERT(sz > 0); allocate_if_needed(b, sz); SASSERT(b.m_ptr->m_capacity >= sz); b.m_ptr->m_size = sz; for (unsigned i = 0; i < sz - 1; i++) b.m_ptr->m_digits[i] = 0; b.m_ptr->m_digits[sz-1] = 1 << shift; b.m_val = 1; } return; } if (a.m_val == 0) { SASSERT(p != 0); reset(b); return; } if (a.m_val == 1) { set(b, 1); return; } } #endif // general purpose unsigned mask = 1; mpz power; set(power, a); set(b, 1); while (mask <= p) { if (mask & p) mul(b, power, b); mul(power, power, power); mask = mask << 1; } del(power); } template bool mpz_manager::is_power_of_two(mpz const & a) { unsigned shift; return is_power_of_two(a, shift); } template bool mpz_manager::is_power_of_two(mpz const & a, unsigned & shift) { if (is_nonpos(a)) return false; if (is_small(a)) { if (::is_power_of_two(a.m_val)) { shift = ::log2((unsigned)a.m_val); return true; } else { return false; } } #ifndef _MP_GMP mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; digit_t * ds = c->m_digits; for (unsigned i = 0; i < sz - 1; i++) { if (ds[i] != 0) return false; } digit_t v = ds[sz-1]; if (!(v & (v - 1)) && v) { shift = log2(a); return true; } else { return false; } #else if (mpz_popcount(*a.m_ptr) == 1) { shift = log2(a); return true; } else { return false; } #endif } // Expand capacity of a #ifndef _MP_GMP template void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { if (capacity <= 1) return; if (capacity < m_init_cell_capacity) capacity = m_init_cell_capacity; if (is_small(a)) { a.m_ptr = allocate(capacity); SASSERT(a.m_ptr->m_capacity == capacity); if (a.m_val == INT_MIN) { unsigned intmin_sz = m_int_min.m_ptr->m_size; for (unsigned i = 0; i < intmin_sz; i++) a.m_ptr->m_digits[i] = m_int_min.m_ptr->m_digits[i]; a.m_val = -1; a.m_ptr->m_size = m_int_min.m_ptr->m_size; } else if (a.m_val < 0) { a.m_ptr->m_digits[0] = -a.m_val; a.m_val = -1; a.m_ptr->m_size = 1; } else { a.m_ptr->m_digits[0] = a.m_val; a.m_val = 1; a.m_ptr->m_size = 1; } } else { if (a.m_ptr->m_capacity >= capacity) return; mpz_cell * new_cell = allocate(capacity); SASSERT(new_cell->m_capacity == capacity); unsigned old_sz = a.m_ptr->m_size; new_cell->m_size = old_sz; for (unsigned i = 0; i < old_sz; i++) new_cell->m_digits[i] = a.m_ptr->m_digits[i]; deallocate(a.m_ptr); a.m_ptr = new_cell; } } template void mpz_manager::normalize(mpz & a) { mpz_cell * c = a.m_ptr; digit_t * ds = c->m_digits; unsigned i = c->m_size; for (; i > 0; --i) { if (ds[i-1] != 0) break; } if (i == 0) { // a is zero... reset(a); return; } if (i == 1 && ds[0] <= INT_MAX) { // a is small int val = a.m_val < 0 ? -static_cast(ds[0]) : static_cast(ds[0]); del(a); a.m_val = val; return; } // adjust size c->m_size = i; } #endif template void mpz_manager::machine_div2k(mpz & a, unsigned k) { if (k == 0 || is_zero(a)) return; if (is_small(a)) { if (k < 32) { int twok = 1 << k; a.m_val /= twok; } else { a.m_val = 0; } return; } #ifndef _MP_GMP unsigned digit_shift = k / (8 * sizeof(digit_t)); mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; if (digit_shift >= sz) { reset(a); return; } unsigned bit_shift = k % (8 * sizeof(digit_t)); unsigned comp_shift = (8 * sizeof(digit_t)) - bit_shift; unsigned new_sz = sz - digit_shift; SASSERT(new_sz >= 1); digit_t * ds = c->m_digits; TRACE("mpz_2k", tout << "bit_shift: " << bit_shift << ", comp_shift: " << comp_shift << ", new_sz: " << new_sz << ", sz: " << sz << "\n";); if (new_sz < sz) { unsigned i = 0; unsigned j = digit_shift; if (bit_shift != 0) { for (; i < new_sz - 1; i++, j++) { ds[i] = ds[j]; ds[i] >>= bit_shift; ds[i] |= (ds[j+1] << comp_shift); } ds[i] = ds[j]; ds[i] >>= bit_shift; } else { for (; i < new_sz; i++, j++) { ds[i] = ds[j]; } } } else { SASSERT(new_sz == sz); SASSERT(bit_shift != 0); unsigned i = 0; for (; i < new_sz - 1; i++) { ds[i] >>= bit_shift; ds[i] |= (ds[i+1] << comp_shift); } ds[i] >>= bit_shift; } c->m_size = new_sz; normalize(a); #else MPZ_BEGIN_CRITICAL(); mpz_t * arg0; get_arg<0>(a, arg0); mpz_tdiv_q_2exp(m_tmp, *arg0, k); mk_big(a); mpz_swap(*a.m_ptr, m_tmp); MPZ_END_CRITICAL(); #endif } template void mpz_manager::mul2k(mpz & a, unsigned k) { if (k == 0 || is_zero(a)) return; if (is_small(a) && k < 32) { set_i64(a, i64(a) * (static_cast(1) << k)); return; } #ifndef _MP_GMP TRACE("mpz_mul2k", tout << "mul2k\na: " << to_string(a) << "\nk: " << k << "\n";); unsigned word_shift = k / (8 * sizeof(digit_t)); unsigned bit_shift = k % (8 * sizeof(digit_t)); unsigned old_sz = is_small(a) ? 1 : a.m_ptr->m_size; unsigned new_sz = old_sz + word_shift + 1; ensure_capacity(a, new_sz); TRACE("mpz_mul2k", tout << "word_shift: " << word_shift << "\nbit_shift: " << bit_shift << "\nold_sz: " << old_sz << "\nnew_sz: " << new_sz << "\na after ensure capacity:\n" << to_string(a) << "\n";); SASSERT(!is_small(a)); mpz_cell * cell_a = a.m_ptr; old_sz = cell_a->m_size; digit_t * ds = cell_a->m_digits; for (unsigned i = old_sz; i < new_sz; i++) ds[i] = 0; cell_a->m_size = new_sz; if (word_shift > 0) { unsigned j = old_sz; unsigned i = old_sz + word_shift; while (j > 0) { --j; --i; ds[i] = ds[j]; } while (i > 0) { --i; ds[i] = 0; } } if (bit_shift > 0) { DEBUG_CODE({ for (unsigned i = 0; i < word_shift; i++) { SASSERT(ds[i] == 0); } }); unsigned comp_shift = (8 * sizeof(digit_t)) - bit_shift; digit_t prev = 0; for (unsigned i = word_shift; i < new_sz; i++) { digit_t new_prev = (ds[i] >> comp_shift); ds[i] <<= bit_shift; ds[i] |= prev; prev = new_prev; } } normalize(a); TRACE("mpz_mul2k", tout << "mul2k result:\n" << to_string(a) << "\n";); #else mpz_t * arg0; get_arg<0>(a, arg0); mk_big(a); mpz_mul_2exp(*a.m_ptr, *arg0, k); #endif } #ifndef _MP_GMP COMPILE_TIME_ASSERT(sizeof(digit_t) == 4 || sizeof(digit_t) == 8); #endif template unsigned mpz_manager::power_of_two_multiple(mpz const & a) { if (is_zero(a)) return 0; if (is_small(a)) { unsigned r = 0; int v = a.m_val; #define COUNT_DIGIT_RIGHT_ZEROS() \ if (v % (1 << 16) == 0) { \ r += 16; \ v /= (1 << 16); \ } \ if (v % (1 << 8) == 0) { \ r += 8; \ v /= (1 << 8); \ } \ if (v % (1 << 4) == 0) { \ r += 4; \ v /= (1 << 4); \ } \ if (v % (1 << 2) == 0) { \ r += 2; \ v /= (1 << 2); \ } \ if (v % 2 == 0) { \ r++; \ } COUNT_DIGIT_RIGHT_ZEROS(); return r; } #ifndef _MP_GMP mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; unsigned r = 0; digit_t * source = c->m_digits; for (unsigned i = 0; i < sz; i++) { if (source[i] != 0) { digit_t v = source[i]; if (sizeof(digit_t) == 8) { // TODO: we can remove this if after we move to MPN // In MPN the digit_t is always an unsigned integer if (static_cast(v) % (static_cast(1) << 32) == 0) { r += 32; v = static_cast(static_cast(v) / (static_cast(1) << 32)); } } COUNT_DIGIT_RIGHT_ZEROS(); return r; } r += (8 * sizeof(digit_t)); } return r; #else return mpz_scan1(*a.m_ptr, 0); #endif } template unsigned mpz_manager::log2(mpz const & a) { if (is_nonpos(a)) return 0; if (is_small(a)) return ::log2((unsigned)a.m_val); #ifndef _MP_GMP COMPILE_TIME_ASSERT(sizeof(digit_t) == 8 || sizeof(digit_t) == 4); mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; digit_t * ds = c->m_digits; if (sizeof(digit_t) == 8) return (sz - 1)*64 + uint64_log2(ds[sz-1]); else return (sz - 1)*32 + ::log2(static_cast(ds[sz-1])); #else unsigned r = mpz_sizeinbase(*a.m_ptr, 2); SASSERT(r > 0); return r - 1; #endif } template unsigned mpz_manager::mlog2(mpz const & a) { if (is_nonneg(a)) return 0; if (is_small(a)) return ::log2((unsigned)-a.m_val); #ifndef _MP_GMP COMPILE_TIME_ASSERT(sizeof(digit_t) == 8 || sizeof(digit_t) == 4); mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; digit_t * ds = c->m_digits; if (sizeof(digit_t) == 8) return (sz - 1)*64 + uint64_log2(ds[sz-1]); else return (sz - 1)*32 + ::log2(static_cast(ds[sz-1])); #else mpz_neg(m_tmp, *a.m_ptr); unsigned r = mpz_sizeinbase(m_tmp, 2); SASSERT(r > 0); return r - 1; #endif } template unsigned mpz_manager::bitsize(mpz const & a) { if (is_nonneg(a)) return log2(a) + 1; else return mlog2(a) + 1; } template bool mpz_manager::is_perfect_square(mpz const & a, mpz & root) { if (is_neg(a)) return false; reset(root); if (is_zero(a)) { return true; } if (is_one(a)) { set(root, 1); return true; } mpz lo, hi, mid, sq_lo, sq_hi, sq_mid; set(lo, 1); set(hi, a); set(sq_lo, 1); mul(hi, hi, sq_hi); bool result; // lo*lo <= *this < hi*hi while (true) { SASSERT(lt(lo, hi)); SASSERT(le(sq_lo, a) && lt(a, sq_hi)); if (eq(sq_lo, a)) { set(root, lo); result = true; break; } mpz & tmp = mid; add(lo, mpz(1), tmp); if (eq(tmp, hi)) { set(root, hi); result = false; break; } add(hi, lo, tmp); div(tmp, mpz(2), mid); SASSERT(lt(lo, mid) && lt(mid, hi)); mul(mid, mid, sq_mid); if (gt(sq_mid, a)) { set(hi, mid); set(sq_hi, sq_mid); } else { set(lo, mid); set(sq_lo, sq_mid); } } del(lo); del(hi); del(mid); del(sq_lo); del(sq_hi); del(sq_mid); return result; } static unsigned div_l(unsigned k, unsigned n) { return k/n; } static unsigned div_u(unsigned k, unsigned n) { return k%n == 0 ? k/n : k/n + 1; } template bool mpz_manager::root(mpz & a, unsigned n) { SASSERT(n % 2 != 0 || is_nonneg(a)); if (is_zero(a)) { return true; // precise } // Initial approximation // // We have that: // a > 0 -> 2^{log2(a)} <= a <= 2^{(log2(a) + 1)} // a < 0 -> -2^{log2(a) + 1} <= a <= -2^{log2(a)} // // Thus // a > 0 -> 2^{div_l(log2(a), n)} <= a^{1/n} <= 2^{div_u(log2(a) + 1, n)} // a < 0 -> -2^{div_u(log2(a) + 1, n)} <= a^{1/n} <= -2^{div_l(log2(a), n)} // mpz lower; mpz upper; mpz mid; mpz mid_n; if (is_pos(a)) { unsigned k = log2(a); power(mpz(2), div_l(k, n), lower); power(mpz(2), div_u(k + 1, n), upper); } else { unsigned k = mlog2(a); power(mpz(2), div_u(k + 1, n), lower); power(mpz(2), div_l(k, n), upper); neg(lower); neg(upper); } bool result; SASSERT(le(lower, upper)); if (eq(lower, upper)) { swap(a, lower); result = true; } else { // Refine using bisection. TODO: use Newton's method if this is a bottleneck while (true) { add(upper, lower, mid); machine_div2k(mid, 1); TRACE("mpz", tout << "upper: "; display(tout, upper); tout << "\nlower: "; display(tout, lower); tout << "\nmid: "; display(tout, mid); tout << "\n";); power(mid, n, mid_n); if (eq(mid_n, a)) { swap(a, mid); result = true; break; } if (eq(mid, lower) || eq(mid, upper)) { swap(a, upper); result = false; break; } if (lt(mid_n, a)) { // new lower bound swap(mid, lower); } else { SASSERT(lt(a, mid_n)); // new upper bound swap(mid, upper); } } } del(lower); del(upper); del(mid); del(mid_n); return result; } template bool mpz_manager::decompose(mpz const & a, svector & digits) { digits.reset(); if (is_small(a)) { if (a.m_val < 0) { digits.push_back(-a.m_val); return true; } else { digits.push_back(a.m_val); return false; } } else { #ifndef _MP_GMP mpz_cell * cell_a = a.m_ptr; unsigned sz = cell_a->m_size; for (unsigned i = 0; i < sz; i++) { digits.push_back(cell_a->m_digits[i]); } return a.m_val < 0; #else bool r = is_neg(a); mpz_set(m_tmp, *a.m_ptr); mpz_abs(m_tmp, m_tmp); while (mpz_sgn(m_tmp) != 0) { mpz_tdiv_r_2exp(m_tmp2, m_tmp, 32); unsigned v = mpz_get_ui(m_tmp2); digits.push_back(v); mpz_tdiv_q_2exp(m_tmp, m_tmp, 32); } return r; #endif } } template bool mpz_manager::divides(mpz const & a, mpz const & b) { _scoped_numeral > tmp(*this); bool r; if (is_zero(a)) { // I assume 0 | 0. // Remark a|b is a shorthand for (exists x. a x = b) // If b is zero, any x will do. If b != 0, then a does not divide b r = is_zero(b); } else { rem(b, a, tmp); r = is_zero(tmp); } STRACE("divides", tout << "[mpz] Divisible["; display(tout, b); tout << ", "; display(tout, a); tout << "] == " << (r?"True":"False") << "\n";); TRACE("divides_bug", tout << "tmp: "; display(tout, tmp); tout << "\n";); return r; } template class mpz_manager; template class mpz_manager; z3-z3-4.4.1/src/util/mpz.h000066400000000000000000000532571260446376700151640ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpz.h Abstract: Author: Leonardo de Moura (leonardo) 2010-06-17. Revision History: --*/ #ifndef MPZ_H_ #define MPZ_H_ #include #include #include"util.h" #include"small_object_allocator.h" #include"trace.h" #include"scoped_numeral.h" #include"scoped_numeral_vector.h" #include"z3_omp.h" #include"mpn.h" unsigned u_gcd(unsigned u, unsigned v); uint64 u64_gcd(uint64 u, uint64 v); #ifdef _MP_GMP typedef unsigned digit_t; #endif #ifdef _MSC_VER #pragma warning(disable : 4200) #endif template class mpz_manager; template class mpq_manager; #if !defined(_MP_GMP) && !defined(_MP_MSBIGNUM) && !defined(_MP_INTERNAL) #ifdef _WINDOWS #define _MP_INTERNAL #else #define _MP_GMP #endif #endif #if defined(_MP_MSBIGNUM) typedef size_t digit_t; #elif defined(_MP_INTERNAL) typedef unsigned int digit_t; #endif #ifndef _MP_GMP class mpz_cell { unsigned m_size; unsigned m_capacity; digit_t m_digits[0]; friend class mpz_manager; friend class mpz_manager; }; #else #include #endif /** \brief Multi-precision integer. If m_ptr == 0, the it is a small number and the value is stored at m_val. Otherwise, m_val contains the sign (-1 negative, 1 positive), and m_ptr points to a mpz_cell that store the value. <<< This last statement is true only in Windows. */ class mpz { int m_val; #ifndef _MP_GMP mpz_cell * m_ptr; #else mpz_t * m_ptr; #endif friend class mpz_manager; friend class mpz_manager; friend class mpq_manager; friend class mpq_manager; friend class mpq; friend class mpbq; friend class mpbq_manager; mpz & operator=(mpz const & other) { UNREACHABLE(); return *this; } public: mpz(int v):m_val(v), m_ptr(0) {} mpz():m_val(0), m_ptr(0) {} void swap(mpz & other) { std::swap(m_val, other.m_val); std::swap(m_ptr, other.m_ptr); } }; inline void swap(mpz & m1, mpz & m2) { m1.swap(m2); } template class mpz_manager { small_object_allocator m_allocator; omp_nest_lock_t m_lock; #define MPZ_BEGIN_CRITICAL() if (SYNCH) omp_set_nest_lock(&m_lock); #define MPZ_END_CRITICAL() if (SYNCH) omp_unset_nest_lock(&m_lock); mpn_manager m_mpn_manager; #ifndef _MP_GMP unsigned m_init_cell_capacity; mpz_cell * m_tmp[2]; mpz_cell * m_arg[2]; mpz m_int_min; static unsigned cell_size(unsigned capacity) { return sizeof(mpz_cell) + sizeof(digit_t) * capacity; } mpz_cell * allocate(unsigned capacity) { SASSERT(capacity >= m_init_cell_capacity); mpz_cell * cell = reinterpret_cast(m_allocator.allocate(cell_size(capacity))); cell->m_capacity = capacity; return cell; } // make sure that n is a big number and has capacity equal to at least c. void allocate_if_needed(mpz & n, unsigned c) { if (c < m_init_cell_capacity) c = m_init_cell_capacity; if (is_small(n)) { n.m_val = 1; n.m_ptr = allocate(c); n.m_ptr->m_capacity = c; } else if (capacity(n) < c) { deallocate(n.m_ptr); n.m_val = 1; n.m_ptr = allocate(c); n.m_ptr->m_capacity = c; } } void deallocate(mpz_cell * ptr) { m_allocator.deallocate(cell_size(ptr->m_capacity), ptr); } /** \brief Make sure that m_tmp[IDX] can hold the given number of digits */ template void ensure_tmp_capacity(unsigned capacity) { if (m_tmp[IDX]->m_capacity >= capacity) return; deallocate(m_tmp[IDX]); unsigned new_capacity = (3 * capacity + 1) >> 1; m_tmp[IDX] = allocate(new_capacity); SASSERT(m_tmp[IDX]->m_capacity >= capacity); } // Expand capacity of a while preserving its content. void ensure_capacity(mpz & a, unsigned sz); void normalize(mpz & a); #else // GMP code mpz_t m_tmp, m_tmp2; mpz_t m_two32; mpz_t * m_arg[2]; mpz_t m_uint64_max; mpz_t m_int64_max; mpz_t m_int64_min; mpz_t * allocate() { mpz_t * cell = reinterpret_cast(m_allocator.allocate(sizeof(mpz_t))); mpz_init(*cell); return cell; } void deallocate(mpz_t * ptr) { mpz_clear(*ptr); m_allocator.deallocate(sizeof(mpz_t), ptr); } #endif mpz m_two64; /** \brief Set \c a with the value stored at m_tmp[IDX], and the given sign. \c sz is an overapproximation of the the size of the number stored at \c tmp. */ template void set(mpz & a, int sign, unsigned sz); static int64 i64(mpz const & a) { return static_cast(a.m_val); } void set_big_i64(mpz & c, int64 v); void set_i64(mpz & c, int64 v) { if (v >= INT_MIN && v <= INT_MAX) { del(c); c.m_val = static_cast(v); } else { MPZ_BEGIN_CRITICAL(); set_big_i64(c, v); MPZ_END_CRITICAL(); } } void set_big_ui64(mpz & c, uint64 v); #ifndef _MP_GMP static unsigned capacity(mpz const & c) { return c.m_ptr->m_capacity; } static unsigned size(mpz const & c) { return c.m_ptr->m_size; } static digit_t * digits(mpz const & c) { return c.m_ptr->m_digits; } // Return true if the absolute value fits in a UINT64 static bool is_abs_uint64(mpz const & a) { if (is_small(a)) return true; if (sizeof(digit_t) == sizeof(uint64)) return size(a) <= 1; else return size(a) <= 2; } // CAST the absolute value into a UINT64 static uint64 big_abs_to_uint64(mpz const & a) { SASSERT(is_abs_uint64(a)); SASSERT(!is_small(a)); if (a.m_ptr->m_size == 1) return digits(a)[0]; if (sizeof(digit_t) == sizeof(uint64)) // 64-bit machine return digits(a)[0]; else // 32-bit machine return ((static_cast(digits(a)[1]) << 32) | (static_cast(digits(a)[0]))); } template void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell) { if (is_small(a)) { if (a.m_val == INT_MIN) { sign = -1; cell = m_int_min.m_ptr; } else { cell = m_arg[IDX]; SASSERT(cell->m_size == 1); if (a.m_val < 0) { sign = -1; cell->m_digits[0] = -a.m_val; } else { sign = 1; cell->m_digits[0] = a.m_val; } } } else { sign = a.m_val; cell = a.m_ptr; } } #else // GMP code template void get_arg(mpz const & a, mpz_t * & result) { if (is_small(a)) { result = m_arg[IDX]; mpz_set_si(*result, a.m_val); } else { result = a.m_ptr; } } void mk_big(mpz & a) { if (a.m_ptr == 0) { a.m_val = 0; a.m_ptr = allocate(); } } #endif #ifndef _MP_GMP template void big_add_sub(mpz const & a, mpz const & b, mpz & c); #endif void big_add(mpz const & a, mpz const & b, mpz & c); void big_sub(mpz const & a, mpz const & b, mpz & c); void big_mul(mpz const & a, mpz const & b, mpz & c); void big_set(mpz & target, mpz const & source); #ifndef _MP_GMP #define QUOT_ONLY 0 #define REM_ONLY 1 #define QUOT_AND_REM 2 #define qr_mode int template void quot_rem_core(mpz const & a, mpz const & b, mpz & q, mpz & r); #endif void big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r); void big_div(mpz const & a, mpz const & b, mpz & c); void big_rem(mpz const & a, mpz const & b, mpz & c); int big_compare(mpz const & a, mpz const & b); unsigned size_info(mpz const & a); struct sz_lt; public: static bool precise() { return true; } static bool field() { return false; } typedef mpz numeral; mpz_manager(); ~mpz_manager(); static bool is_small(mpz const & a) { return a.m_ptr == 0; } static mpz mk_z(int val) { return mpz(val); } void del(mpz & a) { if (a.m_ptr != 0) { MPZ_BEGIN_CRITICAL(); deallocate(a.m_ptr); MPZ_END_CRITICAL(); a.m_ptr = 0; } } void add(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz] " << to_string(a) << " + " << to_string(b) << " == ";); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) + i64(b)); } else { MPZ_BEGIN_CRITICAL(); big_add(a, b, c); MPZ_END_CRITICAL(); } STRACE("mpz", tout << to_string(c) << "\n";); } void sub(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz] " << to_string(a) << " - " << to_string(b) << " == ";); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) - i64(b)); } else { MPZ_BEGIN_CRITICAL(); big_sub(a, b, c); MPZ_END_CRITICAL(); } STRACE("mpz", tout << to_string(c) << "\n";); } void inc(mpz & a) { add(a, mpz(1), a); } void dec(mpz & a) { add(a, mpz(-1), a); } void mul(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz] " << to_string(a) << " * " << to_string(b) << " == ";); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) * i64(b)); } else { MPZ_BEGIN_CRITICAL(); big_mul(a, b, c); MPZ_END_CRITICAL(); } STRACE("mpz", tout << to_string(c) << "\n";); } // d <- a + b*c void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { if (is_one(b)) { add(a, c, d); } else if (is_minus_one(b)) { sub(a, c, d); } else { mpz tmp; mul(b,c,tmp); add(a,tmp,d); del(tmp); } } // d <- a - b*c void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { if (is_one(b)) { sub(a, c, d); } else if (is_minus_one(b)) { add(a, c, d); } else { mpz tmp; mul(b,c,tmp); sub(a,tmp,d); del(tmp); } } void machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { STRACE("mpz", tout << "[mpz-ext] divrem(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_small(a) && is_small(b)) { int64 _a = i64(a); int64 _b = i64(b); set_i64(q, _a / _b); set_i64(r, _a % _b); } else { MPZ_BEGIN_CRITICAL(); big_div_rem(a, b, q, r); MPZ_END_CRITICAL(); } STRACE("mpz", tout << "(" << to_string(q) << ", " << to_string(r) << ")\n";); } void machine_div(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz-ext] machine-div(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) / i64(b)); } else { MPZ_BEGIN_CRITICAL(); big_div(a, b, c); MPZ_END_CRITICAL(); } STRACE("mpz", tout << to_string(c) << "\n";); } void rem(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz-ext] rem(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) % i64(b)); } else { MPZ_BEGIN_CRITICAL(); big_rem(a, b, c); MPZ_END_CRITICAL(); } STRACE("mpz", tout << to_string(c) << "\n";); } void div(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz-ext] div(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_neg(a)) { mpz tmp; machine_div_rem(a, b, c, tmp); if (!is_zero(tmp)) { if (is_neg(b)) add(c, mk_z(1), c); else sub(c, mk_z(1), c); } del(tmp); } else { machine_div(a, b, c); } STRACE("mpz", tout << to_string(c) << "\n";); } void mod(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz-ext] mod(" << to_string(a) << ", " << to_string(b) << ") == ";); rem(a, b, c); if (is_neg(c)) { if (is_pos(b)) add(c, b, c); else sub(c, b, c); } STRACE("mpz", tout << to_string(c) << "\n";); } void neg(mpz & a) { STRACE("mpz", tout << "[mpz] 0 - " << to_string(a) << " == ";); if (is_small(a) && a.m_val == INT_MIN) { // neg(INT_MIN) is not a small int set_big_i64(a, - static_cast(INT_MIN)); return; } #ifndef _MP_GMP a.m_val = -a.m_val; #else if (is_small(a)) { a.m_val = -a.m_val; } else { mpz_neg(*a.m_ptr, *a.m_ptr); } #endif STRACE("mpz", tout << to_string(a) << "\n";); } void abs(mpz & a) { if (is_small(a)) { if (a.m_val < 0) { if (a.m_val == INT_MIN) { // abs(INT_MIN) is not a small int set_big_i64(a, - static_cast(INT_MIN)); } else a.m_val = -a.m_val; } } else { #ifndef _MP_GMP a.m_val = 1; #else mpz_abs(*a.m_ptr, *a.m_ptr); #endif } } static bool is_pos(mpz const & a) { #ifndef _MP_GMP return a.m_val > 0; #else if (is_small(a)) return a.m_val > 0; else return mpz_sgn(*a.m_ptr) > 0; #endif } static bool is_neg(mpz const & a) { #ifndef _MP_GMP return a.m_val < 0; #else if (is_small(a)) return a.m_val < 0; else return mpz_sgn(*a.m_ptr) < 0; #endif } static bool is_zero(mpz const & a) { #ifndef _MP_GMP return a.m_val == 0; #else if (is_small(a)) return a.m_val == 0; else return mpz_sgn(*a.m_ptr) == 0; #endif } static int sign(mpz const & a) { #ifndef _MP_GMP return a.m_val; #else if (is_small(a)) return a.m_val; else return mpz_sgn(*a.m_ptr); #endif } static bool is_nonpos(mpz const & a) { return !is_pos(a); } static bool is_nonneg(mpz const & a) { return !is_neg(a); } bool eq(mpz const & a, mpz const & b) { if (is_small(a) && is_small(b)) { return a.m_val == b.m_val; } else { MPZ_BEGIN_CRITICAL(); bool res = big_compare(a, b) == 0; MPZ_END_CRITICAL(); return res; } } bool lt(mpz const & a, mpz const & b) { if (is_small(a) && is_small(b)) { return a.m_val < b.m_val; } else { MPZ_BEGIN_CRITICAL(); bool res = big_compare(a, b) < 0; MPZ_END_CRITICAL(); return res; } } bool neq(mpz const & a, mpz const & b) { return !eq(a, b); } bool gt(mpz const & a, mpz const & b) { return lt(b, a); } bool ge(mpz const & a, mpz const & b) { return !lt(a, b); } bool le(mpz const & a, mpz const & b) { return !lt(b, a); } void gcd(mpz const & a, mpz const & b, mpz & c); void gcd(unsigned sz, mpz const * as, mpz & g); /** \brief Extended Euclid: r1*a + r2*b = g */ void gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & g); void lcm(mpz const & a, mpz const & b, mpz & c); /** \brief Return true if a | b */ bool divides(mpz const & a, mpz const & b); // not a field void inv(mpz & a) { SASSERT(false); } void bitwise_or(mpz const & a, mpz const & b, mpz & c); void bitwise_and(mpz const & a, mpz const & b, mpz & c); void bitwise_xor(mpz const & a, mpz const & b, mpz & c); void bitwise_not(unsigned sz, mpz const & a, mpz & c); void set(mpz & target, mpz const & source) { if (is_small(source)) { del(target); target.m_val = source.m_val; } else { MPZ_BEGIN_CRITICAL(); big_set(target, source); MPZ_END_CRITICAL(); } } void set(mpz & a, int val) { del(a); a.m_val = val; } void set(mpz & a, unsigned val) { if (val <= INT_MAX) set(a, static_cast(val)); else set(a, static_cast(static_cast(val))); } void set(mpz & a, char const * val); void set(mpz & a, int64 val) { set_i64(a, val); } void set(mpz & a, uint64 val) { if (val < INT_MAX) { del(a); a.m_val = static_cast(val); } else { MPZ_BEGIN_CRITICAL(); set_big_ui64(a, val); MPZ_END_CRITICAL(); } } void set(mpz & target, unsigned sz, digit_t const * digits); void reset(mpz & a) { del(a); a.m_val = 0; } void swap(mpz & a, mpz & b) { std::swap(a.m_val, b.m_val); std::swap(a.m_ptr, b.m_ptr); } bool is_uint64(mpz const & a) const; bool is_int64(mpz const & a) const; uint64 get_uint64(mpz const & a) const; int64 get_int64(mpz const & a) const; bool is_uint(mpz const & a) const { return is_uint64(a) && get_uint64(a) < UINT_MAX; } unsigned get_uint(mpz const & a) const { SASSERT(is_uint(a)); return static_cast(get_uint64(a)); } bool is_int(mpz const & a) const { return is_int64(a) && INT_MIN < get_int64(a) && get_int64(a) < INT_MAX; } int get_int(mpz const & a) const { SASSERT(is_int(a)); return static_cast(get_int64(a)); } double get_double(mpz const & a) const; std::string to_string(mpz const & a) const; void display(std::ostream & out, mpz const & a) const; /** \brief Display mpz number in SMT 2.0 format. If decimal == true, then ".0" is appended. */ void display_smt2(std::ostream & out, mpz const & a, bool decimal) const; static unsigned hash(mpz const & a); static bool is_one(mpz const & a) { #ifndef _MP_GMP return is_small(a) && a.m_val == 1; #else if (is_small(a)) return a.m_val == 1; return mpz_cmp_si(*a.m_ptr, 1) == 0; #endif } static bool is_minus_one(mpz const & a) { #ifndef _MP_GMP return is_small(a) && a.m_val == -1; #else if (is_small(a)) return a.m_val == -1; return mpz_cmp_si(*a.m_ptr, -1) == 0; #endif } void power(mpz const & a, unsigned p, mpz & b); bool is_power_of_two(mpz const & a); bool is_power_of_two(mpz const & a, unsigned & shift); void machine_div2k(mpz & a, unsigned k); void machine_div2k(mpz const & a, unsigned k, mpz & r) { set(r, a); machine_div2k(r, k); } void mul2k(mpz & a, unsigned k); void mul2k(mpz const & a, unsigned k, mpz & r) { set(r, a); mul2k(r, k); } /** \brief Return largest k s.t. n is a multiple of 2^k */ unsigned power_of_two_multiple(mpz const & n); /** \brief Return the position of the most significant bit. Return 0 if the number is negative */ unsigned log2(mpz const & n); /** \brief log2(-n) Return 0 if the number is nonegative */ unsigned mlog2(mpz const & n); /** \brief Return the bit-size of n. This method is mainly used for collecting statistics. */ unsigned bitsize(mpz const & n); /** \brief Return true if the number is a perfect square, and store the square root in 'root'. If the number n is positive and the result is false, then root will contain the smallest integer r such that r*r > n. */ bool is_perfect_square(mpz const & a, mpz & root); /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpz const & a) { return log2(a); } /** \brief Return true if a^{1/n} is an integer, and store the result in a. Otherwise return false, and update a with the smallest integer r such that r*r > n. \remark This method assumes that if n is even, then a is nonegative */ bool root(mpz & a, unsigned n); bool root(mpz const & a, unsigned n, mpz & r) { set(r, a); return root(r, n); } bool is_even(mpz const & a) { if (is_small(a)) return !(a.m_val & 0x1); #ifndef _MP_GMP return !(0x1 & digits(a)[0]); #else return mpz_even_p(*a.m_ptr); #endif } bool is_odd(mpz const & n) { return !is_even(n); } // Store the digits of n into digits, and return the sign. bool decompose(mpz const & n, svector & digits); }; typedef mpz_manager synch_mpz_manager; typedef mpz_manager unsynch_mpz_manager; typedef _scoped_numeral scoped_mpz; typedef _scoped_numeral scoped_synch_mpz; typedef _scoped_numeral_vector scoped_mpz_vector; #endif /* MPZ_H_ */ z3-z3-4.4.1/src/util/mpzzp.h000066400000000000000000000254531260446376700155330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpzzp.h Abstract: Combines Z ring, GF(p) finite field, and Z_p ring (when p is not a prime) in a single manager; That is, the manager may be dynamically configured to be Z Ring, GF(p), etc. Author: Leonardo 2012-01-17. Revision History: This code is based on mpzp.h. In the future, it will replace it. --*/ #ifndef MPZZP_H_ #define MPZZP_H_ #include "mpz.h" class mpzzp_manager { typedef unsynch_mpz_manager numeral_manager; numeral_manager & m_manager; bool m_z; // instead the usual [0..p) we will keep the numbers in [lower, upper] mpz m_p, m_lower, m_upper; bool m_p_prime; mpz m_inv_tmp1, m_inv_tmp2, m_inv_tmp3; mpz m_div_tmp; bool is_p_normalized_core(mpz const & x) const { return m().ge(x, m_lower) && m().le(x, m_upper); } void setup_p() { SASSERT(m().is_pos(m_p) && !m().is_one(m_p)); bool even = m().is_even(m_p); m().div(m_p, 2, m_upper); m().set(m_lower, m_upper); m().neg(m_lower); if (even) { m().inc(m_lower); } TRACE("mpzzp", tout << "lower: " << m_manager.to_string(m_lower) << ", upper: " << m_manager.to_string(m_upper) << "\n";); } void p_normalize_core(mpz & x) { SASSERT(!m_z); m().rem(x, m_p, x); if (m().gt(x, m_upper)) { m().sub(x, m_p, x); } else { if (m().lt(x, m_lower)) { m().add(x, m_p, x); } } SASSERT(is_p_normalized(x)); } public: typedef mpz numeral; static bool precise() { return true; } bool field() { return !m_z && m_p_prime; } bool finite() const { return !m_z; } bool modular() const { return !m_z; } mpzzp_manager(numeral_manager & _m): m_manager(_m), m_z(true) { } mpzzp_manager(numeral_manager & _m, mpz const & p, bool prime = true): m_manager(_m), m_z(false) { m().set(m_p, p); setup_p(); } mpzzp_manager(numeral_manager & _m, uint64 p, bool prime = true): m_manager(_m), m_z(false) { m().set(m_p, p); setup_p(); } ~mpzzp_manager() { m().del(m_p); m().del(m_lower); m().del(m_upper); m().del(m_inv_tmp1); m().del(m_inv_tmp2); m().del(m_inv_tmp3); m().del(m_div_tmp); } bool is_p_normalized(mpz const & x) const { return m_z || is_p_normalized_core(x); } void p_normalize(mpz & x) { if (!m_z) p_normalize_core(x); SASSERT(is_p_normalized(x)); } numeral_manager & m() const { return m_manager; } mpz const & p() const { return m_p; } void set_z() { m_z = true; } void set_zp(mpz const & new_p) { m_z = false; m_p_prime = true; m().set(m_p, new_p); setup_p(); } void set_zp(uint64 new_p) { m_z = false; m_p_prime = true; m().set(m_p, new_p); setup_p(); } // p = p^2 void set_p_sq() { SASSERT(!m_z); m_p_prime = false; m().mul(m_p, m_p, m_p); setup_p(); } void set_zp_swap(mpz & new_p) { SASSERT(!m_z); m().swap(m_p, new_p); setup_p(); } void reset(mpz & a) { m().reset(a); } bool is_small(mpz const & a) { return m().is_small(a); } void del(mpz & a) { m().del(a); } void neg(mpz & a) { m().neg(a); p_normalize(a); } void abs(mpz & a) { m().abs(a); p_normalize(a); } bool is_zero(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_zero(a); } bool is_one(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_one(a); } bool is_pos(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_pos(a); } bool is_neg(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_neg(a); } bool is_nonpos(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_nonpos(a); } bool is_nonneg(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_nonneg(a); } bool is_minus_one(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_minus_one(a); } bool eq(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().eq(a, b); } bool lt(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().lt(a, b); } bool le(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().le(a, b); } bool gt(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().gt(a, b); } bool ge(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().ge(a, b); } std::string to_string(mpz const & a) const { SASSERT(is_p_normalized(a)); return m().to_string(a); } void display(std::ostream & out, mpz const & a) const { m().display(out, a); } void add(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().add(a, b, c); p_normalize(c); } void sub(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().sub(a, b, c); p_normalize(c); } void inc(mpz & a) { SASSERT(is_p_normalized(a)); m().inc(a); p_normalize(a); } void dec(mpz & a) { SASSERT(is_p_normalized(a)); m().dec(a); p_normalize(a); } void mul(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().mul(a, b, c); p_normalize(c); } void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { SASSERT(is_p_normalized(a) && is_p_normalized(b) && is_p_normalized(c)); m().addmul(a, b, c, d); p_normalize(d); } // d <- a - b*c void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { SASSERT(is_p_normalized(a)); SASSERT(is_p_normalized(b)); SASSERT(is_p_normalized(c)); m().submul(a, b, c, d); p_normalize(d); } void inv(mpz & a) { if (m_z) { UNREACHABLE(); } else { SASSERT(!is_zero(a)); // eulers theorem a^(p - 2), but gcd could be more efficient // a*t1 + p*t2 = 1 => a*t1 = 1 (mod p) => t1 is the inverse (t3 == 1) TRACE("mpzp_inv_bug", tout << "a: " << m().to_string(a) << ", p: " << m().to_string(m_p) << "\n";); p_normalize(a); TRACE("mpzp_inv_bug", tout << "after normalization a: " << m().to_string(a) << "\n";); m().gcd(a, m_p, m_inv_tmp1, m_inv_tmp2, m_inv_tmp3); TRACE("mpzp_inv_bug", tout << "tmp1: " << m().to_string(m_inv_tmp1) << "\ntmp2: " << m().to_string(m_inv_tmp2) << "\ntmp3: " << m().to_string(m_inv_tmp3) << "\n";); p_normalize(m_inv_tmp1); m().swap(a, m_inv_tmp1); SASSERT(m().is_one(m_inv_tmp3)); // otherwise p is not prime and inverse is not defined } } void swap(mpz & a, mpz & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().swap(a, b); } bool divides(mpz const & a, mpz const & b) { return (field() && !is_zero(a)) || m().divides(a, b); } // a/b = a*inv(b) void div(mpz const & a, mpz const & b, mpz & c) { if (m_z) { return m().div(a, b, c); } else { SASSERT(m_p_prime); SASSERT(is_p_normalized(a)); m().set(m_div_tmp, b); inv(m_div_tmp); mul(a, m_div_tmp, c); SASSERT(is_p_normalized(c)); } } static unsigned hash(mpz const & a) { return numeral_manager::hash(a); } void gcd(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().gcd(a, b, c); SASSERT(is_p_normalized(c)); } void gcd(unsigned sz, mpz const * as, mpz & g) { m().gcd(sz, as, g); SASSERT(is_p_normalized(g)); } void gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & g) { SASSERT(is_p_normalized(r1) && is_p_normalized(r2)); m().gcd(r1, r2, a, b, g); p_normalize(a); p_normalize(b); } void set(mpz & a, mpz & val) { m().set(a, val); p_normalize(a); } void set(mpz & a, int val) { m().set(a, val); p_normalize(a); } void set(mpz & a, unsigned val) { m().set(a, val); p_normalize(a); } void set(mpz & a, char const * val) { m().set(a, val); p_normalize(a); } void set(mpz & a, int64 val) { m().set(a, val); p_normalize(a); } void set(mpz & a, uint64 val) { m().set(a, val); p_normalize(a); } void set(mpz & a, mpz const & val) { m().set(a, val); p_normalize(a); } bool is_uint64(mpz & a) const { const_cast(this)->p_normalize(a); return m().is_uint64(a); } bool is_int64(mpz & a) const { const_cast(this)->p_normalize(a); return m().is_int64(a); } uint64 get_uint64(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_uint64(a); } int64 get_int64(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_int64(a); } double get_double(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_double(a); } void power(mpz const & a, unsigned k, mpz & b) { SASSERT(is_p_normalized(a)); unsigned mask = 1; mpz power; set(power, a); set(b, 1); while (mask <= k) { if (mask & k) mul(b, power, b); mul(power, power, power); mask = mask << 1; } del(power); } bool is_perfect_square(mpz const & a, mpz & root) { if (m_z) { return m().is_perfect_square(a, root); } else { NOT_IMPLEMENTED_YET(); return false; } } bool is_uint64(mpz const & a) const { return m().is_uint64(a); } bool is_int64(mpz const & a) const { return m().is_int64(a); } uint64 get_uint64(mpz const & a) const { return m().get_uint64(a); } int64 get_int64(mpz const & a) const { return m().get_int64(a); } void mul2k(mpz & a, unsigned k) { m().mul2k(a, k); p_normalize(a); } void mul2k(mpz const & a, unsigned k, mpz & r) { m().mul2k(a, k, r); p_normalize(r); } unsigned power_of_two_multiple(mpz const & n) { return m().power_of_two_multiple(n); } unsigned log2(mpz const & n) { return m().log2(n); } unsigned mlog2(mpz const & n) { return m().mlog2(n); } void machine_div2k(mpz & a, unsigned k) { m().machine_div2k(a, k); SASSERT(is_p_normalized(a)); } void machine_div2k(mpz const & a, unsigned k, mpz & r) { m().machine_div2k(a, k, r); SASSERT(is_p_normalized(r)); } bool root(mpz & a, unsigned n) { SASSERT(!modular()); return m().root(a, n); } bool root(mpz const & a, unsigned n, mpz & r) { SASSERT(!modular()); return m().root(a, n, r); } }; #endif z3-z3-4.4.1/src/util/nat_set.h000066400000000000000000000033541260446376700160040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: nat_set.h Abstract: Set of natural number with fast reset method. Author: Leonardo de Moura (leonardo) 2007-03-27. Revision History: --*/ #ifndef NAT_SET_H_ #define NAT_SET_H_ #include #include"vector.h" class nat_set { unsigned m_curr_timestamp; svector m_timestamps; public: nat_set(unsigned s = 0): m_curr_timestamp(0), m_timestamps() { if (s > 0) { m_timestamps.resize(s, 0); } } // A nat_set is a function from [0..s-1] -> boolean. // This method sets the domain of this function. void set_domain(unsigned s) { m_timestamps.resize(s, 0); } unsigned get_domain() const { return m_timestamps.size(); } // Assure that v is in the domain of the set. void assure_domain(unsigned v) { if (v >= get_domain()) { set_domain(v+1); } } bool contains(unsigned v) const { return m_timestamps[v] > m_curr_timestamp; } void insert(unsigned v) { m_timestamps[v] = m_curr_timestamp + 1; } void remove(unsigned v) { m_timestamps[v] = m_curr_timestamp; } void reset() { m_curr_timestamp++; if (m_curr_timestamp == UINT_MAX) { m_timestamps.fill(0); m_curr_timestamp = 0; } } bool empty() const { svector::const_iterator it = m_timestamps.begin(); svector::const_iterator end = m_timestamps.end(); for (; it != end; ++it) { if (*it > m_curr_timestamp) { return false; } } return true; } }; #endif /* NAT_SET_H_ */ z3-z3-4.4.1/src/util/numeral_buffer.h000066400000000000000000000033711260446376700173420ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: numeral_buffer.h Abstract: Basic buffer for managing big nums. Author: Leonardo de Moura (leonardo) 2011-06-18. Revision History: --*/ #ifndef NUMERAL_BUFFER_H_ #define NUMERAL_BUFFER_H_ #include"vector.h" template class numeral_buffer { NumeralManager & m_manager; svector m_buffer; public: typedef Numeral numeral; typedef Numeral data; typedef NumeralManager manager; numeral_buffer(NumeralManager & m):m_manager(m) {} ~numeral_buffer() { reset(); } NumeralManager & m() const { return m_manager; } unsigned size() const { return m_buffer.size(); } bool empty() const { return m_buffer.empty(); } void push_back(Numeral const & num) { m_buffer.push_back(Numeral()); m().set(m_buffer.back(), num); } void pop_back() { m().del(m_buffer.back()); m_buffer.pop_back(); } Numeral & back() { return m_buffer.back(); } Numeral const & back() const { return m_buffer.back(); } Numeral const & operator[](unsigned idx) const { return m_buffer[idx]; } Numeral & operator[](unsigned idx) { return m_buffer[idx]; } void reset() { typename vector::iterator it = m_buffer.begin(); typename vector::iterator end = m_buffer.end(); for (; it != end; ++it) m().del(*it); m_buffer.reset(); } Numeral * c_ptr() { return m_buffer.c_ptr(); } void reserve(unsigned sz) { m_buffer.reserve(sz); } void swap(svector & other) { m_buffer.swap(other); } }; #endif z3-z3-4.4.1/src/util/obj_hashtable.h000066400000000000000000000134411260446376700171320ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: obj_hashtable.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-16. Revision History: --*/ #ifndef OBJ_HASHTABLE_H_ #define OBJ_HASHTABLE_H_ #include"hash.h" #include"hashtable.h" /** \brief Special entry for a hashtable of obj pointers (i.e., objects that have a hash() method). This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED. */ template class obj_hash_entry { T * m_ptr; public: typedef T * data; obj_hash_entry():m_ptr(0) {} unsigned get_hash() const { return m_ptr->hash(); } bool is_free() const { return m_ptr == 0; } bool is_deleted() const { return m_ptr == reinterpret_cast(1); } bool is_used() const { return m_ptr != reinterpret_cast(0) && m_ptr != reinterpret_cast(1); } T * get_data() const { return m_ptr; } T * & get_data() { return m_ptr; } void set_data(T * d) { m_ptr = d; } void set_hash(unsigned h) { SASSERT(h == m_ptr->hash()); } void mark_as_deleted() { m_ptr = reinterpret_cast(1); } void mark_as_free() { m_ptr = 0; } }; template class obj_hashtable : public core_hashtable, obj_ptr_hash, ptr_eq > { public: obj_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY): core_hashtable, obj_ptr_hash, ptr_eq >(initial_capacity) {} }; template class obj_map { public: struct key_data { Key * m_key; Value m_value; key_data():m_key(0) { } key_data(Key * k): m_key(k) { } key_data(Key * k, Value const & v): m_key(k), m_value(v) { } Value const & get_value() const { return m_value; } unsigned hash() const { return m_key->hash(); } bool operator==(key_data const & other) const { return m_key == other.m_key; } }; class obj_map_entry { key_data m_data; public: typedef key_data data; obj_map_entry() {} unsigned get_hash() const { return m_data.hash(); } bool is_free() const { return m_data.m_key == 0; } bool is_deleted() const { return m_data.m_key == reinterpret_cast(1); } bool is_used() const { return m_data.m_key != reinterpret_cast(0) && m_data.m_key != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key = reinterpret_cast(1); } void mark_as_free() { m_data.m_key = 0; } }; typedef core_hashtable, default_eq > table; table m_table; public: obj_map(): m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY) {} typedef typename table::iterator iterator; typedef Key key; typedef Value value; void reset() { m_table.reset(); } void finalize() { m_table.finalize(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(Key * k, Value const & v) { m_table.insert(key_data(k, v)); } key_data const & insert_if_not_there(Key * k, Value const & v) { return m_table.insert_if_not_there(key_data(k, v)); } obj_map_entry * insert_if_not_there2(Key * k, Value const & v) { return m_table.insert_if_not_there2(key_data(k, v)); } obj_map_entry * find_core(Key * k) const { return m_table.find_core(key_data(k)); } bool find(Key * k, Value & v) const { obj_map_entry * e = find_core(k); if (e) { v = e->get_data().m_value; } return (0 != e); } value const & find(key * k) const { obj_map_entry * e = find_core(k); SASSERT(e); return e->get_data().m_value; } value & find(key * k) { obj_map_entry * e = find_core(k); SASSERT(e); return e->get_data().m_value; } value const & operator[](key * k) const { return find(k); } value & operator[](key * k) { return find(k); } iterator find_iterator(Key * k) const { return m_table.find(key_data(k)); } bool contains(Key * k) const { return find_core(k) != 0; } void remove(Key * k) { m_table.remove(key_data(k)); } void erase(Key * k) { remove(k); } unsigned long long get_num_collision() const { return m_table.get_num_collision(); } void swap(obj_map & other) { m_table.swap(other.m_table); } }; /** \brief Reset and deallocate the values stored in a mapping of the form obj_map */ template void reset_dealloc_values(obj_map & m) { typename obj_map::iterator it = m.begin(); typename obj_map::iterator end = m.end(); for (; it != end; ++it) { dealloc(it->m_value); } m.reset(); } /** \brief Remove the key k from the mapping m, and delete the value associated with k. */ template void erase_dealloc_value(obj_map & m, Key * k) { Value * v = 0; bool contains = m.find(k, v); m.erase(k); if (contains) { dealloc(v); } } #endif /* OBJ_HASHTABLE_H_ */ z3-z3-4.4.1/src/util/obj_mark.h000066400000000000000000000024021260446376700161240ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: obj_mark.h Abstract: A mapping from object to boolean (for objects that can be mapped to unsigned integers). Author: Leonardo de Moura (leonardo) 2008-01-02. Revision History: --*/ #ifndef OBJ_MARK_H_ #define OBJ_MARK_H_ #include"bit_vector.h" template struct default_t2uint { unsigned operator()(T const & obj) const { return obj.get_id(); } }; template > class obj_mark { T2UInt m_proc; BV m_marks; public: obj_mark(T2UInt const & p = T2UInt()):m_proc(p) {} bool is_marked(T const & obj) const { unsigned id = m_proc(obj); return id < m_marks.size() && m_marks.get(id); } bool is_marked(T * obj) const { return is_marked(*obj); } void mark(T const & obj, bool flag) { unsigned id = m_proc(obj); if (id >= m_marks.size()) { m_marks.resize(id+1, 0); } m_marks.set(id, flag); } void mark(T const * obj, bool flag) { mark(*obj, flag); } void mark(T const & obj) { mark(obj, true); } void mark(T const * obj) { mark(obj, true); } void reset() { m_marks.reset(); } }; #endif /* OBJ_MARK_H_ */ z3-z3-4.4.1/src/util/obj_pair_hashtable.h000066400000000000000000000120731260446376700201450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: obj_pair_hashtable.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef OBJ_PAIR_HASHTABLE_H_ #define OBJ_PAIR_HASHTABLE_H_ #include"hash.h" #include"hashtable.h" /** \brief Special entry for a hashtable of pairs of obj pointers (i.e., objects that have a hash() method). This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED. */ template class obj_pair_hash_entry { unsigned m_hash; // cached hash code std::pair m_data; public: typedef std::pair data; obj_pair_hash_entry():m_data(static_cast(0),static_cast(0)) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_data.first == 0; } bool is_deleted() const { return m_data.first == reinterpret_cast(1); } bool is_used() const { return m_data.first != reinterpret_cast(0) && m_data.first != reinterpret_cast(1); } data const & get_data() const { return m_data; } data & get_data() { return m_data; } void set_data(data const d) { m_data = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_data.first = reinterpret_cast(1); } void mark_as_free() { m_data.first = 0; } }; template class obj_pair_hashtable : public core_hashtable, obj_ptr_pair_hash, default_eq > > { public: obj_pair_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY): core_hashtable, obj_ptr_pair_hash, default_eq > >(initial_capacity) { } }; template class obj_pair_map { protected: class entry; public: class key_data { Key1 * m_key1; Key2 * m_key2; Value m_value; unsigned m_hash; friend class entry; public: key_data(): m_key1(0), m_key2(0), m_hash(0) { } key_data(Key1 * k1, Key2 * k2): m_key1(k1), m_key2(k2) { m_hash = combine_hash(m_key1->hash(), m_key2->hash()); } key_data(Key1 * k1, Key2 * k2, const Value & v): m_key1(k1), m_key2(k2), m_value(v) { m_hash = combine_hash(m_key1->hash(), m_key2->hash()); } unsigned hash() const { return m_hash; } bool operator==(key_data const & other) const { return m_key1 == other.m_key1 && m_key2 == other.m_key2; } Key1 * get_key1() const { return m_key1; } Key2 * get_key2() const { return m_key2; } Value const & get_value() const { return m_value; } }; protected: class entry { key_data m_data; public: typedef key_data data; entry() {} unsigned get_hash() const { return m_data.hash(); } bool is_free() const { return m_data.m_key1 == 0; } bool is_deleted() const { return m_data.m_key1 == reinterpret_cast(1); } bool is_used() const { return m_data.m_key1 != reinterpret_cast(0) && m_data.m_key1 != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key1 = reinterpret_cast(1); } void mark_as_free() { m_data.m_key1 = 0; } }; typedef core_hashtable, default_eq > table; table m_table; entry * find_core(Key1 * k1, Key2 * k2) const { return m_table.find_core(key_data(k1, k2)); } public: obj_pair_map(): m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY) {} typedef typename table::iterator iterator; void reset() { m_table.reset(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(Key1 * k1, Key2 * k2, Value const & v) { m_table.insert(key_data(k1, k2, v)); } key_data const & insert_if_not_there(Key1 * k1, Key2 * k2, Value const & v) { return m_table.insert_if_not_there(key_data(k1, k2, v)); } bool find(Key1 * k1, Key2 * k2, Value & v) const { entry * e = find_core(k1, k2); if (e) { v = e->get_data().get_value(); } return (0 != e); } bool contains(Key1 * k1, Key2 * k2) const { return find_core(k1, k2) != 0; } void erase(Key1 * k1, Key2 * k2) { m_table.remove(key_data(k1, k2)); } }; #endif /* OBJ_PAIR_HASHTABLE_H_ */ z3-z3-4.4.1/src/util/obj_pair_set.h000066400000000000000000000026031260446376700170030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: obj_pair_set.h Abstract: Author: Leonardo de Moura (leonardo) 2011-04-19 Revision History: --*/ #ifndef OBJ_PAIR_SET_H_ #define OBJ_PAIR_SET_H_ #include"chashtable.h" template class obj_pair_set { public: typedef std::pair obj_pair; protected: struct hash_proc { unsigned operator()(obj_pair const & p) const { return combine_hash(p.first->hash(), p.second->hash()); } }; struct eq_proc { bool operator()(obj_pair const & p1, obj_pair const & p2) const { return p1 == p2; } }; typedef chashtable set; set m_set; public: obj_pair_set() {} void insert(T1 * t1, T2 * t2) { m_set.insert(obj_pair(t1, t2)); } void insert(obj_pair const & p) { m_set.insert(p); } bool insert_if_not_there(T1 * t1, T2 * t2) { return m_set.insert_if_not_there2(obj_pair(t1, t2)); } bool insert_if_not_there(obj_pair const & p) { return m_set.insert_if_not_there2(p); } void erase(T1 * t1, T2 * t2) { return m_set.erase(obj_pair(t1, t2)); } void erase(obj_pair const & p) { return m_set.erase(p); } bool contains(T1 * t1, T2 * t2) const { return m_set.contains(obj_pair(t1, t2)); } bool contains(obj_pair const & p) const { return m_set.contains(p); } void reset() { m_set.reset(); } }; #endif z3-z3-4.4.1/src/util/obj_ref.h000066400000000000000000000056031260446376700157540ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: obj_ref.h Abstract: Smart pointer. Author: Leonardo de Moura (leonardo) 2008-01-03. Revision History: --*/ #ifndef OBJ_REF_H_ #define OBJ_REF_H_ /** Smart pointer for T objects. TManager must provide the functions: - void dec_ref(T * obj) - void inc_ref(T * obj) */ template class obj_ref { T * m_obj; TManager & m_manager; void dec_ref() { if (m_obj) m_manager.dec_ref(m_obj); } void inc_ref() { if (m_obj) m_manager.inc_ref(m_obj); } public: typedef TManager manager; obj_ref(T * n, TManager & m): m_obj(n), m_manager(m) { inc_ref(); } explicit obj_ref(TManager & m): m_obj(0), m_manager(m) { } obj_ref(obj_ref const & n): m_obj(n.m_obj), m_manager(n.m_manager) { inc_ref(); } ~obj_ref() { dec_ref(); } TManager & get_manager() const { return m_manager; } TManager & m() const { return m_manager; } T * operator->() const { return m_obj; } T * get() const { return m_obj; } operator bool() const { return m_obj != 0; } bool operator!() const { return m_obj == 0; } operator T*() const { return m_obj; } T const & operator*() const { return *m_obj; } obj_ref & operator=(T * n) { if (n) { m_manager.inc_ref(n); } dec_ref(); m_obj = n; return *this; } obj_ref & operator=(obj_ref & n) { SASSERT(&m_manager == &n.m_manager); n.inc_ref(); dec_ref(); m_obj = n.m_obj; return *this; } void reset() { dec_ref(); m_obj = 0; } void swap(obj_ref & n) { std::swap(m_obj, n.m_obj); } /** \brief Steal ownership without decrementing the reference counter. */ T * steal() { T * r = m_obj; m_obj = 0; return r; } }; template inline bool operator==(obj_ref const & n1, obj_ref const & n2) { return n1.get() == n2.get(); } template inline bool operator==(obj_ref const & n1, obj_ref const & n2) { return n1.get() == n2.get(); } template inline bool operator!=(obj_ref const & n1, obj_ref const & n2) { return n1.get() != n2.get(); } template inline bool operator!=(obj_ref const & n1, obj_ref const & n2) { return n1.get() != n2.get(); } template inline void dec_range_ref(IT const & begin, IT const & end, TManager & m) { for (IT it = begin; it != end; ++it) { if (*it) { m.dec_ref(*it); } } } #endif /* OBJ_REF_H_ */ z3-z3-4.4.1/src/util/obj_triple_hashtable.h000066400000000000000000000127001260446376700205060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: obj_triple_hashtable.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef OBJ_TRIPLE_HASHTABLE_H_ #define OBJ_TRIPLE_HASHTABLE_H_ #include"hashtable.h" /** \brief Special entry for a hashtable of pairs of obj pointers (i.e., objects that have a hash() method). This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED. */ template class obj_triple_hash_entry { unsigned m_hash; // cached hash code triple m_data; public: typedef triple data; obj_triple_hash_entry():m_data(0,0,0) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_data.first == 0; } bool is_deleted() const { return m_data.first == reinterpret_cast(1); } bool is_used() const { return m_data.first != reinterpret_cast(0) && m_data.first != reinterpret_cast(1); } data const & get_data() const { return m_data; } data & get_data() { return m_data; } void set_data(data const d) { m_data = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_data.first = reinterpret_cast(1); } void mark_as_free() { m_data.first = 0; } }; template class obj_triple_hashtable : public core_hashtable, obj_ptr_triple_hash, default_eq > > { public: obj_triple_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY): core_hashtable, obj_ptr_triple_hash, default_eq > >(initial_capacity) { } }; template class obj_triple_map { protected: class entry; public: class key_data { Key1 * m_key1; Key2 * m_key2; Key3 * m_key3; Value m_value; unsigned m_hash; friend class entry; public: key_data(): m_key1(0), m_key2(0), m_key3(0), m_hash(0) { } key_data(Key1 * k1, Key2 * k2, Key3 * k3): m_key1(k1), m_key2(k2), m_key3(k3){ m_hash = combine_hash(combine_hash(m_key1->hash(), m_key2->hash()), m_key3->hash()); } key_data(Key1 * k1, Key2 * k2, Key3* k3, const Value & v): m_key1(k1), m_key2(k2), m_key3(k3), m_value(v) { m_hash = combine_hash(combine_hash(m_key1->hash(), m_key2->hash()), m_key3->hash()); } unsigned hash() const { return m_hash; } bool operator==(key_data const & other) const { return m_key1 == other.m_key1 && m_key2 == other.m_key2 && m_key3 == other.m_key3; } Key1 * get_key1() const { return m_key1; } Key2 * get_key2() const { return m_key2; } Key3 * get_key3() const { return m_key3; } Value const & get_value() const { return m_value; } }; protected: class entry { key_data m_data; public: typedef key_data data; entry() {} unsigned get_hash() const { return m_data.hash(); } bool is_free() const { return m_data.m_key1 == 0; } bool is_deleted() const { return m_data.m_key1 == reinterpret_cast(1); } bool is_used() const { return m_data.m_key1 != reinterpret_cast(0) && m_data.m_key1 != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key1 = reinterpret_cast(1); } void mark_as_free() { m_data.m_key1 = 0; } }; typedef core_hashtable, default_eq > table; table m_table; entry * find_core(Key1 * k1, Key2 * k2, Key3 * k3) const { return m_table.find_core(key_data(k1, k2, k3)); } public: obj_triple_map(): m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY) {} typedef typename table::iterator iterator; void reset() { m_table.reset(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(Key1 * k1, Key2 * k2, Key3* k3, Value const & v) { m_table.insert(key_data(k1, k2, k3, v)); } key_data const & insert_if_not_there(Key1 * k1, Key2 * k2, Key3 * k3, Value const & v) { return m_table.insert_if_not_there(key_data(k1, k2, k3, v)); } bool find(Key1 * k1, Key2 * k2,Key3 * k3, Value & v) const { entry * e = find_core(k1, k2, k3); if (e) { v = e->get_data().get_value(); } return (0 != e); } bool contains(Key1 * k1, Key2 * k2, Key3 * k3) const { return find_core(k1, k2, k3) != 0; } void erase(Key1 * k1, Key2 * k2, Key3 * k3) { m_table.remove(key_data(k1, k2, k3)); } }; #endif /* OBJ_TRIPLE_HASHTABLE_H_ */ z3-z3-4.4.1/src/util/object_allocator.h000066400000000000000000000204371260446376700176560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: object_allocator.h Abstract: Yet another object allocator. This allocator is supposed to be efficient when there is a collection of worker threads accessing it. Author: Leonardo de Moura (leonardo) 2010-06-09. Revision History: --*/ #ifndef OBJECT_ALLOCATOR_H_ #define OBJECT_ALLOCATOR_H_ #include"util.h" #include"vector.h" #define DEFAULT_NUM_WORKERS 8 #define NUM_OBJECTS_PER_PAGE 1024 template struct do_nothing_reset_proc { public: void operator()(T * obj) {} }; template struct simple_reset_proc { public: void operator()(T * obj) { obj->reset(); } }; /** \brief Allocator for T objects. This allocator is supposed to be efficient even when a collection of working threads are accessing it. Assumptions: - T must have an empty constructor. - The destructors for T objects are only invoked when the object_allocator is deleted. - The destructors are not invoked if CallDestructors == false. - The functor ResetProc is invoked for \c ptr when recycle(ptr) or recycle(worker_id, ptr) are invoked. The default ResetProc does nothing. */ template > class object_allocator : public ResetProc { /** \brief Auxiliary allocator for storing object into chunks of memory. */ class region { ptr_vector m_pages; unsigned m_idx; //!< next position in the current page. void allocate_new_page() { T * new_page = static_cast(memory::allocate(sizeof(T) * NUM_OBJECTS_PER_PAGE)); m_pages.push_back(new_page); m_idx = 0; } void call_destructors_for_page(T * page, unsigned end) { T * page_end = page + end; for (; page < page_end; page++) page->~T(); } void call_destructors() { if (CallDestructors) { SASSERT(!m_pages.empty()); typename ptr_vector::iterator it = m_pages.begin(); typename ptr_vector::iterator end = m_pages.end(); end--; call_destructors_for_page(*end, m_idx); for (; it != end; ++it) call_destructors_for_page(*it, NUM_OBJECTS_PER_PAGE); } } void free_memory() { call_destructors(); typename ptr_vector::iterator it = m_pages.begin(); typename ptr_vector::iterator end = m_pages.end(); for (; it != end; ++it) memory::deallocate(*it); } public: region() { allocate_new_page(); } ~region() { free_memory(); } template T * allocate() { SASSERT(!m_pages.empty()); T * r = m_pages.back() + m_idx; if (construct) new (r) T(); m_idx++; if (m_idx == NUM_OBJECTS_PER_PAGE) allocate_new_page(); return r; } void reset() { free_memory(); m_pages.reset(); allocate_new_page(); } unsigned get_objects_count() { return (m_pages.size() - 1) * NUM_OBJECTS_PER_PAGE + m_idx; } }; #ifdef Z3DEBUG bool m_concurrent; //!< True when the allocator can be accessed concurrently. #endif ptr_vector m_regions; vector > m_free_lists; template T * allocate_core(unsigned idx) { ptr_vector & free_list = m_free_lists[idx]; if (!free_list.empty()) { T * r = free_list.back(); free_list.pop_back(); return r; } return m_regions[idx]->template allocate(); } void recycle_core(unsigned idx, T * ptr) { ResetProc::operator()(ptr); m_free_lists[idx].push_back(ptr); } public: object_allocator(ResetProc const & r = ResetProc()):ResetProc(r) { DEBUG_CODE(m_concurrent = false;); reserve(DEFAULT_NUM_WORKERS); } ~object_allocator() { std::for_each(m_regions.begin(), m_regions.end(), delete_proc()); } /** \brief Enable/Disable concurrent access. */ void enable_concurrent(bool flag) { DEBUG_CODE(m_concurrent = flag;); } /** \brief Make sure that \c num_workers can access this object allocator concurrently. This method must only be invoked if the allocator is not in concurrent mode. */ void reserve(unsigned num_workers) { SASSERT(!m_concurrent); unsigned old_capacity = capacity(); if (num_workers > old_capacity) { m_regions.resize(num_workers); m_free_lists.resize(num_workers); for (unsigned i = old_capacity; i < capacity(); i++) { m_regions[i] = alloc(region); } } } /** \brief Return the number of workers supported by this object allocator. */ unsigned capacity() const { return m_regions.size(); } /** \brief Free all memory allocated using this allocator. This method must only be invoked when the allocator is not in concurrent mode. */ void reset() { SASSERT(!m_concurrent); unsigned c = capacity(); for (unsigned i = 0; i < c; i++) { m_regions[i]->reset(); m_free_lists[i].reset(); } } /** \brief Allocate a new object. This method must only be invoked when the object_allocator is not in concurrent mode. */ template T * allocate() { SASSERT(!m_concurrent); return allocate_core(0); } /** \brief Recycle the given object. This method must only be invoked when the object_allocator is not in concurrent mode. \remark It is OK to recycle an object allocated by a worker when the object_allocator was in concurrent mode. */ void recycle(T * ptr) { SASSERT(!m_concurrent); recycle_core(0, ptr); } /** \brief Allocate a new object for the given worker. This method must only be invoked when the object_allocator is in concurrent mode. */ template T * allocate(unsigned worker_id) { SASSERT(m_concurrent); return allocate_core(worker_id); } /** \brief Recycle the given object. This method must only be invoked when the object_allocator is in concurrent mode. \remark It is OK to recycle an object allocated by a different worker, or allocated when the object_allocator was not in concurrent mode. */ void recycle(unsigned worker_id, T * ptr) { SASSERT(m_concurrent); return recycle_core(worker_id, ptr); } /** \brief Wrapper for currying worker_id in allocate and recycle methods. */ class worker_object_allocator { object_allocator & m_owner; unsigned m_worker_id; friend class object_allocator; worker_object_allocator(object_allocator & owner, unsigned id):m_owner(owner), m_worker_id(id) {} public: template T * allocate() { return m_owner.allocate(m_worker_id); } void recycle(T * ptr) { return m_owner.recycle(m_worker_id, ptr); } }; /** \brief Return a wrapper for allocating memory for the given worker. The wrapper remains valid even when the object_allocator is not in concurrent mode. However, the methods allocate/recycle of the wrapper must only be invoked when the object_allocator is in concurrent mode. */ worker_object_allocator get_worker_allocator(unsigned worker_id) { SASSERT(worker_id < capacity()); return worker_object_allocator(*this, worker_id); } unsigned get_objects_count() const { unsigned count = 0; unsigned n_regions = m_regions.size(); for (unsigned i = 0; i < n_regions; i++) { count += m_regions[i]->get_objects_count(); count -= m_free_lists[i].size(); } return count; } }; #endif /* OBJECT_ALLOCATOR_H_ */ z3-z3-4.4.1/src/util/optional.h000066400000000000000000000063001260446376700161660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: optional.h Abstract: Discriminated union of a type T. It defines the notion of initialized/uninitialized objects. Author: Leonardo de Moura (leonardo) 2006-09-29. Revision History: --*/ #ifndef OPTIONAL_H_ #define OPTIONAL_H_ template class optional { char m_obj[sizeof(T)]; char m_initialized; void construct(const T & val) { m_initialized = 1; new (reinterpret_cast(m_obj)) T(val); } void destroy() { if (m_initialized == 1) { reinterpret_cast(m_obj)->~T(); } m_initialized = 0; } public: optional(): m_initialized(0) {} explicit optional(const T & val) { construct(val); } optional(const optional & val): m_initialized(0) { if (val.m_initialized == 1) { construct(*val); } } ~optional() { destroy(); } static optional const & undef() { static optional u; return u; } bool initialized() const { return m_initialized == 1; } operator bool() const { return m_initialized == 1; } bool operator!() const { return m_initialized == 0; } T * get() const { if (m_initialized == 1) { return reinterpret_cast(m_obj); } else { return 0; } } void set_invalid() { if (m_initialized == 1) { destroy(); } } T * operator->() { SASSERT(m_initialized==1); return reinterpret_cast(m_obj); } T const * operator->() const { SASSERT(m_initialized==1); return reinterpret_cast(m_obj); } const T & operator*() const { SASSERT(m_initialized==1); return *reinterpret_cast(m_obj); } T & operator*() { SASSERT(m_initialized==1); return *reinterpret_cast(m_obj); } optional & operator=(const T & val) { destroy(); construct(val); return * this; } optional & operator=(const optional & val) { if (&val != this) { destroy(); if (val.m_initialized) { construct(*val); } } return *this; } }; /** \brief Template specialization for pointers. NULL represents uninitialized pointers. */ template class optional { T * m_ptr; static optional m_undef; public: optional():m_ptr(0) {} explicit optional(T * val):m_ptr(val) {} optional(const optional & val):m_ptr(val.m_ptr) {} static optional const & undef() { return m_undef; } bool initialized() const { return m_ptr != 0 ; } operator bool() const { return m_ptr != 0; } bool operator!() const { return m_ptr == 0; } void reset() { m_ptr = 0; } optional & operator=(T * val) { m_ptr = val; return *this; } optional & operator=(const optional & val) { m_ptr = val.m_ptr; return *this; } T ** operator->() { return &m_ptr; } T * operator*() const { return m_ptr; } T * & operator*() { return m_ptr; } }; #endif /* OPTIONAL_H_ */ z3-z3-4.4.1/src/util/page.cpp000066400000000000000000000027501260446376700156150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: page.cpp Abstract: Goodies for manipulating pages of memory. Author: Leonardo de Moura (leonardo) 2011-02-27. Revision History: --*/ #include"page.h" #include"debug.h" inline void set_page_header(char * page, char * prev, bool default_page) { size_t header = reinterpret_cast(prev) | static_cast(default_page); reinterpret_cast(page)[-1] = header; SASSERT(is_default_page(page) == default_page); SASSERT(prev_page(page) == prev); } inline char * alloc_page(size_t s) { char * r = alloc_svect(char, s+PAGE_HEADER_SZ); return r + PAGE_HEADER_SZ; } inline void del_page(char * page) { dealloc_svect(page - PAGE_HEADER_SZ); } void del_pages(char * page) { while (page != 0) { char * prev = prev_page(page); del_page(page); page = prev; } } char * allocate_default_page(char * prev, char * & free_pages) { char * r; if (free_pages) { r = free_pages; free_pages = prev_page(free_pages); } else { r = alloc_page(DEFAULT_PAGE_SIZE); } set_page_header(r, prev, true); return r; } char * allocate_page(char * prev, size_t sz) { char * r = alloc_page(sz); set_page_header(r, prev, false); return r; } void recycle_page(char * p, char * & free_pages) { if (is_default_page(p)) { set_page_header(p, free_pages, true); free_pages = p; } else { del_page(p); } } z3-z3-4.4.1/src/util/page.h000066400000000000000000000017661260446376700152700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: page.h Abstract: Goodies for manipulating pages of memory. Author: Leonardo de Moura (leonardo) 2011-02-27. Revision History: --*/ #ifndef PAGE_H_ #define PAGE_H_ #include"memory_manager.h" #define PAGE_HEADER_SZ sizeof(size_t) #define DEFAULT_PAGE_SIZE (8192 - PAGE_HEADER_SZ) #define PAGE_HEADER_MASK (static_cast(-1) - 1) inline char * prev_page(char * page) { size_t tagged_ptr = reinterpret_cast(page)[-1]; return reinterpret_cast(tagged_ptr & PAGE_HEADER_MASK); } inline bool is_default_page(char * page) { size_t tagged_ptr = reinterpret_cast(page)[-1]; return static_cast(tagged_ptr & 1); } inline char * end_of_default_page(char * p) { return p + DEFAULT_PAGE_SIZE; } void del_pages(char * page); char * allocate_default_page(char * prev, char * & free_pages); char * allocate_page(char * prev, size_t sz); void recycle_page(char * p, char * & free_pages); #endif z3-z3-4.4.1/src/util/params.cpp000066400000000000000000001022311260446376700161570ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: params.cpp Abstract: Parameters Author: Leonardo (leonardo) 2011-05-09 Notes: --*/ #include"params.h" #include"rational.h" #include"symbol.h" #include"dictionary.h" params_ref params_ref::g_empty_params_ref; std::string norm_param_name(char const * n) { if (n == 0) return "_"; if (*n == ':') n++; std::string r = n; unsigned sz = static_cast(r.size()); if (sz == 0) return "_"; for (unsigned i = 0; i < sz; i++) { char curr = r[i]; if ('A' <= curr && curr <= 'Z') r[i] = curr - 'A' + 'a'; else if (curr == '-' || curr == ':') r[i] = '_'; } return r; } std::string norm_param_name(symbol const & n) { return norm_param_name(n.bare_str()); } struct param_descrs::imp { struct info { param_kind m_kind; char const * m_descr; char const * m_default; char const * m_module; info(param_kind k, char const * descr, char const * def, char const* module): m_kind(k), m_descr(descr), m_default(def), m_module(module) { } info(): m_kind(CPK_INVALID), m_descr(0), m_default(0), m_module(0) { } }; dictionary m_info; svector m_names; void insert(symbol const & name, param_kind k, char const * descr, char const * def, char const* module) { SASSERT(!name.is_numerical()); info i; if (m_info.find(name, i)) { SASSERT(i.m_kind == k); return; } m_info.insert(name, info(k, descr, def, module)); m_names.push_back(name); } void erase(symbol const & name) { m_info.erase(name); } bool contains(symbol const & name) const { return m_info.contains(name); } param_kind get_kind(symbol const & name) const { info i; if (m_info.find(name, i)) return i.m_kind; return CPK_INVALID; } bool split_name(symbol const& name, symbol & prefix, symbol & suffix) const { if (name.is_numerical()) return false; char const* str = name.bare_str(); char const* period = strchr(str,'.'); if (!period) return false; svector prefix_((unsigned)(period-str), str); prefix_.push_back(0); prefix = symbol(prefix_.c_ptr()); suffix = symbol(period + 1); return true; } param_kind get_kind_in_module(symbol & name) const { param_kind k = get_kind(name); symbol prefix, suffix; if (k == CPK_INVALID && split_name(name, prefix, suffix)) { k = get_kind(suffix); if (k != CPK_INVALID) { if (symbol(get_module(suffix)) == prefix) { name = suffix; } else { k = CPK_INVALID; } } } return k; } char const* get_module(symbol const& name) const { info i; if (m_info.find(name, i)) return i.m_module; return 0; } char const * get_descr(symbol const & name) const { info i; if (m_info.find(name, i)) return i.m_descr; return 0; } char const * get_default(symbol const & name) const { info i; if (m_info.find(name, i)) return i.m_default; return 0; } unsigned size() const { return m_names.size(); } symbol get_param_name(unsigned idx) const { return m_names[idx]; } struct lt { bool operator()(symbol const & s1, symbol const & s2) const { return strcmp(s1.bare_str(), s2.bare_str()) < 0; } }; void display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) const { svector names; dictionary::iterator it = m_info.begin(); dictionary::iterator end = m_info.end(); for (; it != end; ++it) { names.push_back(it->m_key); } std::sort(names.begin(), names.end(), lt()); svector::iterator it2 = names.begin(); svector::iterator end2 = names.end(); for (; it2 != end2; ++it2) { for (unsigned i = 0; i < indent; i++) out << " "; if (smt2_style) out << ':'; char const * s = it2->bare_str(); unsigned n = static_cast(strlen(s)); for (unsigned i = 0; i < n; i++) { if (smt2_style && s[i] == '_') out << '-'; else if (!smt2_style && s[i] == '-') out << '_'; else if (s[i] >= 'A' && s[i] <= 'Z') out << (s[i] - 'A' + 'a'); else out << s[i]; } info d; m_info.find(*it2, d); SASSERT(d.m_descr); out << " (" << d.m_kind << ")"; if (include_descr) out << " " << d.m_descr; if (d.m_default != 0) out << " (default: " << d.m_default << ")"; out << "\n"; } } void copy(param_descrs & other) { dictionary::iterator it = other.m_imp->m_info.begin(); dictionary::iterator end = other.m_imp->m_info.end(); for (; it != end; ++it) { insert(it->m_key, it->m_value.m_kind, it->m_value.m_descr, it->m_value.m_default, it->m_value.m_module); } } }; param_descrs::param_descrs() { m_imp = alloc(imp); } param_descrs::~param_descrs() { dealloc(m_imp); } void param_descrs::copy(param_descrs & other) { m_imp->copy(other); } void param_descrs::insert(symbol const & name, param_kind k, char const * descr, char const * def, char const* module) { m_imp->insert(name, k, descr, def, module); } void param_descrs::insert(char const * name, param_kind k, char const * descr, char const * def, char const* module) { insert(symbol(name), k, descr, def, module); } bool param_descrs::contains(char const * name) const { return contains(symbol(name)); } bool param_descrs::contains(symbol const & name) const { return m_imp->contains(name); } char const * param_descrs::get_descr(char const * name) const { return get_descr(symbol(name)); } char const * param_descrs::get_descr(symbol const & name) const { return m_imp->get_descr(name); } char const * param_descrs::get_default(char const * name) const { return get_default(symbol(name)); } char const * param_descrs::get_default(symbol const & name) const { return m_imp->get_default(name); } void param_descrs::erase(symbol const & name) { m_imp->erase(name); } void param_descrs::erase(char const * name) { erase(symbol(name)); } param_kind param_descrs::get_kind_in_module(symbol & name) const { return m_imp->get_kind_in_module(name); } param_kind param_descrs::get_kind(symbol const & name) const { return m_imp->get_kind(name); } param_kind param_descrs::get_kind(char const * name) const { return get_kind(symbol(name)); } unsigned param_descrs::size() const { return m_imp->size(); } symbol param_descrs::get_param_name(unsigned i) const { return m_imp->get_param_name(i); } char const* param_descrs::get_module(symbol const& name) const { return m_imp->get_module(name); } void param_descrs::display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) const { return m_imp->display(out, indent, smt2_style, include_descr); } void insert_max_memory(param_descrs & r) { r.insert("max_memory", CPK_UINT, "(default: infty) maximum amount of memory in megabytes."); } void insert_max_steps(param_descrs & r) { r.insert("max_steps", CPK_UINT, "(default: infty) maximum number of steps."); } void insert_produce_models(param_descrs & r) { r.insert("produce_models", CPK_BOOL, "(default: false) model generation."); } void insert_produce_proofs(param_descrs & r) { r.insert("produce_proofs", CPK_BOOL, "(default: false) proof generation."); } void insert_timeout(param_descrs & r) { r.insert("timeout", CPK_UINT, "(default: infty) timeout in milliseconds."); } class params { friend class params_ref; struct value { param_kind m_kind; union { bool m_bool_value; unsigned m_uint_value; double m_double_value; char const * m_str_value; char const * m_sym_value; rational * m_rat_value; }; }; typedef std::pair entry; svector m_entries; unsigned m_ref_count; void del_value(entry & e); void del_values(); public: params():m_ref_count(0) {} ~params() { reset(); } void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } bool empty() const { return m_entries.empty(); } bool contains(symbol const & k) const; bool contains(char const * k) const; void reset(); void reset(symbol const & k); void reset(char const * k); void validate(param_descrs const & p) { svector::iterator it = m_entries.begin(); svector::iterator end = m_entries.end(); symbol suffix, prefix; for (; it != end; ++it) { param_kind expected = p.get_kind_in_module(it->first); if (expected == CPK_INVALID) { std::stringstream strm; strm << "unknown parameter '" << it->first.str() << "'\n"; strm << "Legal parameters are:\n"; p.display(strm, 2, false, false); throw default_exception(strm.str()); } if (it->second.m_kind != expected && !(it->second.m_kind == CPK_UINT && expected == CPK_NUMERAL)) { std::stringstream strm; strm << "Parameter " << it->first.str() << " was given argument of type "; strm << it->second.m_kind << ", expected " << expected; throw default_exception(strm.str()); } } } // getters bool get_bool(symbol const & k, bool _default) const; bool get_bool(char const * k, bool _default) const; unsigned get_uint(symbol const & k, unsigned _default) const; unsigned get_uint(char const * k, unsigned _default) const; double get_double(symbol const & k, double _default) const; double get_double(char const * k, double _default) const; char const * get_str(symbol const & k, char const * _default) const; char const * get_str(char const * k, char const * _default) const; rational get_rat(symbol const & k, rational const & _default) const; rational get_rat(char const * k, rational const & _default) const; symbol get_sym(symbol const & k, symbol const & _default) const; symbol get_sym(char const * k, symbol const & _default) const; bool get_bool(char const * k, params_ref const & fallback, bool _default) const; unsigned get_uint(char const * k, params_ref const & fallback, unsigned _default) const; double get_double(char const * k, params_ref const & fallback, double _default) const; char const * get_str(char const * k, params_ref const & fallback, char const * _default) const; symbol get_sym(char const * k, params_ref const & fallback, symbol const & _default) const; // setters void set_bool(symbol const & k, bool v); void set_bool(char const * k, bool v); void set_uint(symbol const & k, unsigned v); void set_uint(char const * k, unsigned v); void set_double(symbol const & k, double v); void set_double(char const * k, double v); void set_str(symbol const & k, char const * v); void set_str(char const * k, char const * v); void set_rat(symbol const & k, rational const & v); void set_rat(char const * k, rational const & v); void set_sym(symbol const & k, symbol const & v); void set_sym(char const * k, symbol const & v); void display(std::ostream & out) const { out << "(params"; svector::const_iterator it = m_entries.begin(); svector::const_iterator end = m_entries.end(); for (; it != end; ++it) { out << " " << it->first; switch (it->second.m_kind) { case CPK_BOOL: out << " " << (it->second.m_bool_value?"true":"false"); break; case CPK_UINT: out << " " <second.m_uint_value; break; case CPK_DOUBLE: out << " " << it->second.m_double_value; break; case CPK_NUMERAL: out << " " << *(it->second.m_rat_value); break; case CPK_SYMBOL: out << " " << symbol::mk_symbol_from_c_ptr(it->second.m_sym_value); break; case CPK_STRING: out << " " << it->second.m_str_value; break; default: UNREACHABLE(); break; } } out << ")"; } void display_smt2(std::ostream & out, char const* module, param_descrs& descrs) const { svector::const_iterator it = m_entries.begin(); svector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!descrs.contains(it->first)) continue; out << "(set-option :"; out << module << "."; out << it->first; switch (it->second.m_kind) { case CPK_BOOL: out << " " << (it->second.m_bool_value?"true":"false"); break; case CPK_UINT: out << " " <second.m_uint_value; break; case CPK_DOUBLE: out << " " << it->second.m_double_value; break; case CPK_NUMERAL: out << " " << *(it->second.m_rat_value); break; case CPK_SYMBOL: out << " " << symbol::mk_symbol_from_c_ptr(it->second.m_sym_value); break; case CPK_STRING: out << " " << it->second.m_str_value; break; default: UNREACHABLE(); break; } out << ")\n"; } } void display(std::ostream & out, symbol const & k) const { svector::const_iterator it = m_entries.begin(); svector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (it->first != k) continue; switch (it->second.m_kind) { case CPK_BOOL: out << (it->second.m_bool_value?"true":"false"); return; case CPK_UINT: out << it->second.m_uint_value; return; case CPK_DOUBLE: out << it->second.m_double_value; return; case CPK_NUMERAL: out << *(it->second.m_rat_value); return; case CPK_SYMBOL: out << symbol::mk_symbol_from_c_ptr(it->second.m_sym_value); return; case CPK_STRING: out << it->second.m_str_value; return; default: out << "internal"; return; } } out << "default"; } }; params_ref::~params_ref() { if (m_params) m_params->dec_ref(); } params_ref::params_ref(params_ref const & p): m_params(0) { operator=(p); } void params_ref::display(std::ostream & out) const { if (m_params) m_params->display(out); else out << "(params)"; } void params_ref::display_smt2(std::ostream& out, char const* module, param_descrs& descrs) const { if (m_params) m_params->display_smt2(out, module, descrs); } void params_ref::display(std::ostream & out, char const * k) const { display(out, symbol(k)); } void params_ref::display(std::ostream & out, symbol const & k) const { if (m_params) m_params->display(out, k); else out << "default"; } void params_ref::validate(param_descrs const & p) { if (m_params) m_params->validate(p); } params_ref & params_ref::operator=(params_ref const & p) { if (p.m_params) p.m_params->inc_ref(); if (m_params) m_params->dec_ref(); m_params = p.m_params; return *this; } void params_ref::copy(params_ref const & src) { if (m_params == 0) operator=(src); else { init(); copy_core(src.m_params); } } void params_ref::copy_core(params const * src) { if (src == 0) return; svector::const_iterator it = src->m_entries.begin(); svector::const_iterator end = src->m_entries.end(); for (; it != end; ++it) { switch (it->second.m_kind) { case CPK_BOOL: m_params->set_bool(it->first, it->second.m_bool_value); break; case CPK_UINT: m_params->set_uint(it->first, it->second.m_uint_value); break; case CPK_DOUBLE: m_params->set_double(it->first, it->second.m_double_value); break; case CPK_NUMERAL: m_params->set_rat(it->first, *(it->second.m_rat_value)); break; case CPK_SYMBOL: m_params->set_sym(it->first, symbol::mk_symbol_from_c_ptr(it->second.m_sym_value)); break; case CPK_STRING: m_params->set_str(it->first, it->second.m_str_value); break; default: UNREACHABLE(); break; } } } void params_ref::init() { if (!m_params) { m_params = alloc(params); m_params->inc_ref(); } else if (m_params->m_ref_count > 1) { params * old = m_params; m_params = alloc(params); m_params->inc_ref(); copy_core(old); old->dec_ref(); } SASSERT(m_params->m_ref_count == 1); } bool params_ref::get_bool(symbol const & k, bool _default) const { return m_params ? m_params->get_bool(k, _default) : _default; } bool params_ref::get_bool(char const * k, bool _default) const { return m_params ? m_params->get_bool(k, _default) : _default; } unsigned params_ref::get_uint(symbol const & k, unsigned _default) const { return m_params ? m_params->get_uint(k, _default) : _default; } unsigned params_ref::get_uint(char const * k, unsigned _default) const { return m_params ? m_params->get_uint(k, _default) : _default; } double params_ref::get_double(symbol const & k, double _default) const { return m_params ? m_params->get_double(k, _default) : _default; } double params_ref::get_double(char const * k, double _default) const { return m_params ? m_params->get_double(k, _default) : _default; } char const * params_ref::get_str(symbol const & k, char const * _default) const { return m_params ? m_params->get_str(k, _default) : _default; } char const * params_ref::get_str(char const * k, char const * _default) const { return m_params ? m_params->get_str(k, _default) : _default; } rational params_ref::get_rat(symbol const & k, rational const & _default) const { return m_params ? m_params->get_rat(k, _default) : _default; } rational params_ref::get_rat(char const * k, rational const & _default) const { return m_params ? m_params->get_rat(k, _default) : _default; } symbol params_ref::get_sym(symbol const & k, symbol const & _default) const { return m_params ? m_params->get_sym(k, _default) : _default; } symbol params_ref::get_sym(char const * k, symbol const & _default) const { return m_params ? m_params->get_sym(k, _default) : _default; } bool params_ref::get_bool(char const * k, params_ref const & fallback, bool _default) const { return m_params ? m_params->get_bool(k, fallback, _default) : fallback.get_bool(k, _default); } unsigned params_ref::get_uint(char const * k, params_ref const & fallback, unsigned _default) const { return m_params ? m_params->get_uint(k, fallback, _default) : fallback.get_uint(k, _default); } double params_ref::get_double(char const * k, params_ref const & fallback, double _default) const { return m_params ? m_params->get_double(k, fallback, _default) : fallback.get_double(k, _default); } char const * params_ref::get_str(char const * k, params_ref const & fallback, char const * _default) const { return m_params ? m_params->get_str(k, fallback, _default) : fallback.get_str(k, _default); } symbol params_ref::get_sym(char const * k, params_ref const & fallback, symbol const & _default) const { return m_params ? m_params->get_sym(k, fallback, _default) : fallback.get_sym(k, _default); } bool params_ref::empty() const { if (!m_params) return true; return m_params->empty(); } bool params_ref::contains(symbol const & k) const { if (!m_params) return false; return m_params->contains(k); } bool params_ref::contains(char const * k) const { if (!m_params) return false; return m_params->contains(k); } void params_ref::reset() { if (m_params) m_params->reset(); } void params_ref::reset(symbol const & k) { if (m_params) m_params->reset(k); } void params_ref::reset(char const * k) { if (m_params) m_params->reset(k); } void params_ref::set_bool(symbol const & k, bool v) { init(); m_params->set_bool(k, v); } void params_ref::set_bool(char const * k, bool v) { init(); m_params->set_bool(k, v); } void params_ref::set_uint(symbol const & k, unsigned v) { init(); m_params->set_uint(k, v); } void params_ref::set_uint(char const * k, unsigned v) { init(); m_params->set_uint(k, v); } void params_ref::set_double(symbol const & k, double v) { init(); m_params->set_double(k, v); } void params_ref::set_double(char const * k, double v) { init(); m_params->set_double(k, v); } void params_ref::set_str(symbol const & k, char const * v) { init(); m_params->set_str(k, v); } void params_ref::set_str(char const * k, char const * v) { init(); m_params->set_str(k, v); } void params_ref::set_rat(symbol const & k, rational const & v) { init(); m_params->set_rat(k, v); } void params_ref::set_rat(char const * k, rational const & v) { init(); m_params->set_rat(k, v); } void params_ref::set_sym(symbol const & k, symbol const & v) { init(); m_params->set_sym(k, v); } void params_ref::set_sym(char const * k, symbol const & v) { init(); m_params->set_sym(k, v); } void params::del_value(entry & e) { switch (e.second.m_kind) { case CPK_NUMERAL: if (e.second.m_kind == CPK_NUMERAL) dealloc(e.second.m_rat_value); break; default: return; } } #define TRAVERSE_ENTRIES(CODE) { \ svector::iterator it = m_entries.begin(); \ svector::iterator end = m_entries.end(); \ for (; it != end; ++it) { \ CODE \ } \ } #define TRAVERSE_CONST_ENTRIES(CODE) { \ svector::const_iterator it = m_entries.begin(); \ svector::const_iterator end = m_entries.end(); \ for (; it != end; ++it) { \ CODE \ } \ } void params::del_values() { TRAVERSE_ENTRIES(del_value(*it);); } #define CONTAINS(k) { \ if (empty()) \ return false; \ TRAVERSE_CONST_ENTRIES(if (it->first == k) return true;); \ return false; \ } bool params::contains(symbol const & k) const { CONTAINS(k); } bool params::contains(char const * k) const { CONTAINS(k); } void params::reset() { del_values(); m_entries.finalize(); SASSERT(empty()); } #define RESET(k) { \ if (empty()) return; \ TRAVERSE_ENTRIES(if (it->first == k) { \ svector::iterator it2 = it; \ del_value(*it2); \ ++it; \ for (; it != end; ++it, ++it2) { \ *it2 = *it; \ } \ m_entries.pop_back(); \ return; \ }); \ } void params::reset(symbol const & k) { RESET(k); } void params::reset(char const * k) { RESET(k); } #define GET_VALUE(MATCH_CODE, KIND) { \ if (empty()) return _default; \ TRAVERSE_CONST_ENTRIES(if (it->first == k && it->second.m_kind == KIND) { \ MATCH_CODE \ }); \ return _default; \ } #define GET_SIMPLE_VALUE(FIELD_NAME, KIND) GET_VALUE(return it->second.FIELD_NAME;, KIND) bool params::get_bool(symbol const & k, bool _default) const { GET_SIMPLE_VALUE(m_bool_value, CPK_BOOL); } bool params::get_bool(char const * k, bool _default) const { GET_SIMPLE_VALUE(m_bool_value, CPK_BOOL); } unsigned params::get_uint(symbol const & k, unsigned _default) const { GET_SIMPLE_VALUE(m_uint_value, CPK_UINT); } unsigned params::get_uint(char const * k, unsigned _default) const { GET_SIMPLE_VALUE(m_uint_value, CPK_UINT); } double params::get_double(symbol const & k, double _default) const { GET_SIMPLE_VALUE(m_double_value, CPK_DOUBLE); } double params::get_double(char const * k, double _default) const { GET_SIMPLE_VALUE(m_double_value, CPK_DOUBLE); } char const * params::get_str(symbol const & k, char const * _default) const { GET_SIMPLE_VALUE(m_str_value, CPK_STRING); } char const * params::get_str(char const * k, char const * _default) const { GET_SIMPLE_VALUE(m_str_value, CPK_STRING); } rational params::get_rat(symbol const & k, rational const & _default) const { if (empty()) return _default; TRAVERSE_CONST_ENTRIES(if (it->first == k) { if (it->second.m_kind == CPK_NUMERAL) { return *(it->second.m_rat_value); } if (it->second.m_kind == CPK_UINT) { return rational(static_cast(it->second.m_uint_value)); } }); return _default; } rational params::get_rat(char const * k, rational const & _default) const { if (empty()) return _default; TRAVERSE_CONST_ENTRIES(if (it->first == k) { if (it->second.m_kind == CPK_NUMERAL) { return *(it->second.m_rat_value); } if (it->second.m_kind == CPK_UINT) { return rational(static_cast(it->second.m_uint_value)); } }); return _default; } symbol params::get_sym(symbol const & k, symbol const & _default) const { GET_VALUE(return symbol::mk_symbol_from_c_ptr(it->second.m_sym_value);, CPK_SYMBOL); } symbol params::get_sym(char const * k, symbol const & _default) const { GET_VALUE(return symbol::mk_symbol_from_c_ptr(it->second.m_sym_value);, CPK_SYMBOL); } #define GET_VALUE2(MATCH_CODE, KIND) { \ if (!empty()) { \ TRAVERSE_CONST_ENTRIES(if (it->first == k && it->second.m_kind == KIND) { \ MATCH_CODE \ }); \ } \ } #define GET_SIMPLE_VALUE2(FIELD_NAME, KIND) GET_VALUE2(return it->second.FIELD_NAME;, KIND) bool params::get_bool(char const * k, params_ref const & fallback, bool _default) const { GET_SIMPLE_VALUE2(m_bool_value, CPK_BOOL); return fallback.get_bool(k, _default); } unsigned params::get_uint(char const * k, params_ref const & fallback, unsigned _default) const { GET_SIMPLE_VALUE2(m_uint_value, CPK_UINT); return fallback.get_uint(k, _default); } double params::get_double(char const * k, params_ref const & fallback, double _default) const { GET_SIMPLE_VALUE2(m_double_value, CPK_DOUBLE); return fallback.get_double(k, _default); } char const * params::get_str(char const * k, params_ref const & fallback, char const * _default) const { GET_SIMPLE_VALUE2(m_str_value, CPK_STRING); return fallback.get_str(k, _default); } symbol params::get_sym(char const * k, params_ref const & fallback, symbol const & _default) const { GET_VALUE2(return symbol::mk_symbol_from_c_ptr(it->second.m_sym_value);, CPK_SYMBOL); return fallback.get_sym(k, _default); } #define SET_VALUE(MATCH_CODE, ADD_CODE) { \ TRAVERSE_ENTRIES(if (it->first == k) { \ MATCH_CODE \ return; \ }); \ ADD_CODE \ } #define SET_SIMPLE_VALUE(FIELD_NAME, KIND) SET_VALUE({ \ del_value(*it); \ it->second.m_kind = KIND; \ it->second.FIELD_NAME = v; \ }, \ { \ entry new_entry; \ new_entry.first = symbol(k); \ new_entry.second.m_kind = KIND; \ new_entry.second.FIELD_NAME = v; \ m_entries.push_back(new_entry); \ }) // setters void params::set_bool(symbol const & k, bool v) { SET_SIMPLE_VALUE(m_bool_value, CPK_BOOL); } void params::set_bool(char const * k, bool v) { SET_SIMPLE_VALUE(m_bool_value, CPK_BOOL); } void params::set_uint(symbol const & k, unsigned v) { SET_SIMPLE_VALUE(m_uint_value, CPK_UINT); } void params::set_uint(char const * k, unsigned v) { SET_SIMPLE_VALUE(m_uint_value, CPK_UINT); } void params::set_double(symbol const & k, double v) { SET_SIMPLE_VALUE(m_double_value, CPK_DOUBLE); } void params::set_double(char const * k, double v) { SET_SIMPLE_VALUE(m_double_value, CPK_DOUBLE); } void params::set_str(symbol const & k, char const * v) { SET_SIMPLE_VALUE(m_str_value, CPK_STRING); } void params::set_str(char const * k, char const * v) { SET_SIMPLE_VALUE(m_str_value, CPK_STRING); } #define SET_RAT_VALUE() SET_VALUE({ \ if (it->second.m_kind != CPK_NUMERAL) { \ del_value(*it); \ it->second.m_kind = CPK_NUMERAL; \ it->second.m_rat_value = alloc(rational); \ } \ *(it->second.m_rat_value) = v; \ }, \ { \ entry new_entry; \ new_entry.first = symbol(k); \ new_entry.second.m_kind = CPK_NUMERAL; \ new_entry.second.m_rat_value = alloc(rational); \ *(new_entry.second.m_rat_value) = v; \ m_entries.push_back(new_entry); \ }) void params::set_rat(symbol const & k, rational const & v) { SET_RAT_VALUE(); } void params::set_rat(char const * k, rational const & v) { SET_RAT_VALUE(); } #define SET_SYM_VALUE() SET_VALUE({ \ del_value(*it); \ it->second.m_kind = CPK_SYMBOL; \ it->second.m_sym_value = v.bare_str(); \ }, \ { \ entry new_entry; \ new_entry.first = symbol(k); \ new_entry.second.m_kind = CPK_SYMBOL; \ new_entry.second.m_sym_value = v.bare_str(); \ m_entries.push_back(new_entry); \ }) void params::set_sym(symbol const & k, symbol const & v) { SET_SYM_VALUE(); } void params::set_sym(char const * k, symbol const & v) { SET_SYM_VALUE(); } #ifdef Z3DEBUG void pp(params_ref const & p) { std::cout << p << std::endl; } #endif z3-z3-4.4.1/src/util/params.h000066400000000000000000000114701260446376700156300ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: params.h Abstract: Parameters. Author: Leonardo (leonardo) 2011-04-22 Notes: --*/ #ifndef PARAMS_H_ #define PARAMS_H_ #include"cmd_context_types.h" #include"vector.h" std::string norm_param_name(char const * n); std::string norm_param_name(symbol const & n); typedef cmd_arg_kind param_kind; class params; class param_descrs; class params_ref { static params_ref g_empty_params_ref; params * m_params; void init(); void copy_core(params const * p); public: params_ref():m_params(0) {} params_ref(params_ref const & p); ~params_ref(); static params_ref const & get_empty() { return g_empty_params_ref; } params_ref & operator=(params_ref const & p); // copy params from src void copy(params_ref const & src); void append(params_ref const & src) { copy(src); } bool get_bool(symbol const & k, bool _default) const; bool get_bool(char const * k, bool _default) const; unsigned get_uint(symbol const & k, unsigned _default) const; unsigned get_uint(char const * k, unsigned _default) const; double get_double(symbol const & k, double _default) const; double get_double(char const * k, double _default) const; char const * get_str(symbol const & k, char const * _default) const; char const * get_str(char const * k, char const * _default) const; rational get_rat(symbol const & k, rational const & _default) const; rational get_rat(char const * k, rational const & _default) const; symbol get_sym(symbol const & k, symbol const & _default) const; symbol get_sym(char const * k, symbol const & _default) const; bool get_bool(char const * k, params_ref const & fallback, bool _default) const; unsigned get_uint(char const * k, params_ref const & fallback, unsigned _default) const; double get_double(char const * k, params_ref const & fallback, double _default) const; char const * get_str(char const * k, params_ref const & fallback, char const * _default) const; symbol get_sym(char const * k, params_ref const & fallback, symbol const & _default) const; bool empty() const; bool contains(symbol const & k) const; bool contains(char const * k) const; void reset(); void reset(symbol const & k); void reset(char const * k); void set_bool(symbol const & k, bool v); void set_bool(char const * k, bool v); void set_uint(symbol const & k, unsigned v); void set_uint(char const * k, unsigned v); void set_double(symbol const & k, double v); void set_double(char const * k, double v); void set_str(symbol const & k, char const * v); void set_str(char const * k, char const * v); void set_rat(symbol const & k, rational const & v); void set_rat(char const * k, rational const & v); void set_sym(symbol const & k, symbol const & v); void set_sym(char const * k, symbol const & v); void display(std::ostream & out) const; void display_smt2(std::ostream& out, char const* module, param_descrs& module_desc) const; void validate(param_descrs const & p); /* \brief Display the value of the given parameter. It displays 'default' if k is not in the parameter set. */ void display(std::ostream & out, char const * k) const; void display(std::ostream & out, symbol const & k) const; }; inline std::ostream & operator<<(std::ostream & out, params_ref const & ref) { ref.display(out); return out; } class param_descrs { struct imp; imp * m_imp; public: param_descrs(); ~param_descrs(); void copy(param_descrs & other); void insert(char const * name, param_kind k, char const * descr, char const * def = 0, char const* module = 0); void insert(symbol const & name, param_kind k, char const * descr, char const * def = 0, char const* module = 0); bool contains(char const * name) const; bool contains(symbol const & name) const; void erase(char const * name); void erase(symbol const & name); param_kind get_kind(char const * name) const; param_kind get_kind(symbol const & name) const; param_kind get_kind_in_module(symbol & name) const; char const * get_descr(char const * name) const; char const * get_descr(symbol const & name) const; char const * get_default(char const * name) const; char const * get_default(symbol const & name) const; void display(std::ostream & out, unsigned indent = 0, bool smt2_style=false, bool include_descr=true) const; unsigned size() const; symbol get_param_name(unsigned idx) const; char const * get_module(symbol const& name) const; }; void insert_max_memory(param_descrs & r); void insert_max_steps(param_descrs & r); void insert_produce_models(param_descrs & r); void insert_produce_proofs(param_descrs & r); void insert_timeout(param_descrs & r); #endif z3-z3-4.4.1/src/util/parray.h000066400000000000000000000423611260446376700156460ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: parray.h Abstract: Persistent Arrays. Author: Leonardo de Moura (leonardo) 2011-02-21. Revision History: --*/ #ifndef PARRAY_H_ #define PARRAY_H_ #include"vector.h" #include"trace.h" template class parray_manager { public: typedef typename C::value value; typedef typename C::value_manager value_manager; typedef typename C::allocator allocator; private: static size_t capacity(value * vs) { return vs == 0 ? 0 : (reinterpret_cast(vs))[-1]; } value * allocate_values(size_t c) { size_t * mem = static_cast(m_allocator.allocate(sizeof(value)*c + sizeof(size_t))); *mem = c; ++mem; value * r = reinterpret_cast(mem); SASSERT(capacity(r) == c); TRACE("parray_mem", tout << "allocated values[" << c << "]: " << r << "\n";); return r; } void deallocate_values(value * vs) { if (vs == 0) return; size_t c = capacity(vs); TRACE("parray_mem", tout << "deallocated values[" << c << "]: " << vs << "\n";); size_t * mem = reinterpret_cast(vs); --mem; m_allocator.deallocate(sizeof(value)*c + sizeof(size_t), mem); } enum ckind { SET, PUSH_BACK, POP_BACK, ROOT }; struct cell { unsigned m_ref_count:30; unsigned m_kind:2; union { unsigned m_idx; unsigned m_size; }; value m_elem; union { cell * m_next; value * m_values; }; ckind kind() const { return static_cast(m_kind); } unsigned idx() const { SASSERT(kind() != ROOT); return m_idx; } unsigned size() const { SASSERT(kind() == ROOT); return m_size; } cell * next() const { SASSERT(kind() != ROOT); return m_next; } value const & elem() const { SASSERT(kind() == SET || kind() == PUSH_BACK); return m_elem; } cell(ckind k):m_ref_count(1), m_kind(k), m_size(0), m_values(0) {} }; value_manager & m_vmanager; allocator & m_allocator; ptr_vector m_get_values_tmp; ptr_vector m_reroot_tmp; void inc_ref(value const & v) { if (C::ref_count) m_vmanager.inc_ref(v); } void dec_ref(value const & v) { if (C::ref_count) m_vmanager.dec_ref(v); } void dec_ref(unsigned sz, value * vs) { if (C::ref_count) for (unsigned i = 0; i < sz; i++) m_vmanager.dec_ref(vs[i]); } cell * mk(ckind k) { cell * r = new (m_allocator.allocate(sizeof(cell))) cell(k); TRACE("parray_mem", tout << "allocated cell: " << r << "\n";); return r; } void del(cell * c) { while (true) { cell * next = 0; switch (c->kind()) { case SET: case PUSH_BACK: dec_ref(c->elem()); next = c->next(); break; case POP_BACK: next = c->next(); break; case ROOT: dec_ref(c->size(), c->m_values); deallocate_values(c->m_values); break; } TRACE("parray_mem", tout << "deallocated cell: " << c << "\n";); c->~cell(); m_allocator.deallocate(sizeof(cell), c); if (next == 0) return; SASSERT(next->m_ref_count > 0); next->m_ref_count--; if (next->m_ref_count > 0) return; c = next; } } void inc_ref(cell * c) { if (!c) return; c->m_ref_count++; } void dec_ref(cell * c) { if (!c) return; TRACE("parray_mem", tout << "dec_ref(" << c << "), ref_count: " << c->m_ref_count << "\n";); SASSERT(c->m_ref_count > 0); c->m_ref_count--; if (c->m_ref_count == 0) del(c); } void expand(value * & vs) { size_t curr_capacity = capacity(vs); size_t new_capacity = curr_capacity == 0 ? 2 : (3 * curr_capacity + 1) >> 1; value * new_vs = allocate_values(new_capacity); if (curr_capacity > 0) { for (size_t i = 0; i < curr_capacity; i++) new_vs[i] = vs[i]; deallocate_values(vs); } vs = new_vs; } void rset(value * vs, unsigned i, value const & v) { inc_ref(v); dec_ref(vs[i]); vs[i] = v; } void rset(cell * c, unsigned i, value const & v) { SASSERT(c->kind() == ROOT); SASSERT(i < c->size()); rset(c->m_values, i, v); } void rpush_back(value * & vs, unsigned & sz, value const & v) { if (sz == capacity(vs)) expand(vs); SASSERT(sz < capacity(vs)); inc_ref(v); vs[sz] = v; sz++; } void rpush_back(cell * c, value const & v) { SASSERT(c->kind() == ROOT); rpush_back(c->m_values, c->m_size, v); } void rpop_back(value * vs, unsigned & sz) { sz--; dec_ref(vs[sz]); } void rpop_back(cell * c) { SASSERT(c->kind() == ROOT); rpop_back(c->m_values, c->m_size); } void copy_values(value * s, unsigned sz, value * & t) { SASSERT(t == 0); t = allocate_values(capacity(s)); for (unsigned i = 0; i < sz; i++) { t[i] = s[i]; inc_ref(t[i]); } } unsigned get_values(cell * s, value * & vs) { ptr_vector & cs = m_get_values_tmp; cs.reset(); cell * r = s; while (r->kind() != ROOT) { cs.push_back(r); r = r->next(); } SASSERT(r->kind() == ROOT); unsigned sz = r->m_size; vs = 0; copy_values(r->m_values, sz, vs); unsigned i = cs.size(); while (i > 0) { --i; cell * curr = cs[i]; switch (curr->kind()) { case SET: rset(vs, curr->m_idx, curr->m_elem); break; case POP_BACK: rpop_back(vs, sz); break; case PUSH_BACK: rpush_back(vs, sz, curr->m_elem); break; case ROOT: UNREACHABLE(); break; } } return sz; } void unfold(cell * c) { if (c->kind() == ROOT) return; value * vs; unsigned sz = get_values(c, vs); dec_ref(c->m_next); if (c->kind() == SET || c->kind() == PUSH_BACK) dec_ref(c->m_elem); c->m_next = 0; c->m_kind = ROOT; c->m_size = sz; c->m_values = vs; SASSERT(c->kind() == ROOT); } public: class ref { cell * m_ref; unsigned m_updt_counter; // counter for minimizing memory consumption when using preserve_roots option ref(cell * r):m_ref(r), m_updt_counter(0) {} bool root() const { return m_ref->kind() == ROOT; } bool unshared() const { return m_ref->m_ref_count == 1; } friend class parray_manager; public: ref():m_ref(0), m_updt_counter(0) {} }; public: parray_manager(value_manager & m, allocator & a): m_vmanager(m), m_allocator(a) { } value_manager & manager() { return m_vmanager; } void mk(ref & r) { dec_ref(r.m_ref); cell * new_c = mk(ROOT); r.m_ref = new_c; r.m_updt_counter = 0; SASSERT(new_c->m_ref_count == 1); } void del(ref & r) { dec_ref(r.m_ref); r.m_ref = 0; r.m_updt_counter = 0; } void copy(ref const & s, ref & t) { inc_ref(s.m_ref); dec_ref(t.m_ref); t.m_ref = s.m_ref; t.m_updt_counter = 0; } unsigned size(ref const & r) const { cell * c = r.m_ref; if (c == 0) return 0; while (true) { switch (c->kind()) { case SET: c = c->next(); break; case PUSH_BACK: return c->idx() + 1; case POP_BACK: return c->idx() - 1; case ROOT: return c->size(); } } } bool empty(ref const & r) const { return size(r) == 0; } value const & get(ref const & r, unsigned i) const { SASSERT(i < size(r)); unsigned trail_sz = 0; cell * c = r.m_ref; while (true) { if (trail_sz > C::max_trail_sz) { const_cast(this)->reroot(const_cast(r)); SASSERT(r.m_ref->kind() == ROOT); return r.m_ref->m_values[i]; } switch (c->kind()) { case SET: case PUSH_BACK: if (i == c->idx()) return c->elem(); trail_sz++; c = c->next(); break; case POP_BACK: trail_sz++; c = c->next(); break; case ROOT: return c->m_values[i]; } } } void set(ref & r, unsigned i, value const & v) { SASSERT(i < size(r)); if (r.root()) { if (r.unshared()) { rset(r.m_ref, i, v); return; } if (C::preserve_roots) { if (r.m_updt_counter > size(r)) { unshare(r); SASSERT(r.unshared()); SASSERT(r.m_updt_counter == 0); rset(r.m_ref, i, v); return; } r.m_updt_counter++; cell * c = r.m_ref; cell * new_c = mk(ROOT); new_c->m_size = c->m_size; new_c->m_values = c->m_values; inc_ref(new_c); c->m_kind = SET; c->m_idx = i; c->m_elem = c->m_values[i]; inc_ref(c->m_elem); c->m_next = new_c; dec_ref(c); r.m_ref = new_c; rset(new_c, i, v); SASSERT(new_c->m_ref_count == 2); return; } } cell * new_c = mk(SET); new_c->m_idx = i; inc_ref(v); new_c->m_elem = v; new_c->m_next = r.m_ref; r.m_ref = new_c; SASSERT(new_c->m_ref_count == 1); } void set(ref const & s, unsigned i, value const & v, ref & r) { SASSERT(i < size(s)); if (&s == &r) { set(r, i, v); return; } copy(s, r); set(r, i, v); } void push_back(ref & r, value const & v) { if (r.m_ref == 0) mk(r); if (r.root()) { if (r.unshared()) { rpush_back(r.m_ref, v); return; } if (C::preserve_roots) { if (r.m_updt_counter > size(r)) { unshare(r); SASSERT(r.unshared()); SASSERT(r.m_updt_counter == 0); rpush_back(r.m_ref, v); return; } r.m_updt_counter++; cell * c = r.m_ref; SASSERT(c->m_ref_count > 1); cell * new_c = mk(ROOT); new_c->m_size = c->m_size; new_c->m_values = c->m_values; inc_ref(new_c); c->m_kind = POP_BACK; c->m_idx = new_c->m_size + 1; c->m_next = new_c; dec_ref(c); r.m_ref = new_c; rpush_back(new_c, v); SASSERT(new_c->m_ref_count == 2); return; } } cell * new_c = mk(PUSH_BACK); new_c->m_idx = size(r.m_ref); inc_ref(v); new_c->m_elem = v; new_c->m_next = r.m_ref; r.m_ref = new_c; SASSERT(new_c->m_ref_count == 1); } void push_back(ref const & s, value const & v, ref & r) { if (&s == &r) { push_back(r, v); return; } copy(s, r); push_back(r, v); } void pop_back(ref & r) { SASSERT(!empty(r)); if (r.root()) { if (r.unshared()) { rpop_back(r.m_ref); return; } if (C::preserve_roots) { if (r.m_updt_counter > size(r)) { unshare(r); SASSERT(r.unshared()); SASSERT(r.m_updt_counter == 0); rpop_back(r.m_ref); return; } r.m_updt_counter++; cell * c = r.m_ref; SASSERT(c->m_ref_count > 1); cell * new_c = mk(ROOT); new_c->m_size = c->m_size; new_c->m_values = c->m_values; inc_ref(new_c); c->m_kind = PUSH_BACK; c->m_idx = new_c->m_size - 1; c->m_elem = new_c->m_values[c->m_idx]; inc_ref(c->m_elem); c->m_next = new_c; dec_ref(c); r.m_ref = new_c; rpop_back(new_c); SASSERT(new_c->m_ref_count == 2); return; } } cell * new_c = mk(POP_BACK); new_c->m_idx = size(r.m_ref); new_c->m_next = r.m_ref; r.m_ref = new_c; SASSERT(new_c->m_ref_count == 1); } void pop_back(ref const & s, ref & r) { SASSERT(!empty(s)); if (&s == &r) { pop_back(r); return; } copy(s, r); pop_back(r); } void unshare(ref & r) { if (r.root() && r.unshared()) return; cell * c = r.m_ref; cell * new_c = mk(ROOT); new_c->m_size = get_values(c, new_c->m_values); SASSERT(new_c->m_ref_count == 1); dec_ref(c); r.m_ref = new_c; r.m_updt_counter = 0; SASSERT(r.root()); SASSERT(r.unshared()); } void unfold(ref & r) { if (r.root()) return; unfold(r.m_ref); r.m_updt_counter = 0; SASSERT(r.root()); } void reroot(ref & r) { if (r.root()) return; ptr_vector & cs = m_reroot_tmp; cs.reset(); unsigned r_sz = size(r); unsigned trail_split_idx = r_sz / C::factor; unsigned i = 0; cell * c = r.m_ref; while (c->kind() != ROOT && i < trail_split_idx) { cs.push_back(c); c = c->next(); i++; } if (c->kind() != ROOT) { // root is too far away. unfold(c); } SASSERT(c->kind() == ROOT); i = cs.size(); while (i > 0) { --i; cell * p = cs[i]; SASSERT(c->m_kind == ROOT); unsigned sz = c->m_size; value * vs = c->m_values; SASSERT(p->m_kind != ROOT); SASSERT(p->m_next == c); switch (p->m_kind) { case SET: c->m_kind = SET; c->m_idx = p->m_idx; c->m_elem = vs[c->m_idx]; vs[p->m_idx] = p->m_elem; break; case PUSH_BACK: c->m_kind = POP_BACK; if (sz == capacity(vs)) expand(vs); c->m_idx = sz; vs[sz] = p->m_elem; sz++; break; case POP_BACK: c->m_kind = PUSH_BACK; --sz; c->m_idx = sz; c->m_elem = vs[sz]; break; case ROOT: UNREACHABLE(); break; } inc_ref(p); c->m_next = p; // p does not point to c anymore dec_ref(c); p->m_kind = ROOT; p->m_size = sz; p->m_values = vs; c = p; } SASSERT(c == r.m_ref); SASSERT(c->kind() == ROOT); SASSERT(c->m_size == r_sz); r.m_updt_counter = 0; SASSERT(r.root()); } void display_info(std::ostream & out, ref const & r) { cell * c = r.m_ref; if (c == 0) { out << ""; return; } while (true) { out << "cell[" << c << ", "; switch (c->kind()) { case SET: out << "set, " << c->m_idx; break; case PUSH_BACK: out << "push, " << c->m_idx; break; case POP_BACK: out << "pop, " << c->m_idx; break; case ROOT: out << "root, " << c->m_size << ", " << capacity(c->m_values); break; } out << "]#" << c->m_ref_count; if (c->kind() == ROOT) break; out << " -> "; c = c->next(); } } }; template struct dummy_value_manager { void inc_ref(T const &) {} void dec_ref(T const &) {} }; #endif z3-z3-4.4.1/src/util/permutation.cpp000066400000000000000000000030121260446376700172400ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: permutation.cpp Abstract: Goodies for managing permutations. Author: Leonardo de Moura (leonardo) 2011-06-10. Revision History: --*/ #include"permutation.h" permutation::permutation(unsigned size) { reset(size); } void permutation::reset(unsigned size) { m_p.reset(); m_inv_p.reset(); for (unsigned i = 0; i < size; i++) { m_p.push_back(i); m_inv_p.push_back(i); } } void permutation::swap(unsigned i, unsigned j) { unsigned i_prime = m_p[i]; unsigned j_prime = m_p[j]; std::swap(m_p[i], m_p[j]); std::swap(m_inv_p[i_prime], m_inv_p[j_prime]); } /** \brief Move i after j. */ void permutation::move_after(unsigned i, unsigned j) { if (i >= j) return; unsigned i_prime = m_p[i]; for (unsigned k = i; k < j; k++) { m_p[k] = m_p[k+1]; m_inv_p[m_p[k]] = k; } m_p[j] = i_prime; m_inv_p[i_prime] = j; SASSERT(check_invariant()); } void permutation::display(std::ostream & out) const { unsigned n = m_p.size(); for (unsigned i = 0; i < n; i++) { if (i > 0) out << " "; out << i << ":" << m_p[i]; } } bool permutation::check_invariant() const { SASSERT(m_p.size() == m_inv_p.size()); unsigned n = m_p.size(); for (unsigned i = 0; i < n; i++) { SASSERT(m_p[i] < n); SASSERT(m_inv_p[i] < n); SASSERT(m_p[m_inv_p[i]] == i); SASSERT(m_inv_p[m_p[i]] == i); } return true; } z3-z3-4.4.1/src/util/permutation.h000066400000000000000000000042741260446376700167200ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: permutation.h Abstract: Simple abstraction for managing permutations. Author: Leonardo de Moura (leonardo) 2011-06-10. Revision History: --*/ #ifndef PERMUTATION_H_ #define PERMUTATION_H_ #include #include"vector.h" class permutation { unsigned_vector m_p; unsigned_vector m_inv_p; public: permutation(unsigned size = 0); void reset(unsigned size = 0); unsigned operator()(unsigned i) const { return m_p[i]; } unsigned inv(unsigned i_prime) const { return m_inv_p[i_prime]; } void swap(unsigned i, unsigned j); void move_after(unsigned i, unsigned j); void display(std::ostream & out) const; bool check_invariant() const; }; inline std::ostream & operator<<(std::ostream & out, permutation const & p) { p.display(out); return out; } /** \brief Apply permutation p to data. The algorithm does not use any extra memory. Requirement: swap(T, T) must be available. This version will perform destructive updates to p. Use apply_permutation if p must not be preserved */ template void apply_permutation_core(unsigned sz, T * data, unsigned * p) { int * p1 = reinterpret_cast(p); for (int i = 0; i < static_cast(sz); i++) { if (p1[i] < 0) continue; // already processed int j = i; while (true) { SASSERT(j >= 0); int p_j = p1[j]; SASSERT(p_j >= 0); SASSERT(p_j < static_cast(sz)); p1[j] = - p1[j] - 1; // mark as done if (p_j == i) break; // cycle starting at i is done swap(data[j], data[p_j]); j = p_j; } } } /** \brief Apply permutation p to data. The algorithm does not use any extra memory. Requirement: swap(T, T) must be available. */ template void apply_permutation(unsigned sz, T * data, unsigned const * p) { apply_permutation_core(sz, data, const_cast(p)); // restore p int * p1 = reinterpret_cast(const_cast(p)); for (unsigned i = 0; i < sz; i++) { p1[i] = - p1[i] - 1; } } #endif z3-z3-4.4.1/src/util/plugin_manager.h000066400000000000000000000024271260446376700173370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: plugin_manager.h Abstract: Author: Leonardo de Moura (leonardo) 2007-09-18. Revision History: --*/ #ifndef PLUGIN_MANAGER_H_ #define PLUGIN_MANAGER_H_ #include"util.h" template class plugin_manager { ptr_vector m_fid2plugins; ptr_vector m_plugins; public: ~plugin_manager() { std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); } /** \brief Release ownership of the plugins. */ void release() { m_fid2plugins.reset(); m_plugins.reset(); } void register_plugin(Plugin * p) { SASSERT(p); family_id fid = p->get_family_id(); SASSERT(m_fid2plugins.get(fid, 0) == 0); m_fid2plugins.setx(fid, p, 0); m_plugins.push_back(p); } Plugin * get_plugin(family_id fid) const { if (fid == null_family_id) { return 0; } return m_fid2plugins.get(fid, 0); } typename ptr_vector::const_iterator begin() const { return m_plugins.begin(); } typename ptr_vector::const_iterator end() const { return m_plugins.end(); } }; #endif /* PLUGIN_MANAGER_H_ */ z3-z3-4.4.1/src/util/pool.h000066400000000000000000000013061260446376700153130ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pool.h Abstract: Object pool. Author: Leonardo de Moura (leonardo) 2007-02-15. Revision History: --*/ #ifndef POOL_H_ #define POOL_H_ #include"util.h" #include"vector.h" template class pool { ptr_vector m_objs; public: ~pool() { std::for_each(m_objs.begin(), m_objs.end(), delete_proc()); } T * mk() { if (m_objs.empty()) { return alloc(T); } else { T * r = m_objs.back(); m_objs.pop_back(); return r; } } void recycle(T * obj) { m_objs.push_back(obj); } }; #endif /* POOL_H_ */ z3-z3-4.4.1/src/util/pop_scopes.h000066400000000000000000000021001260446376700165050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pop_scopes.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-02. Revision History: --*/ #ifndef POP_SCOPES_H_ #define POP_SCOPES_H_ #define POP_SCOPES(_num_scopes, _lim, _trail, _action) \ if (_num_scopes > 0) \ { \ unsigned scope_lvl = _lim.size(); \ unsigned new_lvl = scope_lvl - _num_scopes; \ unsigned curr_size = _trail.size(); \ unsigned old_size = _lim[new_lvl]; \ for (unsigned i = curr_size-1; i >= old_size && i != static_cast(-1); --i) { \ _action; \ } \ _trail.shrink(old_size); \ _lim.shrink(new_lvl); \ } #endif /* POP_SCOPES_H_ */ z3-z3-4.4.1/src/util/prime_generator.cpp000066400000000000000000000056601260446376700200660ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: prime_generator.cpp Abstract: Prime generator Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include"prime_generator.h" #define PRIME_LIST_MAX_SIZE 1<<20 prime_generator::prime_generator() { m_primes.push_back(2); m_primes.push_back(3); process_next_k_numbers(128); } void prime_generator::process_next_k_numbers(uint64 k) { svector todo; uint64 begin = m_primes.back() + 2; uint64 end = begin + k; for (uint64 i = begin; i < end; i+=2) { todo.push_back(i); } unsigned j = 1; SASSERT(m_primes[j] == 3); while (!todo.empty()) { unsigned sz = m_primes.size(); for (; j < sz; j++) { uint64 p = m_primes[j]; unsigned todo_sz = todo.size(); unsigned k1 = 0; unsigned k2 = 0; for (; k1 < todo_sz; k1++) { if (todo[k1] % p == 0) continue; todo[k2] = todo[k1]; k2++; } todo.shrink(k2); if (k2 == 0) return; if (p > (todo[k2-1] / p) + 1) { // all numbers in todo are primes for (unsigned k1 = 0; k1 < k2; k1++) { m_primes.push_back(todo[k1]); } return; } } uint64 p = m_primes.back(); p = p*p; unsigned todo_sz = todo.size(); unsigned k1 = 0; for (k1 = 0; k1 < todo_sz; k1++) { if (todo[k1] < p) { m_primes.push_back(todo[k1]); } else { break; } } unsigned k2 = 0; for (; k1 < todo_sz; k1++, k2++) { todo[k2] = todo[k1]; } todo.shrink(k2); } } void prime_generator::finalize() { m_primes.finalize(); } uint64 prime_generator::operator()(unsigned idx) { if (idx < m_primes.size()) return m_primes[idx]; if (idx > PRIME_LIST_MAX_SIZE) throw prime_generator_exception("prime generator capacity exceeded"); process_next_k_numbers(1024); if (idx < m_primes.size()) return m_primes[idx]; while (idx <= m_primes.size()) process_next_k_numbers(1024*16); return m_primes[idx]; } prime_generator g_prime_generator; prime_iterator::prime_iterator(prime_generator * g):m_idx(0) { if (g == 0) { m_generator = &g_prime_generator; m_global = true; } else { m_generator = g; m_global = false; } } uint64 prime_iterator::next() { unsigned idx = m_idx; m_idx++; if (!m_global) { return (*m_generator)(idx); } else { uint64 r; #pragma omp critical (prime_iterator) { r = (*m_generator)(idx); } return r; } } void prime_iterator::finalize() { g_prime_generator.finalize(); } z3-z3-4.4.1/src/util/prime_generator.h000066400000000000000000000016741260446376700175340ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: prime_generator.h Abstract: Prime generator Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #ifndef PRIME_GENERATOR_H_ #define PRIME_GENERATOR_H_ #include"vector.h" #include"z3_exception.h" #include"util.h" class prime_generator_exception : public default_exception { public: prime_generator_exception(char const * msg):default_exception(msg) {} }; /** \brief Prime generator */ class prime_generator { svector m_primes; void process_next_k_numbers(uint64 k); public: prime_generator(); uint64 operator()(unsigned idx); void finalize(); }; class prime_iterator { unsigned m_idx; prime_generator * m_generator; bool m_global; public: prime_iterator(prime_generator * g = 0); uint64 next(); static void finalize(); /* ADD_FINALIZER('prime_iterator::finalize();') */ }; #endif z3-z3-4.4.1/src/util/ptr_scoped_buffer.h000066400000000000000000000041661260446376700200440ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ptr_scoped_buffer.h Abstract: Author: Leonardo de Moura (leonardo) 2011-03-03. Revision History: --*/ #ifndef PTR_SCOPED_BUFFER_H_ #define PTR_SCOPED_BUFFER_H_ #include"util.h" #include"debug.h" #include"buffer.h" template > class ptr_scoped_buffer : private ptr_buffer { D m_deallocator; void deallocate_all() { typename ptr_buffer::iterator it = ptr_buffer::begin(); typename ptr_buffer::iterator end = ptr_buffer::end(); for (; it != end; ++it) m_deallocator(*it); } public: typedef typename ptr_buffer::const_iterator const_iterator; ptr_scoped_buffer(D const & m = D()):ptr_buffer(), m_deallocator(m) {} ~ptr_scoped_buffer() { deallocate_all(); } void reset() { deallocate_all(); ptr_buffer::reset(); } void finalize() { deallocate_all(); ptr_buffer::finalize(); } /** \brief Release ownership of the pointers stored in the buffer */ void release() { ptr_buffer::reset(); } unsigned size() const { return ptr_buffer::size(); } bool empty() const { return ptr_buffer::empty(); } const_iterator begin() const { return ptr_buffer::begin(); } const_iterator end() const { return ptr_buffer::end(); } void push_back(T * elem) { return ptr_buffer::push_back(elem); } T * back() const { return ptr_buffer::back(); } void pop_back() { m_deallocator(back()); ptr_buffer::pop_back(); } T * get(unsigned idx) const { return ptr_buffer::get(idx); } void set(unsigned idx, T * e) { T * old_e = get(idx); if (e != old_e) m_deallocator(old_e); ptr_buffer::set(idx, e); } void append(unsigned n, T * const * elems) { ptr_buffer::append(n, elems); } }; #endif z3-z3-4.4.1/src/util/rational.cpp000066400000000000000000000034471260446376700165160ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: rational.cpp Abstract: Rational numbers Author: Leonardo de Moura (leonardo) 2006-09-18. Revision History: --*/ #include #include"util.h" #include"rational.h" #ifdef _WINDOWS #include #endif synch_mpq_manager * rational::g_mpq_manager = 0; rational rational::m_zero; rational rational::m_one; rational rational::m_minus_one; vector rational::m_powers_of_two; static void mk_power_up_to(vector & pws, unsigned n) { if (pws.empty()) { pws.push_back(rational::one()); } unsigned sz = pws.size(); rational curr = pws[sz - 1]; rational two(2); for (unsigned i = sz; i <= n; i++) { curr *= two; pws.push_back(curr); } } rational rational::power_of_two(unsigned k) { rational result; #pragma omp critical (powers_of_two) { if (k >= m_powers_of_two.size()) mk_power_up_to(m_powers_of_two, k+1); result = m_powers_of_two[k]; } return result; } // in inf_rational.cpp void initialize_inf_rational(); void finalize_inf_rational(); // in inf_int_rational.cpp void initialize_inf_int_rational(); void finalize_inf_int_rational(); void rational::initialize() { if (!g_mpq_manager) { g_mpq_manager = alloc(synch_mpq_manager); m().set(m_zero.m_val, 0); m().set(m_one.m_val, 1); m().set(m_minus_one.m_val, -1); initialize_inf_rational(); initialize_inf_int_rational(); } } void rational::finalize() { finalize_inf_rational(); finalize_inf_int_rational(); m_powers_of_two.finalize(); m_zero.~rational(); m_one.~rational(); m_minus_one.~rational(); dealloc(g_mpq_manager); g_mpq_manager = 0; } z3-z3-4.4.1/src/util/rational.h000066400000000000000000000274161260446376700161650ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: rational.h Abstract: Rational numbers Author: Leonardo de Moura (leonardo) 2006-09-18. Revision History: --*/ #ifndef RATIONAL_H_ #define RATIONAL_H_ #include"mpq.h" class rational { mpq m_val; static rational m_zero; static rational m_one; static rational m_minus_one; static vector m_powers_of_two; static synch_mpq_manager * g_mpq_manager; static synch_mpq_manager & m() { return *g_mpq_manager; } public: static void initialize(); static void finalize(); /* ADD_INITIALIZER('rational::initialize();') ADD_FINALIZER('rational::finalize();') */ rational() {} rational(rational const & r) { m().set(m_val, r.m_val); } explicit rational(int n) { m().set(m_val, n); } explicit rational(unsigned n) { m().set(m_val, n); } rational(int n, int d) { m().set(m_val, n, d); } rational(mpq const & q) { m().set(m_val, q); } rational(mpz const & z) { m().set(m_val, z); } explicit rational(char const * v) { m().set(m_val, v); } struct i64 {}; rational(int64 i, i64) { m().set(m_val, i); } struct ui64 {}; rational(uint64 i, ui64) { m().set(m_val, i); } ~rational() { m().del(m_val); } mpq const & to_mpq() const { return m_val; } unsigned bitsize() const { return m().bitsize(m_val); } void reset() { m().reset(m_val); } bool is_int() const { return m().is_int(m_val); } bool is_small() const { return m().is_small(m_val); } bool is_big() const { return !is_small(); } unsigned hash() const { return m().hash(m_val); } struct hash_proc { unsigned operator()(rational const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(rational const& r1, rational const& r2) const { return r1 == r2; } }; void swap(rational & n) { m().swap(m_val, n.m_val); } std::string to_string() const { return m().to_string(m_val); } void display(std::ostream & out) const { return m().display(out, m_val); } void display_decimal(std::ostream & out, unsigned prec) const { return m().display_decimal(out, m_val, prec); } bool is_uint64() const { return m().is_uint64(m_val); } bool is_int64() const { return m().is_int64(m_val); } uint64 get_uint64() const { return m().get_uint64(m_val); } int64 get_int64() const { return m().get_int64(m_val); } bool is_unsigned() const { return is_uint64() && (get_uint64() < (1ull << 32)); } unsigned get_unsigned() const { SASSERT(is_unsigned()); return static_cast(get_uint64()); } bool is_int32() const { if (is_small() && is_int()) return true; // we don't assume that if it is small, then it is int32. if (!is_int64()) return false; int64 v = get_int64(); return INT_MIN <= v && v <= INT_MAX; } int get_int32() const { SASSERT(is_int32()); return (int)get_int64(); } double get_double() const { return m().get_double(m_val); } rational const & get_rational() const { return *this; } rational const & get_infinitesimal() const { return m_zero; } rational & operator=(rational const & r) { m().set(m_val, r.m_val); return *this; } friend inline rational numerator(rational const & r) { rational result; m().get_numerator(r.m_val, result.m_val); return result; } friend inline rational denominator(rational const & r) { rational result; m().get_denominator(r.m_val, result.m_val); return result; } rational & operator+=(rational const & r) { m().add(m_val, r.m_val, m_val); return *this; } rational & operator-=(rational const & r) { m().sub(m_val, r.m_val, m_val); return *this; } rational & operator*=(rational const & r) { m().mul(m_val, r.m_val, m_val); return *this; } rational & operator/=(rational const & r) { m().div(m_val, r.m_val, m_val); return *this; } rational & operator%=(rational const & r) { m().rem(m_val, r.m_val, m_val); return *this; } friend inline rational div(rational const & r1, rational const & r2) { rational r; rational::m().idiv(r1.m_val, r2.m_val, r.m_val); return r; } friend inline void div(rational const & r1, rational const & r2, rational & r) { rational::m().idiv(r1.m_val, r2.m_val, r.m_val); } friend inline rational machine_div(rational const & r1, rational const & r2) { rational r; rational::m().machine_idiv(r1.m_val, r2.m_val, r.m_val); return r; } friend inline rational mod(rational const & r1, rational const & r2) { rational r; rational::m().mod(r1.m_val, r2.m_val, r.m_val); return r; } friend inline void mod(rational const & r1, rational const & r2, rational & r) { rational::m().mod(r1.m_val, r2.m_val, r.m_val); } friend inline rational operator%(rational const & r1, rational const & r2) { rational r; rational::m().rem(r1.m_val, r2.m_val, r.m_val); return r; } friend inline rational mod_hat(rational const & a, rational const & b) { SASSERT(b.is_pos()); rational r = mod(a,b); SASSERT(r.is_nonneg()); rational r2 = r; r2 *= rational(2); if (operator<(b, r2)) { r -= b; } return r; } rational & operator++() { m().add(m_val, m().mk_q(1), m_val); return *this; } const rational operator++(int) { rational tmp(*this); ++(*this); return tmp; } rational & operator--() { m().sub(m_val, m().mk_q(1), m_val); return *this; } const rational operator--(int) { rational tmp(*this); --(*this); return tmp; } friend inline bool operator==(rational const & r1, rational const & r2) { return rational::m().eq(r1.m_val, r2.m_val); } friend inline bool operator<(rational const & r1, rational const & r2) { return rational::m().lt(r1.m_val, r2.m_val); } void neg() { m().neg(m_val); } bool is_zero() const { return m().is_zero(m_val); } bool is_one() const { return m().is_one(m_val); } bool is_minus_one() const { return m().is_minus_one(m_val); } bool is_neg() const { return m().is_neg(m_val); } bool is_pos() const { return m().is_pos(m_val); } bool is_nonneg() const { return m().is_nonneg(m_val); } bool is_nonpos() const { return m().is_nonpos(m_val); } bool is_even() const { return m().is_even(m_val); } friend inline rational floor(rational const & r) { rational f; rational::m().floor(r.m_val, f.m_val); return f; } friend inline rational ceil(rational const & r) { rational f; rational::m().ceil(r.m_val, f.m_val); return f; } rational expt(int n) const { rational result; m().power(m_val, n, result.m_val); return result; } static rational power_of_two(unsigned k); bool is_power_of_two(unsigned & shift) { return m().is_power_of_two(m_val, shift); } static rational const & zero() { return m_zero; } static rational const & one() { return m_one; } static rational const & minus_one() { return m_minus_one; } void addmul(rational const & c, rational const & k) { if (c.is_one()) operator+=(k); else if (c.is_minus_one()) operator-=(k); else { rational tmp(k); tmp *= c; operator+=(tmp); } } // Perform: this -= c * k void submul(const rational & c, const rational & k) { if (c.is_one()) operator-=(k); else if (c.is_minus_one()) operator+=(k); else { rational tmp(k); tmp *= c; operator-=(tmp); } } bool is_int_perfect_square(rational & root) const { return m().is_int_perfect_square(m_val, root.m_val); } bool is_perfect_square(rational & root) const { return m().is_perfect_square(m_val, root.m_val); } bool root(unsigned n, rational & root) const { return m().root(m_val, n, root.m_val); } friend inline std::ostream & operator<<(std::ostream & target, rational const & r) { target << m().to_string(r.m_val); return target; } friend inline rational gcd(rational const & r1, rational const & r2); // // extended Euclid: // r1*a + r2*b = gcd // friend inline rational gcd(rational const & r1, rational const & r2, rational & a, rational & b); friend inline rational lcm(rational const & r1, rational const & r2) { rational result; m().lcm(r1.m_val, r2.m_val, result.m_val); return result; } friend inline rational bitwise_or(rational const & r1, rational const & r2) { rational result; m().bitwise_or(r1.m_val, r2.m_val, result.m_val); return result; } friend inline rational bitwise_and(rational const & r1, rational const & r2) { rational result; m().bitwise_and(r1.m_val, r2.m_val, result.m_val); return result; } friend inline rational bitwise_xor(rational const & r1, rational const & r2) { rational result; m().bitwise_xor(r1.m_val, r2.m_val, result.m_val); return result; } friend inline rational bitwise_not(unsigned sz, rational const & r1) { rational result; m().bitwise_not(sz, r1.m_val, result.m_val); return result; } friend inline rational abs(rational const & r); rational to_rational() const { return *this; } static bool is_rational() { return true; } unsigned get_num_bits() const { rational two(2); SASSERT(is_int()); SASSERT(!is_neg()); rational n(*this); unsigned num_bits = 1; n = div(n, two); while (n.is_pos()) { ++num_bits; n = div(n, two); } return num_bits; } }; inline bool operator!=(rational const & r1, rational const & r2) { return !operator==(r1, r2); } inline bool operator>(rational const & r1, rational const & r2) { return operator<(r2, r1); } inline bool operator<=(rational const & r1, rational const & r2) { return !operator>(r1, r2); } inline bool operator>=(rational const & r1, rational const & r2) { return !operator<(r1, r2); } inline rational operator+(rational const & r1, rational const & r2) { return rational(r1) += r2; } inline rational operator-(rational const & r1, rational const & r2) { return rational(r1) -= r2; } inline rational operator-(rational const & r) { rational result(r); result.neg(); return result; } inline rational operator*(rational const & r1, rational const & r2) { return rational(r1) *= r2; } inline rational operator/(rational const & r1, rational const & r2) { return rational(r1) /= r2; } inline rational power(rational const & r, unsigned p) { return r.expt(p); } inline rational abs(rational const & r) { rational result(r); rational::m().abs(result.m_val); return result; } inline rational gcd(rational const & r1, rational const & r2) { rational result; rational::m().gcd(r1.m_val, r2.m_val, result.m_val); return result; } inline rational gcd(rational const & r1, rational const & r2, rational & a, rational & b) { rational result; rational::m().gcd(r1.m_val, r2.m_val, a.m_val, b.m_val, result.m_val); return result; } #endif /* RATIONAL_H_ */ z3-z3-4.4.1/src/util/ref.h000066400000000000000000000036601260446376700151230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ref.h Abstract: Simple smart pointer class Author: Leonardo de Moura (leonardo) 2007-08-14. Revision History: --*/ #ifndef REF_H_ #define REF_H_ template class ref { T * m_ptr; void inc_ref() { if (m_ptr) { m_ptr->inc_ref(); } } void dec_ref() { if (m_ptr) { m_ptr->dec_ref(); } } public: ref(): m_ptr(0) { } ref(T * ptr): m_ptr(ptr) { inc_ref(); } ref(const ref & r): m_ptr(r.m_ptr) { inc_ref(); } ~ref() { dec_ref(); } T * operator->() const { return m_ptr; } T * get() const { return m_ptr; } operator bool() const { return m_ptr != 0; } const T & operator*() const { return *m_ptr; } T & operator*() { return *m_ptr; } ref & operator=(T * ptr) { if (ptr) ptr->inc_ref(); dec_ref(); m_ptr = ptr; return *this; } ref & operator=(ref & r) { r.inc_ref(); dec_ref(); m_ptr = r.m_ptr; return *this; } void reset() { dec_ref(); m_ptr = 0; } T* detach() { T* tmp = m_ptr; m_ptr = 0; return tmp; } friend bool operator==(const ref & r1, const ref & r2) { return r1.m_ptr == r2.m_ptr; } friend bool operator!=(const ref & r1, const ref & r2) { return r1.m_ptr != r2.m_ptr; } }; /** \brief Manager for references that are not managed. This class is used for allowing us to create instantiations of the ref_vector and ref_buffer templates for unmanaged objects. */ template class unmanged_ref_manager { static void inc_ref(T * o) { o->inc_ref(); } static void dec_ref(T * o) { o->dec_ref(); } }; #endif /* REF_H_ */ z3-z3-4.4.1/src/util/ref_buffer.h000066400000000000000000000072101260446376700164470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ref_buffer.h Abstract: Buffer of smart pointers. Author: Leonardo de Moura (leonardo) 2008-01-04. Revision History: --*/ #ifndef REF_BUFFER_H_ #define REF_BUFFER_H_ #include"buffer.h" #include"obj_ref.h" #include"ref_vector.h" /** \brief Buffer of smart pointers. Ref must provide the methods - void dec_ref(T * obj) - void inc_ref(T * obj) */ template class ref_buffer_core : public Ref { protected: ptr_buffer m_buffer; void inc_ref(T * o) { Ref::inc_ref(o); } void dec_ref(T * o) { Ref::dec_ref(o); } void dec_range_ref(T * const * begin, T * const * end) { for (T * const * it = begin; it < end; ++it) dec_ref(*it); } public: typedef T * data; ref_buffer_core(Ref const & r = Ref()): Ref(r) { } ~ref_buffer_core() { dec_range_ref(m_buffer.begin(), m_buffer.end()); } void push_back(T * n) { inc_ref(n); m_buffer.push_back(n); } void pop_back() { SASSERT(!m_buffer.empty()); T * n = m_buffer.back(); m_buffer.pop_back(); dec_ref(n); } T * back() const { return m_buffer.back(); } T * & back() { return m_buffer.back(); } T ** c_ptr() const { return m_buffer.c_ptr(); } T * operator[](unsigned idx) const { return m_buffer[idx]; } void set(unsigned idx, T * n) { inc_ref(n); dec_ref(m_buffer[idx]); m_buffer[idx] = n; } unsigned size() const { return m_buffer.size(); } bool empty() const { return m_buffer.empty(); } void reset() { dec_range_ref(m_buffer.begin(), m_buffer.end()); m_buffer.reset(); } void finalize() { dec_range_ref(m_buffer.begin(), m_buffer.end()); m_buffer.finalize(); } void append(unsigned n, T * const * elems) { for (unsigned i = 0; i < n; i++) { push_back(elems[i]); } } void append(ref_buffer_core const & other) { append(other.size(), other.c_ptr()); } void resize(unsigned sz) { if (sz < m_buffer.size()) dec_range_ref(m_buffer.begin() + sz, m_buffer.end()); m_buffer.resize(sz, 0); } void shrink(unsigned sz) { SASSERT(sz <= m_buffer.size()); resize(sz); } // set pos idx with elem. If idx >= size, then expand. void setx(unsigned idx, T * elem) { if (idx >= size()) { resize(idx+1); } set(idx, elem); } ref_buffer_core & operator=(ref_buffer_core const & other) { if (this == &other) return *this; reset(); append(other); return *this; } }; /** \brief Buffer of managed references */ template class ref_buffer : public ref_buffer_core, INITIAL_SIZE> { typedef ref_buffer_core, INITIAL_SIZE> super; public: ref_buffer(TManager & m): super(ref_manager_wrapper(m)) { } ref_buffer(ref_buffer const & other): super(ref_manager_wrapper(other.m_manager)) { SASSERT(this->m_buffer.size() == 0); append(other); } }; /** \brief Buffer of unmanaged references */ template class sref_buffer : public ref_buffer_core, INITIAL_SIZE> { public: }; #endif /* REF_BUFFER_H_ */ z3-z3-4.4.1/src/util/ref_util.h000066400000000000000000000037261260446376700161630ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ref_util.h Abstract: Some goodies for managing reference counters. Author: Leonardo (leonardo) 2011-06-07 Notes: --*/ #ifndef REF_UTIL_H_ #define REF_UTIL_H_ /** \brief Decrement the reference counter of the keys and values stored in the map, then reset the map. */ template void dec_ref_map_key_values(Mng1 & m1, Mng2 & m2, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m1.dec_ref(it->m_key); m2.dec_ref(it->m_value); } map.reset(); } /** \brief Decrement the reference counter of the keys and values stored in the map, then reset the map. */ template void dec_ref_map_key_values(Mng & m, Map & map) { dec_ref_map_key_values(m, m, map); } /** \brief Decrement the reference counter of the keys stored in the map, then reset the map. */ template void dec_ref_map_keys(Mng & m, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_key); } map.reset(); } /** \brief Decrement the reference counter of the values stored in the map, then reset the map. */ template void dec_ref_map_values(Mng & m, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_value); } map.reset(); } /** \brief Decrement the reference counter of the values stored in the map, then reset the map. */ template void dec_ref_collection_values(Mng & m, C & c) { typename C::iterator it = c.begin(); typename C::iterator end = c.end(); for (; it != end; ++it) { m.dec_ref(*it); } c.reset(); } #endif z3-z3-4.4.1/src/util/ref_vector.h000066400000000000000000000177701260446376700165140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ref_vector.h Abstract: Vector of smart pointers. Author: Leonardo de Moura (leonardo) 2008-01-04. Revision History: --*/ #ifndef REF_VECTOR_H_ #define REF_VECTOR_H_ #include"vector.h" #include"obj_ref.h" /** \brief Vector of smart pointers. Ref must provided the methods - void dec_ref(T * obj) - void inc_ref(T * obj) */ template class ref_vector_core : public Ref { protected: ptr_vector m_nodes; void inc_ref(T * o) { Ref::inc_ref(o); } void dec_ref(T * o) { Ref::dec_ref(o); } void dec_range_ref(T * const * begin, T * const * end) { for (T * const * it = begin; it < end; ++it) dec_ref(*it); } public: typedef T * data; ref_vector_core(Ref const & r = Ref()):Ref(r) {} ~ref_vector_core() { dec_range_ref(m_nodes.begin(), m_nodes.end()); } void reset() { dec_range_ref(m_nodes.begin(), m_nodes.end()); m_nodes.reset(); } void finalize() { dec_range_ref(m_nodes.begin(), m_nodes.end()); m_nodes.finalize(); } void resize(unsigned sz) { if (sz < m_nodes.size()) dec_range_ref(m_nodes.begin() + sz, m_nodes.end()); m_nodes.resize(sz, 0); } void resize(unsigned sz, T * d) { if (sz < m_nodes.size()) { dec_range_ref(m_nodes.begin() + sz, m_nodes.end()); m_nodes.shrink(sz); } else { for (unsigned i = m_nodes.size(); i < sz; i++) push_back(d); } } void reserve(unsigned sz) { if (sz <= m_nodes.size()) return; m_nodes.resize(sz, 0); } void shrink(unsigned sz) { SASSERT(sz <= m_nodes.size()); dec_range_ref(m_nodes.begin() + sz, m_nodes.end()); m_nodes.shrink(sz); } void push_back(T * n) { inc_ref(n); m_nodes.push_back(n); } void pop_back() { SASSERT(!m_nodes.empty()); T * n = m_nodes.back(); m_nodes.pop_back(); dec_ref(n); } T * back() const { return m_nodes.back(); } unsigned size() const { return m_nodes.size(); } bool empty() const { return m_nodes.empty(); } T * get(unsigned idx) const { return m_nodes[idx]; } T * get(unsigned idx, T * d) const { return m_nodes.get(idx, d); } T * const * c_ptr() const { return m_nodes.begin(); } typedef T* const* iterator; T ** c_ptr() { return m_nodes.begin(); } iterator begin() const { return m_nodes.begin(); } iterator end() const { return begin() + size(); } void set(unsigned idx, T * n) { inc_ref(n); dec_ref(m_nodes[idx]); m_nodes[idx] = n; } void erase(unsigned idx) { T * curr = m_nodes[idx]; m_nodes.erase(m_nodes.begin() + idx); dec_ref(curr); } void erase(T * elem) { unsigned sz = size(); for (unsigned idx = 0; idx < sz; idx++) { if (m_nodes[idx] == elem) { erase(idx); return; } } } bool contains(T * elem) const { unsigned sz = size(); for (unsigned idx = 0; idx < sz; idx++) if (m_nodes[idx] == elem) return true; return false; } T * operator[](unsigned idx) const { return m_nodes[idx]; } void append(ref_vector_core const & other) { for (unsigned i = 0; i < other.size(); ++i) push_back(other[i]); } void append(unsigned sz, T * const * data) { for(unsigned i = 0; i < sz; ++i) push_back(data[i]); } void swap(unsigned idx1, unsigned idx2) { std::swap(m_nodes[idx1], m_nodes[idx2]); } void reverse() { unsigned sz = size(); for (unsigned i = 0; i < sz/2; ++i) { std::swap(m_nodes[i], m_nodes[sz-i-1]); } } }; template class ref_manager_wrapper { protected: TManager & m_manager; public: ref_manager_wrapper(TManager & m):m_manager(m) {} void inc_ref(T * n) { m_manager.inc_ref(n); } void dec_ref(T * n) { m_manager.dec_ref(n); } }; /** \brief Vector of smart pointers. TManager must provide the functions: - void dec_ref(T * obj) - void inc_ref(T * obj) */ template class ref_vector : public ref_vector_core > { typedef ref_vector_core > super; public: ref_vector(TManager & m): super(ref_manager_wrapper(m)) { } ref_vector(ref_vector const & other): super(ref_manager_wrapper(other.m_manager)) { this->append(other); } ref_vector(TManager & m, unsigned sz, T * const * data): super(ref_manager_wrapper(m)) { this->append(sz, data); } TManager & get_manager() const { return this->m_manager; } TManager & m() const { return get_manager(); } void swap(ref_vector & other) { SASSERT(&(this->m_manager) == &(other.m_manager)); this->m_nodes.swap(other.m_nodes); } class element_ref { T * & m_ref; TManager & m_manager; public: element_ref(T * & ref, TManager & m): m_ref(ref), m_manager(m) { } element_ref & operator=(T * n) { SASSERT(n); m_manager.inc_ref(n); m_manager.dec_ref(m_ref); m_ref = n; return *this; } element_ref & operator=(element_ref& n) { *this = n.get(); return *this; } T * get() const { return m_ref; } T * operator->() const { return m_ref; } T const & operator*() const { SASSERT(m_ref); return *m_ref; } bool operator==(T * n) const { return m_ref == n; } }; T * operator[](unsigned idx) const { return super::operator[](idx); } element_ref operator[](unsigned idx) { return element_ref(this->m_nodes[idx], this->m_manager); } void set(unsigned idx, T * n) { super::set(idx, n); } // enable abuse: ref_vector & set(ref_vector const& other) { if (this != &other) { this->reset(); this->append(other); } return *this; } private: // prevent abuse: ref_vector & operator=(ref_vector const & other); }; template class ref_unmanaged_wrapper { public: static void inc_ref(T * n) { if (n) n->inc_ref(); } static void dec_ref(T * n) { if (n) n->dec_ref(); } }; /** \brief Vector of unmanaged references. */ template class sref_vector : public ref_vector_core > { }; /** \brief Hash utilities on ref_vector pointers. */ template struct ref_vector_ptr_hash { typedef ref_vector RefV; struct hash_proc { unsigned operator()(RefV* v, unsigned idx) const { return (*v)[idx]->get_id(); } }; unsigned operator()(RefV* v) const { if (!v) { return 0; } unsigned sz = v->size(); if (sz == 0) { return 0; } return get_composite_hash(v, sz, default_kind_hash_proc(), hash_proc()); } }; template struct ref_vector_ptr_eq { typedef ref_vector RefV; bool operator()(RefV* v1, RefV* v2) const { if (!v1 && !v2) { return true; } if ((!v1 && v2) || (v1 && !v2)) { return false; } if (v1->size() != v2->size()) { return false; } for (unsigned i = 0; i < v1->size(); ++i) { if ((*v1)[i].get() != (*v2)[i].get()) { return false; } } return true; } }; #endif /* REF_VECTOR_H_ */ z3-z3-4.4.1/src/util/region.cpp000066400000000000000000000052411260446376700161620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: region.cpp Abstract: Region/Arena memory manager Author: Leonardo de Moura (leonardo) 2006-09-18. Revision History: --*/ #include"region.h" #ifdef Z3DEBUG void region::display_mem_stats(std::ostream & out) const { out << "num. objects: " << m_chuncks.size() << "\n"; } #else #include"tptr.h" #include"debug.h" #include"memory_manager.h" #include"page.h" inline void region::allocate_page() { m_curr_page = allocate_default_page(m_curr_page, m_free_pages); m_curr_ptr = m_curr_page; m_curr_end_ptr = end_of_default_page(m_curr_page); } region::region() { m_curr_page = 0; m_curr_ptr = 0; m_curr_end_ptr = 0; m_free_pages = 0; m_mark = 0; allocate_page(); } region::~region() { del_pages(m_curr_page); del_pages(m_free_pages); } void * region::allocate(size_t size) { char * new_curr_ptr = m_curr_ptr + size; if (new_curr_ptr < m_curr_end_ptr) { char * result = m_curr_ptr; m_curr_ptr = ALIGN(char *, new_curr_ptr); return result; } else if (size < DEFAULT_PAGE_SIZE) { allocate_page(); char * result = m_curr_ptr; m_curr_ptr += size; m_curr_ptr = ALIGN(char *, m_curr_ptr); return result; } else { // big page m_curr_page = ::allocate_page(m_curr_page, size); char * result = m_curr_page; allocate_page(); return result; } } inline void region::recycle_curr_page() { char * prev = prev_page(m_curr_page); recycle_page(m_curr_page, m_free_pages); m_curr_page = prev; } void region::reset() { while (m_curr_page != 0) { recycle_curr_page(); } SASSERT(m_curr_page == 0); m_curr_ptr = 0; m_curr_end_ptr = 0; m_mark = 0; allocate_page(); } void region::push_scope() { char * curr_page = m_curr_page; char * curr_ptr = m_curr_ptr; m_mark = new (*this) mark(curr_page, curr_ptr, m_mark); } void region::pop_scope() { SASSERT(m_mark); char * old_curr_page = m_mark->m_curr_page; SASSERT(is_default_page(old_curr_page)); m_curr_ptr = m_mark->m_curr_ptr; m_mark = m_mark->m_prev_mark; while (m_curr_page != old_curr_page) { recycle_curr_page(); } SASSERT(is_default_page(m_curr_page)); m_curr_end_ptr = end_of_default_page(m_curr_page); } void region::display_mem_stats(std::ostream & out) const { unsigned n = 0; char * page = m_curr_page; while (page != 0) { n++; page = prev_page(page); } out << "num. pages: " << n << "\n"; } #endif z3-z3-4.4.1/src/util/region.h000066400000000000000000000051661260446376700156350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: region.h Abstract: Region/Arena memory manager Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #ifndef REGION_H_ #define REGION_H_ #include #include #ifdef Z3DEBUG #include"vector.h" class region { ptr_vector m_chuncks; unsigned_vector m_scopes; public: ~region() { reset(); } void * allocate(size_t size) { char * r = alloc_svect(char, size); m_chuncks.push_back(r); return r; } void reset() { ptr_vector::iterator it = m_chuncks.begin(); ptr_vector::iterator end = m_chuncks.end(); for (; it != end; ++it) { dealloc_svect(*it); } m_chuncks.reset(); m_scopes.reset(); } void push_scope() { m_scopes.push_back(m_chuncks.size()); } void pop_scope() { unsigned old_size = m_scopes.back(); m_scopes.pop_back(); ptr_vector::iterator it = m_chuncks.begin() + old_size; ptr_vector::iterator end = m_chuncks.end(); for (; it != end; ++it) { dealloc_svect(*it); } m_chuncks.shrink(old_size); } void pop_scope(unsigned num_scopes) { for (unsigned i = 0; i < num_scopes; i++) { pop_scope(); } } void display_mem_stats(std::ostream & out) const; }; #else /** \brief Implement explicit region memory manager. */ class region { struct mark { char * m_curr_page; char * m_curr_ptr; mark * m_prev_mark; mark(char * page, char * ptr, mark * m):m_curr_page(page), m_curr_ptr(ptr), m_prev_mark(m) {} }; char * m_curr_page; char * m_curr_ptr; //!< Next free space in the current page. char * m_curr_end_ptr; //!< Point to the end of the current page. char * m_free_pages; mark * m_mark; void allocate_page(); void recycle_curr_page(); public: region(); ~region(); void * allocate(size_t size); void reset(); void push_scope(); void pop_scope(); void pop_scope(unsigned num_scopes) { for (unsigned i = 0; i < num_scopes; i++) { pop_scope(); } } void display_mem_stats(std::ostream & out) const; }; #endif inline void * operator new(size_t s, region & r) { return r.allocate(s); } inline void * operator new[](size_t s, region & r) { return r.allocate(s); } inline void operator delete(void *, region & ) { /* do nothing */ } inline void operator delete[](void *, region & ) { /* do nothing */ } #endif /* REGION_H_ */ z3-z3-4.4.1/src/util/resource_limit.h000066400000000000000000000072771260446376700174040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: resource_limit.h Abstract: Author: Leonardo de Moura (leonardo) 2007-04-18. Revision History: --*/ #ifndef RESOURCE_LIMIT_H_ #define RESOURCE_LIMIT_H_ // // This object is used to limit the availability of "resources" during a search. // The main applications: // // - limiting the number of conflicts. // // - limiting the number of branching&bound steps in the ILP // // - limiting the number of quantifier instantiations. // // - etc. // // The idea is that when the resources are exhausted, the bounded_search terminates with // the undefined result. // // Then, the search can restart with bigger limits. class resource_limit { public: virtual ~resource_limit() { } virtual const char * get_description() const = 0; // - Reset counters associated with the limit. virtual void reset_counters() = 0; // - The limit was exhausted. virtual bool exhausted() const = 0; // - Return true if the limit is incremented. virtual bool inc_limit() = 0; virtual int get_limit() const = 0; virtual void reset() = 0; }; class base_resource_limit : public resource_limit { protected: int m_counter; int m_curr_limit; int m_init_limit; int m_max_limit; // <0 if unbounded const char * m_description; public: base_resource_limit(int init_limit, int max_limit, const char * desc): m_counter(0), m_curr_limit(init_limit), m_init_limit(init_limit), m_max_limit(max_limit), m_description(desc) { } virtual ~base_resource_limit() { } int get_counter() const { return m_counter; } bool inc_counter() { m_counter++; return m_counter <= m_curr_limit; } void set_init_limit(int l) { m_init_limit = l; m_curr_limit = l; } void set_max_limit(int l) { m_max_limit = l; } virtual const char * get_description() const { return m_description; } virtual void reset_counters() { m_counter = 0; } virtual bool exhausted() const { return m_counter >= m_curr_limit; } virtual int get_limit() const { return m_curr_limit; } virtual void reset() { m_counter = 0; m_curr_limit = m_init_limit; } }; class li_resource_limit : public base_resource_limit { int m_increment; public: li_resource_limit(int init_limit, int max_limit, int inc, const char * desc): base_resource_limit(init_limit, max_limit, desc), m_increment(inc) { } virtual ~li_resource_limit() { } virtual bool inc_limit() { int new_limit = m_curr_limit + m_increment; if (m_max_limit < 0 || new_limit <= m_max_limit) { m_curr_limit = new_limit; return true; } TRACE("resource_limit", tout << new_limit << " exhausts " << m_max_limit << "\n";); return false; } }; class ei_resource_limit : public base_resource_limit { double m_increment_ratio; public: ei_resource_limit(int init_limit, int max_limit, double inc, const char * desc): base_resource_limit(init_limit, max_limit, desc), m_increment_ratio(inc) { } virtual ~ei_resource_limit() { } virtual bool inc_limit() { int new_limit = static_cast(m_curr_limit * m_increment_ratio); if (m_max_limit < 0 || new_limit <= m_max_limit) { m_curr_limit = new_limit; return true; } TRACE("resource_limit", tout << new_limit << " exhausts " << m_max_limit << "\n";); return false; } }; #endif /* RESOURCE_LIMIT_H_ */ z3-z3-4.4.1/src/util/rlimit.cpp000066400000000000000000000015711260446376700162010ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: rlimit.cpp Abstract: Resource limit container. Author: Nikolaj Bjorner (nbjorner) 2015-09-28 Revision History: --*/ #include "rlimit.h" reslimit::reslimit(): m_count(0), m_limit(UINT_MAX) { } unsigned reslimit::count() const { return m_count; } bool reslimit::inc() { ++m_count; return m_count <= m_limit; } bool reslimit::inc(unsigned offset) { m_count += offset; return m_count <= m_limit; } void reslimit::push(unsigned delta_limit) { unsigned new_limit = delta_limit + m_count; if (new_limit <= m_count) { new_limit = UINT_MAX; } m_limits.push_back(m_limit); m_limit = std::min(new_limit, m_limit); } void reslimit::pop() { if (m_count > m_limit) { m_count = m_limit; } m_limit = m_limits.back(); m_limits.pop_back(); } z3-z3-4.4.1/src/util/rlimit.h000066400000000000000000000013001260446376700156340ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: rlimit.h Abstract: Resource limit container. Author: Nikolaj Bjorner (nbjorner) 2015-09-28 Revision History: --*/ #ifndef RLIMIT_H_ #define RLIMIT_H_ #include "vector.h" class reslimit { unsigned m_count; unsigned m_limit; unsigned_vector m_limits; public: reslimit(); bool inc(); bool inc(unsigned offset); void push(unsigned delta_limit); void pop(); unsigned count() const; }; class scoped_rlimit { reslimit& m_limit; public: scoped_rlimit(reslimit& r, unsigned l): m_limit(r) { r.push(l); } ~scoped_rlimit() { m_limit.pop(); } }; #endif z3-z3-4.4.1/src/util/s_integer.cpp000066400000000000000000000024201260446376700166520ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: s_integer.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-06-10. Revision History: --*/ #include"s_integer.h" s_integer s_integer::m_zero(0); s_integer s_integer::m_one(1); s_integer s_integer::m_minus_one(-1); s_integer::s_integer(const char * str) { m_val = static_cast(strtol(str, 0, 10)); } s_integer power(const s_integer & r, unsigned p) { unsigned mask = 1; s_integer result = s_integer(1); s_integer power = r; while (mask <= p) { if (mask & p) { result *= power; } power *= power; mask = mask << 1; } return result; } s_integer gcd(const s_integer & r1, const s_integer & r2) { SASSERT(r1.is_int() && r2.is_int()); s_integer tmp1(r1); s_integer tmp2(r2); if (tmp1.is_neg()) { tmp1.neg(); } if (tmp2.is_neg()) { tmp2.neg(); } if (tmp1 < tmp2) { tmp1.swap(tmp2); } for(;;) { s_integer aux = tmp1 % tmp2; if (aux.is_zero()) { return tmp2; } tmp1 = tmp2; tmp2 = aux; } } s_integer lcm(const s_integer & r1, const s_integer & r2) { s_integer g = gcd(r1, r2); return (r1 / g) * r2; } z3-z3-4.4.1/src/util/s_integer.h000066400000000000000000000137431260446376700163310ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: integer.h Abstract: machine s_integer wrapper Author: Leonardo de Moura (leonardo) 2007-06-01. Revision History: --*/ #ifndef S_INTEGER_H_ #define S_INTEGER_H_ #include"rational.h" class s_integer { int m_val; static s_integer m_zero; static s_integer m_one; static s_integer m_minus_one; public: unsigned hash() const { return m_val; } struct hash_proc { unsigned operator()(s_integer const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(s_integer const& r1, s_integer const& r2) const { return r1 == r2; } }; void swap(s_integer & n) { std::swap(m_val, n.m_val); } std::string to_string() const; public: s_integer(): m_val(0) {} s_integer(const s_integer & r):m_val(r.m_val) {} explicit s_integer(int n):m_val(n) {} struct i64 {}; explicit s_integer(int64 i, i64):m_val(static_cast(i)) {} struct ui64 {}; explicit s_integer(uint64 i, ui64):m_val(static_cast(i)) {} explicit s_integer(const char * str); explicit s_integer(const rational & r):m_val(static_cast(r.get_int64())) {} void reset() { m_val = 0; } static bool is_big() { return false; } static bool is_int() { return true; } static bool is_s_integer() { return true; } static bool is_int64() { return true; } static bool is_uint64() { return true; } int get_int() const { return m_val; } int64 get_int64() const { return m_val; } uint64 get_uint64() const { return m_val; } static bool is_unsigned() { return true; } unsigned get_unsigned() const { return static_cast(m_val); } s_integer const& get_s_integer() const { return *this; } s_integer const& get_infinitesimal() const { return zero(); } static bool is_rational() { return true; } s_integer const& get_rational() const { return *this; } s_integer & operator=(const s_integer & r) { m_val = r.m_val; return *this; } friend inline s_integer numerator(const s_integer & r) { return r; } friend inline s_integer denominator(const s_integer & r) { return one(); } s_integer & operator+=(const s_integer & r) { m_val += r.m_val; return *this; } s_integer & operator-=(const s_integer & r) { m_val -= r.m_val; return *this; } s_integer & operator*=(const s_integer & r) { m_val *= r.m_val; return *this; } s_integer & operator/=(const s_integer & r) { m_val /= r.m_val; return *this; } s_integer & operator%=(const s_integer & r) { m_val %= r.m_val; return *this; } friend inline s_integer div(const s_integer & r1, const s_integer & r2) { return s_integer(r1.m_val / r2.m_val); } friend inline s_integer mod(const s_integer & r1, const s_integer & r2) { s_integer r = r1; r %= r2; if (r.is_neg()) { r += r2; } return r; } s_integer & operator++() { m_val++; return *this; } const s_integer operator++(int) { s_integer tmp(*this); ++(*this); return tmp; } s_integer & operator--() { m_val--; return *this; } const s_integer operator--(int) { s_integer tmp(*this); --(*this); return tmp; } friend inline bool operator==(const s_integer & r1, const s_integer & r2) { return r1.m_val == r2.m_val; } friend inline bool operator<(const s_integer & r1, const s_integer & r2) { return r1.m_val < r2.m_val; } void neg() { m_val = -m_val; } bool is_zero() const { return m_val == 0; } bool is_one() const { return m_val == 1; } bool is_minus_one() const { return m_val == -1; } bool is_neg() const { return m_val < 0; } bool is_pos() const { return m_val > 0; } bool is_nonneg() const {return m_val >= 0; } bool is_nonpos() const { return m_val <= 0; } bool is_even() const { return (!(m_val & 0x1)); } friend inline s_integer floor(const s_integer & r) { return r; } friend inline s_integer ceil(const s_integer & r) { return r; } s_integer expt(int n) const { s_integer result(1); for (int i = 0; i < n; i++) { result *= *this; } return result; } static const s_integer & zero() { return m_zero; } static const s_integer & one() { return m_one; } static const s_integer & minus_one() { return m_minus_one; } // Perform: this += c * k void addmul(const s_integer & c, const s_integer & k) { m_val += c.m_val * k.m_val; } void submul(const s_integer & c, const s_integer & k) { m_val -= c.m_val * k.m_val; } friend inline std::ostream & operator<<(std::ostream & target, const s_integer & r) { target << r.m_val; return target; } rational to_rational() const { return rational(m_val); } }; inline bool operator!=(const s_integer & r1, const s_integer & r2) { return !operator==(r1, r2); } inline bool operator>(const s_integer & r1, const s_integer & r2) { return operator<(r2, r1); } inline bool operator<=(const s_integer & r1, const s_integer & r2) { return !operator>(r1, r2); } inline bool operator>=(const s_integer & r1, const s_integer & r2) { return !operator<(r1, r2); } inline s_integer operator+(const s_integer & r1, const s_integer & r2) { return s_integer(r1) += r2; } inline s_integer operator-(const s_integer & r1, const s_integer & r2) { return s_integer(r1) -= r2; } inline s_integer operator-(const s_integer & r) { s_integer result(r); result.neg(); return result; } inline s_integer operator*(const s_integer & r1, const s_integer & r2) { return s_integer(r1) *= r2; } inline s_integer operator/(const s_integer & r1, const s_integer & r2) { return s_integer(r1) /= r2; } inline s_integer operator%(const s_integer & r1, const s_integer & r2) { return s_integer(r1) %= r2; } s_integer power(const s_integer & r, unsigned p); s_integer gcd(const s_integer & r1, const s_integer & r2); s_integer lcm(const s_integer & r1, const s_integer & r2); inline s_integer abs(const s_integer & r) { s_integer result(r); if (result.is_neg()) { result.neg(); } return result; } #endif /* S_INTEGER_H_ */ z3-z3-4.4.1/src/util/scoped_ctrl_c.cpp000066400000000000000000000021531260446376700175010ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_ctrl_c.cpp Abstract: Scoped control-c handler. Author: Leonardo de Moura (leonardo) 2011-04-27. Revision History: --*/ #include #include #include"scoped_ctrl_c.h" scoped_ctrl_c * scoped_ctrl_c::g_obj = 0; void scoped_ctrl_c::on_ctrl_c(int) { if (g_obj->m_first) { g_obj->m_cancel_eh(); if (g_obj->m_once) { g_obj->m_first = false; signal(SIGINT, on_ctrl_c); // re-install the handler } } else { signal(SIGINT, g_obj->m_old_handler); raise(SIGINT); } } scoped_ctrl_c::scoped_ctrl_c(event_handler & eh, bool once, bool enabled): m_cancel_eh(eh), m_first(true), m_once(once), m_enabled(enabled), m_old_scoped_ctrl_c(g_obj) { if (m_enabled) { g_obj = this; m_old_handler = signal(SIGINT, on_ctrl_c); } } scoped_ctrl_c::~scoped_ctrl_c() { if (m_enabled) { g_obj = m_old_scoped_ctrl_c; if (m_old_handler != SIG_ERR) { signal(SIGINT, m_old_handler); } } } z3-z3-4.4.1/src/util/scoped_ctrl_c.h000066400000000000000000000015601260446376700171470ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_ctrl_c.h Abstract: Scoped control-c handler. Author: Leonardo de Moura (leonardo) 2011-04-27. Revision History: --*/ #ifndef SCOPED_CTRL_C_H_ #define SCOPED_CTRL_C_H_ #include"event_handler.h" struct scoped_ctrl_c { event_handler & m_cancel_eh; bool m_first; bool m_once; bool m_enabled; void (*m_old_handler)(int); scoped_ctrl_c * m_old_scoped_ctrl_c; static scoped_ctrl_c * g_obj; static void on_ctrl_c(int); public: // If once == true, then the cancel_eh is invoked only at the first Ctrl-C. // The next time, the old signal handler will take over. // if enabled == false, then scoped_ctrl_c is a noop scoped_ctrl_c(event_handler & eh, bool once=true, bool enabled=true); ~scoped_ctrl_c(); void reset() { m_first = true; } }; #endif z3-z3-4.4.1/src/util/scoped_numeral.h000066400000000000000000000106531260446376700173470ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_numeral.h Abstract: Wrapper for easying the pain when using primitive numeral objects such as: mpz, mpq, mpbq, mpf, mpzp Author: Leonardo de Moura (leonardo) 2011-12-03 Revision History: --*/ #ifndef SCOPED_NUMERAL_H_ #define SCOPED_NUMERAL_H_ template class _scoped_numeral { public: typedef typename Manager::numeral numeral; private: Manager & m_manager; numeral m_num; public: _scoped_numeral(Manager & m):m_manager(m) {} _scoped_numeral(_scoped_numeral const & n):m_manager(n.m_manager) { m().set(m_num, n.m_num); } ~_scoped_numeral() { m_manager.del(m_num); } Manager & m() const { return m_manager; } operator numeral const &() const { return m_num; } operator numeral&() { return m_num; } numeral const & get() const { return m_num; } numeral & get() { return m_num; } _scoped_numeral & operator=(_scoped_numeral const & n) { if (this == &n) return *this; m().set(m_num, n.m_num); return *this; } _scoped_numeral & operator=(int n) { m().set(m_num, n); return *this; } _scoped_numeral & operator=(numeral const & n) { m().set(m_num, n); return *this; } void reset() { m().reset(m_num); } void swap(_scoped_numeral & n) { m_num.swap(n.m_num); } void swap(numeral & n) { m_num.swap(n); } _scoped_numeral & operator+=(numeral const & a) { m().add(m_num, a, m_num); return *this; } _scoped_numeral & operator-=(numeral const & a) { m().sub(m_num, a, m_num); return *this; } _scoped_numeral & operator*=(numeral const & a) { m().mul(m_num, a, m_num); return *this; } _scoped_numeral & operator/=(numeral const & a) { m().div(m_num, a, m_num); return *this; } _scoped_numeral & operator%=(numeral const & a) { m().rem(m_num, a, m_num); return *this; } friend bool operator==(_scoped_numeral const & a, numeral const & b) { return a.m().eq(a, b); } friend bool operator!=(_scoped_numeral const & a, numeral const & b) { return !a.m().eq(a, b); } friend bool operator<(_scoped_numeral const & a, numeral const & b) { return a.m().lt(a, b); } friend bool operator>(_scoped_numeral const & a, numeral const & b) { return a.m().gt(a, b); } friend bool operator<=(_scoped_numeral const & a, numeral const & b) { return a.m().le(a, b); } friend bool operator>=(_scoped_numeral const & a, numeral const & b) { return a.m().ge(a, b); } bool is_zero() const { return m().is_zero(*this); } bool is_pos() const { return m().is_pos(*this); } bool is_neg() const { return m().is_neg(*this); } bool is_nonpos() const { return m().is_nonpos(*this); } bool is_nonneg() const { return m().is_nonneg(*this); } friend bool is_zero(_scoped_numeral const & a) { return a.m().is_zero(a); } friend bool is_pos(_scoped_numeral const & a) { return a.m().is_pos(a); } friend bool is_neg(_scoped_numeral const & a) { return a.m().is_neg(a); } friend bool is_nonneg(_scoped_numeral const & a) { return a.m().is_nonneg(a); } friend bool is_nonpos(_scoped_numeral const & a) { return a.m().is_nonpos(a); } friend _scoped_numeral abs(_scoped_numeral const& a) { _scoped_numeral res(a); a.m().abs(res); return res; } void neg() { m().neg(m_num); } friend _scoped_numeral operator+(_scoped_numeral const & r1, numeral const & r2) { return _scoped_numeral(r1) += r2; } friend _scoped_numeral operator-(_scoped_numeral const & r1, numeral const & r2) { return _scoped_numeral(r1) -= r2; } friend _scoped_numeral operator*(_scoped_numeral const & r1, numeral const & r2) { return _scoped_numeral(r1) *= r2; } friend _scoped_numeral operator/(_scoped_numeral const & r1, numeral const & r2) { return _scoped_numeral(r1) /= r2; } friend std::ostream & operator<<(std::ostream & out, _scoped_numeral const & s) { s.m().display(out, s); return out; } }; #endif z3-z3-4.4.1/src/util/scoped_numeral_buffer.h000066400000000000000000000030721260446376700206750ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: scoped_numeral_buffer.h Abstract: Wrapper for easying the pain when using buffers of numerals. Author: Leonardo de Moura (leonardo) 2012-01-23 Revision History: --*/ #ifndef SCOPED_NUMERAL_BUFFER_H_ #define SCOPED_NUMERAL_BUFFER_H_ #include"buffer.h" template class _scoped_numeral_buffer : public sbuffer { typedef sbuffer super; Manager & m_manager; _scoped_numeral_buffer(_scoped_numeral_buffer const & v); public: _scoped_numeral_buffer(Manager & m):m_manager(m) {} ~_scoped_numeral_buffer() { reset(); } void reset() { unsigned sz = this->size(); for (unsigned i = 0; i < sz; i++) { m().del(this->operator[](i)); } super::reset(); } Manager & m() const { return m_manager; } void push_back(typename Manager::numeral const & v) { super::push_back(typename Manager::numeral()); m_manager.set(this->back(), v); } void shrink(unsigned sz) { unsigned old_sz = this->size(); if (old_sz == sz) return; for (unsigned i = sz; i < old_sz; i++) m().del(this->operator[](i)); super::shrink(sz); } void resize(unsigned sz) { unsigned old_sz = this->size(); if (sz <= old_sz) shrink(sz); typename Manager::numeral zero(0); super::resize(sz, zero); } }; #endif z3-z3-4.4.1/src/util/scoped_numeral_vector.h000066400000000000000000000031751260446376700207320ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_numeral_vector.h Abstract: Wrapper for easying the pain when using vectors of numerals. Author: Leonardo de Moura (leonardo) 2011-12-03 Revision History: --*/ #ifndef SCOPED_NUMERAL_VECTOR_H_ #define SCOPED_NUMERAL_VECTOR_H_ #include"vector.h" template class _scoped_numeral_vector : public svector { Manager & m_manager; _scoped_numeral_vector(_scoped_numeral_vector const & v); public: _scoped_numeral_vector(Manager & m):m_manager(m) {} ~_scoped_numeral_vector() { reset(); } void reset() { unsigned sz = this->size(); for (unsigned i = 0; i < sz; i++) { m().del(this->operator[](i)); } svector::reset(); } Manager & m() const { return m_manager; } void push_back(typename Manager::numeral const & v) { svector::push_back(typename Manager::numeral()); m_manager.set(this->back(), v); } void pop_back() { shrink(this->size()-1); } void shrink(unsigned sz) { unsigned old_sz = this->size(); if (old_sz == sz) return; for (unsigned i = sz; i < old_sz; i++) m().del(this->operator[](i)); svector::shrink(sz); } void resize(unsigned sz) { unsigned old_sz = this->size(); if (sz <= old_sz) shrink(sz); typename Manager::numeral zero(0); svector::resize(sz, zero); } }; #endif z3-z3-4.4.1/src/util/scoped_ptr_vector.h000066400000000000000000000023621260446376700200710ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_ptr_vector.h Abstract: Basic template of scoped ptrs. TODO: improve Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #ifndef SCOPED_PTR_VECTOR_H_ #define SCOPED_PTR_VECTOR_H_ #include"vector.h" #include"util.h" template class scoped_ptr_vector { ptr_vector m_vector; public: ~scoped_ptr_vector() { reset(); } void reset() { std::for_each(m_vector.begin(), m_vector.end(), delete_proc()); m_vector.reset(); } void push_back(T * ptr) { m_vector.push_back(ptr); } T * operator[](unsigned idx) const { return m_vector[idx]; } void set(unsigned idx, T * ptr) { if (m_vector[idx] == ptr) return; dealloc(m_vector[idx]); m_vector[idx] = ptr; } unsigned size() const { return m_vector.size(); } bool empty() const { return m_vector.empty(); } void resize(unsigned sz) { if (sz < m_vector.size()) { for (unsigned i = m_vector.size(); i < sz; i++) dealloc(m_vector[i]); m_vector.shrink(sz); } else { for (unsigned i = m_vector.size(); i < sz; i++) push_back(0); } } }; #endif z3-z3-4.4.1/src/util/scoped_timer.cpp000066400000000000000000000150001260446376700173460ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_timer.cpp Abstract: Author: Leonardo de Moura (leonardo) 2011-04-26. Revision History: --*/ #ifdef _CYGWIN // Hack to make CreateTimerQueueTimer available on cygwin #define _WIN32_WINNT 0x0600 #endif #include"z3_exception.h" #include"z3_omp.h" #if defined(_WINDOWS) || defined(_CYGWIN) // Windows #include #elif defined(__APPLE__) && defined(__MACH__) // Mac OS X #include #include #include #include #include #elif defined(_LINUX_) || defined(_FREEBSD_) // Linux #include #include #include #include"warning.h" #ifdef _LINUX_ #define CLOCKID CLOCK_PROCESS_CPUTIME_ID #else // FreeBSD does not support CLOCK_PROCESS_CPUTIME_ID #define CLOCKID CLOCK_MONOTONIC #endif #define SIG SIGRTMIN // --------- #else // Other platforms #endif #include"scoped_timer.h" #ifdef _CYGWIN #undef min #undef max #endif #include"util.h" #include #include"z3_omp.h" struct scoped_timer::imp { event_handler * m_eh; #if defined(_WINDOWS) || defined(_CYGWIN) HANDLE m_timer; bool m_first; #elif defined(__APPLE__) && defined(__MACH__) // Mac OS X pthread_t m_thread_id; pthread_attr_t m_attributes; unsigned m_interval; pthread_mutex_t m_mutex; pthread_cond_t m_condition_var; struct timespec m_end_time; #elif defined(_LINUX_) || defined(_FREEBSD_) // Linux & FreeBSD timer_t m_timerid; #else // Other #endif #if defined(_WINDOWS) || defined(_CYGWIN) static void CALLBACK abort_proc(PVOID param, BOOLEAN timer_or_wait_fired) { imp * obj = static_cast(param); if (obj->m_first) { obj->m_first = false; } else { obj->m_eh->operator()(); } } #elif defined(__APPLE__) && defined(__MACH__) // Mac OS X static void * thread_func(void * arg) { scoped_timer::imp * st = static_cast(arg); pthread_mutex_lock(&st->m_mutex); int e = pthread_cond_timedwait(&st->m_condition_var, &st->m_mutex, &st->m_end_time); if (e != 0 && e != ETIMEDOUT) throw default_exception("failed to start timed wait"); st->m_eh->operator()(); pthread_mutex_unlock(&st->m_mutex); return st; } #elif defined(_LINUX_) || defined(_FREEBSD_) static void sig_handler(union sigval s) { void * ptr = s.sival_ptr; static_cast(ptr)->m_eh->operator()(); } #else // Other #endif imp(unsigned ms, event_handler * eh): m_eh(eh) { #if defined(_WINDOWS) || defined(_CYGWIN) m_first = true; CreateTimerQueueTimer(&m_timer, NULL, abort_proc, this, 0, ms, WT_EXECUTEINTIMERTHREAD); #elif defined(__APPLE__) && defined(__MACH__) // Mac OS X m_interval = ms?ms:0xFFFFFFFF; if (pthread_attr_init(&m_attributes) != 0) throw default_exception("failed to initialize timer thread attributes"); if (pthread_cond_init(&m_condition_var, NULL) != 0) throw default_exception("failed to initialize timer condition variable"); if (pthread_mutex_init(&m_mutex, NULL) != 0) throw default_exception("failed to initialize timer mutex"); clock_serv_t host_clock; mach_timespec_t now; unsigned long long nano = static_cast(m_interval) * 1000000ull; host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &host_clock); m_end_time.tv_sec = nano / 1000000000ull; m_end_time.tv_nsec = nano % 1000000000ull; clock_get_time(host_clock, &now); ADD_MACH_TIMESPEC(&m_end_time, &now); if (pthread_create(&m_thread_id, &m_attributes, &thread_func, this) != 0) throw default_exception("failed to start timer thread"); #elif defined(_LINUX_) || defined(_FREEBSD_) // Linux & FreeBSD struct sigevent sev; memset(&sev, 0, sizeof(sigevent)); sev.sigev_notify = SIGEV_THREAD; sev.sigev_value.sival_ptr = this; sev.sigev_notify_function = sig_handler; if (timer_create(CLOCKID, &sev, &m_timerid) == -1) throw default_exception("failed to create timer"); unsigned long long nano = static_cast(ms) * 1000000ull; struct itimerspec its; its.it_value.tv_sec = nano / 1000000000ull; its.it_value.tv_nsec = nano % 1000000000ull; its.it_interval.tv_sec = 0; // timer experies once its.it_interval.tv_nsec = 0; if (timer_settime(m_timerid, 0, &its, NULL) == -1) throw default_exception("failed to set timer"); #else // Other platforms #endif } ~imp() { #if defined(_WINDOWS) || defined(_CYGWIN) DeleteTimerQueueTimer(NULL, m_timer, INVALID_HANDLE_VALUE); #elif defined(__APPLE__) && defined(__MACH__) // Mac OS X // If the waiting-thread is not up and waiting yet, // we can make sure that it finishes quickly by // setting the end-time to zero. m_end_time.tv_sec = 0; m_end_time.tv_nsec = 0; // Otherwise it's already up and waiting, and // we can send a signal on m_condition_var: pthread_mutex_lock(&m_mutex); pthread_cond_signal(&m_condition_var); pthread_mutex_unlock(&m_mutex); if (pthread_join(m_thread_id, NULL) != 0) throw default_exception("failed to join thread"); if (pthread_mutex_destroy(&m_mutex) != 0) throw default_exception("failed to destroy pthread mutex"); if (pthread_cond_destroy(&m_condition_var) != 0) throw default_exception("failed to destroy pthread condition variable"); if (pthread_attr_destroy(&m_attributes) != 0) throw default_exception("failed to destroy pthread attributes object"); #elif defined(_LINUX_) || defined(_FREEBSD_) // Linux & FreeBSD timer_delete(m_timerid); #else // Other Platforms #endif } }; scoped_timer::scoped_timer(unsigned ms, event_handler * eh) { if (ms != UINT_MAX) m_imp = alloc(imp, ms, eh); else m_imp = 0; } scoped_timer::~scoped_timer() { if (m_imp) dealloc(m_imp); } z3-z3-4.4.1/src/util/scoped_timer.h000066400000000000000000000006311260446376700170170ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_timer.h Abstract: Author: Leonardo de Moura (leonardo) 2011-04-26. Revision History: --*/ #ifndef SCOPED_TIMER_H_ #define SCOPED_TIMER_H_ #include"event_handler.h" class scoped_timer { struct imp; imp * m_imp; public: scoped_timer(unsigned ms, event_handler * eh); ~scoped_timer(); }; #endif z3-z3-4.4.1/src/util/sexpr.cpp000066400000000000000000000213311260446376700160360ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sexpr.h Abstract: Generic sexpr Author: Leonardo (leonardo) 2011-07-28 Notes: --*/ #include"sexpr.h" #include"vector.h" #include"buffer.h" #ifdef _MSC_VER #pragma warning(disable : 4200) #pragma warning(disable : 4355) #endif struct sexpr_composite : public sexpr { unsigned m_num_chilren; sexpr * m_children[0]; sexpr_composite(unsigned num_children, sexpr * const * children, unsigned line, unsigned pos): sexpr(COMPOSITE, line, pos), m_num_chilren(num_children) { for (unsigned i = 0; i < num_children; i++) { m_children[i] = children[i]; children[i]->inc_ref(); } } }; struct sexpr_numeral : public sexpr { rational m_val; sexpr_numeral(kind_t k, rational const & val, unsigned line, unsigned pos): sexpr(k, line, pos), m_val(val) { } sexpr_numeral(rational const & val, unsigned line, unsigned pos): sexpr(NUMERAL, line, pos), m_val(val) { } }; struct sexpr_bv : public sexpr_numeral { unsigned m_size; sexpr_bv(rational const & val, unsigned size, unsigned line, unsigned pos): sexpr_numeral(BV_NUMERAL, val, line, pos), m_size(size) { } }; struct sexpr_string : public sexpr { std::string m_val; sexpr_string(std::string const & val, unsigned line, unsigned pos): sexpr(STRING, line, pos), m_val(val) { } sexpr_string(char const * val, unsigned line, unsigned pos): sexpr(STRING, line, pos), m_val(val) { } }; struct sexpr_symbol : public sexpr { symbol m_val; sexpr_symbol(bool keyword, symbol const & val, unsigned line, unsigned pos): sexpr(keyword ? KEYWORD : SYMBOL, line, pos), m_val(val) { } }; sexpr::sexpr(kind_t k, unsigned line, unsigned pos): m_kind(k), m_ref_count(0), m_line(line), m_pos(pos) { } rational const & sexpr::get_numeral() const { SASSERT(is_numeral() || is_bv_numeral()); return static_cast(this)->m_val; } unsigned sexpr::get_bv_size() const { SASSERT(is_bv_numeral()); return static_cast(this)->m_size; } symbol sexpr::get_symbol() const { SASSERT(is_symbol() || is_keyword()); return static_cast(this)->m_val; } std::string const & sexpr::get_string() const { SASSERT(is_string()); return static_cast(this)->m_val; } unsigned sexpr::get_num_children() const { SASSERT(is_composite()); return static_cast(this)->m_num_chilren; } sexpr * sexpr::get_child(unsigned idx) const { SASSERT(idx < get_num_children()); return static_cast(this)->m_children[idx]; } sexpr * const * sexpr::get_children() const { SASSERT(is_composite()); return static_cast(this)->m_children; } void sexpr::display_atom(std::ostream & out) const { switch (get_kind()) { case sexpr::COMPOSITE: UNREACHABLE(); case sexpr::NUMERAL: out << static_cast(this)->m_val; break; case sexpr::BV_NUMERAL: { out << '#'; unsigned bv_size = static_cast(this)->m_size; rational val = static_cast(this)->m_val; sbuffer buf; unsigned sz = 0; if (bv_size % 4 == 0) { out << '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 { out << '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; } } std::reverse(buf.begin(), buf.end()); buf.push_back(0); out << buf.c_ptr(); break; } case sexpr::STRING: out << "\"" << escaped(static_cast(this)->m_val.c_str()) << "\""; break; case sexpr::SYMBOL: case sexpr::KEYWORD: out << static_cast(this)->m_val; break; default: UNREACHABLE(); } } void sexpr::display(std::ostream & out) const { if (!is_composite()) display_atom(out); vector > todo; todo.push_back(std::make_pair(static_cast(this), 0)); while (!todo.empty()) { loop: sexpr_composite const * n = todo.back().first; unsigned & idx = todo.back().second; unsigned num = n->get_num_children(); while (idx < num) { sexpr const * child = n->get_child(idx); if (idx == 0) out << "("; else out << " "; idx++; if (child->is_composite()) { todo.push_back(std::make_pair(static_cast(child), 0)); goto loop; } else { child->display_atom(out); } } out << ")"; todo.pop_back(); } } void sexpr_manager::del(sexpr * n) { m_to_delete.push_back(n); while (!m_to_delete.empty()) { sexpr * n = m_to_delete.back(); m_to_delete.pop_back(); switch (n->get_kind()) { case sexpr::COMPOSITE: { unsigned num = n->get_num_children(); for (unsigned i = 0; i < num; i++) { sexpr * child = n->get_child(i); SASSERT(child->m_ref_count > 0); child->m_ref_count--; if (child->m_ref_count == 0) m_to_delete.push_back(child); } static_cast(n)->~sexpr_composite(); m_allocator.deallocate(sizeof(sexpr_composite) + num * sizeof(sexpr*), n); break; } case sexpr::NUMERAL: static_cast(n)->~sexpr_numeral(); m_allocator.deallocate(sizeof(sexpr_numeral), n); break; case sexpr::BV_NUMERAL: static_cast(n)->~sexpr_bv(); m_allocator.deallocate(sizeof(sexpr_bv), n); break; case sexpr::STRING: static_cast(n)->~sexpr_string(); m_allocator.deallocate(sizeof(sexpr_string), n); break; case sexpr::SYMBOL: case sexpr::KEYWORD: static_cast(n)->~sexpr_symbol(); m_allocator.deallocate(sizeof(sexpr_symbol), n); break; default: UNREACHABLE(); } } } sexpr_manager::sexpr_manager(): m_allocator("sexpr-manager") { } sexpr * sexpr_manager::mk_composite(unsigned num_children, sexpr * const * children, unsigned line, unsigned pos) { void * mem = m_allocator.allocate(sizeof(sexpr_composite) + num_children * sizeof(sexpr*)); return new (mem) sexpr_composite(num_children, children, line, pos); } sexpr * sexpr_manager::mk_numeral(rational const & val, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_numeral))) sexpr_numeral(val, line, pos); } sexpr * sexpr_manager::mk_bv_numeral(rational const & val, unsigned bv_size, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_bv))) sexpr_bv(val, bv_size, line, pos); } sexpr * sexpr_manager::mk_string(std::string const & val, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_string))) sexpr_string(val, line, pos); } sexpr * sexpr_manager::mk_string(char const * val, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_string))) sexpr_string(val, line, pos); } sexpr * sexpr_manager::mk_keyword(symbol const & val, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_symbol))) sexpr_symbol(true, val, line, pos); } sexpr * sexpr_manager::mk_symbol(symbol const & val, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_symbol))) sexpr_symbol(false, val, line, pos); } z3-z3-4.4.1/src/util/sexpr.h000066400000000000000000000053001260446376700155010ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sexpr.h Abstract: Generic sexpr Author: Leonardo (leonardo) 2011-07-28 Notes: --*/ #ifndef SEXPR_H_ #define SEXPR_H_ #include"rational.h" #include"symbol.h" #include"obj_ref.h" #include"ref_vector.h" class sexpr_manager; class sexpr { public: enum kind_t { COMPOSITE, NUMERAL, BV_NUMERAL, STRING, KEYWORD, SYMBOL }; protected: kind_t m_kind; unsigned m_ref_count; unsigned m_line; unsigned m_pos; sexpr(kind_t k, unsigned line, unsigned pos); void display_atom(std::ostream & out) const; friend class sexpr_manager; public: void inc_ref() { m_ref_count++; } unsigned get_ref_count() const { return m_ref_count; } unsigned get_line() const { return m_line; } unsigned get_pos() const { return m_pos; } kind_t get_kind() const { return m_kind; } bool is_composite() const { return get_kind() == COMPOSITE; } bool is_numeral() const { return get_kind() == NUMERAL; } bool is_bv_numeral() const { return get_kind() == BV_NUMERAL; } bool is_string() const { return get_kind() == STRING; } bool is_keyword() const { return get_kind() == KEYWORD; } bool is_symbol() const { return get_kind() == SYMBOL; } rational const & get_numeral() const; unsigned get_bv_size() const; symbol get_symbol() const; std::string const & get_string() const; unsigned get_num_children() const; sexpr * get_child(unsigned idx) const; sexpr * const * get_children() const; void display(std::ostream & out) const; }; class sexpr_manager { small_object_allocator m_allocator; ptr_vector m_to_delete; void del(sexpr * n); public: sexpr_manager(); sexpr * mk_composite(unsigned num_children, sexpr * const * children, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_numeral(rational const & val, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_bv_numeral(rational const & val, unsigned bv_size, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_string(std::string const & val, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_string(char const * val, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_keyword(symbol const & val, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_symbol(symbol const & val, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); void inc_ref(sexpr * n) { n->inc_ref(); } void dec_ref(sexpr * n) { SASSERT(n->m_ref_count > 0); n->m_ref_count--; if (n->m_ref_count == 0) del(n); } }; typedef obj_ref sexpr_ref; typedef ref_vector sexpr_ref_vector; #endif z3-z3-4.4.1/src/util/small_object_allocator.cpp000066400000000000000000000161701260446376700214000ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: small_object_allocator.cpp Abstract: Small object allocator. Author: Nikolaj bjorner (nbjorner) 2007-08-06. Revision History: Leonardo de Moura (leonardo) 2011-04-27 Rewrote/Simplified the allocator --*/ #include"memory_manager.h" #include"small_object_allocator.h" #include"debug.h" #include"util.h" #include"vector.h" #include small_object_allocator::small_object_allocator(char const * id) { for (unsigned i = 0; i < NUM_SLOTS; i++) { m_chunks[i] = 0; m_free_list[i] = 0; } DEBUG_CODE({ m_id = id; }); m_alloc_size = 0; } small_object_allocator::~small_object_allocator() { for (unsigned i = 0; i < NUM_SLOTS; i++) { chunk * c = m_chunks[i]; while (c) { chunk * next = c->m_next; dealloc(c); c = next; } } DEBUG_CODE({ if (m_alloc_size > 0) { std::cerr << "Memory leak detected for small object allocator '" << m_id << "'. " << m_alloc_size << " bytes leaked" << std::endl; } }); } void small_object_allocator::reset() { for (unsigned i = 0; i < NUM_SLOTS; i++) { chunk * c = m_chunks[i]; while (c) { chunk * next = c->m_next; dealloc(c); c = next; } m_chunks[i] = 0; m_free_list[i] = 0; } m_alloc_size = 0; } #define MASK ((1 << PTR_ALIGNMENT) - 1) void small_object_allocator::deallocate(size_t size, void * p) { #if defined(Z3DEBUG) && !defined(_WINDOWS) // Valgrind friendly memory::deallocate(p); return; #endif SASSERT(m_alloc_size >= size); SASSERT(p); m_alloc_size -= size; if (size > SMALL_OBJ_SIZE - (1 << PTR_ALIGNMENT)) { memory::deallocate(p); return; } unsigned slot_id = static_cast(size >> PTR_ALIGNMENT); if ((size & MASK) != 0) slot_id++; SASSERT(slot_id > 0); SASSERT(slot_id < NUM_SLOTS); *(reinterpret_cast(p)) = m_free_list[slot_id]; m_free_list[slot_id] = p; } void * small_object_allocator::allocate(size_t size) { #if defined(Z3DEBUG) && !defined(_WINDOWS) // Valgrind friendly return memory::allocate(size); #endif m_alloc_size += size; if (size > SMALL_OBJ_SIZE - (1 << PTR_ALIGNMENT)) return memory::allocate(size); #ifdef Z3DEBUG size_t osize = size; #endif unsigned slot_id = static_cast(size >> PTR_ALIGNMENT); if ((size & MASK) != 0) slot_id++; SASSERT(slot_id < NUM_SLOTS); SASSERT(slot_id > 0); if (m_free_list[slot_id] != 0) { void * r = m_free_list[slot_id]; m_free_list[slot_id] = *(reinterpret_cast(r)); return r; } chunk * c = m_chunks[slot_id]; size = slot_id << PTR_ALIGNMENT; SASSERT(size >= osize); if (c != 0) { char * new_curr = c->m_curr + size; if (new_curr < c->m_data + CHUNK_SIZE) { void * r = c->m_curr; c->m_curr = new_curr; return r; } } chunk * new_c = alloc(chunk); new_c->m_next = c; m_chunks[slot_id] = new_c; void * r = new_c->m_curr; new_c->m_curr += size; return r; } size_t small_object_allocator::get_wasted_size() const { size_t r = 0; for (unsigned slot_id = 0; slot_id < NUM_SLOTS; slot_id++) { size_t slot_obj_size = slot_id << PTR_ALIGNMENT; void ** ptr = reinterpret_cast(const_cast(this)->m_free_list[slot_id]); while (ptr != 0) { r += slot_obj_size; ptr = reinterpret_cast(*ptr); } } return r; } size_t small_object_allocator::get_num_free_objs() const { size_t r = 0; for (unsigned slot_id = 0; slot_id < NUM_SLOTS; slot_id++) { void ** ptr = reinterpret_cast(const_cast(this)->m_free_list[slot_id]); while (ptr != 0) { r ++; ptr = reinterpret_cast(*ptr); } } return r; } template struct ptr_lt { bool operator()(T * p1, T * p2) const { return p1 < p2; } }; #define CONSOLIDATE_VB_LVL 20 void small_object_allocator::consolidate() { IF_VERBOSE(CONSOLIDATE_VB_LVL, verbose_stream() << "(allocator-consolidate :wasted-size " << get_wasted_size() << " :memory " << std::fixed << std::setprecision(2) << static_cast(memory::get_allocation_size())/static_cast(1024*1024) << ")" << std::endl;); ptr_vector chunks; ptr_vector free_objs; for (unsigned slot_id = 1; slot_id < NUM_SLOTS; slot_id++) { if (m_free_list[slot_id] == 0) continue; chunks.reset(); free_objs.reset(); chunk * c = m_chunks[slot_id]; while (c != 0) { chunks.push_back(c); c = c->m_next; } char * ptr = static_cast(m_free_list[slot_id]); while (ptr != 0) { free_objs.push_back(ptr); ptr = *(reinterpret_cast(ptr)); } unsigned obj_size = slot_id << PTR_ALIGNMENT; unsigned num_objs_per_chunk = CHUNK_SIZE / obj_size; if (free_objs.size() < num_objs_per_chunk) continue; SASSERT(!chunks.empty()); std::sort(chunks.begin(), chunks.end(), ptr_lt()); std::sort(free_objs.begin(), free_objs.end(), ptr_lt()); chunk * last_chunk = 0; void * last_free_obj = 0; unsigned chunk_idx = 0; unsigned obj_idx = 0; unsigned num_chunks = chunks.size(); unsigned num_objs = free_objs.size(); while (chunk_idx < num_chunks) { chunk * curr_chunk = chunks[chunk_idx]; char * curr_begin = curr_chunk->m_data; char * curr_end = curr_begin + CHUNK_SIZE; unsigned num_free_in_chunk = 0; unsigned saved_obj_idx = obj_idx; while (obj_idx < num_objs) { char * free_obj = free_objs[obj_idx]; if (free_obj > curr_end) break; obj_idx++; num_free_in_chunk++; } if (num_free_in_chunk == num_objs_per_chunk) { dealloc(curr_chunk); } else { curr_chunk->m_next = last_chunk; last_chunk = curr_chunk; for (unsigned i = saved_obj_idx; i < obj_idx; i++) { // relink objects void * free_obj = free_objs[i]; *(reinterpret_cast(free_obj)) = last_free_obj; last_free_obj = free_obj; } } chunk_idx++; } m_chunks[slot_id] = last_chunk; m_free_list[slot_id] = last_free_obj; } IF_VERBOSE(CONSOLIDATE_VB_LVL, verbose_stream() << "(end-allocator-consolidate :wasted-size " << get_wasted_size() << " :memory " << std::fixed << std::setprecision(2) << static_cast(memory::get_allocation_size())/static_cast(1024*1024) << ")" << std::endl;); } z3-z3-4.4.1/src/util/small_object_allocator.h000066400000000000000000000032111260446376700210350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: small_object_allocator.h Abstract: Small object allocator. Author: Nikolaj bjorner (nbjorner) 2007-08-06. Revision History: Leonardo de Moura (leonardo) 2011-04-27 Rewrote/Simplified the allocator --*/ #ifndef SMALL_OBJECT_ALLOCATOR_H_ #define SMALL_OBJECT_ALLOCATOR_H_ #include"machine.h" #include"debug.h" class small_object_allocator { static const unsigned CHUNK_SIZE = (8192 - sizeof(void*)*2); static const unsigned SMALL_OBJ_SIZE = 256; static const unsigned NUM_SLOTS = (SMALL_OBJ_SIZE >> PTR_ALIGNMENT); struct chunk { chunk * m_next; char * m_curr; char m_data[CHUNK_SIZE]; chunk():m_curr(m_data) {} }; chunk * m_chunks[NUM_SLOTS]; void * m_free_list[NUM_SLOTS]; size_t m_alloc_size; #ifdef Z3DEBUG char const * m_id; #endif public: small_object_allocator(char const * id = "unknown"); ~small_object_allocator(); void reset(); void * allocate(size_t size); void deallocate(size_t size, void * p); size_t get_allocation_size() const { return m_alloc_size; } size_t get_wasted_size() const; size_t get_num_free_objs() const; void consolidate(); }; inline void * operator new(size_t s, small_object_allocator & r) { return r.allocate(s); } inline void * operator new[](size_t s, small_object_allocator & r) { return r.allocate(s); } inline void operator delete(void * p, small_object_allocator & r) { UNREACHABLE(); } inline void operator delete[](void * p, small_object_allocator & r) { UNREACHABLE(); } #endif /* SMALL_OBJECT_ALLOCATOR_H_ */ z3-z3-4.4.1/src/util/smt2_util.cpp000066400000000000000000000026521260446376700166240ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt2_util.cpp Abstract: Goodies for SMT2 standard Author: Leonardo (leonardo) 2012-10-20 Notes: --*/ #include"smt2_util.h" bool is_smt2_simple_symbol_char(char s) { return ('0' <= s && s <= '9') || ('a' <= s && s <= 'z') || ('A' <= s && s <= 'Z') || s == '~' || s == '!' || s == '@' || s == '$' || s == '%' || s == '^' || s == '&' || s == '*' || s == '_' || s == '-' || s == '+' || s == '=' || s == '<' || s == '>' || s == '.' || s == '?' || s == '/'; } bool is_smt2_quoted_symbol(char const * s) { if (s == 0) return false; if ('0' <= s[0] && s[0] <= '9') return true; unsigned len = static_cast(strlen(s)); for (unsigned i = 0; i < len; i++) if (!is_smt2_simple_symbol_char(s[i])) return true; return false; } bool is_smt2_quoted_symbol(symbol const & s) { if (s.is_numerical()) return false; return is_smt2_quoted_symbol(s.bare_str()); } std::string mk_smt2_quoted_symbol(symbol const & s) { SASSERT(is_smt2_quoted_symbol(s)); string_buffer<> buffer; buffer.append('|'); char const * str = s.bare_str(); while (*str) { if (*str == '|' || *str == '\\') buffer.append('\\'); buffer.append(*str); str++; } buffer.append('|'); return std::string(buffer.c_str()); } z3-z3-4.4.1/src/util/smt2_util.h000066400000000000000000000006611260446376700162670ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt2_util.h Abstract: Goodies for SMT2 standard Author: Leonardo (leonardo) 2012-10-20 Notes: --*/ #ifndef SMT2_UTIL_H_ #define SMT2_UTIL_H_ #include"symbol.h" bool is_smt2_simple_symbol_char(char c); bool is_smt2_quoted_symbol(char const * s); bool is_smt2_quoted_symbol(symbol const & s); std::string mk_smt2_quoted_symbol(symbol const & s); #endif z3-z3-4.4.1/src/util/sorting_network.h000066400000000000000000000646321260446376700176130ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: sorting_network.h Abstract: Utility for creating a sorting network. Author: Nikolaj Bjorner (nbjorner) 2013-11-07 Notes: Same routine is used in Formula. --*/ #include "vector.h" #ifndef SORTING_NETWORK_H_ #define SORTING_NETWORK_H_ template class sorting_network { typedef typename Ext::vector vect; Ext& m_ext; svector m_currentv; svector m_nextv; svector* m_current; svector* m_next; unsigned& current(unsigned i) { return (*m_current)[i]; } unsigned& next(unsigned i) { return (*m_next)[i]; } void exchange(unsigned i, unsigned j, vect& out) { SASSERT(i <= j); if (i < j) { typename Ext::T ei = out.get(i); typename Ext::T ej = out.get(j); out.set(i, m_ext.mk_ite(m_ext.mk_le(ei, ej), ei, ej)); out.set(j, m_ext.mk_ite(m_ext.mk_le(ej, ei), ei, ej)); } } void sort(unsigned k, vect& out) { SASSERT(is_power_of2(k) && k > 0); if (k == 2) { for (unsigned i = 0; i < out.size()/2; ++i) { exchange(current(2*i), current(2*i+1), out); next(2*i) = current(2*i); next(2*i+1) = current(2*i+1); } std::swap(m_current, m_next); } else { for (unsigned i = 0; i < out.size()/k; ++i) { unsigned ki = k * i; for (unsigned j = 0; j < k / 2; ++j) { next(ki + j) = current(ki + (2 * j)); next(ki + (k / 2) + j) = current(ki + (2 * j) + 1); } } std::swap(m_current, m_next); sort(k / 2, out); for (unsigned i = 0; i < out.size() / k; ++i) { unsigned ki = k * i; for (unsigned j = 0; j < k / 2; ++j) { next(ki + (2 * j)) = current(ki + j); next(ki + (2 * j) + 1) = current(ki + (k / 2) + j); } for (unsigned j = 0; j < (k / 2) - 1; ++j) { exchange(next(ki + (2 * j) + 1), next(ki + (2 * (j + 1))), out); } } std::swap(m_current, m_next); } } bool is_power_of2(unsigned n) const { return n != 0 && ((n-1) & n) == 0; } public: sorting_network(Ext& ext): m_ext(ext), m_current(&m_currentv), m_next(&m_nextv) {} void operator()(vect const& in, vect& out) { out.reset(); out.append(in); if (in.size() <= 1) { return; } while (!is_power_of2(out.size())) { out.push_back(m_ext.mk_default()); } for (unsigned i = 0; i < out.size(); ++i) { m_currentv.push_back(i); m_nextv.push_back(i); } unsigned k = 2; while (k <= out.size()) { sort(k, out); k *= 2; } } }; // parametric sorting network // Described in Abio et.al. CP 2013. template class psort_nw { typedef typename psort_expr::literal literal; typedef typename psort_expr::literal_vector literal_vector; class vc { unsigned v; // number of vertices unsigned c; // number of clauses static const unsigned lambda = 5; public: vc(unsigned v, unsigned c):v(v), c(c) {} bool operator<(vc const& other) const { return to_int() < other.to_int(); } vc operator+(vc const& other) const { return vc(v + other.v, c + other.c); } unsigned to_int() const { return lambda*v + c; } vc operator*(unsigned n) const { return vc(n*v, n*c); } }; static vc mk_min(vc const& v1, vc const& v2) { return (v1.to_int() < v2.to_int())?v1:v2; } enum cmp_t { LE, GE, EQ, GE_FULL, LE_FULL }; psort_expr& ctx; cmp_t m_t; // for testing static const bool m_disable_dcard = false; static const bool m_disable_dsorting = false; static const bool m_disable_dsmerge = false; static const bool m_force_dcard = false; static const bool m_force_dsorting = false; static const bool m_force_dsmerge = false; public: struct stats { unsigned m_num_compiled_vars; unsigned m_num_compiled_clauses; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; stats m_stats; psort_nw(psort_expr& c): ctx(c) {} literal ge(bool full, unsigned k, unsigned n, literal const* xs) { if (k > n) { return ctx.mk_false(); } if (k == 0) { return ctx.mk_true(); } SASSERT(0 < k && k <= n); literal_vector in, out; if (dualize(k, n, xs, in)) { return le(full, k, in.size(), in.c_ptr()); } else { SASSERT(2*k <= n); m_t = full?GE_FULL:GE; psort_nw::card(k, n, xs, out); return out[k-1]; } } literal le(bool full, unsigned k, unsigned n, literal const* xs) { if (k >= n) { return ctx.mk_true(); } SASSERT(k < n); literal_vector in, out; if (dualize(k, n, xs, in)) { return ge(full, k, n, in.c_ptr()); } else { SASSERT(2*k <= n); m_t = full?LE_FULL:LE; card(k + 1, n, xs, out); return ctx.mk_not(out[k]); } } literal eq(unsigned k, unsigned n, literal const* xs) { if (k > n) { return ctx.mk_false(); } SASSERT(k <= n); literal_vector in, out; if (dualize(k, n, xs, in)) { return eq(k, n, in.c_ptr()); } else { SASSERT(2*k <= n); m_t = EQ; card(k+1, n, xs, out); SASSERT(out.size() >= k+1); return ctx.mk_min(out[k-1], ctx.mk_not(out[k])); } } private: std::ostream& pp(std::ostream& out, unsigned n, literal const* lits) { for (unsigned i = 0; i < n; ++i) ctx.pp(out, lits[i]) << " "; return out; } std::ostream& pp(std::ostream& out, literal_vector const& lits) { for (unsigned i = 0; i < lits.size(); ++i) ctx.pp(out, lits[i]) << " "; return out; } // 0 <= k <= N // SUM x_i >= k // <=> // SUM ~x_i <= N - k // suppose k > N/2, then it is better to solve dual. bool dualize(unsigned& k, unsigned N, literal const* xs, literal_vector& in) { SASSERT(0 <= k && k <= N); if (2*k <= N) { return false; } k = N - k; for (unsigned i = 0; i < N; ++i) { in.push_back(ctx.mk_not(xs[i])); } TRACE("pb", pp(tout << N << ": ", in); tout << " ~ " << k << "\n";); return true; } bool even(unsigned n) const { return (0 == (n & 0x1)); } bool odd(unsigned n) const { return !even(n); } unsigned ceil2(unsigned n) const { return n/2 + odd(n); } unsigned floor2(unsigned n) const { return n/2; } unsigned power2(unsigned n) const { SASSERT(n < 10); return 1 << n; } literal mk_max(literal a, literal b) { if (a == b) return a; m_stats.m_num_compiled_vars++; return ctx.mk_max(a, b); } literal mk_min(literal a, literal b) { if (a == b) return a; m_stats.m_num_compiled_vars++; return ctx.mk_min(a, b); } literal fresh() { m_stats.m_num_compiled_vars++; return ctx.fresh(); } void add_clause(literal l1, literal l2, literal l3) { literal lits[3] = { l1, l2, l3 }; add_clause(3, lits); } void add_clause(literal l1, literal l2) { literal lits[2] = { l1, l2 }; add_clause(2, lits); } void add_clause(unsigned n, literal const* ls) { m_stats.m_num_compiled_clauses++; literal_vector tmp(n, ls); ctx.mk_clause(n, tmp.c_ptr()); } // y1 <= mk_max(x1,x2) // y2 <= mk_min(x1,x2) void cmp_ge(literal x1, literal x2, literal y1, literal y2) { add_clause(ctx.mk_not(y2), x1); add_clause(ctx.mk_not(y2), x2); add_clause(ctx.mk_not(y1), x1, x2); } // mk_max(x1,x2) <= y1 // mk_min(x1,x2) <= y2 void cmp_le(literal x1, literal x2, literal y1, literal y2) { add_clause(ctx.mk_not(x1), y1); add_clause(ctx.mk_not(x2), y1); add_clause(ctx.mk_not(x1), ctx.mk_not(x2), y2); } void cmp_eq(literal x1, literal x2, literal y1, literal y2) { cmp_ge(x1, x2, y1, y2); cmp_le(x1, x2, y1, y2); } void cmp(literal x1, literal x2, literal y1, literal y2) { switch(m_t) { case LE: case LE_FULL: cmp_le(x1, x2, y1, y2); break; case GE: case GE_FULL: cmp_ge(x1, x2, y1, y2); break; case EQ: cmp_eq(x1, x2, y1, y2); break; } } vc vc_cmp() { return vc(2, (m_t==EQ)?6:3); } void card(unsigned k, unsigned n, literal const* xs, literal_vector& out) { TRACE("pb", tout << "card k:" << k << " n: " << n << "\n";); if (n <= k) { psort_nw::sorting(n, xs, out); } else if (use_dcard(k, n)) { dsorting(k, n, xs, out); } else { literal_vector out1, out2; unsigned l = n/2; // TBD card(k, l, xs, out1); card(k, n-l, xs + l, out2); smerge(k, out1.size(), out1.c_ptr(), out2.size(), out2.c_ptr(), out); } TRACE("pb", tout << "card k:" << k << " n: " << n << "\n"; pp(tout << "in:", n, xs) << "\n"; pp(tout << "out:", out) << "\n";); } vc vc_card(unsigned k, unsigned n) { if (n <= k) { return vc_sorting(n); } else if (use_dcard(k, n)) { return vc_dsorting(k, n); } else { return vc_card_rec(k, n); } } vc vc_card_rec(unsigned k, unsigned n) { unsigned l = n/2; return vc_card(k, l) + vc_card(k, n-l) + vc_smerge(k, l, n-l); } bool use_dcard(unsigned k, unsigned n) { return m_force_dcard || (!m_disable_dcard && n < 10 && vc_dsorting(k, n) < vc_card_rec(k, n)); } void merge(unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { TRACE("pb", tout << "merge a: " << a << " b: " << b << "\n";); if (a == 1 && b == 1) { literal y1 = mk_max(as[0], bs[0]); literal y2 = mk_min(as[0], bs[0]); out.push_back(y1); out.push_back(y2); psort_nw::cmp(as[0], bs[0], y1, y2); } else if (a == 0) { out.append(b, bs); } else if (b == 0) { out.append(a, as); } else if (use_dsmerge(a, b, a + b)) { dsmerge(a + b, a, as, b, bs, out); } else if (even(a) && odd(b)) { merge(b, bs, a, as, out); } else { literal_vector even_a, odd_a; literal_vector even_b, odd_b; literal_vector out1, out2; SASSERT(a > 1 || b > 1); split(a, as, even_a, odd_a); split(b, bs, even_b, odd_b); SASSERT(!even_a.empty()); SASSERT(!even_b.empty()); merge(even_a.size(), even_a.c_ptr(), even_b.size(), even_b.c_ptr(), out1); merge(odd_a.size(), odd_a.c_ptr(), odd_b.size(), odd_b.c_ptr(), out2); interleave(out1, out2, out); } TRACE("pb", tout << "merge a: " << a << " b: " << b << "\n"; pp(tout << "a:", a, as) << "\n"; pp(tout << "b:", b, bs) << "\n"; pp(tout << "out:", out) << "\n";); } vc vc_merge(unsigned a, unsigned b) { if (a == 1 && b == 1) { return vc_cmp(); } else if (a == 0 || b == 0) { return vc(0, 0); } else if (use_dsmerge(a, b, a + b)) { return vc_dsmerge(a, b, a + b); } else { return vc_merge_rec(a, b); } } vc vc_merge_rec(unsigned a, unsigned b) { return vc_merge(ceil2(a), ceil2(b)) + vc_merge(floor2(a), floor2(b)) + vc_interleave(ceil2(a) + ceil2(b), floor2(a) + floor2(b)); } void split(unsigned n, literal const* ls, literal_vector& even, literal_vector& odd) { for (unsigned i = 0; i < n; i += 2) { even.push_back(ls[i]); } for (unsigned i = 1; i < n; i += 2) { odd.push_back(ls[i]); } } void interleave(literal_vector const& as, literal_vector const& bs, literal_vector& out) { TRACE("pb", tout << "interleave: " << as.size() << " " << bs.size() << "\n";); SASSERT(as.size() >= bs.size()); SASSERT(as.size() <= bs.size() + 2); SASSERT(!as.empty()); out.push_back(as[0]); unsigned sz = std::min(as.size()-1, bs.size()); for (unsigned i = 0; i < sz; ++i) { literal y1 = mk_max(as[i+1],bs[i]); literal y2 = mk_min(as[i+1],bs[i]); psort_nw::cmp(as[i+1], bs[i], y1, y2); out.push_back(y1); out.push_back(y2); } if (as.size() == bs.size()) { out.push_back(bs[sz]); } else if (as.size() == bs.size() + 2) { out.push_back(as[sz+1]); } SASSERT(out.size() == as.size() + bs.size()); TRACE("pb", tout << "interleave: " << as.size() << " " << bs.size() << "\n"; pp(tout << "a: ", as) << "\n"; pp(tout << "b: ", bs) << "\n"; pp(tout << "out: ", out) << "\n";); } vc vc_interleave(unsigned a, unsigned b) { return vc_cmp()*std::min(a-1,b); } void sorting(unsigned n, literal const* xs, literal_vector& out) { TRACE("pb", tout << "sorting: " << n << "\n";); switch(n) { case 0: break; case 1: out.push_back(xs[0]); break; case 2: psort_nw::merge(1, xs, 1, xs+1, out); break; default: if (use_dsorting(n)) { dsorting(n, n, xs, out); } else { literal_vector out1, out2; unsigned l = n/2; // TBD sorting(l, xs, out1); sorting(n-l, xs+l, out2); merge(out1.size(), out1.c_ptr(), out2.size(), out2.c_ptr(), out); } break; } TRACE("pb", tout << "sorting: " << n << "\n"; pp(tout << "in:", n, xs) << "\n"; pp(tout << "out:", out) << "\n";); } vc vc_sorting(unsigned n) { switch(n) { case 0: return vc(0,0); case 1: return vc(0,0); case 2: return vc_merge(1,1); default: if (use_dsorting(n)) { return vc_dsorting(n, n); } else { return vc_sorting_rec(n); } } } vc vc_sorting_rec(unsigned n) { SASSERT(n > 2); unsigned l = n/2; return vc_sorting(l) + vc_sorting(n-l) + vc_merge(l, n-l); } bool use_dsorting(unsigned n) { SASSERT(n > 2); return m_force_dsorting || (!m_disable_dsorting && n < 10 && vc_dsorting(n, n) < vc_sorting_rec(n)); } void smerge(unsigned c, unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { TRACE("pb", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n";); if (a == 1 && b == 1 && c == 1) { literal y = mk_max(as[0], bs[0]); if (m_t != GE) { // x1 <= mk_max(x1,x2) // x2 <= mk_max(x1,x2) add_clause(ctx.mk_not(as[0]), y); add_clause(ctx.mk_not(bs[0]), y); } if (m_t != LE) { // mk_max(x1,x2) <= x1, x2 add_clause(ctx.mk_not(y), as[0], bs[0]); } out.push_back(y); } else if (a == 0) { out.append(std::min(c, b), bs); } else if (b == 0) { out.append(std::min(c, a), as); } else if (a > c) { smerge(c, c, as, b, bs, out); } else if (b > c) { smerge(c, a, as, c, bs, out); } else if (a + b <= c) { merge(a, as, b, bs, out); } else if (use_dsmerge(a, b, c)) { dsmerge(c, a, as, b, bs, out); } else { literal_vector even_a, odd_a; literal_vector even_b, odd_b; literal_vector out1, out2; split(a, as, even_a, odd_a); split(b, bs, even_b, odd_b); SASSERT(!even_a.empty()); SASSERT(!even_b.empty()); unsigned c1, c2; if (even(c)) { c1 = 1 + c/2; c2 = c/2; } else { c1 = (c + 1)/2; c2 = (c - 1)/2; } smerge(c1, even_a.size(), even_a.c_ptr(), even_b.size(), even_b.c_ptr(), out1); smerge(c2, odd_a.size(), odd_a.c_ptr(), odd_b.size(), odd_b.c_ptr(), out2); SASSERT(out1.size() == std::min(even_a.size()+even_b.size(), c1)); SASSERT(out2.size() == std::min(odd_a.size()+odd_b.size(), c2)); literal y; if (even(c)) { literal z1 = out1.back(); literal z2 = out2.back(); out1.pop_back(); out2.pop_back(); y = mk_max(z1, z2); if (m_t != GE) { add_clause(ctx.mk_not(z1), y); add_clause(ctx.mk_not(z2), y); } if (m_t != LE) { add_clause(ctx.mk_not(y), z1, z2); } } interleave(out1, out2, out); if (even(c)) { out.push_back(y); } } TRACE("pb", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n"; pp(tout << "a:", a, as) << "\n"; pp(tout << "b:", b, bs) << "\n"; pp(tout << "out:", out) << "\n"; ); SASSERT(out.size() == std::min(a + b, c)); } vc vc_smerge(unsigned a, unsigned b, unsigned c) { if (a == 1 && b == 1 && c == 1) { vc v(1,0); if (m_t != GE) v = v + vc(0, 2); if (m_t != LE) v = v + vc(0, 1); return v; } if (a == 0 || b == 0) return vc(0, 0); if (a > c) return vc_smerge(c, b, c); if (b > c) return vc_smerge(a, c, c); if (a + b <= c) return vc_merge(a, b); if (use_dsmerge(a, b, c)) return vc_dsmerge(a, b, c); return vc_smerge_rec(a, b, c); } vc vc_smerge_rec(unsigned a, unsigned b, unsigned c) { return vc_smerge(ceil2(a), ceil2(b), even(c)?(1+c/2):((c+1)/2)) + vc_smerge(floor2(a), floor2(b), even(c)?(c/2):((c-1)/2)) + vc_interleave(ceil2(a)+ceil2(b),floor2(a)+floor2(b)) + vc(1, 0) + ((m_t != GE)?vc(0, 2):vc(0, 0)) + ((m_t != LE)?vc(0, 1):vc(0, 0)); } bool use_dsmerge(unsigned a, unsigned b, unsigned c) { return m_force_dsmerge || (!m_disable_dsmerge && a < (1 << 15) && b < (1 << 15) && vc_dsmerge(a, b, a + b) < vc_smerge_rec(a, b, c)); } void dsmerge( unsigned c, unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { TRACE("pb", tout << "dsmerge: c:" << c << " a:" << a << " b:" << b << "\n";); SASSERT(a <= c); SASSERT(b <= c); SASSERT(a + b >= c); for (unsigned i = 0; i < c; ++i) { out.push_back(fresh()); } if (m_t != GE) { for (unsigned i = 0; i < a; ++i) { add_clause(ctx.mk_not(as[i]), out[i]); } for (unsigned i = 0; i < b; ++i) { add_clause(ctx.mk_not(bs[i]), out[i]); } for (unsigned i = 1; i <= a; ++i) { for (unsigned j = 1; j <= b && i + j <= c; ++j) { add_clause(ctx.mk_not(as[i-1]),ctx.mk_not(bs[j-1]),out[i+j-1]); } } } if (m_t != LE) { literal_vector ls; for (unsigned k = 0; k < c; ++k) { ls.reset(); ls.push_back(ctx.mk_not(out[k])); if (a <= k) { add_clause(ctx.mk_not(out[k]), bs[k-a]); } if (b <= k) { add_clause(ctx.mk_not(out[k]), as[k-b]); } for (unsigned i = 0; i < std::min(a,k + 1); ++i) { unsigned j = k - i; SASSERT(i + j == k); if (j < b) { ls.push_back(as[i]); ls.push_back(bs[j]); add_clause(ls.size(), ls.c_ptr()); ls.pop_back(); ls.pop_back(); } } } } } vc vc_dsmerge(unsigned a, unsigned b, unsigned c) { vc v(c, 0); if (m_t != GE) { v = v + vc(0, a + b + std::min(a, c)*std::min(b, c)/2); } if (m_t != LE) { v = v + vc(0, std::min(a, c)*std::min(b, c)/2); } return v; } void dsorting(unsigned m, unsigned n, literal const* xs, literal_vector& out) { TRACE("pb", tout << "dsorting m: " << m << " n: " << n << "\n";); SASSERT(m <= n); literal_vector lits; for (unsigned i = 0; i < m; ++i) { out.push_back(fresh()); } if (m_t != GE) { for (unsigned k = 1; k <= m; ++k) { lits.push_back(out[k-1]); add_subset(true, k, 0, lits, n, xs); lits.pop_back(); } } if (m_t != LE) { for (unsigned k = 1; k <= m; ++k) { lits.push_back(ctx.mk_not(out[k-1])); add_subset(false, n-k+1, 0, lits, n, xs); lits.pop_back(); } } } vc vc_dsorting(unsigned m, unsigned n) { SASSERT(m <= n && n < 10); vc v(m, 0); if (m_t != GE) { v = v + vc(0, power2(n-1)); } if (m_t != LE) { v = v + vc(0, power2(n-1)); } return v; } void add_subset(bool polarity, unsigned k, unsigned offset, literal_vector& lits, unsigned n, literal const* xs) { TRACE("pb", tout << "k:" << k << " offset: " << offset << " n: " << n << " "; pp(tout, lits) << "\n";); SASSERT(k + offset <= n); if (k == 0) { add_clause(lits.size(), lits.c_ptr()); return; } for (unsigned i = offset; i < n - k + 1; ++i) { lits.push_back(polarity?ctx.mk_not(xs[i]):xs[i]); add_subset(polarity, k-1, i+1, lits, n, xs); lits.pop_back(); } } }; #endif z3-z3-4.4.1/src/util/stack.cpp000066400000000000000000000071551260446376700160120ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: stack.cpp Abstract: Low level stack (aka stack-based allocator). Author: Leonardo (leonardo) 2011-02-27 Notes: --*/ #include"stack.h" #include"page.h" #include"tptr.h" inline void stack::store_mark(size_t m) { reinterpret_cast(m_curr_ptr)[0] = m; m_curr_ptr += sizeof(size_t); } inline size_t stack::top_mark() const { return reinterpret_cast(m_curr_ptr)[-1]; } inline size_t ptr2mark(void * ptr, bool external) { return reinterpret_cast(ptr) | static_cast(external); } #define MASK (static_cast(-1) - 1) inline char * mark2ptr(size_t m) { return reinterpret_cast(m & MASK); } inline bool external_ptr(size_t m) { return static_cast(m & 1); } inline void stack::allocate_page(size_t m) { m_curr_page = allocate_default_page(m_curr_page, m_free_pages); m_curr_ptr = m_curr_page; m_curr_end_ptr = end_of_default_page(m_curr_page); store_mark(m); } inline void stack::store_mark(void * ptr, bool external) { SASSERT(m_curr_ptr < m_curr_end_ptr || m_curr_ptr == m_curr_end_ptr); // mem is aligned if (m_curr_ptr + sizeof(size_t) > m_curr_end_ptr) { SASSERT(m_curr_ptr == m_curr_end_ptr); // doesn't fit in the current page allocate_page(ptr2mark(ptr, external)); } else { store_mark(ptr2mark(ptr, external)); } } stack::stack() { m_curr_page = 0; m_curr_ptr = 0; m_curr_end_ptr = 0; m_free_pages = 0; allocate_page(0); SASSERT(empty()); } stack::~stack() { reset(); del_pages(m_curr_page); del_pages(m_free_pages); } void stack::reset() { while(!empty()) deallocate(); } void * stack::top() const { SASSERT(!empty()); size_t m = top_mark(); void * r = mark2ptr(m); if (external_ptr(m)) r = reinterpret_cast(r)[0]; return r; } void * stack::allocate_small(size_t size, bool external) { SASSERT(size < DEFAULT_PAGE_SIZE); char * new_curr_ptr = m_curr_ptr + size; char * result; if (new_curr_ptr < m_curr_end_ptr) { result = m_curr_ptr; m_curr_ptr = ALIGN(char *, new_curr_ptr); } else { allocate_page(top_mark()); result = m_curr_ptr; m_curr_ptr += size; m_curr_ptr = ALIGN(char *, m_curr_ptr); } store_mark(result, external); SASSERT(m_curr_ptr > m_curr_page); SASSERT(m_curr_ptr <= m_curr_end_ptr); return result; } void * stack::allocate_big(size_t size) { char * r = alloc_svect(char, size); void * mem = allocate_small(sizeof(char*), true); reinterpret_cast(mem)[0] = r; SASSERT(m_curr_ptr > m_curr_page); SASSERT(m_curr_ptr <= m_curr_end_ptr); return r; } void stack::deallocate() { SASSERT(m_curr_ptr > m_curr_page); SASSERT(m_curr_ptr <= m_curr_end_ptr); size_t m = top_mark(); SASSERT(m != 0); if (m_curr_ptr == m_curr_page + sizeof(size_t)) { // mark is in the beginning of the page char * prev = prev_page(m_curr_page); recycle_page(m_curr_page, m_free_pages); m_curr_page = prev; m_curr_ptr = mark2ptr(m); m_curr_end_ptr = end_of_default_page(m_curr_page); SASSERT(m_curr_ptr > m_curr_page); SASSERT(m_curr_ptr <= m_curr_end_ptr); } else { // mark is in the middle of the page m_curr_ptr = mark2ptr(m); SASSERT(m_curr_ptr < m_curr_end_ptr); } if (external_ptr(m)) { dealloc_svect(reinterpret_cast(m_curr_ptr)[0]); } SASSERT(m_curr_ptr > m_curr_page); } z3-z3-4.4.1/src/util/stack.h000066400000000000000000000024261260446376700154530ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: stack.h Abstract: Low level stack (aka stack-based allocator). Author: Leonardo (leonardo) 2011-02-27 Notes: --*/ #ifndef STACK_H_ #define STACK_H_ #include"page.h" #include"debug.h" class stack { char * m_curr_page; char * m_curr_ptr; //!< Next free space in the current page. char * m_curr_end_ptr; //!< Point to the end of the current page. char * m_free_pages; void store_mark(size_t m); void store_mark(void * ptr, bool external); size_t top_mark() const; void allocate_page(size_t mark); void * allocate_small(size_t size, bool external); void * allocate_big(size_t size); public: stack(); ~stack(); void * allocate(size_t size) { return size < DEFAULT_PAGE_SIZE ? allocate_small(size, false) : allocate_big(size); } void deallocate(); bool empty() const { return reinterpret_cast(m_curr_ptr)[-1] == 0; } void * top() const; void reset(); template void deallocate(T * ptr) { SASSERT(ptr == top()); ptr->~T(); deallocate(); } }; inline void * operator new(size_t s, stack & r) { return r.allocate(s); } inline void operator delete(void * ptr, stack & r) { SASSERT(ptr == r.top()); r.deallocate(); } #endif z3-z3-4.4.1/src/util/statistics.cpp000066400000000000000000000156661260446376700171050ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: statistics.h Abstract: Wrapper for reporting statistics Author: Leonardo (leonardo) 2011-05-17 Notes: --*/ #include"statistics.h" #include"map.h" #include"str_hashtable.h" #include"buffer.h" #include"smt2_util.h" #include void statistics::update(char const * key, unsigned inc) { if (inc != 0) m_stats.push_back(key_val_pair(key, inc)); } void statistics::update(char const * key, double inc) { if (inc != 0.0) m_d_stats.push_back(key_d_val_pair(key, inc)); } void statistics::copy(statistics const & st) { m_stats.append(st.m_stats); m_d_stats.append(st.m_d_stats); } void statistics::reset() { m_stats.reset(); m_d_stats.reset(); } template static void mk_map(V const & v, M & m) { typename V::const_iterator it = v.begin(); typename V::const_iterator end = v.end(); for (; it != end; ++it) { typename V::data::second_type val; if (m.find(it->first, val)) m.insert(it->first, it->second + val); else m.insert(it->first, it->second); } } template static void get_keys(M const & m, ptr_buffer & keys) { typename M::iterator it = m.begin(); typename M::iterator end = m.end(); for (; it != end; ++it) { keys.push_back(const_cast(it->m_key)); } } static void display_smt2_key(std::ostream & out, char const * key) { SASSERT(key != 0); out << ":"; if (*key == ':') key++; while (*key) { if (is_smt2_simple_symbol_char(*key)) out << *key; else out << "-"; key++; } } struct str_lt { bool operator()(char const * s1, char const * s2) const { return strcmp(s1, s2) < 0; } }; typedef map key2val; typedef map key2dval; unsigned get_max_len(ptr_buffer & keys) { unsigned max = 0; for (unsigned i = 0; i < static_cast(keys.size()); i++) { char * k = keys.get(i); if (*k == ':') k++; unsigned curr = static_cast(strlen(k)); if (curr > max) max = curr; } return max; } void statistics::display_smt2(std::ostream & out) const { #define INIT_DISPLAY() \ key2val m_u; \ key2dval m_d; \ mk_map(m_stats, m_u); \ mk_map(m_d_stats, m_d); \ ptr_buffer keys; \ get_keys(m_u, keys); \ get_keys(m_d, keys); \ std::sort(keys.begin(), keys.end(), str_lt()); \ unsigned max = get_max_len(keys); INIT_DISPLAY(); bool first = true; #define DISPLAY_KEY() { \ if (!first) \ out << "\n "; \ display_smt2_key(out, k); \ unsigned len = static_cast(strlen(k)); \ for (unsigned j = len; j < max; j++) \ out << " "; \ first = false; \ } out << "("; for (unsigned i = 0; i < keys.size(); i++) { char * k = keys.get(i); unsigned val; if (m_u.find(k, val)) { DISPLAY_KEY(); out << " " << val; } else { double d_val = 0.0; m_d.find(k, d_val); DISPLAY_KEY(); out << " " << std::fixed << std::setprecision(2) << d_val; } } out << ")\n"; } void statistics::display(std::ostream & out) const { INIT_DISPLAY(); #undef DISPLAY_KEY #define DISPLAY_KEY() { \ if (*k == ':') \ k++; \ out << k << ":"; \ unsigned len = static_cast(strlen(k)); \ for (unsigned j = len; j < max; j++) \ out << " "; \ } for (unsigned i = 0; i < keys.size(); i++) { char * k = keys.get(i); unsigned val; if (m_u.find(k, val)) { DISPLAY_KEY(); out << " " << val << "\n"; } else { double d_val = 0.0; m_d.find(k, d_val); DISPLAY_KEY(); out << " " << std::fixed << std::setprecision(2) << d_val << "\n"; } } } template static void display_internal(std::ostream & out, M const & m) { typename M::iterator it = m.begin(); typename M::iterator end = m.end(); for (; it != end; it++) { char const * key = it->m_key; if (*key == ':') key++; while (*key) { if ('a' <= *key && *key <= 'z') out << ('A' + (*key - 'a')); else if (*key == ' ') out << "_"; else out << *key; } out << " " << it->m_value << "\n"; } } void statistics::display_internal(std::ostream & out) const { key2val m_u; key2dval m_d; mk_map(m_stats, m_u); mk_map(m_d_stats, m_d); ::display_internal(out, m_u); ::display_internal(out, m_d); } unsigned statistics::size() const { return m_stats.size() + m_d_stats.size(); } bool statistics::is_uint(unsigned idx) const { return idx < m_stats.size(); } char const * statistics::get_key(unsigned idx) const { if (is_uint(idx)) return m_stats[idx].first; else return m_d_stats[idx - m_stats.size()].first; } unsigned statistics::get_uint_value(unsigned idx) const { SASSERT(idx < size()); SASSERT(is_uint(idx)); return m_stats[idx].second; } double statistics::get_double_value(unsigned idx) const { SASSERT(idx < size()); SASSERT(!is_uint(idx)); return m_d_stats[idx - m_stats.size()].second; } void get_memory_statistics(statistics& st) { unsigned long long max_mem = memory::get_max_used_memory(); unsigned long long mem = memory::get_allocation_size(); max_mem = (100*max_mem)/(1024*1024); mem = (100*mem)/(1024*1024); st.update("max memory", static_cast(max_mem)/100.0); st.update("memory", static_cast(mem)/100.0); st.update("num allocs", static_cast(memory::get_allocation_count())); } void get_rlimit_statistics(reslimit& l, statistics& st) { st.update("rlimit count", l.count()); } z3-z3-4.4.1/src/util/statistics.h000066400000000000000000000021541260446376700165360ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: statistics.h Abstract: Wrapper for reporting statistics Author: Leonardo (leonardo) 2011-05-17 Notes: --*/ #ifndef STATISTICS_H_ #define STATISTICS_H_ #include #include"vector.h" #include"rlimit.h" class statistics { typedef std::pair key_val_pair; svector m_stats; typedef std::pair key_d_val_pair; svector m_d_stats; public: void copy(statistics const & st); void reset(); void update(char const * key, unsigned inc); void update(char const * key, double inc); void display(std::ostream & out) const; void display_smt2(std::ostream & out) const; void display_internal(std::ostream & out) const; unsigned size() const; bool is_uint(unsigned idx) const; char const * get_key(unsigned idx) const; unsigned get_uint_value(unsigned idx) const; double get_double_value(unsigned idx) const; }; void get_memory_statistics(statistics& st); void get_rlimit_statistics(reslimit& l, statistics& st); #endif z3-z3-4.4.1/src/util/stats.h000066400000000000000000000010221260446376700154730ustar00rootroot00000000000000/*++ Copyright (c) 2009 Microsoft Corporation Module Name: stats.h Abstract: Shared utilities for displaying statistics. Author: nbjorner 2009-12-6 Revision History: --*/ #ifndef STATS_H_ #define STATS_H_ #include inline void print_stat(std::ostream& out, char const* msg, unsigned num) { if (num > 0) { out << msg << num << "\n"; } } inline void print_stat_f(std::ostream& out, char const* msg, float num) { if (num > 0.0) { out << msg << num << "\n"; } } #endif z3-z3-4.4.1/src/util/stopwatch.h000066400000000000000000000076061260446376700163670ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: stopwatch.h Abstract: High resolution time keeping Author: Christoph Wintersteiger (t-cwinte) 2008-12-24 Revision History: --*/ #ifndef STOPWATCH_H_ #define STOPWATCH_H_ #if defined(_WINDOWS) || defined(_CYGWIN) // Does this redefinition work? #define ARRAYSIZE_TEMP ARRAYSIZE #undef ARRAYSIZE #include class stopwatch { private: LARGE_INTEGER m_elapsed; LARGE_INTEGER m_last_start_time; LARGE_INTEGER m_last_stop_time; LARGE_INTEGER m_frequency; public: stopwatch() { QueryPerformanceFrequency(&m_frequency); reset(); } ~stopwatch() {}; void reset() { m_elapsed.QuadPart = 0; } void start() { QueryPerformanceCounter(&m_last_start_time); } void stop() { QueryPerformanceCounter(&m_last_stop_time); m_elapsed.QuadPart += m_last_stop_time.QuadPart - m_last_start_time.QuadPart; } double get_seconds() const { return static_cast(m_elapsed.QuadPart / static_cast(m_frequency.QuadPart)) ; } double get_current_seconds() const { LARGE_INTEGER t; QueryPerformanceCounter(&t); return static_cast( (t.QuadPart - m_last_start_time.QuadPart) / static_cast(m_frequency.QuadPart)); } }; #undef ARRAYSIZE #define ARRAYSIZE ARRAYSIZE_TEMP #undef max #undef min #elif defined(__APPLE__) && defined (__MACH__) // Mac OS X #include #include class stopwatch { unsigned long long m_time; // elapsed time in ns bool m_running; clock_serv_t m_host_clock; mach_timespec_t m_start; public: stopwatch():m_time(0), m_running(false) { host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &m_host_clock); } ~stopwatch() {} void reset() { m_time = 0ull; } void start() { if (!m_running) { clock_get_time(m_host_clock, &m_start); m_running = true; } } void stop() { if (m_running) { mach_timespec_t _stop; clock_get_time(m_host_clock, &_stop); m_time += (_stop.tv_sec - m_start.tv_sec) * 1000000000ull; m_time += (_stop.tv_nsec - m_start.tv_nsec); m_running = false; } } double get_seconds() const { if (m_running) { const_cast(this)->stop(); /* update m_time */ const_cast(this)->start(); } return static_cast(m_time)/static_cast(1000000000ull); } double get_current_seconds() const { return get_seconds(); } }; #else // Linux #include class stopwatch { unsigned long long m_time; // elapsed time in ns bool m_running; struct timespec m_start; public: stopwatch():m_time(0), m_running(false) { } ~stopwatch() {} void reset() { m_time = 0ull; } void start() { if (!m_running) { clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &m_start); m_running = true; } } void stop() { if (m_running) { struct timespec _stop; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &_stop); m_time += (_stop.tv_sec - m_start.tv_sec) * 1000000000ull; if (m_time != 0 || _stop.tv_nsec >= m_start.tv_nsec) m_time += (_stop.tv_nsec - m_start.tv_nsec); m_running = false; } } double get_seconds() const { if (m_running) { const_cast(this)->stop(); /* update m_time */ const_cast(this)->start(); } return static_cast(m_time)/static_cast(1000000000ull); } double get_current_seconds() const { return get_seconds(); } }; #endif #endif z3-z3-4.4.1/src/util/str_hashtable.h000066400000000000000000000013301260446376700171620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: str_hashtable.h Abstract: String hashtable. It uses Jenkin's hash function and the optimized hashtable module. Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #ifndef STR_HASHTABLE_H_ #define STR_HASHTABLE_H_ #include #include"hashtable.h" #include"hash.h" struct str_hash_proc { unsigned operator()(char const * s) const { return string_hash(s, static_cast(strlen(s)), 17); } }; struct str_eq_proc { bool operator()(char const * s1, char const * s2) const { return strcmp(s1, s2) == 0; } }; typedef ptr_hashtable str_hashtable; #endif /* STR_HASHTABLE_H_ */ z3-z3-4.4.1/src/util/stream_buffer.h000066400000000000000000000013641260446376700171720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: stream_buffer.h Abstract: Simple stream buffer interface. In the future we should be able to read different kinds of stream (e.g., compressed files used in the SAT competitions). Author: Leonardo de Moura (leonardo) 2006-10-02. Revision History: --*/ #ifndef STREAM_BUFFER_H_ #define STREAM_BUFFER_H_ #include class stream_buffer { std::istream & m_stream; int m_val; public: stream_buffer(std::istream & s): m_stream(s) { m_val = m_stream.get(); } int operator *() const { return m_val; } void operator ++() { m_val = m_stream.get(); } }; #endif /* STREAM_BUFFER_H_ */ z3-z3-4.4.1/src/util/string_buffer.h000066400000000000000000000074011260446376700172030ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: string_buffer.h Abstract: Simple string buffer Author: Leonardo de Moura (leonardo) 2006-10-14. Revision History: --*/ #ifndef STRING_BUFFER_H_ #define STRING_BUFFER_H_ #include #include #include #include"util.h" #include"memory_manager.h" // This string buffer will not use the heap if the data consumes less than INITIAL_SIZE bytes. template class string_buffer { char m_initial_buffer[INITIAL_SIZE]; char * m_buffer; size_t m_pos; size_t m_capacity; void expand() { size_t new_capacity = m_capacity << 1; char * new_buffer = alloc_svect(char, new_capacity); memcpy(new_buffer, m_buffer, m_pos); if (m_capacity > INITIAL_SIZE) { dealloc_svect(m_buffer); } m_capacity = new_capacity; m_buffer = new_buffer; } public: string_buffer(): m_buffer(m_initial_buffer), m_pos(0), m_capacity(INITIAL_SIZE) { } ~string_buffer() { if (m_capacity > INITIAL_SIZE) { dealloc_svect(m_buffer); } } void reset() { m_pos = 0; } void append(char c) { if (m_pos >= m_capacity) { expand(); } m_buffer[m_pos] = c; m_pos++; } void append(const char * str) { size_t len = strlen(str); size_t new_pos = m_pos + len; while (new_pos > m_capacity) { expand(); } memcpy(m_buffer + m_pos, str, len); m_pos += len; } void append(int n) { char buffer[24]; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "%d", n); #else sprintf(buffer, "%d", n); #endif append(buffer); } void append(unsigned n) { char buffer[24]; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "%d", n); #else sprintf(buffer, "%d", n); #endif append(buffer); } void append(long n) { char buffer[24]; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "%ld", n); #else sprintf(buffer, "%ld", n); #endif append(buffer); } void append(bool b) { if (b) { append("true"); } else { append("false"); } } unsigned size() const { return m_pos; } bool empty() const { return m_pos == 0; } const char * c_str() const { if (m_pos >= m_capacity) { const_cast(this)->expand(); } const_cast(this)->m_buffer[m_pos] = 0; return m_buffer; } }; template inline string_buffer & operator<<(string_buffer & buffer, const char * str) { buffer.append(str); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer, char c) { buffer.append(c); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer, int i) { buffer.append(i); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer, unsigned i) { buffer.append(i); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer, bool b) { buffer.append(b); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer, long l) { buffer.append(l); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer1, const string_buffer & buffer2) { buffer1.append(buffer2.c_str()); return buffer1; } #endif z3-z3-4.4.1/src/util/symbol.cpp000066400000000000000000000074271260446376700162140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: symbol.cpp Abstract: Lisp-like symbols. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #include"symbol.h" #include"str_hashtable.h" #include"region.h" #include"string_buffer.h" #include"z3_omp.h" symbol symbol::m_dummy(TAG(void*, static_cast(0), 2)); const symbol symbol::null; /** \brief Symbol table manager. It stores the symbol strings created at runtime. */ class internal_symbol_table { region m_region; //!< Region used to store symbol strings. str_hashtable m_table; //!< Table of created symbol strings. public: char const * get_str(char const * d) { char * result; #pragma omp critical (cr_symbol) { char * r_d = const_cast(d); str_hashtable::entry * e; if (m_table.insert_if_not_there_core(r_d, e)) { // new entry size_t l = strlen(d); // store the hash-code before the string size_t * mem = static_cast(m_region.allocate(l + 1 + sizeof(size_t))); *mem = e->get_hash(); mem++; result = reinterpret_cast(mem); memcpy(result, d, l+1); // update the entry with the new ptr. e->set_data(result); } else { result = e->get_data(); } SASSERT(m_table.contains(result)); } return result; } }; internal_symbol_table* g_symbol_table = 0; void initialize_symbols() { if (!g_symbol_table) { g_symbol_table = alloc(internal_symbol_table); } } void finalize_symbols() { dealloc(g_symbol_table); g_symbol_table = 0; } symbol::symbol(char const * d) { if (d == 0) m_data = 0; else m_data = g_symbol_table->get_str(d); } symbol & symbol::operator=(char const * d) { m_data = g_symbol_table->get_str(d); return *this; } std::string symbol::str() const { SASSERT(!is_marked()); if (GET_TAG(m_data) == 0) { if (m_data) return m_data; else return ""; } else { string_buffer<128> buffer; buffer << "k!" << UNBOXINT(m_data); return buffer.c_str(); } } bool symbol::contains(char ch) const { SASSERT(!is_marked()); if (GET_TAG(m_data) == 0) { return strchr(m_data, ch) != 0; } else { return false; } } unsigned symbol::size() const { SASSERT(!is_marked()); if (GET_TAG(m_data) == 0) { return static_cast(strlen(m_data)); } else { unsigned v = UNBOXINT(m_data); unsigned sz = 4; v = v >> 1; while (v > 0) { sz++; v = v >> 1; } return sz; } } bool lt(symbol const & s1, symbol const & s2) { if (s1 == s2) return false; if (s1.is_numerical()) { if (!s2.is_numerical()) return true; // numeral symbols are smaller than non-numerical ones. return s1.get_num() < s2.get_num(); } if (s2.is_numerical()) { SASSERT(!s1.is_numerical()); return false; } SASSERT(!s1.is_numerical() && !s2.is_numerical()); char const * str1 = s1.bare_str(); char const * str2 = s2.bare_str(); while (true) { if (*str1 < *str2) { return true; } else if (*str1 == *str2) { str1++; str2++; if (!*str1) { SASSERT(*str2); // the strings can't be equal. return true; } if (!*str2) { SASSERT(*str1); // the strings can't be equal. return false; } } else { SASSERT(*str1 > *str2); return false; } } } z3-z3-4.4.1/src/util/symbol.h000066400000000000000000000104161260446376700156510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: symbol.h Abstract: Lisp-like symbols. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #ifndef SYMBOL_H_ #define SYMBOL_H_ #include #include #include"util.h" #include"tptr.h" #include"string_buffer.h" template class symbol_table; class symbol { char const * m_data; template friend class symbol_table; explicit symbol(void const * data): m_data(reinterpret_cast(data)) { } bool is_marked() const { return GET_TAG(m_data) > 1; } static symbol mark(symbol s) { SASSERT(!s.is_marked()); return symbol(TAG(void *, UNTAG(void *, s.m_data), GET_TAG(s.m_data) + 2)); } static symbol unmark(symbol s) { SASSERT(s.is_marked()); return symbol(TAG(void *, UNTAG(void *, s.m_data), GET_TAG(s.m_data) - 2)); } static symbol m_dummy; public: symbol(): m_data(0) { } explicit symbol(char const * d); explicit symbol(unsigned idx): m_data(BOXTAGINT(char const *, idx, 1)) { #ifndef _AMD64_ SASSERT(idx < (SIZE_MAX >> PTR_ALIGNMENT)); #endif } static symbol dummy() { return m_dummy; } static const symbol null; symbol & operator=(char const * d); friend bool operator==(symbol const & s1, symbol const & s2) { return s1.m_data == s2.m_data; } friend bool operator!=(symbol const & s1, symbol const & s2) { return s1.m_data != s2.m_data; } bool is_numerical() const { return GET_TAG(m_data) == 1; } unsigned int get_num() const { SASSERT(is_numerical()); return UNBOXINT(m_data); } std::string str() const; friend bool operator==(symbol const & s1, char const * s2) { if (s1.m_data == 0 && s2 == 0) return true; if (s1.m_data == 0 || s2 == 0) return false; if (!s1.is_numerical()) return strcmp(s1.bare_str(), s2) == 0; return s1.str() == s2; } friend bool operator!=(symbol const & s1, char const * s2) { return !operator==(s1, s2); } void const * c_ptr() const { return m_data; } // Low level function. // It is the inverse of c_ptr(). // It was made public to simplify the implementation of the C API. static symbol mk_symbol_from_c_ptr(void const * ptr) { symbol s(ptr); return s; } unsigned hash() const { if (m_data == 0) return 0x9e3779d9; else if (is_numerical()) return get_num(); else return static_cast(reinterpret_cast(m_data)[-1]); } bool contains(char c) const; unsigned size() const; char const * bare_str() const { SASSERT(!is_numerical()); return is_numerical() ? "" : m_data; } friend std::ostream & operator<<(std::ostream & target, symbol s) { SASSERT(!s.is_marked()); if (GET_TAG(s.m_data) == 0) { if (s.m_data) { target << s.m_data; } else { target << "null"; } } else { target << "k!" << UNBOXINT(s.m_data); } return target; } template friend string_buffer & operator<<(string_buffer & target, symbol s) { SASSERT(!s.is_marked()); if (GET_TAG(s.m_data) == 0) { if (s.m_data) { target << s.m_data; } else { target << "null"; } } else { target << "k!" << UNBOXINT(s.m_data); } return target; } }; struct symbol_hash_proc { unsigned operator()(symbol const & s) const { return s.hash(); } }; struct symbol_eq_proc { bool operator()(symbol const & s1, symbol const & s2) const { return s1 == s2; } }; void initialize_symbols(); void finalize_symbols(); /* ADD_INITIALIZER('initialize_symbols();') ADD_FINALIZER('finalize_symbols();') */ // total order on symbols... I did not overloaded '<' to avoid misunderstandings. // numerical symbols are smaller than non numerical symbols. // two numerical symbols are compared using get_num. // two non-numerical symbols are compared using string comparison. bool lt(symbol const & s1, symbol const & s2); #endif /* SYMBOL_H_ */ z3-z3-4.4.1/src/util/symbol_table.h000066400000000000000000000120761260446376700170240ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: symbol_table.h Abstract: Symbol table for parsing. Author: Leonardo de Moura (leonardo) 2006-09-19. Revision History: --*/ #ifndef SYMBOL_TABLE_H_ #define SYMBOL_TABLE_H_ #include"vector.h" #include"hashtable.h" #include"hash.h" #include"symbol.h" /** \brief Quick & Dirty symbol table. */ template class symbol_table { struct key_data { symbol m_key; T m_data; key_data() { } explicit key_data(symbol k): m_key(k) { } key_data(symbol k, const T & d): m_key(k), m_data(d) { } }; struct key_data_hash_proc { unsigned operator()(const key_data & k) const { return k.m_key.hash(); } }; struct key_data_eq_proc { bool operator()(const key_data & k1, const key_data & k2) const { return k1.m_key == k2.m_key; } }; struct hash_entry { typedef key_data data; key_data m_data; hash_entry() { SASSERT(m_data.m_key == symbol::null); } unsigned get_hash() const { return m_data.m_key.hash(); } bool is_free() const { return m_data.m_key == symbol::null; } bool is_deleted() const { return m_data.m_key == symbol::dummy(); } bool is_used() const { return !is_free() && !is_deleted(); } key_data & get_data() { return m_data; } const key_data & get_data() const { return m_data; } void set_data(const key_data & d) { m_data = d; } static void set_hash(unsigned h) { } void mark_as_deleted() { m_data.m_key = symbol::dummy(); } void mark_as_free() { m_data.m_key = symbol::null; } }; typedef core_hashtable sym_table; typedef vector trail_stack; sym_table m_sym_table; trail_stack m_trail_stack; int_vector m_trail_lims; public: void reset() { m_sym_table.reset(); m_trail_stack.reset(); m_trail_lims.reset(); } bool find(symbol key, T & result) const { key_data dummy(key); hash_entry * e = m_sym_table.find_core(dummy); if (e == 0) { return false; } result = e->get_data().m_data; return true; } bool contains(symbol key) const { return m_sym_table.contains(key_data(key)); } unsigned get_scope_level() const { return m_trail_lims.size(); } void insert(symbol key, const T & data) { if (get_scope_level() > 0) { key_data dummy(key); hash_entry * e = m_sym_table.find_core(dummy); if (e != 0) { m_trail_stack.push_back(e->m_data); e->m_data.m_data = data; return; } else { m_trail_stack.push_back(dummy); key_data & new_entry = m_trail_stack.back(); new_entry.m_key = symbol::mark(new_entry.m_key); m_sym_table.insert(key_data(key, data)); } } else { m_sym_table.insert(key_data(key, data)); } } void begin_scope() { m_trail_lims.push_back(m_trail_stack.size()); } void end_scope() { unsigned old_size = m_trail_lims.back(); m_trail_lims.pop_back(); unsigned curr_size = m_trail_stack.size(); SASSERT(old_size <= curr_size); for (unsigned i = old_size; i < curr_size; i++) { key_data & curr_entry = m_trail_stack.back(); symbol key = curr_entry.m_key; if (key.is_marked()) { curr_entry.m_key = symbol::unmark(key); m_sym_table.erase(curr_entry); } else { m_sym_table.insert(curr_entry); } m_trail_stack.pop_back(); } SASSERT(m_trail_stack.size() == old_size); } void append(symbol_table const& other) { typename sym_table::iterator it = other.m_sym_table.begin(); typename sym_table::iterator end = other.m_sym_table.end(); for (; it != end; ++it) { insert((*it).m_key, (*it).m_data); } } void get_range(vector& range) const { typename sym_table::iterator it = m_sym_table.begin(); typename sym_table::iterator end = m_sym_table.end(); for (; it != end; ++it) { range.push_back((*it).m_data); } } void get_dom(svector& dom) const { typename sym_table::iterator it = m_sym_table.begin(); typename sym_table::iterator end = m_sym_table.end(); for (; it != end; ++it) { dom.push_back((*it).m_key); } } }; #endif /* SYMBOL_TABLE_H_ */ z3-z3-4.4.1/src/util/timeit.cpp000066400000000000000000000025201260446376700161670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timeit.h Abstract: Support for timers. Author: Nikolaj Bjorner (nbjorner) 2006-09-22 Revision History: --*/ #include #include"timeit.h" #include"memory_manager.h" #include"stopwatch.h" #include struct timeit::imp { stopwatch m_watch; char const * m_msg; std::ostream & m_out; double m_start_memory; imp(char const * msg, std::ostream & out): m_msg(msg), m_out(out), 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); m_out << "(" << m_msg << " :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; } }; timeit::timeit(bool enable, char const * msg, std::ostream & out) { if (enable) m_imp = alloc(imp, msg, out); else m_imp = 0; } timeit::~timeit() { if (m_imp) dealloc(m_imp); } z3-z3-4.4.1/src/util/timeit.h000066400000000000000000000007541260446376700156430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timeit.h Abstract: Support for timers. Author: Nikolaj Bjorner (nbjorner) 2006-09-22 Revision History: Leonardo de Moura (leonardo) 2011-04-27 Rewrote using stopwatches, added support for tracking memory --*/ #ifndef TIMEIT_H_ #define TIMEIT_H_ class timeit { struct imp; imp * m_imp; public: timeit(bool enable, char const * msg, std::ostream & out = std::cerr); ~timeit(); }; #endif z3-z3-4.4.1/src/util/timeout.cpp000066400000000000000000000021031260446376700163570ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timeout.cpp Abstract: Timeout support Author: Leonardo de Moura (leonardo) 2006-10-02. Revision History: Christoph (cwinter) 2012-02-14: Switch to scoped_timer for timeout support on all platforms. --*/ #include #include"z3_omp.h" #include"util.h" #include"timeout.h" #include"error_codes.h" #include"event_handler.h" #include"scoped_timer.h" scoped_timer * g_timeout = 0; void (* g_on_timeout)() = 0; class g_timeout_eh : public event_handler { public: void operator()() { #pragma omp critical (g_timeout_cs) { std::cout << "timeout\n"; if (g_on_timeout) g_on_timeout(); if (g_timeout) delete g_timeout; g_timeout = 0; throw z3_error(ERR_TIMEOUT); } } }; void set_timeout(long ms) { if (g_timeout) delete g_timeout; g_timeout = new scoped_timer(ms, new g_timeout_eh()); } void register_on_timeout_proc(void (*proc)()) { g_on_timeout = proc; } z3-z3-4.4.1/src/util/timeout.h000066400000000000000000000005041260446376700160270ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timeout.h Abstract: Timeout support Author: Leonardo de Moura (leonardo) 2006-10-02. Revision History: --*/ #ifndef TIMEOUT_H_ #define TIMEOUT_H_ void register_on_timeout_proc(void (*proc)()); void set_timeout(long ms); #endif // _TIMEOUT_H_ z3-z3-4.4.1/src/util/timer.cpp000066400000000000000000000007621260446376700160220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timer.cpp Abstract: Author: Leonardo de Moura (leonardo) 2009-01-06. Revision History: --*/ #include"util.h" #include"memory_manager.h" #include"stopwatch.h" #include"timer.h" timer::timer(){ m_watch = alloc(stopwatch); start(); } timer::~timer() { dealloc(m_watch); } void timer::start() { m_watch->start(); } double timer::get_seconds() { return m_watch->get_current_seconds(); } z3-z3-4.4.1/src/util/timer.h000066400000000000000000000011511260446376700154600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timer.h Abstract: Author: Leonardo de Moura (leonardo) 2009-01-06. Revision History: --*/ #ifndef TIMER_H_ #define TIMER_H_ class stopwatch; /** \brief Wrapper for the stopwatch class. It hides windows.h dependency. */ class timer { stopwatch * m_watch; public: timer(); ~timer(); void start(); double get_seconds(); bool timeout(unsigned secs) { return secs > 0 && get_seconds() > secs; } bool ms_timeout(unsigned ms) { return ms > 0 && get_seconds() * 1000 > ms; } }; #endif /* TIMER_H_ */ z3-z3-4.4.1/src/util/total_order.h000066400000000000000000000244311260446376700166640ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: total_order.h Abstract: Author: Leonardo de Moura (leonardo) 2010-07-01. Revision History: --*/ #ifndef TOTAL_ORDER_H_ #define TOTAL_ORDER_H_ #include"util.h" #include"small_object_allocator.h" #include"map.h" #include"uint_map.h" #include"trace.h" /** \brief An object for maintaining a total-order on sets of T values. \c Map should be a class implementing the map API for T to void *. */ template class total_order { struct cell { cell * m_next; cell * m_prev; uint64 m_val; T m_data; }; small_object_allocator * m_allocator; bool m_own_allocator; Map m_map; cell m_base; unsigned m_size; cell * base() const { return const_cast(&m_base); } void init_base() { m_base.m_next = &m_base; m_base.m_prev = &m_base; m_base.m_val = 0; } uint64 v(cell * a) const { return a->m_val; } uint64 vb(cell * a) const { return v(a) - v(base()); } uint64 vbn(cell * a) const { return a->m_next == base() ? UINT64_MAX : vb(a->m_next); } cell * mk_cell(T const & a) { SASSERT(!m_map.contains(a)); cell * c = reinterpret_cast(m_allocator->allocate(sizeof(cell))); m_map.insert(a, c); c->m_data = a; m_size++; return c; } void del_cell(cell * c) { #ifdef Z3DEBUG T d = c->m_data; #endif m_map.erase(c->m_data); m_allocator->deallocate(sizeof(cell), c); m_size--; SASSERT(!m_map.contains(d)); } cell * to_cell(T const & a) const { void * r; #ifdef Z3DEBUG bool ok = #endif m_map.find(a, r); SASSERT(ok); return reinterpret_cast(r); } void _insert_after(cell * a, cell * b) { uint64 vb_a = vb(a); uint64 vbn_a = vbn(a); SASSERT(vb_a < vbn_a); if (vbn_a < 2 || (vb_a > vbn_a - 2)) { TRACE("total_order", tout << "relabeling...\n"; tout << "\n";); uint64 v0 = v(a); unsigned sz = size(); uint64 ideal_gap = UINT64_MAX / sz; uint64 goal_gap = ideal_gap / 32; cell * c = a->m_next->m_next; unsigned j = 2; uint64 curr_gap = (v(c) - v0) / j; while (j < sz && curr_gap < goal_gap) { j++; c = c->m_next; curr_gap = (v(c) - v0) / j; } TRACE("total_order", tout << "j: " << j << " curr_gap: " << curr_gap << " sz: " << sz << "\n";); if (j == sz) curr_gap = ideal_gap; c = a->m_next; uint64 inc = curr_gap; for (unsigned i = 0; i < j; i++) { c->m_val = v0 + inc; c = c->m_next; inc += curr_gap; } CASSERT("total_order", check_invariant()); vb_a = vb(a); vbn_a = vbn(a); } SASSERT(vb_a <= vbn_a - 2); uint64 vb_b = vb_a + ((vbn_a - vb_a)/2); SASSERT(vb_b > vb_a); SASSERT(vb_b < vbn_a); b->m_val = vb_b + v(base()); b->m_next = a->m_next; a->m_next->m_prev = b; b->m_prev = a; a->m_next = b; SASSERT(vb(a) < vb(b)); CASSERT("total_order", check_invariant()); } void insert_core(cell * a) { _insert_after(base(), a); } void remove_cell(cell * a) { SASSERT(a != base()); cell * p = a->m_prev; cell * n = a->m_next; p->m_next = n; n->m_prev = p; } void move_after(cell * a, cell * b) { if (a->m_next == b) return; remove_cell(b); _insert_after(a, b); } void move_beginning(cell * b) { if (base()->m_next == b) return; // already in the beginning remove_cell(b); insert_core(b); } void erase(cell * a) { remove_cell(a); del_cell(a); } public: total_order(): m_allocator(alloc(small_object_allocator)), m_own_allocator(true), m_size(0) { init_base(); } total_order(Map const & m): m_allocator(alloc(small_object_allocator)), m_own_allocator(true), m_map(m), m_size(0) { init_base(); } total_order(small_object_allocator * allocator): m_allocator(allocator), m_own_allocator(false), m_size(0) { init_base(); } total_order(small_object_allocator * allocator, Map const & m): m_allocator(allocator), m_own_allocator(false), m_map(m), m_size(0) { init_base(); } ~total_order() { cell * curr = base()->m_next; while (curr != base()) { cell * c = curr; curr = curr->m_next; del_cell(c); } if (m_own_allocator) dealloc(m_allocator); } /** \brief Return true if \c a is in the total order. */ bool contains(T const & a) const { return m_map.contains(a); } /** \brief Insert \c a in the beginning of the total order. \pre \c a must not be an element of the total order. */ void insert(T const & a) { SASSERT(!contains(a)); insert_core(mk_cell(a)); } /** \brief Insert \c b after \c a in the total order. \pre \c a is an element of the total order. \pre \c b must not be an element of the total order. */ void insert_after(T const & a, T const & b) { SASSERT(contains(a)); SASSERT(!contains(b)); _insert_after(to_cell(a), mk_cell(b)); SASSERT(lt(a, b)); } /** \brief Move \c a to the beginning of the total order. \pre \c a is an element of the total order. */ void move_beginning(T const & a) { SASSERT(contains(a)); move_beginning(to_cell(a)); } /** \brief Move \b after \c a in the total order. \pre \c a is an element of the total order. \pre \c b is an element of the total order. \pre \c a must be different from \c b. */ void move_after(T const & a, T const & b) { SASSERT(contains(a)); SASSERT(contains(b)); move_after(to_cell(a), to_cell(b)); SASSERT(lt(a, b)); } /** \brief Remove \c a from the total order. \pre \c a is an element of the total order. */ void erase(T const & a) { SASSERT(contains(a)); erase(to_cell(a)); } /** \brief Return true if \c a is less than \c b in the total order. */ bool lt(T const & a, T const & b) const { SASSERT(contains(a)); SASSERT(contains(b)); return vb(to_cell(a)) < vb(to_cell(b)); } /** \brief Return true if \c a is greater than \c b in the total order. */ bool gt(T const & a, T const & b) const { SASSERT(contains(a)); SASSERT(contains(b)); return vb(to_cell(a)) > vb(to_cell(b)); } /** \brief Return true if the total order is empty. */ bool empty() const { return base()->m_next == base(); } /** \brief Return the number of elements in the total order. */ unsigned size() const { return m_size; } /** \brief Return the first element of the total order. */ T const & first() const { SASSERT(!empty()); return base()->m_next->m_data; } /** \brief Return true if \c a has a successor in the total order. \pre \c a is an element of the total order. */ bool has_next(T const & a) const { SASSERT(contains(a)); return to_cell(a)->m_next != base(); } /** \brief Return the successor of \c a in the total order. \pre \c a is an element of the total order. \pre has_next(a) */ T const & next(T const & a) const { SASSERT(contains(a)); SASSERT(has_next(a)); return to_cell(a)->m_next->m_data; } /** \brief Return true if \c a has a predecessor in the total order. \pre \c a is an element of the total order. */ bool has_pred(T const & a) const { SASSERT(contains(a)); return to_cell(a)->m_prev != base(); } /** \brief Return the predecessor of \c a in the total order. \pre \c a is an element of the total order. \pre has_pred(a) */ T const & pred(T const & a) const { SASSERT(has_pred(a)); return to_cell(a)->m_prev->m_data; } /** \brief Display the elements of the total order in increasing order. \remark For debugging purposes. */ void display(std::ostream & out) const { cell * curr = base()->m_next; bool first = true; while (curr != base()) { if (first) first = false; else out << " "; out << curr->m_data; curr = curr->m_next; } } void display_detail(std::ostream & out) const { cell * curr = base()->m_next; bool first = true; while (curr != base()) { if (first) first = false; else out << " "; out << curr->m_data << ":" << curr->m_val; curr = curr->m_next; } } #ifdef Z3DEBUG bool check_invariant() const { cell * curr = base()->m_next; while (curr != base()) { SASSERT(curr->m_next == base() || vb(curr) < vb(curr->m_next)); curr = curr->m_next; } return true; } #endif }; typedef total_order > int_total_order; /** \brief A total order that uses vectors to implement a mapping from unsigned to void *. */ typedef total_order > uint_total_order; template std::ostream & operator<<(std::ostream & out, total_order const & to) { to.display(out); return out; } #endif /* TOTAL_ORDER_H_ */ z3-z3-4.4.1/src/util/tptr.h000066400000000000000000000023711260446376700153360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tptr.h Abstract: Support for tagged pointers and other low level pointer manipulation macros. Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #ifndef TPTR_H_ #define TPTR_H_ #include"machine.h" #define TAG_SHIFT PTR_ALIGNMENT #define ALIGNMENT_VALUE (1 << PTR_ALIGNMENT) #define TAG_MASK (ALIGNMENT_VALUE - 1) #define PTR_MASK (~TAG_MASK) #define ALIGN(T, PTR) reinterpret_cast(((reinterpret_cast(PTR) >> PTR_ALIGNMENT) + \ static_cast((reinterpret_cast(PTR) & TAG_MASK) != 0)) << PTR_ALIGNMENT) #define UNTAG(T, PTR) reinterpret_cast(reinterpret_cast(PTR) & PTR_MASK) #define TAG(T, PTR, TAG_VAL) reinterpret_cast(reinterpret_cast(PTR) | static_cast(TAG_VAL)) #define GET_TAG(PTR) (reinterpret_cast(PTR) & TAG_MASK) #define BOXINT(T, VAL) reinterpret_cast(static_cast(VAL) << PTR_ALIGNMENT) #define BOXTAGINT(T, VAL, TAG_VAL) reinterpret_cast((static_cast(VAL) << PTR_ALIGNMENT) | static_cast(TAG_VAL)) #define UNBOXINT(PTR) static_cast(reinterpret_cast(PTR) >> PTR_ALIGNMENT) #endif /* TPTR_H_ */ z3-z3-4.4.1/src/util/trace.cpp000066400000000000000000000023351260446376700157760ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: trace.cpp Abstract: Trace message support. Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #include"trace.h" #include"str_hashtable.h" #ifdef _TRACE std::ofstream tout(".z3-trace"); #endif static bool g_enable_all_trace_tags = false; static str_hashtable* g_enabled_trace_tags = 0; static str_hashtable& get_enabled_trace_tags() { if (!g_enabled_trace_tags) { g_enabled_trace_tags = alloc(str_hashtable); } return *g_enabled_trace_tags; } void finalize_trace() { dealloc(g_enabled_trace_tags); g_enabled_trace_tags = 0; } void enable_trace(const char * tag) { get_enabled_trace_tags().insert(const_cast(tag)); } void enable_all_trace(bool flag) { g_enable_all_trace_tags = flag; } void disable_trace(const char * tag) { get_enabled_trace_tags().erase(const_cast(tag)); } bool is_trace_enabled(const char * tag) { return g_enable_all_trace_tags || (g_enabled_trace_tags && get_enabled_trace_tags().contains(const_cast(tag))); } void close_trace() { #ifdef _TRACE tout.close(); #endif } void open_trace() { #ifdef _TRACE tout.open(".z3-trace"); #endif } z3-z3-4.4.1/src/util/trace.h000066400000000000000000000025421260446376700154430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: trace.h Abstract: Trace message support. Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #ifndef TRACE_H_ #define TRACE_H_ #ifdef _CYGWIN #undef max #undef min #endif #ifdef __APPLE__ #undef max #undef min #endif #include #ifdef _TRACE extern std::ofstream tout; #define TRACE_CODE(CODE) { CODE } ((void) 0 ) #else #define TRACE_CODE(CODE) ((void) 0) #endif void enable_trace(const char * tag); void enable_all_trace(bool flag); void disable_trace(const char * tag); bool is_trace_enabled(const char * tag); void close_trace(); void open_trace(); void finalize_trace(); /* ADD_FINALIZER('finalize_trace();') */ #define TRACE(TAG, CODE) TRACE_CODE(if (is_trace_enabled(TAG)) { tout << "-------- [" << TAG << "] " << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << " ---------\n"; CODE tout << "------------------------------------------------\n"; tout.flush(); }) #define STRACE(TAG, CODE) TRACE_CODE(if (is_trace_enabled(TAG)) { CODE tout.flush(); }) #define CTRACE(TAG, COND, CODE) TRACE_CODE(if (is_trace_enabled(TAG) && (COND)) { tout << "-------- [" << TAG << "] " << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << " ---------\n"; CODE tout << "------------------------------------------------\n"; tout.flush(); }) #endif /* TRACE_H_ */ z3-z3-4.4.1/src/util/trail.h000066400000000000000000000204271260446376700154620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: trail.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-02. Revision History: --*/ #ifndef TRAIL_H_ #define TRAIL_H_ #include"obj_hashtable.h" #include"region.h" #include"obj_ref.h" template class trail { public: virtual ~trail() { } virtual void undo(Ctx & ctx) = 0; }; template class value_trail : public trail { T & m_value; T m_old_value; public: value_trail(T & value): m_value(value), m_old_value(value) { } virtual ~value_trail() { } virtual void undo(Ctx & ctx) { m_value = m_old_value; } }; template class reset_flag_trail : public trail { bool & m_value; public: reset_flag_trail(bool & value): m_value(value) { } virtual ~reset_flag_trail() { } virtual void undo(Ctx & ctx) { m_value = false; } }; template class set_ptr_trail : public trail { T * & m_ptr; public: set_ptr_trail(T * & ptr): m_ptr(ptr) { SASSERT(m_ptr == 0); } virtual void undo(Ctx & ctx) { m_ptr = 0; } }; template class restore_size_trail : public trail { vector & m_vector; unsigned m_old_size; public: restore_size_trail(vector & v, unsigned sz): m_vector(v), m_old_size(sz) { } restore_size_trail(vector & v): m_vector(v), m_old_size(v.size()) { } virtual ~restore_size_trail() { } virtual void undo(Ctx & ctx) { m_vector.shrink(m_old_size); } }; template class vector_value_trail : public trail { vector & m_vector; unsigned m_idx; T m_old_value; public: vector_value_trail(vector & v, unsigned idx): m_vector(v), m_idx(idx), m_old_value(v[idx]) { } virtual ~vector_value_trail() { } virtual void undo(Ctx & ctx) { m_vector[m_idx] = m_old_value; } }; template class insert_obj_map : public trail { obj_map& m_map; D* m_obj; public: insert_obj_map(obj_map& t, D* o) : m_map(t), m_obj(o) {} virtual ~insert_obj_map() {} virtual void undo(Ctx & ctx) { m_map.remove(m_obj); } }; template class insert_map : public trail { M& m_map; D m_obj; public: insert_map(M& t, D o) : m_map(t), m_obj(o) {} virtual ~insert_map() {} virtual void undo(Ctx & ctx) { m_map.remove(m_obj); } }; template class push_back_vector : public trail { V & m_vector; public: push_back_vector(V & v): m_vector(v) { } virtual void undo(Ctx & ctx) { m_vector.pop_back(); } }; template class set_vector_idx_trail : public trail { ptr_vector & m_vector; unsigned m_idx; public: set_vector_idx_trail(ptr_vector & v, unsigned idx): m_vector(v), m_idx(idx) { } virtual ~set_vector_idx_trail() { } virtual void undo(Ctx & ctx) { m_vector[m_idx] = 0; } }; template class pop_back_trail : public trail { vector & m_vector; T m_value; public: pop_back_trail(vector & v): m_vector(v), m_value(m_vector.back()) { } virtual void undo(Ctx & ctx) { m_vector.push_back(m_value); } }; template class pop_back2_trail : public trail { vector & m_vector; typedef vector, true> vector_t; unsigned m_index; T m_value; public: pop_back2_trail(vector & v, unsigned index): m_vector(v), m_index(index), m_value(m_vector[index].back()) { } virtual void undo(Ctx & ctx) { m_vector[m_index].push_back(m_value); } }; template class push_back_trail : public trail { vector & m_vector; public: push_back_trail(vector & v): m_vector(v) { } virtual void undo(Ctx & ctx) { m_vector.pop_back(); } }; template class push_back2_trail : public trail { typedef vector, true> vector_t; vector_t & m_vector; unsigned m_index; public: push_back2_trail(vector_t & v, unsigned index) : m_vector(v), m_index(index) { } virtual void undo(Ctx & ctx) { m_vector[m_index].pop_back(); } }; template class set_bitvector_trail : public trail { svector & m_vector; unsigned m_idx; public: set_bitvector_trail(svector & v, unsigned idx): m_vector(v), m_idx(idx) { SASSERT(m_vector[m_idx] == false); m_vector[m_idx] = true; } virtual void undo(Ctx & ctx) { m_vector[m_idx] = false; } }; template class new_obj_trail : public trail { T * m_obj; public: new_obj_trail(T * obj): m_obj(obj) { } virtual void undo(Ctx & ctx) { dealloc(m_obj); } }; template class obj_ref_trail : public trail { obj_ref m_obj; public: obj_ref_trail(obj_ref& obj): m_obj(obj) { } virtual void undo(Ctx & ctx) { m_obj.reset(); } }; template class insert_obj_trail : public trail { obj_hashtable& m_table; T* m_obj; public: insert_obj_trail(obj_hashtable& t, T* o) : m_table(t), m_obj(o) {} virtual ~insert_obj_trail() {} virtual void undo(Ctx & ctx) { m_table.remove(m_obj); } }; template class remove_obj_trail : public trail { obj_hashtable& m_table; T* m_obj; public: remove_obj_trail(obj_hashtable& t, T* o) : m_table(t), m_obj(o) {} virtual ~remove_obj_trail() {} virtual void undo(Ctx & ctx) { m_table.insert(m_obj); } }; template void undo_trail_stack(Ctx & ctx, ptr_vector > & s, unsigned old_size) { SASSERT(old_size <= s.size()); typename ptr_vector >::iterator begin = s.begin() + old_size; typename ptr_vector >::iterator it = s.end(); while (it != begin) { --it; (*it)->undo(ctx); } s.shrink(old_size); } template class trail_stack { Ctx & m_ctx; ptr_vector > m_trail_stack; unsigned_vector m_scopes; region m_region; public: trail_stack(Ctx & c):m_ctx(c) {} ~trail_stack() {} region & get_region() { return m_region; } void reset() { pop_scope(m_scopes.size()); // Undo trail objects stored at lvl 0 (avoid memory leaks if lvl 0 contains new_obj_trail objects). undo_trail_stack(m_ctx, m_trail_stack, 0); } void push_ptr(trail * t) { m_trail_stack.push_back(t); } template void push(TrailObject const & obj) { m_trail_stack.push_back(new (m_region) TrailObject(obj)); } unsigned get_num_scopes() const { return m_scopes.size(); } void push_scope() { m_region.push_scope(); m_scopes.push_back(m_trail_stack.size()); } void pop_scope(unsigned num_scopes) { if (num_scopes == 0) return; unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; unsigned old_size = m_scopes[new_lvl]; undo_trail_stack(m_ctx, m_trail_stack, old_size); m_scopes.shrink(new_lvl); m_region.pop_scope(num_scopes); } }; #endif /* TRAIL_H_ */ z3-z3-4.4.1/src/util/uint_map.h000066400000000000000000000016471260446376700161660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: uint_map.h Abstract: Author: Leonardo de Moura (leonardo) 2010-07-01. Revision History: --*/ #ifndef UINT_MAP_H_ #define UINT_MAP_H_ #include"vector.h" /** \brief Implement a map from unsigned to T * using vectors */ template class uint_map { ptr_vector m_map; public: bool contains(unsigned k) const { return m_map.get(k, 0) != 0; } bool find(unsigned k, T * & v) const { if (k >= m_map.size()) return false; else { v = m_map[k]; return v != 0; } } void insert(unsigned k, T * v) { m_map.reserve(k+1); m_map[k] = v; SASSERT(contains(k)); } void erase(unsigned k) { if (k < m_map.size()) m_map[k] = 0; } void reset() { m_map.reset(); } }; #endif /* UINT_MAP_H_ */ z3-z3-4.4.1/src/util/uint_set.h000066400000000000000000000140431260446376700161760ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: uint_set.h Abstract: Sets of unsigned integers. Author: Leonardo de Moura (leonardo) 2006-12-07. Revision History: --*/ #ifndef UINT_SET_H_ #define UINT_SET_H_ #include"util.h" #include"vector.h" COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); class uint_set : unsigned_vector { public: typedef unsigned data; uint_set() {} uint_set(const uint_set & source) { for (unsigned i = 0; i < source.size(); ++i) { push_back(source[i]); } } ~uint_set() {} void swap(uint_set & other) { unsigned_vector::swap(other); } // return the maximum value that can be stored in the set. unsigned get_max_elem() const { return 32 * size(); } void reset() { unsigned_vector::reset(); } bool empty() const { for (unsigned i = 0; i < size(); i++) { if ((*this)[i] != 0) { return false; } } return true; } void insert(unsigned val) { unsigned idx = val >> 5; if (idx >= size()) { resize(idx+1); } (*this)[idx] |= 1 << (val & 31); } void remove(unsigned val) { unsigned idx = val >> 5; if (idx < size()) { (*this)[val >> 5] &= ~(1 << (val & 31)); } } bool contains(unsigned val) const { unsigned idx = val >> 5; return idx < size() && ((*this)[idx] & (1 << (val & 31))) != 0; } unsigned num_elems() const { unsigned r = 0; for (unsigned i = 0; i < size(); i++) { r += get_num_1bits((*this)[i]); } return r; } // Insert in the this object the elements in the set source. uint_set & operator |=(const uint_set & source) { unsigned source_size = source.size(); if (source_size > size()) { resize(source_size + 1); } for (unsigned i = 0; i < source_size; i++) { (*this)[i] |= source[i]; } return *this; } uint_set& operator &=(const uint_set& source) { unsigned source_size = source.size(); if (source_size < size()) { resize(source_size); } for (unsigned i = 0; i < size(); i++) { (*this)[i] &= source[i]; } return *this; } bool operator==(const uint_set & source) const { unsigned min_size = size(); if (source.size() < min_size) { min_size = source.size(); } for (unsigned i = 0; i < min_size; i++) { if ((*this)[i] != source[i]) { return false; } } for (unsigned i = min_size; i < size(); ++i) { if ((*this)[i]) { return false; } } for (unsigned i = min_size; i < source.size(); ++i) { if (source[i]) { return false; } } return true; } bool operator!=(const uint_set & source) const { return !operator==(source); } // Return true if the this set is a subset of source. bool subset_of(const uint_set & source) const { unsigned min_size = size(); if (source.size() < min_size) { min_size = source.size(); } for (unsigned i = 0; i < min_size; i++) { if (((*this)[i] & ~source[i]) != 0) { return false; } } for (unsigned i = min_size; i < size(); ++i) { if ((*this)[i]) { return false; } } return true; } class iterator { uint_set const* m_set; unsigned m_index; bool invariant() const { return m_index <= m_set->get_max_elem(); } bool at_end() const { return m_index == m_set->get_max_elem(); } void scan_idx() { SASSERT(invariant()); while (!at_end() && !m_set->contains(m_index) && 0 != (m_index & 31)) { ++m_index; } SASSERT(invariant()); } void scan_word() { SASSERT((m_index & 31) == 0); SASSERT(invariant()); unsigned idx = m_index >> 5; while (!at_end() && !(*m_set)[idx]) { ++idx; m_index += 32; } SASSERT(invariant()); } bool contains() const { return m_set->contains(m_index); } void scan() { scan_idx(); if (contains() || at_end()) { return; } scan_word(); if (!at_end() && !contains()) { ++m_index; } scan_idx(); SASSERT(invariant()); } public: iterator(uint_set const& s, bool at_end): m_set(&s), m_index(at_end?s.get_max_elem():0) { scan(); SASSERT(invariant()); } unsigned operator*() const { return m_index; } bool operator==(iterator const& it) const { return m_index == it.m_index; } bool operator!=(iterator const& it) const { return m_index != it.m_index; } iterator & operator++() { ++m_index; scan(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } iterator & operator=(iterator const& other) { m_set = other.m_set; m_index = other.m_index; return *this; } }; iterator const begin() const { return iterator(*this, false); } iterator const end() const { return iterator(*this, true); } }; inline std::ostream & operator<<(std::ostream & target, const uint_set & s) { unsigned n = s.get_max_elem() + 1; target << "{"; bool first = true; for (unsigned i = 0; i < n; i++) { if (s.contains(i)) { if (first) { first = false; } else { target << ", "; } target << i; } } target << "}"; return target; } #endif /* UINT_SET_H_ */ z3-z3-4.4.1/src/util/union_find.h000066400000000000000000000137471260446376700165060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: union_find.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-31. Revision History: --*/ #ifndef UNION_FIND_H_ #define UNION_FIND_H_ #include "trail.h" #include "trace.h" class union_find_default_ctx { public: typedef trail_stack _trail_stack; union_find_default_ctx() : m_stack(*this) {} void unmerge_eh(unsigned, unsigned) {} void merge_eh(unsigned, unsigned, unsigned, unsigned) {} void after_merge_eh(unsigned, unsigned, unsigned, unsigned) {} _trail_stack& get_trail_stack() { return m_stack; } private: _trail_stack m_stack; }; template class union_find { Ctx & m_ctx; trail_stack & m_trail_stack; svector m_find; svector m_size; svector m_next; class mk_var_trail; friend class mk_var_trail; class mk_var_trail : public trail { union_find & m_owner; public: mk_var_trail(union_find & o):m_owner(o) {} virtual ~mk_var_trail() {} virtual void undo(Ctx & ctx) { m_owner.m_find.pop_back(); m_owner.m_size.pop_back(); m_owner.m_next.pop_back(); } }; mk_var_trail m_mk_var_trail; class merge_trail; friend class merge_trail; class merge_trail : public trail { union_find & m_owner; unsigned m_r1; public: merge_trail(union_find & o, unsigned r1):m_owner(o), m_r1(r1) {} virtual ~merge_trail() {} virtual void undo(Ctx & ctx) { m_owner.unmerge(m_r1); } }; void unmerge(unsigned r1) { unsigned r2 = m_find[r1]; TRACE("union_find", tout << "unmerging " << r1 << " " << r2 << "\n";); SASSERT(find(r2) == r2); m_size[r2] -= m_size[r1]; m_find[r1] = r1; std::swap(m_next[r1], m_next[r2]); m_ctx.unmerge_eh(r2, r1); CASSERT("union_find", check_invariant()); } public: union_find(Ctx & ctx):m_ctx(ctx), m_trail_stack(ctx.get_trail_stack()), m_mk_var_trail(*this) {} unsigned mk_var() { unsigned r = m_find.size(); m_find.push_back(r); m_size.push_back(1); m_next.push_back(r); m_trail_stack.push_ptr(&m_mk_var_trail); return r; } unsigned get_num_vars() const { return m_find.size(); } unsigned find(unsigned v) const { while (true) { unsigned new_v = m_find[v]; if (new_v == v) return v; v = new_v; } } unsigned next(unsigned v) const { return m_next[v]; } unsigned size(unsigned v) const { return m_size[find(v)]; } bool is_root(unsigned v) const { return m_find[v] == v; } void merge(unsigned v1, unsigned v2) { unsigned r1 = find(v1); unsigned r2 = find(v2); TRACE("union_find", tout << "merging " << r1 << " " << r2 << "\n";); if (r1 == r2) return; if (m_size[r1] > m_size[r2]) std::swap(r1, r2); m_ctx.merge_eh(r2, r1, v2, v1); m_find[r1] = r2; m_size[r2] += m_size[r1]; std::swap(m_next[r1], m_next[r2]); m_trail_stack.push(merge_trail(*this, r1)); m_ctx.after_merge_eh(r2, r1, v2, v1); CASSERT("union_find", check_invariant()); } // dissolve equivalence class of v // this method cannot be used with backtracking. void dissolve(unsigned v) { unsigned w; do { w = next(v); m_size[v] = 1; m_find[v] = v; m_next[v] = v; } while (w != v); } void display(std::ostream & out) const { unsigned num = get_num_vars(); for (unsigned v = 0; v < num; v++) { out << "v" << v << " --> v" << m_find[v] << " (" << size(v) << ")\n"; } } #ifdef Z3DEBUG bool check_invariant() const { unsigned num = get_num_vars(); for (unsigned v = 0; v < num; v++) { if (is_root(v)) { unsigned curr = v; unsigned sz = 0; do { SASSERT(find(curr) == v); sz++; curr = next(curr); } while (curr != v); SASSERT(m_size[v] == sz); } } return true; } #endif }; class basic_union_find { unsigned_vector m_find; unsigned_vector m_size; unsigned_vector m_next; void ensure_size(unsigned v) { while (v >= get_num_vars()) { mk_var(); } } public: unsigned mk_var() { unsigned r = m_find.size(); m_find.push_back(r); m_size.push_back(1); m_next.push_back(r); return r; } unsigned get_num_vars() const { return m_find.size(); } unsigned find(unsigned v) const { if (v >= get_num_vars()) { return v; } while (true) { unsigned new_v = m_find[v]; if (new_v == v) return v; v = new_v; } } unsigned next(unsigned v) const { if (v >= get_num_vars()) { return v; } return m_next[v]; } bool is_root(unsigned v) const { return v >= get_num_vars() || m_find[v] == v; } void merge(unsigned v1, unsigned v2) { unsigned r1 = find(v1); unsigned r2 = find(v2); if (r1 == r2) return; ensure_size(v1); ensure_size(v2); if (m_size[r1] > m_size[r2]) std::swap(r1, r2); m_find[r1] = r2; m_size[r2] += m_size[r1]; std::swap(m_next[r1], m_next[r2]); } void reset() { m_find.reset(); m_next.reset(); m_size.reset(); } }; #endif /* UNION_FIND_H_ */ z3-z3-4.4.1/src/util/util.cpp000066400000000000000000000057701260446376700156630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: util.cpp Abstract: Useful functions & macros Author: Leonardo de Moura (leonardo) 2006-10-10. Revision History: --*/ #include"util.h" static unsigned g_verbosity_level = 0; void set_verbosity_level(unsigned lvl) { g_verbosity_level = lvl; } unsigned get_verbosity_level() { return g_verbosity_level; } static std::ostream* g_verbose_stream = &std::cerr; void set_verbose_stream(std::ostream& str) { g_verbose_stream = &str; } std::ostream& verbose_stream() { return *g_verbose_stream; } static void (*g_fatal_error_handler)(int) = 0; void fatal_error(int error_code) { if (g_fatal_error_handler) { g_fatal_error_handler(error_code); } else { exit(error_code); } } void set_fatal_error_handler(void (*pfn)(int error_code)) { g_fatal_error_handler = pfn; } unsigned log2(unsigned v) { unsigned r = 0; if (v & 0xFFFF0000) { v >>= 16; r |= 16; } if (v & 0xFF00) { v >>= 8; r |= 8; } if (v & 0xF0) { v >>= 4; r |= 4; } if (v & 0xC) { v >>= 2; r |= 2; } if (v & 0x2) { v >>= 1; r |= 1; } return r; } unsigned uint64_log2(uint64 v) { unsigned r = 0; if (v & 0xFFFFFFFF00000000ull) { v >>= 32; r |= 32; } if (v & 0xFFFF0000) { v >>= 16; r |= 16; } if (v & 0xFF00) { v >>= 8; r |= 8; } if (v & 0xF0) { v >>= 4; r |= 4; } if (v & 0xC) { v >>= 2; r |= 2; } if (v & 0x2) { v >>= 1; r |= 1; } return r; } bool product_iterator_next(unsigned n, unsigned const * sz, unsigned * it) { for (unsigned i = 0; i < n; i++) { it[i]++; if (it[i] < sz[i]) return true; it[i] = 0; } return false; } char const * escaped::end() const { if (m_str == 0) return 0; char const * it = m_str; char const * e = m_str; while (*it) { if (!m_trim_nl || *it != '\n') { ++it; e = it; } else { ++it; } } return e; } void escaped::display(std::ostream & out) const { char const * it = m_str; char const * e = end(); for (; it != e; ++it) { char c = *it; if (c == '"') { out << '\\'; } out << c; if (c == '\n') { for (unsigned i = 0; i < m_indent; i++) out << " "; } } } #ifdef _WINDOWS #ifdef ARRAYSIZE #undef ARRAYSIZE #endif #include #endif void z3_bound_num_procs() { #ifdef _Z3_COMMERCIAL #define Z3_COMMERCIAL_MAX_CORES 4 #ifdef _WINDOWS DWORD_PTR numProcs = (1 << Z3_COMMERCIAL_MAX_CORES) - 1; SetProcessAffinityMask(GetCurrentProcess(), numProcs); #endif #else // Not bounded: Research evaluations are // not reasonable if run with artificial // or hidden throttles. #endif } z3-z3-4.4.1/src/util/util.h000066400000000000000000000202751260446376700153250ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: util.h Abstract: Useful functions & macros Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #ifndef UTIL_H_ #define UTIL_H_ #include"debug.h" #include"memory_manager.h" #include #include #include #ifndef SIZE_MAX #define SIZE_MAX std::numeric_limits::max() #endif #ifndef uint64 typedef unsigned long long uint64; #endif COMPILE_TIME_ASSERT(sizeof(uint64) == 8); #ifndef int64 typedef long long int64; #endif COMPILE_TIME_ASSERT(sizeof(int64) == 8); #ifndef INT64_MIN #define INT64_MIN static_cast(0x8000000000000000ull) #endif #ifndef INT64_MAX #define INT64_MAX static_cast(0x7fffffffffffffffull) #endif #ifndef UINT64_MAX #define UINT64_MAX 0xffffffffffffffffull #endif #ifdef _WINDOWS #define SSCANF sscanf_s #define SPRINTF sprintf_s #else #define SSCANF sscanf #define SPRINTF sprintf #endif #ifdef _WINDOWS // Disable thread local declspec as it seems to not work downlevel. // #define THREAD_LOCAL __declspec(thread) #define THREAD_LOCAL #else #define THREAD_LOCAL #endif inline bool is_power_of_two(unsigned v) { return !(v & (v - 1)) && v; } /** \brief Return the next power of two that is greater than or equal to v. \warning This function returns 0 for v == 0. */ inline unsigned next_power_of_two(unsigned v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } /** \brief Return the position of the most significant bit. */ unsigned log2(unsigned v); unsigned uint64_log2(uint64 v); COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); // Return the number of 1 bits in v. static inline unsigned get_num_1bits(unsigned v) { #ifdef __GNUC__ return __builtin_popcount(v); #else #ifdef Z3DEBUG unsigned c; unsigned v1 = v; for (c = 0; v1; c++) { v1 &= v1 - 1; } #endif v = v - ((v >> 1) & 0x55555555); v = (v & 0x33333333) + ((v >> 2) & 0x33333333); unsigned r = (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; SASSERT(c == r); return r; #endif } // Remark: on gcc, the operators << and >> do not produce zero when the second argument >= 64. // So, I'm using the following two definitions to fix the problem static inline uint64 shift_right(uint64 x, uint64 y) { return y < 64ull ? (x >> y) : 0ull; } static inline uint64 shift_left(uint64 x, uint64 y) { return y < 64ull ? (x << y) : 0ull; } template char (*ArraySizer(T (&)[N]))[N]; // For determining the length of an array. See ARRAYSIZE() macro. This function is never actually called. #ifndef ARRAYSIZE #define ARRAYSIZE(a) sizeof(*ArraySizer(a)) #endif template void display(std::ostream & out, const IT & begin, const IT & end, const char * sep, bool & first) { for(IT it = begin; it != end; ++it) { if (first) { first = false; } else { out << sep; } out << *it; } } template void display(std::ostream & out, const IT & begin, const IT & end, const char * sep = " ") { bool first = true; display(out, begin, end, sep, first); } template struct delete_proc { void operator()(T * ptr) { if (ptr) { dealloc(ptr); } } }; void set_verbosity_level(unsigned lvl); unsigned get_verbosity_level(); std::ostream& verbose_stream(); void set_verbose_stream(std::ostream& str); #define IF_VERBOSE(LVL, CODE) { if (get_verbosity_level() >= LVL) { CODE } } ((void) 0) #ifdef _EXTERNAL_RELEASE #define IF_IVERBOSE(LVL, CODE) ((void) 0) #else #define IF_IVERBOSE(LVL, CODE) { if (get_verbosity_level() >= LVL) { CODE } } ((void) 0) #endif template struct default_eq { typedef T data; bool operator()(const T & e1, const T & e2) const { return e1 == e2; } }; template struct ptr_eq { typedef T * data; bool operator()(T * a1, T * a2) const { return a1 == a2; } }; template struct deref_eq { typedef T * data; bool operator()(T * a1, T * a2) const { return *a1 == *a2; } }; template class scoped_ptr { T * m_ptr; public: scoped_ptr(T * ptr=0): m_ptr(ptr) { } ~scoped_ptr() { if (m_ptr) { dealloc(m_ptr); } } T * operator->() const { return m_ptr; } T * get() const { return m_ptr; } operator bool() const { return m_ptr != 0; } const T & operator*() const { return *m_ptr; } T & operator*() { return *m_ptr; } scoped_ptr & operator=(T * n) { if (m_ptr != n) { if (m_ptr) { dealloc(m_ptr); } m_ptr = n; } return *this; } T * detach() { T* tmp = m_ptr; m_ptr = 0; return tmp; } void swap(scoped_ptr & p) { std::swap(m_ptr, p.m_ptr); } }; template inline std::ostream & operator<<(std::ostream & out, std::pair const & p) { out << "(" << p.first << ", " << p.second << ")"; return out; } template bool has_duplicates(const IT & begin, const IT & end) { for (IT it1 = begin; it1 != end; ++it1) { IT it2 = it1; ++it2; for (; it2 != end; ++it2) { if (*it1 == *it2) { return true; } } } return false; } #ifndef __fallthrough #define __fallthrough #endif #ifndef _WINDOWS #ifndef __declspec #define __declspec(X) #endif #endif template class flet { T & m_ref; T m_old_value; public: flet(T & ref, const T & new_value): m_ref(ref), m_old_value(ref) { m_ref = new_value; } ~flet() { m_ref = m_old_value; } }; template bool compare_arrays(const T * array1, const T * array2, unsigned size) { for (unsigned i = 0; i < size; i++) { if (!(array1[i] == array2[i])) { return false; } } return true; } template void force_ptr_array_size(T & v, unsigned sz) { if (sz > v.size()) { v.resize(sz, 0); } } class random_gen { unsigned m_data; public: random_gen(unsigned seed = 0): m_data(seed) { } void set_seed(unsigned s) { m_data = s; } int operator()() { return ((m_data = m_data * 214013L + 2531011L) >> 16) & 0x7fff; } unsigned operator()(unsigned u) { unsigned r = static_cast((*this)()); return r % u; } static int max_value() { return 0x7fff; } }; template void shuffle(unsigned sz, T * array, random_gen & gen) { int n = sz; while (--n > 0) { int k = gen() % (n + 1); std::swap(array[n], array[k]); } } #ifdef _EXTERNAL_RELEASE #define INTERNAL_CODE(CODE) ((void) 0) #else #define INTERNAL_CODE(CODE) { CODE } ((void) 0) #endif void fatal_error(int error_code); void set_fatal_error_handler(void (*pfn)(int error_code)); /** \brief Iterator for the [0..sz[0]) X [0..sz[1]) X ... X [0..sz[n-1]). it contains the current value. Return true if there is a next element, and store the next element in it. */ bool product_iterator_next(unsigned n, unsigned const * sz, unsigned * it); /** \brief Macro for avoiding error messages. */ #define TRUSTME(cond) if (!cond) { UNREACHABLE(); fatal_error(0); exit(0); } class escaped { char const * m_str; bool m_trim_nl; // if true -> eliminate '\n' in the end of m_str. unsigned m_indent; char const * end() const; public: escaped(char const * str, bool trim_nl = false, unsigned indent = 0):m_str(str), m_trim_nl(trim_nl), m_indent(indent) {} void display(std::ostream & out) const; }; inline std::ostream & operator<<(std::ostream & out, escaped const & s) { s.display(out); return out; } inline size_t megabytes_to_bytes(unsigned mb) { if (mb == UINT_MAX) return SIZE_MAX; unsigned long long b = static_cast(mb) * 1024ull * 1024ull; size_t r = static_cast(b); if (r != b) // overflow r = SIZE_MAX; return r; } void z3_bound_num_procs(); #endif /* UTIL_H_ */ z3-z3-4.4.1/src/util/vector.h000066400000000000000000000263421260446376700156530ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: vector.h Abstract: Dynamic array implementation. Remarks: - Empty arrays consume only sizeof(T *) bytes. - There is the option of disabling the destructor invocation for elements stored in the vector. This is useful for vectors of int. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #ifndef VECTOR_H_ #define VECTOR_H_ #include"debug.h" #include #include #include"memory_manager.h" #include"hash.h" #include"z3_exception.h" // disable warning for constant 'if' expressions. // these are used heavily in templates. #ifdef _MSC_VER #pragma warning(disable:4127) #endif template class vector { #define SIZE_IDX -1 #define CAPACITY_IDX -2 T * m_data; void destroy_elements() { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { it->~T(); } } void free_memory() { memory::deallocate(reinterpret_cast(reinterpret_cast(m_data) - 2)); } void expand_vector() { if (m_data == 0) { SZ capacity = 2; SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(SZ) * 2)); *mem = capacity; mem++; *mem = 0; mem++; m_data = reinterpret_cast(mem); } else { SASSERT(capacity() > 0); SZ old_capacity = reinterpret_cast(m_data)[CAPACITY_IDX]; SZ old_capacity_T = sizeof(T) * old_capacity + sizeof(SZ) * 2; SZ new_capacity = (3 * old_capacity + 1) >> 1; SZ new_capacity_T = sizeof(T) * new_capacity + sizeof(SZ) * 2; if (new_capacity <= old_capacity || new_capacity_T <= old_capacity_T) { throw default_exception("Overflow encountered when expanding vector"); } SZ *mem = (SZ*)memory::reallocate(reinterpret_cast(m_data)-2, new_capacity_T); *mem = new_capacity; m_data = reinterpret_cast(mem + 2); } } void copy_core(vector const & source) { SZ size = source.size(); SZ capacity = source.capacity(); SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(SZ) * 2)); *mem = capacity; mem++; *mem = size; mem++; m_data = reinterpret_cast(mem); const_iterator it = source.begin(); iterator it2 = begin(); SASSERT(it2 == m_data); const_iterator e = source.end(); for (; it != e; ++it, ++it2) { new (it2) T(*it); } } void destroy() { if (m_data) { if (CallDestructors) { destroy_elements(); } free_memory(); } } public: typedef T data; typedef T * iterator; typedef const T * const_iterator; vector(): m_data(0) { } vector(SZ s) { SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * s + sizeof(SZ) * 2)); *mem = s; mem++; *mem = s; mem++; m_data = reinterpret_cast(mem); // initialize elements iterator it = begin(); iterator e = end(); for (; it != e; ++it) { new (it) T(); } } vector(SZ s, T const & elem): m_data(0) { resize(s, elem); } vector(vector const & source): m_data(0) { if (source.m_data) { copy_core(source); } SASSERT(size() == source.size()); } vector(SZ s, T const * data): m_data(0) { for (SZ i = 0; i < s; i++) { push_back(data[i]); } } ~vector() { destroy(); } void finalize() { destroy(); m_data = 0; } vector & operator=(vector const & source) { if (this == &source) { return *this; } destroy(); if (source.m_data) { copy_core(source); } else { m_data = 0; } return *this; } void reset() { if (m_data) { if (CallDestructors) { destroy_elements(); } reinterpret_cast(m_data)[SIZE_IDX] = 0; } } bool empty() const { return m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == 0; } SZ size() const { if (m_data == 0) { return 0; } return reinterpret_cast(m_data)[SIZE_IDX]; } SZ capacity() const { if (m_data == 0) { return 0; } return reinterpret_cast(m_data)[CAPACITY_IDX]; } iterator begin() { return m_data; } iterator end() { return m_data + size(); } const_iterator begin() const { return m_data; } const_iterator end() const { return m_data + size(); } void set_end(iterator it) { if (m_data) { SZ new_sz = static_cast(it - m_data); if (CallDestructors) { iterator e = end(); for(; it != e; ++it) { it->~T(); } } reinterpret_cast(m_data)[SIZE_IDX] = new_sz; } else { SASSERT(it == 0); } } T & operator[](SZ idx) { SASSERT(idx < size()); return m_data[idx]; } T const & operator[](SZ idx) const { SASSERT(idx < size()); return m_data[idx]; } T & get(SZ idx) { SASSERT(idx < size()); return m_data[idx]; } T const & get(SZ idx) const { SASSERT(idx < size()); return m_data[idx]; } void set(SZ idx, T const & val) { SASSERT(idx < size()); m_data[idx] = val; } T & back() { SASSERT(!empty()); return operator[](size() - 1); } T const & back() const { SASSERT(!empty()); return operator[](size() - 1); } void pop_back() { SASSERT(!empty()); if (CallDestructors) { back().~T(); } reinterpret_cast(m_data)[SIZE_IDX]--; } void push_back(T const & elem) { if (m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { expand_vector(); } new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(elem); reinterpret_cast(m_data)[SIZE_IDX]++; } void insert(T const & elem) { push_back(elem); } void erase(iterator pos) { SASSERT(pos >= begin() && pos < end()); iterator prev = pos; ++pos; iterator e = end(); for(; pos != e; ++pos, ++prev) { *prev = *pos; } reinterpret_cast(m_data)[SIZE_IDX]--; } void erase(T const & elem) { iterator it = std::find(begin(), end(), elem); if (it != end()) { erase(it); } } void shrink(SZ s) { if (m_data) { SASSERT(s <= reinterpret_cast(m_data)[SIZE_IDX]); if (CallDestructors) { iterator it = m_data + s; iterator e = end(); for(; it != e; ++it) { it->~T(); } } reinterpret_cast(m_data)[SIZE_IDX] = s; } else { SASSERT(s == 0); } } void resize(SZ s, T const & elem=T()) { SZ sz = size(); if (s <= sz) { shrink(s); return; } while (s > capacity()) { expand_vector(); } SASSERT(m_data != 0); reinterpret_cast(m_data)[SIZE_IDX] = s; iterator it = m_data + sz; iterator end = m_data + s; for(; it != end; ++it) { new (it) T(elem); } } void append(vector const & other) { for(SZ i = 0; i < other.size(); ++i) { push_back(other[i]); } } void append(SZ sz, T const * data) { for(SZ i = 0; i < sz; ++i) { push_back(data[i]); } } T * c_ptr() const { return m_data; } void swap(vector & other) { std::swap(m_data, other.m_data); } void reverse() { SZ sz = size(); for (SZ i = 0; i < sz/2; ++i) { std::swap(m_data[i], m_data[sz-i-1]); } } void fill(T const & elem) { iterator i = begin(); iterator e = end(); for (; i != e; ++i) { *i = elem; } } bool contains(T const & elem) const { const_iterator it = begin(); const_iterator e = end(); for (; it != e; ++it) { if (*it == elem) { return true; } } return false; } // set pos idx with elem. If idx >= size, then expand using default. void setx(SZ idx, T const & elem, T const & d) { if (idx >= size()) { resize(idx+1, d); } m_data[idx] = elem; } // return element at position idx, if idx >= size, then return default T const & get(SZ idx, T const & d) const { if (idx >= size()) { return d; } return m_data[idx]; } void reserve(SZ s, T const & d = T()) { if (s > size()) resize(s, d); } }; template class ptr_vector : public vector { public: ptr_vector():vector() {} ptr_vector(unsigned s):vector(s) {} ptr_vector(unsigned s, T * elem):vector(s, elem) {} ptr_vector(ptr_vector const & source):vector(source) {} ptr_vector(unsigned s, T * const * data):vector(s, const_cast(data)) {} }; template class svector : public vector { public: svector():vector() {} svector(SZ s):vector(s) {} svector(SZ s, T const & elem):vector(s, elem) {} svector(svector const & source):vector(source) {} svector(SZ s, T const * data):vector(s, data) {} }; typedef svector int_vector; typedef svector unsigned_vector; typedef svector char_vector; typedef svector signed_char_vector; typedef svector double_vector; template struct vector_hash_tpl { Hash m_hash; typedef Vec data; unsigned operator()(data const& v, unsigned idx) const { return m_hash(v[idx]); } vector_hash_tpl(Hash const& h = Hash()):m_hash(h) {} unsigned operator()(data const& v) const { if (v.empty()) { return 778; } return get_composite_hash, vector_hash_tpl>(v, v.size()); } }; template struct vector_hash : public vector_hash_tpl > {}; template struct svector_hash : public vector_hash_tpl > {}; #endif /* VECTOR_H_ */ z3-z3-4.4.1/src/util/warning.cpp000066400000000000000000000100631260446376700163420ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: warning.cpp Abstract: Support for warning messages. Author: Leonardo de Moura (leonardo) 2006-12-01. Revision History: --*/ #include #include #include "error_codes.h" #include "util.h" #include "buffer.h" #include "vector.h" #ifdef _WINDOWS #define PRF sprintf_s #define VPRF vsprintf_s void myInvalidParameterHandler( const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) { // no-op } #define BEGIN_ERR_HANDLER() \ _invalid_parameter_handler oldHandler, newHandler; \ newHandler = myInvalidParameterHandler; \ oldHandler = _set_invalid_parameter_handler(newHandler); \ _CrtSetReportMode(_CRT_ASSERT, 0); \ #define END_ERR_HANDLER() \ _set_invalid_parameter_handler(oldHandler); // _invalid_parameter_handler oldHandler, newHandler; // newHandler = myInvalidParameterHandler; // oldHandler = _set_invalid_parameter_handler(newHandler); // Disable the message box for assertions. #else #define PRF snprintf #define VPRF vsnprintf #define BEGIN_ERR_HANDLER() {} #define END_ERR_HANDLER() {} #endif static bool g_warning_msgs = true; static bool g_use_std_stdout = false; static std::ostream* g_error_stream = 0; static std::ostream* g_warning_stream = 0; static bool g_show_error_msg_prefix = true; void send_warnings_to_stdout(bool flag) { g_use_std_stdout = flag; } void enable_warning_messages(bool flag) { g_warning_msgs = flag; } void set_error_stream(std::ostream* strm) { g_error_stream = strm; } void set_warning_stream(std::ostream* strm) { g_warning_stream = strm; } void disable_error_msg_prefix() { g_show_error_msg_prefix = false; } #if 0 // [Leo]: Do we need this? static void string2ostream(std::ostream& out, char const* msg) { svector buff; buff.resize(10); BEGIN_ERR_HANDLER(); while (true) { int nc = PRF(buff.c_ptr(), buff.size(), msg); if (nc >= 0 && nc < static_cast(buff.size())) break; // success buff.resize(buff.size()*2 + 1); } END_ERR_HANDLER(); out << buff.c_ptr(); } #endif void format2ostream(std::ostream & out, char const* msg, va_list args) { svector buff; #if !defined(_WINDOWS) && defined(_AMD64_) // see comment below. buff.resize(1024); #else buff.resize(128); #endif BEGIN_ERR_HANDLER(); while (true) { int nc = VPRF(buff.c_ptr(), buff.size(), msg, args); #if !defined(_WINDOWS) && defined(_AMD64_) // For some strange reason, on Linux 64-bit version, va_list args is reset by vsnprintf. // Z3 crashes when trying to use va_list args again. // Hack: I truncate the message instead of expanding the buffer to make sure that // va_list args is only used once. END_ERR_HANDLER(); if (nc < 0) { // vsnprintf didn't work, so we just print the msg out << msg; return; } if (nc >= static_cast(buff.size())) { // truncate the message buff[buff.size() - 1] = 0; } out << buff.c_ptr(); return; #else if (nc >= 0 && nc < static_cast(buff.size())) break; // success buff.resize(buff.size()*2 + 1); #endif } END_ERR_HANDLER(); out << buff.c_ptr(); } void print_msg(std::ostream * out, const char* prefix, const char* msg, va_list args) { if (out) { *out << prefix; format2ostream(*out, msg, args); *out << "\n"; out->flush(); } else { FILE * f = g_use_std_stdout ? stdout : stderr; fprintf(f, "%s", prefix); vfprintf(f, msg, args); fprintf(f, "\n"); fflush(f); }; } void warning_msg(const char * msg, ...) { if (g_warning_msgs) { va_list args; va_start(args, msg); print_msg(g_warning_stream, "WARNING: ", msg, args); va_end(args); } } z3-z3-4.4.1/src/util/warning.h000066400000000000000000000017431260446376700160140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: warning.h Abstract: Support for warning messages. Author: Leonardo de Moura (leonardo) 2006-12-01. Revision History: --*/ #ifndef WARNING_H_ #define WARNING_H_ #include #include void send_warnings_to_stdout(bool flag); void enable_warning_messages(bool flag); void set_error_stream(std::ostream* strm); void set_warning_stream(std::ostream* strm); void warning_msg(const char * msg, ...); void disable_error_msg_prefix(); void format2ostream(std::ostream& out, char const* fmt, va_list args); class warning_displayer { const char * m_msg; bool m_displayed; public: warning_displayer(const char * msg): m_msg(msg), m_displayed(false) { } void sign() { if (!m_displayed) { warning_msg(m_msg); m_displayed = true; } } void reset() { m_displayed = false; } }; #endif /* WARNING_H_ */ z3-z3-4.4.1/src/util/z3_exception.cpp000066400000000000000000000033271260446376700173140ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_exception.cpp Abstract: Generic Z3 exception Author: Leonardo (leonardo) 2012-04-12 Notes: --*/ #include #include #include #include"z3_exception.h" #include"warning.h" #include"error_codes.h" #include"debug.h" unsigned z3_exception::error_code() const { return ERR_OK; } bool z3_exception::has_error_code() const { return error_code() != ERR_OK; } z3_error::z3_error(unsigned error_code):m_error_code(error_code) { SASSERT(error_code != 0); } char const * z3_error::msg() const { switch (m_error_code) { case ERR_MEMOUT: return "out of memory"; case ERR_TIMEOUT: return "timeout"; case ERR_PARSER: return "parser error"; case ERR_UNSOUNDNESS: return "unsoundess"; case ERR_INCOMPLETENESS: return "incompleteness"; case ERR_INI_FILE: return "invalid INI file"; case ERR_NOT_IMPLEMENTED_YET: return "not implemented yet"; case ERR_OPEN_FILE: return "open file"; case ERR_CMD_LINE: return "invalid command line"; case ERR_INTERNAL_FATAL: return "internal error"; case ERR_TYPE_CHECK: return "type error"; case ERR_ALLOC_EXCEEDED: return "number of configured allocations exceeded"; default: return "unknown error"; } } unsigned z3_error::error_code() const { return m_error_code; } default_exception::default_exception(fmt, char const* msg, ...) { std::stringstream out; va_list args; va_start(args, msg); format2ostream(out, msg, args); va_end(args); m_msg = out.str(); } default_exception::default_exception(std::string const & msg): m_msg(msg) { } char const * default_exception::msg() const { return m_msg.c_str(); } z3-z3-4.4.1/src/util/z3_exception.h000066400000000000000000000015771260446376700167660ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_exception.h Abstract: Generic Z3 exception Author: Leonardo (leonardo) 2011-04-28 Notes: --*/ #ifndef Z3_EXCEPTION_H_ #define Z3_EXCEPTION_H_ #include class z3_exception { public: virtual ~z3_exception() {} virtual char const * msg() const = 0; virtual unsigned error_code() const; bool has_error_code() const; }; class z3_error : public z3_exception { unsigned m_error_code; public: z3_error(unsigned error_code); virtual char const * msg() const; virtual unsigned error_code() const; }; class default_exception : public z3_exception { std::string m_msg; public: struct fmt {}; default_exception(std::string const& msg); default_exception(fmt, char const* msg, ...); virtual ~default_exception() {} virtual char const * msg() const; }; #endif z3-z3-4.4.1/src/util/z3_omp.h000066400000000000000000000012151260446376700155500ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: z3_omp.h Abstract: Wrapper for OMP functions and data-structures Author: Leonardo (leonardo) 2012-01-05 Notes: --*/ #ifndef Z3_OMP_H_ #define Z3_OMP_H_ #ifndef _NO_OMP_ #include #else #define omp_in_parallel() false #define omp_set_num_threads(SZ) ((void)0) #define omp_get_thread_num() 0 #define omp_get_num_procs() 1 #define omp_set_nested(V) ((void)0) #define omp_init_nest_lock(L) ((void) 0) #define omp_destroy_nest_lock(L) ((void) 0) #define omp_set_nest_lock(L) ((void) 0) #define omp_unset_nest_lock(L) ((void) 0) struct omp_nest_lock_t { }; #endif #endif