├── .gitignore ├── .pylintrc ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── TODO.txt ├── __init__.py ├── examples ├── functions.py ├── mcsat │ ├── Makefile │ ├── integer.py │ ├── integer.ys │ ├── mcsat.c │ ├── mcsat.py │ ├── v.py │ ├── v.ys │ └── yiceslib.py ├── readme_qf_bv.py ├── readme_qf_lra.py ├── readme_qf_nra.py └── sudoku │ ├── 4-extreme-9x9-sudokus.pdf │ ├── Solver.py │ ├── SudokuLib.py │ ├── puzzle1.pdf │ ├── sudoku.py │ ├── sudoku.ys │ ├── sudoku_api.py │ ├── sudoku_cores.py │ └── sudoku_generator.py ├── setup.py ├── test ├── __init__.py ├── context_test.py ├── delegate_test.py ├── dimacs_test.py ├── error_test.py ├── interpolationcontext_test.py ├── model_test.py ├── terms_test.py ├── types_test.py └── utils.py ├── test_api ├── __init__.py ├── context_test.py ├── error_test.py ├── model_test.py ├── terms_test.py ├── types_test.py └── vector_test.py ├── yices ├── Census.py ├── Config.py ├── Constructors.py ├── Context.py ├── Delegates.py ├── Dimacs.py ├── InterpolationContext.py ├── Model.py ├── Parameters.py ├── Profiler.py ├── Status.py ├── StringBuilder.py ├── Terms.py ├── Types.py ├── Yices.py ├── YicesException.py ├── Yvals.py └── __init__.py └── yices_api.py /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | yices.egg-info 4 | .pytest_cache 5 | **/*.cache 6 | **/*.pyc 7 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | # iam: generated by 2 | # 3 | # pylint --generate-rcfile > .pylintrc 4 | # 5 | # then customized to ignore overly zealous complaints. 6 | 7 | [MASTER] 8 | 9 | #disable bad-whitespace 10 | disable=C0326 11 | 12 | 13 | # Specify a configuration file. 14 | #rcfile= 15 | 16 | # Python code to execute, usually for sys.path manipulation such as 17 | # pygtk.require(). 18 | #init-hook= 19 | 20 | # Add files or directories to the blacklist. They should be base names, not 21 | # paths. 22 | ignore=CVS 23 | 24 | # Add files or directories matching the regex patterns to the blacklist. The 25 | # regex matches against base names, not paths. 26 | ignore-patterns= 27 | 28 | # Pickle collected data for later comparisons. 29 | persistent=yes 30 | 31 | # List of plugins (as comma separated values of python modules names) to load, 32 | # usually to register additional checkers. 33 | load-plugins= 34 | 35 | # Use multiple processes to speed up Pylint. 36 | jobs=1 37 | 38 | # Allow loading of arbitrary C extensions. Extensions are imported into the 39 | # active Python interpreter and may run arbitrary code. 40 | unsafe-load-any-extension=no 41 | 42 | # A comma-separated list of package or module names from where C extensions may 43 | # be loaded. Extensions are loading into the active Python interpreter and may 44 | # run arbitrary code 45 | extension-pkg-whitelist=ctypes 46 | 47 | # Allow optimization of some AST trees. This will activate a peephole AST 48 | # optimizer, which will apply various small optimizations. For instance, it can 49 | # be used to obtain the result of joining multiple strings with the addition 50 | # operator. Joining a lot of strings can lead to a maximum recursion error in 51 | # Pylint and this flag can prevent that. It has one side effect, the resulting 52 | # AST will be different than the one from reality. This option is deprecated 53 | # and it will be removed in Pylint 2.0. 54 | optimize-ast=no 55 | 56 | 57 | [MESSAGES CONTROL] 58 | 59 | # Only show warnings with the listed confidence levels. Leave empty to show 60 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED 61 | confidence= 62 | 63 | # Enable the message, report, category or checker with the given id(s). You can 64 | # either give multiple identifier separated by comma (,) or put this option 65 | # multiple time (only on the command line, not in the configuration file where 66 | # it should appear only once). See also the "--disable" option for examples. 67 | #enable= 68 | 69 | # Disable the message, report, category or checker with the given id(s). You 70 | # can either give multiple identifiers separated by comma (,) or put this 71 | # option multiple times (only on the command line, not in the configuration 72 | # file where it should appear only once).You can also use "--disable=all" to 73 | # disable everything first and then reenable specific checks. For example, if 74 | # you want to run only the similarities checker, you can use "--disable=all 75 | # --enable=similarities". If you want to run only the classes checker, but have 76 | # no Warning level messages displayed, use"--disable=all --enable=classes 77 | # --disable=W" 78 | disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating 79 | 80 | 81 | [REPORTS] 82 | 83 | # Set the output format. Available formats are text, parseable, colorized, msvs 84 | # (visual studio) and html. You can also give a reporter class, eg 85 | # mypackage.mymodule.MyReporterClass. 86 | output-format=text 87 | 88 | # Put messages in a separate file for each module / package specified on the 89 | # command line instead of printing them on stdout. Reports (if any) will be 90 | # written in a file name "pylint_global.[txt|html]". This option is deprecated 91 | # and it will be removed in Pylint 2.0. 92 | files-output=no 93 | 94 | # Tells whether to display a full report or only the messages 95 | reports=yes 96 | 97 | # Python expression which should return a note less than 10 (10 is the highest 98 | # note). You have access to the variables errors warning, statement which 99 | # respectively contain the number of errors / warnings messages and the total 100 | # number of statements analyzed. This is used by the global evaluation report 101 | # (RP0004). 102 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 103 | 104 | # Template used to display messages. This is a python new-style format string 105 | # used to format the message information. See doc for all details 106 | #msg-template= 107 | 108 | 109 | [BASIC] 110 | 111 | # Good variable names which should always be accepted, separated by a comma 112 | good-names=i,j,k,ex,Run,_,a,m,n 113 | 114 | # Bad variable names which should always be refused, separated by a comma 115 | bad-names=foo,bar,baz,toto,tutu,tata 116 | 117 | # Colon-delimited sets of names that determine each other's naming style when 118 | # the name regexes allow several styles. 119 | name-group= 120 | 121 | # Include a hint for the correct naming format with invalid-name 122 | include-naming-hint=no 123 | 124 | # List of decorators that produce properties, such as abc.abstractproperty. Add 125 | # to this list to register other decorators that produce valid properties. 126 | property-classes=abc.abstractproperty 127 | 128 | # Regular expression matching correct function names 129 | function-rgx=[a-zA-Z_][a-zA-Z0-9_]{0,40}$ 130 | 131 | # Naming hint for function names 132 | function-name-hint=[a-zA-Z_][a-zA-Z0-9_]{1,40}$ 133 | 134 | # Regular expression matching correct variable names 135 | variable-rgx=[a-z_][a-z0-9_]{1,40}$ 136 | 137 | # Naming hint for variable names 138 | variable-name-hint=[a-z_][a-z0-9_]{2,40}$ 139 | 140 | # Regular expression matching correct constant names 141 | const-rgx=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$ 142 | 143 | # Naming hint for constant names 144 | const-name-hint=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$ 145 | 146 | # Regular expression matching correct attribute names 147 | attr-rgx=[a-z_][a-z0-9_]{2,40}$ 148 | 149 | # Naming hint for attribute names 150 | attr-name-hint=[a-z_][a-z0-9_]{2,40}$ 151 | 152 | # Regular expression matching correct argument names 153 | argument-rgx=[a-z_][a-z0-9_]{0,40}$ 154 | 155 | # Naming hint for argument names 156 | argument-name-hint=[a-z_][a-z0-9_]{0,40}$ 157 | 158 | # Regular expression matching correct class attribute names 159 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{1,40}|(__.*__))$ 160 | 161 | # Naming hint for class attribute names 162 | class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{1,40}|(__.*__))$ 163 | 164 | # Regular expression matching correct inline iteration names 165 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ 166 | 167 | # Naming hint for inline iteration names 168 | inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ 169 | 170 | # Regular expression matching correct class names 171 | class-rgx=[a-zA-Z0-9_]+$ 172 | 173 | # Naming hint for class names 174 | class-name-hint=[a-zA-Z0-9_]+$ 175 | 176 | # Regular expression matching correct module names 177 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 178 | 179 | # Naming hint for module names 180 | module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 181 | 182 | # Regular expression matching correct method names 183 | method-rgx=[a-z_][a-z0-9_]{1,40}$ 184 | 185 | # Naming hint for method names 186 | method-name-hint=[a-z_][a-z0-9_]{2,40}$ 187 | 188 | # Regular expression which should only match function or class names that do 189 | # not require a docstring. 190 | no-docstring-rgx=^. 191 | 192 | # Minimum line length for functions/classes that require docstrings, shorter 193 | # ones are exempt. 194 | docstring-min-length=-1 195 | 196 | 197 | [ELIF] 198 | 199 | # Maximum number of nested blocks for function / method body 200 | max-nested-blocks=5 201 | 202 | 203 | [FORMAT] 204 | 205 | # Maximum number of characters on a single line. 206 | max-line-length=300 207 | 208 | # Regexp for a line that is allowed to be longer than the limit. 209 | ignore-long-lines=^\s*(# )??$ 210 | 211 | # Allow the body of an if to be on the same line as the test if there is no 212 | # else. 213 | single-line-if-stmt=no 214 | 215 | # List of optional constructs for which whitespace checking is disabled. `dict- 216 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 217 | # `trailing-comma` allows a space between comma and closing bracket: (a, ). 218 | # `empty-line` allows space-only lines. 219 | no-space-check=trailing-comma,dict-separator 220 | 221 | # Maximum number of lines in a module 222 | max-module-lines=6000 223 | 224 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 225 | # tab). 226 | indent-string=' ' 227 | 228 | # Number of spaces of indent required inside a hanging or continued line. 229 | indent-after-paren=4 230 | 231 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 232 | expected-line-ending-format= 233 | 234 | 235 | [LOGGING] 236 | 237 | # Logging modules to check that the string format arguments are in logging 238 | # function parameter format 239 | logging-modules=logging 240 | 241 | 242 | [MISCELLANEOUS] 243 | 244 | # List of note tags to take in consideration, separated by a comma. 245 | notes=FIXME,XXX,TODO 246 | 247 | 248 | [SIMILARITIES] 249 | 250 | # Minimum lines number of a similarity. 251 | min-similarity-lines=4 252 | 253 | # Ignore comments when computing similarities. 254 | ignore-comments=yes 255 | 256 | # Ignore docstrings when computing similarities. 257 | ignore-docstrings=yes 258 | 259 | # Ignore imports when computing similarities. 260 | ignore-imports=no 261 | 262 | 263 | [SPELLING] 264 | 265 | # Spelling dictionary name. Available dictionaries: none. To make it working 266 | # install python-enchant package. 267 | spelling-dict= 268 | 269 | # List of comma separated words that should not be checked. 270 | spelling-ignore-words= 271 | 272 | # A path to a file that contains private dictionary; one word per line. 273 | spelling-private-dict-file= 274 | 275 | # Tells whether to store unknown words to indicated private dictionary in 276 | # --spelling-private-dict-file option instead of raising a message. 277 | spelling-store-unknown-words=no 278 | 279 | 280 | [TYPECHECK] 281 | 282 | # Tells whether missing members accessed in mixin class should be ignored. A 283 | # mixin class is detected if its name ends with "mixin" (case insensitive). 284 | ignore-mixin-members=yes 285 | 286 | # List of module names for which member attributes should not be checked 287 | # (useful for modules/projects where namespaces are manipulated during runtime 288 | # and thus existing member attributes cannot be deduced by static analysis. It 289 | # supports qualified module names, as well as Unix pattern matching. 290 | ignored-modules= 291 | 292 | # List of class names for which member attributes should not be checked (useful 293 | # for classes with dynamically set attributes). This supports the use of 294 | # qualified names. 295 | ignored-classes=optparse.Values,thread._local,_thread._local 296 | 297 | # List of members which are set dynamically and missed by pylint inference 298 | # system, and so shouldn't trigger E1101 when accessed. Python regular 299 | # expressions are accepted. 300 | generated-members= 301 | 302 | # List of decorators that produce context managers, such as 303 | # contextlib.contextmanager. Add to this list to register other decorators that 304 | # produce valid context managers. 305 | contextmanager-decorators=contextlib.contextmanager 306 | 307 | 308 | [VARIABLES] 309 | 310 | # Tells whether we should check for unused import in __init__ files. 311 | init-import=no 312 | 313 | # A regular expression matching the name of dummy variables (i.e. expectedly 314 | # not used). 315 | dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy 316 | 317 | # List of additional names supposed to be defined in builtins. Remember that 318 | # you should avoid to define new builtins when possible. 319 | additional-builtins= 320 | 321 | # List of strings which can identify a callback function by name. A callback 322 | # name must start or end with one of those strings. 323 | callbacks=cb_,_cb 324 | 325 | # List of qualified module names which can have objects that can redefine 326 | # builtins. 327 | redefining-builtins-modules=six.moves,future.builtins 328 | 329 | 330 | [CLASSES] 331 | 332 | # List of method names used to declare (i.e. assign) instance attributes. 333 | defining-attr-methods=__init__,__new__,setUp 334 | 335 | # List of valid names for the first argument in a class method. 336 | valid-classmethod-first-arg=cls 337 | 338 | # List of valid names for the first argument in a metaclass class method. 339 | valid-metaclass-classmethod-first-arg=mcs 340 | 341 | # List of member names, which should be excluded from the protected access 342 | # warning. 343 | exclude-protected=_asdict,_fields,_replace,_source,_make, __gmpz_init, __gmpq_init, __gmpz_set_str, __gmpq_set_str, __gmpz_set_si, __gmpq_set_si,__gmpq_canonicalize 344 | 345 | 346 | [DESIGN] 347 | 348 | # Maximum number of arguments for function / method 349 | max-args=7 350 | 351 | # Argument names that match this expression will be ignored. Default to name 352 | # with leading underscore 353 | ignored-argument-names=_.* 354 | 355 | # Maximum number of locals for function / method body 356 | max-locals=15 357 | 358 | # Maximum number of return / yield for function / method body 359 | max-returns=10 360 | 361 | # Maximum number of branch for function / method body 362 | max-branches=12 363 | 364 | # Maximum number of statements in function / method body 365 | max-statements=50 366 | 367 | # Maximum number of parents for a class (see R0901). 368 | max-parents=7 369 | 370 | # Maximum number of attributes for a class (see R0902). 371 | max-attributes=7 372 | 373 | # Minimum number of public methods for a class (see R0903). 374 | min-public-methods=0 375 | 376 | # Maximum number of public methods for a class (see R0904). 377 | max-public-methods=200 378 | 379 | # Maximum number of boolean expressions in a if statement 380 | max-bool-expr=5 381 | 382 | 383 | [IMPORTS] 384 | 385 | # Deprecated modules which should not be used, separated by a comma 386 | deprecated-modules=regsub,TERMIOS,Bastion,rexec 387 | 388 | # Create a graph of every (i.e. internal and external) dependencies in the 389 | # given file (report RP0402 must not be disabled) 390 | import-graph= 391 | 392 | # Create a graph of external dependencies in the given file (report RP0402 must 393 | # not be disabled) 394 | ext-import-graph= 395 | 396 | # Create a graph of internal dependencies in the given file (report RP0402 must 397 | # not be disabled) 398 | int-import-graph= 399 | 400 | # Force import order to recognize a module as part of the standard 401 | # compatibility libraries. 402 | known-standard-library= 403 | 404 | # Force import order to recognize a module as part of a third party library. 405 | known-third-party=enchant 406 | 407 | # Analyse import fallback blocks. This can be used to support both Python 2 and 408 | # 3 compatible code, which means that the block might have code that exists 409 | # only in one or another interpreter, leading to false positives when analysed. 410 | analyse-fallback-blocks=no 411 | 412 | 413 | [EXCEPTIONS] 414 | 415 | # Exceptions that will emit a warning when being caught. Defaults to 416 | # "Exception" 417 | overgeneral-exceptions=Exception 418 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ian A Mason 4 | Copyright (c) 2024 Ahmed Irfan 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include yices_api.py 2 | include yices 3 | 4 | recursive-exclude test * 5 | exclude doc 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #iam's makefile; maybe migrate some targets to the main Makefile when done. 2 | 3 | all: help 4 | 5 | 6 | help: 7 | @echo '' 8 | @echo 'Here are the targets:' 9 | @echo '' 10 | @echo 'To test : "make check"' 11 | @echo 'To develop python : "make develop"' 12 | @echo 'To install : "make install"' 13 | @echo 'To publish : "make publish"' 14 | @echo 'To pylint (errors) : "make lint"' 15 | @echo 'To pylint (all) : "make lint_all"' 16 | @echo '' 17 | @echo 'To check for missing docstrings : "make doc_check"' 18 | @echo '' 19 | 20 | PYTEST ?= $(shell which pytest) 21 | 22 | check_pytest: 23 | ifeq ($(PYTEST),) 24 | $(error either you do not have pytest installed, or you need to set the env var PYTEST to your installation of pytest) 25 | endif 26 | 27 | 28 | check: check_pytest 29 | $(PYTEST) -rP test_api test 30 | 31 | 32 | #local editable install for developing 33 | develop: 34 | pip install -e . 35 | 36 | iam: 37 | python -m pip install -e . 38 | 39 | dist: clean 40 | python setup.py bdist_wheel 41 | 42 | # If you need to push this project again, 43 | # INCREASE the version number in yices/version.py, 44 | # otherwise the server will give you an error. 45 | 46 | publish: dist 47 | python -m twine upload --repository pypi dist/* 48 | 49 | install: 50 | pip install . 51 | 52 | clean: 53 | rm -rf *.pyc *~ __pycache__ */*.pyc */*~ */__pycache__ */*/*.pyc */*/*~ */*/__pycache_ 54 | 55 | 56 | 57 | PYLINT = $(shell which pylint) 58 | 59 | 60 | check_lint: 61 | ifeq ($(PYLINT),) 62 | $(error lint target requires pylint) 63 | endif 64 | 65 | 66 | lint: check_lint 67 | # for detecting just errors: 68 | @ $(PYLINT) -E yices_api.py yices/*.py test/*.py test_api/*.py examples/sudoku/sudoku.py 69 | 70 | lint_all: check_lint 71 | # for detecting more than just errors: 72 | @ $(PYLINT) --disable=missing-docstring --disable=global-statement --disable=duplicate-code --rcfile=.pylintrc yices_api.py yices/*.py test/*.py test_api/*.py examples/sudoku/*.py 73 | 74 | doc_check: 75 | # a target to help me add docstrings where they are required. 76 | @ $(PYLINT) --disable=global-statement --disable=duplicate-code --rcfile=.pylintrc yices_api.py yices/*.py 77 | 78 | .PHONY: test lint lint_all check_lint doc_check 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: MIT](https://img.shields.io/badge/License-MIT-blueviolet.svg)](https://opensource.org/licenses/MIT) 2 | [![PyPI version](https://badge.fury.io/py/yices.svg)](https://badge.fury.io/py/yices) 3 | [![PyPI Statistics](https://img.shields.io/pypi/dm/yices.svg)](https://pypistats.org/packages/yices) 4 | 5 | # Python Bindings for Yices 2 6 | 7 | As the name indicates, this provides a Python interface to the Yices SMT Solvers. 8 | 9 | ## Installation 10 | 11 | Install the [Yices SMT Solver](http://yices.csl.sri.com/) first, then, install 12 | the python language bindings with: 13 | ``` 14 | pip install yices 15 | ``` 16 | 17 | This will install two python packages and a binary. 18 | 19 | - yices_api 20 | 21 | This gives you access to the low-level Yices API from Python. To use this API, you will need to be familiar 22 | with `ctypes` and know the Yices C API, see [yices.h](https://github.com/SRI-CSL/yices2/blob/master/src/include/yices.h). 23 | Unless you really need it, we recommend that you use the Pythonesque API below. 24 | 25 | - yices 26 | 27 | This a more Pythonesque API that bridges the gap between the low level yices_api and the python user. It provides 28 | Python classes to represent Yices context, models, configurations, etc. 29 | 30 | - yices_python_info 31 | 32 | The binary `yices_python_info` prints information about the system: 33 | 34 | ``` 35 | > yices_python_info 36 | Python Yices Bindings. Version 1.1.3 37 | Yices library loaded from /usr/local/lib/libyices.dylib 38 | Version: 2.6.2 39 | Architecture: x86_64-apple-darwin18.7.0 40 | Build mode: debug 41 | Build date: 2020-04-27 42 | MCSat support: yes 43 | Thread safe: no 44 | ``` 45 | 46 | ## Examples 47 | 48 | The following three examples show how to use the yices module. 49 | 50 | #### Linear Real Arithmetic 51 | 52 | ``` 53 | from yices import * 54 | 55 | cfg = Config() 56 | cfg.default_config_for_logic('QF_LRA') 57 | ctx = Context(cfg) 58 | 59 | real_t = Types.real_type() 60 | x = Terms.new_uninterpreted_term(real_t, 'x') 61 | y = Terms.new_uninterpreted_term(real_t, 'y') 62 | 63 | fmla0 = Terms.parse_term('(> (+ x y) 0)') 64 | fmla1 = Terms.parse_term('(or (< x 0) (< y 0))') 65 | 66 | ctx.assert_formulas([fmla0, fmla1]) 67 | 68 | status = ctx.check_context() 69 | 70 | if status == Status.SAT: 71 | model = Model.from_context(ctx, 1) 72 | model_string = model.to_string(80, 100, 0) 73 | print(model_string) 74 | xval = model.get_value(x) 75 | yval = model.get_value(y) 76 | print('x = {0}, y = {1}'.format(xval, yval)) 77 | ``` 78 | The complete file can be found [here.](https://github.com/SRI-CSL/yices2_python_bindings/blob/master/examples/readme_qf_lra.py) 79 | Running this example should show this: 80 | 81 | ``` 82 | > python examples/readme_qf_lra.py 83 | (= x 2) 84 | (= y -1) 85 | x = 2, y = -1 86 | ``` 87 | 88 | #### Bit-Vectors 89 | 90 | ``` 91 | from yices import * 92 | 93 | cfg = Config() 94 | cfg.default_config_for_logic('QF_BV') 95 | ctx = Context(cfg) 96 | 97 | bv32_t = Types.bv_type(32) 98 | x = Terms.new_uninterpreted_term(bv32_t, 'x') 99 | y = Terms.new_uninterpreted_term(bv32_t, 'y') 100 | 101 | 102 | zero = Terms.bvconst_integer(32, 0) 103 | fmla0 = Terms.bvsgt_atom(x, zero) 104 | fmla1 = Terms.bvsgt_atom(y, zero) 105 | fmla2 = Terms.bvslt_atom(Terms.bvadd(x, y), x) 106 | 107 | ctx.assert_formulas([fmla0, fmla1, fmla2]) 108 | 109 | status = ctx.check_context() 110 | 111 | if status == Status.SAT: 112 | model = Model.from_context(ctx, 1) 113 | model_string = model.to_string(80, 100, 0) 114 | print(model_string) 115 | xval = model.get_value(x) 116 | yval = model.get_value(y) 117 | print('x = {0}\ny = {1}'.format(xval, yval)) 118 | 119 | ``` 120 | The complete file is [here.](https://github.com/SRI-CSL/yices2_python_bindings/blob/master/examples/readme_qf_bv.py) 121 | 122 | #### Non-Linear Real Arithmetic 123 | 124 | ``` 125 | from yices import * 126 | 127 | cfg = Config() 128 | cfg.default_config_for_logic('QF_NRA') 129 | ctx = Context(cfg) 130 | 131 | real_t = Types.real_type() 132 | x = Terms.new_uninterpreted_term(real_t, 'x') 133 | y = Terms.new_uninterpreted_term(real_t, 'y') 134 | 135 | fmla0 = Terms.parse_term('(= (+ (* x x) (* y y)) 1)') 136 | fmla1 = Terms.parse_term('(= x (* 2 y))') 137 | fmla2 = Terms.parse_term('(> x 0)') 138 | 139 | ctx.assert_formulas([fmla0, fmla1, fmla2]) 140 | 141 | status = ctx.check_context() 142 | 143 | if status == Status.SAT: 144 | model = Model.from_context(ctx, 1) 145 | model_string = model.to_string(80, 100, 0) 146 | print(model_string) 147 | xval = model.get_value(x) 148 | yval = model.get_value(y) 149 | print('x = {0}, y = {1}'.format(xval, yval)) 150 | ``` 151 | The complete file is [here.](https://github.com/SRI-CSL/yices2_python_bindings/blob/master/examples/readme_qf_nra.py) 152 | 153 | ### More Examples 154 | 155 | Directory [test](https://github.com/SRI-CSL/yices2_python_bindings/tree/master/test) 156 | contains tests of the API routines. 157 | 158 | #### Sudoku 159 | 160 | A more advanced example is in directory [sudoku](https://github.com/SRI-CSL/yices2_python_bindings/tree/master/examples/sudoku). 161 | It shows three different ways of solving the same sudoku puzzle using Yices: 162 | 163 | - `sudoku.ys` is a Yices input file 164 | 165 | - `sudoku.py` does the same thing using the Python `yices` API 166 | 167 | - `sudoku_api.py` does it using the low-level `yices_api` module and `ctypes` 168 | 169 | ### SudokuSensei 170 | 171 | We keep a GUI-based Sudoku solver written using the Yices Python API in a separate 172 | [repository](https://github.com/ianamason/SudokuSensei). 173 | 174 | #### MC-SAT 175 | 176 | Another example in [mcsat](https://github.com/SRI-CSL/yices2_python_bindings/tree/master/examples/mcsat) 177 | demonstrates simple use of Yices' non-linear capabilites. Because this example requires the libpoly library, 178 | the python code uses the low-level API. 179 | 180 | 181 | ## Details 182 | 183 | The `yices` Python API introduces different classes to represent Yices objects such as 184 | contexts, models, configurations, and search parameters. Term and type constructors are 185 | implemented as static methods of the Python classes `Terms` and `Types`, respectively. 186 | We do not wrap the Yices notions of terms and types into Python classes. Just as in the C-API, 187 | terms and types are represented as integer in Python. 188 | 189 | To use the API, it is sufficient to just import the `yices` module: 190 | ``` 191 | from yices import * 192 | ``` 193 | This will automatically load the `libyices` dynamic library. 194 | You can also import incrementally if needed: 195 | ``` 196 | from yices.Config import Config 197 | from yices.Context import Context 198 | from yices.Constructors import Constructor 199 | from yices.Model import Model 200 | from yices.Parameters import Parameters 201 | from yices.Status import Status 202 | from yices.Types import Types 203 | from yices.Terms import Terms 204 | from yices.YicesException import YicesException 205 | from yices.Yices import Yices 206 | from yices.Yvals import Yval 207 | ``` 208 | 209 | 210 | Most functions in the C-API have a corresponding Python method of the same name, except 211 | where this would clash with Python's reserved words. To avoid such a clash, we prepend the 212 | function names with 'y'. Currently, this affects a few functions in the `Terms` class: 213 | ``` 214 | Terms.yand([t0, ...., tN]) 215 | Terms.yor([t0, ...., tN]) 216 | Terms.ynot(t0) 217 | Terms.ylambda(variables, body) 218 | ``` 219 | 220 | 221 | ## Incompatibility with the pip yices package version 1.0.8 222 | 223 | We have made incompatible changes to the low-level `yices_api` module. In our previous version 224 | (pip package version 1.0.8), low-level operations raised exception on error. In the current 225 | version (pip package version 1.1.0), we have changed this to return an error code. 226 | 227 | We have also changed the module names. What used to be module `yices` in version 1.0.8 is 228 | now called `yices_api`. So to keep using the low-level Python API, you have to change 229 | ``` 230 | import yices 231 | ``` 232 | to 233 | ``` 234 | import yices_api 235 | ``` 236 | and similar variations of the `import` statement. 237 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | 2 | [X] 1. Move exceptions out of yices_api and into yices. yices_api should not throw exceptions. 3 | yices should throw them instead. 4 | 5 | [] 2. doc strings in yices to reflect this. do the yices_api doc strings need TLC? 6 | 7 | [] 3. Solve the gmp and libpoly conundrums, maybe ask Dejan? 8 | 9 | [X] 4. Add tests for the yices package. 10 | 11 | [X] 5. Fix the `lint_all` complaints. 12 | 13 | [] 6. Investigate the new type annotation mechanism. 14 | 15 | [] 7. Implement the Limits class (and uses) in BD's Java bindings. 16 | ctypes quietly truncates 64 to 32 so we need to make sure we know 17 | when this happens. 18 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SRI-CSL/yices2_python_bindings/c1136bc48032ca5a59d534f9950f6e21343d767d/__init__.py -------------------------------------------------------------------------------- /examples/functions.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | 4 | from yices import * 5 | 6 | 7 | def define_const(name, ytype, defn=None): 8 | '''Tries to emulate yices define_term 9 | (see eval_define_term in yices2/src/parser_utils/term_stack2) 10 | ''' 11 | if defn is None: 12 | term = Terms.new_uninterpreted_term(ytype) 13 | Terms.set_name(term, name) 14 | return term 15 | # Have a defn 16 | if isinstance(defn, basestring): 17 | term = Terms.parse_term(defn) 18 | else: 19 | term = defn 20 | term_type = Terms.type_of_term(term) 21 | if not Types.is_subtype(term_type, ytype): 22 | raise YicesException(msg='incompatible sort in definition') 23 | Terms.set_name(term, name) 24 | return term 25 | 26 | def assert_formula(formula, ctx): 27 | if isinstance(formula, basestring): 28 | formula = Terms.parse_term(formula) 29 | ctx.assert_formula(formula) 30 | 31 | 32 | 33 | cfg = Config() 34 | ctx = Context(cfg) 35 | param = Parameters() 36 | param.default_params_for_context(ctx) 37 | global bool_t, int_t, real_t 38 | bool_t = Types.bool_type() 39 | int_t = Types.int_type() 40 | real_t = Types.real_type() 41 | 42 | 43 | funtype = Types.new_function_type([int_t, bool_t, real_t], real_t) 44 | ftystr = Types.to_string(funtype, 100, 80, 0) 45 | Types.print_to_fd(1, funtype, 100, 80, 0) 46 | #assertEqual(ftystr, '(-> int bool real real)') 47 | fun1 = define_const('fun1', funtype) 48 | b1 = define_const('b1', bool_t) 49 | i1 = define_const('i1', int_t) 50 | r1 = define_const('r1', real_t) 51 | assert_formula('(> (fun1 i1 b1 r1) (fun1 (+ i1 1) (not b1) (- r1 i1)))', ctx) 52 | print(ctx.check_context(param) == Status.SAT) 53 | mdl = Model.from_context(ctx, 1) 54 | mdlstr = mdl.to_string(80, 100, 0) 55 | print(mdlstr) 56 | print('(= b1 false)\n(= i1 1463)\n(= r1 -579)\n(function fun1\n (type (-> int bool real real))\n (= (fun1 1463 false -579) 1)\n (= (fun1 1464 true -2042) 0)\n (default 2))') 57 | fun1val = mdl.get_value(fun1) 58 | print(fun1val((1463, False, -579))) 59 | print(fun1val((1464, True, -2042))) 60 | print(fun1val((1462, True, -2041))) 61 | 62 | 63 | 64 | cfg.dispose() 65 | ctx.dispose() 66 | param.dispose() 67 | Yices.exit() 68 | -------------------------------------------------------------------------------- /examples/mcsat/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: clean mcsat python 3 | 4 | 5 | mcsat: mcsat.c 6 | ${CC} mcsat.c -o mcsat -lyices -lpoly 7 | ./mcsat 8 | 9 | python: mcsat.py 10 | ./mcsat.py 11 | 12 | PYLINT = $(shell which pylint) 13 | 14 | check_lint: 15 | ifeq ($(PYLINT),) 16 | $(error lint target requires pylint) 17 | endif 18 | 19 | 20 | lint: check_lint 21 | # for detecting just errors: 22 | @ $(PYLINT) -E mcsat.py 23 | 24 | clean: 25 | rm -f *~ mcsat 26 | -------------------------------------------------------------------------------- /examples/mcsat/integer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from ctypes import ( c_int64 ) 4 | 5 | from yices_api import ( 6 | 7 | 8 | ) 9 | 10 | from yiceslib import (term_to_string, declare_real_var, declare_integer_var, make_context) 11 | 12 | """ 13 | 14 | iv7:Integer > (0).Integer 15 | and 16 | ( iv8:Integer === iv1:Integer * iv7:Integer 17 | and 18 | ( iv3:Integer - iv8:Integer === iv5:Integer 19 | and 20 | ( iv5:Integer >= (0).Integer 21 | and ( iv9:Integer === iv1:Integer * iv7:Integer 22 | and ( iv4:Integer - iv9:Integer === iv6:Integer 23 | and 24 | ( iv6:Integer >= (0).Integer 25 | and ( iv1:Integer === (1).Integer 26 | and ( iv2:Integer === (0).Integer 27 | and ( iv3:Integer === (100).Integer 28 | and ( iv4:Integer === (100).Integer 29 | and (true).Boolean)))))))))) 30 | 31 | simplifies by hand to: 32 | 33 | iv7:Integer > 0 34 | and 35 | iv8:Integer === iv1:Integer * iv7:Integer 36 | and 37 | iv3:Integer - iv8:Integer === iv5:Integer 38 | and 39 | iv5:Integer >= 0 40 | and 41 | iv9:Integer === iv1:Integer * iv7:Integer 42 | and 43 | iv4:Integer - iv9:Integer === iv6:Integer 44 | and 45 | iv6:Integer >= 0 46 | and 47 | iv1:Integer === 1 48 | and 49 | iv2:Integer === 0 50 | and 51 | iv3:Integer === 100 52 | and 53 | iv4:Integer === 100 54 | 55 | and translates to: 56 | 57 | (and (> x7 0) 58 | (= x8 (* x1 x2)) 59 | (= x5 (- x3 x8)) 60 | (> x5 0) 61 | (= x9 (* x1 x7)) 62 | (= x6 (- x4 x9)) 63 | (>= x6 0) 64 | (= x1 1) 65 | (= x2 0) 66 | (= x3 100) 67 | (= x4 100)) 68 | 69 | """ 70 | 71 | 72 | def solve_problem(): 73 | problem = """ 74 | (and (> x7 0) 75 | (= x8 (* x1 x2)) 76 | (= x5 (- x3 x8)) 77 | (> x5 0) 78 | (= x9 (* x1 x7)) 79 | (= x6 (- x4 x9)) 80 | (>= x6 0) 81 | (= x1 1) 82 | (= x2 0) 83 | (= x3 100) 84 | (= x4 100) 85 | ) 86 | """ 87 | variables = [None] * 10 88 | values = [None] * 10 89 | 90 | for i in range(1, 10): 91 | variables[i] = declare_integer_var('x{0}'.format(i)) 92 | 93 | p = yices_parse_term(problem) 94 | s = term_to_string(p) 95 | print 'Assertion: {0}\n'.format(s) 96 | 97 | ctx = make_context() 98 | yices_assert_formula(ctx, p) 99 | 100 | status = yices_check_context(ctx, None) 101 | if status != STATUS_SAT: 102 | print 'Test failed: status = {0} != STATUS_SAT\n'.format(status) 103 | 104 | print 'Satisfiable!\n' 105 | 106 | model = yices_get_model(ctx, 1) 107 | 108 | print "Model:\n" 109 | yices_pp_model_fd(1, model, 80, 20, 0) 110 | 111 | 112 | 113 | print "\nValues of variables:\n" 114 | 115 | i64 = c_int64() 116 | 117 | for i in range(1, 10): 118 | yices_get_int64_value(model, variables[i], i64) 119 | values[i] = i64.value 120 | print "x{0} = {1}".format(i, values[i]) 121 | 122 | # cleanup 123 | yices_free_model(model) 124 | yices_free_context(ctx) 125 | 126 | 127 | 128 | def main(args): 129 | if yices_has_mcsat(): 130 | yices_init() 131 | solve_problem() 132 | yices_exit() 133 | 134 | print '\nbye\n' 135 | 136 | 137 | 138 | if __name__ == '__main__': 139 | sys.exit(main(sys.argv)) 140 | -------------------------------------------------------------------------------- /examples/mcsat/integer.ys: -------------------------------------------------------------------------------- 1 | ;;yices --logic=QF_NRA integer.ys 2 | 3 | (define x1 :: int) 4 | (define x2 :: int) 5 | (define x3 :: int) 6 | (define x4 :: int) 7 | (define x5 :: int) 8 | (define x6 :: int) 9 | (define x7 :: int) 10 | (define x8 :: int) 11 | (define x9 :: int) 12 | 13 | 14 | (assert 15 | (and (> x7 0) 16 | (= x8 (* x1 x2)) 17 | (= x5 (- x3 x8)) 18 | (> x5 0) 19 | (= x9 (* x1 x7)) 20 | (= x6 (- x4 x9)) 21 | (>= x6 0) 22 | (= x1 1) 23 | (= x2 0) 24 | (= x3 100) 25 | (= x4 100) 26 | ) 27 | ) 28 | 29 | (check) 30 | (show-model) 31 | 32 | -------------------------------------------------------------------------------- /examples/mcsat/mcsat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Yices SMT Solver. 3 | * Copyright (C) 2017 SRI International. 4 | * 5 | * Yices is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Yices is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Yices. If not, see . 17 | */ 18 | 19 | /* 20 | * EXAMPLE USE OF THE MCSAT SOLVER 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include "yices.h" 32 | 33 | /* 34 | * Print an error message then exit 35 | */ 36 | static void print_error(void) { 37 | char *s; 38 | 39 | s = yices_error_string(); 40 | fprintf(stderr, "Yices error: %s\n", s); 41 | yices_free_string(s); 42 | exit(1); 43 | } 44 | 45 | /* 46 | * Convert to string 47 | */ 48 | static char *term_to_string(term_t term) { 49 | char *s; 50 | 51 | s = yices_term_to_string(term, 80, 20, 0); 52 | if (s == NULL) print_error(); 53 | return s; 54 | } 55 | 56 | /* 57 | * Create a real variable. 58 | */ 59 | static term_t declare_var(const char *name) { 60 | term_t x; 61 | 62 | x = yices_new_uninterpreted_term(yices_real_type()); 63 | if (x < 0) print_error(); 64 | yices_set_term_name(x, name); 65 | return x; 66 | } 67 | 68 | 69 | /* 70 | * Create a context for QF_NRA 71 | */ 72 | static context_t *make_context(void) { 73 | ctx_config_t *cfg; 74 | context_t *ctx; 75 | int32_t code; 76 | 77 | /* 78 | * We set the logic to QF_NRA and mode to "one-shot" 79 | * (one-shot is currently required when MCSAT is used). 80 | */ 81 | cfg = yices_new_config(); 82 | code = yices_default_config_for_logic(cfg, "QF_NRA"); 83 | if (code < 0) print_error(); 84 | code = yices_set_config(cfg, "mode", "one-shot"); 85 | if (code < 0) print_error(); 86 | 87 | ctx = yices_new_context(cfg); 88 | if (ctx == NULL) print_error(); 89 | yices_free_config(cfg); 90 | 91 | return ctx; 92 | } 93 | 94 | 95 | /* 96 | * Print the value of term t in mdl as an algebraic number 97 | */ 98 | static void show_algebraic_value(model_t *mdl, term_t t) { 99 | lp_algebraic_number_t n; 100 | int32_t code; 101 | 102 | code = yices_get_algebraic_number_value(mdl, t, &n); 103 | if (code < 0) print_error(); 104 | lp_algebraic_number_print(&n, stdout); 105 | fflush(stdout); 106 | lp_algebraic_number_destruct(&n); 107 | } 108 | 109 | 110 | static void test_mcsat(void) { 111 | context_t *ctx; 112 | model_t *mdl; 113 | term_t x, p; 114 | char *s; 115 | int32_t code; 116 | smt_status_t status; 117 | double root; 118 | 119 | x = declare_var("x"); 120 | p = yices_parse_term("(= (* x x) 2)"); 121 | if (p < 0) print_error(); 122 | 123 | s = term_to_string(p); 124 | printf("Assertion: %s\n", s); 125 | fflush(stdout); 126 | yices_free_string(s); 127 | 128 | ctx = make_context(); 129 | code = yices_assert_formula(ctx, p); 130 | if (code < 0) print_error(); 131 | 132 | status = yices_check_context(ctx, NULL); 133 | if (status != STATUS_SAT) { 134 | fprintf(stderr, "Test failed: status != STATUS_SAT\n"); 135 | exit(1); 136 | } 137 | 138 | printf("Satisfiable\n"); 139 | mdl = yices_get_model(ctx, true); 140 | if (mdl == NULL) print_error(); 141 | printf("Model:\n"); 142 | yices_pp_model(stdout, mdl, 80, 20, 0); 143 | printf("\n"); 144 | 145 | // test: get value as a double 146 | code = yices_get_double_value(mdl, x, &root); 147 | if (code < 0) print_error(); 148 | printf("double value of x = %f\n", root); 149 | 150 | // test use the lp_algebraic_number interface 151 | printf("algebraic value of x = "); 152 | show_algebraic_value(mdl, x); 153 | printf("\n"); 154 | 155 | printf("Test succeeded\n"); 156 | 157 | // cleanup 158 | yices_free_model(mdl); 159 | yices_free_context(ctx); 160 | } 161 | 162 | int main(void) { 163 | if (yices_has_mcsat()) { 164 | yices_init(); 165 | test_mcsat(); 166 | yices_exit(); 167 | } else { 168 | fprintf(stderr, "MCSAT is not supported by this Yices build\n"); 169 | } 170 | 171 | return 0; 172 | } 173 | -------------------------------------------------------------------------------- /examples/mcsat/mcsat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | 5 | from yices_api import ( 6 | yices_parse_term, 7 | yices_has_mcsat, 8 | yices_init, 9 | yices_exit, 10 | lp_algebraic_number_t, 11 | yices_get_algebraic_number_value, 12 | yices_assert_formula, 13 | yices_check_context, 14 | yices_get_model, 15 | yices_free_model, 16 | yices_free_context, 17 | yices_pp_model_fd, 18 | STATUS_SAT 19 | ) 20 | 21 | from yiceslib import (term_to_string, declare_real_var, declare_integer_var, make_context) 22 | 23 | 24 | 25 | def show_algebraic_value(model, term): 26 | alg = lp_algebraic_number_t() 27 | yices_get_algebraic_number_value(model, term, alg) 28 | # FIXME: libpoly python API is not part of yices.py 29 | #lp_algebraic_number_print(alg, 1) # 1 is stdout 30 | #lp_algebraic_number_destruct(alg) 31 | 32 | 33 | def test_mcsat(): 34 | x = declare_real_var('x'); 35 | p = yices_parse_term('(= (* x x) 2)') 36 | s = term_to_string(p) 37 | print 'Assertion: {0}\n'.format(s) 38 | ctx = make_context() 39 | yices_assert_formula(ctx, p) 40 | status = yices_check_context(ctx, None) 41 | if status != STATUS_SAT: 42 | print 'Test failed: status = {0} != STATUS_SAT\n'.format(status) 43 | 44 | print 'Satisfiable!\n' 45 | 46 | model = yices_get_model(ctx, 1) 47 | 48 | print "Model:\n" 49 | yices_pp_model_fd(1, model, 80, 20, 0) 50 | 51 | 52 | #print "algebraic value of x = " 53 | #show_algebraic_value(model, x) 54 | #print "\n" 55 | 56 | print "Test succeeded\n" 57 | 58 | # cleanup 59 | yices_free_model(model) 60 | yices_free_context(ctx) 61 | 62 | 63 | 64 | def main(args): 65 | if yices_has_mcsat(): 66 | yices_init() 67 | test_mcsat() 68 | yices_exit() 69 | 70 | print 'bye' 71 | 72 | 73 | 74 | if __name__ == '__main__': 75 | sys.exit(main(sys.argv)) 76 | -------------------------------------------------------------------------------- /examples/mcsat/v.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from ctypes import ( c_int64 ) 4 | 5 | from yices_api import * 6 | 7 | from yiceslib import (term_to_string, declare_real_var, declare_integer_var, make_context) 8 | 9 | # 10 | # Work in progress: Not clear (currently impossible?) how to do an EF using the API. 11 | # 12 | 13 | """ 14 | Formula: 15 | the negation of: 16 | Forall: 17 | tt(10) t(11)... tt(24), tw(1), tw(2) 18 | Exists 19 | tt(1010) tt(1011) ... tt(1024) 20 | Formula body 21 | tt(24) === tt(1024) 22 | and 23 | (tt(22) === tt(1022) 24 | and 25 | (tt(21) === tt(1021) 26 | and 27 | (tt(19) === tt(1019) 28 | and 29 | (tt(16) === tt(1016) 30 | and 31 | (tt(15) === tt(1015) 32 | and 33 | (tt(14) === tt(1014) 34 | and 35 | (tt(13) === tt(1013) 36 | and 37 | (tt(12) === tt(1012) 38 | and 39 | (tt(10) === tt(1010) 40 | and 41 | (tt(24) === tt(22) + tw(1) 42 | and 43 | (tt(24) >= (0/1).Real 44 | and 45 | (tt(24) >= tt(23) 46 | and 47 | (tt(23) >= tt(22) 48 | and 49 | (tt(22) >= tt(21) 50 | and 51 | (tt(21) >= tt(20) 52 | and 53 | (tt(20) >= tt(19) 54 | and 55 | (tt(19) === tt(16) + tw(1) + tw(2) 56 | and 57 | (tt(19) >= (0/1).Real 58 | and 59 | (tt(19) >= tt(18) 60 | and 61 | (tt(18) >= tt(17) 62 | and 63 | (tt(17) >= tt(16) 64 | and 65 | (tt(16) >= tt(15) 66 | and 67 | (tt(15) >= tt(14) 68 | and 69 | (tt(14) >= tt(13) 70 | and 71 | (tt(13) >= tt(12) 72 | and 73 | (tt(12) >= tt(11) 74 | and 75 | (tt(11) >= tt(10) 76 | and 77 | ((0/1).Real >= (0/1).Real 78 | and 79 | tt(10) >= (0/1).Real 80 | and 81 | (tw(1) >= (1/1).Real 82 | and 83 | (tw(2) >= (4/1).Real 84 | and 85 | (true).Boolean)))))))))))))))))))) 86 | 87 | and 88 | 89 | (tt(1024) === tt(1022) + tw(1) 90 | and 91 | (tt(1024) >= (0/1).Real 92 | and 93 | (tt(1024) >= tt(1023) 94 | and 95 | (tt(1023) >= tt(1022) 96 | and 97 | (tt(1022) >= tt(1021) 98 | and 99 | (tt(1021) >= tt(1020) 100 | and 101 | (tt(1020) >= tt(1019) 102 | and 103 | (tt(1019) === tt(1016) + tw(1) + tw(2) 104 | and 105 | (tt(1019) >= (0/1).Real 106 | and 107 | (tt(1019) >= tt(1018) 108 | and 109 | (tt(1018) >= tt(1017) 110 | and 111 | (tt(1017) >= tt(1016) 112 | and 113 | (tt(1016) >= tt(1015) 114 | and 115 | (tt(1015) >= tt(1014) 116 | and 117 | (tt(1014) >= tt(1013) 118 | and 119 | (tt(1013) >= tt(1012) 120 | and 121 | (tt(1012) >= tt(1011) 122 | and 123 | (tt(1011) >= tt(1010) 124 | and 125 | ((0/1).Real >= (0/1).Real 126 | and 127 | tt(10) >= (0/1).Real 128 | and 129 | (tw(1) >= (1/1).Real 130 | and 131 | (tw(2) >= (4/1).Real 132 | and 133 | (true).Boolean))))))))))))))))))))))))))))))) 134 | 135 | 136 | see v.ys or below for the translation. 137 | 138 | 139 | 140 | """ 141 | 142 | def solve_problem(): 143 | problem = """ 144 | 145 | (forall (tt1010 :: real 146 | tt1011 :: real 147 | tt1012 :: real 148 | tt1013 :: real 149 | tt1014 :: real 150 | tt1015 :: real 151 | tt1016 :: real 152 | tt1017 :: real 153 | tt1018 :: real 154 | tt1019 :: real 155 | tt1020 :: real 156 | tt1021 :: real 157 | tt1022 :: real 158 | tt1023 :: real 159 | tt1024 :: real 160 | ) 161 | (not 162 | (and 163 | (= tt24 tt1024) 164 | (= tt22 tt1022) 165 | (= tt21 tt1021) 166 | (= tt19 tt1019) 167 | (= tt16 tt1016) 168 | (= tt15 tt1015) 169 | (= tt14 tt1014) 170 | (= tt13 tt1013) 171 | (= tt12 tt1012) 172 | (= tt10 tt1010) 173 | (= tt24 (+ tt22 tw1)) 174 | (>= tt24 0) 175 | (>= tt24 tt23) 176 | (>= tt23 tt22) 177 | (>= tt22 tt21) 178 | (>= tt21 tt20) 179 | (>= tt20 tt19) 180 | (= tt19 (+ tt16 tw1 tw2)) 181 | (>= tt19 0) 182 | (>= tt19 tt18) 183 | (>= tt18 tt17) 184 | (>= tt17 tt16) 185 | (>= tt16 tt15) 186 | (>= tt15 tt14) 187 | (>= tt14 tt13) 188 | (>= tt13 tt12) 189 | (>= tt12 tt11) 190 | (>= tt11 tt10) 191 | (>= 0 0) 192 | (>= tt10 0) 193 | (>= tw1 1) 194 | (>= tw2 4) 195 | (= tt1024 (+ tt1022 tw1)) 196 | (>= tt1024 0) 197 | (>= tt1024 tt1023) 198 | (>= tt1023 tt1022) 199 | (>= tt1022 tt1021) 200 | (>= tt1021 tt1020) 201 | (>= tt1020 tt1019) 202 | (= tt1019 (+ tt1016 tw1 tw2)) 203 | (>= tt1019 0) 204 | (>= tt1019 tt1018) 205 | (>= tt1018 tt1017) 206 | (>= tt1017 tt1016) 207 | (>= tt1016 tt1015) 208 | (>= tt1015 tt1014) 209 | (>= tt1014 tt1013) 210 | (>= tt1013 tt1012) 211 | (>= tt1012 tt1011) 212 | (>= tt1011 tt1010) 213 | (>= 0 0) 214 | (>= tt10 0) 215 | (>= tw1 1) 216 | (>= tw2 4) 217 | ) 218 | ) 219 | ) 220 | """ 221 | 222 | 223 | for i in range(10, 25): 224 | declare_real_var('tt{0}'.format(i)) 225 | 226 | for i in range(1, 3): 227 | declare_real_var('tw{0}'.format(i)) 228 | 229 | p = yices_parse_term(problem) 230 | s = term_to_string(p) 231 | print 'Assertion: {0}\n'.format(s) 232 | 233 | ctx = make_context() 234 | yices_assert_formula(ctx, p) 235 | 236 | status = yices_check_context(ctx, None) 237 | if status != STATUS_SAT: 238 | print 'Test failed: status = {0} != STATUS_SAT\n'.format(status) 239 | 240 | print 'Satisfiable!\n' 241 | 242 | model = yices_get_model(ctx, 1) 243 | 244 | print "Model:\n" 245 | yices_pp_model_fd(1, model, 80, 20, 0) 246 | 247 | # cleanup 248 | yices_free_model(model) 249 | yices_free_context(ctx) 250 | 251 | 252 | 253 | def main(args): 254 | if yices_has_mcsat(): 255 | yices_init() 256 | solve_problem() 257 | yices_exit() 258 | 259 | print '\nbye\n' 260 | 261 | 262 | 263 | if __name__ == '__main__': 264 | sys.exit(main(sys.argv)) 265 | -------------------------------------------------------------------------------- /examples/mcsat/v.ys: -------------------------------------------------------------------------------- 1 | ;;yices --mode=ef v.ys 2 | 3 | (define tt10 :: real) 4 | (define tt11 :: real) 5 | (define tt12 :: real) 6 | (define tt13 :: real) 7 | (define tt14 :: real) 8 | (define tt15 :: real) 9 | (define tt16 :: real) 10 | (define tt17 :: real) 11 | (define tt18 :: real) 12 | (define tt19 :: real) 13 | (define tt20 :: real) 14 | (define tt21 :: real) 15 | (define tt22 :: real) 16 | (define tt23 :: real) 17 | (define tt24 :: real) 18 | (define tw1 :: real) 19 | (define tw2 :: real) 20 | 21 | 22 | 23 | 24 | (assert 25 | (forall (tt1010 :: real 26 | tt1011 :: real 27 | tt1012 :: real 28 | tt1013 :: real 29 | tt1014 :: real 30 | tt1015 :: real 31 | tt1016 :: real 32 | tt1017 :: real 33 | tt1018 :: real 34 | tt1019 :: real 35 | tt1020 :: real 36 | tt1021 :: real 37 | tt1022 :: real 38 | tt1023 :: real 39 | tt1024 :: real 40 | ) 41 | (not 42 | (and 43 | (= tt24 tt1024) 44 | (= tt22 tt1022) 45 | (= tt21 tt1021) 46 | (= tt19 tt1019) 47 | (= tt16 tt1016) 48 | (= tt15 tt1015) 49 | (= tt14 tt1014) 50 | (= tt13 tt1013) 51 | (= tt12 tt1012) 52 | (= tt10 tt1010) 53 | (= tt24 (+ tt22 tw1)) 54 | (>= tt24 0) 55 | (>= tt24 tt23) 56 | (>= tt23 tt22) 57 | (>= tt22 tt21) 58 | (>= tt21 tt20) 59 | (>= tt20 tt19) 60 | (= tt19 (+ tt16 tw1 tw2)) 61 | (>= tt19 0) 62 | (>= tt19 tt18) 63 | (>= tt18 tt17) 64 | (>= tt17 tt16) 65 | (>= tt16 tt15) 66 | (>= tt15 tt14) 67 | (>= tt14 tt13) 68 | (>= tt13 tt12) 69 | (>= tt12 tt11) 70 | (>= tt11 tt10) 71 | (>= 0 0) 72 | (>= tt10 0) 73 | (>= tw1 1) 74 | (>= tw2 4) 75 | (= tt1024 (+ tt1022 tw1)) 76 | (>= tt1024 0) 77 | (>= tt1024 tt1023) 78 | (>= tt1023 tt1022) 79 | (>= tt1022 tt1021) 80 | (>= tt1021 tt1020) 81 | (>= tt1020 tt1019) 82 | (= tt1019 (+ tt1016 tw1 tw2)) 83 | (>= tt1019 0) 84 | (>= tt1019 tt1018) 85 | (>= tt1018 tt1017) 86 | (>= tt1017 tt1016) 87 | (>= tt1016 tt1015) 88 | (>= tt1015 tt1014) 89 | (>= tt1014 tt1013) 90 | (>= tt1013 tt1012) 91 | (>= tt1012 tt1011) 92 | (>= tt1011 tt1010) 93 | (>= 0 0) 94 | (>= tt10 0) 95 | (>= tw1 1) 96 | (>= tw2 4) 97 | ) 98 | ) 99 | ) 100 | ) 101 | 102 | (ef-solve) 103 | (show-model) 104 | -------------------------------------------------------------------------------- /examples/mcsat/yiceslib.py: -------------------------------------------------------------------------------- 1 | from ctypes import ( c_int64 ) 2 | 3 | from yices_api import * 4 | 5 | 6 | def term_to_string(term): 7 | """Convert a term to a string.""" 8 | return yices_term_to_string(term, 80, 20, 0) 9 | 10 | def declare_real_var(name): 11 | """Creates a real variable with given name.""" 12 | try: 13 | x = yices_new_uninterpreted_term(yices_real_type()) 14 | yices_set_term_name(x, name) 15 | return x 16 | except YicesException as e: 17 | print 'declare_real_var: ', e 18 | return None 19 | 20 | def declare_integer_var(name): 21 | """Creates an integer variable with given name.""" 22 | try: 23 | x = yices_new_uninterpreted_term(yices_int_type()) 24 | yices_set_term_name(x, name) 25 | return x 26 | except YicesException as e: 27 | print 'declare_integer_var: ', e 28 | return None 29 | 30 | 31 | def make_context(): 32 | """Create a QF_NRA, non-linear real arithmetic, context.""" 33 | cfg = yices_new_config() 34 | yices_default_config_for_logic(cfg, 'QF_NRA') 35 | yices_set_config(cfg, 'mode', 'one-shot') 36 | ctx = yices_new_context(cfg) 37 | yices_free_config(cfg) 38 | return ctx 39 | -------------------------------------------------------------------------------- /examples/readme_qf_bv.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | from yices import * 4 | 5 | 6 | cfg = Config() 7 | cfg.default_config_for_logic('QF_BV') 8 | ctx = Context(cfg) 9 | 10 | bv32_t = Types.bv_type(32) 11 | x = Terms.new_uninterpreted_term(bv32_t, 'x') 12 | y = Terms.new_uninterpreted_term(bv32_t, 'y') 13 | 14 | 15 | zero = Terms.bvconst_integer(32, 0) 16 | fmla0 = Terms.bvsgt_atom(x, zero) 17 | fmla1 = Terms.bvsgt_atom(y, zero) 18 | fmla2 = Terms.bvslt_atom(Terms.bvadd(x, y), x) 19 | 20 | ctx.assert_formulas([fmla0, fmla1, fmla2]) 21 | 22 | status = ctx.check_context() 23 | 24 | if status == Status.SAT: 25 | model = Model.from_context(ctx, 1) 26 | model_string = model.to_string(80, 100, 0) 27 | print(model_string) 28 | xval = model.get_value(x) 29 | yval = model.get_value(y) 30 | print('x = {0}\ny = {1}'.format(xval, yval)) 31 | 32 | cfg.dispose() 33 | ctx.dispose() 34 | Yices.exit() 35 | -------------------------------------------------------------------------------- /examples/readme_qf_lra.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | from yices import * 4 | 5 | 6 | cfg = Config() 7 | cfg.default_config_for_logic('QF_LRA') 8 | ctx = Context(cfg) 9 | 10 | real_t = Types.real_type() 11 | x = Terms.new_uninterpreted_term(real_t, 'x') 12 | y = Terms.new_uninterpreted_term(real_t, 'y') 13 | 14 | fmla0 = Terms.parse_term('(> (+ x y) 0)') 15 | fmla1 = Terms.parse_term('(or (< x 0) (< y 0))') 16 | 17 | ctx.assert_formulas([fmla0, fmla1]) 18 | 19 | status = ctx.check_context() 20 | 21 | if status == Status.SAT: 22 | model = Model.from_context(ctx, 1) 23 | model_string = model.to_string(80, 100, 0) 24 | print(model_string) 25 | xval = model.get_value(x) 26 | yval = model.get_value(y) 27 | print('x = {0}, y = {1}'.format(xval, yval)) 28 | 29 | cfg.dispose() 30 | ctx.dispose() 31 | Yices.exit() 32 | -------------------------------------------------------------------------------- /examples/readme_qf_nra.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | from yices import * 4 | 5 | 6 | cfg = Config() 7 | cfg.default_config_for_logic('QF_NRA') 8 | ctx = Context(cfg) 9 | 10 | real_t = Types.real_type() 11 | x = Terms.new_uninterpreted_term(real_t, 'x') 12 | y = Terms.new_uninterpreted_term(real_t, 'y') 13 | 14 | fmla0 = Terms.parse_term('(= (+ (* x x) (* y y)) 1)') 15 | fmla1 = Terms.parse_term('(= x (* 2 y))') 16 | fmla2 = Terms.parse_term('(> x 0)') 17 | 18 | ctx.assert_formulas([fmla0, fmla1, fmla2]) 19 | 20 | status = ctx.check_context() 21 | 22 | if status == Status.SAT: 23 | model = Model.from_context(ctx, 1) 24 | model_string = model.to_string(80, 100, 0) 25 | print(model_string) 26 | xval = model.get_value(x) 27 | yval = model.get_value(y) 28 | print('x = {0}, y = {1}'.format(xval, yval)) 29 | 30 | cfg.dispose() 31 | ctx.dispose() 32 | Yices.exit() 33 | -------------------------------------------------------------------------------- /examples/sudoku/4-extreme-9x9-sudokus.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SRI-CSL/yices2_python_bindings/c1136bc48032ca5a59d534f9950f6e21343d767d/examples/sudoku/4-extreme-9x9-sudokus.pdf -------------------------------------------------------------------------------- /examples/sudoku/Solver.py: -------------------------------------------------------------------------------- 1 | from SudokuLib import make_grid 2 | from SudokuLib import Syntax, Puzzle, Cores 3 | 4 | from yices.Terms import Terms 5 | from yices.Context import Context 6 | from yices.Status import Status 7 | from yices.Model import Model 8 | 9 | 10 | 11 | class Solver: 12 | 13 | def __init__(self, pzl): 14 | self.puzzle = pzl 15 | 16 | # isolate all the syntax in one place 17 | self.syntax = Syntax() 18 | 19 | self.variables = self.syntax.variables 20 | self.constants = self.syntax.constants 21 | 22 | self.duplicate_rules = self.syntax.duplicate_rules 23 | self.trivial_rules = self.syntax.trivial_rules 24 | self.all_rules = self.syntax.all_rules 25 | 26 | def var(self, i, j): 27 | return self.variables[i][j] 28 | 29 | def assert_rules(self, ctx): 30 | ctx.assert_formulas(self.all_rules) 31 | 32 | def assert_trivial_rules(self, ctx): 33 | ctx.assert_formulas(self.trivial_rules) 34 | 35 | def assert_duplicate_rules(self, ctx): 36 | ctx.assert_formulas(self.duplicate_rules) 37 | 38 | def _equality(self, i, j, val): 39 | return Terms.arith_eq_atom(self.var(i,j), self.constants[val]) 40 | 41 | def _inequality(self, i, j, val): 42 | return Terms.arith_neq_atom(self.var(i,j), self.constants[val]) 43 | 44 | def assert_value(self, ctx, i, j, val): 45 | if not (0 <= i <= 8 and 0 <= j <= 8 and 1 <= val <= 9): 46 | raise Exception(f'Index error: {i} {j} {val}') 47 | ctx.assert_formula(self._equality(i, j, val)) 48 | 49 | def assert_not_value(self, ctx, i, j, val): 50 | if not (0 <= i <= 8 and 0 <= j <= 8 and 1 <= val <= 9): 51 | raise Exception(f'Index error: {i} {j} {val}') 52 | ctx.assert_formula(self._inequality(i, j, val)) 53 | 54 | def assert_puzzle(self, ctx): 55 | terms = [] 56 | for i in range(9): 57 | for j in range(9): 58 | val = self.puzzle.get_cell(i, j) 59 | if val is not None: 60 | terms.append(self._equality(i, j, val)) 61 | ctx.assert_formulas(terms) 62 | 63 | def assert_puzzle_except(self, ctx, row, col, ans): 64 | assert ans == self.puzzle.get_cell(row, col) 65 | terms = [] 66 | for i in range(9): 67 | for j in range(9): 68 | if i != row and j != col: 69 | val = self.puzzle.get_cell(i, j) 70 | if val is not None: 71 | terms.append(self._equality(i, j, val)) 72 | ctx.assert_formulas(terms) 73 | 74 | 75 | def puzzle_from_model(self, model): 76 | if model is None: 77 | return None 78 | matrix = make_grid() 79 | for i in range(9): 80 | for j in range(9): 81 | matrix[i][j] = model.get_value(self.var(i, j)) 82 | return Puzzle(matrix) 83 | 84 | def solve(self): 85 | context = Context() 86 | self.assert_rules(context) 87 | self.assert_puzzle(context) 88 | smt_stat = context.check_context(None) 89 | answer = None 90 | if smt_stat == Status.SAT: 91 | model = Model.from_context(context, 1) 92 | answer = self.puzzle_from_model(model) 93 | model.dispose() 94 | context.dispose() 95 | return answer 96 | 97 | def filter_cores(self, solution, cutoff=5): 98 | cores = self.compute_cores(solution) 99 | if cores is None: 100 | return None 101 | print('\nCores:\n') 102 | smallest = cores.least(cutoff) 103 | filtered = Cores(len(self.duplicate_rules)) 104 | for core in smallest: 105 | ncore = self.filter_core(core) 106 | filtered.add(*ncore) 107 | print('\nFiltered Cores:\n') 108 | smallest = filtered.least(5) 109 | return smallest 110 | 111 | def compute_cores(self, solution, reduced=False): 112 | cores = Cores(len(self.duplicate_rules)) 113 | if solution is not None: 114 | for i in range(9): 115 | for j in range(9): 116 | slot = self.puzzle.get_cell(i, j) 117 | if slot is None: 118 | ans = solution.get_cell(i, j) 119 | core = self.compute_reduced_core(i, j, ans) if reduced else self.compute_core(i, j, ans) 120 | if core is None: 121 | return None 122 | cores.add(*core) 123 | return cores 124 | 125 | def compute_core(self, i, j, val): 126 | """We compute the unsat core of the duplicate_rules when asserting self.var(i, j) != val w.r.t the puzzle (val is assumed to be the unique solution).""" 127 | if not (0 <= i <= 8 and 0 <= j <= 8 and 1 <= val <= 9): 128 | raise Exception(f'Index error: {i} {j} {val}') 129 | context = Context() 130 | self.assert_puzzle(context) 131 | self.assert_not_value(context, i, j, val) 132 | self.assert_trivial_rules(context) 133 | smt_stat = context.check_context_with_assumptions(None, self.duplicate_rules) 134 | # a valid puzzle should have a unique solution, so this should not happen, if it does we bail 135 | if smt_stat != Status.UNSAT: 136 | print(f'Error: {i} {j} {val} - not UNSAT: {Status.name(smt_stat)}') 137 | model = Model.from_context(context, 1) 138 | answer = self.puzzle_from_model(model) 139 | print('Counter example (i.e. origonal puzzle does not have a unique solution):') 140 | answer.pprint() 141 | model.dispose() 142 | context.dispose() 143 | return None 144 | core = context.get_unsat_core() 145 | context.dispose() 146 | print(f'Core: {i} {j} {val} {len(core)} / {len(self.duplicate_rules)}') 147 | return (i, j, val, core) 148 | 149 | def compute_reduced_core(self, i, j, val): 150 | """We compute the unsat core then remove any unnecessary terms.""" 151 | if not (0 <= i <= 8 and 0 <= j <= 8 and 1 <= val <= 9): 152 | raise Exception(f'Index error: {i} {j} {val}') 153 | context = Context() 154 | self.assert_puzzle(context) 155 | self.assert_not_value(context, i, j, val) 156 | self.assert_trivial_rules(context) 157 | smt_stat = context.check_context_with_assumptions(None, self.duplicate_rules) 158 | # a valid puzzle should have a unique solution, so this should not happen, if it does we bail 159 | if smt_stat != Status.UNSAT: 160 | print(f'Error: {i} {j} {val} - not UNSAT: {Status.name(smt_stat)}') 161 | model = Model.from_context(context, 1) 162 | answer = self.puzzle_from_model(model) 163 | print('Counter example (i.e. origonal puzzle does not have a unique solution):') 164 | answer.pprint() 165 | model.dispose() 166 | context.dispose() 167 | return None 168 | core = context.get_unsat_core() 169 | filtered = core.copy() 170 | for term in core: 171 | filtered.remove(term) 172 | smt_stat = context.check_context_with_assumptions(None, filtered) 173 | if smt_stat != Status.UNSAT: 174 | filtered.append(term) 175 | context.dispose() 176 | print(f'Core: {i} {j} {val} {len(filtered)} / {len(self.duplicate_rules)}') 177 | return (i, j, val, filtered) 178 | 179 | def filter_core(self, core): 180 | i, j, val, terms = core 181 | context = Context() 182 | self.assert_puzzle(context) 183 | self.assert_not_value(context, i, j, val) 184 | self.assert_trivial_rules(context) 185 | filtered = terms.copy() 186 | for term in terms: 187 | filtered.remove(term) 188 | smt_stat = context.check_context_with_assumptions(None, filtered) 189 | if smt_stat != Status.UNSAT: 190 | filtered.append(term) 191 | context.dispose() 192 | return (i, j, val, filtered) 193 | 194 | def erasable(self, ctx, i, j, val): 195 | """erasable returns True if puzzle (with [row, col] = val omitted) implies that [row, col] = val, it returns False otherwise. 196 | 197 | It is assumed that puzzle.get_cell(i,j) == val 198 | The context has already been informed of the rules. 199 | """ 200 | ctx.push() 201 | self.assert_puzzle_except(ctx, i, j, val) 202 | self.assert_not_value(ctx, i, j, val) 203 | smt_stat = ctx.check_context(None) 204 | ctx.pop() 205 | return smt_stat == Status.UNSAT 206 | 207 | def show_hints(self, cores): 208 | for core in cores: 209 | i, j, val, terms = core 210 | print(f'[{i}, {j}] = {val} is forced by the following rules:') 211 | for term in terms: 212 | print(f'\t{self.syntax.explanation[term]}') 213 | -------------------------------------------------------------------------------- /examples/sudoku/SudokuLib.py: -------------------------------------------------------------------------------- 1 | """The shared code used in the cores and generator scripts.""" 2 | 3 | import copy 4 | 5 | from yices.Types import Types 6 | from yices.Terms import Terms 7 | 8 | 9 | int_t = Types.int_type() 10 | 11 | 12 | def make_grid(): 13 | """make_grid constructs a 9x9 grid all whose entries are initially None.""" 14 | grid = [None] * 9 15 | for i in range(9): 16 | grid[i] = [None] * 9 17 | return grid 18 | 19 | def make_constants(): 20 | """make_constants makes a map from the digits 1 through 9 to the Yices constant denoting that digit.""" 21 | constants = {} 22 | for i in range(1, 10): 23 | constants[i] = Terms.integer(i) 24 | return constants 25 | 26 | def make_variables(): 27 | """make_variables creates a 9x9 grid with each cell containing the Yices term representing that cell.""" 28 | variables = make_grid() 29 | for i in range(9): 30 | for j in range(9): 31 | variables[i][j] = Terms.new_uninterpreted_term(int_t) 32 | return variables 33 | 34 | class SudokuError(Exception): 35 | """ 36 | An application specific error. 37 | """ 38 | 39 | class Syntax: 40 | 41 | def __init__(self): 42 | self.constants = make_constants() 43 | self.variables = make_variables() 44 | 45 | # maps non-trivial rules to an informative string describing them 46 | self.explanation = {} 47 | # dividing the rules into trivial and non trivial is used in getting unsat cores (of non-trivial rules) 48 | self.trivial_rules = self.make_trivial_rules() 49 | self.duplicate_rules = self.make_duplicate_rules() 50 | self.all_rules = self.trivial_rules.copy() 51 | self.all_rules.extend(self.duplicate_rules) 52 | 53 | 54 | def var(self, i, j): 55 | return self.variables[i][j] 56 | 57 | # x is between 1 and 9 58 | def between_1_and_9(self, x): 59 | return Terms.yor([Terms.eq(x, self.constants[i+1]) for i in range(9)]) 60 | 61 | def make_trivial_rules(self): 62 | rules = [] 63 | # Every variable is between 1 and 9 inclusive 64 | for i in range(9): 65 | for j in range(9): 66 | rules.append(self.between_1_and_9(self.var(i, j))) 67 | return rules 68 | 69 | 70 | def make_duplicate_rules(self): 71 | rules = [] 72 | # All elements in a row must be distinct 73 | for i in range(9): 74 | rule = Terms.distinct([self.var(i, j) for j in range(9)]) 75 | self.explanation[rule] = f'Row {i + 1} cannot contain duplicates' 76 | rules.append(rule) 77 | # All elements in a column must be distinct 78 | for i in range(9): 79 | rule = Terms.distinct([self.var(j, i) for j in range(9)]) # pylint: disable=W1114 80 | self.explanation[rule] = f'Column {i + 1} cannot contain duplicates' 81 | rules.append(rule) 82 | # All elements in each 3x3 square must be distinct 83 | def subsquare(row, column): 84 | rname = { 0: 'Top', 1: 'Middle', 2: 'Bottom'} 85 | cname = { 0: 'left', 1: 'center', 2: 'right'} 86 | return f'{rname[row]}-{cname[column]}' 87 | for row in range(3): 88 | for column in range(3): 89 | rule = Terms.distinct([self.var(i + 3 * row, j + 3 * column) for i in range(3) for j in range(3)]) 90 | self.explanation[rule] = f'{subsquare(row,column)} subsquare cannot contain duplicates' 91 | rules.append(rule) 92 | return rules 93 | 94 | 95 | 96 | class Puzzle: 97 | """Puzzle is a 9x9 grid of digits between 1 and 9 inclusive, or None.""" 98 | 99 | def puzzle2path(self, path): 100 | with open(path, 'w') as fp: 101 | fp.write(self.to_string('', '0')) 102 | 103 | @staticmethod 104 | def path2puzzle(path): 105 | with open(path, 'r') as fp: 106 | matrix = make_grid() 107 | row = 0 108 | col = 0 109 | for line in fp: 110 | line = line.strip() 111 | if len(line) != 9: 112 | raise SudokuError('Each line in the sudoku puzzle must be 9 chars long.') 113 | for char in line: 114 | if not char.isdigit(): 115 | raise SudokuError('Valid characters for a sudoku puzzle must be in 0-9') 116 | matrix[row][col] = int(char) # pylint: disable=E1137 117 | col += 1 118 | row += 1 119 | col = 0 120 | if row == 9: 121 | break 122 | return Puzzle(matrix) 123 | 124 | 125 | def __init__(self, matrix): 126 | self.grid = make_grid() 127 | if matrix is not None: 128 | for i in range(9): 129 | for j in range(9): 130 | val = matrix[i][j] 131 | if val != 0: 132 | self.set_cell(i, j, matrix[i][j]) 133 | 134 | def clone(self): 135 | """clone creates a deep copy of the puzzle.""" 136 | result = Puzzle(None) 137 | result.grid = copy.deepcopy(self.grid) 138 | return result 139 | 140 | def erase_cell(self, i, j): 141 | if 0 <= i <= 8 and 0 <= j <= 8: 142 | self.grid[i][j] = None 143 | return None 144 | raise Exception(f'Index error: {i} {j}') 145 | 146 | def set_cell(self, i, j, val): 147 | if 0 <= i <= 8 and 0 <= j <= 8 and 1 <= val <= 9: 148 | self.grid[i][j] = val 149 | return None 150 | raise Exception(f'Index error: {i} {j} {val}') 151 | 152 | def get_cell(self, i, j): 153 | if 0 <= i <= 8 and 0 <= j <= 8: 154 | return self.grid[i][j] 155 | raise Exception(f'Index error: {i} {j}') 156 | 157 | def to_string(self, pad=' ', blank='.', newline='\n'): 158 | def pp(i, j, blank='.'): 159 | val = self.get_cell(i, j) 160 | return str(val) if val is not None else blank 161 | rows = [] 162 | for row in range(9): 163 | line = [pp(row, col, blank) for col in range(9)] 164 | rows.append(pad.join(line)) 165 | return newline.join(rows) 166 | 167 | 168 | def pprint(self, pad=' ', blank='.', newline='\n'): 169 | print(self.to_string(pad, blank, newline)) 170 | 171 | class Cores: 172 | 173 | def __init__(self, card): 174 | self.core_map = {} 175 | self.maximum = card 176 | 177 | def add(self, i, j, val, core): 178 | key = len(core) 179 | entry = [] 180 | if key not in self.core_map: 181 | self.core_map[key] = entry 182 | else: 183 | entry = self.core_map[key] 184 | entry.append(tuple([i, j, val, core])) 185 | 186 | def least(self, count): 187 | retval = [] 188 | counter = 0 189 | for i in range(self.maximum + 1): 190 | if i in self.core_map: 191 | vec = self.core_map[i] 192 | for v in vec: # pylint: disable=C0103 193 | retval.append(v) 194 | print(f'OK: {v[0]} {v[1]} {v[2]} {len(v[3])} / {self.maximum}') 195 | counter += 1 196 | if counter >= count: 197 | return retval 198 | return retval 199 | -------------------------------------------------------------------------------- /examples/sudoku/puzzle1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SRI-CSL/yices2_python_bindings/c1136bc48032ca5a59d534f9950f6e21343d767d/examples/sudoku/puzzle1.pdf -------------------------------------------------------------------------------- /examples/sudoku/sudoku.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | from yices.Types import Types 5 | 6 | from yices.Terms import Terms 7 | 8 | from yices.Config import Config 9 | 10 | from yices.Context import Context 11 | 12 | from yices.Status import Status 13 | 14 | from yices.Model import Model 15 | 16 | from yices.Yices import Yices 17 | 18 | 19 | 20 | int_t = Types.int_type() 21 | 22 | 23 | #seems logical to make the terms in a grid. 24 | X = [None] * 9 25 | for i in range(9): 26 | X[i] = [None] * 9 27 | for j in range(9): 28 | X[i][j] = Terms.new_uninterpreted_term(int_t) 29 | 30 | #not real happy about the indexing going from 0 to 8, but 31 | #isolating access via V could make it easier to go from 1 to 9 32 | def V(vi, vj): 33 | return X[vi][vj] 34 | 35 | 36 | 37 | #make the constants that we will need 38 | C = {} 39 | for i in range(1, 10): 40 | C[i] = Terms.integer(i) 41 | 42 | one = C[1] 43 | nine = C[9] 44 | 45 | 46 | config = Config() 47 | config.default_config_for_logic("QF_LIA") 48 | 49 | context = Context(config) 50 | 51 | 52 | 53 | # x is between 1 and 9 54 | def between_1_and_9(x): 55 | return Terms.yand([Terms.arith_leq_atom(one, x), Terms.arith_leq_atom(x, nine)]) 56 | 57 | for i in range(9): 58 | for j in range(9): 59 | context.assert_formula(between_1_and_9(V(i, j))) 60 | 61 | 62 | # All elements in a row must be distinct 63 | for i in range(9): 64 | context.assert_formula(Terms.distinct([V(i, j) for j in range(9)])) 65 | 66 | 67 | # All elements in a column must be distinct 68 | for i in range(9): 69 | context.assert_formula(Terms.distinct([V(j, i) for j in range(9)])) 70 | 71 | # All elements in each 3x3 square must be distinct 72 | for k in range(3): 73 | for l in range(3): 74 | context.assert_formula(Terms.distinct([V(i + 3 * l, j + 3 * k) for i in range(3) for j in range(3)])) 75 | 76 | 77 | #initial conditions (part of the UI) 78 | def set_value(ctx, position, value): 79 | (row, column) = position 80 | assert 1 <= row <= 9 81 | assert 1 <= column <= 9 82 | assert 1 <= value <= 9 83 | ctx.assert_formula(Terms.arith_eq_atom(V(row - 1, column - 1), C[value])) 84 | 85 | 86 | # 87 | # Constraints: 88 | # 89 | # ------------------------- 90 | # | 2 | | 7 6 8 | 91 | # | 4 7 | | 5 | 92 | # | | 8 7 | | 93 | # ------------------------- 94 | # | 5 | 1 | | 95 | # | 2 8 | | 4 | 96 | # | 3 | 4 | 7 | 97 | # ------------------------- 98 | # | | 3 1 | | 99 | # | 9 | | 8 5 | 100 | # | 6 7 1 | | 2 | 101 | # ------------------------- 102 | # 103 | 104 | set_value(context, (1, 3), 2) 105 | set_value(context, (1, 7), 7) 106 | set_value(context, (1, 8), 6) 107 | set_value(context, (1, 9), 8) 108 | 109 | set_value(context, (2, 1), 4) 110 | set_value(context, (2, 3), 7) 111 | set_value(context, (2, 7), 5) 112 | 113 | set_value(context, (3, 4), 8) 114 | set_value(context, (3, 6), 7) 115 | 116 | set_value(context, (4, 2), 5) 117 | set_value(context, (4, 5), 1) 118 | 119 | set_value(context, (5, 2), 2) 120 | set_value(context, (5, 3), 8) 121 | set_value(context, (5, 7), 4) 122 | 123 | set_value(context, (6, 1), 3) 124 | set_value(context, (6, 5), 4) 125 | set_value(context, (6, 8), 7) 126 | 127 | set_value(context, (7, 4), 3) 128 | set_value(context, (7, 6), 1) 129 | 130 | set_value(context, (8, 3), 9) 131 | set_value(context, (8, 7), 8) 132 | set_value(context, (8, 9), 5) 133 | 134 | set_value(context, (9, 1), 6) 135 | set_value(context, (9, 2), 7) 136 | set_value(context, (9, 3), 1) 137 | set_value(context, (9, 7), 2) 138 | 139 | #check sat 140 | 141 | smt_stat = context.check_context(None) 142 | 143 | if smt_stat != Status.SAT: 144 | print('No solution: smt_stat = {0}\n'.format(smt_stat)) 145 | else: 146 | #print model 147 | model = Model.from_context(context, 1) 148 | for i in range(9): 149 | for j in range(9): 150 | val = model.get_value(V(i, j)) 151 | print('V({0}, {1}) = {2}'.format(i, j, val)) 152 | model.dispose() 153 | 154 | print('Cleaning up\n') 155 | 156 | context.dispose() 157 | config.dispose() 158 | 159 | 160 | Yices.exit() 161 | -------------------------------------------------------------------------------- /examples/sudoku/sudoku.ys: -------------------------------------------------------------------------------- 1 | ; 2 | ; Sudoku encoded using integer variables 3 | ; x_i_j = value in row i, column j for i=1 to 9 and j=1 to 9 4 | 5 | (define x_1_1::int) 6 | (define x_1_2::int) 7 | (define x_1_3::int) 8 | (define x_1_4::int) 9 | (define x_1_5::int) 10 | (define x_1_6::int) 11 | (define x_1_7::int) 12 | (define x_1_8::int) 13 | (define x_1_9::int) 14 | 15 | (define x_2_1::int) 16 | (define x_2_2::int) 17 | (define x_2_3::int) 18 | (define x_2_4::int) 19 | (define x_2_5::int) 20 | (define x_2_6::int) 21 | (define x_2_7::int) 22 | (define x_2_8::int) 23 | (define x_2_9::int) 24 | 25 | (define x_3_1::int) 26 | (define x_3_2::int) 27 | (define x_3_3::int) 28 | (define x_3_4::int) 29 | (define x_3_5::int) 30 | (define x_3_6::int) 31 | (define x_3_7::int) 32 | (define x_3_8::int) 33 | (define x_3_9::int) 34 | 35 | (define x_4_1::int) 36 | (define x_4_2::int) 37 | (define x_4_3::int) 38 | (define x_4_4::int) 39 | (define x_4_5::int) 40 | (define x_4_6::int) 41 | (define x_4_7::int) 42 | (define x_4_8::int) 43 | (define x_4_9::int) 44 | 45 | (define x_5_1::int) 46 | (define x_5_2::int) 47 | (define x_5_3::int) 48 | (define x_5_4::int) 49 | (define x_5_5::int) 50 | (define x_5_6::int) 51 | (define x_5_7::int) 52 | (define x_5_8::int) 53 | (define x_5_9::int) 54 | 55 | (define x_6_1::int) 56 | (define x_6_2::int) 57 | (define x_6_3::int) 58 | (define x_6_4::int) 59 | (define x_6_5::int) 60 | (define x_6_6::int) 61 | (define x_6_7::int) 62 | (define x_6_8::int) 63 | (define x_6_9::int) 64 | 65 | (define x_7_1::int) 66 | (define x_7_2::int) 67 | (define x_7_3::int) 68 | (define x_7_4::int) 69 | (define x_7_5::int) 70 | (define x_7_6::int) 71 | (define x_7_7::int) 72 | (define x_7_8::int) 73 | (define x_7_9::int) 74 | 75 | (define x_8_1::int) 76 | (define x_8_2::int) 77 | (define x_8_3::int) 78 | (define x_8_4::int) 79 | (define x_8_5::int) 80 | (define x_8_6::int) 81 | (define x_8_7::int) 82 | (define x_8_8::int) 83 | (define x_8_9::int) 84 | 85 | (define x_9_1::int) 86 | (define x_9_2::int) 87 | (define x_9_3::int) 88 | (define x_9_4::int) 89 | (define x_9_5::int) 90 | (define x_9_6::int) 91 | (define x_9_7::int) 92 | (define x_9_8::int) 93 | (define x_9_9::int) 94 | 95 | ; 96 | ; Every value is between 1 and 9 97 | ; 98 | (assert (and (<= 1 x_1_1) (<= x_1_1 9))) 99 | (assert (and (<= 1 x_1_2) (<= x_1_2 9))) 100 | (assert (and (<= 1 x_1_3) (<= x_1_3 9))) 101 | (assert (and (<= 1 x_1_4) (<= x_1_4 9))) 102 | (assert (and (<= 1 x_1_5) (<= x_1_5 9))) 103 | (assert (and (<= 1 x_1_6) (<= x_1_6 9))) 104 | (assert (and (<= 1 x_1_7) (<= x_1_7 9))) 105 | (assert (and (<= 1 x_1_8) (<= x_1_8 9))) 106 | (assert (and (<= 1 x_1_9) (<= x_1_9 9))) 107 | 108 | (assert (and (<= 1 x_2_1) (<= x_2_1 9))) 109 | (assert (and (<= 1 x_2_2) (<= x_2_2 9))) 110 | (assert (and (<= 1 x_2_3) (<= x_2_3 9))) 111 | (assert (and (<= 1 x_2_4) (<= x_2_4 9))) 112 | (assert (and (<= 1 x_2_5) (<= x_2_5 9))) 113 | (assert (and (<= 1 x_2_6) (<= x_2_6 9))) 114 | (assert (and (<= 1 x_2_7) (<= x_2_7 9))) 115 | (assert (and (<= 1 x_2_8) (<= x_2_8 9))) 116 | (assert (and (<= 1 x_2_9) (<= x_2_9 9))) 117 | 118 | (assert (and (<= 1 x_3_1) (<= x_3_1 9))) 119 | (assert (and (<= 1 x_3_2) (<= x_3_2 9))) 120 | (assert (and (<= 1 x_3_3) (<= x_3_3 9))) 121 | (assert (and (<= 1 x_3_4) (<= x_3_4 9))) 122 | (assert (and (<= 1 x_3_5) (<= x_3_5 9))) 123 | (assert (and (<= 1 x_3_6) (<= x_3_6 9))) 124 | (assert (and (<= 1 x_3_7) (<= x_3_7 9))) 125 | (assert (and (<= 1 x_3_8) (<= x_3_8 9))) 126 | (assert (and (<= 1 x_3_9) (<= x_3_9 9))) 127 | 128 | (assert (and (<= 1 x_4_1) (<= x_4_1 9))) 129 | (assert (and (<= 1 x_4_2) (<= x_4_2 9))) 130 | (assert (and (<= 1 x_4_3) (<= x_4_3 9))) 131 | (assert (and (<= 1 x_4_4) (<= x_4_4 9))) 132 | (assert (and (<= 1 x_4_5) (<= x_4_5 9))) 133 | (assert (and (<= 1 x_4_6) (<= x_4_6 9))) 134 | (assert (and (<= 1 x_4_7) (<= x_4_7 9))) 135 | (assert (and (<= 1 x_4_8) (<= x_4_8 9))) 136 | (assert (and (<= 1 x_4_9) (<= x_4_9 9))) 137 | 138 | (assert (and (<= 1 x_5_1) (<= x_5_1 9))) 139 | (assert (and (<= 1 x_5_2) (<= x_5_2 9))) 140 | (assert (and (<= 1 x_5_3) (<= x_5_3 9))) 141 | (assert (and (<= 1 x_5_4) (<= x_5_4 9))) 142 | (assert (and (<= 1 x_5_5) (<= x_5_5 9))) 143 | (assert (and (<= 1 x_5_6) (<= x_5_6 9))) 144 | (assert (and (<= 1 x_5_7) (<= x_5_7 9))) 145 | (assert (and (<= 1 x_5_8) (<= x_5_8 9))) 146 | (assert (and (<= 1 x_5_9) (<= x_5_9 9))) 147 | 148 | (assert (and (<= 1 x_6_1) (<= x_6_1 9))) 149 | (assert (and (<= 1 x_6_2) (<= x_6_2 9))) 150 | (assert (and (<= 1 x_6_3) (<= x_6_3 9))) 151 | (assert (and (<= 1 x_6_4) (<= x_6_4 9))) 152 | (assert (and (<= 1 x_6_5) (<= x_6_5 9))) 153 | (assert (and (<= 1 x_6_6) (<= x_6_6 9))) 154 | (assert (and (<= 1 x_6_7) (<= x_6_7 9))) 155 | (assert (and (<= 1 x_6_8) (<= x_6_8 9))) 156 | (assert (and (<= 1 x_6_9) (<= x_6_9 9))) 157 | 158 | (assert (and (<= 1 x_7_1) (<= x_7_1 9))) 159 | (assert (and (<= 1 x_7_2) (<= x_7_2 9))) 160 | (assert (and (<= 1 x_7_3) (<= x_7_3 9))) 161 | (assert (and (<= 1 x_7_4) (<= x_7_4 9))) 162 | (assert (and (<= 1 x_7_5) (<= x_7_5 9))) 163 | (assert (and (<= 1 x_7_6) (<= x_7_6 9))) 164 | (assert (and (<= 1 x_7_7) (<= x_7_7 9))) 165 | (assert (and (<= 1 x_7_8) (<= x_7_8 9))) 166 | (assert (and (<= 1 x_7_9) (<= x_7_9 9))) 167 | 168 | (assert (and (<= 1 x_8_1) (<= x_8_1 9))) 169 | (assert (and (<= 1 x_8_2) (<= x_8_2 9))) 170 | (assert (and (<= 1 x_8_3) (<= x_8_3 9))) 171 | (assert (and (<= 1 x_8_4) (<= x_8_4 9))) 172 | (assert (and (<= 1 x_8_5) (<= x_8_5 9))) 173 | (assert (and (<= 1 x_8_6) (<= x_8_6 9))) 174 | (assert (and (<= 1 x_8_7) (<= x_8_7 9))) 175 | (assert (and (<= 1 x_8_8) (<= x_8_8 9))) 176 | (assert (and (<= 1 x_8_9) (<= x_8_9 9))) 177 | 178 | (assert (and (<= 1 x_9_1) (<= x_9_1 9))) 179 | (assert (and (<= 1 x_9_2) (<= x_9_2 9))) 180 | (assert (and (<= 1 x_9_3) (<= x_9_3 9))) 181 | (assert (and (<= 1 x_9_4) (<= x_9_4 9))) 182 | (assert (and (<= 1 x_9_5) (<= x_9_5 9))) 183 | (assert (and (<= 1 x_9_6) (<= x_9_6 9))) 184 | (assert (and (<= 1 x_9_7) (<= x_9_7 9))) 185 | (assert (and (<= 1 x_9_8) (<= x_9_8 9))) 186 | (assert (and (<= 1 x_9_9) (<= x_9_9 9))) 187 | 188 | ; 189 | ; All elements in a row must be distinct 190 | ; 191 | (assert (distinct x_1_1 x_1_2 x_1_3 x_1_4 x_1_5 x_1_6 x_1_7 x_1_8 x_1_9)) 192 | (assert (distinct x_2_1 x_2_2 x_2_3 x_2_4 x_2_5 x_2_6 x_2_7 x_2_8 x_2_9)) 193 | (assert (distinct x_3_1 x_3_2 x_3_3 x_3_4 x_3_5 x_3_6 x_3_7 x_3_8 x_3_9)) 194 | (assert (distinct x_4_1 x_4_2 x_4_3 x_4_4 x_4_5 x_4_6 x_4_7 x_4_8 x_4_9)) 195 | (assert (distinct x_5_1 x_5_2 x_5_3 x_5_4 x_5_5 x_5_6 x_5_7 x_5_8 x_5_9)) 196 | (assert (distinct x_6_1 x_6_2 x_6_3 x_6_4 x_6_5 x_6_6 x_6_7 x_6_8 x_6_9)) 197 | (assert (distinct x_7_1 x_7_2 x_7_3 x_7_4 x_7_5 x_7_6 x_7_7 x_7_8 x_7_9)) 198 | (assert (distinct x_8_1 x_8_2 x_8_3 x_8_4 x_8_5 x_8_6 x_8_7 x_8_8 x_8_9)) 199 | (assert (distinct x_9_1 x_9_2 x_9_3 x_9_4 x_9_5 x_9_6 x_9_7 x_9_8 x_9_9)) 200 | 201 | ; 202 | ; All elements in a column must be distinct 203 | ; 204 | (assert (distinct x_1_1 x_2_1 x_3_1 x_4_1 x_5_1 x_6_1 x_7_1 x_8_1 x_9_1)) 205 | (assert (distinct x_1_2 x_2_2 x_3_2 x_4_2 x_5_2 x_6_2 x_7_2 x_8_2 x_9_2)) 206 | (assert (distinct x_1_3 x_2_3 x_3_3 x_4_3 x_5_3 x_6_3 x_7_3 x_8_3 x_9_3)) 207 | (assert (distinct x_1_4 x_2_4 x_3_4 x_4_4 x_5_4 x_6_4 x_7_4 x_8_4 x_9_4)) 208 | (assert (distinct x_1_5 x_2_5 x_3_5 x_4_5 x_5_5 x_6_5 x_7_5 x_8_5 x_9_5)) 209 | (assert (distinct x_1_6 x_2_6 x_3_6 x_4_6 x_5_6 x_6_6 x_7_6 x_8_6 x_9_6)) 210 | (assert (distinct x_1_7 x_2_7 x_3_7 x_4_7 x_5_7 x_6_7 x_7_7 x_8_7 x_9_7)) 211 | (assert (distinct x_1_8 x_2_8 x_3_8 x_4_8 x_5_8 x_6_8 x_7_8 x_8_8 x_9_8)) 212 | (assert (distinct x_1_9 x_2_9 x_3_9 x_4_9 x_5_9 x_6_9 x_7_9 x_8_9 x_9_9)) 213 | 214 | ; 215 | ; All elements in each 3x3 square must be distinct 216 | ; 217 | (assert (distinct x_1_1 x_1_2 x_1_3 x_2_1 x_2_2 x_2_3 x_3_1 x_3_2 x_3_3)) 218 | (assert (distinct x_1_4 x_1_5 x_1_6 x_2_4 x_2_5 x_2_6 x_3_4 x_3_5 x_3_6)) 219 | (assert (distinct x_1_7 x_1_8 x_1_9 x_2_7 x_2_8 x_2_9 x_3_7 x_3_8 x_3_9)) 220 | (assert (distinct x_4_1 x_4_2 x_4_3 x_5_1 x_5_2 x_5_3 x_6_1 x_6_2 x_6_3)) 221 | (assert (distinct x_4_4 x_4_5 x_4_6 x_5_4 x_5_5 x_5_6 x_6_4 x_6_5 x_6_6)) 222 | (assert (distinct x_4_7 x_4_8 x_4_9 x_5_7 x_5_8 x_5_9 x_6_7 x_6_8 x_6_9)) 223 | (assert (distinct x_7_1 x_7_2 x_7_3 x_8_1 x_8_2 x_8_3 x_9_1 x_9_2 x_9_3)) 224 | (assert (distinct x_7_4 x_7_5 x_7_6 x_8_4 x_8_5 x_8_6 x_9_4 x_9_5 x_9_6)) 225 | (assert (distinct x_7_7 x_7_8 x_7_9 x_8_7 x_8_8 x_8_9 x_9_7 x_9_8 x_9_9)) 226 | 227 | ; 228 | ; Constraints: 229 | ; 230 | ; ------------------------- 231 | ; | 2 | | 7 6 8 | 232 | ; | 4 7 | | 5 | 233 | ; | | 8 7 | | 234 | ; ------------------------- 235 | ; | 5 | 1 | | 236 | ; | 2 8 | | 4 | 237 | ; | 3 | 4 | 7 | 238 | ; ------------------------- 239 | ; | | 3 1 | | 240 | ; | 9 | | 8 5 | 241 | ; | 6 7 1 | | 2 | 242 | ; ------------------------- 243 | ; 244 | 245 | (assert (= x_1_3 2)) 246 | (assert (= x_1_7 7)) 247 | (assert (= x_1_8 6)) 248 | (assert (= x_1_9 8)) 249 | 250 | (assert (= x_2_1 4)) 251 | (assert (= x_2_3 7)) 252 | (assert (= x_2_7 5)) 253 | 254 | (assert (= x_3_4 8)) 255 | (assert (= x_3_6 7)) 256 | 257 | (assert (= x_4_2 5)) 258 | (assert (= x_4_5 1)) 259 | 260 | (assert (= x_5_2 2)) 261 | (assert (= x_5_3 8)) 262 | (assert (= x_5_7 4)) 263 | 264 | (assert (= x_6_1 3)) 265 | (assert (= x_6_5 4)) 266 | (assert (= x_6_8 7)) 267 | 268 | (assert (= x_7_4 3)) 269 | (assert (= x_7_6 1)) 270 | 271 | (assert (= x_8_3 9)) 272 | (assert (= x_8_7 8)) 273 | (assert (= x_8_9 5)) 274 | 275 | (assert (= x_9_1 6)) 276 | (assert (= x_9_2 7)) 277 | (assert (= x_9_3 1)) 278 | (assert (= x_9_7 2)) 279 | 280 | (check) 281 | 282 | ; 283 | ; Print the solution 284 | ; 285 | (echo "x_1_1 = ") 286 | (eval x_1_1) 287 | (echo "x_1_2 = ") 288 | (eval x_1_2) 289 | (echo "x_1_3 = ") 290 | (eval x_1_3) 291 | (echo "x_1_4 = ") 292 | (eval x_1_4) 293 | (echo "x_1_5 = ") 294 | (eval x_1_5) 295 | (echo "x_1_6 = ") 296 | (eval x_1_6) 297 | (echo "x_1_7 = ") 298 | (eval x_1_7) 299 | (echo "x_1_8 = ") 300 | (eval x_1_8) 301 | (echo "x_1_9 = ") 302 | (eval x_1_9) 303 | 304 | (echo "\nx_2_1 = ") 305 | (eval x_2_1) 306 | (echo "x_2_2 = ") 307 | (eval x_2_2) 308 | (echo "x_2_3 = ") 309 | (eval x_2_3) 310 | (echo "x_2_4 = ") 311 | (eval x_2_4) 312 | (echo "x_2_5 = ") 313 | (eval x_2_5) 314 | (echo "x_2_6 = ") 315 | (eval x_2_6) 316 | (echo "x_2_7 = ") 317 | (eval x_2_7) 318 | (echo "x_2_8 = ") 319 | (eval x_2_8) 320 | (echo "x_2_9 = ") 321 | (eval x_2_9) 322 | 323 | (echo "\nx_3_1 = ") 324 | (eval x_3_1) 325 | (echo "x_3_2 = ") 326 | (eval x_3_2) 327 | (echo "x_3_3 = ") 328 | (eval x_3_3) 329 | (echo "x_3_4 = ") 330 | (eval x_3_4) 331 | (echo "x_3_5 = ") 332 | (eval x_3_5) 333 | (echo "x_3_6 = ") 334 | (eval x_3_6) 335 | (echo "x_3_7 = ") 336 | (eval x_3_7) 337 | (echo "x_3_8 = ") 338 | (eval x_3_8) 339 | (echo "x_3_9 = ") 340 | (eval x_3_9) 341 | 342 | (echo "\nx_4_1 = ") 343 | (eval x_4_1) 344 | (echo "x_4_2 = ") 345 | (eval x_4_2) 346 | (echo "x_4_3 = ") 347 | (eval x_4_3) 348 | (echo "x_4_4 = ") 349 | (eval x_4_4) 350 | (echo "x_4_5 = ") 351 | (eval x_4_5) 352 | (echo "x_4_6 = ") 353 | (eval x_4_6) 354 | (echo "x_4_7 = ") 355 | (eval x_4_7) 356 | (echo "x_4_8 = ") 357 | (eval x_4_8) 358 | (echo "x_4_9 = ") 359 | (eval x_4_9) 360 | 361 | (echo "\nx_5_1 = ") 362 | (eval x_5_1) 363 | (echo "x_5_2 = ") 364 | (eval x_5_2) 365 | (echo "x_5_3 = ") 366 | (eval x_5_3) 367 | (echo "x_5_4 = ") 368 | (eval x_5_4) 369 | (echo "x_5_5 = ") 370 | (eval x_5_5) 371 | (echo "x_5_6 = ") 372 | (eval x_5_6) 373 | (echo "x_5_7 = ") 374 | (eval x_5_7) 375 | (echo "x_5_8 = ") 376 | (eval x_5_8) 377 | (echo "x_5_9 = ") 378 | (eval x_5_9) 379 | 380 | (echo "\nx_6_1 = ") 381 | (eval x_6_1) 382 | (echo "x_6_2 = ") 383 | (eval x_6_2) 384 | (echo "x_6_3 = ") 385 | (eval x_6_3) 386 | (echo "x_6_4 = ") 387 | (eval x_6_4) 388 | (echo "x_6_5 = ") 389 | (eval x_6_5) 390 | (echo "x_6_6 = ") 391 | (eval x_6_6) 392 | (echo "x_6_7 = ") 393 | (eval x_6_7) 394 | (echo "x_6_8 = ") 395 | (eval x_6_8) 396 | (echo "x_6_9 = ") 397 | (eval x_6_9) 398 | 399 | (echo "\nx_7_1 = ") 400 | (eval x_7_1) 401 | (echo "x_7_2 = ") 402 | (eval x_7_2) 403 | (echo "x_7_3 = ") 404 | (eval x_7_3) 405 | (echo "x_7_4 = ") 406 | (eval x_7_4) 407 | (echo "x_7_5 = ") 408 | (eval x_7_5) 409 | (echo "x_7_6 = ") 410 | (eval x_7_6) 411 | (echo "x_7_7 = ") 412 | (eval x_7_7) 413 | (echo "x_7_8 = ") 414 | (eval x_7_8) 415 | (echo "x_7_9 = ") 416 | (eval x_7_9) 417 | 418 | (echo "\nx_8_1 = ") 419 | (eval x_8_1) 420 | (echo "x_8_2 = ") 421 | (eval x_8_2) 422 | (echo "x_8_3 = ") 423 | (eval x_8_3) 424 | (echo "x_8_4 = ") 425 | (eval x_8_4) 426 | (echo "x_8_5 = ") 427 | (eval x_8_5) 428 | (echo "x_8_6 = ") 429 | (eval x_8_6) 430 | (echo "x_8_7 = ") 431 | (eval x_8_7) 432 | (echo "x_8_8 = ") 433 | (eval x_8_8) 434 | (echo "x_8_9 = ") 435 | (eval x_8_9) 436 | 437 | (echo "\nx_9_1 = ") 438 | (eval x_9_1) 439 | (echo "x_9_2 = ") 440 | (eval x_9_2) 441 | (echo "x_9_3 = ") 442 | (eval x_9_3) 443 | (echo "x_9_4 = ") 444 | (eval x_9_4) 445 | (echo "x_9_5 = ") 446 | (eval x_9_5) 447 | (echo "x_9_6 = ") 448 | (eval x_9_6) 449 | (echo "x_9_7 = ") 450 | (eval x_9_7) 451 | (echo "x_9_8 = ") 452 | (eval x_9_8) 453 | (echo "x_9_9 = ") 454 | (eval x_9_9) 455 | 456 | ; 457 | ; This should give this 458 | ; 459 | ; ------------------------- 460 | ; | 1 9 2 | 4 5 3 | 7 6 8 | 461 | ; | 4 8 7 | 1 6 2 | 5 3 9 | 462 | ; | 5 6 3 | 8 9 7 | 1 2 4 | 463 | ; ------------------------- 464 | ; | 7 5 4 | 2 1 9 | 3 8 6 | 465 | ; | 9 2 8 | 7 3 6 | 4 5 1 | 466 | ; | 3 1 6 | 5 4 8 | 9 7 2 | 467 | ; ------------------------- 468 | ; | 8 4 5 | 3 2 1 | 6 9 7 | 469 | ; | 2 3 9 | 6 7 4 | 8 1 5 | 470 | ; | 6 7 1 | 9 8 5 | 2 4 3 | 471 | ; ------------------------- 472 | ; -------------------------------------------------------------------------------- /examples/sudoku/sudoku_api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from ctypes import (c_int32) 4 | 5 | from yices_api import ( 6 | yices_init, 7 | yices_int_type, 8 | yices_new_uninterpreted_term, 9 | yices_int32, 10 | yices_new_config, 11 | yices_default_config_for_logic, 12 | yices_new_context, 13 | yices_and2, 14 | yices_arith_leq_atom, 15 | yices_assert_formula, 16 | make_term_array, 17 | yices_distinct, 18 | yices_arith_eq_atom, 19 | yices_get_model, 20 | yices_get_int32_value, 21 | yices_free_model, 22 | yices_check_context, 23 | yices_free_context, 24 | yices_free_config, 25 | yices_exit, 26 | STATUS_SAT 27 | ) 28 | 29 | yices_init() 30 | 31 | 32 | int_t = yices_int_type() 33 | 34 | 35 | #seems logical to make the terms in a grid. 36 | X = [None] * 9 37 | for i in range(9): 38 | X[i] = [None] * 9 39 | for j in range(9): 40 | X[i][j] = yices_new_uninterpreted_term(int_t) 41 | 42 | #not real happy about the indexing going from 0 to 8, but 43 | #isolating access via V could make it easier to go from 1 to 9 44 | def V(vi, vj): 45 | return X[vi][vj] 46 | 47 | 48 | 49 | #make the constants that we will need 50 | C = {} 51 | for i in range(1, 10): 52 | C[i] = yices_int32(i) 53 | 54 | one = C[1] 55 | nine = C[9] 56 | 57 | 58 | config = yices_new_config() 59 | yices_default_config_for_logic(config, "QF_LIA") 60 | context = yices_new_context(config) 61 | 62 | 63 | 64 | # x is between 1 and 9 65 | def between_1_and_9(x): 66 | return yices_and2(yices_arith_leq_atom(one, x), yices_arith_leq_atom(x, nine)) 67 | 68 | for i in range(9): 69 | for j in range(9): 70 | yices_assert_formula(context, between_1_and_9(V(i, j))) 71 | 72 | 73 | def all_distinct(x): 74 | n = len(x) 75 | a = make_term_array(x) 76 | return yices_distinct(n, a) 77 | 78 | # All elements in a row must be distinct 79 | for i in range(9): 80 | yices_assert_formula(context, all_distinct([V(i, j) for j in range(9)])) #I.e. all_distinct(X[i]) 81 | 82 | 83 | # All elements in a column must be distinct 84 | for i in range(9): 85 | yices_assert_formula(context, all_distinct([V(j, i) for j in range(9)])) 86 | 87 | # All elements in each 3x3 square must be distinct 88 | for k in range(3): 89 | for l in range(3): 90 | yices_assert_formula(context, all_distinct([V(i + 3 * l, j + 3 * k) for i in range(3) for j in range(3)])) 91 | 92 | 93 | #initial conditions (part of the UI) 94 | def set_value(ctx, position, value): 95 | (row, column) = position 96 | assert 1 <= row <= 9 97 | assert 1 <= column <= 9 98 | assert 1 <= value <= 9 99 | yices_assert_formula(ctx, yices_arith_eq_atom(V(row - 1, column - 1), C[value])) 100 | 101 | 102 | # 103 | # Constraints: 104 | # 105 | # ------------------------- 106 | # | 2 | | 7 6 8 | 107 | # | 4 7 | | 5 | 108 | # | | 8 7 | | 109 | # ------------------------- 110 | # | 5 | 1 | | 111 | # | 2 8 | | 4 | 112 | # | 3 | 4 | 7 | 113 | # ------------------------- 114 | # | | 3 1 | | 115 | # | 9 | | 8 5 | 116 | # | 6 7 1 | | 2 | 117 | # ------------------------- 118 | # 119 | 120 | set_value(context, (1, 3), 2) 121 | set_value(context, (1, 7), 7) 122 | set_value(context, (1, 8), 6) 123 | set_value(context, (1, 9), 8) 124 | 125 | set_value(context, (2, 1), 4) 126 | set_value(context, (2, 3), 7) 127 | set_value(context, (2, 7), 5) 128 | 129 | set_value(context, (3, 4), 8) 130 | set_value(context, (3, 6), 7) 131 | 132 | set_value(context, (4, 2), 5) 133 | set_value(context, (4, 5), 1) 134 | 135 | set_value(context, (5, 2), 2) 136 | set_value(context, (5, 3), 8) 137 | set_value(context, (5, 7), 4) 138 | 139 | set_value(context, (6, 1), 3) 140 | set_value(context, (6, 5), 4) 141 | set_value(context, (6, 8), 7) 142 | 143 | set_value(context, (7, 4), 3) 144 | set_value(context, (7, 6), 1) 145 | 146 | set_value(context, (8, 3), 9) 147 | set_value(context, (8, 7), 8) 148 | set_value(context, (8, 9), 5) 149 | 150 | set_value(context, (9, 1), 6) 151 | set_value(context, (9, 2), 7) 152 | set_value(context, (9, 3), 1) 153 | set_value(context, (9, 7), 2) 154 | 155 | #check sat 156 | 157 | smt_stat = yices_check_context(context, None) 158 | 159 | if smt_stat != STATUS_SAT: 160 | print('No solution: smt_stat = {0}\n'.format(smt_stat)) 161 | else: 162 | #print model 163 | model = yices_get_model(context, 1) 164 | val = c_int32() 165 | for i in range(9): 166 | for j in range(9): 167 | yices_get_int32_value(model, V(i, j), val) 168 | print('V({0}, {1}) = {2}'.format(i, j, val.value)) 169 | yices_free_model(model) 170 | 171 | print('Cleaning up\n') 172 | 173 | yices_free_context(context) 174 | yices_free_config(config) 175 | 176 | 177 | yices_exit() 178 | -------------------------------------------------------------------------------- /examples/sudoku/sudoku_cores.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | """Using unsat cores to give hints.""" 5 | 6 | from SudokuLib import Puzzle 7 | from Solver import Solver 8 | 9 | from yices.Yices import Yices 10 | from yices.Census import Census 11 | 12 | 13 | 14 | puzzle_blank = [ 15 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 16 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 17 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 18 | # 19 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 20 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 21 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 22 | # 23 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 24 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 25 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 26 | ] 27 | 28 | 29 | puzzle_1 = [ 30 | [ 0, 6, 0, 0, 0, 8, 0, 7, 3], 31 | [ 0, 0, 2, 0, 0, 0, 0, 4, 0], 32 | [ 5, 0, 0, 0, 6, 0, 0, 0, 0], 33 | # 34 | [ 0, 0, 0, 6, 0, 2, 0, 0, 5], 35 | [ 0, 0, 4, 0, 0, 0, 1, 0, 0], 36 | [ 6, 0, 0, 8, 0, 7, 0, 0, 0], 37 | # 38 | [ 0, 0, 0, 0, 7, 0, 0, 0, 1], 39 | [ 0, 5, 0, 0, 0, 0, 3, 0, 0], 40 | [ 4, 3, 0, 1, 0, 0, 0, 8, 0], 41 | ] 42 | 43 | 44 | # puzzle_2 come from here: 45 | # https://puzzling.stackexchange.com/questions/29/what-are-the-criteria-for-determining-the-difficulty-of-sudoku-puzzle 46 | # where it is claimed to be the "hardest sudoku in the world" 47 | # but in fact is not a valid sudoku since it has more than one solution. tut tut. 48 | # I added it to one of the predefined boards ('escargot') of SudokuSensei and 49 | # it has 29 non isomorphic models (aka solutions). 50 | puzzle_ai_escargot = [ 51 | [ 1, 0, 0, 0, 0, 7, 0, 9, 0], 52 | [ 0, 3, 0, 0, 2, 0, 0, 0, 8], 53 | [ 0, 0, 9, 6, 0, 0, 5, 0, 0], 54 | # 55 | [ 0, 0, 5, 3, 0, 0, 9, 0, 0], 56 | [ 0, 1, 0, 0, 8, 0, 0, 0, 2], 57 | [ 6, 0, 0, 0, 0, 4, 0, 0, 0], 58 | # 59 | [ 3, 0, 0, 0, 0, 0, 0, 1, 0], 60 | [ 0, 4, 0, 0, 0, 0, 0, 0, 7], 61 | [ 0, 0, 7, 0, 0, 0, 0, 3, 0], 62 | ] 63 | 64 | extreme_1 = [ 65 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 66 | [ 0, 0, 2, 0, 0, 7, 1, 5, 0], 67 | [ 4, 0, 0, 0, 0, 9, 3, 0, 6], 68 | # 69 | [ 0, 1, 0, 0, 0, 3, 0, 0, 5], 70 | [ 0, 0, 0, 5, 2, 4, 0, 0, 0], 71 | [ 3, 0, 0, 7, 0, 0, 0, 6, 0], 72 | # 73 | [ 1, 0, 7, 6, 0, 0, 0, 0, 9], 74 | [ 0, 5, 6, 8, 0, 0, 4, 0, 0], 75 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0], 76 | ] 77 | 78 | extreme_2 = [ 79 | [ 0, 0, 0, 0, 0, 0, 7, 0, 3], 80 | [ 0, 0, 6, 0, 0, 8, 5, 4, 0], 81 | [ 5, 0, 0, 0, 7, 0, 0, 0, 0], 82 | # 83 | [ 0, 1, 9, 0, 0, 4, 8, 0, 0], 84 | [ 7, 0, 0, 0, 0, 0, 0, 0, 9], 85 | [ 0, 0, 8, 9, 0, 0, 2, 1, 0], 86 | # 87 | [ 0, 0, 0, 0, 5, 0, 0, 0, 2], 88 | [ 0, 5, 7, 3, 0, 0, 1, 0, 0], 89 | [ 4, 0, 3, 0, 0, 0, 0, 0, 0], 90 | ] 91 | 92 | extreme_3 = [ 93 | [ 8, 0, 1, 0, 9, 0, 0, 0, 0], 94 | [ 0, 7, 2, 0, 0, 1, 0, 0, 0], 95 | [ 0, 0, 0, 3, 0, 0, 8, 0, 0], 96 | # 97 | [ 5, 0, 0, 1, 0, 0, 0, 4, 0], 98 | [ 1, 0, 0, 0, 3, 0, 0, 0, 9], 99 | [ 0, 2, 0, 0, 0, 7, 0, 0, 5], 100 | # 101 | [ 0, 0, 5, 0, 0, 2, 0, 0, 0], 102 | [ 0, 0, 0, 4, 0, 0, 5, 9, 0], 103 | [ 0, 0, 0, 0, 8, 0, 4, 0, 3], 104 | ] 105 | 106 | extreme_4 = [ 107 | [ 7, 0, 0, 0, 0, 4, 0, 5, 0], 108 | [ 0, 0, 0, 5, 0, 0, 1, 0, 0], 109 | [ 0, 0, 0, 0, 0, 6, 0, 7, 8], 110 | # 111 | [ 0, 0, 4, 0, 0, 0, 8, 0, 0], 112 | [ 3, 5, 0, 0, 8, 0, 0, 1, 9], 113 | [ 0, 0, 8, 0, 0, 0, 2, 0, 0], 114 | # 115 | [ 5, 4, 0, 1, 0, 0, 0, 0, 0], 116 | [ 0, 0, 6, 0, 0, 5, 0, 0, 0], 117 | [ 0, 8, 0, 9, 0, 0, 0, 0, 1], 118 | ] 119 | 120 | #https://www.conceptispuzzles.com/index.aspx?uri=info/article/424 121 | hardest = [ 122 | [ 8, 0, 0, 0, 0, 0, 0, 0, 0], 123 | [ 0, 0, 3, 6, 0, 0, 0, 0, 0], 124 | [ 0, 7, 0, 0, 9, 0, 2, 0, 0], 125 | # 126 | [ 0, 5, 0, 0, 0, 7, 0, 0, 0], 127 | [ 0, 0, 0, 0, 4, 5, 7, 0, 0], 128 | [ 0, 0, 0, 1, 0, 0, 0, 3, 0], 129 | # 130 | [ 0, 0, 1, 0, 0, 0, 0, 6, 8], 131 | [ 0, 0, 8, 5, 0, 0, 0, 1, 0], 132 | [ 0, 9, 0, 0, 0, 0, 4, 0, 0], 133 | ] 134 | 135 | 136 | 137 | def analyze(rawpuzzle, name): 138 | puzzle = Puzzle(rawpuzzle) 139 | print(f'\nPuzzle ({name}):\n') 140 | puzzle.pprint() 141 | solver = Solver(puzzle) 142 | solution = solver.solve() 143 | 144 | if solution is not None: 145 | print(f'\nSolution ({name}):\n') 146 | solution.pprint() 147 | 148 | # 149 | simplest = solver.filter_cores(solution) 150 | if simplest is not None: 151 | solver.show_hints(simplest) 152 | # 153 | 154 | 155 | 156 | def main(): 157 | analyze(puzzle_1, "evil") 158 | analyze(extreme_1, "extreme #1") 159 | analyze(extreme_2, "extreme #2") 160 | analyze(extreme_3, "extreme #3") 161 | analyze(extreme_4, "extreme #4") 162 | analyze(hardest, "hardest") 163 | 164 | if __name__ == '__main__': 165 | main() 166 | print(Census.dump()) 167 | Yices.exit(True) 168 | -------------------------------------------------------------------------------- /examples/sudoku/sudoku_generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Using Yices to generate Sudoku puzzles analagous to https://github.com/arel/arels-sudoku-generator.git.""" 4 | 5 | import sys 6 | import random 7 | from datetime import date 8 | 9 | from SudokuLib import Puzzle 10 | from Solver import Solver 11 | 12 | from yices.Context import Context 13 | from yices.Census import Census 14 | from yices.Yices import Yices 15 | 16 | 17 | 18 | def make_solution(): 19 | """make_solution loops until we're able to fill all 81 cells with numbers, while satisfying the sudoku constraints.""" 20 | while True: 21 | try: 22 | puzzle = [[0]*9 for i in range(9)] # start with blank puzzle 23 | rows = [set(range(1,10)) for i in range(9)] # set of available 24 | columns = [set(range(1,10)) for i in range(9)] # numbers for each 25 | squares = [set(range(1,10)) for i in range(9)] # row, column and square 26 | for i in range(9): 27 | for j in range(9): 28 | # pick a number for cell (i,j) from the set of remaining available numbers 29 | choices = rows[i].intersection(columns[j]).intersection(squares[(i//3)*3 + j//3]) 30 | choice = random.choice(list(choices)) 31 | 32 | puzzle[i][j] = choice 33 | 34 | rows[i].discard(choice) 35 | columns[j].discard(choice) 36 | squares[(i//3)*3 + j//3].discard(choice) 37 | 38 | # success! every cell is filled. 39 | return puzzle 40 | 41 | except IndexError: 42 | # if there is an IndexError, we have worked ourselves in a corner (we just start over) 43 | pass 44 | 45 | 46 | def pluck(puzzle, cardinality): 47 | 48 | # prepare our solver and context 49 | solver = Solver(puzzle) 50 | context = Context() 51 | solver.assert_rules(context) 52 | 53 | # start with a set of all 81 cells, and tries to remove one (randomly) at a time 54 | # but not before checking that the cell can still be deduced from the remaining cells. 55 | cells = set(range(81)) 56 | cells_remaining = cells.copy() 57 | 58 | while len(cells) > cardinality and len(cells_remaining) != 0: 59 | cell = random.choice(list(cells_remaining)) 60 | cells_remaining.discard(cell) 61 | row, column = cell // 9, cell % 9 62 | val = puzzle.get_cell(row, column) 63 | assert val is not None 64 | if solver.erasable(context, row, column, val): 65 | puzzle.erase_cell(row, column) 66 | cells.discard(cell) 67 | 68 | context.dispose() 69 | 70 | return (puzzle, len(cells)) 71 | 72 | 73 | def run(n = 28, iterations=10): 74 | all_results = {} 75 | print(f'Using n={n} and iterations={iterations}') 76 | solution = make_solution() 77 | answer = Puzzle(solution) 78 | answer.pprint() 79 | 80 | best_so_far = 81 81 | 82 | for i in range(iterations): 83 | puzzle = answer.clone() 84 | (result, number_of_cells) = pluck(puzzle, n) 85 | if number_of_cells < best_so_far: 86 | best_so_far = number_of_cells 87 | all_results.setdefault(number_of_cells, []).append(result) 88 | if i % 100 == 0: 89 | print(f'iteration {i}: {best_so_far}') 90 | 91 | if number_of_cells <= n: 92 | print(f'success of iteration {i}') 93 | break 94 | 95 | return all_results 96 | 97 | def best(set_of_puzzles): 98 | # Could run some evaluation function here. For now just pick 99 | # the one with the fewest "givens". 100 | least = min(set_of_puzzles.keys()) 101 | print(f'least number of givens: {least}') 102 | return least, set_of_puzzles[least][0] 103 | 104 | def main(): 105 | results = None 106 | if len(sys.argv) == 3: 107 | n = int(sys.argv[1]) 108 | iterations = int(sys.argv[2]) 109 | results = run(n, iterations) 110 | else: 111 | results = run() 112 | 113 | least, puzzle = best(results) # use the best one of those puzzles. 114 | puzzle.pprint() # display that puzzle. 115 | puzzle.puzzle2path(f'puzzle_{least}_{str(date.today())}.sudoku') 116 | 117 | 118 | if __name__ == '__main__': 119 | main() 120 | print(Census.dump()) 121 | Yices.exit(True) 122 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import os 3 | import glob 4 | 5 | from codecs import open 6 | from os import path 7 | 8 | here = path.abspath(path.dirname(__file__)) 9 | 10 | #FIXME: 11 | # Get the long description from the README file 12 | # with open(path.join(here, 'README.rst'), encoding='utf-8') as f: 13 | # long_description = f.read() 14 | long_description = "This is the long description." 15 | 16 | # use the in house version number so we stay in synch with ourselves. 17 | from yices_api import yices_python_version 18 | 19 | setup( 20 | name='yices', 21 | version=yices_python_version, 22 | description='Python Bindings for the Yices SMT Solver', 23 | long_description=long_description, 24 | url='https://github.com/SRI-CSL/yices2_python_bindings', 25 | author='Sam Owre, Ian A. Mason, Bruno Dutertre, Ahmed Irfan.', 26 | author_email='ahmed.irfan@sri.com', 27 | 28 | 29 | include_package_data=True, 30 | 31 | packages=find_packages(), 32 | 33 | entry_points = { 34 | 'console_scripts': [ 35 | 'yices_python_info = yices_api:yices_python_info_main', 36 | ], 37 | }, 38 | 39 | license='GPLv3', 40 | 41 | #FIXME: pypi rejects the license and windows strings. Find ones that work. 42 | classifiers=[ 43 | 'Development Status :: 4 - Beta', 44 | 'Natural Language :: English', 45 | 'Intended Audience :: Science/Research', 46 | 'Intended Audience :: Developers', 47 | 'Topic :: Software Development :: Compilers', 48 | 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 49 | 'Operating System :: Microsoft :: Windows', 50 | 'Operating System :: MacOS', 51 | 'Operating System :: POSIX :: Linux', 52 | 'Operating System :: POSIX :: BSD', 53 | 'Programming Language :: C', 54 | 'Programming Language :: Python', 55 | 'Programming Language :: Python :: 2', 56 | 'Programming Language :: Python :: 2.7', 57 | 'Programming Language :: Python :: 3', 58 | 'Programming Language :: Python :: 3.4', 59 | 'Programming Language :: Python :: 3.5', 60 | 'Programming Language :: Python :: 3.6', 61 | 'Topic :: Scientific/Engineering :: Mathematics', 62 | 'Topic :: Software Development :: Libraries :: Python Modules', 63 | ], 64 | 65 | py_modules=['yices_api'], 66 | 67 | ) 68 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SRI-CSL/yices2_python_bindings/c1136bc48032ca5a59d534f9950f6e21343d767d/test/__init__.py -------------------------------------------------------------------------------- /test/context_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yices.Config import Config 4 | from yices.Context import Context 5 | from yices.Parameters import Parameters 6 | from yices.Status import Status 7 | from yices.Types import Types 8 | from yices.Terms import Terms 9 | from yices.YicesException import YicesException 10 | from yices.Yices import Yices 11 | 12 | # pylint: disable=R0914 13 | # pylint: disable=W0612 14 | 15 | def assertRaisesRegex(cxt, e, s): 16 | return cxt.assertRaisesRegex(e, s) 17 | 18 | class TestContext(unittest.TestCase): 19 | 20 | def setUp(self): 21 | Yices.init() 22 | 23 | def tearDown(self): 24 | Yices.exit() 25 | 26 | def test_config(self): 27 | cfg = Config() 28 | # Valid call 29 | cfg.set_config("mode", "push-pop") 30 | # Invalid name 31 | with assertRaisesRegex(self, YicesException, 'invalid parameter'): 32 | cfg.set_config("baz", "bar") 33 | # Invalid value 34 | with assertRaisesRegex(self, YicesException, 'value not valid for parameter'): 35 | cfg.set_config("mode", "bar") 36 | cfg.default_config_for_logic("QF_UFNIRA") 37 | cfg.dispose() 38 | 39 | def test_context(self): 40 | cfg = Config() 41 | ctx = Context(cfg) 42 | stat = ctx.status() 43 | ret = ctx.push() 44 | ret = ctx.pop() 45 | ctx.reset_context() 46 | ret = ctx.enable_option("arith-elim") 47 | ret = ctx.disable_option("arith-elim") 48 | stat = ctx.status() 49 | self.assertEqual(stat, 0) 50 | ctx.reset_context() 51 | bool_t = Types.bool_type() 52 | bvar1 = Terms.new_variable(bool_t) 53 | with assertRaisesRegex(self, YicesException, 'assertion contains a free variable'): 54 | ctx.assert_formula(bvar1) 55 | bv_t = Types.bv_type(3) 56 | bvvar1 = Terms.new_uninterpreted_term(bv_t, 'x') 57 | bvvar2 = Terms.new_uninterpreted_term(bv_t, 'y') 58 | bvvar3 = Terms.new_uninterpreted_term(bv_t, 'z') 59 | fmla1 = Terms.parse_term('(= x (bv-add y z))') 60 | fmla2 = Terms.parse_term('(bv-gt y 0b000)') 61 | fmla3 = Terms.parse_term('(bv-gt z 0b000)') 62 | ctx.assert_formula(fmla1) 63 | ctx.assert_formulas([fmla1, fmla2, fmla3]) 64 | smt_stat = ctx.check_context(None) 65 | self.assertEqual(smt_stat, Status.SAT) 66 | ctx.assert_blocking_clause() 67 | ctx.stop_search() 68 | param = Parameters() 69 | param.default_params_for_context(ctx) 70 | param.set_param("dyn-ack", "true") 71 | with assertRaisesRegex(self, YicesException, 'invalid parameter'): 72 | param.set_param("foo", "bar") 73 | with assertRaisesRegex(self, YicesException, 'value not valid for parameter'): 74 | param.set_param("dyn-ack", "bar") 75 | param.dispose() 76 | ctx.dispose() 77 | 78 | # pylint: disable=C0103 79 | def test_timeout(self): 80 | cfg = Config() 81 | cfg.default_config_for_logic('QF_NIA') 82 | ctx = Context(cfg) 83 | int_t = Types.int_type() 84 | [x, y, z] = [Terms.new_uninterpreted_term(int_t, id) for id in ['x', 'y', 'z']] 85 | # x, y, z > 0 86 | for var in [x, y, z]: 87 | ctx.assert_formula(Terms.arith_gt0_atom(var)) 88 | # x^3 + y^3 = z3 89 | [x3, y3, z3] = [Terms.product([var, var, var]) for var in [x, y, z]] 90 | lhs = Terms.sum([x3, y3]) 91 | eq = Terms.arith_eq_atom(lhs, z3) 92 | ctx.assert_formula(eq) 93 | status = ctx.check_context(timeout=1) 94 | self.assertEqual(status, Status.INTERRUPTED) 95 | ctx.dispose() 96 | cfg.dispose() 97 | 98 | if __name__ == '__main__': 99 | unittest.main() 100 | -------------------------------------------------------------------------------- /test/delegate_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | 4 | from yices.Config import Config 5 | from yices.Context import Context 6 | from yices.Delegates import Delegates 7 | from yices.Status import Status 8 | from yices.Terms import Terms 9 | from yices.Types import Types 10 | from yices.Yices import Yices 11 | 12 | def make_formulas(): 13 | tau = Types.bv_type(20) 14 | x0 = Terms.new_uninterpreted_term(tau, "x") 15 | y0 = Terms.new_uninterpreted_term(tau, "y") 16 | z0 = Terms.new_uninterpreted_term(tau, "z") 17 | f0 = Terms.bveq_atom(Terms.bvmul(x0, y0), Terms.bvconst_integer(20, 12289)) 18 | f1 = Terms.bveq_atom(Terms.bvmul(y0, z0), Terms.bvconst_integer(20, 20031)) 19 | f2 = Terms.bveq_atom(Terms.bvmul(x0, z0), Terms.bvconst_integer(20, 10227)) 20 | return [f0, f1, f2] 21 | 22 | def truncate(formulas, n): 23 | """Returns an array of the first n members of formulas.""" 24 | if n >= len(formulas): 25 | return formulas 26 | return formulas[0:n] 27 | 28 | def conjoin(formulas, n): 29 | return Terms.yand(truncate(formulas, n)) 30 | 31 | 32 | def notify(message): 33 | sys.stdout.write(message) 34 | sys.stdout.flush() 35 | 36 | 37 | class TestDelegates(unittest.TestCase): 38 | 39 | def setUp(self): 40 | Yices.init() 41 | 42 | def tearDown(self): 43 | Yices.exit() 44 | 45 | def delgado(self, delegate): 46 | 47 | if not Delegates.has_delegate(delegate): 48 | notify(f'delgado skipping missing delegate {delegate}\n') 49 | return 50 | 51 | formulas = make_formulas() 52 | 53 | bound = len(formulas) + 1 54 | 55 | for i in range(1, bound): 56 | config = Config() 57 | config.default_config_for_logic("QF_BV") 58 | context = Context(config) 59 | terms = truncate(formulas, i) 60 | context.assert_formulas(terms) 61 | status = context.check_context() 62 | notify(f'delgado status = {Status.name(status)} for i = {i}\n') 63 | self.assertEqual(status, Status.SAT if i < 3 else Status.UNSAT) 64 | config.dispose() 65 | context.dispose() 66 | 67 | 68 | for i in range(1, bound): 69 | model = [] 70 | terms = truncate(formulas, i) 71 | status = Delegates.check_formulas(terms, "QF_BV", delegate, model) 72 | notify(f'delagdo({delegate}) status = {Status.name(status)} for i = {i}\n') 73 | self.assertEqual(status, Status.SAT if i < 3 else Status.UNSAT) 74 | if status is Status.SAT: 75 | notify(f'delagdo({delegate}) model = {model[0].to_string(80, 100, 0)} for i = {i}\n') 76 | else: 77 | self.assertEqual(len(model), 0) 78 | 79 | 80 | for i in range(1, bound): 81 | model = [] 82 | term = conjoin(formulas, i) 83 | status = Delegates.check_formula(term, "QF_BV", delegate, model) 84 | notify(f'delagdo({delegate}) status = {Status.name(status)} for i = {i}\n') 85 | self.assertEqual(status, Status.SAT if i < 3 else Status.UNSAT) 86 | if status is Status.SAT: 87 | notify(f'delagdo({delegate}) model = {model[0].to_string(80, 100, 0)} for i = {i}\n') 88 | else: 89 | self.assertEqual(len(model), 0) 90 | 91 | 92 | 93 | 94 | 95 | def test_cadical(self): 96 | Yices.reset() 97 | self.delgado("cadical") 98 | 99 | def test_cryptominisat(self): 100 | Yices.reset() 101 | self.delgado("cryptominisat") 102 | 103 | def test_y2sat(self): 104 | Yices.reset() 105 | self.delgado("y2sat") 106 | -------------------------------------------------------------------------------- /test/dimacs_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | import os.path 4 | 5 | from yices.Config import Config 6 | from yices.Context import Context 7 | from yices.Dimacs import Dimacs 8 | from yices.Status import Status 9 | from yices.Terms import Terms 10 | from yices.Types import Types 11 | from yices.Yices import Yices 12 | 13 | def make_formulas(): 14 | tau = Types.bv_type(20) 15 | x0 = Terms.new_uninterpreted_term(tau, "x") 16 | y0 = Terms.new_uninterpreted_term(tau, "y") 17 | z0 = Terms.new_uninterpreted_term(tau, "z") 18 | f0 = Terms.bveq_atom(Terms.bvmul(x0, y0), Terms.bvconst_integer(20, 12289)) 19 | f1 = Terms.bveq_atom(Terms.bvmul(y0, z0), Terms.bvconst_integer(20, 20031)) 20 | f2 = Terms.bveq_atom(Terms.bvmul(x0, z0), Terms.bvconst_integer(20, 10227)) 21 | return [f0, f1, f2] 22 | 23 | def truncate(formulas, n): 24 | """Returns an array of the first n members of formulas.""" 25 | if n >= len(formulas): 26 | return formulas 27 | return formulas[0:n] 28 | 29 | def conjoin(formulas, n): 30 | return Terms.yand(truncate(formulas, n)) 31 | 32 | 33 | def notify(message): 34 | sys.stdout.write(message) 35 | sys.stdout.flush() 36 | 37 | 38 | class TestDimacs(unittest.TestCase): 39 | 40 | def setUp(self): 41 | Yices.init() 42 | 43 | def tearDown(self): 44 | Yices.exit() 45 | 46 | def test_dimacs(self): 47 | 48 | formulas = make_formulas() 49 | 50 | bound = len(formulas) + 1 51 | 52 | simplified = [None] * bound 53 | 54 | # first round, don't simplify the CNF 55 | for i in range(1, bound): 56 | simplify = False 57 | filename = f'/tmp/basic{1}.cnf' 58 | terms = truncate(formulas, i) 59 | 60 | file_ok, status = Dimacs.export_formulas(terms, filename, simplify) 61 | 62 | notify(f'Round 1: {file_ok}, {Status.name(status)} = export@{i}({terms}, {filename}, {simplify})\n') 63 | 64 | if file_ok: 65 | self.assertEqual(os.path.exists(filename), True) 66 | else: 67 | self.assertEqual(status in [Status.SAT, Status.UNSAT], True) 68 | 69 | term = Terms.yand(terms) 70 | file_ok_c, status_c = Dimacs.export_formula(term, filename, simplify) 71 | 72 | notify(f'Round 1: {file_ok_c}, {Status.name(status_c)} = export@{i}({term}, {filename}, {simplify})\n') 73 | 74 | 75 | 76 | # second round, simplify the CNF 77 | for i in range(1, bound): 78 | simplify = True 79 | filename = f'/tmp/simplify{i}.cnf' 80 | terms = truncate(formulas, i) 81 | 82 | file_ok, status = Dimacs.export_formulas(terms, filename, simplify) 83 | 84 | # save the status for later 85 | simplified[i] = status 86 | 87 | notify(f'Round 2: {file_ok}, {Status.name(status)} = export@{i}({terms}, {filename}, {simplify})\n') 88 | 89 | if file_ok: 90 | self.assertEqual(os.path.exists(filename), True) 91 | else: 92 | self.assertEqual(status in [Status.SAT, Status.UNSAT], True) 93 | 94 | term = Terms.yand(terms) 95 | file_ok_c, status_c = Dimacs.export_formula(term, filename, simplify) 96 | 97 | notify(f'Round 2: {file_ok_c}, {Status.name(status_c)} = export@{i}({term}, {filename}, {simplify})\n') 98 | 99 | self.assertEqual(status_c, simplified[i]) 100 | 101 | 102 | # third round check the results 103 | for i in range(1, bound): 104 | config = Config() 105 | config.default_config_for_logic("QF_BV") 106 | context = Context(config) 107 | terms = truncate(formulas, i) 108 | context.assert_formulas(terms) 109 | status = context.check_context() 110 | notify(f'Round 3: status = {Status.name(status)} for i = {i}\n') 111 | self.assertEqual(status, simplified[i]) 112 | config.dispose() 113 | context.dispose() 114 | -------------------------------------------------------------------------------- /test/error_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yices.Terms import Terms 4 | from yices.Types import Types 5 | from yices.Yices import Yices 6 | from yices.YicesException import YicesException 7 | 8 | def assertRaisesRegex(cxt, e, s): 9 | return cxt.assertRaisesRegex(e, s) 10 | 11 | 12 | class TestError(unittest.TestCase): 13 | 14 | def setUp(self): 15 | Yices.init() 16 | 17 | def tearDown(self): 18 | Yices.exit() 19 | 20 | def test_error(self): 21 | Yices.reset() 22 | 23 | # First with no error 24 | errcode = Yices.error_code() 25 | self.assertEqual(errcode, 0) 26 | errep = Yices.error_report() 27 | self.assertEqual(errep.code, 0) 28 | Yices.clear_error() 29 | errstr = Yices.error_string() 30 | self.assertEqual(errstr, 'no error') 31 | Yices.print_error(1) 32 | 33 | # Illegal - only scalar or uninterpreted types allowed 34 | bool_t = Types.bool_type() 35 | self.assertTrue(Types.is_bool(bool_t)) 36 | with assertRaisesRegex(self, YicesException, 'The function yices_constant failed because: invalid type in constant creation'): 37 | Terms.constant(bool_t, 0) 38 | Yices.clear_error() 39 | errpt = Yices.error_report() 40 | self.assertEqual(Yices.error_code(), 0) 41 | self.assertEqual(Yices.error_code(), errpt.code) 42 | errstr = Yices.error_string() 43 | self.assertEqual(errstr, 'no error') 44 | Yices.print_error(1) 45 | Yices.clear_error() 46 | self.assertEqual(Yices.error_code(), 0) 47 | -------------------------------------------------------------------------------- /test/interpolationcontext_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yices.Config import Config 4 | from yices.Context import Context 5 | from yices.Parameters import Parameters 6 | from yices.Status import Status 7 | from yices.InterpolationContext import InterpolationContext 8 | from yices.Types import Types 9 | from yices.Terms import Terms 10 | #from yices.YicesException import YicesException 11 | from yices.Yices import Yices 12 | 13 | # pylint: disable=E0401 14 | from .utils import define_const, assert_formula 15 | 16 | class TestInterpolationContext(unittest.TestCase): 17 | 18 | def setUp(self): 19 | Yices.init() 20 | self.real_t = Types.real_type() 21 | 22 | def tearDown(self): 23 | Yices.exit() 24 | 25 | 26 | def test_trivial_ok(self): 27 | cfg = Config() 28 | cfg.set_config("solver-type", "mcsat") 29 | cfg.set_config("model-interpolation", "true") 30 | ctx_a = Context(cfg) 31 | ctx_b = Context(cfg) 32 | param = Parameters() 33 | param.default_params_for_context(ctx_a) 34 | ictx = InterpolationContext(ctx_a, ctx_b) 35 | status = ictx.check(param, True) 36 | print(f'Status = {Status.name(status)}') 37 | print(f'Yices.error_string() = {Yices.error_string()}') 38 | self.assertEqual(status, Status.SAT) 39 | param.dispose() 40 | ctx_a.dispose() 41 | ctx_b.dispose() 42 | cfg.dispose() 43 | 44 | def test_trivial_bad(self): 45 | cfg = Config() 46 | ctx_a = Context(cfg) 47 | ctx_b = Context(cfg) 48 | param = Parameters() 49 | param.default_params_for_context(ctx_a) 50 | ictx = InterpolationContext(ctx_a, ctx_b) 51 | status = ictx.check(param, True) 52 | print(f'Status = {Status.name(status)}') 53 | print(f'Yices.error_string() = {Yices.error_string()}') 54 | self.assertEqual(status, Status.ERROR) 55 | self.assertEqual(Yices.error_string(), "operation not supported by the context") 56 | param.dispose() 57 | ctx_a.dispose() 58 | ctx_b.dispose() 59 | cfg.dispose() 60 | 61 | 62 | def test_model_sat(self): 63 | cfg = Config() 64 | cfg.set_config("solver-type", "mcsat") 65 | cfg.set_config("model-interpolation", "true") 66 | ctx_a = Context(cfg) 67 | ctx_b = Context(cfg) 68 | 69 | r1 = define_const('r1', self.real_t) 70 | r2 = define_const('r2', self.real_t) 71 | assert_formula('(> r1 3)', ctx_a) 72 | assert_formula('(< r1 4)', ctx_a) 73 | assert_formula('(< (- r1 r2) 0)', ctx_a) 74 | 75 | 76 | ictx = InterpolationContext(ctx_a, ctx_b) 77 | param = Parameters() 78 | param.default_params_for_context(ctx_a) 79 | status = ictx.check(param, True) 80 | print(f'Yices.error_string() = {Yices.error_string()}') 81 | print(f'Status = {Status.name(status)}') 82 | print(f'Model = {ictx.model}') 83 | self.assertEqual(status, Status.SAT) 84 | 85 | v1 = ictx.model.get_fraction_value(r1) 86 | v2 = ictx.model.get_fraction_value(r2) 87 | 88 | print(v1, v2) 89 | self.assertEqual(v1.numerator, 7) 90 | self.assertEqual(v1.denominator, 2) 91 | self.assertEqual(v2.numerator, 5) 92 | self.assertEqual(v2.denominator, 1) 93 | 94 | param.dispose() 95 | ctx_a.dispose() 96 | ctx_b.dispose() 97 | cfg.dispose() 98 | 99 | 100 | def test_model_unsat(self): 101 | cfg = Config() 102 | cfg.set_config("solver-type", "mcsat") 103 | cfg.set_config("model-interpolation", "true") 104 | ctx_a = Context(cfg) 105 | ctx_b = Context(cfg) 106 | 107 | define_const('r1', self.real_t) 108 | define_const('r2', self.real_t) 109 | assert_formula('(> r1 3)', ctx_a) 110 | assert_formula('(< r1 4)', ctx_a) 111 | assert_formula('(< (- r1 r2) 0)', ctx_a) 112 | 113 | assert_formula('(< r2 3)', ctx_b) 114 | 115 | ictx = InterpolationContext(ctx_a, ctx_b) 116 | param = Parameters() 117 | param.default_params_for_context(ctx_a) 118 | status = ictx.check(param, True) 119 | print(f'Yices.error_string() = {Yices.error_string()}') 120 | print(f'Status = {Status.name(status)}') 121 | print(f'Interpolant = {Terms.to_string(ictx.interpolant)}') 122 | self.assertEqual(status, Status.UNSAT) 123 | self.assertTrue(ictx.interpolant > 0) 124 | 125 | param.dispose() 126 | ctx_a.dispose() 127 | ctx_b.dispose() 128 | cfg.dispose() 129 | 130 | 131 | 132 | 133 | 134 | if __name__ == '__main__': 135 | unittest.main() 136 | -------------------------------------------------------------------------------- /test/model_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from fractions import Fraction 4 | 5 | from yices.Config import Config 6 | from yices.Context import Context 7 | from yices.Model import Model 8 | from yices.Parameters import Parameters 9 | from yices.Status import Status 10 | from yices.Types import Types 11 | from yices.Terms import Terms 12 | from yices.Yices import Yices 13 | 14 | # pylint: disable=E0401 15 | from .utils import define_const, assert_formula 16 | 17 | # pylint: disable=W0612 18 | 19 | class TestModels(unittest.TestCase): 20 | 21 | # pylint: disable=W0601 22 | 23 | def setUp(self): 24 | # this is required for some strange reason. 25 | # seems like yices/__init__.py does not get evaluated 26 | Yices.init() 27 | self.cfg = Config() 28 | self.ctx = Context(self.cfg) 29 | self.param = Parameters() 30 | self.param.default_params_for_context(self.ctx) 31 | global bool_t, int_t, real_t 32 | bool_t = Types.bool_type() 33 | int_t = Types.int_type() 34 | real_t = Types.real_type() 35 | 36 | 37 | def tearDown(self): 38 | self.cfg.dispose() 39 | self.ctx.dispose() 40 | self.param.dispose() 41 | Yices.exit() 42 | 43 | def test_bool_models(self): 44 | b1 = define_const('b1', bool_t) 45 | b2 = define_const('b2', bool_t) 46 | b3 = define_const('b3', bool_t) 47 | b_fml1 = Terms.parse_term('(or b1 b2 b3)') 48 | self.ctx.assert_formula(b_fml1) 49 | self.assertEqual(self.ctx.check_context(self.param), Status.SAT) 50 | b_mdl1 = Model.from_context(self.ctx, 1) 51 | self.assertNotEqual(b_mdl1, None) 52 | bval1 = b_mdl1.get_bool_value(b1) 53 | bval2 = b_mdl1.get_bool_value(b2) 54 | bval3 = b_mdl1.get_bool_value(b3) 55 | self.assertEqual(bval1, False) 56 | self.assertEqual(bval2, False) 57 | self.assertEqual(bval3, True) 58 | b_fmla2 = Terms.parse_term('(not b3)') 59 | self.ctx.assert_formula(b_fmla2) 60 | self.assertEqual(self.ctx.check_context(self.param), Status.SAT) 61 | b_mdl1 = Model.from_context(self.ctx, 1) 62 | self.assertNotEqual(b_mdl1, None) 63 | bval1 = b_mdl1.get_bool_value(b1) 64 | bval2 = b_mdl1.get_bool_value(b2) 65 | bval3 = b_mdl1.get_bool_value(b3) 66 | val1 = b_mdl1.get_value(b1) 67 | val2 = b_mdl1.get_value(b2) 68 | val3 = b_mdl1.get_value(b3) 69 | self.assertEqual(bval1, False) 70 | self.assertEqual(bval2, True) 71 | self.assertEqual(bval3, False) 72 | self.assertEqual(bval1, val1) 73 | self.assertEqual(bval2, val2) 74 | self.assertEqual(bval3, val3) 75 | 76 | def test_int_models(self): 77 | ''' int32, int64 ''' 78 | i1 = define_const('i1', int_t) 79 | i2 = define_const('i2', int_t) 80 | assert_formula('(> i1 3)', self.ctx) 81 | assert_formula('(< i2 i1)', self.ctx) 82 | self.assertEqual(self.ctx.check_context(self.param), Status.SAT) 83 | mdl = Model.from_context(self.ctx, 1) 84 | i32v1 = mdl.get_integer_value(i1) 85 | i32v2 = mdl.get_integer_value(i2) 86 | self.assertEqual(i32v1, 4) 87 | self.assertEqual(i32v2, 3) 88 | mdl.print_to_fd(1) 89 | mdl.print_to_fd(1, 80, 100, 0) 90 | mdlstr = mdl.to_string(80, 100, 0) 91 | self.assertEqual(mdlstr, '(= i1 4)\n(= i2 3)') 92 | 93 | def test_rat_models(self): 94 | ''' rational32, rational64, double ''' 95 | r1 = define_const('r1', real_t) 96 | r2 = define_const('r2', real_t) 97 | assert_formula('(> r1 3)', self.ctx) 98 | assert_formula('(< r1 4)', self.ctx) 99 | assert_formula('(< (- r1 r2) 0)', self.ctx) 100 | self.assertEqual(self.ctx.check_context(self.param), Status.SAT) 101 | mdl = Model.from_context(self.ctx, 1) 102 | v1 = mdl.get_fraction_value(r1) 103 | v2 = mdl.get_fraction_value(r2) 104 | # r1 = 7/2, r2 = 4/1 105 | self.assertEqual(v1.numerator, 7) 106 | self.assertEqual(v1.denominator, 2) 107 | self.assertEqual(v2.numerator, 4) 108 | self.assertEqual(v2.denominator, 1) 109 | rdoub1 = mdl.get_float_value(r1) 110 | rdoub2 = mdl.get_float_value(r2) 111 | self.assertEqual(rdoub1, 3.5) 112 | self.assertEqual(rdoub2, 4.0) 113 | val1 = mdl.get_value(r1) 114 | val2 = mdl.get_value(r2) 115 | self.assertEqual(val1, 3.5) 116 | self.assertEqual(val2, 4.0) 117 | 118 | 119 | def test_bv_models(self): 120 | bv_t = Types.bv_type(3) 121 | bv1 = define_const('bv1', bv_t) 122 | bv2 = define_const('bv2', bv_t) 123 | bv3 = define_const('bv3', bv_t) 124 | fmla1 = Terms.parse_term('(= bv1 (bv-add bv2 bv3))') 125 | fmla2 = Terms.parse_term('(bv-gt bv2 0b000)') 126 | fmla3 = Terms.parse_term('(bv-gt bv3 0b000)') 127 | self.ctx.assert_formula(fmla1) 128 | self.ctx.assert_formulas([fmla1, fmla2, fmla3]) 129 | self.assertEqual(self.ctx.check_context(self.param), Status.SAT) 130 | mdl1 = Model.from_context(self.ctx, 1) 131 | val1 = mdl1.get_value(bv1) 132 | self.assertEqual(val1[0], 0) 133 | self.assertEqual(val1[1], 0) 134 | self.assertEqual(val1[2], 0) 135 | val2 = mdl1.get_value(bv2) 136 | self.assertEqual(val2[0], 0) 137 | self.assertEqual(val2[1], 0) 138 | self.assertEqual(val2[2], 1) 139 | val3 = mdl1.get_value(bv3) 140 | self.assertEqual(val3[0], 0) 141 | self.assertEqual(val3[1], 0) 142 | self.assertEqual(val3[2], 1) 143 | mdl1.dispose() 144 | 145 | 146 | def test_tuple_models(self): 147 | tup_t = Types.new_tuple_type([bool_t, real_t, int_t]) 148 | t1 = define_const('t1', tup_t) 149 | assert_formula('(ite (select t1 1) (< (select t1 2) (select t1 3)) (> (select t1 2) (select t1 3)))', self.ctx) 150 | self.assertEqual(self.ctx.check_context(self.param), Status.SAT) 151 | mdl = Model.from_context(self.ctx, 1) 152 | mdlstr = mdl.to_string(80, 100, 0) 153 | self.assertEqual(mdlstr, '(= t1 (mk-tuple false 1 0))') 154 | val = mdl.get_value(t1) 155 | self.assertEqual(val[0], False) 156 | self.assertEqual(val[1], 1) 157 | self.assertEqual(val[2], 0) 158 | 159 | 160 | def test_model_from_map(self): 161 | bv_t = Types.bv_type(8) 162 | i1 = define_const('i1', int_t) 163 | r1 = define_const('r1', real_t) 164 | bv1 = define_const('bv1', bv_t) 165 | iconst1 = Terms.integer(42) 166 | rconst1 = Terms.rational(13, 131) 167 | bvconst1 = Terms.bvconst_integer(8, 134) 168 | mapping = { i1: iconst1, r1: rconst1, bv1: bvconst1 } 169 | mdl = Model.from_map(mapping) 170 | mdlstr = mdl.to_string(80, 100, 0) 171 | self.assertEqual(mdlstr, '(= i1 42)\n(= r1 13/131)\n(= bv1 0b10000110)') 172 | mdl.dispose() 173 | 174 | 175 | def test_implicant(self): 176 | i1 = define_const('i1', int_t) 177 | assert_formula('(and (> i1 2) (< i1 8) (/= i1 4))', self.ctx) 178 | self.assertEqual(self.ctx.check_context(self.param), Status.SAT) 179 | mdl = Model.from_context(self.ctx, 1) 180 | mdlstr = mdl.to_string(80, 100, 0) 181 | self.assertEqual(mdlstr, '(= i1 7)') 182 | fml = Terms.parse_term('(>= i1 3)') 183 | tarray = mdl.implicant_for_formula(fml) 184 | self.assertEqual(len(tarray), 1) 185 | implstr = Terms.to_string(tarray[0], 200, 10, 0) 186 | self.assertEqual(implstr, '(>= (+ -3 i1) 0)') 187 | fml2 = Terms.parse_term('(<= i1 9)') 188 | tarray2 = mdl.implicant_for_formulas([fml, fml2]) 189 | self.assertEqual(len(tarray2), 2) 190 | implstr2 = Terms.to_string(tarray2[0], 200, 10, 0) 191 | self.assertEqual(implstr2, '(>= (+ -3 i1) 0)') 192 | implstr3 = Terms.to_string(tarray2[1], 200, 10, 0) 193 | self.assertEqual(implstr3, '(>= (+ 9 (* -1 i1)) 0)') 194 | 195 | 196 | 197 | def test_scalar_models(self): 198 | scalar_t = Types.new_scalar_type(10) 199 | sc1 = define_const('sc1', scalar_t) 200 | sc2 = define_const('sc2', scalar_t) 201 | sc3 = define_const('sc3', scalar_t) 202 | assert_formula('(/= sc1 sc2)', self.ctx) 203 | assert_formula('(/= sc1 sc3)', self.ctx) 204 | self.assertEqual(self.ctx.check_context(self.param), Status.SAT) 205 | mdl = Model.from_context(self.ctx, 1) 206 | val1 = mdl.get_scalar_value(sc1) 207 | val2 = mdl.get_scalar_value(sc2) 208 | val3 = mdl.get_scalar_value(sc3) 209 | self.assertEqual(val1, 9) 210 | self.assertEqual(val2, 8) 211 | self.assertEqual(val3, 8) 212 | self.assertEqual(Terms.is_scalar(sc1), True) 213 | sc1val = mdl.get_value_as_term(sc1) 214 | self.assertEqual(Terms.is_scalar(sc1val), True) 215 | self.assertEqual(mdl.get_value(sc1), sc1val) 216 | 217 | 218 | 219 | def test_function_models(self): 220 | funtype = Types.new_function_type([int_t, bool_t, real_t], real_t) 221 | ftystr = Types.to_string(funtype, 100, 80, 0) 222 | Types.print_to_fd(1, funtype, 100, 80, 0) 223 | self.assertEqual(ftystr, '(-> int bool real real)') 224 | fun1 = define_const('fun1', funtype) 225 | b1 = define_const('b1', bool_t) 226 | i1 = define_const('i1', int_t) 227 | r1 = define_const('r1', real_t) 228 | assert_formula('(> (fun1 i1 b1 r1) (fun1 (+ i1 1) (not b1) (- r1 i1)))', self.ctx) 229 | self.assertEqual(self.ctx.check_context(self.param), Status.SAT) 230 | mdl = Model.from_context(self.ctx, 1) 231 | mdlstr = mdl.to_string(80, 100, 0) 232 | self.assertEqual(mdlstr, '(= b1 false)\n(= i1 1463)\n(= r1 -579)\n(function fun1\n (type (-> int bool real real))\n (= (fun1 1463 false -579) 1)\n (= (fun1 1464 true -2042) 0)\n (default 2))') 233 | fun1val = mdl.get_value(fun1) 234 | self.assertEqual(fun1val((1463, False, -579)), 1) 235 | self.assertEqual(fun1val((1464, True, -2042)), 0) 236 | self.assertEqual(fun1val((1462, True, -2041)), 2) 237 | 238 | # pylint: disable=C0103 239 | def test_model_support(self): 240 | x = define_const('x', real_t) 241 | y = define_const('y', real_t) 242 | z = define_const('z', real_t) 243 | formula = Terms.parse_term('(> x 0)') 244 | t0 = Terms.parse_term('(ite (> x 0) (+ x z) y)') 245 | t1 = Terms.parse_term('(+ (* x z) y)') 246 | self.ctx.assert_formula(formula) 247 | self.assertEqual(self.ctx.check_context(), Status.SAT) 248 | mdl = Model.from_context(self.ctx, 1) 249 | support = mdl.support_for_term(t0) 250 | self.assertEqual(len(support), 2) 251 | self.assertEqual(support[0], x) 252 | self.assertEqual(support[1], z) 253 | support = mdl.support_for_terms([t0, t1]) 254 | self.assertEqual(len(support), 3) 255 | self.assertEqual(support[0], x) 256 | self.assertEqual(support[1], y) 257 | self.assertEqual(support[2], z) 258 | 259 | def test_2_6_4(self): 260 | mdl = Model() 261 | # bool set and then get 262 | bt = define_const('bt', bool_t) 263 | mdl.set_bool(bt, True) 264 | val = mdl.get_bool_value(bt) 265 | self.assertEqual(val, True) 266 | # integer set and then get 267 | it = define_const('it', int_t) 268 | mdl.set_integer(it, 123456789) 269 | val = mdl.get_integer_value(it) 270 | self.assertEqual(val, 123456789) 271 | # fraction set and get 272 | rt = define_const('rt', real_t) 273 | frac = Fraction(1000, 3000) 274 | mdl.set_fraction(rt, frac) 275 | val = mdl.get_fraction_value(rt) 276 | self.assertEqual(val, frac) 277 | 278 | mdl.dispose() 279 | -------------------------------------------------------------------------------- /test/terms_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yices.Terms import Terms 4 | from yices.Types import Types 5 | from yices.Yices import Yices 6 | 7 | # pylint: disable=R0914 8 | # pylint: disable=W0612 9 | # pylint: disable=R0915 10 | 11 | class TestTerms(unittest.TestCase): 12 | 13 | def setUp(self): 14 | Yices.init() 15 | 16 | def tearDown(self): 17 | Yices.exit() 18 | 19 | def test_terms(self): 20 | 21 | self.assertTrue(Yices.is_inited()) 22 | 23 | true_ = Terms.true() 24 | false_ = Terms.false() 25 | bool_t = Types.bool_type() 26 | int_t = Types.int_type() 27 | unint_t = Types.new_uninterpreted_type() 28 | self.assertNotEqual(true_, false_) 29 | const1 = Terms.constant(unint_t, 0) 30 | const2 = Terms.new_uninterpreted_term(unint_t) 31 | bconst1 = Terms.new_uninterpreted_term(bool_t) 32 | iconst1 = Terms.new_uninterpreted_term(int_t) 33 | var1 = Terms.new_variable(unint_t) 34 | bvar1 = Terms.new_variable(bool_t) 35 | ivar1 = Terms.new_variable(int_t) 36 | ivar2 = Terms.new_variable(int_t) 37 | ivar3 = Terms.new_variable(int_t) 38 | ivar4 = Terms.new_variable(int_t) 39 | zero = Terms.zero() 40 | int1 = Terms.integer(13) 41 | int2 = Terms.integer(17) 42 | self.assertEqual(zero, Terms.integer(0)) 43 | fun1_t = Types.new_function_type([int_t], bool_t) 44 | fun1 = Terms.new_variable(fun1_t) 45 | app1 = Terms.application(fun1, [int1]) 46 | fun2_t = Types.new_function_type([int_t, int_t], bool_t) 47 | fun2 = Terms.new_variable(fun2_t) 48 | app2 = Terms.application(fun2, [int1, int1]) 49 | fun3_t = Types.new_function_type([int_t, int_t, int_t], bool_t) 50 | fun3 = Terms.new_variable(fun3_t) 51 | app3 = Terms.application(fun3, [int1, int1, int1]) 52 | tup3_t = Types.new_tuple_type([bool_t, int_t, unint_t]) 53 | tupconst1 = Terms.new_variable(tup3_t) 54 | fun4_t = Types.new_function_type([int_t, int_t, int_t, int_t], bool_t) 55 | fun4 = Terms.new_variable(fun4_t) 56 | app4 = Terms.application(fun4,[int1, int2, iconst1, ivar1] ) 57 | ite1 = Terms.ite(bconst1, int1, int2) 58 | eq1 = Terms.eq(int1, int1) 59 | neq1 = Terms.neq(int1, int1) 60 | not1 = Terms.ynot(false_) 61 | or1 = Terms.yor([false_, eq1, neq1, app4, false_]) 62 | and1 = Terms.yand([false_, eq1, neq1, app4, false_]) 63 | xor1 = Terms.xor([false_, eq1, neq1, app4, false_]) 64 | or2 = Terms.yor([or1, and1]) 65 | and2 = Terms.yand([or1, and1]) 66 | xor2 = Terms.xor([or1, and1]) 67 | or3 = Terms.yor([or1, and1, or2]) 68 | and3 = Terms.yand([or1, and1, and2]) 69 | xor3 = Terms.xor([or1, and1, xor2]) 70 | iff1 = Terms.iff(and1, or1) 71 | implies1 = Terms.implies(and1, or1) 72 | tup1 = Terms.tuple([int1, int2, iconst1, ivar1]) 73 | pair1 = Terms.tuple([eq1, xor2]) 74 | triple1 = Terms.tuple([ite1, fun4, or3]) 75 | select1 = Terms.select(2, tup1) 76 | select2 = Terms.select(2, tupconst1) 77 | tupup1 = Terms.tuple_update(tup1, 2, int2) 78 | update1 = Terms.update(fun1, [int1], false_) 79 | update2 = Terms.update(fun2, [int1, int1], false_) 80 | update3 = Terms.update(fun3, [int1, int1, int1], false_) 81 | update4 = Terms.update(fun4, [int1, int2, iconst1, ivar1], false_) 82 | distinct1 = Terms.distinct([int1, int2, iconst1, ivar1]) 83 | var2 = Terms.new_variable(unint_t) 84 | vareq = Terms.eq(var1, var2) 85 | forall1 = Terms.forall([var1, var2], vareq) 86 | exists1 = Terms.exists([var1, var2], vareq) 87 | lambda1 = Terms.ylambda([var1, var2], vareq) 88 | zero = Terms.zero() 89 | int64_1 = Terms.integer(42) 90 | rat32_1 = Terms.rational(13, 7) 91 | rat64_1 = Terms.rational(-47, 111) 92 | rat1 = Terms.parse_rational('-3/117') 93 | float1 = Terms.parse_float('-3.117e-2') 94 | add1 = Terms.add(int1, int1) 95 | sub1 = Terms.sub(int1, zero) 96 | neg1 = Terms.neg(int1) 97 | self.assertEqual(Terms.neg(zero), zero) 98 | self.assertNotEqual(neg1, int1) 99 | mul1 = Terms.mul(int1, int1) 100 | square1 = Terms.square(int1) 101 | self.assertEqual(mul1, square1) 102 | power1 = Terms.power(int1, 4) 103 | sum1 = Terms.sum([int1, int2, iconst1, ivar1]) 104 | product1 = Terms.product([int1, int2, iconst1, ivar1]) 105 | product2 = Terms.product([ivar1, ivar2, ivar3, ivar4]) 106 | div1 = Terms.division(int1, int1) 107 | idiv1 = Terms.idiv(int1, int1) 108 | imod1 = Terms.imod(int1, int1) 109 | divatom1 = Terms.divides_atom(int1, int1) 110 | intatom1 = Terms.is_int_atom(int1) 111 | abs1 = Terms.abs(neg1) 112 | self.assertEqual(abs1, int1) 113 | floor1 = Terms.floor(rat1) 114 | ceil1 = Terms.ceil(rat1) 115 | areqatom1 = Terms.arith_eq_atom(int1, zero) 116 | arneqatom1 = Terms.arith_neq_atom(int1, zero) 117 | argeqatom1 = Terms.arith_geq_atom(int1, zero) 118 | arleqatom1 = Terms.arith_leq_atom(int1, zero) 119 | argtatom1 = Terms.arith_gt_atom(int1, zero) 120 | arltatom1 = Terms.arith_lt_atom(int1, zero) 121 | areq0atom1 = Terms.arith_eq0_atom(int1) 122 | arneq0atom1 = Terms.arith_neq0_atom(int1) 123 | argeq0atom1 = Terms.arith_geq0_atom(int1) 124 | arleq0atom1 = Terms.arith_leq0_atom(int1) 125 | argt0atom1 = Terms.arith_gt0_atom(int1) 126 | arlt0atom1 = Terms.arith_lt0_atom(int1) 127 | bv_t = Types.bv_type(8) 128 | bvconstu32_1 = Terms.bvconst_integer(8, 42) 129 | bvconstu64_1 = Terms.bvconst_integer(8, 42) 130 | bvconst32_1 = Terms.bvconst_integer(8, 42) 131 | bvconst64_1 = Terms.bvconst_integer(8, 42) 132 | bvconstzero_1 = Terms.bvconst_zero(16) 133 | bvconstone_1 = Terms.bvconst_one(16) 134 | bvconstminusone_1 = Terms.bvconst_minus_one(32) 135 | bvvar1 = Terms.new_variable(bv_t) 136 | bvvar2 = Terms.new_variable(bv_t) 137 | bvvar3 = Terms.new_variable(bv_t) 138 | bvvar4 = Terms.new_variable(bv_t) 139 | bvbin1 = Terms.parse_bvbin('100101') 140 | bvhex1 = Terms.parse_bvhex('f0a1b3') 141 | bvadd1 = Terms.bvadd(bvbin1, bvbin1) 142 | bvsub1 = Terms.bvsub(bvbin1, bvbin1) 143 | bvneg1 = Terms.bvneg(bvbin1) 144 | bvmul1 = Terms.bvmul(bvbin1, bvbin1) 145 | bvsquare1 = Terms.bvsquare(bvbin1) 146 | bvpower1 = Terms.bvpower(bvbin1, 3) 147 | bvdiv1 = Terms.bvdiv(bvbin1, bvbin1) 148 | bvrem1 = Terms.bvrem(bvbin1, bvbin1) 149 | bvsdiv1 = Terms.bvsdiv(bvbin1, bvbin1) 150 | bvsrem1 = Terms.bvsrem(bvbin1, bvbin1) 151 | bvsmod1 = Terms.bvsmod(bvbin1, bvbin1) 152 | bvnot1 = Terms.bvnot(bvbin1) 153 | bvnand1 = Terms.bvnand(bvbin1, bvbin1) 154 | bvnor1 = Terms.bvnor(bvbin1, bvbin1) 155 | bvxnor1 = Terms.bvxnor(bvbin1, bvbin1) 156 | bvshl1 = Terms.bvshl(bvbin1, bvbin1) 157 | bvlshr1 = Terms.bvlshr(bvbin1, bvbin1) 158 | bvashr1 = Terms.bvashr(bvbin1, bvbin1) 159 | bvand1 = Terms.bvand([bvbin1, bvbin1, bvbin1, bvbin1]) 160 | bvor1 = Terms.bvor([bvbin1, bvbin1, bvbin1, bvbin1]) 161 | bvand2_1 = Terms.bvand([bvbin1, bvbin1]) 162 | bvor2_1 = Terms.bvor([bvbin1, bvbin1]) 163 | bvxor2_1 = Terms.bvxor([bvbin1, bvbin1]) 164 | bvand3_1 = Terms.bvand([bvbin1, bvbin1, bvbin1]) 165 | bvor3_1 = Terms.bvor([bvbin1, bvbin1, bvbin1]) 166 | bvxor3_1 = Terms.bvxor([bvbin1, bvbin1, bvbin1]) 167 | bvsum1 = Terms.bvsum([bvbin1, bvbin1, bvbin1, bvbin1]) 168 | bvsum2 = Terms.bvsum([bvvar1, bvvar2, bvvar3, bvvar4]) 169 | bvproduct1 = Terms.bvproduct([bvbin1, bvbin1, bvbin1, bvbin1]) 170 | shleft0_1 = Terms.shift_left0(bvbin1, 5) 171 | shleft1_1 = Terms.shift_left1(bvbin1, 4) 172 | shright0_1 = Terms.shift_right0(bvbin1, 3) 173 | shright1_1 = Terms.shift_right1(bvbin1, 2) 174 | ashright_1 = Terms.ashift_right(bvbin1, 1) 175 | rotleft_1 = Terms.rotate_left(bvbin1, 6) 176 | rotright_1 = Terms.rotate_right(bvbin1, 5) 177 | bvextract1 = Terms.bvextract(bvbin1, 2, 4) 178 | bvconcat2_1 = Terms.bvconcat([bvbin1, bvbin1]) 179 | bvconcat_1 = Terms.bvconcat([bvbin1, bvbin1, bvbin1, bvbin1]) 180 | bvrepeat1 = Terms.bvrepeat(bvbin1, 8) 181 | signext1 = Terms.sign_extend(bvbin1, 3) 182 | zeroext1 = Terms.zero_extend(bvbin1, 4) 183 | redand1 = Terms.redand(bvbin1) 184 | redor1 = Terms.redor(bvbin1) 185 | redcomp1 = Terms.redcomp(bvbin1, bvbin1) 186 | bvarray1 = Terms.bvarray([true_, false_, true_, false_]) 187 | bitextract1 = Terms.bitextract(bvbin1, 3) 188 | bveqatom1 = Terms.bveq_atom(bvbin1, bvbin1) 189 | bvneqatom1 = Terms.bvneq_atom(bvbin1, bvbin1) 190 | bvgeatom1 = Terms.bvge_atom(bvbin1, bvbin1) 191 | bvgtatom1 = Terms.bvgt_atom(bvbin1, bvbin1) 192 | bvleatom1 = Terms.bvle_atom(bvbin1, bvbin1) 193 | bvltatom1 = Terms.bvlt_atom(bvbin1, bvbin1) 194 | bvsgeatom1 = Terms.bvsge_atom(bvbin1, bvbin1) 195 | bvsgtatom1 = Terms.bvsgt_atom(bvbin1, bvbin1) 196 | bvsleatom1 = Terms.bvsle_atom(bvbin1, bvbin1) 197 | bvsltatom1 = Terms.bvslt_atom(bvbin1, bvbin1) 198 | ptype1 = Types.parse_type('int') 199 | self.assertEqual(ptype1, Types.int_type()) 200 | pterm1 = Terms.parse_term('42') 201 | self.assertEqual(pterm1, Terms.integer(42)) 202 | subst1 = Terms.subst([Terms.new_variable(ptype1),Terms.new_variable(ptype1)], 203 | [Terms.integer(2), Terms.integer(3)], 204 | Terms.integer(42)) 205 | substarr1 = Terms.substs([Terms.new_variable(ptype1), Terms.new_variable(ptype1)], 206 | [Terms.integer(2), Terms.integer(3)], 207 | [Terms.integer(2), Terms.integer(3), Terms.integer(7)]) 208 | settypename1 = Types.set_name(ptype1, 'I') 209 | self.assertTrue(settypename1) 210 | settermname1 = Terms.set_name(pterm1, 'answer') 211 | self.assertTrue(settermname1) 212 | gettype1 = Types.get_by_name('I') 213 | self.assertEqual(gettype1, ptype1) 214 | getterm1 = Terms.get_by_name('answer') 215 | self.assertEqual(getterm1, pterm1) 216 | gettypename1 = Types.get_name(ptype1) 217 | self.assertEqual(gettypename1, 'I') 218 | gettermname1 = Terms.get_name(pterm1) 219 | self.assertEqual(gettermname1, 'answer') 220 | Types.remove_name('I') 221 | Terms.remove_name('answer') 222 | Types.clear_name(ptype1) 223 | Terms.clear_name(pterm1) 224 | typeofterm1 = Terms.type_of_term(pterm1) 225 | self.assertEqual(typeofterm1, Types.int_type()) 226 | self.assertEqual(Terms.is_bool(false_), 1) 227 | self.assertEqual(Terms.is_bool(pterm1), 0) 228 | self.assertEqual(Terms.is_int(false_), 0) 229 | self.assertEqual(Terms.is_int(pterm1), 1) 230 | self.assertEqual(Terms.is_real(false_), 0) 231 | self.assertEqual(Terms.is_real(pterm1), 0) 232 | self.assertEqual(Terms.is_arithmetic(false_), 0) 233 | self.assertEqual(Terms.is_arithmetic(pterm1), 1) 234 | self.assertEqual(Terms.is_bitvector(false_), 0) 235 | self.assertEqual(Terms.is_bitvector(bvbin1), 1) 236 | self.assertEqual(Terms.is_tuple(false_), 0) 237 | self.assertEqual(Terms.is_tuple(tup1), 1) 238 | self.assertEqual(Terms.is_function(false_), 0) 239 | self.assertEqual(Terms.is_function(fun1), 1) 240 | self.assertEqual(Terms.is_scalar(false_), 0) 241 | self.assertEqual(Terms.is_scalar(fun1), 0) 242 | self.assertEqual(Terms.bitsize(bvbin1), 6) 243 | self.assertEqual(Terms.is_ground(false_), 1) 244 | self.assertEqual(Terms.is_ground(var1), 0) 245 | self.assertEqual(Terms.is_atomic(false_), 1) 246 | # or1 is atomic because it simplifies to true 247 | self.assertEqual(Terms.is_atomic(or1), 1) 248 | self.assertEqual(Terms.is_composite(false_), 0) 249 | self.assertEqual(Terms.is_composite(ite1), 1) 250 | self.assertEqual(Terms.is_composite(tup1), 1) 251 | self.assertEqual(Terms.is_projection(false_), 0) 252 | # Select1 simplifies 253 | self.assertEqual(Terms.is_projection(select1), 0) 254 | self.assertEqual(Terms.is_projection(select2), 1) 255 | self.assertEqual(Terms.is_sum(ite1), 0) 256 | self.assertEqual(Terms.is_sum(sum1), 1) 257 | self.assertEqual(Terms.is_bvsum(select1), 0) 258 | # bvsum1 simplifies since the terms are all numbers 259 | self.assertEqual(Terms.is_bvsum(bvsum1), 0) 260 | self.assertEqual(Terms.is_bvsum(bvsum2), 1) 261 | self.assertEqual(Terms.is_product(ite1), 0) 262 | self.assertEqual(Terms.is_product(product1), 0) 263 | self.assertEqual(Terms.is_product(product2), 1) 264 | self.assertEqual(Terms.constructor(true_), 0) 265 | self.assertEqual(Terms.constructor(int1), 1) 266 | self.assertEqual(Terms.constructor(bvconst32_1), 2) 267 | self.assertEqual(Terms.num_children(bvconst32_1), 0) 268 | self.assertEqual(Terms.num_children(select2), 1) 269 | self.assertEqual(Terms.num_children(tup1), 4) 270 | self.assertEqual(Terms.child(tup1, 2), iconst1) 271 | projarg1 = Terms.proj_arg(select2) 272 | self.assertEqual(Terms.proj_index(select2), 2) 273 | self.assertEqual(Terms.proj_arg(select2), tupconst1) 274 | -------------------------------------------------------------------------------- /test/types_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | 5 | from yices.Types import Types 6 | from yices.Yices import Yices 7 | 8 | # pylint: disable=W0612 9 | # pylint: disable=R0914 10 | 11 | class TestTypes(unittest.TestCase): 12 | 13 | def setUp(self): 14 | Yices.init() 15 | 16 | def tearDown(self): 17 | Yices.exit() 18 | 19 | def test_types(self): 20 | bool_t = Types.bool_type() 21 | int_t = Types.int_type() 22 | self.assertNotEqual(bool_t, int_t) 23 | real_t = Types.real_type() 24 | self.assertNotEqual(real_t, bool_t) 25 | self.assertNotEqual(real_t, int_t) 26 | bv_t = Types.bv_type(8) 27 | scal_t = Types.new_scalar_type(12) 28 | unint_t = Types.new_uninterpreted_type() 29 | tup1_t = Types.new_tuple_type([bool_t]) 30 | tup2_t = Types.new_tuple_type([int_t, real_t]) 31 | tup3_t = Types.new_tuple_type([bv_t, scal_t, unint_t]) 32 | tup4_t = Types.new_tuple_type([bool_t, tup1_t, tup2_t, tup3_t]) 33 | fun1_t = Types.new_function_type([int_t], bool_t) 34 | fun2_t = Types.new_function_type([real_t, bv_t], scal_t) 35 | fun3_t = Types.new_function_type([tup1_t, tup2_t, tup3_t], fun1_t) 36 | fun4_t = Types.new_function_type([bool_t, tup1_t, tup2_t, tup3_t], fun3_t) 37 | 38 | self.assertTrue(Types.is_bool(bool_t)) 39 | self.assertFalse(Types.is_bool(int_t)) 40 | self.assertTrue(Types.is_int(int_t)) 41 | self.assertTrue(Types.is_real(real_t)) 42 | self.assertTrue(Types.is_arithmetic(real_t)) 43 | self.assertTrue(Types.is_bitvector(bv_t)) 44 | self.assertTrue(Types.is_tuple(tup1_t)) 45 | self.assertTrue(Types.is_function(fun4_t)) 46 | self.assertTrue(Types.is_scalar(scal_t)) 47 | self.assertTrue(Types.is_uninterpreted(unint_t)) 48 | self.assertTrue(Types.is_subtype(int_t, real_t)) 49 | self.assertFalse(Types.is_subtype(real_t, int_t)) 50 | self.assertEqual(Types.bvtype_size(bv_t), 8) 51 | self.assertEqual(Types.scalar_type_card(scal_t), 12) 52 | self.assertEqual(Types.num_children(tup3_t), 3) 53 | self.assertEqual(Types.child(tup3_t, 1), scal_t) 54 | type_v = Types.children(tup4_t) 55 | self.assertEqual(len(type_v), 4) 56 | self.assertEqual(type_v[0], bool_t) 57 | self.assertEqual(type_v[1], tup1_t) 58 | self.assertEqual(type_v[2], tup2_t) 59 | self.assertEqual(type_v[3], tup3_t) 60 | -------------------------------------------------------------------------------- /test/utils.py: -------------------------------------------------------------------------------- 1 | from yices.Types import Types 2 | from yices.Terms import Terms 3 | from yices.YicesException import YicesException 4 | 5 | 6 | def isstr(s): 7 | return isinstance(s, str) 8 | 9 | 10 | def define_type(name, ytype=None): 11 | '''Tries to emulate yices type declarations''' 12 | if ytype is None: 13 | ytyp = Types.new_uninterpreted_type() 14 | elif isstr(ytype): 15 | ytyp = Types.parse_type(ytype) 16 | else: 17 | ytyp = ytype 18 | Types.set_name(ytyp, name) 19 | return ytyp 20 | 21 | def define_const(name, ytype, defn=None): 22 | '''Tries to emulate yices define_term 23 | (see eval_define_term in yices2/src/parser_utils/term_stack2) 24 | ''' 25 | if defn is None: 26 | term = Terms.new_uninterpreted_term(ytype) 27 | Terms.set_name(term, name) 28 | return term 29 | # Have a defn 30 | if isstr(defn): 31 | term = Terms.parse_term(defn) 32 | else: 33 | term = defn 34 | term_type = Terms.type_of_term(term) 35 | if not Types.is_subtype(term_type, ytype): 36 | raise YicesException(msg='incompatible sort in definition') 37 | Terms.set_name(term, name) 38 | return term 39 | 40 | def assert_formula(formula, ctx): 41 | if isstr(formula): 42 | formula = Terms.parse_term(formula) 43 | ctx.assert_formula(formula) 44 | -------------------------------------------------------------------------------- /test_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SRI-CSL/yices2_python_bindings/c1136bc48032ca5a59d534f9950f6e21343d767d/test_api/__init__.py -------------------------------------------------------------------------------- /test_api/context_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import yices_api as yapi 4 | 5 | #from yices_api import YicesAPIException 6 | 7 | # pylint: disable=R0914 8 | 9 | class TestContext(unittest.TestCase): 10 | 11 | def setUp(self): 12 | yapi.yices_init() 13 | 14 | def tearDown(self): 15 | yapi.yices_exit() 16 | 17 | def test_config(self): 18 | cfg = yapi.yices_new_config() 19 | # Valid call 20 | yapi.yices_set_config(cfg, "mode", "push-pop") 21 | # Invalid name 22 | #iam: 9/19/2018 with self.assertRaisesRegexp(YicesAPIException, 'invalid parameter'): 23 | #iam: 9/19/2018 yapi.yices_set_config(cfg, "baz", "bar") 24 | errcode = yapi.yices_set_config(cfg, "baz", "bar") 25 | error_string = yapi.yices_error_string() 26 | self.assertEqual(errcode, -1) 27 | self.assertEqual(error_string, 'invalid parameter') 28 | # Invalid value 29 | #iam: 9/19/2018 with self.assertRaisesRegexp(YicesAPIException, 'value not valid for parameter'): 30 | #iam: 9/19/2018 yapi.yices_set_config(cfg, "mode", "bar") 31 | errcode = yapi.yices_set_config(cfg, "mode", "bar") 32 | error_string = yapi.yices_error_string() 33 | self.assertEqual(errcode, -1) 34 | self.assertEqual(error_string, 'value not valid for parameter') 35 | yapi.yices_default_config_for_logic(cfg, "QF_UFNIRA") 36 | yapi.yices_free_config(cfg) 37 | 38 | def test_context(self): 39 | cfg = yapi.yices_new_config() 40 | ctx = yapi.yices_new_context(cfg) 41 | stat = yapi.yices_context_status(ctx) 42 | yapi.yices_push(ctx) 43 | yapi.yices_pop(ctx) 44 | yapi.yices_reset_context(ctx) 45 | yapi.yices_context_enable_option(ctx, "arith-elim") 46 | yapi.yices_context_disable_option(ctx, "arith-elim") 47 | stat = yapi.yices_context_status(ctx) 48 | self.assertEqual(stat, 0) 49 | yapi.yices_reset_context(ctx) 50 | bool_t = yapi.yices_bool_type() 51 | bvar1 = yapi.yices_new_variable(bool_t) 52 | #iam: 9/19/2018 with self.assertRaisesRegexp(YicesAPIException, 'assertion contains a free variable'): 53 | #iam: 9/19/2018 yapi.yices_assert_formula(ctx, bvar1) 54 | errcode = yapi.yices_assert_formula(ctx, bvar1) 55 | error_string = yapi.yices_error_string() 56 | self.assertEqual(errcode, -1) 57 | self.assertEqual(error_string, 'assertion contains a free variable') 58 | bv_t = yapi.yices_bv_type(3) 59 | bvvar1 = yapi.yices_new_uninterpreted_term(bv_t) 60 | yapi.yices_set_term_name(bvvar1, 'x') 61 | bvvar2 = yapi.yices_new_uninterpreted_term(bv_t) 62 | yapi.yices_set_term_name(bvvar2, 'y') 63 | bvvar3 = yapi.yices_new_uninterpreted_term(bv_t) 64 | yapi.yices_set_term_name(bvvar3, 'z') 65 | fmla1 = yapi.yices_parse_term('(= x (bv-add y z))') 66 | fmla2 = yapi.yices_parse_term('(bv-gt y 0b000)') 67 | fmla3 = yapi.yices_parse_term('(bv-gt z 0b000)') 68 | yapi.yices_assert_formula(ctx, fmla1) 69 | yapi.yices_assert_formulas(ctx, 3, yapi.make_term_array([fmla1, fmla2, fmla3])) 70 | smt_stat = yapi.yices_check_context(ctx, None) 71 | self.assertEqual(smt_stat, yapi.STATUS_SAT) 72 | yapi.yices_assert_blocking_clause(ctx) 73 | yapi.yices_stop_search(ctx) 74 | param = yapi.yices_new_param_record() 75 | yapi.yices_default_params_for_context(ctx, param) 76 | yapi.yices_set_param(param, "dyn-ack", "true") 77 | #iam: 9/19/2018 with self.assertRaisesRegexp(YicesAPIException, 'invalid parameter'): 78 | #iam: 9/19/2018 yapi.yices_set_param(param, "foo", "bar") 79 | errcode = yapi.yices_set_param(param, "foo", "bar") 80 | error_string = yapi.yices_error_string() 81 | self.assertEqual(errcode, -1) 82 | self.assertEqual(error_string, 'invalid parameter') 83 | #iam: 9/19/2018 with self.assertRaisesRegexp(YicesAPIException, 'value not valid for parameter'): 84 | #iam: 9/19/2018 yapi.yices_set_param(param, "dyn-ack", "bar") 85 | errcode = yapi.yices_set_param(param, "dyn-ack", "bar") 86 | error_string = yapi.yices_error_string() 87 | self.assertEqual(errcode, -1) 88 | self.assertEqual(error_string, 'value not valid for parameter') 89 | yapi.yices_free_param_record(param) 90 | yapi.yices_free_context(ctx) 91 | 92 | 93 | if __name__ == '__main__': 94 | unittest.main() 95 | -------------------------------------------------------------------------------- /test_api/error_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import yices_api as yapi 4 | 5 | #from yices_api import YicesAPIException 6 | 7 | 8 | class TestError(unittest.TestCase): 9 | 10 | def setUp(self): 11 | yapi.yices_init() 12 | 13 | def tearDown(self): 14 | yapi.yices_exit() 15 | 16 | def test_error(self): 17 | yapi.yices_reset() 18 | 19 | # First with no error 20 | errcode = yapi.yices_error_code() 21 | self.assertEqual(errcode, 0) 22 | errep = yapi.yices_error_report() 23 | self.assertEqual(errep.code, 0) 24 | yapi.yices_clear_error() 25 | errstr = yapi.yices_error_string() 26 | self.assertEqual(errstr, 'no error') 27 | yapi.yices_print_error_fd(1) 28 | 29 | # Illegal - only scalar or uninterpreted types allowed 30 | bool_t = yapi.yices_bool_type() 31 | self.assertTrue(yapi.yices_type_is_bool(bool_t)) 32 | #iam: 9/19/2018 with self.assertRaisesRegexp(YicesAPIException, 'invalid type in constant creation'): 33 | #iam: 9/19/2018 const1 = yices_constant(bool_t, 0) 34 | const1 = yapi.yices_constant(bool_t, 0) 35 | error_string = yapi.yices_error_string() 36 | self.assertEqual(const1, -1) 37 | self.assertEqual(error_string, 'invalid type in constant creation') 38 | yapi.yices_clear_error() 39 | errpt = yapi.yices_error_report() 40 | self.assertEqual(yapi.yices_error_code(), 0) 41 | self.assertEqual(yapi.yices_error_code(), errpt.code) 42 | errstr = yapi.yices_error_string() 43 | self.assertEqual(errstr, 'no error') 44 | yapi.yices_print_error_fd(1) 45 | yapi.yices_clear_error() 46 | self.assertEqual(yapi.yices_error_code(), 0) 47 | -------------------------------------------------------------------------------- /test_api/types_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import yices_api as yapi 4 | 5 | 6 | # pylint: disable=R0914 7 | 8 | class TestTypes(unittest.TestCase): 9 | 10 | def setUp(self): 11 | yapi.yices_init() 12 | 13 | def tearDown(self): 14 | yapi.yices_exit() 15 | 16 | def test_types(self): 17 | bool_t = yapi.yices_bool_type() 18 | int_t = yapi.yices_int_type() 19 | self.assertNotEqual(bool_t, int_t) 20 | real_t = yapi.yices_real_type() 21 | self.assertNotEqual(real_t, bool_t) 22 | self.assertNotEqual(real_t, int_t) 23 | bv_t = yapi.yices_bv_type(8) 24 | scal_t = yapi.yices_new_scalar_type(12) 25 | unint_t = yapi.yices_new_uninterpreted_type() 26 | tup1_t = yapi.yices_tuple_type1(bool_t) 27 | tup2_t = yapi.yices_tuple_type2(int_t, real_t) 28 | tup3_t = yapi.yices_tuple_type3(bv_t, scal_t, unint_t) 29 | ta4 = yapi.make_type_array([bool_t, tup1_t, tup2_t, tup3_t]) 30 | tup4_t = yapi.yices_tuple_type(4, ta4) 31 | fun1_t = yapi.yices_function_type1(int_t, bool_t) 32 | #fun2_t = yapi.yices_function_type2(real_t, bv_t, scal_t) 33 | fun3_t = yapi.yices_function_type3(tup1_t, tup2_t, tup3_t, fun1_t) 34 | fun4_t = yapi.yices_function_type(4, ta4, fun3_t) 35 | 36 | self.assertTrue(yapi.yices_type_is_bool(bool_t)) 37 | self.assertFalse(yapi.yices_type_is_bool(int_t)) 38 | self.assertTrue(yapi.yices_type_is_int(int_t)) 39 | self.assertTrue(yapi.yices_type_is_real(real_t)) 40 | self.assertTrue(yapi.yices_type_is_arithmetic(real_t)) 41 | self.assertTrue(yapi.yices_type_is_bitvector(bv_t)) 42 | self.assertTrue(yapi.yices_type_is_tuple(tup1_t)) 43 | self.assertTrue(yapi.yices_type_is_function(fun4_t)) 44 | self.assertTrue(yapi.yices_type_is_scalar(scal_t)) 45 | self.assertTrue(yapi.yices_type_is_uninterpreted(unint_t)) 46 | self.assertTrue(yapi.yices_test_subtype(int_t, real_t)) 47 | self.assertFalse(yapi.yices_test_subtype(real_t, int_t)) 48 | self.assertEqual(yapi.yices_bvtype_size(bv_t), 8) 49 | self.assertEqual(yapi.yices_scalar_type_card(scal_t), 12) 50 | self.assertEqual(yapi.yices_type_num_children(tup3_t), 3) 51 | self.assertEqual(yapi.yices_type_child(tup3_t, 1), scal_t) 52 | type_v = yapi.type_vector_t() 53 | yapi.yices_init_type_vector(type_v) 54 | yapi.yices_type_children(tup4_t, type_v) 55 | self.assertEqual(type_v.size, 4) 56 | self.assertEqual(type_v.data[0], bool_t) 57 | self.assertEqual(type_v.data[1], tup1_t) 58 | self.assertEqual(type_v.data[2], tup2_t) 59 | self.assertEqual(type_v.data[3], tup3_t) 60 | -------------------------------------------------------------------------------- /test_api/vector_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | from yices_api import ( 5 | yices_init, 6 | yices_exit, 7 | term_vector_t, 8 | yices_init_term_vector, 9 | yices_delete_term_vector, 10 | yices_reset_term_vector, 11 | type_vector_t, 12 | yices_init_type_vector, 13 | yices_delete_type_vector, 14 | yices_reset_type_vector, 15 | yval_vector_t, 16 | yices_init_yval_vector, 17 | yices_delete_yval_vector, 18 | yices_reset_yval_vector 19 | ) 20 | 21 | # pylint: disable=R0201 22 | 23 | class TestVector(unittest.TestCase): 24 | 25 | def setUp(self): 26 | yices_init() 27 | 28 | def tearDown(self): 29 | yices_exit() 30 | 31 | def test_term_vector(self): 32 | term_v = term_vector_t() 33 | yices_init_term_vector(term_v) 34 | yices_delete_term_vector(term_v) 35 | yices_reset_term_vector(term_v) 36 | 37 | def test_type_vector(self): 38 | type_v = type_vector_t() 39 | yices_init_type_vector(type_v) 40 | yices_reset_type_vector(type_v) 41 | yices_delete_type_vector(type_v) 42 | 43 | def test_yval_vector(self): 44 | yval_v = yval_vector_t() 45 | yices_init_yval_vector(yval_v) 46 | yices_reset_yval_vector(yval_v) 47 | yices_delete_yval_vector(yval_v) 48 | -------------------------------------------------------------------------------- /yices/Census.py: -------------------------------------------------------------------------------- 1 | """For leak detection""" 2 | 3 | from .Context import Context 4 | from .Config import Config 5 | from .Parameters import Parameters 6 | from .Model import Model 7 | from .StringBuilder import StringBuilder 8 | 9 | class Census: 10 | 11 | @staticmethod 12 | def dump(): 13 | sb = StringBuilder() 14 | sb.append('\nCensus:\n') 15 | sb.append(f'\tContexts {Context.population()}\n') 16 | sb.append(f'\tConfigs {Config.population()}\n') 17 | sb.append(f'\tModels {Model.population()}\n') 18 | sb.append(f'\tParameters {Parameters.population()}\n') 19 | return str(sb) 20 | -------------------------------------------------------------------------------- /yices/Config.py: -------------------------------------------------------------------------------- 1 | """Config is a Pythonesque wrapper around a yices2 ctx_config_t object. Used to configure Contexts.""" 2 | import yices_api as yapi 3 | 4 | from .YicesException import YicesException 5 | 6 | class Config: 7 | 8 | __population = 0 9 | 10 | def __init__(self): 11 | self.config = yapi.yices_new_config() 12 | Config.__population += 1 13 | 14 | def default_config_for_logic(self, logicstr): 15 | assert self.config is not None 16 | errcode = yapi.yices_default_config_for_logic(self.config, logicstr) 17 | if errcode == -1: 18 | raise YicesException('yices_default_config_for_logic') 19 | 20 | def set_config(self, key, value): 21 | assert self.config is not None 22 | errcode = yapi.yices_set_config(self.config, key, value) 23 | if errcode == -1: 24 | raise YicesException('yices_set_config') 25 | 26 | def dispose(self): 27 | yapi.yices_free_config(self.config) 28 | self.config = None 29 | Config.__population -= 1 30 | 31 | @staticmethod 32 | def population(): 33 | """returns the current live population of Config objects.""" 34 | return Config.__population 35 | -------------------------------------------------------------------------------- /yices/Constructors.py: -------------------------------------------------------------------------------- 1 | """Constructor replicates the yices2 term_constructor_t enum.""" 2 | import yices_api as yapi 3 | 4 | 5 | class Constructor: 6 | 7 | CONSTRUCTOR_ERROR = yapi.YICES_CONSTRUCTOR_ERROR 8 | BOOL_CONSTANT = yapi.YICES_BOOL_CONSTANT 9 | ARITH_CONSTANT = yapi.YICES_ARITH_CONSTANT 10 | BV_CONSTANT = yapi.YICES_BV_CONSTANT 11 | SCALAR_CONSTANT = yapi.YICES_SCALAR_CONSTANT 12 | VARIABLE = yapi.YICES_VARIABLE 13 | UNINTERPRETED_TERM = yapi.YICES_UNINTERPRETED_TERM 14 | ITE_TERM = yapi.YICES_ITE_TERM 15 | APP_TERM = yapi.YICES_APP_TERM 16 | UPDATE_TERM = yapi.YICES_UPDATE_TERM 17 | TUPLE_TERM = yapi.YICES_TUPLE_TERM 18 | EQ_TERM = yapi.YICES_EQ_TERM 19 | DISTINCT_TERM = yapi.YICES_DISTINCT_TERM 20 | FORALL_TERM = yapi.YICES_FORALL_TERM 21 | LAMBDA_TERM = yapi.YICES_LAMBDA_TERM 22 | NOT_TERM = yapi.YICES_NOT_TERM 23 | OR_TERM = yapi.YICES_OR_TERM 24 | XOR_TERM = yapi.YICES_XOR_TERM 25 | BV_ARRAY = yapi.YICES_BV_ARRAY 26 | BV_DIV = yapi.YICES_BV_DIV 27 | BV_REM = yapi.YICES_BV_REM 28 | BV_SDIV = yapi.YICES_BV_SDIV 29 | BV_SREM = yapi.YICES_BV_SREM 30 | BV_SMOD = yapi.YICES_BV_SMOD 31 | BV_SHL = yapi.YICES_BV_SHL 32 | BV_LSHR = yapi.YICES_BV_LSHR 33 | BV_ASHR = yapi.YICES_BV_ASHR 34 | BV_GE_ATOM = yapi.YICES_BV_GE_ATOM 35 | BV_SGE_ATOM = yapi.YICES_BV_SGE_ATOM 36 | ARITH_GE_ATOM = yapi.YICES_ARITH_GE_ATOM 37 | ARITH_ROOT_ATOM = yapi.YICES_ARITH_ROOT_ATOM 38 | ABS = yapi.YICES_ABS 39 | CEIL = yapi.YICES_CEIL 40 | FLOOR = yapi.YICES_FLOOR 41 | RDIV = yapi.YICES_RDIV 42 | IDIV = yapi.YICES_IDIV 43 | IMOD = yapi.YICES_IMOD 44 | IS_INT_ATOM = yapi.YICES_IS_INT_ATOM 45 | DIVIDES_ATOM = yapi.YICES_DIVIDES_ATOM 46 | SELECT_TERM = yapi.YICES_SELECT_TERM 47 | BIT_TERM = yapi.YICES_BIT_TERM 48 | BV_SUM = yapi.YICES_BV_SUM 49 | ARITH_SUM = yapi.YICES_ARITH_SUM 50 | POWER_PRODUCT = yapi.YICES_POWER_PRODUCT 51 | -------------------------------------------------------------------------------- /yices/Context.py: -------------------------------------------------------------------------------- 1 | """Contexts wrap the important yices2 context_t structure. 2 | 3 | A context contains one or more solvers and supports operations for 4 | manipulating assertions and for checking whether these assertions are 5 | satisfiable. If they are, a model can be constructed from the context.""" 6 | 7 | import threading 8 | 9 | import yices_api as yapi 10 | 11 | from .YicesException import YicesException 12 | 13 | from .Status import Status 14 | from .Yices import Yices 15 | 16 | class Context: 17 | 18 | __population = 0 19 | 20 | def __init__(self, config=None): 21 | cfg = config.config if config else None 22 | self.context = Yices.new_context(cfg) 23 | if self.context == -1: 24 | raise YicesException('yices_new_context') 25 | Context.__population += 1 26 | 27 | # option is a string 28 | def enable_option(self, option): 29 | assert self.context is not None 30 | errcode = Yices.context_enable_option(self.context, option) 31 | if errcode == -1: 32 | raise YicesException('yices_context_enable_option') 33 | return True 34 | 35 | # option is a string 36 | def disable_option(self, option): 37 | assert self.context is not None 38 | errcode = Yices.context_disable_option(self.context, option) 39 | if errcode == -1: 40 | raise YicesException('yices_context_disable_option') 41 | return True 42 | 43 | 44 | def status(self): 45 | assert self.context is not None 46 | return Yices.context_status(self.context) 47 | 48 | 49 | def assert_formula(self, term): 50 | assert self.context is not None 51 | errcode = Yices.assert_formula(self.context, term) 52 | if errcode == -1: 53 | raise YicesException('yices_assert_formula') 54 | return True 55 | 56 | # to be very pythonesque we should handle iterables, but we do need to know the length 57 | def assert_formulas(self, python_array_or_tuple): 58 | assert self.context is not None 59 | alen = len(python_array_or_tuple) 60 | a = yapi.make_term_array(python_array_or_tuple) 61 | errcode = Yices.assert_formulas(self.context, alen, a) 62 | if errcode == -1: 63 | raise YicesException('yices_assert_formulas') 64 | return True 65 | 66 | 67 | def check_context(self, params=None, timeout=None): 68 | assert self.context is not None 69 | # unwrap the params object 70 | if params is not None: 71 | params = params.params 72 | # set the timeout 73 | if timeout is not None: 74 | timer = threading.Timer(timeout, Context.stop_search, [self]) 75 | timer.start() 76 | status = Yices.check_context(self.context, params) 77 | if timeout is not None: 78 | timer.cancel() 79 | if status == -1: 80 | raise YicesException('yices_check_context') 81 | return status 82 | 83 | 84 | def stop_search(self): 85 | assert self.context is not None 86 | #yapi.yices_stop_search(self.context) 87 | Yices.stop_search(self.context) 88 | 89 | def reset_context(self): 90 | assert self.context is not None 91 | #yapi.yices_reset_context(self.context) 92 | Yices.reset_context(self.context) 93 | 94 | def assert_blocking_clause(self): 95 | assert self.context is not None 96 | errcode = Yices.assert_blocking_clause(self.context) 97 | if errcode == -1: 98 | raise YicesException('yices_assert_blocking_clause') 99 | return True 100 | 101 | 102 | def push(self): 103 | assert self.context is not None 104 | errcode = Yices.push(self.context) 105 | if errcode == -1: 106 | raise YicesException('yices_push') 107 | return True 108 | 109 | def pop(self): 110 | assert self.context is not None 111 | errcode = Yices.pop(self.context) 112 | if errcode == -1: 113 | raise YicesException('yices_pop') 114 | return True 115 | 116 | 117 | def check_context_with_assumptions(self, params, python_array_or_tuple): 118 | assert self.context is not None 119 | alen = len(python_array_or_tuple) 120 | a = yapi.make_term_array(python_array_or_tuple) 121 | status = Yices.check_context_with_assumptions(self.context, params, alen, a) 122 | if status == Status.ERROR: 123 | raise YicesException('check_context_with_assumptions') 124 | return status 125 | 126 | def check_context_with_model(self, params, model, python_array_or_tuple): 127 | assert self.context is not None 128 | assert model is not None 129 | alen = len(python_array_or_tuple) 130 | a = yapi.make_term_array(python_array_or_tuple) 131 | status = yapi.yices_check_context_with_model(self.context, params, model.model, alen, a) 132 | if status == Status.ERROR: 133 | raise YicesException('check_context_with_model') 134 | return status 135 | 136 | def check_context_with_model_and_hint(self, params, model, python_array_or_tuple, python_array_or_tuple_hints): 137 | assert self.context is not None 138 | assert model is not None 139 | m = len(python_array_or_tuple) 140 | alist = list(python_array_or_tuple) + list(python_array_or_tuple_hints) 141 | alen = len(alist) 142 | a = yapi.make_term_array(alist) 143 | status = yapi.yices_check_context_with_model_and_hint(self.context, params, model.model, alen, a, m) # pylint: disable=E1101 144 | if status == Status.ERROR: 145 | raise YicesException('check_context_with_model_and_hint') 146 | return status 147 | 148 | 149 | def mcsat_set_fixed_var_order(self, python_array_or_tuple): 150 | assert self.context is not None 151 | alen = len(python_array_or_tuple) 152 | a = yapi.make_term_array(python_array_or_tuple) 153 | status = yapi.yices_mcsat_set_fixed_var_order(self.context, alen, a) # pylint: disable=E1101 154 | if status == Status.ERROR: 155 | raise YicesException('mcsat_set_fixed_var_order') 156 | return status 157 | 158 | def mcsat_set_initial_var_order(self, python_array_or_tuple): 159 | assert self.context is not None 160 | alen = len(python_array_or_tuple) 161 | a = yapi.make_term_array(python_array_or_tuple) 162 | status = yapi.yices_mcsat_set_initial_var_order(self.context, alen, a) # pylint: disable=E1101 163 | if status == Status.ERROR: 164 | raise YicesException('mcsat_set_initial_var_order') 165 | return status 166 | 167 | 168 | def get_unsat_core(self): 169 | retval = [] 170 | unsat_core = yapi.term_vector_t() 171 | yapi.yices_init_term_vector(unsat_core) 172 | errcode = Yices.get_unsat_core(self.context, unsat_core) 173 | if errcode == -1: 174 | raise YicesException('yices_get_unsat_core') 175 | 176 | for i in range(0, unsat_core.size): 177 | retval.append(unsat_core.data[i]) 178 | return retval 179 | 180 | 181 | def dispose(self): 182 | Yices.free_context(self.context) 183 | self.context = None 184 | Context.__population -= 1 185 | 186 | @staticmethod 187 | def population(): 188 | """returns the current live population of Context objects.""" 189 | return Context.__population 190 | -------------------------------------------------------------------------------- /yices/Delegates.py: -------------------------------------------------------------------------------- 1 | """Yices has the ability to use third-party SAT solvers as backends to the bit-vector solvers. 2 | 3 | The Delegate class encapulates this feature of the API.""" 4 | 5 | from ctypes import pointer 6 | 7 | import yices_api as yapi 8 | 9 | from .Model import Model 10 | from .Status import Status 11 | 12 | 13 | class Delegates: 14 | 15 | # new in 2.6.2 16 | @staticmethod 17 | def has_delegate(delegate): 18 | """Returns True if the underlying libyices has been compiled with the given delegate supported. 19 | 20 | Valid delegates are "cadical", "cryptominisat", and "y2sat". 21 | """ 22 | return yapi.yices_has_delegate(delegate) == 1 23 | 24 | 25 | # new in 2.6.2 26 | @staticmethod 27 | def check_formula(f, logic, delegate, model_array=None): 28 | """Checks whether the formula f is satisfiable in the logic using the delegate. 29 | 30 | If the formula is satisfiable, and the model_array is not None, then it 31 | inserts a model into the beginning of the array. 32 | """ 33 | model = None 34 | if model_array is not None: 35 | model = pointer(yapi.model_t(model)) 36 | status = yapi.yices_check_formula(f, logic, model, delegate) 37 | if status == Status.SAT and model_array is not None: 38 | model_array.append(Model(model.contents)) 39 | return status 40 | 41 | 42 | # new in 2.6.2 43 | @staticmethod 44 | def check_formulas(term_array, logic, delegate, model_array=None): 45 | """Checks whether the formulas in the array are satisfiable in the logic using the delegate. 46 | 47 | If the formulas are satisfiable, and the model_array is not None, then it 48 | inserts a model into the beginning of the array. 49 | """ 50 | tarray = yapi.make_term_array(term_array) 51 | model = None 52 | if model_array is not None: 53 | model = pointer(yapi.model_t(model)) 54 | status = yapi.yices_check_formulas(tarray, len(term_array), logic, model, delegate) 55 | if status == Status.SAT and model_array is not None: 56 | model_array.append(Model(model.contents)) 57 | return status 58 | -------------------------------------------------------------------------------- /yices/Dimacs.py: -------------------------------------------------------------------------------- 1 | """Yices allows one to bit blast and then export the results out to a file in the DIMACS format. 2 | 3 | This class encapsulates this feature.""" 4 | 5 | from ctypes import pointer 6 | 7 | import yices_api as yapi 8 | 9 | 10 | class Dimacs: 11 | 12 | 13 | # new in 2.6.2 14 | @staticmethod 15 | def export_formula(f, filename, simplify): 16 | """Bit-blast then export the CNF to a file, returns a pair consisting of a boolean 17 | and a status. The boolean is True if the file was written, or False indicating 18 | that the status of the formulas was determined, and is the second component. 19 | 20 | If the simplify flag is true, then is also possible for CNF 21 | simplification to detect that the CNF is sat or unsat. 22 | """ 23 | status = pointer(yapi.smt_status_t(yapi.STATUS_IDLE)) 24 | code = yapi.yices_export_formula_to_dimacs(f, filename, simplify, status) 25 | return (code == 1, status.contents.value) 26 | 27 | # new in 2.6.2 28 | @staticmethod 29 | def export_formulas(f_array, filename, simplify): 30 | """Bit-blast then export the CNFs to a file, returns a pair consisting of a boolean 31 | and a status. The boolean is True if the file was written, or False indicating 32 | that the status of the formulas was determined, and is the second component. 33 | 34 | If the simplify flag is true, then is also possible for CNF 35 | simplification to detect that the CNF is sat or unsat. 36 | """ 37 | farray = yapi.make_term_array(f_array) 38 | status = pointer(yapi.smt_status_t(yapi.STATUS_IDLE)) 39 | code = yapi.yices_export_formulas_to_dimacs(farray, len(f_array), filename, simplify, status) 40 | return (code == 1, status.contents.value) 41 | -------------------------------------------------------------------------------- /yices/InterpolationContext.py: -------------------------------------------------------------------------------- 1 | """ The InterpolationContext class mimics, but doesn't wrap the yices interpolation_context_t struct. 2 | 3 | It provides access to the yices_check_context_with_interpolation api call in a pythonesque manner. 4 | """ 5 | from ctypes import ( 6 | c_int32, 7 | pointer, 8 | ) 9 | 10 | 11 | import yices_api as yapi 12 | from .Status import Status 13 | from .Model import Model 14 | 15 | class InterpolationContext: 16 | 17 | def __init__(self, ctx_a, ctx_b): 18 | self.ctx_a = ctx_a 19 | self.ctx_b = ctx_b 20 | self.model = None 21 | self.interpolant = None 22 | 23 | def check(self, params, build_model): 24 | interpolation_ctx = yapi.interpolation_context_t(self.ctx_a.context, self.ctx_b.context, 0, 0) 25 | parameters = 0 if not params else params.params 26 | build = c_int32(1 if build_model else 0) 27 | status = yapi.yices_check_context_with_interpolation(pointer(interpolation_ctx), parameters, build) 28 | if status == Status.UNSAT: 29 | # get the interpolant 30 | self.interpolant = interpolation_ctx.interpolant 31 | elif status == Status.SAT: 32 | # get the model 33 | self.model = Model(interpolation_ctx.model) 34 | return status 35 | -------------------------------------------------------------------------------- /yices/Model.py: -------------------------------------------------------------------------------- 1 | """The Model class wraps the Yices model_t structure. 2 | 3 | If a context is satisfiable, Yices can build a model of the context’s 4 | assertions. Functions are provided to extract the values of terms in a 5 | model. Atomic values (e.g., integer or bitvector constants) can be 6 | obtained directly. Non-atomic values—that is, tuples or functions—are 7 | represented internally as nodes in a DAG. The API includes functions 8 | to explore this DAG and get the values of tuples or functions. 9 | """ 10 | 11 | import ctypes 12 | 13 | from fractions import Fraction 14 | 15 | import yices_api as yapi 16 | 17 | from .Yvals import Yval 18 | from .YicesException import YicesException 19 | from .Yices import Yices 20 | 21 | class Model: 22 | 23 | GEN_DEFAULT = yapi.YICES_GEN_DEFAULT 24 | GEN_BY_SUBST = yapi.YICES_GEN_BY_SUBST 25 | GEN_BY_PROJ = yapi.YICES_GEN_BY_PROJ 26 | 27 | 28 | __population = 0 29 | 30 | 31 | def __init__(self, model=None): 32 | if model is not None: 33 | self.model = model 34 | else: 35 | self.model = Yices.new_model() 36 | Model.__population += 1 37 | 38 | 39 | @staticmethod 40 | def from_context(context, keep_subst): 41 | #model = yapi.yices_get_model(context.context, keep_subst) 42 | model = Yices.get_model(context.context, keep_subst) 43 | if model == 0: 44 | raise YicesException('yices_get_model') 45 | return Model(model) 46 | 47 | 48 | @staticmethod 49 | def from_map(mapping): 50 | dom = mapping.keys() 51 | rng = [ mapping[d] for d in dom] 52 | #model = yapi.yices_model_from_map(len(dom), yapi.make_term_array(dom), yapi.make_term_array(rng)) 53 | model = Yices.model_from_map(len(dom), yapi.make_term_array(dom), yapi.make_term_array(rng)) 54 | if model == 0: 55 | raise YicesException('yices_model_from_map') 56 | return Model(model) 57 | 58 | 59 | def collect_defined_terms(self): 60 | defined_terms = yapi.term_vector_t() 61 | yapi.yices_init_term_vector(defined_terms) 62 | #yapi.yices_model_collect_defined_terms(self.model, defined_terms) 63 | Yices.model_collect_defined_terms(self.model, defined_terms) 64 | retval = [] 65 | for i in range(0, defined_terms.size): 66 | retval.append(defined_terms.data[i]) 67 | yapi.yices_delete_term_vector(defined_terms) 68 | return retval 69 | 70 | def dispose(self): 71 | assert self.model is not None 72 | #yapi.yices_free_model(self.model) 73 | Yices.free_model(self.model) 74 | self.model = None 75 | Model.__population -= 1 76 | 77 | 78 | def get_bool_value(self, term): 79 | ytval = ctypes.c_int32() 80 | errcode = yapi.yices_get_bool_value(self.model, term, ytval) 81 | if errcode == -1: 82 | raise YicesException('yices_get_bool_value') 83 | return bool(ytval.value) 84 | 85 | 86 | def get_integer_value(self, term): 87 | ytval = ctypes.c_int64() 88 | errcode = yapi.yices_get_int64_value(self.model, term, ytval) 89 | if errcode == -1: 90 | raise YicesException('yices_get_int64_value') 91 | return ytval.value 92 | 93 | def get_fraction_value(self, term): 94 | ytnum = ctypes.c_int64() 95 | ytden = ctypes.c_uint64() 96 | errcode = yapi.yices_get_rational64_value(self.model, term, ytnum, ytden) 97 | if errcode == -1: 98 | raise YicesException('yices_get_rational64_value') 99 | return Fraction(ytnum.value, ytden.value) 100 | 101 | 102 | def get_float_value(self, term): 103 | ytval = ctypes.c_double() 104 | errcode = yapi.yices_get_double_value(self.model, term, ytval) 105 | if errcode == -1: 106 | raise YicesException('yices_get_double_value') 107 | return ytval.value 108 | 109 | def get_scalar_value(self, term): 110 | """ Returns the index of the value. This is the low level version, and does not use yapi.yices_constant. """ 111 | ytval = ctypes.c_int32() 112 | errcode = yapi.yices_get_scalar_value(self.model, term, ytval) 113 | if errcode == -1: 114 | raise YicesException('yices_get_scalar_value') 115 | return ytval.value 116 | 117 | 118 | def set_bool(self, term, val): 119 | """set the value of a boolean term.""" 120 | yval = ctypes.c_int32() 121 | yval.value = 1 if val else 0 122 | errcode = yapi.yices_model_set_bool(self.model, term, val) 123 | if errcode == -1: 124 | raise YicesException('yices_model_set_bool') 125 | 126 | def set_integer(self, term, val): 127 | """set the value of an integer term.""" 128 | yval = ctypes.c_int64() 129 | yval.value = val 130 | errcode = yapi.yices_model_set_int64(self.model, term, val) 131 | if errcode == -1: 132 | raise YicesException('yices_model_set_int64') 133 | 134 | def set_fraction(self, term, fraction): 135 | """set the value of an real term.""" 136 | ytnum = ctypes.c_int64() 137 | ytnum.value = fraction.numerator 138 | ytden = ctypes.c_uint64() 139 | ytden.value = fraction.denominator 140 | errcode = yapi.yices_model_set_rational64(self.model, term, ytnum, ytden) 141 | if errcode == -1: 142 | raise YicesException('yices_model_set_rational64') 143 | 144 | def set_bv(self, term, integer): 145 | """set the value of an bv term.""" 146 | ytnum = ctypes.c_int64() 147 | ytnum.value = integer 148 | errcode = yapi.yices_model_set_bv_int64(self.model, term, ytnum) 149 | if errcode == -1: 150 | raise YicesException('yices_model_set_bv_int64') 151 | 152 | def set_bv_from_array(self, term, int_array): 153 | """set the value of an bv term from an array of integers.""" 154 | iarray = yapi.make_int32_array(int_array) 155 | errcode = yapi.yices_model_set_bv_from_array(self.model, term, len(int_array), iarray) 156 | if errcode == -1: 157 | raise YicesException('yices_model_set_bv_from_array') 158 | 159 | def formula_true_in_model(self, term): 160 | return yapi.yices_formula_true_in_model(self.model, term) == 1 161 | #return Yices.formula_true_in_model(self.model, term) == 1 162 | 163 | def formulas_true_in_model(self, term_array): 164 | tarray = yapi.make_term_array(term_array) 165 | return yapi.yices_formulas_true_in_model(self.model, len(term_array), tarray) == 1 166 | #return Yices.yices_formulas_true_in_model(self.model, len(term_array), tarray) == 1 167 | 168 | def get_value_from_rational_yval(self, yval): 169 | if yapi.yices_val_is_int64(self.model, yval): 170 | val = ctypes.c_int64() 171 | errcode = yapi.yices_val_get_int64(self.model, yval, val) 172 | if errcode == -1: 173 | raise YicesException('yices_val_get_int64') 174 | return val.value 175 | if yapi.yices_val_is_rational64(self.model, yval): 176 | ytnum = ctypes.c_int64() 177 | ytden = ctypes.c_uint64() 178 | errcode = yapi.yices_val_get_rational64(self.model, yval, ytnum, ytden) 179 | if errcode == -1: 180 | raise YicesException('yices_val_get_rational64') 181 | return Fraction(ytnum.value, ytden.value) 182 | val = ctypes.c_double() 183 | errcode = yapi.yices_val_get_double(self.model, yval, val) 184 | if errcode == -1: 185 | raise YicesException('yices_val_get_double') 186 | return val.value 187 | 188 | def get_value_from_bool_yval(self, yval): 189 | value = ctypes.c_int32() 190 | errcode = yapi.yices_val_get_bool(self.model, yval, value) 191 | if errcode == -1: 192 | raise YicesException('yices_val_get_bool') 193 | return bool(value.value) 194 | 195 | def get_value_from_scalar_yval(self, yval): 196 | value = ctypes.c_int32() 197 | typev = ctypes.c_int32() 198 | errcode = yapi.yices_val_get_scalar(self.model, yval, value, typev) 199 | if errcode == -1: 200 | raise YicesException('yices_val_get_scalar') 201 | return yapi.yices_constant(typev.value, value.value) 202 | 203 | def get_value_from_bv_yval(self, yval): 204 | bvsize = yapi.yices_val_bitsize(self.model, yval) 205 | if bvsize <= 0: 206 | return None 207 | bvarray = yapi.make_empty_int32_array(bvsize) 208 | errcode = yapi.yices_val_get_bv(self.model, yval, bvarray) 209 | if errcode == -1: 210 | raise YicesException('yices_val_get_bv') 211 | return [ bvarray[i] for i in range(0, bvsize) ] 212 | 213 | #this problem is part of the gmp libpoly conundrum 214 | def get_value_from_algebraic_yval(self, yval): 215 | val = ctypes.c_double() 216 | errcode = yapi.yices_val_get_double(self.model, yval, val) 217 | if errcode == -1: 218 | raise YicesException('yices_val_get_double') 219 | return val.value 220 | 221 | def get_value_from_tuple_yval(self, yval): 222 | tuple_size = yapi.yices_val_tuple_arity(self.model, yval) 223 | if tuple_size <= 0: 224 | return None 225 | yval_array = yapi.make_empty_yval_array(tuple_size) 226 | errcode = yapi.yices_val_expand_tuple(self.model, yval, yval_array) 227 | if errcode == -1: 228 | raise YicesException('yices_val_expand_tuple') 229 | retval = [ self.get_value_from_yval(yval_array[i]) for i in range(0, tuple_size) ] 230 | return tuple(retval) 231 | 232 | def get_value_from_mapping_yval(self, yval): 233 | mapping_size = yapi.yices_val_mapping_arity(self.model, yval) 234 | if mapping_size <= 0: 235 | return None 236 | ytgt = yapi.yval_t() 237 | ysrc = yapi.make_empty_yval_array(mapping_size) 238 | errcode = yapi.yices_val_expand_mapping(self.model, yval, ysrc, ytgt) 239 | if errcode == -1: 240 | raise YicesException('yices_val_expand_mapping') 241 | src = [self.get_value_from_yval(ysrc[i]) for i in range(0, mapping_size) ] 242 | tgt = self.get_value_from_yval(ytgt) 243 | return (tuple(src), tgt) 244 | 245 | def get_value_from_function_yval(self, yval): 246 | function_size = yapi.yices_val_function_arity(self.model, yval) 247 | if function_size <= 0: 248 | return None 249 | ydefault = yapi.yval_t() 250 | ymapping = yapi.yval_vector_t() 251 | yapi.yices_init_yval_vector(ymapping) 252 | errcode = yapi.yices_val_expand_function(self.model, yval, ydefault, ymapping) 253 | if errcode == -1: 254 | yapi.yices_delete_yval_vector(ymapping) 255 | raise YicesException('yices_val_expand_function') 256 | default = self.get_value_from_yval(ydefault) 257 | mapping = [ self.get_value_from_yval(ymapping.data[i]) for i in range(0, ymapping.size) ] 258 | dict_map = {} 259 | for (src, tgt) in mapping: 260 | dict_map[src] = tgt 261 | yapi.yices_delete_yval_vector(ymapping) 262 | def retfun(src): 263 | if src in dict_map: 264 | return dict_map[src] 265 | return default 266 | return retfun 267 | 268 | 269 | def get_value_from_yval(self, yval): 270 | tag = yval.node_tag 271 | if tag == Yval.BOOL: 272 | return self.get_value_from_bool_yval(yval) 273 | if tag == Yval.RATIONAL: 274 | return self.get_value_from_rational_yval(yval) 275 | if tag == Yval.SCALAR: 276 | return self.get_value_from_scalar_yval(yval) 277 | if tag == Yval.BV: 278 | return self.get_value_from_bv_yval(yval) 279 | if tag == Yval.ALGEBRAIC: 280 | return self.get_value_from_algebraic_yval(yval) 281 | if tag == Yval.TUPLE: 282 | return self.get_value_from_tuple_yval(yval) 283 | if tag == Yval.MAPPING: 284 | return self.get_value_from_mapping_yval(yval) 285 | if tag == Yval.FUNCTION: 286 | return self.get_value_from_function_yval(yval) 287 | raise YicesException(msg='Model.get_value_from_yval: unexpected yval tag {0}\n'.format(tag)) 288 | 289 | 290 | 291 | def get_value(self, term): 292 | yval = yapi.yval_t() 293 | errcode = yapi.yices_get_value(self.model, term, yval) 294 | if errcode == -1: 295 | raise YicesException('yices_get_value') 296 | return self.get_value_from_yval(yval) 297 | 298 | 299 | #yices tuples should be returned as python tuples 300 | 301 | #yices functions should be returned as closures (i.e functions) 302 | 303 | def get_value_as_term(self, term): 304 | return yapi.yices_get_value_as_term(self.model, term) 305 | 306 | def implicant_for_formula(self, term): 307 | termv = yapi.term_vector_t() 308 | yapi.yices_init_term_vector(termv) 309 | try: 310 | code = yapi.yices_implicant_for_formula(self.model, term, termv) 311 | retval = [] 312 | if code != -1: 313 | for i in range(0, termv.size): 314 | retval.append(termv.data[i]) 315 | yapi.yices_delete_term_vector(termv) 316 | return retval 317 | except yapi.YicesAPIException as catastrophy: 318 | yapi.yices_delete_term_vector(termv) 319 | raise YicesException('implicant_for_formula') from catastrophy 320 | 321 | 322 | def implicant_for_formulas(self, term_array): 323 | tarray = yapi.make_term_array(term_array) 324 | termv = yapi.term_vector_t() 325 | yapi.yices_init_term_vector(termv) 326 | try: 327 | code = yapi.yices_implicant_for_formulas(self.model, len(term_array), tarray, termv) 328 | retval = [] 329 | if code != -1: 330 | for i in range(0, termv.size): 331 | retval.append(termv.data[i]) 332 | yapi.yices_delete_term_vector(termv) 333 | return retval 334 | except yapi.YicesAPIException as catastrophy: 335 | yapi.yices_delete_term_vector(termv) 336 | raise YicesException('implicant_for_formulas') from catastrophy 337 | 338 | def generalize_model(self, term, elim_array, mode): 339 | var_array = yapi.make_term_array(elim_array) 340 | termv = yapi.term_vector_t() 341 | yapi.yices_init_term_vector(termv) 342 | errcode = yapi.yices_generalize_model(self.model, term, len(elim_array), var_array, mode, termv) 343 | if errcode == -1: 344 | yapi.yices_delete_term_vector(termv) 345 | raise YicesException('yices_generalize_model') 346 | retval = [] 347 | for i in range(0, termv.size): 348 | retval.append(termv.data[i]) 349 | yapi.yices_delete_term_vector(termv) 350 | return retval 351 | 352 | def generalize_model_array(self, term_array, elim_array, mode): 353 | tarray = yapi.make_term_array(term_array) 354 | var_array = yapi.make_term_array(elim_array) 355 | termv = yapi.term_vector_t() 356 | yapi.yices_init_term_vector(termv) 357 | errcode = yapi.yices_generalize_model_array(self.model, len(term_array), tarray, len(elim_array), var_array, mode, termv) 358 | if errcode == -1: 359 | yapi.yices_delete_term_vector(termv) 360 | raise YicesException('yices_generalize_model_array') 361 | retval = [] 362 | for i in range(0, termv.size): 363 | retval.append(termv.data[i]) 364 | yapi.yices_delete_term_vector(termv) 365 | return retval 366 | 367 | # new in 2.6.2 368 | # term support 369 | def support_for_term(self, term): 370 | """Returns the list of uninterpreted terms that fix the value of the given term in the model.""" 371 | termv = yapi.term_vector_t() 372 | yapi.yices_init_term_vector(termv) 373 | try: 374 | code = yapi.yices_model_term_support(self.model, term, termv) 375 | retval = [] 376 | if code != -1: 377 | for i in range(0, termv.size): 378 | retval.append(termv.data[i]) 379 | yapi.yices_delete_term_vector(termv) 380 | return retval 381 | except yapi.YicesAPIException as catastrophy: 382 | yapi.yices_delete_term_vector(termv) 383 | raise YicesException('support_for_term') from catastrophy 384 | 385 | # new in 2.6.2 386 | # term array support 387 | def support_for_terms(self, term_array): 388 | """Returns the list of uninterpreted terms that fix the value in the model of every term in the given array.""" 389 | tarray = yapi.make_term_array(term_array) 390 | termv = yapi.term_vector_t() 391 | try: 392 | yapi.yices_init_term_vector(termv) 393 | code = yapi.yices_model_term_array_support(self.model, len(term_array), tarray, termv) 394 | retval = [] 395 | if code != -1: 396 | for i in range(0, termv.size): 397 | retval.append(termv.data[i]) 398 | yapi.yices_delete_term_vector(termv) 399 | return retval 400 | except yapi.YicesAPIException as catastrophy: 401 | yapi.yices_delete_term_vector(termv) 402 | raise YicesException('support_for_terms') from catastrophy 403 | 404 | 405 | # printing 406 | 407 | def print_to_fd(self, fd, width=None, height=None, offset=None): 408 | if (width is None) or (height is None) or (offset is None): 409 | errcode = yapi.yices_print_model_fd(fd, self.model) 410 | if errcode == -1: 411 | raise YicesException('yices_print_model_fd') 412 | else: 413 | errcode = yapi.yices_pp_model_fd(fd, self.model, int(width), int(height), int(offset)) 414 | if errcode == -1: 415 | raise YicesException('yices_pp_print_model_fd') 416 | 417 | def print_term_values(self, fd, term_array, width=None, height=None, offset=None): 418 | """Print the values of the terms in the model to the file descriptor, pretty print if width, height, and offset are supplied.""" 419 | tarray = yapi.make_term_array(term_array) 420 | if (width is None) or (height is None) or (offset is None): 421 | yapi.yices_print_term_values_fd(fd, self.model, len(term_array), tarray) 422 | else: 423 | yapi.yices_pp_term_values_fd(fd, self.model, len(term_array), tarray, int(width), int(height), int(offset)) 424 | 425 | 426 | def to_string(self, width, height, offset): 427 | #this gonna just have to leak 428 | return yapi.yices_model_to_string(self.model, int(width), int(height), int(offset)) 429 | 430 | 431 | @staticmethod 432 | def population(): 433 | """returns the current live population of Model objects.""" 434 | return Model.__population 435 | -------------------------------------------------------------------------------- /yices/Parameters.py: -------------------------------------------------------------------------------- 1 | """The Parameters class wraps the yicesparameter record structure param_t . 2 | 3 | A parameter record stores search parameters and options that control the heuristics used by a solver. 4 | """ 5 | 6 | 7 | import yices_api as yapi 8 | 9 | from .YicesException import YicesException 10 | 11 | class Parameters: 12 | 13 | __population = 0 14 | 15 | def __init__(self): 16 | self.params = yapi.yices_new_param_record() 17 | Parameters.__population += 1 18 | 19 | def set_param(self, key, value): 20 | assert self.params is not None 21 | errcode = yapi.yices_set_param(self.params, key, value) 22 | if errcode == -1: 23 | raise YicesException('yices_set_param') 24 | return True 25 | 26 | 27 | def default_params_for_context(self, context): 28 | yapi.yices_default_params_for_context(context.context, self.params) 29 | 30 | def dispose(self): 31 | assert self.params is not None 32 | yapi.yices_free_param_record(self.params) 33 | self.params = None 34 | Parameters.__population -= 1 35 | 36 | @staticmethod 37 | def population(): 38 | """returns the current live population of Parameters objects.""" 39 | return Parameters.__population 40 | -------------------------------------------------------------------------------- /yices/Profiler.py: -------------------------------------------------------------------------------- 1 | """Profiler is for measuring how much time (nanoseconds) spent in the Yices shared library.""" 2 | 3 | import functools 4 | 5 | import time 6 | 7 | from .StringBuilder import StringBuilder 8 | 9 | def profile(func): 10 | """Record the runtime of the decorated function""" 11 | @functools.wraps(func) 12 | def wrapper_timer(*args, **kwargs): 13 | if Profiler.is_enabled(): 14 | start = time.perf_counter_ns() 15 | value = func(*args, **kwargs) 16 | stop = time.perf_counter_ns() 17 | Profiler.delta(func.__name__, start, stop) 18 | return value 19 | return func(*args, **kwargs) 20 | return wrapper_timer 21 | 22 | 23 | class Profiler: 24 | 25 | __enabled = True 26 | 27 | """maps (yices API) function names to the total accumulated time spent within them.""" 28 | __line_items = {} 29 | 30 | """keeps track of the total time spent in the shared library.""" 31 | __total = 0 32 | 33 | __max_len = 0 34 | 35 | @staticmethod 36 | def set_enabled(value): 37 | Profiler.__enabled = bool(value) 38 | 39 | 40 | @staticmethod 41 | def is_enabled(): 42 | return Profiler.__enabled 43 | 44 | @staticmethod 45 | def get_time(fname): 46 | return Profiler.__line_items.get(fname, 0) 47 | 48 | @staticmethod 49 | def delta(fname, start, stop): 50 | nanos = stop - start 51 | Profiler.__total += nanos 52 | if Profiler.__max_len < len(fname): 53 | Profiler.__max_len = len(fname) 54 | if fname in Profiler.__line_items: 55 | Profiler.__line_items[fname] += nanos 56 | else: 57 | Profiler.__line_items[fname] = nanos 58 | 59 | @staticmethod 60 | def dump(): 61 | def percent(nanos): 62 | return (nanos * 100) // Profiler.__total 63 | def pad(fname): 64 | return ' ' * (Profiler.__max_len - len(fname)) 65 | sb = StringBuilder() 66 | sb.append('\nYices API Call Profile:\n') 67 | for fname, cost in Profiler.__line_items.items(): 68 | pc = percent(cost) 69 | if pc > 0: 70 | sb.append(f'\t{fname}{pad(fname)}\t\t{pc}%\n') 71 | sb.append(f'\n\tTotal:{pad("Total:")}\t\t{int(Profiler.__total / 10e6)} milliseconds\n') 72 | return str(sb) 73 | -------------------------------------------------------------------------------- /yices/Status.py: -------------------------------------------------------------------------------- 1 | """Status wraps the smt_status_t enum, that enumerates a context's possible states.""" 2 | import yices_api as yapi 3 | 4 | from .YicesException import YicesException 5 | 6 | class Status: 7 | 8 | IDLE = yapi.STATUS_IDLE 9 | SEARCHING = yapi.STATUS_SEARCHING 10 | UNKNOWN = yapi.STATUS_UNKNOWN 11 | SAT = yapi.STATUS_SAT 12 | UNSAT = yapi.STATUS_UNSAT 13 | INTERRUPTED = yapi.STATUS_INTERRUPTED 14 | ERROR = yapi.STATUS_ERROR 15 | 16 | 17 | @staticmethod 18 | def name(status): 19 | """given a status returns its name, a string describing it.""" 20 | if status == Status.IDLE: 21 | return 'IDLE' 22 | if status == Status.SEARCHING: 23 | return 'SEARCHING' 24 | if status == Status.UNKNOWN: 25 | return 'UNKNOWN' 26 | if status == Status.SAT: 27 | return 'SAT' 28 | if status == Status.UNSAT: 29 | return 'UNSAT' 30 | if status == Status.INTERRUPTED: 31 | return 'INTERRUPTED' 32 | if status == Status.ERROR: 33 | return 'ERROR' 34 | raise YicesException('unknown status: {0}'.format(status)) 35 | -------------------------------------------------------------------------------- /yices/StringBuilder.py: -------------------------------------------------------------------------------- 1 | """ A StringBuilder class for string buffer addicts. 2 | """ 3 | import io 4 | 5 | class StringBuilder: 6 | 7 | def __init__(self): 8 | self.empty = True 9 | self._stringio = io.StringIO() 10 | 11 | def __str__(self): 12 | val = self._stringio.getvalue() 13 | self._stringio.close() 14 | return val 15 | 16 | # this one returns the string buffer (and so could be closed by a print) 17 | def append(self, obj): 18 | data = str(obj) 19 | if self.empty and len(data) > 0: 20 | self.empty = False 21 | self._stringio.write(data) 22 | return self 23 | 24 | # this one returns None 25 | def add(self, obj): 26 | data = str(obj) 27 | if self.empty and len(data) > 0: 28 | self.empty = False 29 | self._stringio.write(data) 30 | 31 | def isempty(self): 32 | return self.empty 33 | -------------------------------------------------------------------------------- /yices/Types.py: -------------------------------------------------------------------------------- 1 | """ The Types class provides Pythonesque static methods for constructing and manipulating yices' types.""" 2 | import yices_api as yapi 3 | 4 | from .YicesException import YicesException 5 | 6 | class Types: 7 | 8 | 9 | NULL_TYPE = -1 10 | BOOL = yapi.yices_bool_type() 11 | INT = yapi.yices_int_type() 12 | REAL = yapi.yices_real_type() 13 | BV8 = yapi.yices_bv_type(8) 14 | BV16 = yapi.yices_bv_type(16) 15 | BV32 = yapi.yices_bv_type(32) 16 | BV64 = yapi.yices_bv_type(64) 17 | 18 | 19 | @staticmethod 20 | def bv_type(nbits, name=None): 21 | if nbits <= 0: 22 | raise YicesException(msg="nbits must be positive") 23 | tau = yapi.yices_bv_type(nbits) 24 | if name and not Types.set_name(tau, name): 25 | return None 26 | return tau 27 | 28 | @staticmethod 29 | def bool_type(name=None): 30 | tau = yapi.yices_bool_type() 31 | if name and not Types.set_name(tau, name): 32 | return None 33 | return tau 34 | 35 | @staticmethod 36 | def int_type(name=None): 37 | tau = yapi.yices_int_type() 38 | if name and not Types.set_name(tau, name): 39 | return None 40 | return tau 41 | 42 | @staticmethod 43 | def real_type(name=None): 44 | tau = yapi.yices_real_type() 45 | if name and not Types.set_name(tau, name): 46 | return None 47 | return tau 48 | 49 | @staticmethod 50 | def new_scalar_type(card, name=None): 51 | if card <= 0: 52 | raise YicesException(msg="new_scalar_type: card must be positive") 53 | tau = yapi.yices_new_scalar_type(card) 54 | if name and not Types.set_name(tau, name): 55 | return None 56 | return tau 57 | 58 | @staticmethod 59 | def new_uninterpreted_type(name=None): 60 | tau = yapi.yices_new_uninterpreted_type() 61 | if name and not Types.set_name(tau, name): 62 | return None 63 | return tau 64 | 65 | @staticmethod 66 | def new_tuple_type(types, name=None): 67 | tau = -1 68 | tlen = len(types) 69 | if tlen <= 0: 70 | raise YicesException(msg="new_tuple_type: len(types) must be positive") 71 | if tlen == 1: 72 | tau = yapi.yices_tuple_type1(types[0]) 73 | elif tlen == 2: 74 | tau = yapi.yices_tuple_type2(types[0], types[1]) 75 | elif tlen == 3: 76 | tau = yapi.yices_tuple_type3(types[0], types[1], types[2]) 77 | else: 78 | tarray = yapi.make_type_array(types) 79 | tau = yapi.yices_tuple_type(tlen, tarray) 80 | if tau == Types.NULL_TYPE: 81 | raise YicesException('yices_tuple_type') 82 | if name and not Types.set_name(tau, name): 83 | return None 84 | return tau 85 | 86 | 87 | @staticmethod 88 | def new_function_type(doms, rng, name=None): 89 | tau = -1 90 | dlen = len(doms) 91 | if dlen <= 0: 92 | raise YicesException(msg="new_function_type: len(doms) must be positive") 93 | if dlen == 1: 94 | tau = yapi.yices_function_type1(doms[0], rng) 95 | elif dlen == 2: 96 | tau = yapi.yices_function_type2(doms[0], doms[1], rng) 97 | elif dlen == 3: 98 | tau = yapi.yices_function_type3(doms[0], doms[1], doms[2], rng) 99 | else: 100 | darray = yapi.make_type_array(doms) 101 | tau = yapi.yices_function_type(dlen, darray, rng) 102 | if tau == Types.NULL_TYPE: 103 | raise YicesException('yices_function_type') 104 | if name and not Types.set_name(tau, name): 105 | return None 106 | return tau 107 | 108 | 109 | 110 | @staticmethod 111 | def declare_enum(name, element_names): 112 | """Declares a new scalar type with the given element_names, which 113 | should all be distinct. 114 | 115 | It returns the yices term for the type, and the list of yices terms 116 | corresponding to the element with the associated name. 117 | """ 118 | assert name 119 | assert element_names 120 | cardinality = len(element_names) 121 | tau = yapi.yices_new_scalar_type(cardinality) 122 | if not Types.set_name(tau, name): 123 | return (None, None) 124 | elements = [None] * cardinality 125 | for i in range(0, cardinality): 126 | elements[i] = yapi.yices_constant(tau, i) 127 | ni = element_names[i] 128 | errcode = yapi.yices_set_term_name(elements[i], ni) 129 | if errcode == -1: 130 | raise YicesException('yices_set_term_name') 131 | return (tau, elements) 132 | 133 | 134 | # recognizers 135 | 136 | @staticmethod 137 | def is_bool(tau): 138 | return bool(yapi.yices_type_is_bool(tau)) 139 | 140 | @staticmethod 141 | def is_int(tau): 142 | return bool(yapi.yices_type_is_int(tau)) 143 | 144 | @staticmethod 145 | def is_real(tau): 146 | return bool(yapi.yices_type_is_real(tau)) 147 | 148 | @staticmethod 149 | def is_arithmetic(tau): 150 | return bool(yapi.yices_type_is_arithmetic(tau)) 151 | 152 | @staticmethod 153 | def is_bitvector(tau): 154 | return bool(yapi.yices_type_is_bitvector(tau)) 155 | 156 | @staticmethod 157 | def is_scalar(tau): 158 | return bool(yapi.yices_type_is_scalar(tau)) 159 | 160 | @staticmethod 161 | def is_uninterpreted(tau): 162 | return bool(yapi.yices_type_is_uninterpreted(tau)) 163 | 164 | @staticmethod 165 | def is_tuple(tau): 166 | return bool(yapi.yices_type_is_tuple(tau)) 167 | 168 | @staticmethod 169 | def is_function(tau): 170 | return bool(yapi.yices_type_is_function(tau)) 171 | 172 | @staticmethod 173 | def is_subtype(tau0, tau1): 174 | return bool(yapi.yices_test_subtype(tau0, tau1)) 175 | 176 | @staticmethod 177 | def compatible_types(tau0, tau1): 178 | return bool(yapi.yices_compatible_types(tau0, tau1)) 179 | 180 | # type deconstruction 181 | 182 | @staticmethod 183 | def bvtype_size(tau): 184 | retval = yapi.yices_bvtype_size(tau) 185 | if retval == 0: 186 | raise YicesException('yices_bvtype_size') 187 | return retval 188 | 189 | @staticmethod 190 | def scalar_type_card(tau): 191 | retval = yapi.yices_scalar_type_card(tau) 192 | if retval == 0: 193 | raise YicesException('yices_scalar_type_card') 194 | return retval 195 | 196 | @staticmethod 197 | def num_children(tau): 198 | retval = yapi.yices_type_num_children(tau) 199 | if retval == -1: 200 | raise YicesException('yices_type_num_children') 201 | return retval 202 | 203 | @staticmethod 204 | def child(tau, i): 205 | retval = yapi.yices_type_child(tau, i) 206 | if retval == Types.NULL_TYPE: 207 | raise YicesException('yices_type_child') 208 | return retval 209 | 210 | @staticmethod 211 | def children(tau): 212 | typev = yapi.type_vector_t() 213 | yapi.yices_init_type_vector(typev) 214 | errcode = yapi.yices_type_children(tau, typev) 215 | if errcode == -1: 216 | yapi.yices_delete_type_vector(typev) 217 | raise YicesException('yices_type_children') 218 | retval = [] 219 | for i in range(0, typev.size): 220 | retval.append(typev.data[i]) 221 | yapi.yices_delete_type_vector(typev) 222 | return retval 223 | 224 | 225 | # parsing 226 | 227 | @staticmethod 228 | def parse_type(s): 229 | return yapi.yices_parse_type(s) 230 | 231 | 232 | # names 233 | 234 | @staticmethod 235 | def set_name(tau, name): 236 | if name is None: 237 | return False 238 | errcode = yapi.yices_set_type_name(tau, name) 239 | if errcode == -1: 240 | raise YicesException('yices_set_type_name') 241 | return True 242 | 243 | @staticmethod 244 | def remove_name(name): 245 | if name is None: 246 | return False 247 | yapi.yices_remove_type_name(name) 248 | return True 249 | 250 | 251 | @staticmethod 252 | def clear_name(tau): 253 | errcode = yapi.yices_clear_type_name(tau) 254 | return errcode == 0 255 | 256 | @staticmethod 257 | def get_name(tau): 258 | name = yapi.yices_get_type_name(tau) 259 | if name == 0: 260 | return None 261 | return name 262 | 263 | @staticmethod 264 | def get_by_name(name): 265 | return yapi.yices_get_type_by_name(name) 266 | 267 | # printing 268 | 269 | @staticmethod 270 | def print_to_fd(fd, tau, width, height, offset): 271 | errcode = yapi.yices_pp_type_fd(fd, tau, int(width), int(height), int(offset)) 272 | if errcode == -1: 273 | raise YicesException('yices_pp_type_fd') 274 | 275 | 276 | @staticmethod 277 | def to_string(tau, width, height, offset): 278 | retval = yapi.yices_type_to_string(tau, int(width), int(height), int(offset)) 279 | if retval == 0: 280 | raise YicesException('yices_type_to_string') 281 | return retval 282 | -------------------------------------------------------------------------------- /yices/Yices.py: -------------------------------------------------------------------------------- 1 | """Yices is the top level interface with the yices library.""" 2 | 3 | 4 | import yices_api as yapi 5 | 6 | from .Profiler import Profiler, profile 7 | 8 | 9 | 10 | 11 | class Yices: 12 | """A thin wrapper to the yices_api class used for things like profiling.""" 13 | 14 | version = yapi.yices_version 15 | 16 | build_arch = yapi.yices_build_arch 17 | 18 | build_mode = yapi.yices_build_mode 19 | 20 | build_date = yapi.yices_build_date 21 | 22 | @staticmethod 23 | @profile 24 | def has_mcsat(): 25 | """Return true if the underlying libyices has been compiled with mcsat support, false otherwise.""" 26 | return yapi.yices_has_mcsat() == 1 27 | 28 | @staticmethod 29 | def is_thread_safe(): 30 | """Return true if the underlying libyices has been compiled with thread safety enabled, false otherwise.""" 31 | return yapi.yices_is_thread_safe() == 1 32 | 33 | 34 | # new in 2.6.2 35 | @staticmethod 36 | def has_delegate(delegate): 37 | """Returns True if the underlying libyices has been compiled with the given delegate supported. 38 | 39 | Valid delegates are "cadical", "cryptominisat", and "y2sat". 40 | """ 41 | return yapi.yices_has_delegate(delegate) == 1 42 | 43 | @staticmethod 44 | def error_code(): 45 | """Return the last error code, see yices_types.h for a full list.""" 46 | return yapi.yices_error_code() 47 | 48 | @staticmethod 49 | def error_string(): 50 | """Returns a string explaining the last error.""" 51 | return yapi.yices_error_string() 52 | 53 | @staticmethod 54 | def error_report(): 55 | """Return the latest error report, see yices.h.""" 56 | return yapi.yices_error_report() 57 | 58 | @staticmethod 59 | def clear_error(): 60 | """Clears the error reprt structure.""" 61 | return yapi.yices_clear_error() 62 | 63 | @staticmethod 64 | def print_error(fd): 65 | """Prints the error report out to the given file descriptor.""" 66 | return yapi.yices_print_error_fd(fd) 67 | 68 | @staticmethod 69 | def init(): 70 | """Must be called before any other API routine (other than is_inited), to initialize internal data structures.""" 71 | yapi.yices_init() 72 | 73 | @staticmethod 74 | def is_inited(): 75 | """Return True if the library has been initialized, False otherwise.""" 76 | return yapi.yices_is_inited() 77 | 78 | @staticmethod 79 | def exit(show_profile=False): 80 | """Deletes all the internal data structure, must be called on exiting to prevent leaks.""" 81 | if show_profile: 82 | print(Profiler.dump()) 83 | yapi.yices_exit() 84 | 85 | @staticmethod 86 | def reset(): 87 | """Resets all the internal data structures.""" 88 | yapi.yices_reset() 89 | 90 | 91 | ################# 92 | # CONTEXTS # 93 | ################# 94 | 95 | @staticmethod 96 | @profile 97 | def new_context(config): 98 | """Returns a newly allocated context; a context is a stack of assertions.""" 99 | return yapi.yices_new_context(config) 100 | 101 | @staticmethod 102 | @profile 103 | def free_context(ctx): 104 | """Frees the given context.""" 105 | yapi.yices_free_context(ctx) 106 | 107 | @staticmethod 108 | @profile 109 | def context_status(ctx): 110 | """The context status.""" 111 | return yapi.yices_context_status(ctx) 112 | 113 | @staticmethod 114 | @profile 115 | def reset_context(ctx): 116 | """Removes all assertions from the context.""" 117 | yapi.yices_reset_context(ctx) 118 | 119 | @staticmethod 120 | @profile 121 | def push(ctx): 122 | """Marks a backtrack point in the context.""" 123 | return yapi.yices_push(ctx) 124 | 125 | @staticmethod 126 | @profile 127 | def pop(ctx): 128 | """Backtracks to the previous backtrack point.""" 129 | return yapi.yices_pop(ctx) 130 | 131 | @staticmethod 132 | @profile 133 | def context_enable_option(ctx, option): 134 | """Used to tune the amount of simplification used when evaluating assertions.""" 135 | return yapi.yices_context_enable_option(ctx, option) 136 | 137 | @staticmethod 138 | @profile 139 | def context_disable_option(ctx, option): 140 | """Used to tune the amount of simplification used when evaluating assertions.""" 141 | return yapi.yices_context_disable_option(ctx, option) 142 | 143 | @staticmethod 144 | @profile 145 | def assert_formula(ctx, t): 146 | """Assert the formula t in the context ctx.""" 147 | return yapi.yices_assert_formula(ctx, t) 148 | 149 | @staticmethod 150 | @profile 151 | def assert_formulas(ctx, n, t): 152 | """Assert an array of formulas of length n in the context ctx.""" 153 | return yapi.yices_assert_formulas(ctx, n, t) 154 | 155 | @staticmethod 156 | @profile 157 | def check_context(ctx, params): 158 | """Checks whether all the assertions stored in the context ctx are satisfiable.""" 159 | return yapi.yices_check_context(ctx, params) 160 | 161 | @staticmethod 162 | @profile 163 | def check_context_with_assumptions(ctx, params, n, t): 164 | """Checks whether the assertions in the context ctx together with n assumptions are satisfiable.""" 165 | return yapi.yices_check_context_with_assumptions(ctx, params, n, t) 166 | 167 | @staticmethod 168 | @profile 169 | def assert_blocking_clause(ctx): 170 | """Adds a blocking clause, this is intended to help enumerate different models for a set of assertions.""" 171 | return yapi.yices_assert_blocking_clause(ctx) 172 | 173 | @staticmethod 174 | @profile 175 | def stop_search(ctx): 176 | """Interupts the search.""" 177 | yapi.yices_stop_search(ctx) 178 | 179 | ################# 180 | # UNSAT CORES # 181 | ################# 182 | 183 | @staticmethod 184 | @profile 185 | def get_unsat_core(ctx, v): 186 | """Compute an unsat core after a call to yices_check_with_assumptions.""" 187 | return yapi.yices_get_unsat_core(ctx, v) 188 | 189 | ################ 190 | # MODELS # 191 | ################ 192 | 193 | @staticmethod 194 | @profile 195 | def new_model(): 196 | """Builds a model from scratch.""" 197 | return yapi.yices_new_model() 198 | 199 | 200 | @staticmethod 201 | @profile 202 | def get_model(ctx, keep_subst): 203 | """Builds a model from the context ctx.""" 204 | return yapi.yices_get_model(ctx, keep_subst) 205 | 206 | @staticmethod 207 | @profile 208 | def free_model(mdl): 209 | """Frees the model.""" 210 | yapi.yices_free_model(mdl) 211 | 212 | @staticmethod 213 | @profile 214 | def model_from_map(n, var, mp): 215 | """Builds a model from a term to term mapping.""" 216 | return yapi.yices_model_from_map(n, var, mp) 217 | 218 | @staticmethod 219 | @profile 220 | def model_collect_defined_terms(mdl, v): 221 | """Collects all the uninterpreted terms that have a value in mdl and store them in v.""" 222 | return yapi.yices_model_collect_defined_terms(mdl, v) 223 | 224 | # new in 2.6.2 225 | @staticmethod 226 | @profile 227 | def check_formula(f, logic, model, delegate): 228 | return yapi.yices_check_formula(f, logic, model, delegate) 229 | 230 | # new in 2.6.2 231 | @staticmethod 232 | @profile 233 | def check_formulas(f, n, logic, model, delegate): 234 | return yapi.yices_check_formulas(f, n, logic, model, delegate) 235 | 236 | # new in 2.6.2 237 | @staticmethod 238 | @profile 239 | def export_formula_to_dimacs(f, filename, simplify_cnf, status): 240 | """Bit-blast then export the CNF to a file.""" 241 | return yapi.yices_export_formula_to_dimacs(f, filename, simplify_cnf, status) 242 | 243 | 244 | # new in 2.6.2 245 | @staticmethod 246 | @profile 247 | def export_formulas_to_dimacs(f, n, filename, simplify_cnf, status): 248 | """Bit-blast then export the CNF to a file.""" 249 | return yapi.yices_export_formulas_to_dimacs(f, n, filename, simplify_cnf, status) 250 | 251 | # new in 2.6.2 252 | @staticmethod 253 | @profile 254 | def model_term_support(mdl, t, v): 255 | """Get the support of a term t in mdl.""" 256 | return yapi.yices_model_term_support(mdl, t, v) 257 | 258 | # new in 2.6.2 259 | @staticmethod 260 | @profile 261 | def model_term_array_support(mdl, n, t, v): 262 | """Get the support of a term t in mdl.""" 263 | return yapi.yices_model_term_array_support(mdl, n, t, v) 264 | 265 | 266 | ######################## 267 | # VALUES IN A MODEL # 268 | ######################## 269 | -------------------------------------------------------------------------------- /yices/YicesException.py: -------------------------------------------------------------------------------- 1 | """YicesException is the base class for exceptions from the Pythonesque Yices Package.""" 2 | 3 | import yices_api as yapi 4 | 5 | 6 | class YicesException(Exception): 7 | """Base class for exceptions from the Pythonesque Yices Package.""" 8 | 9 | 10 | LONG_MSG = 'The function {0} failed because: {1}' 11 | 12 | def __init__(self, function=None, msg=None): 13 | if function is None: 14 | super().__init__('' if msg is None else msg) 15 | else: 16 | super().__init__(YicesException.LONG_MSG.format(function, yapi.yices_error_string())) 17 | -------------------------------------------------------------------------------- /yices/Yvals.py: -------------------------------------------------------------------------------- 1 | """YVals is the Python representation of the yval_t enum, the node tags of value DAGS is a Model.""" 2 | import yices_api as yapi 3 | 4 | 5 | class Yval: 6 | 7 | UNKNOWN = yapi.YVAL_UNKNOWN 8 | BOOL = yapi.YVAL_BOOL 9 | RATIONAL = yapi.YVAL_RATIONAL 10 | ALGEBRAIC = yapi.YVAL_ALGEBRAIC 11 | BV = yapi.YVAL_BV 12 | SCALAR = yapi.YVAL_SCALAR 13 | TUPLE = yapi.YVAL_TUPLE 14 | FUNCTION = yapi.YVAL_FUNCTION 15 | MAPPING = yapi.YVAL_MAPPING 16 | -------------------------------------------------------------------------------- /yices/__init__.py: -------------------------------------------------------------------------------- 1 | """This module defines the wild card imports from the yices package.""" 2 | import yices_api as yapi 3 | 4 | # iam: this has to go before the imports below to ensure that the 5 | # yices2 library is initialized (and so static fields get initialized 6 | # ok). 7 | yapi.yices_init() # pylint: disable=wrong-import-position 8 | 9 | from yices.Census import Census 10 | from yices.Config import Config 11 | from yices.Context import Context 12 | from yices.Constructors import Constructor 13 | from yices.Delegates import Delegates 14 | from yices.Model import Model 15 | from yices.Profiler import Profiler 16 | from yices.Parameters import Parameters 17 | from yices.Status import Status 18 | from yices.Types import Types 19 | from yices.Terms import Terms 20 | from yices.YicesException import YicesException 21 | from yices.Yices import Yices 22 | from yices.Yvals import Yval 23 | 24 | 25 | __all__ = ['Census', 26 | 'Config', 27 | 'Context', 28 | 'Constructor', 29 | 'Delegates', 30 | 'Model', 31 | 'Parameters', 32 | 'Profiler', 33 | 'Status', 34 | 'Types', 35 | 'Terms', 36 | 'YicesException', 37 | 'Yices', 38 | 'Yval'] 39 | --------------------------------------------------------------------------------