summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml20
-rw-r--r--CMakeLists.txt116
-rw-r--r--cmake/FindCython.cmake77
-rw-r--r--cmake/FindPythonExtensions.cmake503
-rw-r--r--cmake/UseCython.cmake403
-rw-r--r--cmake/targetLinkLibrariesWithDynamicLookup.cmake478
-rwxr-xr-xconfigure.sh11
-rwxr-xr-xexamples/api/python/bitvectors.py121
-rwxr-xr-xexamples/api/python/bitvectors_and_arrays.py91
-rwxr-xr-xexamples/api/python/combination.py99
-rwxr-xr-xexamples/api/python/datatypes.py137
-rwxr-xr-xexamples/api/python/extract.py51
-rwxr-xr-xexamples/api/python/helloworld.py21
-rwxr-xr-xexamples/api/python/linear_arith.py70
-rwxr-xr-xexamples/api/python/sets.py85
-rwxr-xr-xexamples/api/python/strings.py87
-rw-r--r--src/api/cvc4cpp.cpp25
-rw-r--r--src/api/cvc4cpp.h20
-rw-r--r--src/api/python/CMakeLists.txt42
-rw-r--r--src/api/python/cvc4.pxd274
-rw-r--r--src/api/python/cvc4.pxi1181
-rwxr-xr-xsrc/api/python/genkinds.py255
-rw-r--r--src/api/python/pycvc4.pyx2
-rw-r--r--src/bindings/CMakeLists.txt10
-rw-r--r--test/CMakeLists.txt2
25 files changed, 4116 insertions, 65 deletions
diff --git a/.travis.yml b/.travis.yml
index fbf0d2bb9..1f646f0de 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -23,6 +23,9 @@ addons:
- libgmp-dev
- libhamcrest-java
- openjdk-8-jdk
+ - python3
+ - python3-pip
+ - python3-setuptools
- swig3.0
before_install:
- eval "${MATRIX_EVAL}"
@@ -43,7 +46,8 @@ script:
- ccache -z
- ${CC} --version
- ${CXX} --version
- - sudo pip install toml
+ - sudo ${TRAVIS_PYTHON} -m pip install toml
+ - sudo ${TRAVIS_PYTHON} -m pip install Cython==0.29 --install-option="--no-cython-compile"
- |
echo "travis_fold:start:load_script"
normal="$(echo -e '\033[0m')" red="$normal$(echo -e '\033[01;31m')" green="$normal$(echo -e '\033[01;32m')"
@@ -103,16 +107,22 @@ matrix:
# Test with GCC
- compiler: gcc
env:
- - TRAVIS_CVC4=yes TRAVIS_WITH_LFSC=yes TRAVIS_CVC4_CONFIG='production --language-bindings=java --lfsc'
+ - TRAVIS_CVC4=yes TRAVIS_WITH_LFSC=yes TRAVIS_CVC4_CONFIG='production --language-bindings=java --lfsc' TRAVIS_PYTHON="python"
- compiler: gcc
env:
- - TRAVIS_CVC4=yes TRAVIS_WITH_LFSC=yes TRAVIS_CVC4_CONFIG='debug --symfpu --lfsc --no-debug-symbols'
-
+ - TRAVIS_CVC4=yes TRAVIS_WITH_LFSC=yes TRAVIS_CVC4_CONFIG='debug --symfpu --lfsc --no-debug-symbols' TRAVIS_PYTHON="python"
+ # Test python bindings
+ - compiler: gcc
+ env:
+ - TRAVIS_CVC4=yes TRAVIS_WITH_LFSC=yes TRAVIS_CVC4_CONFIG="production --python-bindings --python2" TRAVIS_PYTHON="python"
+ - compiler: gcc
+ env:
+ - TRAVIS_CVC4=yes TRAVIS_WITH_LFSC=yes TRAVIS_CVC4_CONFIG="production --python-bindings --python3" TRAVIS_PYTHON="python3"
#
# Test with Clang
- compiler: clang
env:
- - TRAVIS_CVC4=yes TRAVIS_WITH_LFSC=yes TRAVIS_CVC4_CONFIG='debug --symfpu --cln --gpl --no-debug-symbols --no-proofs'
+ - TRAVIS_CVC4=yes TRAVIS_WITH_LFSC=yes TRAVIS_CVC4_CONFIG='debug --symfpu --cln --gpl --no-debug-symbols --no-proofs' TRAVIS_PYTHON="python"
notifications:
email:
on_success: change
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1582a3bde..0cc1174d9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -174,9 +174,12 @@ set(SYMFPU_DIR "" CACHE STRING "Set SymFPU install directory")
# Prepend binaries with prefix on make install
set(PROGRAM_PREFIX "" CACHE STRING "Program prefix on make install")
-# Supported language bindings
-option(BUILD_BINDINGS_JAVA "Build Java bindings")
-option(BUILD_BINDINGS_PYTHON "Build Python bindings")
+# Supported SWIG language bindings
+option(BUILD_SWIG_BINDINGS_JAVA "Build Java bindings with SWIG")
+option(BUILD_SWIG_BINDINGS_PYTHON "Build Python bindings with SWIG")
+
+# Supprted language bindings based on new C++ API
+option(BUILD_BINDINGS_PYTHON "Build Python bindings based on new C++ API ")
#-----------------------------------------------------------------------------#
# Internal cmake variables
@@ -531,10 +534,14 @@ add_subdirectory(doc)
add_subdirectory(src)
add_subdirectory(test)
-if(BUILD_BINDINGS_JAVA OR BUILD_BINDINGS_PYTHON)
+if(BUILD_SWIG_BINDINGS_JAVA OR BUILD_SWIG_BINDINGS_PYTHON)
add_subdirectory(src/bindings)
endif()
+if(BUILD_BINDINGS_PYTHON)
+ add_subdirectory(src/api/python)
+endif()
+
#-----------------------------------------------------------------------------#
# Package configuration
#
@@ -585,89 +592,90 @@ else()
message("Build profile : ${CVC4_BUILD_PROFILE_STRING}")
endif()
message("")
-print_config("GPL :" ENABLE_GPL)
-print_config("Best configuration :" ENABLE_BEST)
-print_config("Optimization level :" OPTIMIZATION_LEVEL)
+print_config("GPL :" ENABLE_GPL)
+print_config("Best configuration :" ENABLE_BEST)
+print_config("Optimization level :" OPTIMIZATION_LEVEL)
message("")
-print_config("Assertions :" ENABLE_ASSERTIONS)
-print_config("Debug symbols :" ENABLE_DEBUG_SYMBOLS)
-print_config("Debug context mem mgr:" ENABLE_DEBUG_CONTEXT_MM)
+print_config("Assertions :" ENABLE_ASSERTIONS)
+print_config("Debug symbols :" ENABLE_DEBUG_SYMBOLS)
+print_config("Debug context mem mgr :" ENABLE_DEBUG_CONTEXT_MM)
message("")
-print_config("Dumping :" ENABLE_DUMPING)
-print_config("Muzzle :" ENABLE_MUZZLE)
-print_config("Proofs :" ENABLE_PROOFS)
-print_config("Replay :" ENABLE_REPLAY)
-print_config("Statistics :" ENABLE_STATISTICS)
-print_config("Tracing :" ENABLE_TRACING)
+print_config("Dumping :" ENABLE_DUMPING)
+print_config("Muzzle :" ENABLE_MUZZLE)
+print_config("Proofs :" ENABLE_PROOFS)
+print_config("Replay :" ENABLE_REPLAY)
+print_config("Statistics :" ENABLE_STATISTICS)
+print_config("Tracing :" ENABLE_TRACING)
message("")
-print_config("ASan :" ENABLE_ASAN)
-print_config("UBSan :" ENABLE_UBSAN)
-print_config("TSan :" ENABLE_TSAN)
-print_config("Coverage (gcov) :" ENABLE_COVERAGE)
-print_config("Profiling (gprof) :" ENABLE_PROFILING)
-print_config("Unit tests :" ENABLE_UNIT_TESTING)
-print_config("Valgrind :" ENABLE_VALGRIND)
+print_config("ASan :" ENABLE_ASAN)
+print_config("UBSan :" ENABLE_UBSAN)
+print_config("TSan :" ENABLE_TSAN)
+print_config("Coverage (gcov) :" ENABLE_COVERAGE)
+print_config("Profiling (gprof) :" ENABLE_PROFILING)
+print_config("Unit tests :" ENABLE_UNIT_TESTING)
+print_config("Valgrind :" ENABLE_VALGRIND)
message("")
-print_config("Shared libs :" ENABLE_SHARED)
-print_config("Static binary :" ENABLE_STATIC_BINARY)
-print_config("Java bindings :" BUILD_BINDINGS_JAVA)
-print_config("Python bindings :" BUILD_BINDINGS_PYTHON)
-print_config("Python2 :" USE_PYTHON2)
-print_config("Python3 :" USE_PYTHON3)
+print_config("Shared libs :" ENABLE_SHARED)
+print_config("Static binary :" ENABLE_STATIC_BINARY)
+print_config("Java SWIG bindings :" BUILD_SWIG_BINDINGS_JAVA)
+print_config("Python SWIG bindings :" BUILD_SWIG_BINDINGS_PYTHON)
+print_config("Python bindings :" BUILD_BINDINGS_PYTHON)
+print_config("Python2 :" USE_PYTHON2)
+print_config("Python3 :" USE_PYTHON3)
message("")
-print_config("ABC :" USE_ABC)
-print_config("CaDiCaL :" USE_CADICAL)
-print_config("CryptoMiniSat :" USE_CRYPTOMINISAT)
-print_config("drat2er :" USE_DRAT2ER)
-print_config("GLPK :" USE_GLPK)
-print_config("LFSC :" USE_LFSC)
+print_config("ABC :" USE_ABC)
+print_config("CaDiCaL :" USE_CADICAL)
+print_config("CryptoMiniSat :" USE_CRYPTOMINISAT)
+print_config("drat2er :" USE_DRAT2ER)
+print_config("GLPK :" USE_GLPK)
+print_config("LFSC :" USE_LFSC)
if(CVC4_USE_CLN_IMP)
- message("MP library : cln")
+ message("MP library : cln")
else()
- message("MP library : gmp")
+ message("MP library : gmp")
endif()
-print_config("Readline :" ${USE_READLINE})
-print_config("SymFPU :" ${USE_SYMFPU})
+print_config("Readline :" ${USE_READLINE})
+print_config("SymFPU :" ${USE_SYMFPU})
message("")
if(ABC_DIR)
- message("ABC dir : ${ABC_DIR}")
+ message("ABC dir : ${ABC_DIR}")
endif()
if(ANTLR_DIR)
- message("ANTLR dir : ${ANTLR_DIR}")
+ message("ANTLR dir : ${ANTLR_DIR}")
endif()
if(CADICAL_DIR)
- message("CADICAL dir : ${CADICAL_DIR}")
+ message("CADICAL dir : ${CADICAL_DIR}")
endif()
if(CRYPTOMINISAT_DIR)
- message("CRYPTOMINISAT dir : ${CRYPTOMINISAT_DIR}")
+ message("CRYPTOMINISAT dir : ${CRYPTOMINISAT_DIR}")
endif()
if(DRAT2ER_DIR)
- message("DRAT2ER dir : ${DRAT2ER_DIR}")
+ message("DRAT2ER dir : ${DRAT2ER_DIR}")
endif()
if(GLPK_DIR)
- message("GLPK dir : ${GLPK_DIR}")
+ message("GLPK dir : ${GLPK_DIR}")
endif()
if(GMP_DIR)
- message("GMP dir : ${GMP_DIR}")
+ message("GMP dir : ${GMP_DIR}")
endif()
if(LFSC_DIR)
- message("LFSC dir : ${LFSC_DIR}")
+ message("LFSC dir : ${LFSC_DIR}")
endif()
if(SYMFPU_DIR)
- message("SYMFPU dir : ${SYMFPU_DIR}")
+ message("SYMFPU dir : ${SYMFPU_DIR}")
endif()
message("")
-message("CPPLAGS (-D...) : ${CVC4_DEFINITIONS}")
-message("CXXFLAGS : ${CMAKE_CXX_FLAGS}")
-message("CFLAGS : ${CMAKE_C_FLAGS}")
+message("CPPLAGS (-D...) : ${CVC4_DEFINITIONS}")
+message("CXXFLAGS : ${CMAKE_CXX_FLAGS}")
+message("CFLAGS : ${CMAKE_C_FLAGS}")
message("")
-message("Install prefix : ${CMAKE_INSTALL_PREFIX}")
+message("Install prefix : ${CMAKE_INSTALL_PREFIX}")
message("")
if(GPL_LIBS)
message(
- "CVC4 license : GPLv3 (due to optional libraries; see below)"
+ "CVC4 license : GPLv3 (due to optional libraries; see below)"
"\n"
"\n"
"Please note that CVC4 will be built against the following GPLed libraries:"
@@ -684,7 +692,7 @@ if(GPL_LIBS)
)
else()
message(
- "CVC4 license : modified BSD"
+ "CVC4 license : modified BSD"
"\n"
"\n"
"Note that this configuration is NOT built against any GPL'ed libraries, so"
diff --git a/cmake/FindCython.cmake b/cmake/FindCython.cmake
new file mode 100644
index 000000000..6294d2429
--- /dev/null
+++ b/cmake/FindCython.cmake
@@ -0,0 +1,77 @@
+#.rst:
+# FindCython
+# ----------
+#
+# Find ``cython`` executable.
+#
+# This module defines the following variables:
+#
+# ``CYTHON_EXECUTABLE``
+# path to the ``cython`` program
+#
+# ``CYTHON_VERSION``
+# version of ``cython``
+#
+# ``CYTHON_FOUND``
+# true if the program was found
+#
+# See also UseCython.cmake
+#
+#=============================================================================
+# Copyright 2011 Kitware, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#=============================================================================
+
+# Use the Cython executable that lives next to the Python executable
+# if it is a local installation.
+find_package(PythonInterp)
+if(PYTHONINTERP_FOUND)
+ get_filename_component(_python_path ${PYTHON_EXECUTABLE} PATH)
+ find_program(CYTHON_EXECUTABLE
+ NAMES cython cython.bat cython3
+ HINTS ${_python_path}
+ DOC "path to the cython executable")
+else()
+ find_program(CYTHON_EXECUTABLE
+ NAMES cython cython.bat cython3
+ DOC "path to the cython executable")
+endif()
+
+if(CYTHON_EXECUTABLE)
+ set(CYTHON_version_command ${CYTHON_EXECUTABLE} --version)
+
+ execute_process(COMMAND ${CYTHON_version_command}
+ OUTPUT_VARIABLE CYTHON_version_output
+ ERROR_VARIABLE CYTHON_version_error
+ RESULT_VARIABLE CYTHON_version_result
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ if(NOT ${CYTHON_version_result} EQUAL 0)
+ set(_error_msg "Command \"${CYTHON_version_command}\" failed with")
+ set(_error_msg "${_error_msg} output:\n${CYTHON_version_error}")
+ message(SEND_ERROR "${_error_msg}")
+ else()
+ if("${CYTHON_version_output}" MATCHES "^[Cc]ython version ([^,]+)")
+ set(CYTHON_VERSION "${CMAKE_MATCH_1}")
+ endif()
+ endif()
+endif()
+
+include(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cython REQUIRED_VARS CYTHON_EXECUTABLE)
+
+mark_as_advanced(CYTHON_EXECUTABLE)
+
+include(UseCython)
+
diff --git a/cmake/FindPythonExtensions.cmake b/cmake/FindPythonExtensions.cmake
new file mode 100644
index 000000000..892e17c08
--- /dev/null
+++ b/cmake/FindPythonExtensions.cmake
@@ -0,0 +1,503 @@
+#.rst
+# Define functions to create Python modules and executables.
+#
+# This file defines CMake functions to build Python extension modules and
+# stand-alone executables. To use it, first include this file.
+#
+# find_package(PythonExtensions)
+#
+# The following variables are defined:
+# ::
+#
+# PYTHON_PREFIX - absolute path to the current Python
+# distribution's prefix
+# PYTHON_SITE_PACKAGES_DIR - absolute path to the current Python
+# distribution's site-packages directory
+# PYTHON_RELATIVE_SITE_PACKAGES_DIR - path to the current Python
+# distribution's site-packages directory
+# relative to its prefix
+# PYTHON_SEPARATOR - separator string for file path
+# components. Equivalent to ``os.sep`` in
+# Python.
+# PYTHON_PATH_SEPARATOR - separator string for PATH-style
+# environment variables. Equivalent to
+# ``os.pathsep`` in Python.
+#
+# The following functions are defined:
+#
+# python_extension_module(<Target>
+# [LINKED_MODULES_VAR <LinkedModVar>]
+# [FORWARD_DECL_MODULES_VAR <ForwardDeclModVar>])
+#
+# For libraries meant to be used as Python extension modules, either dynamically
+# loaded or directly linked. Amend the configuration of the library target
+# (created using ``add_library``) with additional options needed to build and
+# use the referenced library as a Python extension module.
+#
+# Only extension modules that are configured to be built as MODULE libraries can
+# be runtime-loaded through the standard Python import mechanism. All other
+# modules can only be included in standalone applications that are written to
+# expect their presence. In addition to being linked against the libraries for
+# these modules, such applications must forward declare their entry points and
+# initialize them prior to use. To generate these forward declarations and
+# initializations, see ``python_modules_header``.
+#
+# If ``<Target>`` does not refer to a target, then it is assumed to refer to an
+# extension module that is not linked at all, but compiled along with other
+# source files directly into an executable. Adding these modules does not cause
+# any library configuration modifications, and they are not added to the list of
+# linked modules. They still must be forward declared and initialized, however,
+# and so are added to the forward declared modules list.
+#
+# Options:
+#
+# ``LINKED_MODULES_VAR <LinkedModVar>``
+# Name of the variable referencing a list of extension modules whose libraries
+# must be linked into the executables of any stand-alone applications that use
+# them. By default, the global property ``PY_LINKED_MODULES_LIST`` is used.
+#
+# ``FORWARD_DECL_MODULES_VAR <ForwardDeclModVar>``
+# Name of the variable referencing a list of extension modules whose entry
+# points must be forward declared and called by any stand-alone applications
+# that use them. By default, the global property
+# ``PY_FORWARD_DECL_MODULES_LIST`` is used.
+#
+#
+# python_standalone_executable(<Target>)
+#
+# For standalone executables that initialize their own Python runtime
+# (such as when building source files that include one generated by Cython with
+# the --embed option). Amend the configuration of the executable target
+# (created using ``add_executable``) with additional options needed to properly
+# build the referenced executable.
+#
+# python_modules_header(<Name> [HeaderFilename]
+# [FORWARD_DECL_MODULES_LIST <ForwardDeclModList>]
+# [HEADER_OUTPUT_VAR <HeaderOutputVar>]
+# [INCLUDE_DIR_OUTPUT_VAR <IncludeDirOutputVar>])
+#
+# Generate a header file that contains the forward declarations and
+# initialization routines for the given list of Python extension modules.
+# ``<Name>`` is the logical name for the header file (no file extensions).
+# ``<HeaderFilename>`` is the actual destination filename for the header file
+# (e.g.: decl_modules.h).
+#
+# If only ``<Name>`` is provided, and it ends in the ".h" extension, then it
+# is assumed to be the ``<HeaderFilename>``. The filename of the header file
+# without the extension is used as the logical name. If only ``<Name>`` is
+# provided, and it does not end in the ".h" extension, then the
+# ``<HeaderFilename>`` is assumed to ``<Name>.h``.
+#
+# The exact contents of the generated header file depend on the logical
+# ``<Name>``. It should be set to a value that corresponds to the target
+# application, or for the case of multiple applications, some identifier that
+# conveyes its purpose. It is featured in the generated multiple inclusion
+# guard as well as the names of the generated initialization routines.
+#
+# The generated header file includes forward declarations for all listed
+# modules, as well as implementations for the following class of routines:
+#
+# ``int <Name>_<Module>(void)``
+# Initializes the python extension module, ``<Module>``. Returns an integer
+# handle to the module.
+#
+# ``void <Name>_LoadAllPythonModules(void)``
+# Initializes all listed python extension modules.
+#
+# ``void CMakeLoadAllPythonModules(void);``
+# Alias for ``<Name>_LoadAllPythonModules`` whose name does not depend on
+# ``<Name>``. This function is excluded during preprocessing if the
+# preprocessing macro ``EXCLUDE_LOAD_ALL_FUNCTION`` is defined.
+#
+# ``void Py_Initialize_Wrapper();``
+# Wrapper arpund ``Py_Initialize()`` that initializes all listed python
+# extension modules. This function is excluded during preprocessing if the
+# preprocessing macro ``EXCLUDE_PY_INIT_WRAPPER`` is defined. If this
+# function is generated, then ``Py_Initialize()`` is redefined to a macro
+# that calls this function.
+#
+# Options:
+#
+# ``FORWARD_DECL_MODULES_LIST <ForwardDeclModList>``
+# List of extension modules for which to generate forward declarations of
+# their entry points and their initializations. By default, the global
+# property ``PY_FORWARD_DECL_MODULES_LIST`` is used.
+
+# ``HEADER_OUTPUT_VAR <HeaderOutputVar>``
+# Name of the variable to set to the path to the generated header file. By
+# default, ``<Name>`` is used.
+#
+# ``INCLUDE_DIR_OUTPUT_VAR <IncludeDirOutputVar>``
+# Name of the variable to set to the path to the directory containing the
+# generated header file. By default, ``<Name>_INCLUDE_DIRS`` is used.
+#
+# Defined variables:
+#
+# ``<HeaderOutputVar>``
+# The path to the generated header file
+#
+# ``<IncludeDirOutputVar>``
+# Directory containing the generated header file
+#
+# Example usage:
+#
+# .. code-block:: cmake
+#
+# find_package(PythonInterp)
+# find_package(PythonLibs)
+# find_package(PythonExtensions)
+# find_package(Cython)
+# find_package(Boost COMPONENTS python)
+#
+# # Simple Cython Module -- no executables
+# add_cython_target(_module.pyx)
+# add_library(_module MODULE ${_module})
+# python_extension_module(_module)
+#
+# # Mix of Cython-generated code and C++ code using Boost Python
+# # Stand-alone executable -- no modules
+# include_directories(${Boost_INCLUDE_DIRS})
+# add_cython_target(main.pyx CXX EMBED_MAIN)
+# add_executable(main boost_python_module.cxx ${main})
+# target_link_libraries(main ${Boost_LIBRARIES})
+# python_standalone_executable(main)
+#
+# # stand-alone executable with three extension modules:
+# # one statically linked, one dynamically linked, and one loaded at runtime
+# #
+# # Freely mixes Cython-generated code, code using Boost-Python, and
+# # hand-written code using the CPython API.
+#
+# # module1 -- statically linked
+# add_cython_target(module1.pyx)
+# add_library(module1 STATIC ${module1})
+# python_extension_module(module1
+# LINKED_MODULES_VAR linked_module_list
+# FORWARD_DECL_MODULES_VAR fdecl_module_list)
+#
+# # module2 -- dynamically linked
+# include_directories({Boost_INCLUDE_DIRS})
+# add_library(module2 SHARED boost_module2.cxx)
+# target_link_libraries(module2 ${Boost_LIBRARIES})
+# python_extension_module(module2
+# LINKED_MODULES_VAR linked_module_list
+# FORWARD_DECL_MODULES_VAR fdecl_module_list)
+#
+# # module3 -- loaded at runtime
+# add_cython_target(module3a.pyx)
+# add_library(module1 MODULE ${module3a} module3b.cxx)
+# target_link_libraries(module3 ${Boost_LIBRARIES})
+# python_extension_module(module3
+# LINKED_MODULES_VAR linked_module_list
+# FORWARD_DECL_MODULES_VAR fdecl_module_list)
+#
+# # application executable -- generated header file + other source files
+# python_modules_header(modules
+# FORWARD_DECL_MODULES_LIST ${fdecl_module_list})
+# include_directories(${modules_INCLUDE_DIRS})
+#
+# add_cython_target(mainA)
+# add_cython_target(mainC)
+# add_executable(main ${mainA} mainB.cxx ${mainC} mainD.c)
+#
+# target_link_libraries(main ${linked_module_list} ${Boost_LIBRARIES})
+# python_standalone_executable(main)
+#
+#=============================================================================
+# Copyright 2011 Kitware, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#=============================================================================
+
+find_package(PythonInterp REQUIRED)
+find_package(PythonLibs)
+include(targetLinkLibrariesWithDynamicLookup)
+
+set(_command "
+import distutils.sysconfig
+import itertools
+import os
+import os.path
+import site
+import sys
+
+result = None
+rel_result = None
+candidate_lists = []
+
+try:
+ candidate_lists.append((distutils.sysconfig.get_python_lib(),))
+except AttributeError: pass
+
+try:
+ candidate_lists.append(site.getsitepackages())
+except AttributeError: pass
+
+try:
+ candidate_lists.append((site.getusersitepackages(),))
+except AttributeError: pass
+
+candidates = itertools.chain.from_iterable(candidate_lists)
+
+for candidate in candidates:
+ rel_candidate = os.path.relpath(
+ candidate, sys.prefix)
+ if not rel_candidate.startswith(\"..\"):
+ result = candidate
+ rel_result = rel_candidate
+ break
+
+sys.stdout.write(\";\".join((
+ os.sep,
+ os.pathsep,
+ sys.prefix,
+ result,
+ rel_result,
+)))
+")
+
+execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "${_command}"
+ OUTPUT_VARIABLE _list
+ RESULT_VARIABLE _result)
+
+list(GET _list 0 _item)
+set(PYTHON_SEPARATOR "${_item}")
+mark_as_advanced(PYTHON_SEPARATOR)
+
+list(GET _list 1 _item)
+set(PYTHON_PATH_SEPARATOR "${_item}")
+mark_as_advanced(PYTHON_PATH_SEPARATOR)
+
+list(GET _list 2 _item)
+set(PYTHON_PREFIX "${_item}")
+mark_as_advanced(PYTHON_PREFIX)
+
+list(GET _list 3 _item)
+set(PYTHON_SITE_PACKAGES_DIR "${_item}")
+mark_as_advanced(PYTHON_SITE_PACKAGES_DIR)
+
+list(GET _list 4 _item)
+set(PYTHON_RELATIVE_SITE_PACKAGES_DIR "${_item}")
+mark_as_advanced(PYTHON_RELATIVE_SITE_PACKAGES_DIR)
+
+function(python_extension_module _target)
+ set(one_ops LINKED_MODULES_VAR FORWARD_DECL_MODULES_VAR)
+ cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN})
+
+ set(_lib_type "NA")
+ if(TARGET ${_target})
+ get_property(_lib_type TARGET ${_target} PROPERTY TYPE)
+ endif()
+
+ set(_is_non_lib TRUE)
+
+ set(_is_static_lib FALSE)
+ if(_lib_type STREQUAL "STATIC_LIBRARY")
+ set(_is_static_lib TRUE)
+ set(_is_non_lib FALSE)
+ endif()
+
+ set(_is_shared_lib FALSE)
+ if(_lib_type STREQUAL "SHARED_LIBRARY")
+ set(_is_shared_lib TRUE)
+ set(_is_non_lib FALSE)
+ endif()
+
+ set(_is_module_lib FALSE)
+ if(_lib_type STREQUAL "MODULE_LIBRARY")
+ set(_is_module_lib TRUE)
+ set(_is_non_lib FALSE)
+ endif()
+
+ if(_is_static_lib OR _is_shared_lib OR _is_non_lib)
+
+ if(_is_static_lib OR _is_shared_lib)
+ if(_args_LINKED_MODULES_VAR)
+ set(${_args_LINKED_MODULES_VAR}
+ ${${_args_LINKED_MODULES_VAR}} ${_target} PARENT_SCOPE)
+ else()
+ set_property(GLOBAL APPEND PROPERTY PY_LINKED_MODULES_LIST ${_target})
+ endif()
+ endif()
+
+ if(_args_FORWARD_DECL_MODULES_VAR)
+ set(${_args_FORWARD_DECL_MODULES_VAR}
+ ${${_args_FORWARD_DECL_MODULES_VAR}} ${_target} PARENT_SCOPE)
+ else()
+ set_property(GLOBAL APPEND PROPERTY
+ PY_FORWARD_DECL_MODULES_LIST ${_target})
+ endif()
+ endif()
+
+ if(NOT _is_non_lib)
+ include_directories("${PYTHON_INCLUDE_DIRS}")
+ endif()
+
+ if(_is_module_lib)
+ set_target_properties(${_target} PROPERTIES
+ PREFIX "${PYTHON_MODULE_PREFIX}")
+ endif()
+
+ if(_is_module_lib OR _is_shared_lib)
+ if(_is_module_lib AND WIN32 AND NOT CYGWIN)
+ set_target_properties(${_target} PROPERTIES SUFFIX ".pyd")
+ endif()
+
+ target_link_libraries_with_dynamic_lookup(${_target} ${PYTHON_LIBRARIES})
+ endif()
+endfunction()
+
+function(python_standalone_executable _target)
+ include_directories(${PYTHON_INCLUDE_DIRS})
+ target_link_libraries(${_target} ${PYTHON_LIBRARIES})
+endfunction()
+
+function(python_modules_header _name)
+ set(one_ops FORWARD_DECL_MODULES_LIST
+ HEADER_OUTPUT_VAR
+ INCLUDE_DIR_OUTPUT_VAR)
+ cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN})
+
+ list(GET _args_UNPARSED_ARGUMENTS 0 _arg0)
+ # if present, use arg0 as the input file path
+ if(_arg0)
+ set(_source_file ${_arg0})
+
+ # otherwise, must determine source file from name, or vice versa
+ else()
+ get_filename_component(_name_ext "${_name}" EXT)
+
+ # if extension provided, _name is the source file
+ if(_name_ext)
+ set(_source_file ${_name})
+ get_filename_component(_name "${_source_file}" NAME_WE)
+
+ # otherwise, assume the source file is ${_name}.h
+ else()
+ set(_source_file ${_name}.h)
+ endif()
+ endif()
+
+ if(_args_FORWARD_DECL_MODULES_LIST)
+ set(static_mod_list ${_args_FORWARD_DECL_MODULES_LIST})
+ else()
+ get_property(static_mod_list GLOBAL PROPERTY PY_FORWARD_DECL_MODULES_LIST)
+ endif()
+
+ string(REPLACE "." "_" _header_name "${_name}")
+ string(TOUPPER ${_header_name} _header_name_upper)
+ set(_header_name_upper "_${_header_name_upper}_H")
+ set(generated_file ${CMAKE_CURRENT_BINARY_DIR}/${_source_file})
+
+ set(generated_file_tmp "${generated_file}.in")
+ file(WRITE ${generated_file_tmp}
+ "/* Created by CMake. DO NOT EDIT; changes will be lost. */\n")
+
+ set(_chunk "")
+ set(_chunk "${_chunk}#ifndef ${_header_name_upper}\n")
+ set(_chunk "${_chunk}#define ${_header_name_upper}\n")
+ set(_chunk "${_chunk}\n")
+ set(_chunk "${_chunk}#include <Python.h>\n")
+ set(_chunk "${_chunk}\n")
+ set(_chunk "${_chunk}#ifdef __cplusplus\n")
+ set(_chunk "${_chunk}extern \"C\" {\n")
+ set(_chunk "${_chunk}#endif /* __cplusplus */\n")
+ set(_chunk "${_chunk}\n")
+ set(_chunk "${_chunk}#if PY_MAJOR_VERSION < 3\n")
+ file(APPEND ${generated_file_tmp} "${_chunk}")
+
+ foreach(_module ${static_mod_list})
+ file(APPEND ${generated_file_tmp}
+ "PyMODINIT_FUNC init${PYTHON_MODULE_PREFIX}${_module}(void);\n")
+ endforeach()
+
+ file(APPEND ${generated_file_tmp} "#else /* PY_MAJOR_VERSION >= 3*/\n")
+
+ foreach(_module ${static_mod_list})
+ file(APPEND ${generated_file_tmp}
+ "PyMODINIT_FUNC PyInit_${PYTHON_MODULE_PREFIX}${_module}(void);\n")
+ endforeach()
+
+ set(_chunk "")
+ set(_chunk "${_chunk}#endif /* PY_MAJOR_VERSION >= 3*/\n\n")
+ set(_chunk "${_chunk}#ifdef __cplusplus\n")
+ set(_chunk "${_chunk}}\n")
+ set(_chunk "${_chunk}#endif /* __cplusplus */\n")
+ set(_chunk "${_chunk}\n")
+ file(APPEND ${generated_file_tmp} "${_chunk}")
+
+ foreach(_module ${static_mod_list})
+ set(_import_function "${_header_name}_${_module}")
+ set(_prefixed_module "${PYTHON_MODULE_PREFIX}${_module}")
+
+ set(_chunk "")
+ set(_chunk "${_chunk}int ${_import_function}(void)\n")
+ set(_chunk "${_chunk}{\n")
+ set(_chunk "${_chunk} static char name[] = \"${_prefixed_module}\";\n")
+ set(_chunk "${_chunk} #if PY_MAJOR_VERSION < 3\n")
+ set(_chunk "${_chunk} return PyImport_AppendInittab(")
+ set(_chunk "${_chunk}name, init${_prefixed_module});\n")
+ set(_chunk "${_chunk} #else /* PY_MAJOR_VERSION >= 3 */\n")
+ set(_chunk "${_chunk} return PyImport_AppendInittab(")
+ set(_chunk "${_chunk}name, PyInit_${_prefixed_module});\n")
+ set(_chunk "${_chunk} #endif /* PY_MAJOR_VERSION >= 3 */\n")
+ set(_chunk "${_chunk}}\n\n")
+ file(APPEND ${generated_file_tmp} "${_chunk}")
+ endforeach()
+
+ file(APPEND ${generated_file_tmp}
+ "void ${_header_name}_LoadAllPythonModules(void)\n{\n")
+ foreach(_module ${static_mod_list})
+ file(APPEND ${generated_file_tmp} " ${_header_name}_${_module}();\n")
+ endforeach()
+ file(APPEND ${generated_file_tmp} "}\n\n")
+
+ set(_chunk "")
+ set(_chunk "${_chunk}#ifndef EXCLUDE_LOAD_ALL_FUNCTION\n")
+ set(_chunk "${_chunk}void CMakeLoadAllPythonModules(void)\n")
+ set(_chunk "${_chunk}{\n")
+ set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n")
+ set(_chunk "${_chunk}}\n")
+ set(_chunk "${_chunk}#endif /* !EXCLUDE_LOAD_ALL_FUNCTION */\n\n")
+
+ set(_chunk "${_chunk}#ifndef EXCLUDE_PY_INIT_WRAPPER\n")
+ set(_chunk "${_chunk}static void Py_Initialize_Wrapper()\n")
+ set(_chunk "${_chunk}{\n")
+ set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n")
+ set(_chunk "${_chunk} Py_Initialize();\n")
+ set(_chunk "${_chunk}}\n")
+ set(_chunk "${_chunk}#define Py_Initialize Py_Initialize_Wrapper\n")
+ set(_chunk "${_chunk}#endif /* !EXCLUDE_PY_INIT_WRAPPER */\n\n")
+
+ set(_chunk "${_chunk}#endif /* !${_header_name_upper} */\n")
+ file(APPEND ${generated_file_tmp} "${_chunk}")
+
+ # with configure_file() cmake complains that you may not use a file created
+ # using file(WRITE) as input file for configure_file()
+ execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${generated_file_tmp}" "${generated_file}"
+ OUTPUT_QUIET ERROR_QUIET)
+
+ set(_header_output_var ${_name})
+ if(_args_HEADER_OUTPUT_VAR)
+ set(_header_output_var ${_args_HEADER_OUTPUT_VAR})
+ endif()
+ set(${_header_output_var} ${generated_file} PARENT_SCOPE)
+
+ set(_include_dir_var ${_name}_INCLUDE_DIRS)
+ if(_args_INCLUDE_DIR_OUTPUT_VAR)
+ set(_include_dir_var ${_args_INCLUDE_DIR_OUTPUT_VAR})
+ endif()
+ set(${_include_dirs_var} ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
+endfunction()
+
diff --git a/cmake/UseCython.cmake b/cmake/UseCython.cmake
new file mode 100644
index 000000000..7ff4a276b
--- /dev/null
+++ b/cmake/UseCython.cmake
@@ -0,0 +1,403 @@
+#.rst
+# Define a function to create Cython modules.
+#
+# For more information on the Cython project, see http://cython.org/.
+# "Cython is a language that makes writing C extensions for the Python language
+# as easy as Python itself."
+#
+# This file defines a CMake function to build a Cython Python module.
+# To use it, first include this file.
+#
+# include(UseCython)
+#
+# The following functions are defined:
+#
+# add_cython_target(<Name> [<CythonInput>]
+# [EMBED_MAIN]
+# [C | CXX]
+# [PY2 | PY3]
+# [OUTPUT_VAR <OutputVar>])
+#
+# Create a custom rule to generate the source code for a Python extension module
+# using cython. ``<Name>`` is the name of the new target, and ``<CythonInput>``
+# is the path to a cython source file. Note that, despite the name, no new
+# targets are created by this function. Instead, see ``OUTPUT_VAR`` for
+# retrieving the path to the generated source for subsequent targets.
+#
+# If only ``<Name>`` is provided, and it ends in the ".pyx" extension, then it
+# is assumed to be the ``<CythonInput>``. The name of the input without the
+# extension is used as the target name. If only ``<Name>`` is provided, and it
+# does not end in the ".pyx" extension, then the ``<CythonInput>`` is assumed to
+# be ``<Name>.pyx``.
+#
+# The Cython include search path is amended with any entries found in the
+# ``INCLUDE_DIRECTORIES`` property of the directory containing the
+# ``<CythonInput>`` file. Use ``iunclude_directories`` to add to the Cython
+# include search path.
+#
+# Options:
+#
+# ``EMBED_MAIN``
+# Embed a main() function in the generated output (for stand-alone
+# applications that initialize their own Python runtime).
+#
+# ``C | CXX``
+# Force the generation of either a C or C++ file. By default, a C file is
+# generated, unless the C language is not enabled for the project; in this
+# case, a C++ file is generated by default.
+#
+# ``PY2 | PY3``
+# Force compilation using either Python-2 or Python-3 syntax and code
+# semantics. By default, Python-2 syntax and semantics are used if the major
+# version of Python found is 2. Otherwise, Python-3 syntax and sematics are
+# used.
+#
+# ``OUTPUT_VAR <OutputVar>``
+# Set the variable ``<OutputVar>`` in the parent scope to the path to the
+# generated source file. By default, ``<Name>`` is used as the output
+# variable name.
+#
+# Defined variables:
+#
+# ``<OutputVar>``
+# The path of the generated source file.
+#
+#
+# Example usage:
+#
+# .. code-block:: cmake
+#
+# find_package(Cython)
+#
+# # Note: In this case, either one of these arguments may be omitted; their
+# # value would have been inferred from that of the other.
+# add_cython_target(cy_code cy_code.pyx)
+#
+# add_library(cy_code MODULE ${cy_code})
+# target_link_libraries(cy_code ...)
+#
+# Cache variables that effect the behavior include:
+#
+# ``CYTHON_ANNOTATE``
+# whether to create an annotated .html file when compiling
+#
+# ``CYTHON_FLAGS``
+# additional flags to pass to the Cython compiler
+#
+# See also FindCython.cmake
+#
+#=============================================================================
+# Copyright 2011 Kitware, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#=============================================================================
+
+# Configuration options.
+set(CYTHON_ANNOTATE OFF
+ CACHE BOOL "Create an annotated .html file when compiling *.pyx.")
+
+set(CYTHON_FLAGS "" CACHE STRING
+ "Extra flags to the cython compiler.")
+mark_as_advanced(CYTHON_ANNOTATE CYTHON_FLAGS)
+
+find_package(PythonLibs REQUIRED)
+
+set(CYTHON_CXX_EXTENSION "cxx")
+set(CYTHON_C_EXTENSION "c")
+
+get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES)
+
+function(add_cython_target _name)
+ set(options EMBED_MAIN C CXX PY2 PY3)
+ set(options1 OUTPUT_VAR)
+ cmake_parse_arguments(_args "${options}" "${options1}" "" ${ARGN})
+
+ list(GET _args_UNPARSED_ARGUMENTS 0 _arg0)
+
+ # if provided, use _arg0 as the input file path
+ if(_arg0)
+ set(_source_file ${_arg0})
+
+ # otherwise, must determine source file from name, or vice versa
+ else()
+ get_filename_component(_name_ext "${_name}" EXT)
+
+ # if extension provided, _name is the source file
+ if(_name_ext)
+ set(_source_file ${_name})
+ get_filename_component(_name "${_source_file}" NAME_WE)
+
+ # otherwise, assume the source file is ${_name}.pyx
+ else()
+ set(_source_file ${_name}.pyx)
+ endif()
+ endif()
+
+ set(_embed_main FALSE)
+
+ if("C" IN_LIST languages)
+ set(_output_syntax "C")
+ elseif("CXX" IN_LIST languages)
+ set(_output_syntax "CXX")
+ else()
+ message(FATAL_ERROR "Either C or CXX must be enabled to use Cython")
+ endif()
+
+ if("${PYTHONLIBS_VERSION_STRING}" MATCHES "^2.")
+ set(_input_syntax "PY2")
+ else()
+ set(_input_syntax "PY3")
+ endif()
+
+ if(_args_EMBED_MAIN)
+ set(_embed_main TRUE)
+ endif()
+
+ if(_args_C)
+ set(_output_syntax "C")
+ endif()
+
+ if(_args_CXX)
+ set(_output_syntax "CXX")
+ endif()
+
+ if(_args_PY2)
+ set(_input_syntax "PY2")
+ endif()
+
+ if(_args_PY3)
+ set(_input_syntax "PY3")
+ endif()
+
+ set(embed_arg "")
+ if(_embed_main)
+ set(embed_arg "--embed")
+ endif()
+
+ set(cxx_arg "")
+ set(extension "c")
+ if(_output_syntax STREQUAL "CXX")
+ set(cxx_arg "--cplus")
+ set(extension "cxx")
+ endif()
+
+ set(py_version_arg "")
+ if(_input_syntax STREQUAL "PY2")
+ set(py_version_arg "-2")
+ elseif(_input_syntax STREQUAL "PY3")
+ set(py_version_arg "-3")
+ endif()
+
+ set(generated_file "${CMAKE_CURRENT_BINARY_DIR}/${_name}.${extension}")
+ set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE)
+
+ set(_output_var ${_name})
+ if(_args_OUTPUT_VAR)
+ set(_output_var ${_args_OUTPUT_VAR})
+ endif()
+ set(${_output_var} ${generated_file} PARENT_SCOPE)
+
+ file(RELATIVE_PATH generated_file_relative
+ ${CMAKE_BINARY_DIR} ${generated_file})
+
+ set(comment "Generating ${_output_syntax} source ${generated_file_relative}")
+ set(cython_include_directories "")
+ set(pxd_dependencies "")
+ set(c_header_dependencies "")
+
+ # Get the include directories.
+ get_source_file_property(pyx_location ${_source_file} LOCATION)
+ get_filename_component(pyx_path ${pyx_location} PATH)
+ get_directory_property(cmake_include_directories
+ DIRECTORY ${pyx_path}
+ INCLUDE_DIRECTORIES)
+ list(APPEND cython_include_directories ${cmake_include_directories})
+
+ # Determine dependencies.
+ # Add the pxd file with the same basename as the given pyx file.
+ get_filename_component(pyx_file_basename ${_source_file} NAME_WE)
+ unset(corresponding_pxd_file CACHE)
+ find_file(corresponding_pxd_file ${pyx_file_basename}.pxd
+ PATHS "${pyx_path}" ${cmake_include_directories}
+ NO_DEFAULT_PATH)
+ if(corresponding_pxd_file)
+ list(APPEND pxd_dependencies "${corresponding_pxd_file}")
+ endif()
+
+ # pxd files to check for additional dependencies
+ set(pxds_to_check "${_source_file}" "${pxd_dependencies}")
+ set(pxds_checked "")
+ set(number_pxds_to_check 1)
+ while(number_pxds_to_check GREATER 0)
+ foreach(pxd ${pxds_to_check})
+ list(APPEND pxds_checked "${pxd}")
+ list(REMOVE_ITEM pxds_to_check "${pxd}")
+
+ # look for C headers
+ file(STRINGS "${pxd}" extern_from_statements
+ REGEX "cdef[ ]+extern[ ]+from.*$")
+ foreach(statement ${extern_from_statements})
+ # Had trouble getting the quote in the regex
+ string(REGEX REPLACE
+ "cdef[ ]+extern[ ]+from[ ]+[\"]([^\"]+)[\"].*" "\\1"
+ header "${statement}")
+ unset(header_location CACHE)
+ find_file(header_location ${header} PATHS ${cmake_include_directories})
+ if(header_location)
+ list(FIND c_header_dependencies "${header_location}" header_idx)
+ if(${header_idx} LESS 0)
+ list(APPEND c_header_dependencies "${header_location}")
+ endif()
+ endif()
+ endforeach()
+
+ # check for pxd dependencies
+ # Look for cimport statements.
+ set(module_dependencies "")
+ file(STRINGS "${pxd}" cimport_statements REGEX cimport)
+ foreach(statement ${cimport_statements})
+ if(${statement} MATCHES from)
+ string(REGEX REPLACE
+ "from[ ]+([^ ]+).*" "\\1"
+ module "${statement}")
+ else()
+ string(REGEX REPLACE
+ "cimport[ ]+([^ ]+).*" "\\1"
+ module "${statement}")
+ endif()
+ list(APPEND module_dependencies ${module})
+ endforeach()
+
+ # check for pxi dependencies
+ # Look for include statements.
+ set(include_dependencies "")
+ file(STRINGS "${pxd}" include_statements REGEX include)
+ foreach(statement ${include_statements})
+ string(REGEX REPLACE
+ "include[ ]+[\"]([^\"]+)[\"].*" "\\1"
+ module "${statement}")
+ list(APPEND include_dependencies ${module})
+ endforeach()
+
+ list(REMOVE_DUPLICATES module_dependencies)
+ list(REMOVE_DUPLICATES include_dependencies)
+
+ # Add modules to the files to check, if appropriate.
+ foreach(module ${module_dependencies})
+ unset(pxd_location CACHE)
+ find_file(pxd_location ${module}.pxd
+ PATHS "${pyx_path}" ${cmake_include_directories}
+ NO_DEFAULT_PATH)
+ if(pxd_location)
+ list(FIND pxds_checked ${pxd_location} pxd_idx)
+ if(${pxd_idx} LESS 0)
+ list(FIND pxds_to_check ${pxd_location} pxd_idx)
+ if(${pxd_idx} LESS 0)
+ list(APPEND pxds_to_check ${pxd_location})
+ list(APPEND pxd_dependencies ${pxd_location})
+ endif() # if it is not already going to be checked
+ endif() # if it has not already been checked
+ endif() # if pxd file can be found
+ endforeach() # for each module dependency discovered
+
+ # Add includes to the files to check, if appropriate.
+ foreach(_include ${include_dependencies})
+ unset(pxi_location CACHE)
+ find_file(pxi_location ${_include}
+ PATHS "${pyx_path}" ${cmake_include_directories}
+ NO_DEFAULT_PATH)
+ if(pxi_location)
+ list(FIND pxds_checked ${pxi_location} pxd_idx)
+ if(${pxd_idx} LESS 0)
+ list(FIND pxds_to_check ${pxi_location} pxd_idx)
+ if(${pxd_idx} LESS 0)
+ list(APPEND pxds_to_check ${pxi_location})
+ list(APPEND pxd_dependencies ${pxi_location})
+ endif() # if it is not already going to be checked
+ endif() # if it has not already been checked
+ endif() # if include file can be found
+ endforeach() # for each include dependency discovered
+ endforeach() # for each include file to check
+
+ list(LENGTH pxds_to_check number_pxds_to_check)
+ endwhile()
+
+ # Set additional flags.
+ set(annotate_arg "")
+ if(CYTHON_ANNOTATE)
+ set(annotate_arg "--annotate")
+ endif()
+
+ set(no_docstrings_arg "")
+ if(CMAKE_BUILD_TYPE STREQUAL "Release" OR
+ CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
+ set(no_docstrings_arg "--no-docstrings")
+ endif()
+
+ set(cython_debug_arg "")
+ set(embed_pos_arg "")
+ set(line_directives_arg "")
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR
+ CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
+ set(cython_debug_arg "--gdb")
+ set(embed_pos_arg "--embed-positions")
+ set(line_directives_arg "--line-directives")
+ endif()
+
+ # Include directory arguments.
+ list(REMOVE_DUPLICATES cython_include_directories)
+ set(include_directory_arg "")
+ foreach(_include_dir ${cython_include_directories})
+ set(include_directory_arg
+ ${include_directory_arg} "--include-dir" "${_include_dir}")
+ endforeach()
+
+ list(REMOVE_DUPLICATES pxd_dependencies)
+ list(REMOVE_DUPLICATES c_header_dependencies)
+
+ # Add the command to run the compiler.
+ add_custom_command(OUTPUT ${generated_file}
+ COMMAND ${CYTHON_EXECUTABLE}
+ ARGS ${cxx_arg} ${include_directory_arg} ${py_version_arg}
+ ${embed_arg} ${annotate_arg} ${no_docstrings_arg}
+ ${cython_debug_arg} ${embed_pos_arg}
+ ${line_directives_arg} ${CYTHON_FLAGS} ${pyx_location}
+ --output-file ${generated_file}
+ DEPENDS ${_source_file}
+ ${pxd_dependencies}
+ IMPLICIT_DEPENDS ${_output_syntax}
+ ${c_header_dependencies}
+ COMMENT ${comment})
+
+ # NOTE(opadron): I thought about making a proper target, but after trying it
+ # out, I decided that it would be far too convenient to use the same name as
+ # the target for the extension module (e.g.: for single-file modules):
+ #
+ # ...
+ # add_cython_target(_module.pyx)
+ # add_library(_module ${_module})
+ # ...
+ #
+ # The above example would not be possible since the "_module" target name
+ # would already be taken by the cython target. Since I can't think of a
+ # reason why someone would need the custom target instead of just using the
+ # generated file directly, I decided to leave this commented out.
+ #
+ # add_custom_target(${_name} DEPENDS ${generated_file})
+
+ # Remove their visibility to the user.
+ set(corresponding_pxd_file "" CACHE INTERNAL "")
+ set(header_location "" CACHE INTERNAL "")
+ set(pxd_location "" CACHE INTERNAL "")
+endfunction()
+
diff --git a/cmake/targetLinkLibrariesWithDynamicLookup.cmake b/cmake/targetLinkLibrariesWithDynamicLookup.cmake
new file mode 100644
index 000000000..60a0a1956
--- /dev/null
+++ b/cmake/targetLinkLibrariesWithDynamicLookup.cmake
@@ -0,0 +1,478 @@
+#
+# - This module provides the function
+# target_link_libraries_with_dynamic_lookup which can be used to
+# "weakly" link a loadable module.
+#
+# Link a library to a target such that the symbols are resolved at
+# run-time not link-time. This should be used when compiling a
+# loadable module when the symbols should be resolve from the run-time
+# environment where the module is loaded, and not a specific system
+# library.
+#
+# Specifically, for OSX it uses undefined dynamic_lookup. This is
+# similar to using "-shared" on Linux where undefined symbols are
+# ignored.
+#
+# Additionally, the linker is checked to see if it supports undefined
+# symbols when linking a shared library. If it does then the library
+# is not linked when specified with this function.
+#
+# http://blog.tim-smith.us/2015/09/python-extension-modules-os-x/
+#
+#
+# The following functions are defined:
+#
+# _get_target_type(<ResultVar> <Target>)
+#
+# **INTERNAL** Shorthand for querying an abbreviated version of the target type
+# of the given ``<Target>``. ``<ResultVar>`` is set to "STATIC" for a
+# STATIC_LIBRARY, "SHARED" for a SHARED_LIBRARY, "MODULE" for a MODULE_LIBRARY,
+# and "EXE" for an EXECUTABLE.
+#
+# Defined variables:
+#
+# ``<ResultVar>``
+# The abbreviated version of the ``<Target>``'s type.
+#
+#
+# _test_weak_link_project(<TargetType>
+# <LibType>
+# <ResultVar>
+# <LinkFlagsVar>)
+#
+# **INTERNAL** Attempt to compile and run a test project where a target of type
+# ``<TargetType>`` is weakly-linked against a dependency of type ``<LibType>``.
+# ``<TargetType>`` can be one of "STATIC", "SHARED", "MODULE", or "EXE".
+# ``<LibType>`` can be one of "STATIC", "SHARED", or "MODULE".
+#
+# Defined variables:
+#
+# ``<ResultVar>``
+# Whether the current C toolchain can produce a working target binary of type
+# ``<TargetType>`` that is weakly-linked against a dependency target of type
+# ``<LibType>``.
+#
+# ``<LinkFlagsVar>``
+# List of flags to add to the linker command to produce a working target
+# binary of type ``<TargetType>`` that is weakly-linked against a dependency
+# target of type ``<LibType>``.
+#
+#
+# check_dynamic_lookup(<TargetType>
+# <LibType>
+# <ResultVar>
+# <LinkFlagsVar>)
+#
+# Check if the linker requires a command line flag to allow leaving symbols
+# unresolved when producing a target of type ``<TargetType>`` that is
+# weakly-linked against a dependency of type ``<LibType>``. ``<TargetType>``
+# can be one of "STATIC", "SHARED", "MODULE", or "EXE". ``<LibType>`` can be
+# one of "STATIC", "SHARED", or "MODULE". The result is cached between
+# invocations and recomputed only when the value of CMake's linker flag list
+# changes; ``CMAKE_STATIC_LINKER_FLAGS`` if ``<TargetType>`` is "STATIC", and
+# ``CMAKE_SHARED_LINKER_FLAGS`` otherwise.
+#
+#
+# Defined variables:
+#
+# ``<ResultVar>``
+# Whether the current C toolchain supports weak-linking for target binaries of
+# type ``<TargetType>`` that are weakly-linked against a dependency target of
+# type ``<LibType>``.
+#
+# ``<LinkFlagsVar>``
+# List of flags to add to the linker command to produce a working target
+# binary of type ``<TargetType>`` that is weakly-linked against a dependency
+# target of type ``<LibType>``.
+#
+# ``HAS_DYNAMIC_LOOKUP_<TargetType>_<LibType>``
+# Cached, global alias for ``<ResultVar>``
+#
+# ``DYNAMIC_LOOKUP_FLAGS_<TargetType>_<LibType>``
+# Cached, global alias for ``<LinkFlagsVar>``
+#
+#
+# target_link_libraries_with_dynamic_lookup(<Target> [<Libraries>])
+#
+# Like proper linking, except that the given ``<Libraries>`` are not necessarily
+# linked. Instead, the ``<Target>`` is produced in a manner that allows for
+# symbols unresolved within it to be resolved at runtime, presumably by the
+# given ``<Libraries>``. If such a target can be produced, the provided
+# ``<Libraries>`` are not actually linked. On platforms that do not support
+# weak-linking, this function works just like ``target_link_libraries``.
+
+function(_get_target_type result_var target)
+ set(target_type "SHARED_LIBRARY")
+ if(TARGET ${target})
+ get_property(target_type TARGET ${target} PROPERTY TYPE)
+ endif()
+
+ set(result "STATIC")
+
+ if(target_type STREQUAL "STATIC_LIBRARY")
+ set(result "STATIC")
+ endif()
+
+ if(target_type STREQUAL "SHARED_LIBRARY")
+ set(result "SHARED")
+ endif()
+
+ if(target_type STREQUAL "MODULE_LIBRARY")
+ set(result "MODULE")
+ endif()
+
+ if(target_type STREQUAL "EXECUTABLE")
+ set(result "EXE")
+ endif()
+
+ set(${result_var} ${result} PARENT_SCOPE)
+endfunction()
+
+
+function(_test_weak_link_project
+ target_type
+ lib_type
+ can_weak_link_var
+ project_name)
+
+ set(gnu_ld_ignore "-Wl,--unresolved-symbols=ignore-all")
+ set(osx_dynamic_lookup "-undefined dynamic_lookup")
+ set(no_flag "")
+
+ foreach(link_flag_spec gnu_ld_ignore osx_dynamic_lookup no_flag)
+ set(link_flag "${${link_flag_spec}}")
+
+ set(test_project_dir "${PROJECT_BINARY_DIR}/CMakeTmp")
+ set(test_project_dir "${test_project_dir}/${project_name}")
+ set(test_project_dir "${test_project_dir}/${link_flag_spec}")
+ set(test_project_dir "${test_project_dir}/${target_type}")
+ set(test_project_dir "${test_project_dir}/${lib_type}")
+
+ set(test_project_src_dir "${test_project_dir}/src")
+ set(test_project_bin_dir "${test_project_dir}/build")
+
+ file(MAKE_DIRECTORY ${test_project_src_dir})
+ file(MAKE_DIRECTORY ${test_project_bin_dir})
+
+ set(mod_type "STATIC")
+ set(link_mod_lib TRUE)
+ set(link_exe_lib TRUE)
+ set(link_exe_mod FALSE)
+
+ if("${target_type}" STREQUAL "EXE")
+ set(link_exe_lib FALSE)
+ set(link_exe_mod TRUE)
+ else()
+ set(mod_type "${target_type}")
+ endif()
+
+ if("${mod_type}" STREQUAL "MODULE")
+ set(link_mod_lib FALSE)
+ endif()
+
+
+ file(WRITE "${test_project_src_dir}/CMakeLists.txt" "
+ cmake_minimum_required(VERSION ${CMAKE_VERSION})
+ project(${project_name} C)
+
+ include_directories(${test_project_src_dir})
+
+ add_library(number ${lib_type} number.c)
+ add_library(counter ${mod_type} counter.c)
+ ")
+
+ if("${mod_type}" STREQUAL "MODULE")
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ set_target_properties(counter PROPERTIES PREFIX \"\")
+ ")
+ endif()
+
+ if(link_mod_lib)
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ target_link_libraries(counter number)
+ ")
+ elseif(NOT link_flag STREQUAL "")
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ set_target_properties(counter PROPERTIES LINK_FLAGS \"${link_flag}\")
+ ")
+ endif()
+
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ add_executable(main main.c)
+ ")
+
+ if(link_exe_lib)
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ target_link_libraries(main number)
+ ")
+ elseif(NOT link_flag STREQUAL "")
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ target_link_libraries(main \"${link_flag}\")
+ ")
+ endif()
+
+ if(link_exe_mod)
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ target_link_libraries(main counter)
+ ")
+ else()
+ file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
+ target_link_libraries(main \"${CMAKE_DL_LIBS}\")
+ ")
+ endif()
+
+ file(WRITE "${test_project_src_dir}/number.c" "
+ #include <number.h>
+
+ static int _number;
+ void set_number(int number) { _number = number; }
+ int get_number() { return _number; }
+ ")
+
+ file(WRITE "${test_project_src_dir}/number.h" "
+ #ifndef _NUMBER_H
+ #define _NUMBER_H
+ extern void set_number(int);
+ extern int get_number(void);
+ #endif
+ ")
+
+ file(WRITE "${test_project_src_dir}/counter.c" "
+ #include <number.h>
+ int count() {
+ int result = get_number();
+ set_number(result + 1);
+ return result;
+ }
+ ")
+
+ file(WRITE "${test_project_src_dir}/counter.h" "
+ #ifndef _COUNTER_H
+ #define _COUNTER_H
+ extern int count(void);
+ #endif
+ ")
+
+ file(WRITE "${test_project_src_dir}/main.c" "
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <number.h>
+ ")
+
+ if(NOT link_exe_mod)
+ file(APPEND "${test_project_src_dir}/main.c" "
+ #include <dlfcn.h>
+ ")
+ endif()
+
+ file(APPEND "${test_project_src_dir}/main.c" "
+ int my_count() {
+ int result = get_number();
+ set_number(result + 1);
+ return result;
+ }
+
+ int main(int argc, char **argv) {
+ int result;
+ ")
+
+ if(NOT link_exe_mod)
+ file(APPEND "${test_project_src_dir}/main.c" "
+ void *counter_module;
+ int (*count)(void);
+
+ counter_module = dlopen(\"./counter.so\", RTLD_LAZY | RTLD_GLOBAL);
+ if(!counter_module) goto error;
+
+ count = dlsym(counter_module, \"count\");
+ if(!count) goto error;
+ ")
+ endif()
+
+ file(APPEND "${test_project_src_dir}/main.c" "
+ result = count() != 0 ? EXIT_FAILURE :
+ my_count() != 1 ? EXIT_FAILURE :
+ my_count() != 2 ? EXIT_FAILURE :
+ count() != 3 ? EXIT_FAILURE :
+ count() != 4 ? EXIT_FAILURE :
+ count() != 5 ? EXIT_FAILURE :
+ my_count() != 6 ? EXIT_FAILURE : EXIT_SUCCESS;
+ ")
+
+ if(NOT link_exe_mod)
+ file(APPEND "${test_project_src_dir}/main.c" "
+ goto done;
+ error:
+ fprintf(stderr, \"Error occured:\\n %s\\n\", dlerror());
+ result = 1;
+
+ done:
+ if(counter_module) dlclose(counter_module);
+ ")
+ endif()
+
+ file(APPEND "${test_project_src_dir}/main.c" "
+ return result;
+ }
+ ")
+
+ set(_rpath_arg)
+ if(APPLE AND ${CMAKE_VERSION} VERSION_GREATER 2.8.11)
+ set(_rpath_arg "-DCMAKE_MACOSX_RPATH='${CMAKE_MACOSX_RPATH}'")
+ endif()
+
+ try_compile(project_compiles
+ "${test_project_bin_dir}"
+ "${test_project_src_dir}"
+ "${project_name}"
+ CMAKE_FLAGS
+ "-DCMAKE_SHARED_LINKER_FLAGS='${CMAKE_SHARED_LINKER_FLAGS}'"
+ ${_rpath_arg}
+ OUTPUT_VARIABLE compile_output)
+
+ set(project_works 1)
+ set(run_output)
+
+ if(project_compiles)
+ execute_process(COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR}
+ "${test_project_bin_dir}/main"
+ WORKING_DIRECTORY "${test_project_bin_dir}"
+ RESULT_VARIABLE project_works
+ OUTPUT_VARIABLE run_output
+ ERROR_VARIABLE run_output)
+ endif()
+
+ set(test_description
+ "Weak Link ${target_type} -> ${lib_type} (${link_flag_spec})")
+
+ if(project_works EQUAL 0)
+ set(project_works TRUE)
+ message(STATUS "Performing Test ${test_description} - Success")
+ else()
+ set(project_works FALSE)
+ message(STATUS "Performing Test ${test_description} - Failed")
+ file(APPEND ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeError.log
+ "Performing Test ${test_description} failed with the "
+ "following output:\n"
+ "BUILD\n-----\n${compile_output}\nRUN\n---\n${run_output}\n")
+ endif()
+
+ set(${can_weak_link_var} ${project_works} PARENT_SCOPE)
+ if(project_works)
+ set(${project_name} ${link_flag} PARENT_SCOPE)
+ break()
+ endif()
+ endforeach()
+endfunction()
+
+
+function(check_dynamic_lookup
+ target_type
+ lib_type
+ has_dynamic_lookup_var
+ link_flags_var)
+
+ # hash the CMAKE_FLAGS passed and check cache to know if we need to rerun
+ if("${target_type}" STREQUAL "STATIC")
+ string(MD5 cmake_flags_hash "${CMAKE_STATIC_LINKER_FLAGS}")
+ else()
+ string(MD5 cmake_flags_hash "${CMAKE_SHARED_LINKER_FLAGS}")
+ endif()
+
+ set(cache_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}")
+ set(cache_hash_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}_hash")
+ set(result_var "DYNAMIC_LOOKUP_FLAGS_${target_type}_${lib_type}")
+
+ if( NOT DEFINED ${cache_hash_var}
+ OR NOT "${${cache_hash_var}}" STREQUAL "${cmake_flags_hash}")
+ unset(${cache_var} CACHE)
+ endif()
+
+ if(NOT DEFINED ${cache_var})
+ set(skip_test FALSE)
+
+ if(NOT CMAKE_CROSSCOMPILING)
+ set(skip_test TRUE)
+ elseif(CMAKE_CROSSCOMPILING AND CMAKE_CROSSCOMPILING_EMULATOR)
+ set(skip_test TRUE)
+ endif()
+
+ if(skip_test)
+ set(has_dynamic_lookup FALSE)
+ set(link_flags)
+ else()
+ _test_weak_link_project(${target_type}
+ ${lib_type}
+ has_dynamic_lookup
+ link_flags)
+ endif()
+
+ set(caveat " (when linking ${target_type} against ${lib_type})")
+
+ set(${cache_var} "${has_dynamic_lookup}"
+ CACHE BOOL
+ "linker supports dynamic lookup for undefined symbols${caveat}")
+
+ set(${result_var} "${link_flags}"
+ CACHE BOOL
+ "linker flags for dynamic lookup${caveat}")
+
+ set(${cache_hash_var} "${cmake_flags_hash}"
+ CACHE INTERNAL "hashed flags for ${cache_var} check")
+ endif()
+
+ set(${has_dynamic_lookup_var} "${${cache_var}}" PARENT_SCOPE)
+ set(${link_flags_var} "${${result_var}}" PARENT_SCOPE)
+endfunction()
+
+
+function(target_link_libraries_with_dynamic_lookup target)
+ _get_target_type(target_type ${target})
+
+ set(link_props)
+ set(link_items)
+ set(link_libs)
+
+ foreach(lib ${ARGN})
+ _get_target_type(lib_type ${lib})
+ check_dynamic_lookup(${target_type}
+ ${lib_type}
+ has_dynamic_lookup
+ dynamic_lookup_flags)
+
+ if(has_dynamic_lookup)
+ if(dynamic_lookup_flags)
+ if("${target_type}" STREQUAL "EXE")
+ list(APPEND link_items "${dynamic_lookup_flags}")
+ else()
+ list(APPEND link_props "${dynamic_lookup_flags}")
+ endif()
+ endif()
+ else()
+ list(APPEND link_libs "${lib}")
+ endif()
+ endforeach()
+
+ if(link_props)
+ list(REMOVE_DUPLICATES link_props)
+ endif()
+
+ if(link_items)
+ list(REMOVE_DUPLICATES link_items)
+ endif()
+
+ if(link_libs)
+ list(REMOVE_DUPLICATES link_libs)
+ endif()
+
+ if(link_props)
+ set_target_properties(${target}
+ PROPERTIES LINK_FLAGS "${link_props}")
+ endif()
+
+ set(links "${link_items}" "${link_libs}")
+ if(links)
+ target_link_libraries(${target} "${links}")
+ endif()
+endfunction()
+
diff --git a/configure.sh b/configure.sh
index 5763fa7ce..611ac54ad 100755
--- a/configure.sh
+++ b/configure.sh
@@ -44,6 +44,7 @@ The following flags enable optional features (disable with --no-<option name>).
--unit-testing support for unit testing
--python2 prefer using Python 2 (also for Python bindings)
--python3 prefer using Python 3 (also for Python bindings)
+ --python-bindings build Python bindings based on new C++ API
--asan build with ASan instrumentation
--ubsan build with UBSan instrumentation
--tsan build with TSan instrumentation
@@ -141,6 +142,7 @@ tracing=default
unit_testing=default
python2=default
python3=default
+python_bindings=default
valgrind=default
profiling=default
readline=default
@@ -279,6 +281,9 @@ do
--python3) python3=ON;;
--no-python3) python3=OFF;;
+ --python-bindings) python_bindings=ON;;
+ --no-python-bindings) python_bindings=OFF;;
+
--valgrind) valgrind=ON;;
--no-valgrind) valgrind=OFF;;
@@ -404,6 +409,8 @@ cmake_opts=""
&& cmake_opts="$cmake_opts -DUSE_PYTHON2=$python2"
[ $python3 != default ] \
&& cmake_opts="$cmake_opts -DUSE_PYTHON3=$python3"
+[ $python_bindings != default ] \
+ && cmake_opts="$cmake_opts -DBUILD_BINDINGS_PYTHON=$python_bindings"
[ $valgrind != default ] \
&& cmake_opts="$cmake_opts -DENABLE_VALGRIND=$valgrind"
[ $profiling != default ] \
@@ -428,9 +435,9 @@ cmake_opts=""
&& cmake_opts="$cmake_opts -DUSE_SYMFPU=$symfpu"
[ $language_bindings_java != default ] \
- && cmake_opts="$cmake_opts -DBUILD_BINDINGS_JAVA=$language_bindings_java"
+ && cmake_opts="$cmake_opts -DBUILD_SWIG_BINDINGS_JAVA=$language_bindings_java"
[ $language_bindings_python != default ] \
- && cmake_opts="$cmake_opts -DBUILD_BINDINGS_PYTHON=$language_bindings_python"
+ && cmake_opts="$cmake_opts -DBUILD_SWIG_BINDINGS_PYTHON=$language_bindings_python"
[ "$abc_dir" != default ] \
&& cmake_opts="$cmake_opts -DABC_DIR=$abc_dir"
diff --git a/examples/api/python/bitvectors.py b/examples/api/python/bitvectors.py
new file mode 100755
index 000000000..773974cc7
--- /dev/null
+++ b/examples/api/python/bitvectors.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+#####################
+#! \file bitvectors.py
+ ## \verbatim
+ ## Top contributors (to current version):
+ ## Makai Mann
+ ## This file is part of the CVC4 project.
+ ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS
+ ## in the top-level source directory) and their institutional affiliations.
+ ## All rights reserved. See the file COPYING in the top-level source
+ ## directory for licensing information.\endverbatim
+ ##
+ ## \brief A simple demonstration of the solving capabilities of the CVC4
+ ## bit-vector solver through the Python API. This is a direct translation
+ ## of bitvectors-new.cpp.
+import pycvc4
+from pycvc4 import kinds
+
+if __name__ == "__main__":
+ slv = pycvc4.Solver()
+ slv.setLogic("QF_BV") # Set the logic
+ # The following example has been adapted from the book A Hacker's Delight by
+ # Henry S. Warren.
+ #
+ # Given a variable x that can only have two values, a or b. We want to
+ # assign to x a value other than the current one. The straightforward code
+ # to do that is:
+ #
+ #(0) if (x == a ) x = b;
+ # else x = a;
+ #
+ # Two more efficient yet equivalent methods are:
+ #
+ #(1) x = a xor b xor x;
+ #
+ #(2) x = a + b - x;
+ #
+ # We will use CVC4 to prove that the three pieces of code above are all
+ # equivalent by encoding the problem in the bit-vector theory.
+
+ # Creating a bit-vector type of width 32
+ bitvector32 = slv.mkBitVectorSort(32)
+
+ # Variables
+ x = slv.mkConst(bitvector32, "x")
+ a = slv.mkConst(bitvector32, "a")
+ b = slv.mkConst(bitvector32, "b")
+
+ # First encode the assumption that x must be equal to a or b
+ x_eq_a = slv.mkTerm(kinds.Equal, x, a)
+ x_eq_b = slv.mkTerm(kinds.Equal, x, b)
+ assumption = slv.mkTerm(kinds.Or, x_eq_a, x_eq_b)
+
+ # Assert the assumption
+ slv.assertFormula(assumption)
+
+ # Introduce a new variable for the new value of x after assignment.
+ # x after executing code (0)
+ new_x = slv.mkConst(bitvector32, "new_x")
+ # x after executing code (1) or (2)
+ new_x_ = slv.mkConst(bitvector32, "new_x_")
+
+ # Encoding code (0)
+ # new_x = x == a ? b : a
+ ite = slv.mkTerm(kinds.Ite, x_eq_a, b, a)
+ assignment0 = slv.mkTerm(kinds.Equal, new_x, ite)
+
+ # Assert the encoding of code (0)
+ print("Asserting {} to CVC4".format(assignment0))
+ slv.assertFormula(assignment0)
+ print("Pushing a new context.")
+ slv.push()
+
+ # Encoding code (1)
+ # new_x_ = a xor b xor x
+ a_xor_b_xor_x = slv.mkTerm(kinds.BVXor, a, b, x)
+ assignment1 = slv.mkTerm(kinds.Equal, new_x_, a_xor_b_xor_x)
+
+ # Assert encoding to CVC4 in current context
+ print("Asserting {} to CVC4".format(assignment1))
+ slv.assertFormula(assignment1)
+ new_x_eq_new_x_ = slv.mkTerm(kinds.Equal, new_x, new_x_)
+
+ print("Checking validity assuming:", new_x_eq_new_x_)
+ print("Expect valid.")
+ print("CVC4:", slv.checkValidAssuming(new_x_eq_new_x_))
+ print("Popping context.")
+ slv.pop()
+
+ # Encoding code (2)
+ # new_x_ = a + b - x
+ a_plus_b = slv.mkTerm(kinds.BVPlus, a, b)
+ a_plus_b_minus_x = slv.mkTerm(kinds.BVSub, a_plus_b, x)
+ assignment2 = slv.mkTerm(kinds.Equal, new_x_, a_plus_b_minus_x)
+
+ # Assert encoding to CVC4 in current context
+ print("Asserting {} to CVC4".format(assignment2))
+ slv.assertFormula(assignment2)
+
+ print("Checking validity assuming:", new_x_eq_new_x_)
+ print("Expect valid.")
+ print("CVC4:", slv.checkValidAssuming(new_x_eq_new_x_))
+
+
+ x_neq_x = slv.mkTerm(kinds.Equal, x, x).notTerm()
+ v = [new_x_eq_new_x_, x_neq_x]
+ print("Check Validity Assuming: ", v)
+ print("Expect invalid.")
+ print("CVC4:", slv.checkValidAssuming(v))
+
+ # Assert that a is odd
+ extract_op = slv.mkOp(kinds.BVExtract, 0, 0)
+ lsb_of_a = slv.mkTerm(extract_op, a)
+ print("Sort of {} is {}".format(lsb_of_a, lsb_of_a.getSort()))
+ a_odd = slv.mkTerm(kinds.Equal, lsb_of_a, slv.mkBitVector(1, 1))
+ print("Assert", a_odd)
+ print("Check satisifiability")
+ slv.assertFormula(a_odd)
+ print("Expect sat")
+ print("CVC4:", slv.checkSat())
diff --git a/examples/api/python/bitvectors_and_arrays.py b/examples/api/python/bitvectors_and_arrays.py
new file mode 100755
index 000000000..7e3d8f478
--- /dev/null
+++ b/examples/api/python/bitvectors_and_arrays.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+
+#####################
+#! \file bitvectors_and_arrays.py
+ ## \verbatim
+ ## Top contributors (to current version):
+ ## Makai Mann
+ ## This file is part of the CVC4 project.
+ ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS
+ ## in the top-level source directory) and their institutional affiliations.
+ ## All rights reserved. See the file COPYING in the top-level source
+ ## directory for licensing information.\endverbatim
+ ##
+ ## \brief A simple demonstration of the solving capabilities of the CVC4
+ ## bit-vector and array solvers through the Python API. This is a direct
+ ## translation of bitvectors_and_arrays-new.cpp.
+import pycvc4
+from pycvc4 import kinds
+
+import math
+
+if __name__ == "__main__":
+ slv = pycvc4.Solver()
+ slv.setOption("produce-models", "true")
+ slv.setOption("output-language", "smtlib")
+ slv.setLogic("QF_AUFBV")
+
+ # Consider the following code (where size is some previously defined constant):
+ #
+ #
+ # Assert (current_array[0] > 0);
+ # for (unsigned i = 1; i < k; ++i) {
+ # current_array[i] = 2 * current_array[i - 1];
+ # Assert (current_array[i-1] < current_array[i]);
+ # }
+ #
+ # We want to check whether the assertion in the body of the for loop holds
+ # throughout the loop.
+
+ # Setting up the problem parameters
+ k = 4
+ index_size = int(math.ceil(math.log(k, 2)))
+
+ # Sorts
+ elementSort = slv.mkBitVectorSort(32)
+ indexSort = slv.mkBitVectorSort(index_size)
+ arraySort = slv.mkArraySort(indexSort, elementSort)
+
+ # Variables
+ current_array = slv.mkConst(arraySort, "current_array")
+
+ # Making a bit-vector constant
+ zero = slv.mkBitVector(index_size, 0)
+
+ # Test making a constant array
+ constarr0 = slv.mkConstArray(arraySort, slv.mkBitVector(32, 0))
+
+ # Asserting that current_array[0] > 0
+ current_array0 = slv.mkTerm(kinds.Select, current_array, zero)
+ current_array0_gt_0 = slv.mkTerm(kinds.BVSgt,
+ current_array0,
+ slv.mkBitVector(32, 0))
+ slv.assertFormula(current_array0_gt_0)
+
+ # Building the assertions in the loop unrolling
+ index = slv.mkBitVector(index_size, 0)
+ old_current = slv.mkTerm(kinds.Select, current_array, index)
+ two = slv.mkBitVector(32, 2)
+
+ assertions = []
+ for i in range(1, k):
+ index = slv.mkBitVector(index_size, i)
+ new_current = slv.mkTerm(kinds.BVMult, two, old_current)
+ # current[i] = 2*current[i-1]
+ current_array = slv.mkTerm(kinds.Store, current_array, index, new_current)
+ # current[i-1] < current[i]
+ current_slt_new_current = slv.mkTerm(kinds.BVSlt, old_current, new_current)
+ assertions.append(current_slt_new_current)
+ old_current = slv.mkTerm(kinds.Select, current_array, index)
+
+ query = slv.mkTerm(kinds.Not, slv.mkTerm(kinds.And, assertions))
+
+ print("Asserting {} to CVC4".format(query))
+ slv.assertFormula(query)
+ print("Expect sat.")
+ print("CVC4:", slv.checkSatAssuming(slv.mkTrue()))
+
+ # Getting the model
+ print("The satisfying model is: ")
+ print(" current_array =", slv.getValue(current_array))
+ print(" current_array[0]", slv.getValue(current_array0))
diff --git a/examples/api/python/combination.py b/examples/api/python/combination.py
new file mode 100755
index 000000000..1b488d7d5
--- /dev/null
+++ b/examples/api/python/combination.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+
+#####################
+#! \file combination.py
+ ## \verbatim
+ ## Top contributors (to current version):
+ ## Makai Mann
+ ## This file is part of the CVC4 project.
+ ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS
+ ## in the top-level source directory) and their institutional affiliations.
+ ## All rights reserved. See the file COPYING in the top-level source
+ ## directory for licensing information.\endverbatim
+ ##
+ ## \brief A simple demonstration of the solving capabilities of the CVC4
+ ## combination solver through the Python API. This is a direct translation
+ ## of combination-new.cpp.
+import pycvc4
+from pycvc4 import kinds
+
+def prefixPrintGetValue(slv, t, level=0):
+ print("slv.getValue({}): {}".format(t, slv.getValue(t)))
+ for c in t:
+ prefixPrintGetValue(slv, c, level + 1)
+
+if __name__ == "__main__":
+ slv = pycvc4.Solver()
+ slv.setOption("produce-models", "true") # Produce Models
+ slv.setOption("output-language", "cvc4") # Set the output-language to CVC's
+ slv.setOption("default-dag-thresh", "0") # Disable dagifying the output
+ slv.setOption("output-language", "smt2") # use smt-lib v2 as output language
+ slv.setLogic("QF_UFLIRA")
+
+ # Sorts
+ u = slv.mkUninterpretedSort("u")
+ integer = slv.getIntegerSort()
+ boolean = slv.getBooleanSort()
+ uToInt = slv.mkFunctionSort(u, integer)
+ intPred = slv.mkFunctionSort(integer, boolean)
+
+ # Variables
+ x = slv.mkConst(u, "x")
+ y = slv.mkConst(u, "y")
+
+ # Functions
+ f = slv.mkConst(uToInt, "f")
+ p = slv.mkConst(intPred, "p")
+
+ # Constants
+ zero = slv.mkReal(0)
+ one = slv.mkReal(1)
+
+ # Terms
+ f_x = slv.mkTerm(kinds.ApplyUf, f, x)
+ f_y = slv.mkTerm(kinds.ApplyUf, f, y)
+ sum_ = slv.mkTerm(kinds.Plus, f_x, f_y)
+ p_0 = slv.mkTerm(kinds.ApplyUf, p, zero)
+ p_f_y = slv.mkTerm(kinds.ApplyUf, p, f_y)
+
+ # Construct the assertions
+ assertions = slv.mkTerm(kinds.And,
+ [
+ slv.mkTerm(kinds.Leq, zero, f_x), # 0 <= f(x)
+ slv.mkTerm(kinds.Leq, zero, f_y), # 0 <= f(y)
+ slv.mkTerm(kinds.Leq, sum_, one), # f(x) + f(y) <= 1
+ p_0.notTerm(), # not p(0)
+ p_f_y # p(f(y))
+ ])
+
+ slv.assertFormula(assertions)
+
+ print("Given the following assertions:", assertions, "\n")
+ print("Prove x /= y is valid.\nCVC4: ",
+ slv.checkValidAssuming(slv.mkTerm(kinds.Distinct, x, y)), "\n")
+
+ print("Call checkSat to show that the assertions are satisfiable")
+ print("CVC4:", slv.checkSat(), "\n")
+
+ print("Call slv.getValue(...) on terms of interest")
+ print("slv.getValue({}): {}".format(f_x, slv.getValue(f_x)))
+ print("slv.getValue({}): {}".format(f_y, slv.getValue(f_y)))
+ print("slv.getValue({}): {}".format(sum_, slv.getValue(sum_)))
+ print("slv.getValue({}): {}".format(p_0, slv.getValue(p_0)))
+ print("slv.getValue({}): {}".format(p_f_y, slv.getValue(p_f_y)), "\n")
+
+ print("Alternatively, iterate over assertions and call"
+ " slv.getValue(...) on all terms")
+ prefixPrintGetValue(slv, assertions)
+
+ print("Alternatively, print the model", "\n")
+ slv.printModel()
+
+ print()
+ print("You can also use nested loops to iterate over terms")
+ for a in assertions:
+ print("term:", a)
+ for t in a:
+ print(" + child: ", t)
+
+ print()
diff --git a/examples/api/python/datatypes.py b/examples/api/python/datatypes.py
new file mode 100755
index 000000000..c5eda1741
--- /dev/null
+++ b/examples/api/python/datatypes.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+
+#####################
+#! \file datatypes.py
+ ## \verbatim
+ ## Top contributors (to current version):
+ ## Makai Mann
+ ## This file is part of the CVC4 project.
+ ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS
+ ## in the top-level source directory) and their institutional affiliations.
+ ## All rights reserved. See the file COPYING in the top-level source
+ ## directory for licensing information.\endverbatim
+ ##
+ ## \brief A simple demonstration of the solving capabilities of the CVC4
+ ## datatypes solver through the Python API. This is a direct translation
+ ## of datatypes-new.cpp.
+
+import pycvc4
+from pycvc4 import kinds
+
+def test(slv, consListSort):
+ # Now our old "consListSpec" is useless--the relevant information
+ # has been copied out, so we can throw that spec away. We can get
+ # the complete spec for the datatype from the DatatypeSort, and
+ # this Datatype object has constructor symbols (and others) filled in.
+
+ consList = consListSort.getDatatype()
+
+ # t = cons 0 nil
+ #
+ # Here, consList["cons"] gives you the DatatypeConstructor. To get
+ # the constructor symbol for application, use .getConstructor("cons"),
+ # which is equivalent to consList["cons"].getConstructor(). Note that
+ # "nil" is a constructor too
+
+ t = slv.mkTerm(kinds.ApplyConstructor, consList.getConstructorTerm("cons"),
+ slv.mkReal(0),
+ slv.mkTerm(kinds.ApplyConstructor, consList.getConstructorTerm("nil")))
+
+ print("t is {}\nsort of cons is {}\n sort of nil is {}".format(
+ t,
+ consList.getConstructorTerm("cons").getSort(),
+ consList.getConstructorTerm("nil").getSort()))
+
+ # t2 = head(cons 0 nil), and of course this can be evaluated
+ #
+ # Here we first get the DatatypeConstructor for cons (with
+ # consList["cons"]) in order to get the "head" selector symbol
+ # to apply.
+
+ t2 = slv.mkTerm(kinds.ApplySelector, consList["cons"].getSelectorTerm("head"), t)
+
+ print("t2 is {}\nsimplify(t2) is {}\n\n".format(t2, slv.simplify(t2)))
+
+ # You can also iterate over a Datatype to get all its constructors,
+ # and over a DatatypeConstructor to get all its "args" (selectors)
+ for i in consList:
+ print("ctor:", i)
+ for j in i:
+ print(" + args:", j)
+ print()
+
+ # You can also define parameterized datatypes.
+ # This example builds a simple parameterized list of sort T, with one
+ # constructor "cons".
+ sort = slv.mkParamSort("T")
+ paramConsListSpec = slv.mkDatatypeDecl("paramlist", sort)
+ paramCons = pycvc4.DatatypeConstructorDecl("cons")
+ paramNil = pycvc4.DatatypeConstructorDecl("nil")
+ paramHead = pycvc4.DatatypeSelectorDecl("head", sort)
+ paramTail = pycvc4.DatatypeSelectorDecl("tail", pycvc4.DatatypeDeclSelfSort())
+ paramCons.addSelector(paramHead)
+ paramCons.addSelector(paramTail)
+ paramConsListSpec.addConstructor(paramCons)
+ paramConsListSpec.addConstructor(paramNil)
+
+ paramConsListSort = slv.mkDatatypeSort(paramConsListSpec)
+ paramConsIntListSort = paramConsListSort.instantiate([slv.getIntegerSort()])
+ paramConsList = paramConsListSort.getDatatype()
+
+ a = slv.mkConst(paramConsIntListSort, "a")
+ print("term {} is of sort {}".format(a, a.getSort()))
+
+ head_a = slv.mkTerm(kinds.ApplySelector, paramConsList["cons"].getSelectorTerm("head"), a)
+ print("head_a is {} of sort {}".format(head_a, head_a.getSort()))
+ print("sort of cons is", paramConsList.getConstructorTerm("cons").getSort())
+
+ assertion = slv.mkTerm(kinds.Gt, head_a, slv.mkReal(50))
+ print("Assert", assertion)
+ slv.assertFormula(assertion)
+ print("Expect sat.")
+ print("CVC4:", slv.checkSat())
+
+
+if __name__ == "__main__":
+ slv = pycvc4.Solver()
+
+ # This example builds a simple "cons list" of integers, with
+ # two constructors, "cons" and "nil."
+
+ # Building a datatype consists of two steps.
+ # First, the datatype is specified.
+ # Second, it is "resolved" to an actual sort, at which point function
+ # symbols are assigned to its constructors, selectors, and testers.
+
+ consListSpec = slv.mkDatatypeDecl("list") # give the datatype a name
+ cons = pycvc4.DatatypeConstructorDecl("cons")
+ head = pycvc4.DatatypeSelectorDecl("head", slv.getIntegerSort())
+ tail = pycvc4.DatatypeSelectorDecl("tail", pycvc4.DatatypeDeclSelfSort())
+ cons.addSelector(head)
+ cons.addSelector(tail)
+ consListSpec.addConstructor(cons)
+ nil = pycvc4.DatatypeConstructorDecl("nil")
+ consListSpec.addConstructor(nil)
+
+ print("spec is {}".format(consListSpec))
+
+ # Keep in mind that "DatatypeDecl" is the specification class for
+ # datatypes---"DatatypeDecl" is not itself a CVC4 Sort.
+ # Now that our Datatype is fully specified, we can get a Sort for it.
+ # This step resolves the "SelfSort" reference and creates
+ # symbols for all the constructors, etc.
+
+ consListSort = slv.mkDatatypeSort(consListSpec)
+ test(slv, consListSort)
+
+ print("### Alternatively, use declareDatatype")
+
+ cons2 = pycvc4.DatatypeConstructorDecl("cons")
+ head2 = pycvc4.DatatypeSelectorDecl("head", slv.getIntegerSort())
+ tail2 = pycvc4.DatatypeSelectorDecl("tail", pycvc4.DatatypeDeclSelfSort())
+ cons2.addSelector(head2)
+ cons2.addSelector(tail2)
+ nil2 = pycvc4.DatatypeConstructorDecl("nil")
+ ctors = [cons2, nil2]
+ consListSort2 = slv.declareDatatype("list2", ctors)
+ test(slv, consListSort2)
diff --git a/examples/api/python/extract.py b/examples/api/python/extract.py
new file mode 100755
index 000000000..1bfdf652a
--- /dev/null
+++ b/examples/api/python/extract.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+#####################
+#! \file extract.py
+ ## \verbatim
+ ## Top contributors (to current version):
+ ## Makai Mann
+ ## This file is part of the CVC4 project.
+ ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS
+ ## in the top-level source directory) and their institutional affiliations.
+ ## All rights reserved. See the file COPYING in the top-level source
+ ## directory for licensing information.\endverbatim
+ ##
+ ## \brief A simple demonstration of the solving capabilities of the CVC4
+ ## bit-vector solver through the Python API. This is a direct translation
+ ## of extract-new.cpp.
+
+from pycvc4 import Solver
+from pycvc4.kinds import BVExtract, Equal
+
+if __name__ == "__main__":
+ slv = Solver()
+ slv.setLogic("QF_BV")
+
+ bitvector32 = slv.mkBitVectorSort(32)
+
+ x = slv.mkConst(bitvector32, "a")
+
+ ext_31_1 = slv.mkOp(BVExtract, 31, 1)
+ x_31_1 = slv.mkTerm(ext_31_1, x)
+
+ ext_30_0 = slv.mkOp(BVExtract, 30, 0)
+ x_30_0 = slv.mkTerm(ext_30_0, x)
+
+ ext_31_31 = slv.mkOp(BVExtract, 31, 31)
+ x_31_31 = slv.mkTerm(ext_31_31, x)
+
+ ext_0_0 = slv.mkOp(BVExtract, 0, 0)
+ x_0_0 = slv.mkTerm(ext_0_0, x)
+
+ # test getting indices
+ assert ext_30_0.getIndices() == (30, 0)
+
+ eq = slv.mkTerm(Equal, x_31_1, x_30_0)
+ print("Asserting:", eq)
+ slv.assertFormula(eq)
+
+ eq2 = slv.mkTerm(Equal, x_31_31, x_0_0)
+ print("Check validity assuming:", eq2)
+ print("Expect valid")
+ print("CVC4:", slv.checkValidAssuming(eq2))
diff --git a/examples/api/python/helloworld.py b/examples/api/python/helloworld.py
new file mode 100755
index 000000000..472cf945b
--- /dev/null
+++ b/examples/api/python/helloworld.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+#####################
+#! \file helloworld.py
+## \verbatim
+## Top contributors (to current version):
+## Makai Mann
+## This file is part of the CVC4 project.
+## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS
+## in the top-level source directory) and their institutional affiliations.
+## All rights reserved. See the file COPYING in the top-level source
+## directory for licensing information.\endverbatim
+##
+## \brief A very simple CVC4 tutorial example, adapted from helloworld-new.cpp
+import pycvc4
+from pycvc4 import kinds
+
+if __name__ == "__main__":
+ slv = pycvc4.Solver()
+ helloworld = slv.mkConst(slv.getBooleanSort(), "Hello World!")
+ print(helloworld, "is", slv.checkValidAssuming(helloworld))
diff --git a/examples/api/python/linear_arith.py b/examples/api/python/linear_arith.py
new file mode 100755
index 000000000..6ae6427b1
--- /dev/null
+++ b/examples/api/python/linear_arith.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+
+#####################
+#! \file linear_arith.py
+## \verbatim
+## Top contributors (to current version):
+## Makai Mann
+## This file is part of the CVC4 project.
+## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS
+## in the top-level source directory) and their institutional affiliations.
+## All rights reserved. See the file COPYING in the top-level source
+## directory for licensing information.\endverbatim
+##
+## \brief A simple demonstration of the solving capabilities of the CVC4
+## linear arithmetic solver through the Python API. This is a direct
+## translation of linear_arith-new.cpp.
+import pycvc4
+from pycvc4 import kinds
+
+if __name__ == "__main__":
+ slv = pycvc4.Solver()
+ slv.setLogic("QF_LIRA")
+
+ # Prove that if given x (Integer) and y (Real) and some constraints
+ # then the maximum value of y - x is 2/3
+
+ # Sorts
+ real = slv.getRealSort()
+ integer = slv.getIntegerSort()
+
+ # Variables
+ x = slv.mkConst(integer, "x")
+ y = slv.mkConst(real, "y")
+
+ # Constants
+ three = slv.mkReal(3)
+ neg2 = slv.mkReal(-2)
+ two_thirds = slv.mkReal(2, 3)
+
+ # Terms
+ three_y = slv.mkTerm(kinds.Mult, three, y)
+ diff = slv.mkTerm(kinds.Minus, y, x)
+
+ # Formulas
+ x_geq_3y = slv.mkTerm(kinds.Geq, x, three_y)
+ x_leq_y = slv.mkTerm(kinds.Leq, x ,y)
+ neg2_lt_x = slv.mkTerm(kinds.Lt, neg2, x)
+
+ assertions = slv.mkTerm(kinds.And, x_geq_3y, x_leq_y, neg2_lt_x)
+
+ print("Given the assertions", assertions)
+ slv.assertFormula(assertions)
+
+ slv.push()
+ diff_leq_two_thirds = slv.mkTerm(kinds.Leq, diff, two_thirds)
+ print("Prove that", diff_leq_two_thirds, "with CVC4")
+ print("CVC4 should report VALID")
+ print("Result from CVC4 is:",
+ slv.checkValidAssuming(diff_leq_two_thirds))
+ slv.pop()
+
+ print()
+
+ slv.push()
+ diff_is_two_thirds = slv.mkTerm(kinds.Equal, diff, two_thirds)
+ slv.assertFormula(diff_is_two_thirds)
+ print("Show that the assertions are consistent with\n", diff_is_two_thirds, "with CVC4")
+ print("CVC4 should report SAT")
+ print("Result from CVC4 is:", slv.checkSat())
+ slv.pop()
diff --git a/examples/api/python/sets.py b/examples/api/python/sets.py
new file mode 100755
index 000000000..584880b2b
--- /dev/null
+++ b/examples/api/python/sets.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+
+#####################
+#! \file sets.py
+## \verbatim
+## Top contributors (to current version):
+## Makai Mann
+## This file is part of the CVC4 project.
+## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS
+## in the top-level source directory) and their institutional affiliations.
+## All rights reserved. See the file COPYING in the top-level source
+## directory for licensing information.\endverbatim
+##
+## \brief A simple demonstration of the solving capabilities of the CVC4
+## sets solver through the Python API. This is a direct translation
+## of sets-new.cpp.
+import pycvc4
+from pycvc4 import kinds
+
+if __name__ == "__main__":
+ slv = pycvc4.Solver()
+
+ # Optionally, set the logic. We need at least UF for equality predicate,
+ # integers (LIA) and sets (FS).
+ slv.setLogic("QF_UFLIAFS")
+
+ # Produce models
+ slv.setOption("produce-models", "true")
+ slv.setOption("output-language", "smt2")
+
+ integer = slv.getIntegerSort()
+ set_ = slv.mkSetSort(integer)
+
+ # Verify union distributions over intersection
+ # (A union B) intersection C = (A intersection C) union (B intersection C)
+
+ A = slv.mkConst(set_, "A")
+ B = slv.mkConst(set_, "B")
+ C = slv.mkConst(set_, "C")
+
+ unionAB = slv.mkTerm(kinds.Union, A, B)
+ lhs = slv.mkTerm(kinds.Intersection, unionAB, C)
+
+ intersectionAC = slv.mkTerm(kinds.Intersection, A, C)
+ intersectionBC = slv.mkTerm(kinds.Intersection, B, C)
+ rhs = slv.mkTerm(kinds.Union, intersectionAC, intersectionBC)
+
+ theorem = slv.mkTerm(kinds.Equal, lhs, rhs)
+
+ print("CVC4 reports: {} is {}".format(theorem,
+ slv.checkValidAssuming(theorem)))
+
+ # Verify emptset is a subset of any set
+
+ A = slv.mkConst(set_, "A")
+ emptyset = slv.mkEmptySet(set_)
+
+ theorem = slv.mkTerm(kinds.Subset, emptyset, A)
+
+ print("CVC4 reports: {} is {}".format(theorem,
+ slv.checkValidAssuming(theorem)))
+
+ # Find me an element in 1, 2 intersection 2, 3, if there is one.
+
+ one = slv.mkReal(1)
+ two = slv.mkReal(2)
+ three = slv.mkReal(3)
+
+ singleton_one = slv.mkTerm(kinds.Singleton, one)
+ singleton_two = slv.mkTerm(kinds.Singleton, two)
+ singleton_three = slv.mkTerm(kinds.Singleton, three)
+ one_two = slv.mkTerm(kinds.Union, singleton_one, singleton_two)
+ two_three = slv.mkTerm(kinds.Union, singleton_two, singleton_three)
+ intersection = slv.mkTerm(kinds.Intersection, one_two, two_three)
+
+ x = slv.mkConst(integer, "x")
+
+ e = slv.mkTerm(kinds.Member, x, intersection)
+
+ result = slv.checkSatAssuming(e)
+
+ print("CVC4 reports: {} is {}".format(e, result))
+
+ if result:
+ print("For instance, {} is a member".format(slv.getValue(x)))
diff --git a/examples/api/python/strings.py b/examples/api/python/strings.py
new file mode 100755
index 000000000..5c8e91997
--- /dev/null
+++ b/examples/api/python/strings.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+
+#####################
+#! \file strings.py
+## \verbatim
+## Top contributors (to current version):
+## Makai Mann
+## This file is part of the CVC4 project.
+## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS
+## in the top-level source directory) and their institutional affiliations.
+## All rights reserved. See the file COPYING in the top-level source
+## directory for licensing information.\endverbatim
+##
+## \brief A simple demonstration of the solving capabilities of the CVC4
+## strings solver through the Python API. This is a direct translation
+## of strings-new.cpp.
+import pycvc4
+from pycvc4 import kinds
+
+if __name__ == "__main__":
+ slv = pycvc4.Solver()
+ # Set the logic
+ slv.setLogic("S")
+ # Produce models
+ slv.setOption("produce-models", "true")
+ # The option strings-exp is needed
+ slv.setOption("strings-exp", "true")
+ # Set output language to SMTLIB2
+ slv.setOption("output-language", "smt2")
+
+ # String type
+ string = slv.getStringSort()
+
+ # std::string
+ str_ab = "ab"
+ # String constants
+ ab = slv.mkString(str_ab)
+ abc = slv.mkString("abc")
+ # String variables
+ x = slv.mkConst(string, "x")
+ y = slv.mkConst(string, "y")
+ z = slv.mkConst(string, "z")
+
+ # String concatenation: x.ab.y
+ lhs = slv.mkTerm(kinds.StringConcat, x, ab, y)
+ # String concatenation: abc.z
+ rhs = slv.mkTerm(kinds.StringConcat, abc, z)
+ # x.ab.y = abc.z
+ formula1 = slv.mkTerm(kinds.Equal, lhs, rhs)
+
+ # Length of y: |y|
+ leny = slv.mkTerm(kinds.StringLength, y)
+ # |y| >= 0
+ formula2 = slv.mkTerm(kinds.Geq, leny, slv.mkReal(0))
+
+ # Regular expression: (ab[c-e]*f)|g|h
+ r = slv.mkTerm(kinds.RegexpUnion,
+ slv.mkTerm(kinds.RegexpConcat,
+ slv.mkTerm(kinds.StringToRegexp, slv.mkString("ab")),
+ slv.mkTerm(kinds.RegexpStar,
+ slv.mkTerm(kinds.RegexpRange, slv.mkString("c"), slv.mkString("e"))),
+ slv.mkTerm(kinds.StringToRegexp, slv.mkString("f"))),
+ slv.mkTerm(kinds.StringToRegexp, slv.mkString("g")),
+ slv.mkTerm(kinds.StringToRegexp, slv.mkString("h")))
+
+ # String variables
+ s1 = slv.mkConst(string, "s1")
+ s2 = slv.mkConst(string, "s2")
+ # String concatenation: s1.s2
+ s = slv.mkTerm(kinds.StringConcat, s1, s2)
+
+ # s1.s2 in (ab[c-e]*f)|g|h
+ formula3 = slv.mkTerm(kinds.StringInRegexp, s, r)
+
+ # Make a query
+ q = slv.mkTerm(kinds.And,
+ formula1,
+ formula2,
+ formula3)
+
+ # check sat
+ result = slv.checkSatAssuming(q)
+ print("CVC4 reports:", q, "is", result)
+
+ if result:
+ print("x = ", slv.getValue(x))
+ print(" s1.s2 =", slv.getValue(s))
diff --git a/src/api/cvc4cpp.cpp b/src/api/cvc4cpp.cpp
index f4ca1147f..7bdc5008b 100644
--- a/src/api/cvc4cpp.cpp
+++ b/src/api/cvc4cpp.cpp
@@ -1755,27 +1755,40 @@ DatatypeDecl::DatatypeDecl(const Solver* s,
new CVC4::Datatype(s->getExprManager(), name, tparams, isCoDatatype));
}
+bool DatatypeDecl::isNullHelper() const { return !d_dtype; }
+
+DatatypeDecl::DatatypeDecl() {}
+
DatatypeDecl::~DatatypeDecl() {}
void DatatypeDecl::addConstructor(const DatatypeConstructorDecl& ctor)
{
+ CVC4_API_CHECK_NOT_NULL;
d_dtype->addConstructor(*ctor.d_ctor);
}
size_t DatatypeDecl::getNumConstructors() const
{
+ CVC4_API_CHECK_NOT_NULL;
return d_dtype->getNumConstructors();
}
-bool DatatypeDecl::isParametric() const { return d_dtype->isParametric(); }
+bool DatatypeDecl::isParametric() const
+{
+ CVC4_API_CHECK_NOT_NULL;
+ return d_dtype->isParametric();
+}
std::string DatatypeDecl::toString() const
{
+ CVC4_API_CHECK_NOT_NULL;
std::stringstream ss;
ss << *d_dtype;
return ss.str();
}
+bool DatatypeDecl::isNull() const { return isNullHelper(); }
+
// !!! This is only temporarily available until the parser is fully migrated
// to the new API. !!!
const CVC4::Datatype& DatatypeDecl::getDatatype(void) const { return *d_dtype; }
@@ -1895,6 +1908,9 @@ DatatypeConstructor::const_iterator::const_iterator(
d_idx = begin ? 0 : sels->size();
}
+// Nullary constructor for Cython
+DatatypeConstructor::const_iterator::const_iterator() {}
+
DatatypeConstructor::const_iterator& DatatypeConstructor::const_iterator::
operator=(const DatatypeConstructor::const_iterator& it)
{
@@ -1969,6 +1985,9 @@ Datatype::Datatype(const CVC4::Datatype& dtype)
{
}
+// Nullary constructor for Cython
+Datatype::Datatype() {}
+
Datatype::~Datatype() {}
DatatypeConstructor Datatype::operator[](size_t idx) const
@@ -2007,6 +2026,8 @@ size_t Datatype::getNumConstructors() const
bool Datatype::isParametric() const { return d_dtype->isParametric(); }
+std::string Datatype::toString() const { return d_dtype->getName(); }
+
Datatype::const_iterator Datatype::begin() const
{
return Datatype::const_iterator(*d_dtype, true);
@@ -2035,6 +2056,8 @@ Datatype::const_iterator::const_iterator(const CVC4::Datatype& dtype,
d_idx = begin ? 0 : cons->size();
}
+Datatype::const_iterator::const_iterator() {}
+
Datatype::const_iterator& Datatype::const_iterator::operator=(
const Datatype::const_iterator& it)
{
diff --git a/src/api/cvc4cpp.h b/src/api/cvc4cpp.h
index f7f16832a..79c02c031 100644
--- a/src/api/cvc4cpp.h
+++ b/src/api/cvc4cpp.h
@@ -1096,6 +1096,11 @@ class CVC4_PUBLIC DatatypeDecl
friend class Solver;
public:
/**
+ * Nullary constructor for Cython
+ */
+ DatatypeDecl();
+
+ /**
* Destructor.
*/
~DatatypeDecl();
@@ -1112,6 +1117,8 @@ class CVC4_PUBLIC DatatypeDecl
/** Is this Datatype declaration parametric? */
bool isParametric() const;
+ bool isNull() const;
+
/**
* @return a string representation of this datatype declaration
*/
@@ -1158,6 +1165,10 @@ class CVC4_PUBLIC DatatypeDecl
const std::string& name,
const std::vector<Sort>& params,
bool isCoDatatype = false);
+
+ // helper for isNull() to avoid calling API functions from other API functions
+ bool isNullHelper() const;
+
/* The internal (intermediate) datatype wrapped by this datatype
* declaration
* This is a shared_ptr rather than a unique_ptr since CVC4::Datatype is
@@ -1295,6 +1306,9 @@ class CVC4_PUBLIC DatatypeConstructor
friend class DatatypeConstructor; // to access constructor
public:
+ /** Nullary constructor (required for Cython). */
+ const_iterator();
+
/**
* Assignment operator.
* @param it the iterator to assign to
@@ -1398,6 +1412,9 @@ class CVC4_PUBLIC Datatype
*/
Datatype(const CVC4::Datatype& dtype);
+ // Nullary constructor for Cython
+ Datatype();
+
/**
* Destructor.
*/
@@ -1447,6 +1464,9 @@ class CVC4_PUBLIC Datatype
friend class Datatype; // to access constructor
public:
+ /** Nullary constructor (required for Cython). */
+ const_iterator();
+
/**
* Assignment operator.
* @param it the iterator to assign to
diff --git a/src/api/python/CMakeLists.txt b/src/api/python/CMakeLists.txt
new file mode 100644
index 000000000..166a728de
--- /dev/null
+++ b/src/api/python/CMakeLists.txt
@@ -0,0 +1,42 @@
+if(POLICY CMP0057)
+ # For cmake >= 3.3 this policy changed the behavior of IN_LIST
+ # if the policy exists, we use the NEW behavior
+ cmake_policy(SET CMP0057 NEW)
+endif()
+
+find_package(PythonExtensions REQUIRED)
+find_package(Cython 0.29 REQUIRED)
+
+include_directories(${PYTHON_INCLUDE_DIRS})
+include_directories(${CMAKE_CURRENT_LIST_DIR}) # cvc4kinds.pxd
+include_directories(${PROJECT_SOURCE_DIR}/src)
+include_directories(${PROJECT_SOURCE_DIR}/src/include)
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+# TEMP: Only needed while expr/kind.h is public in the C++ api
+include_directories("${CMAKE_BINARY_DIR}/src/")
+
+# Generate cvc4kinds.{pxd,pyx}
+add_custom_target(
+ gen-pycvc4-kinds
+ ALL
+ COMMAND
+ "${PYTHON_EXECUTABLE}"
+ "${CMAKE_CURRENT_LIST_DIR}/genkinds.py"
+ --kinds-header "${PROJECT_SOURCE_DIR}/src/api/cvc4cppkind.h"
+ --kinds-file-prefix "cvc4kinds"
+ DEPENDS
+ genkinds.py
+ COMMENT
+ "Generate cvc4kinds.{pxd,pyx}"
+)
+
+add_cython_target(pycvc4 CXX)
+
+add_library(pycvc4
+ MODULE
+ ${pycvc4})
+
+target_link_libraries(pycvc4 cvc4 ${PYTHON_LIBRARIES})
+
+python_extension_module(pycvc4)
+install(TARGETS pycvc4 DESTINATION lib)
diff --git a/src/api/python/cvc4.pxd b/src/api/python/cvc4.pxd
new file mode 100644
index 000000000..b8bf009af
--- /dev/null
+++ b/src/api/python/cvc4.pxd
@@ -0,0 +1,274 @@
+# import dereference and increment operators
+from cython.operator cimport dereference as deref, preincrement as inc
+from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t
+from libcpp.string cimport string
+from libcpp.vector cimport vector
+from libcpp.pair cimport pair
+from cvc4kinds cimport Kind
+
+
+cdef extern from "<iostream>" namespace "std":
+ cdef cppclass ostream:
+ pass
+ ostream cout
+
+
+cdef extern from "api/cvc4cpp.h" namespace "CVC4":
+ cdef cppclass Options:
+ pass
+
+
+cdef extern from "api/cvc4cpp.h" namespace "CVC4::api":
+ cdef cppclass Datatype:
+ Datatype() except +
+ DatatypeConstructor operator[](const string& name) except +
+ DatatypeConstructor getConstructor(const string& name) except +
+ Term getConstructorTerm(const string& name) except +
+ size_t getNumConstructors() except +
+ bint isParametric() except +
+ string toString() except +
+ cppclass const_iterator:
+ const_iterator() except +
+ bint operator==(const const_iterator& it) except +
+ bint operator!=(const const_iterator& it) except +
+ const_iterator& operator++();
+ const DatatypeConstructor& operator*() except +
+ const_iterator begin() except +
+ const_iterator end() except +
+
+
+ cdef cppclass DatatypeConstructor:
+ DatatypeConstructor() except +
+ DatatypeSelector operator[](const string& name) except +
+ DatatypeSelector getSelector(const string& name) except +
+ Term getSelectorTerm(const string& name) except +
+ string toString() except +
+ cppclass const_iterator:
+ const_iterator() except +
+ bint operator==(const const_iterator& it) except +
+ bint operator!=(const const_iterator& it) except +
+ const_iterator& operator++();
+ const DatatypeSelector& operator*() except +
+ const_iterator begin() except +
+ const_iterator end() except +
+
+
+ cdef cppclass DatatypeConstructorDecl:
+ DatatypeConstructorDecl(const string& name) except +
+ void addSelector(const DatatypeSelectorDecl& stor) except +
+ string toString() except +
+
+
+ cdef cppclass DatatypeDecl:
+ void addConstructor(const DatatypeConstructorDecl& ctor) except +
+ bint isParametric() except +
+ string toString() except +
+
+
+ cdef cppclass DatatypeDeclSelfSort:
+ DatatypeDeclSelfSort() except +
+
+
+ cdef cppclass DatatypeSelector:
+ DatatypeSelector() except +
+ string toString() except +
+
+
+ cdef cppclass DatatypeSelectorDecl:
+ DatatypeSelectorDecl(const string& name, Sort sort) except +
+ DatatypeSelectorDecl(const string& name, DatatypeDeclSelfSort sort) except +
+ string toString() except +
+
+
+ cdef cppclass Op:
+ Op() except +
+ bint operator==(const Op&) except +
+ bint operator!=(const Op&) except +
+ Kind getKind() except +
+ Sort getSort() except +
+ bint isNull() except +
+ T getIndices[T]() except +
+ string toString() except +
+
+
+ cdef cppclass Result:
+ # Note: don't even need constructor
+ bint isSat() except +
+ bint isUnsat() except +
+ bint isSatUnknown() except +
+ bint isValid() except +
+ bint isInvalid() except +
+ bint isValidUnknown() except +
+ string getUnknownExplanation() except +
+ string toString() except +
+
+
+ cdef cppclass RoundingMode:
+ pass
+
+
+ cdef cppclass Solver:
+ Solver(Options*) except +
+ Sort getBooleanSort() except +
+ Sort getIntegerSort() except +
+ Sort getRealSort() except +
+ Sort getRegExpSort() except +
+ Sort getRoundingmodeSort() except +
+ Sort getStringSort() except +
+ Sort mkArraySort(Sort indexSort, Sort elemSort) except +
+ Sort mkBitVectorSort(uint32_t size) except +
+ Sort mkFloatingPointSort(uint32_t exp, uint32_t sig) except +
+ Sort mkDatatypeSort(DatatypeDecl dtypedecl) except +
+ Sort mkFunctionSort(Sort domain, Sort codomain) except +
+ Sort mkFunctionSort(const vector[Sort]& sorts, Sort codomain) except +
+ Sort mkParamSort(const string& symbol) except +
+ Sort mkPredicateSort(const vector[Sort]& sorts) except +
+ Sort mkRecordSort(const vector[pair[string, Sort]]& fields) except +
+ Sort mkSetSort(Sort elemSort) except +
+ Sort mkUninterpretedSort(const string& symbol) except +
+ Sort mkSortConstructorSort(const string& symbol, size_t arity) except +
+ Sort mkTupleSort(const vector[Sort]& sorts) except +
+ Term mkTerm(Op op) except +
+ Term mkTerm(Op op, const vector[Term]& children) except +
+ Op mkOp(Kind kind) except +
+ Op mkOp(Kind kind, Kind k) except +
+ Op mkOp(Kind kind, const string& arg) except +
+ Op mkOp(Kind kind, uint32_t arg) except +
+ Op mkOp(Kind kind, uint32_t arg1, uint32_t arg2) except +
+ Term mkTrue() except +
+ Term mkFalse() except +
+ Term mkBoolean(bint val) except +
+ Term mkPi() except +
+ Term mkReal(const string& s) except +
+ Term mkRegexpEmpty() except +
+ Term mkRegexpSigma() except +
+ Term mkEmptySet(Sort s) except +
+ Term mkSepNil(Sort sort) except +
+ Term mkString(const string& s) except +
+ Term mkString(const vector[unsigned]& s) except +
+ Term mkUniverseSet(Sort sort) except +
+ Term mkBitVector(uint32_t size) except +
+ Term mkBitVector(uint32_t size, uint64_t val) except +
+ Term mkBitVector(const string& s) except +
+ Term mkBitVector(const string& s, uint32_t base) except +
+ Term mkConstArray(Sort sort, Term val) except +
+ Term mkPosInf(uint32_t exp, uint32_t sig) except +
+ Term mkNegInf(uint32_t exp, uint32_t sig) except +
+ Term mkNaN(uint32_t exp, uint32_t sig) except +
+ Term mkPosZero(uint32_t exp, uint32_t sig) except +
+ Term mkNegZero(uint32_t exp, uint32_t sig) except +
+ Term mkRoundingMode(RoundingMode rm) except +
+ Term mkUninterpretedConst(Sort sort, int32_t index) except +
+ Term mkAbstractValue(const string& index) except +
+ Term mkFloatingPoint(uint32_t exp, uint32_t sig, Term val) except +
+ Term mkConst(Sort sort, const string& symbol) except +
+ # default value for symbol defined in cvc4cpp.h
+ Term mkConst(Sort sort) except +
+ Term mkVar(Sort sort, const string& symbol) except +
+ DatatypeDecl mkDatatypeDecl(const string& name) except +
+ DatatypeDecl mkDatatypeDecl(const string& name, bint isCoDatatype) except +
+ DatatypeDecl mkDatatypeDecl(const string& name, Sort param) except +
+ DatatypeDecl mkDatatypeDecl(const string& name, Sort param, bint isCoDatatype) except +
+ DatatypeDecl mkDatatypeDecl(const string& name, vector[Sort]& params) except +
+ DatatypeDecl mkDatatypeDecl(const string& name, vector[Sort]& params, bint isCoDatatype) except +
+ # default value for symbol defined in cvc4cpp.h
+ Term mkVar(Sort sort) except +
+ Term simplify(const Term& t) except +
+ void assertFormula(Term term) except +
+ Result checkSat() except +
+ Result checkSatAssuming(const vector[Term]& assumptions) except +
+ Result checkValid() except +
+ Result checkValidAssuming(const vector[Term]& assumptions) except +
+ Sort declareDatatype(const string& symbol, const vector[DatatypeConstructorDecl]& ctors)
+ Term declareFun(const string& symbol, Sort sort) except +
+ Term declareFun(const string& symbol, const vector[Sort]& sorts, Sort sort) except +
+ Sort declareSort(const string& symbol, uint32_t arity) except +
+ Term defineFun(const string& symbol, const vector[Term]& bound_vars,
+ Sort sort, Term term) except +
+ Term defineFun(Term fun, const vector[Term]& bound_vars, Term term) except +
+ Term defineFunRec(const string& symbol, const vector[Term]& bound_vars,
+ Sort sort, Term term) except +
+ Term defineFunRec(Term fun, const vector[Term]& bound_vars,
+ Term term) except +
+ Term defineFunsRec(vector[Term]& funs, vector[vector[Term]]& bound_vars,
+ vector[Term]& terms) except +
+ vector[Term] getAssertions() except +
+ vector[pair[Term, Term]] getAssignment() except +
+ string getInfo(const string& flag) except +
+ string getOption(string& option) except +
+ vector[Term] getUnsatAssumptions() except +
+ vector[Term] getUnsatCore() except +
+ Term getValue(Term term) except +
+ vector[Term] getValue(const vector[Term]& terms) except +
+ void pop(uint32_t nscopes) except +
+ void printModel(ostream& out)
+ void push(uint32_t nscopes) except +
+ void reset() except +
+ void resetAssertions() except +
+ void setInfo(string& keyword, const string& value) except +
+ void setLogic(const string& logic) except +
+ void setOption(const string& option, const string& value) except +
+
+
+ cdef cppclass Sort:
+ Sort() except +
+ bint operator==(const Sort&) except +
+ bint operator!=(const Sort&) except +
+ bint isBoolean() except +
+ bint isInteger() except +
+ bint isReal() except +
+ bint isString() except +
+ bint isRegExp() except +
+ bint isRoundingMode() except +
+ bint isBitVector() except +
+ bint isFloatingPoint() except +
+ bint isDatatype() except +
+ bint isParametricDatatype() except +
+ bint isFunction() except +
+ bint isPredicate() except +
+ bint isTuple() except +
+ bint isRecord() except +
+ bint isArray() except +
+ bint isSet() except +
+ bint isUninterpretedSort() except +
+ bint isSortConstructor() except +
+ bint isFirstClass() except +
+ bint isFunctionLike() except +
+ Datatype getDatatype() except +
+ Sort instantiate(const vector[Sort]& params) except +
+ bint isUninterpretedSortParameterized() except +
+ string toString() except +
+
+ cdef cppclass Term:
+ Term()
+ bint operator==(const Term&) except +
+ bint operator!=(const Term&) except +
+ Kind getKind() except +
+ Sort getSort() except +
+ bint hasOp() except +
+ Op getOp() except +
+ bint isNull() except +
+ Term notTerm() except +
+ Term andTerm(const Term& t) except +
+ Term orTerm(const Term& t) except +
+ Term xorTerm(const Term& t) except +
+ Term eqTerm(const Term& t) except +
+ Term impTerm(const Term& t) except +
+ Term iteTerm(const Term& then_t, const Term& else_t) except +
+ string toString() except +
+ cppclass const_iterator:
+ const_iterator() except +
+ bint operator==(const const_iterator& it) except +
+ bint operator!=(const const_iterator& it) except +
+ const_iterator& operator++();
+ Term operator*() except +
+ const_iterator begin() except +
+ const_iterator end() except +
+
+
+cdef extern from "api/cvc4cpp.h" namespace "CVC4::api::RoundingMode":
+ cdef RoundingMode ROUND_NEAREST_TIES_TO_EVEN,
+ cdef RoundingMode ROUND_TOWARD_POSITIVE,
+ cdef RoundingMode ROUND_TOWARD_NEGATIVE,
+ cdef RoundingMode ROUND_TOWARD_ZERO,
+ cdef RoundingMode ROUND_NEAREST_TIES_TO_AWAY
diff --git a/src/api/python/cvc4.pxi b/src/api/python/cvc4.pxi
new file mode 100644
index 000000000..39d9d4686
--- /dev/null
+++ b/src/api/python/cvc4.pxi
@@ -0,0 +1,1181 @@
+import sys
+
+from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t
+
+from libcpp.pair cimport pair
+from libcpp.string cimport string
+from libcpp.vector cimport vector
+
+from cvc4 cimport cout
+from cvc4 cimport Datatype as c_Datatype
+from cvc4 cimport DatatypeConstructor as c_DatatypeConstructor
+from cvc4 cimport DatatypeConstructorDecl as c_DatatypeConstructorDecl
+from cvc4 cimport DatatypeDecl as c_DatatypeDecl
+from cvc4 cimport DatatypeDeclSelfSort as c_DatatypeDeclSelfSort
+from cvc4 cimport DatatypeSelector as c_DatatypeSelector
+from cvc4 cimport DatatypeSelectorDecl as c_DatatypeSelectorDecl
+from cvc4 cimport Result as c_Result
+from cvc4 cimport RoundingMode as c_RoundingMode
+from cvc4 cimport Op as c_Op
+from cvc4 cimport Solver as c_Solver
+from cvc4 cimport Sort as c_Sort
+from cvc4 cimport ROUND_NEAREST_TIES_TO_EVEN, ROUND_TOWARD_POSITIVE
+from cvc4 cimport ROUND_TOWARD_ZERO, ROUND_NEAREST_TIES_TO_AWAY
+from cvc4 cimport Term as c_Term
+
+from cvc4kinds cimport Kind as c_Kind
+
+################################## DECORATORS #################################
+def expand_list_arg(num_req_args=0):
+ '''
+ Creates a decorator that looks at index num_req_args of the args,
+ if it's a list, it expands it before calling the function.
+ '''
+ def decorator(func):
+ def wrapper(owner, *args):
+ if len(args) == num_req_args + 1 and \
+ isinstance(args[num_req_args], list):
+ args = list(args[:num_req_args]) + args[num_req_args]
+ return func(owner, *args)
+ return wrapper
+ return decorator
+###############################################################################
+
+# Style Guidelines
+### Using PEP-8 spacing recommendations
+### Limit linewidth to 79 characters
+### Break before binary operators
+### surround top level functions and classes with two spaces
+### separate methods by one space
+### use spaces in functions sparingly to separate logical blocks
+### can omit spaces between unrelated oneliners
+### always use c++ default arguments
+#### only use default args of None at python level
+#### Result class can have default because it's pure python
+
+
+cdef class Datatype:
+ cdef c_Datatype cd
+ def __cinit__(self):
+ pass
+
+ def __getitem__(self, str name):
+ cdef DatatypeConstructor dc = DatatypeConstructor()
+ dc.cdc = self.cd[name.encode()]
+ return dc
+
+ def getConstructor(self, str name):
+ cdef DatatypeConstructor dc = DatatypeConstructor()
+ dc.cdc = self.cd.getConstructor(name.encode())
+ return dc
+
+ def getConstructorTerm(self, str name):
+ cdef Term term = Term()
+ term.cterm = self.cd.getConstructorTerm(name.encode())
+ return term
+
+ def getNumConstructors(self):
+ return self.cd.getNumConstructors()
+
+ def isParametric(self):
+ return self.cd.isParametric()
+
+ def __str__(self):
+ return self.cd.toString().decode()
+
+ def __repr__(self):
+ return self.cd.toString().decode()
+
+ def __iter__(self):
+ for ci in self.cd:
+ dc = DatatypeConstructor()
+ dc.cdc = ci
+ yield dc
+
+
+cdef class DatatypeConstructor:
+ cdef c_DatatypeConstructor cdc
+ def __cinit__(self):
+ self.cdc = c_DatatypeConstructor()
+
+ def __getitem__(self, str name):
+ cdef DatatypeSelector ds = DatatypeSelector()
+ ds.cds = self.cdc[name.encode()]
+ return ds
+
+ def getSelector(self, str name):
+ cdef DatatypeSelector ds = DatatypeSelector()
+ ds.cds = self.cdc.getSelector(name.encode())
+ return ds
+
+ def getSelectorTerm(self, str name):
+ cdef Term term = Term()
+ term.cterm = self.cdc.getSelectorTerm(name.encode())
+ return term
+
+ def __str__(self):
+ return self.cdc.toString().decode()
+
+ def __repr__(self):
+ return self.cdc.toString().decode()
+
+ def __iter__(self):
+ for ci in self.cdc:
+ ds = DatatypeSelector()
+ ds.cds = ci
+ yield ds
+
+
+cdef class DatatypeConstructorDecl:
+ cdef c_DatatypeConstructorDecl* cddc
+ def __cinit__(self, str name):
+ self.cddc = new c_DatatypeConstructorDecl(name.encode())
+
+ def addSelector(self, DatatypeSelectorDecl stor):
+ self.cddc.addSelector(stor.cdsd[0])
+
+ def __str__(self):
+ return self.cddc.toString().decode()
+
+ def __repr__(self):
+ return self.cddc.toString().decode()
+
+
+cdef class DatatypeDecl:
+ cdef c_DatatypeDecl cdd
+ def __cinit__(self):
+ pass
+
+ def addConstructor(self, DatatypeConstructorDecl ctor):
+ self.cdd.addConstructor(ctor.cddc[0])
+
+ def isParametric(self):
+ return self.cdd.isParametric()
+
+ def __str__(self):
+ return self.cdd.toString().decode()
+
+ def __repr__(self):
+ return self.cdd.toString().decode()
+
+
+cdef class DatatypeDeclSelfSort:
+ cdef c_DatatypeDeclSelfSort cddss
+ def __cinit__(self):
+ self.cddss = c_DatatypeDeclSelfSort()
+
+
+cdef class DatatypeSelector:
+ cdef c_DatatypeSelector cds
+ def __cinit__(self):
+ self.cds = c_DatatypeSelector()
+
+ def __str__(self):
+ return self.cds.toString().decode()
+
+ def __repr__(self):
+ return self.cds.toString().decode()
+
+
+cdef class DatatypeSelectorDecl:
+ cdef c_DatatypeSelectorDecl* cdsd
+ def __cinit__(self, str name, sort):
+ if isinstance(sort, Sort):
+ self.cdsd = new c_DatatypeSelectorDecl(
+ <const string &> name.encode(), (<Sort?> sort).csort)
+ elif isinstance(sort, DatatypeDeclSelfSort):
+ self.cdsd = new c_DatatypeSelectorDecl(
+ <const string &> name.encode(),
+ (<DatatypeDeclSelfSort?> sort).cddss)
+
+ def __str__(self):
+ return self.cdsd.toString().decode()
+
+ def __repr__(self):
+ return self.cdsd.toString().decode()
+
+
+cdef class Op:
+ cdef c_Op cop
+ def __cinit__(self):
+ self.cop = c_Op()
+
+ def __eq__(self, Op other):
+ return self.cop == other.cop
+
+ def __ne__(self, Op other):
+ return self.cop != other.cop
+
+ def __str__(self):
+ return self.cop.toString().decode()
+
+ def __repr__(self):
+ return self.cop.toString().decode()
+
+ def getKind(self):
+ return kind(<int> self.cop.getKind())
+
+ def getSort(self):
+ cdef Sort sort = Sort()
+ sort.csort = self.cop.getSort()
+ return sort
+
+ def isNull(self):
+ return self.cop.isNull()
+
+ def getIndices(self):
+ indices = None
+ try:
+ indices = self.cop.getIndices[string]()
+ except:
+ pass
+
+ try:
+ indices = kind(<int> self.cop.getIndices[c_Kind]())
+ except:
+ pass
+
+ try:
+ indices = self.cop.getIndices[uint32_t]()
+ except:
+ pass
+
+ try:
+ indices = self.cop.getIndices[pair[uint32_t, uint32_t]]()
+ except:
+ pass
+
+ if indices is None:
+ raise RuntimeError("Unable to retrieve indices from {}".format(self))
+
+ return indices
+
+
+class Result:
+ def __init__(self, name, explanation=""):
+ name = name.lower()
+ incomplete = False
+ if "(incomplete)" in name:
+ incomplete = True
+ name = name.replace("(incomplete)", "").strip()
+ assert name in {"sat", "unsat", "valid", "invalid", "unknown"}, \
+ "can't interpret result = {}".format(name)
+
+ self._name = name
+ self._explanation = explanation
+ self._incomplete = incomplete
+
+ def __bool__(self):
+ if self._name in {"sat", "valid"}:
+ return True
+ elif self._name in {"unsat", "invalid"}:
+ return False
+ elif self._name == "unknown":
+ raise RuntimeError("Cannot interpret 'unknown' result as a Boolean")
+ else:
+ assert False, "Unhandled result=%s"%self._name
+
+ def __eq__(self, other):
+ if not isinstance(other, Result):
+ return False
+
+ return self._name == other._name
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __str__(self):
+ return self._name
+
+ def __repr__(self):
+ return self._name
+
+ def isUnknown(self):
+ return self._name == "unknown"
+
+ def isIncomplete(self):
+ return self._incomplete
+
+ @property
+ def explanation(self):
+ return self._explanation
+
+
+cdef class RoundingMode:
+ cdef c_RoundingMode crm
+ cdef str name
+ def __cinit__(self, int rm):
+ # crm always assigned externally
+ self.crm = <c_RoundingMode> rm
+ self.name = __rounding_modes[rm]
+
+ def __eq__(self, RoundingMode other):
+ return (<int> self.crm) == (<int> other.crm)
+
+ def __ne__(self, RoundingMode other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash((<int> self.crm, self.name))
+
+ def __str__(self):
+ return self.name
+
+ def __repr__(self):
+ return self.name
+
+
+cdef class Solver:
+ cdef c_Solver* csolver
+
+ def __cinit__(self):
+ self.csolver = new c_Solver(NULL)
+
+ def getBooleanSort(self):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.getBooleanSort()
+ return sort
+
+ def getIntegerSort(self):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.getIntegerSort()
+ return sort
+
+ def getRealSort(self):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.getRealSort()
+ return sort
+
+ def getRegExpSort(self):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.getRegExpSort()
+ return sort
+
+ def getRoundingmodeSort(self):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.getRoundingmodeSort()
+ return sort
+
+ def getStringSort(self):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.getStringSort()
+ return sort
+
+ def mkArraySort(self, Sort indexSort, Sort elemSort):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.mkArraySort(indexSort.csort, elemSort.csort)
+ return sort
+
+ def mkBitVectorSort(self, uint32_t size):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.mkBitVectorSort(size)
+ return sort
+
+ def mkFloatingPointSort(self, uint32_t exp, uint32_t sig):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.mkFloatingPointSort(exp, sig)
+ return sort
+
+ def mkDatatypeSort(self, DatatypeDecl dtypedecl):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.mkDatatypeSort(dtypedecl.cdd)
+ return sort
+
+ def mkFunctionSort(self, sorts, Sort codomain):
+
+ cdef Sort sort = Sort()
+ # populate a vector with dereferenced c_Sorts
+ cdef vector[c_Sort] v
+
+ if isinstance(sorts, Sort):
+ sort.csort = self.csolver.mkFunctionSort((<Sort?> sorts).csort,
+ codomain.csort)
+ elif isinstance(sorts, list):
+ for s in sorts:
+ v.push_back((<Sort?>s).csort)
+
+ sort.csort = self.csolver.mkFunctionSort(<const vector[c_Sort]&> v,
+ codomain.csort)
+ return sort
+
+ def mkParamSort(self, symbolname):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.mkParamSort(symbolname.encode())
+ return sort
+
+ @expand_list_arg(num_req_args=0)
+ def mkPredicateSort(self, *sorts):
+ '''
+ Supports the following arguments:
+ Sort mkPredicateSort(List[Sort] sorts)
+
+ where sorts can also be comma-separated arguments of
+ type Sort
+ '''
+ cdef Sort sort = Sort()
+ cdef vector[c_Sort] v
+ for s in sorts:
+ v.push_back((<Sort?> s).csort)
+ sort.csort = self.csolver.mkPredicateSort(<const vector[c_Sort]&> v)
+ return sort
+
+ @expand_list_arg(num_req_args=0)
+ def mkRecordSort(self, *fields):
+ '''
+ Supports the following arguments:
+ Sort mkRecordSort(List[Tuple[str, Sort]] fields)
+
+ where fields can also be comma-separated arguments of
+ type Tuple[str, Sort]
+ '''
+ cdef Sort sort = Sort()
+ cdef vector[pair[string, c_Sort]] v
+ cdef pair[string, c_Sort] p
+ for f in fields:
+ name, sortarg = f
+ name = name.encode()
+ p = pair[string, c_Sort](<string?> name, (<Sort?> sortarg).csort)
+ v.push_back(p)
+ sort.csort = self.csolver.mkRecordSort(
+ <const vector[pair[string, c_Sort]] &> v)
+ return sort
+
+ def mkSetSort(self, Sort elemSort):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.mkSetSort(elemSort.csort)
+ return sort
+
+ def mkUninterpretedSort(self, str name):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.mkUninterpretedSort(name.encode())
+ return sort
+
+ def mkSortConstructorSort(self, str symbol, size_t arity):
+ cdef Sort sort = Sort()
+ sort.csort =self.csolver.mkSortConstructorSort(symbol.encode(), arity)
+ return sort
+
+ @expand_list_arg(num_req_args=0)
+ def mkTupleSort(self, *sorts):
+ '''
+ Supports the following arguments:
+ Sort mkTupleSort(List[Sort] sorts)
+
+ where sorts can also be comma-separated arguments of
+ type Sort
+ '''
+ cdef Sort sort = Sort()
+ cdef vector[c_Sort] v
+ for s in sorts:
+ v.push_back((<Sort?> s).csort)
+ sort.csort = self.csolver.mkTupleSort(v)
+ return sort
+
+ @expand_list_arg(num_req_args=1)
+ def mkTerm(self, kind_or_op, *args):
+ '''
+ Supports the following arguments:
+ Term mkTerm(Kind kind)
+ Term mkTerm(Kind kind, Op child1, List[Term] children)
+ Term mkTerm(Kind kind, List[Term] children)
+
+ where List[Term] can also be comma-separated arguments
+ '''
+ cdef Term term = Term()
+ cdef vector[c_Term] v
+
+ op = kind_or_op
+ if isinstance(kind_or_op, kind):
+ op = self.mkOp(kind_or_op)
+
+ if len(args) == 0:
+ term.cterm = self.csolver.mkTerm((<Op?> op).cop)
+ else:
+ for a in args:
+ v.push_back((<Term?> a).cterm)
+ term.cterm = self.csolver.mkTerm((<Op?> op).cop, v)
+ return term
+
+ def mkOp(self, kind k, arg0=None, arg1 = None):
+ '''
+ Supports the following uses:
+ Op mkOp(Kind kind)
+ Op mkOp(Kind kind, Kind k)
+ Op mkOp(Kind kind, const string& arg)
+ Op mkOp(Kind kind, uint32_t arg)
+ Op mkOp(Kind kind, uint32_t arg0, uint32_t arg1)
+ '''
+ cdef Op op = Op()
+
+ if arg0 is None:
+ op.cop = self.csolver.mkOp(k.k)
+ elif arg1 is None:
+ if isinstance(arg0, kind):
+ op.cop = self.csolver.mkOp(k.k, (<kind?> arg0).k)
+ elif isinstance(arg0, str):
+ op.cop = self.csolver.mkOp(k.k,
+ <const string &>
+ arg0.encode())
+ elif isinstance(arg0, int):
+ op.cop = self.csolver.mkOp(k.k, <int?> arg0)
+ else:
+ raise ValueError("Unsupported signature"
+ " mkOp: {}".format(" X ".join([k, arg0])))
+ else:
+ if isinstance(arg0, int) and isinstance(arg1, int):
+ op.cop = self.csolver.mkOp(k.k, <int> arg0,
+ <int> arg1)
+ else:
+ raise ValueError("Unsupported signature"
+ " mkOp: {}".format(" X ".join([k, arg0, arg1])))
+ return op
+
+ def mkTrue(self):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkTrue()
+ return term
+
+ def mkFalse(self):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkFalse()
+ return term
+
+ def mkBoolean(self, bint val):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkBoolean(val)
+ return term
+
+ def mkPi(self):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkPi()
+ return term
+
+ def mkReal(self, val, den=None):
+ cdef Term term = Term()
+ if den is None:
+ try:
+ term.cterm = self.csolver.mkReal(str(val).encode())
+ except Exception as e:
+ raise ValueError("Expecting a number"
+ " or a string representing a number"
+ " but got: {}".format(val))
+ else:
+ if not isinstance(val, int) or not isinstance(den, int):
+ raise ValueError("Expecting integers when"
+ " constructing a rational"
+ " but got: {}".format((val, den)))
+ term.cterm = self.csolver.mkReal("{}/{}".format(val, den).encode())
+ return term
+
+ def mkRegexpEmpty(self):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkRegexpEmpty()
+ return term
+
+ def mkRegexpSigma(self):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkRegexpSigma()
+ return term
+
+ def mkEmptySet(self, Sort s):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkEmptySet(s.csort)
+ return term
+
+ def mkSepNil(self, Sort sort):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkSepNil(sort.csort)
+ return term
+
+ def mkString(self, str_or_vec):
+ cdef Term term = Term()
+ cdef vector[unsigned] v
+ if isinstance(str_or_vec, str):
+ term.cterm = self.csolver.mkString(<string &> str_or_vec.encode())
+ elif isinstance(str_or_vec, list):
+ for u in str_or_vec:
+ if not isinstance(u, int):
+ raise ValueError("List should contain ints but got: {}"
+ .format(str_or_vec))
+ v.push_back(<unsigned> u)
+ term.cterm = self.csolver.mkString(<const vector[unsigned]&> v)
+ else:
+ raise ValueError("Expected string or vector of ASCII codes"
+ " but got: {}".format(str_or_vec))
+ return term
+
+ def mkUniverseSet(self, Sort sort):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkUniverseSet(sort.csort)
+ return term
+
+ def mkBitVector(self, size_or_str, val = None):
+ cdef Term term = Term()
+ if isinstance(size_or_str, int):
+ if val is None:
+ term.cterm = self.csolver.mkBitVector(<int> size_or_str)
+ else:
+ term.cterm = self.csolver.mkBitVector(<int> size_or_str,
+ <int> val)
+ elif isinstance(size_or_str, str):
+ # handle default value
+ if val is None:
+ term.cterm = self.csolver.mkBitVector(
+ <const string &> size_or_str.encode())
+ else:
+ term.cterm = self.csolver.mkBitVector(
+ <const string &> size_or_str.encode(), <int> val)
+ else:
+ raise ValueError("Unexpected inputs {} to"
+ " mkBitVector".format((size_or_str, val)))
+ return term
+
+ def mkConstArray(self, Sort sort, Term val):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkConstArray(sort.csort, val.cterm)
+ return term
+
+ def mkPosInf(self, int exp, int sig):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkPosInf(exp, sig)
+ return term
+
+ def mkNegInf(self, int exp, int sig):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkNegInf(exp, sig)
+ return term
+
+ def mkNaN(self, int exp, int sig):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkNaN(exp, sig)
+ return term
+
+ def mkPosZero(self, int exp, int sig):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkPosZero(exp, sig)
+ return term
+
+ def mkNegZero(self, int exp, int sig):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkNegZero(exp, sig)
+ return term
+
+ def mkRoundingMode(self, RoundingMode rm):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkRoundingMode(<c_RoundingMode> rm.crm)
+ return term
+
+ def mkUninterpretedConst(self, Sort sort, int index):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkUninterpretedConst(sort.csort, index)
+ return term
+
+ def mkAbstractValue(self, index):
+ cdef Term term = Term()
+ try:
+ term.cterm = self.csolver.mkAbstractValue(str(index).encode())
+ except:
+ raise ValueError("mkAbstractValue expects a str representing a number"
+ " or an int, but got{}".format(index))
+ return term
+
+ def mkFloatingPoint(self, int exp, int sig, Term val):
+ cdef Term term = Term()
+ term.cterm = self.csolver.mkFloatingPoint(exp, sig, val.cterm)
+ return term
+
+ def mkConst(self, Sort sort, symbol=None):
+ cdef Term term = Term()
+ if symbol is None:
+ term.cterm = self.csolver.mkConst(sort.csort)
+ else:
+ term.cterm = self.csolver.mkConst(sort.csort,
+ (<str?> symbol).encode())
+ return term
+
+ def mkVar(self, Sort sort, symbol=None):
+ cdef Term term = Term()
+ if symbol is None:
+ term.cterm = self.csolver.mkVar(sort.csort)
+ else:
+ term.cterm = self.csolver.mkVar(sort.csort,
+ (<str?> symbol).encode())
+ return term
+
+ def mkDatatypeDecl(self, str name, sorts_or_bool=None, isCoDatatype=None):
+ cdef DatatypeDecl dd = DatatypeDecl()
+ cdef vector[c_Sort] v
+
+ # argument cases
+ if sorts_or_bool is None and isCoDatatype is None:
+ dd.cdd = self.csolver.mkDatatypeDecl(name.encode())
+ elif sorts_or_bool is not None and isCoDatatype is None:
+ if isinstance(sorts_or_bool, bool):
+ dd.cdd = self.csolver.mkDatatypeDecl(<const string &> name.encode(),
+ <bint> sorts_or_bool)
+ elif isinstance(sorts_or_bool, Sort):
+ dd.cdd = self.csolver.mkDatatypeDecl(<const string &> name.encode(),
+ (<Sort> sorts_or_bool).csort)
+ elif isinstance(sorts_or_bool, list):
+ for s in sorts_or_bool:
+ v.push_back((<Sort?> s).csort)
+ dd.cdd = self.csolver.mkDatatypeDecl(<const string &> name.encode(),
+ <const vector[c_Sort]&> v)
+ else:
+ raise ValueError("Unhandled second argument type {}"
+ .format(type(sorts_or_bool)))
+ elif sorts_or_bool is not None and isCoDatatype is not None:
+ if isinstance(sorts_or_bool, Sort):
+ dd.cdd = self.csolver.mkDatatypeDecl(<const string &> name.encode(),
+ (<Sort> sorts_or_bool).csort,
+ <bint> isCoDatatype)
+ elif isinstance(sorts_or_bool, list):
+ for s in sorts_or_bool:
+ v.push_back((<Sort?> s).csort)
+ dd.cdd = self.csolver.mkDatatypeDecl(<const string &> name.encode(),
+ <const vector[c_Sort]&> v,
+ <bint> isCoDatatype)
+ else:
+ raise ValueError("Unhandled second argument type {}"
+ .format(type(sorts_or_bool)))
+ else:
+ raise ValueError("Can't create DatatypeDecl with {}".format([type(a)
+ for a in [name,
+ sorts_or_bool,
+ isCoDatatype]]))
+
+ return dd
+
+ def simplify(self, Term t):
+ cdef Term term = Term()
+ term.cterm = self.csolver.simplify(t.cterm)
+ return term
+
+ def assertFormula(self, Term term):
+ self.csolver.assertFormula(term.cterm)
+
+ def checkSat(self):
+ cdef c_Result r = self.csolver.checkSat()
+ name = r.toString().decode()
+ explanation = ""
+ if r.isSatUnknown():
+ explanation = r.getUnknownExplanation().decode()
+ return Result(name, explanation)
+
+ @expand_list_arg(num_req_args=0)
+ def checkSatAssuming(self, *assumptions):
+ '''
+ Supports the following arguments:
+ Result checkSatAssuming(List[Term] assumptions)
+
+ where assumptions can also be comma-separated arguments of
+ type (boolean) Term
+ '''
+ cdef c_Result r
+ # used if assumptions is a list of terms
+ cdef vector[c_Term] v
+ for a in assumptions:
+ v.push_back((<Term?> a).cterm)
+ r = self.csolver.checkSatAssuming(<const vector[c_Term]&> v)
+ name = r.toString().decode()
+ explanation = ""
+ if r.isSatUnknown():
+ explanation = r.getUnknownExplanation().decode()
+ return Result(name, explanation)
+
+ def checkValid(self):
+ cdef c_Result r = self.csolver.checkValid()
+ name = r.toString().decode()
+ explanation = ""
+ if r.isValidUnknown():
+ explanation = r.getUnknownExplanation().decode()
+ return Result(name, explanation)
+
+ @expand_list_arg(num_req_args=0)
+ def checkValidAssuming(self, *assumptions):
+ '''
+ Supports the following arguments:
+ Result checkValidAssuming(List[Term] assumptions)
+
+ where assumptions can also be comma-separated arguments of
+ type (boolean) Term
+ '''
+ cdef c_Result r
+ # used if assumptions is a list of terms
+ cdef vector[c_Term] v
+ for a in assumptions:
+ v.push_back((<Term?> a).cterm)
+ r = self.csolver.checkValidAssuming(<const vector[c_Term]&> v)
+ name = r.toString().decode()
+ explanation = ""
+ if r.isValidUnknown():
+ explanation = r.getUnknownExplanation().decode()
+ return Result(name, explanation)
+
+ @expand_list_arg(num_req_args=1)
+ def declareDatatype(self, str symbol, *ctors):
+ '''
+ Supports the following arguments:
+ Sort declareDatatype(str symbol, List[Term] ctors)
+
+ where ctors can also be comma-separated arguments of
+ type DatatypeConstructorDecl
+ '''
+ cdef Sort sort = Sort()
+ cdef vector[c_DatatypeConstructorDecl] v
+
+ for c in ctors:
+ v.push_back((<DatatypeConstructorDecl?> c).cddc[0])
+ sort.csort = self.csolver.declareDatatype(symbol.encode(), v)
+ return sort
+
+ def declareFun(self, str symbol, list sorts, Sort sort):
+ cdef Term term = Term()
+ cdef vector[c_Sort] v
+ for s in sorts:
+ v.push_back((<Sort?> s).csort)
+ term.cterm = self.csolver.declareFun(symbol.encode(),
+ <const vector[c_Sort]&> v,
+ sort.csort)
+ return term
+
+ def declareSort(self, str symbol, int arity):
+ cdef Sort sort = Sort()
+ sort.csort = self.csolver.declareSort(symbol.encode(), arity)
+ return sort
+
+ def defineFun(self, sym_or_fun, bound_vars, sort_or_term, t=None):
+ '''
+ Supports two uses:
+ Term defineFun(str symbol, List[Term] bound_vars,
+ Sort sort, Term term)
+ Term defineFun(Term fun, List[Term] bound_vars,
+ Term term)
+ '''
+ cdef Term term = Term()
+ cdef vector[c_Term] v
+ for bv in bound_vars:
+ v.push_back((<Term?> bv).cterm)
+
+ if t is not None:
+ term.cterm = self.csolver.defineFun((<str?> sym_or_fun).encode(),
+ <const vector[c_Term] &> v,
+ (<Sort?> sort_or_term).csort,
+ (<Term?> t).cterm)
+ else:
+ term.cterm = self.csolver.defineFun((<Term?> sym_or_fun).cterm,
+ <const vector[c_Term]&> v,
+ (<Term?> sort_or_term).cterm)
+
+ return term
+
+ def defineFunRec(self, sym_or_fun, bound_vars, sort_or_term, t=None):
+ '''
+ Supports two uses:
+ Term defineFunRec(str symbol, List[Term] bound_vars,
+ Sort sort, Term term)
+ Term defineFunRec(Term fun, List[Term] bound_vars,
+ Term term)
+ '''
+ cdef Term term = Term()
+ cdef vector[c_Term] v
+ for bv in bound_vars:
+ v.push_back((<Term?> bv).cterm)
+
+ if t is not None:
+ term.cterm = self.csolver.defineFunRec((<str?> sym_or_fun).encode(),
+ <const vector[c_Term] &> v,
+ (<Sort?> sort_or_term).csort,
+ (<Term?> t).cterm)
+ else:
+ term.cterm = self.csolver.defineFunRec((<Term?> sym_or_fun).cterm,
+ <const vector[c_Term]&> v,
+ (<Term?> sort_or_term).cterm)
+
+ return term
+
+ def defineFunsRec(self, funs, bound_vars, terms):
+ cdef vector[c_Term] vf
+ cdef vector[vector[c_Term]] vbv
+ cdef vector[c_Term] vt
+
+ for f in funs:
+ vf.push_back((<Term?> f).cterm)
+
+ cdef vector[c_Term] temp
+ for v in bound_vars:
+ for t in v:
+ temp.push_back((<Term?> t).cterm)
+ vbv.push_back(temp)
+ temp.clear()
+
+ for t in terms:
+ vf.push_back((<Term?> t).cterm)
+
+ def getAssertions(self):
+ assertions = []
+ for a in self.csolver.getAssertions():
+ term = Term()
+ term.cterm = a
+ assertions.append(term)
+ return assertions
+
+ def getAssignment(self):
+ '''
+ Gives the assignment of *named* formulas as a dictionary.
+ '''
+ assignments = {}
+ for a in self.csolver.getAssignment():
+ varterm = Term()
+ valterm = Term()
+ varterm.cterm = a.first
+ valterm.cterm = a.second
+ assignments[varterm] = valterm
+ return assignments
+
+ def getInfo(self, str flag):
+ return self.csolver.getInfo(flag.encode())
+
+ def getOption(self, str option):
+ return self.csolver.getOption(option.encode())
+
+ def getUnsatAssumptions(self):
+ assumptions = []
+ for a in self.csolver.getUnsatAssumptions():
+ term = Term()
+ term.cterm = a
+ assumptions.append(term)
+ return assumptions
+
+ def getUnsatCore(self):
+ core = []
+ for a in self.csolver.getUnsatCore():
+ term = Term()
+ term.cterm = a
+ core.append(term)
+ return core
+
+ def getValue(self, Term t):
+ cdef Term term = Term()
+ term.cterm = self.csolver.getValue(t.cterm)
+ return term
+
+ def pop(self, nscopes=1):
+ self.csolver.pop(nscopes)
+
+ def printModel(self):
+ self.csolver.printModel(cout)
+
+ def push(self, nscopes=1):
+ self.csolver.push(nscopes)
+
+ def reset(self):
+ self.csolver.reset()
+
+ def resetAssertions(self):
+ self.csolver.resetAssertions()
+
+ def setInfo(self, str keyword, str value):
+ self.csolver.setInfo(keyword.encode(), value.encode())
+
+ def setLogic(self, str logic):
+ self.csolver.setLogic(logic.encode())
+
+ def setOption(self, str option, str value):
+ self.csolver.setOption(option.encode(), value.encode())
+
+
+cdef class Sort:
+ cdef c_Sort csort
+ def __cinit__(self):
+ # csort always set by Solver
+ pass
+
+ def __eq__(self, Sort other):
+ return self.csort == other.csort
+
+ def __ne__(self, Sort other):
+ return self.csort != other.csort
+
+ def __str__(self):
+ return self.csort.toString().decode()
+
+ def __repr__(self):
+ return self.csort.toString().decode()
+
+ def isBoolean(self):
+ return self.csort.isBoolean()
+
+ def isInteger(self):
+ return self.csort.isInteger()
+
+ def isReal(self):
+ return self.csort.isReal()
+
+ def isString(self):
+ return self.csort.isString()
+
+ def isRegExp(self):
+ return self.csort.isRegExp()
+
+ def isRoundingMode(self):
+ return self.csort.isRoundingMode()
+
+ def isBitVector(self):
+ return self.csort.isBitVector()
+
+ def isFloatingPoint(self):
+ return self.csort.isFloatingPoint()
+
+ def isDatatype(self):
+ return self.csort.isDatatype()
+
+ def isParametricDatatype(self):
+ return self.csort.isParametricDatatype()
+
+ def isFunction(self):
+ return self.csort.isFunction()
+
+ def isPredicate(self):
+ return self.csort.isPredicate()
+
+ def isTuple(self):
+ return self.csort.isTuple()
+
+ def isRecord(self):
+ return self.csort.isRecord()
+
+ def isArray(self):
+ return self.csort.isArray()
+
+ def isSet(self):
+ return self.csort.isSet()
+
+ def isUninterpretedSort(self):
+ return self.csort.isUninterpretedSort()
+
+ def isSortConstructor(self):
+ return self.csort.isSortConstructor()
+
+ def isFirstClass(self):
+ return self.csort.isFirstClass()
+
+ def isFunctionLike(self):
+ return self.csort.isFunctionLike()
+
+ def getDatatype(self):
+ cdef Datatype d = Datatype()
+ d.cd = self.csort.getDatatype()
+ return d
+
+ def instantiate(self, params):
+ cdef Sort sort = Sort()
+ cdef vector[c_Sort] v
+ for s in params:
+ v.push_back((<Sort?> s).csort)
+ sort.csort = self.csort.instantiate(v)
+ return sort
+
+ def isUninterpretedSortParameterized(self):
+ return self.csort.isUninterpretedSortParameterized()
+
+
+cdef class Term:
+ cdef c_Term cterm
+ def __cinit__(self):
+ # cterm always set in the Solver object
+ pass
+
+ def __eq__(self, Term other):
+ return self.cterm == other.cterm
+
+ def __ne__(self, Term other):
+ return self.cterm != other.cterm
+
+ def __str__(self):
+ return self.cterm.toString().decode()
+
+ def __repr__(self):
+ return self.cterm.toString().decode()
+
+ def __iter__(self):
+ for ci in self.cterm:
+ term = Term()
+ term.cterm = ci
+ yield term
+
+ def getKind(self):
+ return kind(<int> self.cterm.getKind())
+
+ def getSort(self):
+ cdef Sort sort = Sort()
+ sort.csort = self.cterm.getSort()
+ return sort
+
+ def hasOp(self):
+ return self.cterm.hasOp()
+
+ def getOp(self):
+ cdef Op op = Op()
+ op.cop = self.cterm.getOp()
+ return op
+
+ def isNull(self):
+ return self.cterm.isNull()
+
+ def notTerm(self):
+ cdef Term term = Term()
+ term.cterm = self.cterm.notTerm()
+ return term
+
+ def andTerm(self, Term t):
+ cdef Term term = Term()
+ term.cterm = self.cterm.andTerm((<Term> t).cterm)
+ return term
+
+ def orTerm(self, Term t):
+ cdef Term term = Term()
+ term.cterm = self.cterm.orTerm(t.cterm)
+ return term
+
+ def xorTerm(self, Term t):
+ cdef Term term = Term()
+ term.cterm = self.cterm.xorTerm(t.cterm)
+ return term
+
+ def eqTerm(self, Term t):
+ cdef Term term = Term()
+ term.cterm = self.cterm.eqTerm(t.cterm)
+ return term
+
+ def impTerm(self, Term t):
+ cdef Term term = Term()
+ term.cterm = self.cterm.impTerm(t.cterm)
+ return term
+
+ def iteTerm(self, Term then_t, Term else_t):
+ cdef Term term = Term()
+ term.cterm = self.cterm.iteTerm(then_t.cterm, else_t.cterm)
+ return term
+
+
+# Generate rounding modes
+cdef __rounding_modes = {
+ <int> ROUND_NEAREST_TIES_TO_EVEN: "RoundNearestTiesToEven",
+ <int> ROUND_TOWARD_POSITIVE: "RoundTowardPositive",
+ <int> ROUND_TOWARD_ZERO: "RoundTowardZero",
+ <int> ROUND_NEAREST_TIES_TO_AWAY: "RoundNearestTiesToAway"
+}
+
+mod_ref = sys.modules[__name__]
+for rm_int, name in __rounding_modes.items():
+ r = RoundingMode(rm_int)
+
+ if name in dir(mod_ref):
+ raise RuntimeError("Redefinition of Python RoundingMode %s."%name)
+
+ setattr(mod_ref, name, r)
+
+del r
+del rm_int
+del name
diff --git a/src/api/python/genkinds.py b/src/api/python/genkinds.py
new file mode 100755
index 000000000..1e22875e7
--- /dev/null
+++ b/src/api/python/genkinds.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+"""
+This script reads CVC4/src/api/cvc4cppkind.h and generates
+.pxd and .pxi files which declare all the CVC4 kinds and
+implement a Python wrapper for kinds, respectively. The
+default names are kinds.pxd / kinds.pxi, but the name is
+configurable from the command line with --kinds-file-prefix.
+
+The script is aware of the '#if 0' pattern and will ignore
+kinds declared between '#if 0' and '#endif'. It can also
+handle nested '#if 0' pairs.
+"""
+
+import argparse
+from collections import OrderedDict
+
+#################### Default Filenames ################
+DEFAULT_HEADER = 'cvc4cppkind.h'
+DEFAULT_PREFIX = 'kinds'
+
+##################### Useful Constants ################
+OCB = '{'
+CCB = '}'
+SC = ';'
+EQ = '='
+C = ','
+US = '_'
+NL = '\n'
+
+#################### Enum Declarations ################
+ENUM_START = 'enum CVC4_PUBLIC Kind'
+ENUM_END = CCB+SC
+
+################ Comments and Macro Tokens ############
+PYCOMMENT = '#'
+COMMENT = '//'
+BLOCK_COMMENT_BEGIN = '/*'
+BLOCK_COMMENT_END = '*/'
+MACRO_BLOCK_BEGIN = '#if 0'
+MACRO_BLOCK_END = '#endif'
+
+#################### Format Kind Names ################
+# special cases for format_name
+_IS = '_IS'
+# replacements after some preprocessing
+replacements = {
+ 'Bitvector' : 'BV',
+ 'Floatingpoint': 'FP'
+}
+
+####################### Code Blocks ###################
+CDEF_KIND = " cdef Kind "
+
+KINDS_PXD_TOP = \
+r"""cdef extern from "api/cvc4cppkind.h" namespace "CVC4::api":
+ cdef cppclass Kind:
+ pass
+
+
+# Kind declarations: See cvc4cppkind.h for additional information
+cdef extern from "api/cvc4cppkind.h" namespace "CVC4::api::Kind":
+"""
+
+KINDS_PXI_TOP = \
+r"""# distutils: language = c++
+# distutils: extra_compile_args = -std=c++11
+
+from cvc4kinds cimport *
+import sys
+from types import ModuleType
+
+cdef class kind:
+ cdef Kind k
+ cdef str name
+ def __cinit__(self, str name):
+ self.name = name
+
+ def __eq__(self, kind other):
+ return (<int> self.k) == (<int> other.k)
+
+ def __ne__(self, kind other):
+ return (<int> self.k) != (<int> other.k)
+
+ def __hash__(self):
+ return hash((<int> self.k, self.name))
+
+ def __str__(self):
+ return self.name
+
+ def __repr__(self):
+ return self.name
+
+ def as_int(self):
+ return <int> self.k
+
+# create a kinds submodule
+kinds = ModuleType('kinds')
+# fake a submodule for dotted imports, e.g. from pycvc4.kinds import *
+sys.modules['%s.%s'%(__name__, kinds.__name__)] = kinds
+kinds.__file__ = kinds.__name__ + ".py"
+"""
+
+KINDS_ATTR_TEMPLATE = \
+r"""
+cdef kind {name} = kind("{name}")
+{name}.k = {kind}
+setattr(kinds, "{name}", {name})
+"""
+
+class KindsParser:
+ tokenmap = {
+ BLOCK_COMMENT_BEGIN : BLOCK_COMMENT_END,
+ MACRO_BLOCK_BEGIN : MACRO_BLOCK_END
+ }
+
+ def __init__(self):
+ self.kinds = OrderedDict()
+ self.endtoken = None
+ self.endtoken_stack = []
+ self.in_kinds = False
+
+ def format_name(self, name):
+ '''
+ In the Python API, each Kind name is reformatted for easier use
+
+ The naming scheme is:
+ 1. capitalize the first letter of each word (delimited by underscores)
+ 2. make the rest of the letters lowercase
+ 3. replace Floatingpoint with FP
+ 4. replace Bitvector with BV
+
+ There is one exception:
+ FLOATINGPOINT_ISNAN --> FPIsNan
+
+ For every "_IS" in the name, there's an underscore added before step 1,
+ so that the word after "Is" is capitalized
+
+ Examples:
+ BITVECTOR_PLUS --> BVPlus
+ APPLY_SELECTOR --> ApplySelector
+ FLOATINGPOINT_ISNAN --> FPIsNan
+ SETMINUS --> Setminus
+
+ See the generated .pxi file for an explicit mapping
+ '''
+ name = name.replace(_IS, _IS+US)
+ words = [w.capitalize() for w in name.lower().split(US)]
+ name = "".join(words)
+
+ for og, new in replacements.items():
+ name = name.replace(og, new)
+
+ return name
+
+ def ignore_block(self, line):
+ '''
+ Returns a boolean telling self.parse whether to ignore a line or not.
+ It also updates all the necessary state to track comments and macro
+ blocks
+ '''
+
+ # check if entering block comment or macro block
+ for token in self.tokenmap:
+ if token in line:
+ if self.tokenmap[token] not in line:
+ if self.endtoken is not None:
+ self.endtoken_stack.append(self.endtoken)
+ self.endtoken = self.tokenmap[token]
+ return True
+
+ # check if currently in block comment or macro block
+ if self.endtoken is not None:
+ # check if reached the end of block comment or macro block
+ if self.endtoken in line:
+ if self.endtoken_stack:
+ self.endtoken = self.endtoken_stack.pop()
+ else:
+ self.endtoken =None
+ return True
+
+ return False
+
+ def parse(self, filename):
+ f = open(filename, "r")
+
+ for line in f.read().split(NL):
+ line = line.strip()
+ if COMMENT in line:
+ line = line[:line.find(COMMENT)]
+ if not line:
+ continue
+
+ if self.ignore_block(line):
+ continue
+
+ if ENUM_END in line:
+ self.in_kinds = False
+ break
+ elif self.in_kinds:
+ if line == OCB:
+ continue
+ if EQ in line:
+ line = line[:line.find(EQ)].strip()
+ elif C in line:
+ line = line[:line.find(C)].strip()
+ self.kinds[line] = self.format_name(line)
+ elif ENUM_START in line:
+ self.in_kinds = True
+ continue
+
+ f.close()
+
+ def gen_pxd(self, filename):
+ f = open(filename, "w")
+ f.write(KINDS_PXD_TOP)
+ # include the format_name docstring in the generated file
+ # could be helpful for users to see the formatting rules
+ for line in self.format_name.__doc__.split(NL):
+ f.write(PYCOMMENT)
+ if not line.isspace():
+ f.write(line)
+ f.write(NL)
+ for kind in self.kinds:
+ f.write(CDEF_KIND + kind + NL)
+ f.close()
+
+ def gen_pxi(self, filename):
+ f = open(filename, "w")
+ pxi = KINDS_PXI_TOP
+ for kind, name in self.kinds.items():
+ pxi += KINDS_ATTR_TEMPLATE.format(name=name, kind=kind)
+ f.write(pxi)
+ f.close()
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser('Read a kinds header file and generate a '
+ 'corresponding pxd file, with simplified kind names.')
+ parser.add_argument('--kinds-header', metavar='<KINDS_HEADER>',
+ help='The header file to read kinds from',
+ default=DEFAULT_HEADER)
+ parser.add_argument('--kinds-file-prefix', metavar='<KIND_FILE_PREFIX>',
+ help='The prefix for the .pxd and .pxi files to write '
+ 'the Cython declarations to.',
+ default=DEFAULT_PREFIX)
+
+ args = parser.parse_args()
+ kinds_header = args.kinds_header
+ kinds_file_prefix = args.kinds_file_prefix
+
+ kp = KindsParser()
+ kp.parse(kinds_header)
+
+ kp.gen_pxd(kinds_file_prefix + ".pxd")
+ kp.gen_pxi(kinds_file_prefix + ".pxi")
diff --git a/src/api/python/pycvc4.pyx b/src/api/python/pycvc4.pyx
new file mode 100644
index 000000000..40766341a
--- /dev/null
+++ b/src/api/python/pycvc4.pyx
@@ -0,0 +1,2 @@
+include "cvc4kinds.pxi"
+include "cvc4.pxi"
diff --git a/src/bindings/CMakeLists.txt b/src/bindings/CMakeLists.txt
index 135331e54..ac2fadd95 100644
--- a/src/bindings/CMakeLists.txt
+++ b/src/bindings/CMakeLists.txt
@@ -3,6 +3,12 @@ if(NOT ENABLE_SHARED)
endif()
find_package(SWIG 3.0.0 REQUIRED)
+if(POLICY CMP0078)
+ cmake_policy(SET CMP0078 OLD)
+endif()
+if(POLICY CMP0086)
+ cmake_policy(SET CMP0086 OLD)
+endif()
if(USE_PYTHON3 AND (SWIG_VERSION VERSION_EQUAL 3.0.8))
message(FATAL_ERROR
@@ -21,9 +27,9 @@ include_directories(
${PROJECT_SOURCE_DIR}/src/include
${CMAKE_BINARY_DIR}/src)
-if(BUILD_BINDINGS_JAVA)
+if(BUILD_SWIG_BINDINGS_JAVA)
add_subdirectory(java)
endif()
-if(BUILD_BINDINGS_PYTHON)
+if(BUILD_SWIG_BINDINGS_PYTHON)
add_subdirectory(python)
endif()
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 447d4909f..ad946e3ca 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -27,7 +27,7 @@ add_subdirectory(system EXCLUDE_FROM_ALL)
if(ENABLE_UNIT_TESTING)
add_subdirectory(unit EXCLUDE_FROM_ALL)
- if(BUILD_BINDINGS_JAVA)
+ if(BUILD_SWIG_BINDINGS_JAVA)
add_subdirectory(java)
endif()
endif()
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback