├── .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 | [](https://opensource.org/licenses/MIT)
2 | [](https://badge.fury.io/py/yices)
3 | [](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 |
--------------------------------------------------------------------------------