Browse Source
Merge commit '5934a428980fb37cf0994a90621cc1c084e1013a' as 'resources/3rdparty/sylvan'
Merge commit '5934a428980fb37cf0994a90621cc1c084e1013a' as 'resources/3rdparty/sylvan'
Former-commit-id: 61d8a2c331
main
68 changed files with 24092 additions and 0 deletions
-
43resources/3rdparty/sylvan/.gitignore
-
100resources/3rdparty/sylvan/.travis.yml
-
50resources/3rdparty/sylvan/CMakeLists.txt
-
202resources/3rdparty/sylvan/LICENSE
-
5resources/3rdparty/sylvan/Makefile.am
-
97resources/3rdparty/sylvan/README.md
-
20resources/3rdparty/sylvan/cmake/FindGMP.cmake
-
21resources/3rdparty/sylvan/configure.ac
-
32resources/3rdparty/sylvan/examples/CMakeLists.txt
-
68resources/3rdparty/sylvan/examples/getrss.c
-
26resources/3rdparty/sylvan/examples/getrss.h
-
499resources/3rdparty/sylvan/examples/lddmc.c
-
616resources/3rdparty/sylvan/examples/mc.c
-
121resources/3rdparty/sylvan/examples/simple.cpp
-
5resources/3rdparty/sylvan/m4/.gitignore
-
72resources/3rdparty/sylvan/m4/m4_ax_check_compile_flag.m4
-
BINresources/3rdparty/sylvan/models/at.5.8-rgs.bdd
-
BINresources/3rdparty/sylvan/models/at.6.8-rgs.bdd
-
BINresources/3rdparty/sylvan/models/at.7.8-rgs.bdd
-
BINresources/3rdparty/sylvan/models/blocks.2.ldd
-
BINresources/3rdparty/sylvan/models/blocks.4.ldd
-
BINresources/3rdparty/sylvan/models/collision.4.9-rgs.bdd
-
BINresources/3rdparty/sylvan/models/collision.5.9-rgs.bdd
-
BINresources/3rdparty/sylvan/models/schedule_world.2.8-rgs.bdd
-
BINresources/3rdparty/sylvan/models/schedule_world.3.8-rgs.bdd
-
81resources/3rdparty/sylvan/src/CMakeLists.txt
-
39resources/3rdparty/sylvan/src/Makefile.am
-
398resources/3rdparty/sylvan/src/avl.h
-
1045resources/3rdparty/sylvan/src/lace.c
-
2743resources/3rdparty/sylvan/src/lace.h
-
564resources/3rdparty/sylvan/src/llmsset.c
-
202resources/3rdparty/sylvan/src/llmsset.h
-
598resources/3rdparty/sylvan/src/refs.c
-
77resources/3rdparty/sylvan/src/refs.h
-
1067resources/3rdparty/sylvan/src/sha2.c
-
197resources/3rdparty/sylvan/src/sha2.h
-
245resources/3rdparty/sylvan/src/stats.c
-
262resources/3rdparty/sylvan/src/stats.h
-
182resources/3rdparty/sylvan/src/sylvan.h
-
2820resources/3rdparty/sylvan/src/sylvan_bdd.c
-
423resources/3rdparty/sylvan/src/sylvan_bdd.h
-
3resources/3rdparty/sylvan/src/sylvan_bdd_storm.h
-
220resources/3rdparty/sylvan/src/sylvan_cache.c
-
113resources/3rdparty/sylvan/src/sylvan_cache.h
-
304resources/3rdparty/sylvan/src/sylvan_common.c
-
85resources/3rdparty/sylvan/src/sylvan_common.h
-
30resources/3rdparty/sylvan/src/sylvan_config.h
-
595resources/3rdparty/sylvan/src/sylvan_gmp.c
-
182resources/3rdparty/sylvan/src/sylvan_gmp.h
-
2560resources/3rdparty/sylvan/src/sylvan_ldd.c
-
288resources/3rdparty/sylvan/src/sylvan_ldd.h
-
2542resources/3rdparty/sylvan/src/sylvan_mtbdd.c
-
608resources/3rdparty/sylvan/src/sylvan_mtbdd.h
-
128resources/3rdparty/sylvan/src/sylvan_mtbdd_int.h
-
514resources/3rdparty/sylvan/src/sylvan_mtbdd_storm.c
-
111resources/3rdparty/sylvan/src/sylvan_mtbdd_storm.h
-
1039resources/3rdparty/sylvan/src/sylvan_obj.cpp
-
855resources/3rdparty/sylvan/src/sylvan_obj.hpp
-
3resources/3rdparty/sylvan/src/sylvan_obj_bdd_storm.hpp
-
51resources/3rdparty/sylvan/src/sylvan_obj_mtbdd_storm.hpp
-
141resources/3rdparty/sylvan/src/sylvan_obj_storm.cpp
-
35resources/3rdparty/sylvan/src/tls.h
-
5resources/3rdparty/sylvan/test/.gitignore
-
15resources/3rdparty/sylvan/test/CMakeLists.txt
-
350resources/3rdparty/sylvan/test/main.c
-
13resources/3rdparty/sylvan/test/test_assert.h
-
331resources/3rdparty/sylvan/test/test_basic.c
-
51resources/3rdparty/sylvan/test/test_cxx.cpp
@ -0,0 +1,43 @@ |
|||||
|
# autotools |
||||
|
**/Makefile |
||||
|
/autom4te.cache/ |
||||
|
config.* |
||||
|
.dirstamp |
||||
|
aclocal.m4 |
||||
|
configure |
||||
|
m4/* |
||||
|
tools |
||||
|
Makefile.in |
||||
|
|
||||
|
# cmake |
||||
|
**/CMakeCache.txt |
||||
|
**/CMakeFiles |
||||
|
**/cmake_install.cmake |
||||
|
|
||||
|
# libtool |
||||
|
.deps/ |
||||
|
.libs/ |
||||
|
/libtool |
||||
|
|
||||
|
# object files |
||||
|
*.lo |
||||
|
*.o |
||||
|
*.la |
||||
|
|
||||
|
# output files |
||||
|
examples/mc |
||||
|
examples/lddmc |
||||
|
test/sylvan_test |
||||
|
test/test_cxx |
||||
|
src/libsylvan.a |
||||
|
|
||||
|
# MacOS file |
||||
|
.DS_Store |
||||
|
|
||||
|
# eclipse files |
||||
|
.cproject |
||||
|
.project |
||||
|
.settings |
||||
|
|
||||
|
# coverage output |
||||
|
coverage |
@ -0,0 +1,100 @@ |
|||||
|
sudo: false |
||||
|
|
||||
|
matrix: |
||||
|
include: |
||||
|
- os: linux |
||||
|
env: TOOLSET=gcc CC=gcc-4.7 CXX=g++-4.7 BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="ON" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["gcc-4.7", "g++-4.7", "libstd++-4.7-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test"] |
||||
|
- os: linux |
||||
|
env: TOOLSET=gcc CC=gcc-4.8 CXX=g++-4.8 BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="ON" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["gcc-4.8", "g++-4.8", "libstd++-4.8-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test"] |
||||
|
- os: linux |
||||
|
env: TOOLSET=gcc CC=gcc-4.9 CXX=g++-4.9 BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="ON" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["gcc-4.9", "g++-4.9", "libstd++-4.9-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test"] |
||||
|
- os: linux |
||||
|
env: TOOLSET=gcc CC=gcc-5 CXX=g++-5 BUILD_TYPE="Debug" HWLOC="OFF" SYLVAN_STATS="OFF" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["gcc-5", "g++-5", "libstd++-5-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test"] |
||||
|
- os: linux |
||||
|
env: TOOLSET=gcc CC=gcc-5 CXX=g++-5 BUILD_TYPE="Debug" HWLOC="ON" SYLVAN_STATS="ON" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["gcc-5", "g++-5", "libstd++-5-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test"] |
||||
|
- os: linux |
||||
|
env: TOOLSET=gcc CC=gcc-5 CXX=g++-5 BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="ON" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["gcc-5", "g++-5", "libstd++-5-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test"] |
||||
|
- os: linux |
||||
|
env: TOOLSET=gcc CC=gcc-5 CXX=g++-5 BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="OFF" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["gcc-5", "g++-5", "libstd++-5-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test"] |
||||
|
- os: linux |
||||
|
env: TOOLSET=gcc CC=gcc-5 CXX=g++-5 BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="ON" VARIANT="coverage" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["gcc-5", "g++-5", "libstd++-5-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test"] |
||||
|
- os: linux |
||||
|
env: TOOLSET=clang CC=/usr/local/clang-3.4/bin/clang CXX=/usr/local/clang-3.4/bin/clang++ BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="ON" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["clang-3.4", "libstdc++-5-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test", "llvm-toolchain-precise-3.4"] |
||||
|
- os: linux |
||||
|
env: TOOLSET=clang CC=clang-3.6 CXX=clang++-3.6 BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="ON" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["clang-3.6", "libstdc++-5-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test", "llvm-toolchain-precise-3.6"] |
||||
|
- os: linux |
||||
|
env: TOOLSET=clang CC=clang-3.7 CXX=clang++-3.7 BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="ON" |
||||
|
addons: |
||||
|
apt: |
||||
|
packages: ["clang-3.7", "libstdc++-5-dev", "libgmp-dev", "cmake", "libhwloc-dev"] |
||||
|
sources: ["ubuntu-toolchain-r-test", "llvm-toolchain-precise-3.7"] |
||||
|
- os: osx |
||||
|
env: TOOLSET=clang CC=clang CXX=clang++ BUILD_TYPE="Debug" HWLOC="ON" SYLVAN_STATS="ON" |
||||
|
- os: osx |
||||
|
env: TOOLSET=clang CC=clang CXX=clang++ BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="OFF" |
||||
|
- os: osx |
||||
|
env: TOOLSET=gcc CC=gcc-4.9 CXX=g++-4.9 BUILD_TYPE="Debug" HWLOC="ON" SYLVAN_STATS="OFF" |
||||
|
- os: osx |
||||
|
env: TOOLSET=gcc CC=gcc-4.9 CXX=g++-4.9 BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="OFF" |
||||
|
- os: osx |
||||
|
env: TOOLSET=gcc CC=gcc-5 CXX=g++-5 BUILD_TYPE="Debug" HWLOC="ON" SYLVAN_STATS="OFF" |
||||
|
- os: osx |
||||
|
env: TOOLSET=gcc CC=gcc-5 CXX=g++-5 BUILD_TYPE="Release" HWLOC="ON" SYLVAN_STATS="OFF" |
||||
|
|
||||
|
install: |
||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew install argp-standalone homebrew/science/hwloc; fi |
||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" && "$CC" == "gcc-5" ]]; then brew install homebrew/versions/gcc5; fi |
||||
|
|
||||
|
script: |
||||
|
- ${CC} --version |
||||
|
- ${CXX} --version |
||||
|
- cmake . -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DUSE_HWLOC=${HWLOC} -DSYLVAN_STATS=${SYLVAN_STATS} -DWITH_COVERAGE=${COVERAGE} |
||||
|
- make -j 2 |
||||
|
- make test |
||||
|
- examples/simple |
||||
|
- examples/mc models/schedule_world.2.8-rgs.bdd -w 2 | tee /dev/fd/2 | grep -q "1,570,340" |
||||
|
- examples/lddmc models/blocks.2.ldd -w 2 | tee /dev/fd/2 | grep -q "7057 states" |
||||
|
|
||||
|
notifications: |
||||
|
email: false |
||||
|
|
@ -0,0 +1,50 @@ |
|||||
|
cmake_minimum_required(VERSION 2.6) |
||||
|
project(sylvan C CXX) |
||||
|
enable_testing() |
||||
|
|
||||
|
set(CMAKE_C_FLAGS "-O3 -Wextra -Wall -Werror -fno-strict-aliasing -std=gnu11") |
||||
|
set(CMAKE_CXX_FLAGS "-O3 -Wextra -Wall -Werror -fno-strict-aliasing -Wno-deprecated-register -std=gnu++11") |
||||
|
|
||||
|
option(WITH_COVERAGE "Add generation of test coverage" OFF) |
||||
|
if(WITH_COVERAGE) |
||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g -coverage") |
||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -coverage") |
||||
|
|
||||
|
find_program(GCOV_PATH gcov) |
||||
|
find_program(LCOV_PATH lcov) |
||||
|
find_program(GENHTML_PATH genhtml) |
||||
|
|
||||
|
add_custom_target(coverage |
||||
|
# Cleanup lcov |
||||
|
${LCOV_PATH} --directory . --zerocounters |
||||
|
# Run tests |
||||
|
COMMAND make test |
||||
|
# Capture counters |
||||
|
COMMAND ${LCOV_PATH} --gcov-tool ${GCOV_PATH} --directory . --capture --output-file coverage.info |
||||
|
COMMAND ${LCOV_PATH} --remove coverage.info 'test/*' '/usr/*' 'examples/*' 'src/sylvan_mtbdd*' 'src/lace*' 'src/sylvan_ldd*' 'src/avl.h' 'src/sha2.c' --output-file coverage.info.cleaned |
||||
|
COMMAND ${GENHTML_PATH} -o coverage coverage.info.cleaned |
||||
|
COMMAND ${CMAKE_COMMAND} -E remove coverage.info coverage.info.cleaned |
||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
||||
|
) |
||||
|
endif() |
||||
|
|
||||
|
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) |
||||
|
find_package(GMP REQUIRED) |
||||
|
include_directories(${GMP_INCLUDE_DIR}) |
||||
|
include_directories(src) |
||||
|
|
||||
|
include_directories(src) |
||||
|
|
||||
|
add_subdirectory(src) |
||||
|
|
||||
|
option(SYLVAN_BUILD_TEST "Build test programs" ON) |
||||
|
|
||||
|
if(SYLVAN_BUILD_TEST) |
||||
|
add_subdirectory(test) |
||||
|
endif() |
||||
|
|
||||
|
option(SYLVAN_BUILD_EXAMPLES "Build example tools" OFF) |
||||
|
|
||||
|
if(SYLVAN_BUILD_EXAMPLES) |
||||
|
add_subdirectory(examples) |
||||
|
endif() |
@ -0,0 +1,202 @@ |
|||||
|
|
||||
|
Apache License |
||||
|
Version 2.0, January 2004 |
||||
|
http://www.apache.org/licenses/ |
||||
|
|
||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
|
||||
|
1. Definitions. |
||||
|
|
||||
|
"License" shall mean the terms and conditions for use, reproduction, |
||||
|
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
|
||||
|
"Licensor" shall mean the copyright owner or entity authorized by |
||||
|
the copyright owner that is granting the License. |
||||
|
|
||||
|
"Legal Entity" shall mean the union of the acting entity and all |
||||
|
other entities that control, are controlled by, or are under common |
||||
|
control with that entity. For the purposes of this definition, |
||||
|
"control" means (i) the power, direct or indirect, to cause the |
||||
|
direction or management of such entity, whether by contract or |
||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
|
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
|
||||
|
"You" (or "Your") shall mean an individual or Legal Entity |
||||
|
exercising permissions granted by this License. |
||||
|
|
||||
|
"Source" form shall mean the preferred form for making modifications, |
||||
|
including but not limited to software source code, documentation |
||||
|
source, and configuration files. |
||||
|
|
||||
|
"Object" form shall mean any form resulting from mechanical |
||||
|
transformation or translation of a Source form, including but |
||||
|
not limited to compiled object code, generated documentation, |
||||
|
and conversions to other media types. |
||||
|
|
||||
|
"Work" shall mean the work of authorship, whether in Source or |
||||
|
Object form, made available under the License, as indicated by a |
||||
|
copyright notice that is included in or attached to the work |
||||
|
(an example is provided in the Appendix below). |
||||
|
|
||||
|
"Derivative Works" shall mean any work, whether in Source or Object |
||||
|
form, that is based on (or derived from) the Work and for which the |
||||
|
editorial revisions, annotations, elaborations, or other modifications |
||||
|
represent, as a whole, an original work of authorship. For the purposes |
||||
|
of this License, Derivative Works shall not include works that remain |
||||
|
separable from, or merely link (or bind by name) to the interfaces of, |
||||
|
the Work and Derivative Works thereof. |
||||
|
|
||||
|
"Contribution" shall mean any work of authorship, including |
||||
|
the original version of the Work and any modifications or additions |
||||
|
to that Work or Derivative Works thereof, that is intentionally |
||||
|
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
|
or by an individual or Legal Entity authorized to submit on behalf of |
||||
|
the copyright owner. For the purposes of this definition, "submitted" |
||||
|
means any form of electronic, verbal, or written communication sent |
||||
|
to the Licensor or its representatives, including but not limited to |
||||
|
communication on electronic mailing lists, source code control systems, |
||||
|
and issue tracking systems that are managed by, or on behalf of, the |
||||
|
Licensor for the purpose of discussing and improving the Work, but |
||||
|
excluding communication that is conspicuously marked or otherwise |
||||
|
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
|
||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
|
on behalf of whom a Contribution has been received by Licensor and |
||||
|
subsequently incorporated within the Work. |
||||
|
|
||||
|
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
|
copyright license to reproduce, prepare Derivative Works of, |
||||
|
publicly display, publicly perform, sublicense, and distribute the |
||||
|
Work and such Derivative Works in Source or Object form. |
||||
|
|
||||
|
3. Grant of Patent License. Subject to the terms and conditions of |
||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
|
(except as stated in this section) patent license to make, have made, |
||||
|
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
|
where such license applies only to those patent claims licensable |
||||
|
by such Contributor that are necessarily infringed by their |
||||
|
Contribution(s) alone or by combination of their Contribution(s) |
||||
|
with the Work to which such Contribution(s) was submitted. If You |
||||
|
institute patent litigation against any entity (including a |
||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
|
or a Contribution incorporated within the Work constitutes direct |
||||
|
or contributory patent infringement, then any patent licenses |
||||
|
granted to You under this License for that Work shall terminate |
||||
|
as of the date such litigation is filed. |
||||
|
|
||||
|
4. Redistribution. You may reproduce and distribute copies of the |
||||
|
Work or Derivative Works thereof in any medium, with or without |
||||
|
modifications, and in Source or Object form, provided that You |
||||
|
meet the following conditions: |
||||
|
|
||||
|
(a) You must give any other recipients of the Work or |
||||
|
Derivative Works a copy of this License; and |
||||
|
|
||||
|
(b) You must cause any modified files to carry prominent notices |
||||
|
stating that You changed the files; and |
||||
|
|
||||
|
(c) You must retain, in the Source form of any Derivative Works |
||||
|
that You distribute, all copyright, patent, trademark, and |
||||
|
attribution notices from the Source form of the Work, |
||||
|
excluding those notices that do not pertain to any part of |
||||
|
the Derivative Works; and |
||||
|
|
||||
|
(d) If the Work includes a "NOTICE" text file as part of its |
||||
|
distribution, then any Derivative Works that You distribute must |
||||
|
include a readable copy of the attribution notices contained |
||||
|
within such NOTICE file, excluding those notices that do not |
||||
|
pertain to any part of the Derivative Works, in at least one |
||||
|
of the following places: within a NOTICE text file distributed |
||||
|
as part of the Derivative Works; within the Source form or |
||||
|
documentation, if provided along with the Derivative Works; or, |
||||
|
within a display generated by the Derivative Works, if and |
||||
|
wherever such third-party notices normally appear. The contents |
||||
|
of the NOTICE file are for informational purposes only and |
||||
|
do not modify the License. You may add Your own attribution |
||||
|
notices within Derivative Works that You distribute, alongside |
||||
|
or as an addendum to the NOTICE text from the Work, provided |
||||
|
that such additional attribution notices cannot be construed |
||||
|
as modifying the License. |
||||
|
|
||||
|
You may add Your own copyright statement to Your modifications and |
||||
|
may provide additional or different license terms and conditions |
||||
|
for use, reproduction, or distribution of Your modifications, or |
||||
|
for any such Derivative Works as a whole, provided Your use, |
||||
|
reproduction, and distribution of the Work otherwise complies with |
||||
|
the conditions stated in this License. |
||||
|
|
||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
|
any Contribution intentionally submitted for inclusion in the Work |
||||
|
by You to the Licensor shall be under the terms and conditions of |
||||
|
this License, without any additional terms or conditions. |
||||
|
Notwithstanding the above, nothing herein shall supersede or modify |
||||
|
the terms of any separate license agreement you may have executed |
||||
|
with Licensor regarding such Contributions. |
||||
|
|
||||
|
6. Trademarks. This License does not grant permission to use the trade |
||||
|
names, trademarks, service marks, or product names of the Licensor, |
||||
|
except as required for reasonable and customary use in describing the |
||||
|
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
|
||||
|
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
|
agreed to in writing, Licensor provides the Work (and each |
||||
|
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
|
implied, including, without limitation, any warranties or conditions |
||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
|
appropriateness of using or redistributing the Work and assume any |
||||
|
risks associated with Your exercise of permissions under this License. |
||||
|
|
||||
|
8. Limitation of Liability. In no event and under no legal theory, |
||||
|
whether in tort (including negligence), contract, or otherwise, |
||||
|
unless required by applicable law (such as deliberate and grossly |
||||
|
negligent acts) or agreed to in writing, shall any Contributor be |
||||
|
liable to You for damages, including any direct, indirect, special, |
||||
|
incidental, or consequential damages of any character arising as a |
||||
|
result of this License or out of the use or inability to use the |
||||
|
Work (including but not limited to damages for loss of goodwill, |
||||
|
work stoppage, computer failure or malfunction, or any and all |
||||
|
other commercial damages or losses), even if such Contributor |
||||
|
has been advised of the possibility of such damages. |
||||
|
|
||||
|
9. Accepting Warranty or Additional Liability. While redistributing |
||||
|
the Work or Derivative Works thereof, You may choose to offer, |
||||
|
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
|
or other liability obligations and/or rights consistent with this |
||||
|
License. However, in accepting such obligations, You may act only |
||||
|
on Your own behalf and on Your sole responsibility, not on behalf |
||||
|
of any other Contributor, and only if You agree to indemnify, |
||||
|
defend, and hold each Contributor harmless for any liability |
||||
|
incurred by, or claims asserted against, such Contributor by reason |
||||
|
of your accepting any such warranty or additional liability. |
||||
|
|
||||
|
END OF TERMS AND CONDITIONS |
||||
|
|
||||
|
APPENDIX: How to apply the Apache License to your work. |
||||
|
|
||||
|
To apply the Apache License to your work, attach the following |
||||
|
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
|
replaced with your own identifying information. (Don't include |
||||
|
the brackets!) The text should be enclosed in the appropriate |
||||
|
comment syntax for the file format. We also recommend that a |
||||
|
file or class name and description of purpose be included on the |
||||
|
same "printed page" as the copyright notice for easier |
||||
|
identification within third-party archives. |
||||
|
|
||||
|
Copyright [yyyy] [name of copyright owner] |
||||
|
|
||||
|
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. |
@ -0,0 +1,5 @@ |
|||||
|
ACLOCAL_AMFLAGS = -I m4 |
||||
|
|
||||
|
AM_CFLAGS = -g -O2 -Wall -Wextra -Werror -std=gnu11 |
||||
|
|
||||
|
SUBDIRS = src |
@ -0,0 +1,97 @@ |
|||||
|
Sylvan [](https://travis-ci.org/trolando/sylvan) |
||||
|
====== |
||||
|
Sylvan is a parallel (multi-core) BDD library in C. Sylvan allows both sequential and parallel BDD-based algorithms to benefit from parallelism. Sylvan uses the work-stealing framework Lace and a scalable lockless hashtable to implement scalable multi-core BDD operations. |
||||
|
|
||||
|
Sylvan is developed (© 2011-2016) by the [Formal Methods and Tools](http://fmt.ewi.utwente.nl/) group at the University of Twente as part of the MaDriD project, which is funded by NWO. Sylvan is licensed with the Apache 2.0 license. |
||||
|
|
||||
|
You can contact the main author of Sylvan at <t.vandijk@utwente.nl>. Please let us know if you use Sylvan in your projects. |
||||
|
|
||||
|
Sylvan is available at: https://github.com/utwente-fmt/sylvan |
||||
|
Java/JNI bindings: https://github.com/trolando/jsylvan |
||||
|
Haskell bindings: https://github.com/adamwalker/sylvan-haskell |
||||
|
|
||||
|
Publications |
||||
|
------------ |
||||
|
T. van Dijk and J. van de Pol (2015) [Sylvan: Multi-core Decision Diagrams](http://dx.doi.org/10.1007/978-3-662-46681-0_60). In: TACAS 2015, LNCS 9035. Springer. |
||||
|
|
||||
|
T. van Dijk and A.W. Laarman and J. van de Pol (2012) [Multi-Core BDD Operations for Symbolic Reachability](http://eprints.eemcs.utwente.nl/22166/). In: PDMC 2012, ENTCS. Elsevier. |
||||
|
|
||||
|
Usage |
||||
|
----- |
||||
|
Simple examples can be found in the `examples` subdirectory. The file `simple.cpp` contains a toy program that |
||||
|
uses the C++ objects to perform basic BDD manipulation. |
||||
|
The `mc.c` and `lddmc.c` programs are more advanced examples of symbolic model checking (with example models in the `models` subdirectory). |
||||
|
|
||||
|
Sylvan depends on the [work-stealing framework Lace](http://fmt.ewi.utwente.nl/tools/lace) for its implementation. Lace is embedded in the Sylvan distribution. |
||||
|
To use Sylvan, Lace must be initialized first. |
||||
|
For more details, see the comments in `src/sylvan.h`. |
||||
|
|
||||
|
### Basic functionality |
||||
|
|
||||
|
To create new BDDs, you can use: |
||||
|
- `sylvan_true`: representation of constant `true`. |
||||
|
- `sylvan_false`: representation of constant `false`. |
||||
|
- `sylvan_ithvar(var)`: representation of literal <var> (negated: `sylvan_nithvar(var)`) |
||||
|
|
||||
|
To follow the BDD edges and obtain the variable at the root of a BDD, you can use: |
||||
|
- `sylvan_var(bdd)`: obtain variable of the root node of <bdd> - requires that <bdd> is not constant `true` or `false`. |
||||
|
- `sylvan_high(bdd)`: follow high edge of <bdd>. |
||||
|
- `sylvan_low(bdd)`: follow low edge of <bdd>. |
||||
|
|
||||
|
You need to manually reference BDDs that you want to keep during garbage collection: |
||||
|
- `sylvan_ref(bdd)`: add reference to <bdd>. |
||||
|
- `sylvan_deref(bdd)`: remove reference to <bdd>. |
||||
|
- `sylvan_protect(bddptr)`: add a pointer reference to the BDD variable <bddptr> |
||||
|
- `sylvan_unprotect(bddptr)`: remove a pointer reference to the BDD variable <bddptr> |
||||
|
|
||||
|
It is recommended to use `sylvan_protect` and `sylvan_unprotect`. |
||||
|
The C++ objects handle this automatically. |
||||
|
|
||||
|
The following 'primitives' are implemented: |
||||
|
- `sylvan_not(bdd)`: negation of <bdd>. |
||||
|
- `sylvan_ite(a,b,c)`: calculate 'if <a> then <b> else <c>'. |
||||
|
- `sylvan_and(a, b)`: calculate a and b |
||||
|
- `sylvan_or(a, b)`: calculate a or b |
||||
|
- `sylvan_nand(a, b)`: calculate not (a and b) |
||||
|
- `sylvan_nor(a, b)`: calculate not (a or b) |
||||
|
- `sylvan_imp(a, b)`: calculate a implies b |
||||
|
- `sylvan_invimp(a, b)`: calculate implies a |
||||
|
- `sylvan_xor(a, b)`: calculate a xor b |
||||
|
- `sylvan_equiv(a, b)`: calculate a = b |
||||
|
- `sylvan_diff(a, b)`: calculate a and not b |
||||
|
- `sylvan_less(a, b)`: calculate b and not a |
||||
|
- `sylvan_exists(bdd, vars)`: existential quantification of <bdd> with respect to variables <vars>. Here, <vars> is a conjunction of literals. |
||||
|
- `sylvan_forall(bdd, vars)`: universal quantification of <bdd> with respect to variables <vars>. Here, <vars> is a conjunction of literals. |
||||
|
|
||||
|
### Other BDD operations |
||||
|
|
||||
|
See `src/sylvan_bdd.h`, `src/sylvan_mtbdd.h` and `src/sylvan_ldd.h` for other implemented operations. |
||||
|
See `src/sylvan_obj.hpp` for the C++ interface. |
||||
|
|
||||
|
### Garbage collection |
||||
|
|
||||
|
Garbage collection is triggered when trying to insert a new node and no new bucket can be found within a reasonable upper bound. |
||||
|
Garbage collection is stop-the-world and all workers must cooperate on garbage collection. (Beware of deadlocks if you use Sylvan operations in critical sections!) |
||||
|
- `sylvan_gc()`: manually trigger garbage collection. |
||||
|
- `sylvan_gc_enable()`: enable garbage collection. |
||||
|
- `sylvan_gc_disable()`: disable garbage collection. |
||||
|
|
||||
|
### Table resizing |
||||
|
|
||||
|
During garbage collection, it is possible to resize the nodes table and the cache. |
||||
|
Sylvan provides two default implementations: an agressive version that resizes every time garbage collection is performed, |
||||
|
and a less agressive version that only resizes when at least half the table is full. |
||||
|
This can be configured in `src/sylvan_config.h` |
||||
|
It is not possible to decrease the size of the nodes table and the cache. |
||||
|
|
||||
|
### Dynamic reordering |
||||
|
|
||||
|
Dynamic reordening is currently not supported. |
||||
|
For now, we suggest users find a good static variable ordering. |
||||
|
|
||||
|
Troubleshooting |
||||
|
--------------- |
||||
|
Sylvan may require a larger than normal program stack. You may need to increase the program stack size on your system using `ulimit -s`. Segmentation faults on large computations typically indicate a program stack overflow. |
||||
|
|
||||
|
### I am getting the error "unable to allocate memory: ...!" |
||||
|
Sylvan allocates virtual memory using mmap. If you specify a combined size for the cache and node table larger than your actual available memory you may need to set `vm.overcommit_memory` to `1`. E.g. `echo 1 > /proc/sys/vm/overcommit_memory`. You can make this setting permanent with `echo "vm.overcommit_memory = 1" > /etc/sysctl.d/99-sylvan.conf`. You can verify the setting with `cat /proc/sys/vm/overcommit_memory`. It should report `1`. |
@ -0,0 +1,20 @@ |
|||||
|
FIND_PATH(GMP_INCLUDE_DIR |
||||
|
gmp.h ) |
||||
|
|
||||
|
FIND_LIBRARY(GMP_LIBRARIES |
||||
|
NAMES gmp |
||||
|
HINTS /usr/local/lib ) |
||||
|
|
||||
|
IF (GMP_INCLUDE_DIR AND GMP_LIBRARIES) |
||||
|
SET(GMP_FOUND TRUE) |
||||
|
ENDIF (GMP_INCLUDE_DIR AND GMP_LIBRARIES) |
||||
|
|
||||
|
IF (GMP_FOUND) |
||||
|
IF (NOT GMP_FIND_QUIETLY) |
||||
|
MESSAGE(STATUS "Found GMP: ${GMP_LIBRARIES}") |
||||
|
ENDIF (NOT GMP_FIND_QUIETLY) |
||||
|
ELSE (GMP_FOUND) |
||||
|
IF (GMP_FIND_REQUIRED) |
||||
|
MESSAGE(FATAL_ERROR "Could not find GMP") |
||||
|
ENDIF (GMP_FIND_REQUIRED) |
||||
|
ENDIF (GMP_FOUND) |
@ -0,0 +1,21 @@ |
|||||
|
AC_PREREQ([2.60]) |
||||
|
AC_INIT([sylvan], [1.0]) |
||||
|
AC_CONFIG_MACRO_DIR([m4]) |
||||
|
AC_CONFIG_AUX_DIR([tools]) |
||||
|
AM_INIT_AUTOMAKE([foreign]) |
||||
|
|
||||
|
AC_PROG_CC |
||||
|
AX_CHECK_COMPILE_FLAG([-std=c11],,[AC_MSG_FAILURE([no acceptable C11 compiler found.])]) |
||||
|
AC_PROG_CXX |
||||
|
LT_INIT |
||||
|
|
||||
|
AC_CHECKING([for any suitable hwloc installation]) |
||||
|
AC_CHECK_LIB([hwloc], [hwloc_topology_init], [AC_CHECK_HEADER([hwloc.h], [hwloc=yes])]) |
||||
|
AM_CONDITIONAL([HAVE_LIBHWLOC], [test "$hwloc" = "yes"]) |
||||
|
|
||||
|
AC_CANONICAL_HOST |
||||
|
AM_CONDITIONAL([DARWIN], [case $host_os in darwin*) true;; *) false;; esac]) |
||||
|
# test x$(uname) == "xDarwin"]) |
||||
|
|
||||
|
AC_CONFIG_FILES([Makefile src/Makefile]) |
||||
|
AC_OUTPUT |
@ -0,0 +1,32 @@ |
|||||
|
cmake_minimum_required(VERSION 2.6) |
||||
|
project(sylvan C CXX) |
||||
|
|
||||
|
include_directories(.) |
||||
|
|
||||
|
add_executable(mc mc.c getrss.h getrss.c) |
||||
|
target_link_libraries(mc sylvan) |
||||
|
|
||||
|
add_executable(lddmc lddmc.c getrss.h getrss.c) |
||||
|
target_link_libraries(lddmc sylvan) |
||||
|
|
||||
|
add_executable(simple simple.cpp) |
||||
|
target_link_libraries(simple sylvan stdc++) |
||||
|
|
||||
|
include(CheckIncludeFiles) |
||||
|
check_include_files("gperftools/profiler.h" HAVE_PROFILER) |
||||
|
|
||||
|
if(HAVE_PROFILER) |
||||
|
set_target_properties(mc PROPERTIES COMPILE_DEFINITIONS "HAVE_PROFILER") |
||||
|
target_link_libraries(mc profiler) |
||||
|
|
||||
|
set_target_properties(lddmc PROPERTIES COMPILE_DEFINITIONS "HAVE_PROFILER") |
||||
|
target_link_libraries(lddmc profiler) |
||||
|
endif() |
||||
|
|
||||
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") |
||||
|
# add argp library for OSX |
||||
|
target_link_libraries(mc argp) |
||||
|
target_link_libraries(lddmc argp) |
||||
|
endif() |
||||
|
|
||||
|
|
@ -0,0 +1,68 @@ |
|||||
|
/* |
||||
|
* Author: David Robert Nadeau |
||||
|
* Site: http://NadeauSoftware.com/ |
||||
|
* License: Creative Commons Attribution 3.0 Unported License |
||||
|
* http://creativecommons.org/licenses/by/3.0/deed.en_US |
||||
|
*/ |
||||
|
|
||||
|
/* |
||||
|
* Modified by Tom van Dijk to remove WIN32 and solaris code |
||||
|
*/ |
||||
|
|
||||
|
#if defined(__APPLE__) && defined(__MACH__) |
||||
|
#include <unistd.h> |
||||
|
#include <sys/resource.h> |
||||
|
#include <mach/mach.h> |
||||
|
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) |
||||
|
#include <unistd.h> |
||||
|
#include <sys/resource.h> |
||||
|
#include <stdio.h> |
||||
|
#else |
||||
|
#error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS." |
||||
|
#endif |
||||
|
|
||||
|
/** |
||||
|
* Returns the peak (maximum so far) resident set size (physical |
||||
|
* memory use) measured in bytes, or zero if the value cannot be |
||||
|
* determined on this OS. |
||||
|
*/ |
||||
|
size_t |
||||
|
getPeakRSS() |
||||
|
{ |
||||
|
struct rusage rusage; |
||||
|
getrusage(RUSAGE_SELF, &rusage); |
||||
|
#if defined(__APPLE__) && defined(__MACH__) |
||||
|
return (size_t)rusage.ru_maxrss; |
||||
|
#else |
||||
|
return (size_t)(rusage.ru_maxrss * 1024L); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the current resident set size (physical memory use) measured |
||||
|
* in bytes, or zero if the value cannot be determined on this OS. |
||||
|
*/ |
||||
|
size_t |
||||
|
getCurrentRSS() |
||||
|
{ |
||||
|
#if defined(__APPLE__) && defined(__MACH__) |
||||
|
/* OSX ------------------------------------------------------ */ |
||||
|
struct mach_task_basic_info info; |
||||
|
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; |
||||
|
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) != KERN_SUCCESS) |
||||
|
return (size_t)0L; /* Can't access? */ |
||||
|
return (size_t)info.resident_size; |
||||
|
#else |
||||
|
/* Linux ---------------------------------------------------- */ |
||||
|
long rss = 0L; |
||||
|
FILE *fp = NULL; |
||||
|
if ((fp = fopen("/proc/self/statm", "r")) == NULL) |
||||
|
return (size_t)0L; /* Can't open? */ |
||||
|
if (fscanf(fp, "%*s%ld", &rss) != 1) { |
||||
|
fclose(fp); |
||||
|
return (size_t)0L; /* Can't read? */ |
||||
|
} |
||||
|
fclose(fp); |
||||
|
return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE); |
||||
|
#endif |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
#ifndef GETRSS_H |
||||
|
#define GETRSS_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
/** |
||||
|
* Returns the peak (maximum so far) resident set size (physical |
||||
|
* memory use) measured in bytes, or zero if the value cannot be |
||||
|
* determined on this OS. |
||||
|
*/ |
||||
|
size_t getPeakRSS(); |
||||
|
|
||||
|
/** |
||||
|
* Returns the current resident set size (physical memory use) measured |
||||
|
* in bytes, or zero if the value cannot be determined on this OS. |
||||
|
*/ |
||||
|
size_t getCurrentRSS(); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
|
||||
|
#endif |
@ -0,0 +1,499 @@ |
|||||
|
#include <argp.h> |
||||
|
#include <assert.h> |
||||
|
#include <inttypes.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <sys/time.h> |
||||
|
|
||||
|
#ifdef HAVE_PROFILER |
||||
|
#include <gperftools/profiler.h> |
||||
|
#endif |
||||
|
|
||||
|
#include <getrss.h> |
||||
|
#include <sylvan.h> |
||||
|
#include <llmsset.h> |
||||
|
|
||||
|
/* Configuration */ |
||||
|
static int report_levels = 0; // report states at start of every level |
||||
|
static int report_table = 0; // report table size at end of every level |
||||
|
static int strategy = 1; // set to 1 = use PAR strategy; set to 0 = use BFS strategy |
||||
|
static int check_deadlocks = 0; // set to 1 to check for deadlocks |
||||
|
static int print_transition_matrix = 1; // print transition relation matrix |
||||
|
static int workers = 0; // autodetect |
||||
|
static char* model_filename = NULL; // filename of model |
||||
|
#ifdef HAVE_PROFILER |
||||
|
static char* profile_filename = NULL; // filename for profiling |
||||
|
#endif |
||||
|
|
||||
|
/* argp configuration */ |
||||
|
static struct argp_option options[] = |
||||
|
{ |
||||
|
{"workers", 'w', "<workers>", 0, "Number of workers (default=0: autodetect)", 0}, |
||||
|
{"strategy", 's', "<bfs|par|sat>", 0, "Strategy for reachability (default=par)", 0}, |
||||
|
#ifdef HAVE_PROFILER |
||||
|
{"profiler", 'p', "<filename>", 0, "Filename for profiling", 0}, |
||||
|
#endif |
||||
|
{"deadlocks", 3, 0, 0, "Check for deadlocks", 1}, |
||||
|
{"count-states", 1, 0, 0, "Report #states at each level", 1}, |
||||
|
{"count-table", 2, 0, 0, "Report table usage at each level", 1}, |
||||
|
{0, 0, 0, 0, 0, 0} |
||||
|
}; |
||||
|
static error_t |
||||
|
parse_opt(int key, char *arg, struct argp_state *state) |
||||
|
{ |
||||
|
switch (key) { |
||||
|
case 'w': |
||||
|
workers = atoi(arg); |
||||
|
break; |
||||
|
case 's': |
||||
|
if (strcmp(arg, "bfs")==0) strategy = 0; |
||||
|
else if (strcmp(arg, "par")==0) strategy = 1; |
||||
|
else if (strcmp(arg, "sat")==0) strategy = 2; |
||||
|
else argp_usage(state); |
||||
|
break; |
||||
|
case 3: |
||||
|
check_deadlocks = 1; |
||||
|
break; |
||||
|
case 1: |
||||
|
report_levels = 1; |
||||
|
break; |
||||
|
case 2: |
||||
|
report_table = 1; |
||||
|
break; |
||||
|
#ifdef HAVE_PROFILER |
||||
|
case 'p': |
||||
|
profile_filename = arg; |
||||
|
break; |
||||
|
#endif |
||||
|
case ARGP_KEY_ARG: |
||||
|
if (state->arg_num >= 1) argp_usage(state); |
||||
|
model_filename = arg; |
||||
|
break; |
||||
|
case ARGP_KEY_END: |
||||
|
if (state->arg_num < 1) argp_usage(state); |
||||
|
break; |
||||
|
default: |
||||
|
return ARGP_ERR_UNKNOWN; |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
static struct argp argp = { options, parse_opt, "<model>", 0, 0, 0, 0 }; |
||||
|
|
||||
|
/* Globals */ |
||||
|
typedef struct set |
||||
|
{ |
||||
|
MDD mdd; |
||||
|
MDD proj; |
||||
|
int size; |
||||
|
} *set_t; |
||||
|
|
||||
|
typedef struct relation |
||||
|
{ |
||||
|
MDD mdd; |
||||
|
MDD meta; |
||||
|
int size; |
||||
|
} *rel_t; |
||||
|
|
||||
|
static size_t vector_size; // size of vector |
||||
|
static int next_count; // number of partitions of the transition relation |
||||
|
static rel_t *next; // each partition of the transition relation |
||||
|
|
||||
|
#define Abort(...) { fprintf(stderr, __VA_ARGS__); exit(-1); } |
||||
|
|
||||
|
/* Load a set from file */ |
||||
|
static set_t |
||||
|
set_load(FILE* f) |
||||
|
{ |
||||
|
lddmc_serialize_fromfile(f); |
||||
|
|
||||
|
size_t mdd; |
||||
|
size_t proj; |
||||
|
int size; |
||||
|
|
||||
|
if (fread(&mdd, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n"); |
||||
|
if (fread(&proj, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n"); |
||||
|
if (fread(&size, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n"); |
||||
|
|
||||
|
LACE_ME; |
||||
|
|
||||
|
set_t set = (set_t)malloc(sizeof(struct set)); |
||||
|
set->mdd = lddmc_ref(lddmc_serialize_get_reversed(mdd)); |
||||
|
set->proj = lddmc_ref(lddmc_serialize_get_reversed(proj)); |
||||
|
set->size = size; |
||||
|
|
||||
|
return set; |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
calculate_size(MDD meta) |
||||
|
{ |
||||
|
int result = 0; |
||||
|
uint32_t val = lddmc_getvalue(meta); |
||||
|
while (val != (uint32_t)-1) { |
||||
|
if (val != 0) result += 1; |
||||
|
meta = lddmc_follow(meta, val); |
||||
|
assert(meta != lddmc_true && meta != lddmc_false); |
||||
|
val = lddmc_getvalue(meta); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/* Load a relation from file */ |
||||
|
static rel_t |
||||
|
rel_load(FILE* f) |
||||
|
{ |
||||
|
lddmc_serialize_fromfile(f); |
||||
|
|
||||
|
size_t mdd; |
||||
|
size_t meta; |
||||
|
|
||||
|
if (fread(&mdd, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n"); |
||||
|
if (fread(&meta, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n"); |
||||
|
|
||||
|
LACE_ME; |
||||
|
|
||||
|
rel_t rel = (rel_t)malloc(sizeof(struct relation)); |
||||
|
rel->mdd = lddmc_ref(lddmc_serialize_get_reversed(mdd)); |
||||
|
rel->meta = lddmc_ref(lddmc_serialize_get_reversed(meta)); |
||||
|
rel->size = calculate_size(rel->meta); |
||||
|
|
||||
|
return rel; |
||||
|
} |
||||
|
|
||||
|
static void |
||||
|
print_example(MDD example) |
||||
|
{ |
||||
|
if (example != lddmc_false) { |
||||
|
LACE_ME; |
||||
|
uint32_t vec[vector_size]; |
||||
|
lddmc_sat_one(example, vec, vector_size); |
||||
|
|
||||
|
size_t i; |
||||
|
printf("["); |
||||
|
for (i=0; i<vector_size; i++) { |
||||
|
if (i>0) printf(","); |
||||
|
printf("%" PRIu32, vec[i]); |
||||
|
} |
||||
|
printf("]"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static void |
||||
|
print_matrix(size_t size, MDD meta) |
||||
|
{ |
||||
|
if (size == 0) return; |
||||
|
uint32_t val = lddmc_getvalue(meta); |
||||
|
if (val == 1) { |
||||
|
printf("+"); |
||||
|
print_matrix(size-1, lddmc_follow(lddmc_follow(meta, 1), 2)); |
||||
|
} else { |
||||
|
if (val == (uint32_t)-1) printf("-"); |
||||
|
else if (val == 0) printf("-"); |
||||
|
else if (val == 3) printf("r"); |
||||
|
else if (val == 4) printf("w"); |
||||
|
print_matrix(size-1, lddmc_follow(meta, val)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static char* |
||||
|
to_h(double size, char *buf) |
||||
|
{ |
||||
|
const char* units[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; |
||||
|
int i = 0; |
||||
|
for (;size>1024;size/=1024) i++; |
||||
|
sprintf(buf, "%.*f %s", i, size, units[i]); |
||||
|
return buf; |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
get_first(MDD meta) |
||||
|
{ |
||||
|
uint32_t val = lddmc_getvalue(meta); |
||||
|
if (val != 0) return 0; |
||||
|
return 1+get_first(lddmc_follow(meta, val)); |
||||
|
} |
||||
|
|
||||
|
/* Straight-forward implementation of parallel reduction */ |
||||
|
TASK_5(MDD, go_par, MDD, cur, MDD, visited, size_t, from, size_t, len, MDD*, deadlocks) |
||||
|
{ |
||||
|
if (len == 1) { |
||||
|
// Calculate NEW successors (not in visited) |
||||
|
MDD succ = lddmc_ref(lddmc_relprod(cur, next[from]->mdd, next[from]->meta)); |
||||
|
if (deadlocks) { |
||||
|
// check which MDDs in deadlocks do not have a successor in this relation |
||||
|
MDD anc = lddmc_ref(lddmc_relprev(succ, next[from]->mdd, next[from]->meta, cur)); |
||||
|
*deadlocks = lddmc_ref(lddmc_minus(*deadlocks, anc)); |
||||
|
lddmc_deref(anc); |
||||
|
} |
||||
|
MDD result = lddmc_ref(lddmc_minus(succ, visited)); |
||||
|
lddmc_deref(succ); |
||||
|
return result; |
||||
|
} else { |
||||
|
MDD deadlocks_left; |
||||
|
MDD deadlocks_right; |
||||
|
if (deadlocks) { |
||||
|
deadlocks_left = *deadlocks; |
||||
|
deadlocks_right = *deadlocks; |
||||
|
} |
||||
|
|
||||
|
// Recursively calculate left+right |
||||
|
SPAWN(go_par, cur, visited, from, (len+1)/2, deadlocks ? &deadlocks_left: NULL); |
||||
|
MDD right = CALL(go_par, cur, visited, from+(len+1)/2, len/2, deadlocks ? &deadlocks_right : NULL); |
||||
|
MDD left = SYNC(go_par); |
||||
|
|
||||
|
// Merge results of left+right |
||||
|
MDD result = lddmc_ref(lddmc_union(left, right)); |
||||
|
lddmc_deref(left); |
||||
|
lddmc_deref(right); |
||||
|
|
||||
|
if (deadlocks) { |
||||
|
*deadlocks = lddmc_ref(lddmc_intersect(deadlocks_left, deadlocks_right)); |
||||
|
lddmc_deref(deadlocks_left); |
||||
|
lddmc_deref(deadlocks_right); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* PAR strategy, parallel strategy (operations called in parallel *and* parallelized by Sylvan) */ |
||||
|
VOID_TASK_1(par, set_t, set) |
||||
|
{ |
||||
|
MDD visited = set->mdd; |
||||
|
MDD new = lddmc_ref(visited); |
||||
|
size_t counter = 1; |
||||
|
do { |
||||
|
char buf[32]; |
||||
|
to_h(getCurrentRSS(), buf); |
||||
|
printf("Memory usage: %s\n", buf); |
||||
|
printf("Level %zu... ", counter++); |
||||
|
if (report_levels) { |
||||
|
printf("%zu states... ", (size_t)lddmc_satcount_cached(visited)); |
||||
|
} |
||||
|
fflush(stdout); |
||||
|
|
||||
|
// calculate successors in parallel |
||||
|
MDD cur = new; |
||||
|
MDD deadlocks = cur; |
||||
|
new = CALL(go_par, cur, visited, 0, next_count, check_deadlocks ? &deadlocks : NULL); |
||||
|
lddmc_deref(cur); |
||||
|
|
||||
|
if (check_deadlocks) { |
||||
|
printf("found %zu deadlock states... ", (size_t)lddmc_satcount_cached(deadlocks)); |
||||
|
if (deadlocks != lddmc_false) { |
||||
|
printf("example: "); |
||||
|
print_example(deadlocks); |
||||
|
printf("... "); |
||||
|
check_deadlocks = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// visited = visited + new |
||||
|
MDD old_visited = visited; |
||||
|
visited = lddmc_ref(lddmc_union(visited, new)); |
||||
|
lddmc_deref(old_visited); |
||||
|
|
||||
|
if (report_table) { |
||||
|
size_t filled, total; |
||||
|
sylvan_table_usage(&filled, &total); |
||||
|
printf("done, table: %0.1f%% full (%zu nodes).\n", 100.0*(double)filled/total, filled); |
||||
|
} else { |
||||
|
printf("done.\n"); |
||||
|
} |
||||
|
} while (new != lddmc_false); |
||||
|
lddmc_deref(new); |
||||
|
set->mdd = visited; |
||||
|
} |
||||
|
|
||||
|
/* Sequential version of merge-reduction */ |
||||
|
TASK_5(MDD, go_bfs, MDD, cur, MDD, visited, size_t, from, size_t, len, MDD*, deadlocks) |
||||
|
{ |
||||
|
if (len == 1) { |
||||
|
// Calculate NEW successors (not in visited) |
||||
|
MDD succ = lddmc_ref(lddmc_relprod(cur, next[from]->mdd, next[from]->meta)); |
||||
|
if (deadlocks) { |
||||
|
// check which MDDs in deadlocks do not have a successor in this relation |
||||
|
MDD anc = lddmc_ref(lddmc_relprev(succ, next[from]->mdd, next[from]->meta, cur)); |
||||
|
*deadlocks = lddmc_ref(lddmc_minus(*deadlocks, anc)); |
||||
|
lddmc_deref(anc); |
||||
|
} |
||||
|
MDD result = lddmc_ref(lddmc_minus(succ, visited)); |
||||
|
lddmc_deref(succ); |
||||
|
return result; |
||||
|
} else { |
||||
|
MDD deadlocks_left; |
||||
|
MDD deadlocks_right; |
||||
|
if (deadlocks) { |
||||
|
deadlocks_left = *deadlocks; |
||||
|
deadlocks_right = *deadlocks; |
||||
|
} |
||||
|
|
||||
|
// Recursively calculate left+right |
||||
|
MDD left = CALL(go_bfs, cur, visited, from, (len+1)/2, deadlocks ? &deadlocks_left : NULL); |
||||
|
MDD right = CALL(go_bfs, cur, visited, from+(len+1)/2, len/2, deadlocks ? &deadlocks_right : NULL); |
||||
|
|
||||
|
// Merge results of left+right |
||||
|
MDD result = lddmc_ref(lddmc_union(left, right)); |
||||
|
lddmc_deref(left); |
||||
|
lddmc_deref(right); |
||||
|
|
||||
|
if (deadlocks) { |
||||
|
*deadlocks = lddmc_ref(lddmc_intersect(deadlocks_left, deadlocks_right)); |
||||
|
lddmc_deref(deadlocks_left); |
||||
|
lddmc_deref(deadlocks_right); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* BFS strategy, sequential strategy (but operations are parallelized by Sylvan) */ |
||||
|
VOID_TASK_1(bfs, set_t, set) |
||||
|
{ |
||||
|
MDD visited = set->mdd; |
||||
|
MDD new = lddmc_ref(visited); |
||||
|
size_t counter = 1; |
||||
|
do { |
||||
|
char buf[32]; |
||||
|
to_h(getCurrentRSS(), buf); |
||||
|
printf("Memory usage: %s\n", buf); |
||||
|
printf("Level %zu... ", counter++); |
||||
|
if (report_levels) { |
||||
|
printf("%zu states... ", (size_t)lddmc_satcount_cached(visited)); |
||||
|
} |
||||
|
fflush(stdout); |
||||
|
|
||||
|
MDD cur = new; |
||||
|
MDD deadlocks = cur; |
||||
|
new = CALL(go_bfs, cur, visited, 0, next_count, check_deadlocks ? &deadlocks : NULL); |
||||
|
lddmc_deref(cur); |
||||
|
|
||||
|
if (check_deadlocks) { |
||||
|
printf("found %zu deadlock states... ", (size_t)lddmc_satcount_cached(deadlocks)); |
||||
|
if (deadlocks != lddmc_false) { |
||||
|
printf("example: "); |
||||
|
print_example(deadlocks); |
||||
|
printf("... "); |
||||
|
check_deadlocks = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// visited = visited + new |
||||
|
MDD old_visited = visited; |
||||
|
visited = lddmc_ref(lddmc_union(visited, new)); |
||||
|
lddmc_deref(old_visited); |
||||
|
|
||||
|
if (report_table) { |
||||
|
size_t filled, total; |
||||
|
sylvan_table_usage(&filled, &total); |
||||
|
printf("done, table: %0.1f%% full (%zu nodes).\n", 100.0*(double)filled/total, filled); |
||||
|
} else { |
||||
|
printf("done.\n"); |
||||
|
} |
||||
|
} while (new != lddmc_false); |
||||
|
lddmc_deref(new); |
||||
|
set->mdd = visited; |
||||
|
} |
||||
|
|
||||
|
/* Obtain current wallclock time */ |
||||
|
static double |
||||
|
wctime() |
||||
|
{ |
||||
|
struct timeval tv; |
||||
|
gettimeofday(&tv, NULL); |
||||
|
return (tv.tv_sec + 1E-6 * tv.tv_usec); |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
main(int argc, char **argv) |
||||
|
{ |
||||
|
argp_parse(&argp, argc, argv, 0, 0, 0); |
||||
|
|
||||
|
FILE *f = fopen(model_filename, "r"); |
||||
|
if (f == NULL) { |
||||
|
fprintf(stderr, "Cannot open file '%s'!\n", model_filename); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
// Init Lace |
||||
|
lace_init(workers, 1000000); // auto-detect number of workers, use a 1,000,000 size task queue |
||||
|
lace_startup(0, NULL, NULL); // auto-detect program stack, do not use a callback for startup |
||||
|
|
||||
|
// Init Sylvan LDDmc |
||||
|
// Nodes table size: 24 bytes * 2**N_nodes |
||||
|
// Cache table size: 36 bytes * 2**N_cache |
||||
|
// With: N_nodes=25, N_cache=24: 1.3 GB memory |
||||
|
sylvan_init_package(1LL<<21, 1LL<<27, 1LL<<20, 1LL<<26); |
||||
|
sylvan_init_ldd(); |
||||
|
|
||||
|
// Read and report domain info (integers per vector and bits per integer) |
||||
|
if (fread(&vector_size, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n"); |
||||
|
|
||||
|
printf("Vector size: %zu\n", vector_size); |
||||
|
|
||||
|
// Read initial state |
||||
|
printf("Loading initial state... "); |
||||
|
fflush(stdout); |
||||
|
set_t states = set_load(f); |
||||
|
printf("done.\n"); |
||||
|
|
||||
|
// Read transitions |
||||
|
if (fread(&next_count, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n"); |
||||
|
next = (rel_t*)malloc(sizeof(rel_t) * next_count); |
||||
|
|
||||
|
printf("Loading transition relations... "); |
||||
|
fflush(stdout); |
||||
|
int i; |
||||
|
for (i=0; i<next_count; i++) { |
||||
|
next[i] = rel_load(f); |
||||
|
printf("%d, ", i); |
||||
|
fflush(stdout); |
||||
|
} |
||||
|
fclose(f); |
||||
|
printf("done.\n"); |
||||
|
|
||||
|
// Report statistics |
||||
|
printf("Read file '%s'\n", argv[1]); |
||||
|
printf("%zu integers per state, %d transition groups\n", vector_size, next_count); |
||||
|
printf("MDD nodes:\n"); |
||||
|
printf("Initial states: %zu MDD nodes\n", lddmc_nodecount(states->mdd)); |
||||
|
for (i=0; i<next_count; i++) { |
||||
|
printf("Transition %d: %zu MDD nodes\n", i, lddmc_nodecount(next[i]->mdd)); |
||||
|
} |
||||
|
|
||||
|
if (print_transition_matrix) { |
||||
|
for (i=0; i<next_count; i++) { |
||||
|
print_matrix(vector_size, next[i]->meta); |
||||
|
printf(" (%d)\n", get_first(next[i]->meta)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
LACE_ME; |
||||
|
|
||||
|
#ifdef HAVE_PROFILER |
||||
|
if (profile_filename != NULL) ProfilerStart(profile_filename); |
||||
|
#endif |
||||
|
if (strategy == 1) { |
||||
|
double t1 = wctime(); |
||||
|
CALL(par, states); |
||||
|
double t2 = wctime(); |
||||
|
printf("PAR Time: %f\n", t2-t1); |
||||
|
} else { |
||||
|
double t1 = wctime(); |
||||
|
CALL(bfs, states); |
||||
|
double t2 = wctime(); |
||||
|
printf("BFS Time: %f\n", t2-t1); |
||||
|
} |
||||
|
#ifdef HAVE_PROFILER |
||||
|
if (profile_filename != NULL) ProfilerStop(); |
||||
|
#endif |
||||
|
|
||||
|
// Now we just have states |
||||
|
printf("Final states: %zu states\n", (size_t)lddmc_satcount_cached(states->mdd)); |
||||
|
printf("Final states: %zu MDD nodes\n", lddmc_nodecount(states->mdd)); |
||||
|
|
||||
|
sylvan_stats_report(stdout, 1); |
||||
|
|
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,616 @@ |
|||||
|
#include <argp.h> |
||||
|
#include <inttypes.h> |
||||
|
#include <locale.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <sys/time.h> |
||||
|
|
||||
|
#ifdef HAVE_PROFILER |
||||
|
#include <gperftools/profiler.h> |
||||
|
#endif |
||||
|
|
||||
|
#include <sylvan.h> |
||||
|
#include <llmsset.h> |
||||
|
|
||||
|
/* Configuration */ |
||||
|
static int report_levels = 0; // report states at end of every level |
||||
|
static int report_table = 0; // report table size at end of every level |
||||
|
static int report_nodes = 0; // report number of nodes of BDDs |
||||
|
static int strategy = 1; // set to 1 = use PAR strategy; set to 0 = use BFS strategy |
||||
|
static int check_deadlocks = 0; // set to 1 to check for deadlocks |
||||
|
static int merge_relations = 0; // merge relations to 1 relation |
||||
|
static int print_transition_matrix = 0; // print transition relation matrix |
||||
|
static int workers = 0; // autodetect |
||||
|
static char* model_filename = NULL; // filename of model |
||||
|
#ifdef HAVE_PROFILER |
||||
|
static char* profile_filename = NULL; // filename for profiling |
||||
|
#endif |
||||
|
|
||||
|
/* argp configuration */ |
||||
|
static struct argp_option options[] = |
||||
|
{ |
||||
|
{"workers", 'w', "<workers>", 0, "Number of workers (default=0: autodetect)", 0}, |
||||
|
{"strategy", 's', "<bfs|par|sat>", 0, "Strategy for reachability (default=par)", 0}, |
||||
|
#ifdef HAVE_PROFILER |
||||
|
{"profiler", 'p', "<filename>", 0, "Filename for profiling", 0}, |
||||
|
#endif |
||||
|
{"deadlocks", 3, 0, 0, "Check for deadlocks", 1}, |
||||
|
{"count-nodes", 5, 0, 0, "Report #nodes for BDDs", 1}, |
||||
|
{"count-states", 1, 0, 0, "Report #states at each level", 1}, |
||||
|
{"count-table", 2, 0, 0, "Report table usage at each level", 1}, |
||||
|
{"merge-relations", 6, 0, 0, "Merge transition relations into one transition relation", 1}, |
||||
|
{"print-matrix", 4, 0, 0, "Print transition matrix", 1}, |
||||
|
{0, 0, 0, 0, 0, 0} |
||||
|
}; |
||||
|
static error_t |
||||
|
parse_opt(int key, char *arg, struct argp_state *state) |
||||
|
{ |
||||
|
switch (key) { |
||||
|
case 'w': |
||||
|
workers = atoi(arg); |
||||
|
break; |
||||
|
case 's': |
||||
|
if (strcmp(arg, "bfs")==0) strategy = 0; |
||||
|
else if (strcmp(arg, "par")==0) strategy = 1; |
||||
|
else if (strcmp(arg, "sat")==0) strategy = 2; |
||||
|
else argp_usage(state); |
||||
|
break; |
||||
|
case 4: |
||||
|
print_transition_matrix = 1; |
||||
|
break; |
||||
|
case 3: |
||||
|
check_deadlocks = 1; |
||||
|
break; |
||||
|
case 1: |
||||
|
report_levels = 1; |
||||
|
break; |
||||
|
case 2: |
||||
|
report_table = 1; |
||||
|
break; |
||||
|
case 6: |
||||
|
merge_relations = 1; |
||||
|
break; |
||||
|
#ifdef HAVE_PROFILER |
||||
|
case 'p': |
||||
|
profile_filename = arg; |
||||
|
break; |
||||
|
#endif |
||||
|
case ARGP_KEY_ARG: |
||||
|
if (state->arg_num >= 1) argp_usage(state); |
||||
|
model_filename = arg; |
||||
|
break; |
||||
|
case ARGP_KEY_END: |
||||
|
if (state->arg_num < 1) argp_usage(state); |
||||
|
break; |
||||
|
default: |
||||
|
return ARGP_ERR_UNKNOWN; |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
static struct argp argp = { options, parse_opt, "<model>", 0, 0, 0, 0 }; |
||||
|
|
||||
|
/* Globals */ |
||||
|
typedef struct set |
||||
|
{ |
||||
|
BDD bdd; |
||||
|
BDD variables; // all variables in the set (used by satcount) |
||||
|
} *set_t; |
||||
|
|
||||
|
typedef struct relation |
||||
|
{ |
||||
|
BDD bdd; |
||||
|
BDD variables; // all variables in the relation (used by relprod) |
||||
|
} *rel_t; |
||||
|
|
||||
|
static int vector_size; // size of vector |
||||
|
static int statebits, actionbits; // number of bits for state, number of bits for action |
||||
|
static int bits_per_integer; // number of bits per integer in the vector |
||||
|
static int next_count; // number of partitions of the transition relation |
||||
|
static rel_t *next; // each partition of the transition relation |
||||
|
|
||||
|
/* Obtain current wallclock time */ |
||||
|
static double |
||||
|
wctime() |
||||
|
{ |
||||
|
struct timeval tv; |
||||
|
gettimeofday(&tv, NULL); |
||||
|
return (tv.tv_sec + 1E-6 * tv.tv_usec); |
||||
|
} |
||||
|
|
||||
|
static double t_start; |
||||
|
#define INFO(s, ...) fprintf(stdout, "[% 8.2f] " s, wctime()-t_start, ##__VA_ARGS__) |
||||
|
#define Abort(...) { fprintf(stderr, __VA_ARGS__); exit(-1); } |
||||
|
|
||||
|
/* Load a set from file */ |
||||
|
#define set_load(f) CALL(set_load, f) |
||||
|
TASK_1(set_t, set_load, FILE*, f) |
||||
|
{ |
||||
|
sylvan_serialize_fromfile(f); |
||||
|
|
||||
|
size_t set_bdd, set_vector_size, set_state_vars; |
||||
|
if ((fread(&set_bdd, sizeof(size_t), 1, f) != 1) || |
||||
|
(fread(&set_vector_size, sizeof(size_t), 1, f) != 1) || |
||||
|
(fread(&set_state_vars, sizeof(size_t), 1, f) != 1)) { |
||||
|
Abort("Invalid input file!\n"); |
||||
|
} |
||||
|
|
||||
|
set_t set = (set_t)malloc(sizeof(struct set)); |
||||
|
set->bdd = sylvan_serialize_get_reversed(set_bdd); |
||||
|
set->variables = sylvan_support(sylvan_serialize_get_reversed(set_state_vars)); |
||||
|
|
||||
|
sylvan_protect(&set->bdd); |
||||
|
sylvan_protect(&set->variables); |
||||
|
|
||||
|
return set; |
||||
|
} |
||||
|
|
||||
|
/* Load a relation from file */ |
||||
|
#define rel_load(f) CALL(rel_load, f) |
||||
|
TASK_1(rel_t, rel_load, FILE*, f) |
||||
|
{ |
||||
|
sylvan_serialize_fromfile(f); |
||||
|
|
||||
|
size_t rel_bdd, rel_vars; |
||||
|
if ((fread(&rel_bdd, sizeof(size_t), 1, f) != 1) || |
||||
|
(fread(&rel_vars, sizeof(size_t), 1, f) != 1)) { |
||||
|
Abort("Invalid input file!\n"); |
||||
|
} |
||||
|
|
||||
|
rel_t rel = (rel_t)malloc(sizeof(struct relation)); |
||||
|
rel->bdd = sylvan_serialize_get_reversed(rel_bdd); |
||||
|
rel->variables = sylvan_support(sylvan_serialize_get_reversed(rel_vars)); |
||||
|
|
||||
|
sylvan_protect(&rel->bdd); |
||||
|
sylvan_protect(&rel->variables); |
||||
|
|
||||
|
return rel; |
||||
|
} |
||||
|
|
||||
|
#define print_example(example, variables) CALL(print_example, example, variables) |
||||
|
VOID_TASK_2(print_example, BDD, example, BDDSET, variables) |
||||
|
{ |
||||
|
uint8_t str[vector_size * bits_per_integer]; |
||||
|
|
||||
|
if (example != sylvan_false) { |
||||
|
sylvan_sat_one(example, variables, str); |
||||
|
printf("["); |
||||
|
for (int i=0; i<vector_size; i++) { |
||||
|
uint32_t res = 0; |
||||
|
for (int j=0; j<bits_per_integer; j++) { |
||||
|
if (str[bits_per_integer*i+j] == 1) res++; |
||||
|
res<<=1; |
||||
|
} |
||||
|
if (i>0) printf(","); |
||||
|
printf("%" PRIu32, res); |
||||
|
} |
||||
|
printf("]"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Straight-forward implementation of parallel reduction */ |
||||
|
TASK_5(BDD, go_par, BDD, cur, BDD, visited, size_t, from, size_t, len, BDD*, deadlocks) |
||||
|
{ |
||||
|
if (len == 1) { |
||||
|
// Calculate NEW successors (not in visited) |
||||
|
BDD succ = sylvan_relnext(cur, next[from]->bdd, next[from]->variables); |
||||
|
bdd_refs_push(succ); |
||||
|
if (deadlocks) { |
||||
|
// check which BDDs in deadlocks do not have a successor in this relation |
||||
|
BDD anc = sylvan_relprev(next[from]->bdd, succ, next[from]->variables); |
||||
|
bdd_refs_push(anc); |
||||
|
*deadlocks = sylvan_diff(*deadlocks, anc); |
||||
|
bdd_refs_pop(1); |
||||
|
} |
||||
|
BDD result = sylvan_diff(succ, visited); |
||||
|
bdd_refs_pop(1); |
||||
|
return result; |
||||
|
} else { |
||||
|
BDD deadlocks_left; |
||||
|
BDD deadlocks_right; |
||||
|
if (deadlocks) { |
||||
|
deadlocks_left = *deadlocks; |
||||
|
deadlocks_right = *deadlocks; |
||||
|
sylvan_protect(&deadlocks_left); |
||||
|
sylvan_protect(&deadlocks_right); |
||||
|
} |
||||
|
|
||||
|
// Recursively calculate left+right |
||||
|
bdd_refs_spawn(SPAWN(go_par, cur, visited, from, (len+1)/2, deadlocks ? &deadlocks_left: NULL)); |
||||
|
BDD right = bdd_refs_push(CALL(go_par, cur, visited, from+(len+1)/2, len/2, deadlocks ? &deadlocks_right : NULL)); |
||||
|
BDD left = bdd_refs_push(bdd_refs_sync(SYNC(go_par))); |
||||
|
|
||||
|
// Merge results of left+right |
||||
|
BDD result = sylvan_or(left, right); |
||||
|
bdd_refs_pop(2); |
||||
|
|
||||
|
if (deadlocks) { |
||||
|
bdd_refs_push(result); |
||||
|
*deadlocks = sylvan_and(deadlocks_left, deadlocks_right); |
||||
|
sylvan_unprotect(&deadlocks_left); |
||||
|
sylvan_unprotect(&deadlocks_right); |
||||
|
bdd_refs_pop(1); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* PAR strategy, parallel strategy (operations called in parallel *and* parallelized by Sylvan) */ |
||||
|
VOID_TASK_1(par, set_t, set) |
||||
|
{ |
||||
|
BDD visited = set->bdd; |
||||
|
BDD next_level = visited; |
||||
|
BDD cur_level = sylvan_false; |
||||
|
BDD deadlocks = sylvan_false; |
||||
|
|
||||
|
sylvan_protect(&visited); |
||||
|
sylvan_protect(&next_level); |
||||
|
sylvan_protect(&cur_level); |
||||
|
sylvan_protect(&deadlocks); |
||||
|
|
||||
|
int iteration = 1; |
||||
|
do { |
||||
|
// calculate successors in parallel |
||||
|
cur_level = next_level; |
||||
|
deadlocks = cur_level; |
||||
|
|
||||
|
next_level = CALL(go_par, cur_level, visited, 0, next_count, check_deadlocks ? &deadlocks : NULL); |
||||
|
|
||||
|
if (check_deadlocks && deadlocks != sylvan_false) { |
||||
|
INFO("Found %'0.0f deadlock states... ", sylvan_satcount(deadlocks, set->variables)); |
||||
|
if (deadlocks != sylvan_false) { |
||||
|
printf("example: "); |
||||
|
print_example(deadlocks, set->variables); |
||||
|
check_deadlocks = 0; |
||||
|
} |
||||
|
printf("\n"); |
||||
|
} |
||||
|
|
||||
|
// visited = visited + new |
||||
|
visited = sylvan_or(visited, next_level); |
||||
|
|
||||
|
if (report_table && report_levels) { |
||||
|
size_t filled, total; |
||||
|
sylvan_table_usage(&filled, &total); |
||||
|
INFO("Level %d done, %'0.0f states explored, table: %0.1f%% full (%'zu nodes)\n", |
||||
|
iteration, sylvan_satcount(visited, set->variables), |
||||
|
100.0*(double)filled/total, filled); |
||||
|
} else if (report_table) { |
||||
|
size_t filled, total; |
||||
|
sylvan_table_usage(&filled, &total); |
||||
|
INFO("Level %d done, table: %0.1f%% full (%'zu nodes)\n", |
||||
|
iteration, |
||||
|
100.0*(double)filled/total, filled); |
||||
|
} else if (report_levels) { |
||||
|
INFO("Level %d done, %'0.0f states explored\n", iteration, sylvan_satcount(visited, set->variables)); |
||||
|
} else { |
||||
|
INFO("Level %d done\n", iteration); |
||||
|
} |
||||
|
iteration++; |
||||
|
} while (next_level != sylvan_false); |
||||
|
|
||||
|
set->bdd = visited; |
||||
|
|
||||
|
sylvan_unprotect(&visited); |
||||
|
sylvan_unprotect(&next_level); |
||||
|
sylvan_unprotect(&cur_level); |
||||
|
sylvan_unprotect(&deadlocks); |
||||
|
} |
||||
|
|
||||
|
/* Sequential version of merge-reduction */ |
||||
|
TASK_5(BDD, go_bfs, BDD, cur, BDD, visited, size_t, from, size_t, len, BDD*, deadlocks) |
||||
|
{ |
||||
|
if (len == 1) { |
||||
|
// Calculate NEW successors (not in visited) |
||||
|
BDD succ = sylvan_relnext(cur, next[from]->bdd, next[from]->variables); |
||||
|
bdd_refs_push(succ); |
||||
|
if (deadlocks) { |
||||
|
// check which BDDs in deadlocks do not have a successor in this relation |
||||
|
BDD anc = sylvan_relprev(next[from]->bdd, succ, next[from]->variables); |
||||
|
bdd_refs_push(anc); |
||||
|
*deadlocks = sylvan_diff(*deadlocks, anc); |
||||
|
bdd_refs_pop(1); |
||||
|
} |
||||
|
BDD result = sylvan_diff(succ, visited); |
||||
|
bdd_refs_pop(1); |
||||
|
return result; |
||||
|
} else { |
||||
|
BDD deadlocks_left; |
||||
|
BDD deadlocks_right; |
||||
|
if (deadlocks) { |
||||
|
deadlocks_left = *deadlocks; |
||||
|
deadlocks_right = *deadlocks; |
||||
|
sylvan_protect(&deadlocks_left); |
||||
|
sylvan_protect(&deadlocks_right); |
||||
|
} |
||||
|
|
||||
|
// Recursively calculate left+right |
||||
|
BDD left = CALL(go_bfs, cur, visited, from, (len+1)/2, deadlocks ? &deadlocks_left : NULL); |
||||
|
bdd_refs_push(left); |
||||
|
BDD right = CALL(go_bfs, cur, visited, from+(len+1)/2, len/2, deadlocks ? &deadlocks_right : NULL); |
||||
|
bdd_refs_push(right); |
||||
|
|
||||
|
// Merge results of left+right |
||||
|
BDD result = sylvan_or(left, right); |
||||
|
bdd_refs_pop(2); |
||||
|
|
||||
|
if (deadlocks) { |
||||
|
bdd_refs_push(result); |
||||
|
*deadlocks = sylvan_and(deadlocks_left, deadlocks_right); |
||||
|
sylvan_unprotect(&deadlocks_left); |
||||
|
sylvan_unprotect(&deadlocks_right); |
||||
|
bdd_refs_pop(1); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* BFS strategy, sequential strategy (but operations are parallelized by Sylvan) */ |
||||
|
VOID_TASK_1(bfs, set_t, set) |
||||
|
{ |
||||
|
BDD visited = set->bdd; |
||||
|
BDD next_level = visited; |
||||
|
BDD cur_level = sylvan_false; |
||||
|
BDD deadlocks = sylvan_false; |
||||
|
|
||||
|
sylvan_protect(&visited); |
||||
|
sylvan_protect(&next_level); |
||||
|
sylvan_protect(&cur_level); |
||||
|
sylvan_protect(&deadlocks); |
||||
|
|
||||
|
int iteration = 1; |
||||
|
do { |
||||
|
// calculate successors in parallel |
||||
|
cur_level = next_level; |
||||
|
deadlocks = cur_level; |
||||
|
|
||||
|
next_level = CALL(go_bfs, cur_level, visited, 0, next_count, check_deadlocks ? &deadlocks : NULL); |
||||
|
|
||||
|
if (check_deadlocks && deadlocks != sylvan_false) { |
||||
|
INFO("Found %'0.0f deadlock states... ", sylvan_satcount(deadlocks, set->variables)); |
||||
|
if (deadlocks != sylvan_false) { |
||||
|
printf("example: "); |
||||
|
print_example(deadlocks, set->variables); |
||||
|
check_deadlocks = 0; |
||||
|
} |
||||
|
printf("\n"); |
||||
|
} |
||||
|
|
||||
|
// visited = visited + new |
||||
|
visited = sylvan_or(visited, next_level); |
||||
|
|
||||
|
if (report_table && report_levels) { |
||||
|
size_t filled, total; |
||||
|
sylvan_table_usage(&filled, &total); |
||||
|
INFO("Level %d done, %'0.0f states explored, table: %0.1f%% full (%'zu nodes)\n", |
||||
|
iteration, sylvan_satcount(visited, set->variables), |
||||
|
100.0*(double)filled/total, filled); |
||||
|
} else if (report_table) { |
||||
|
size_t filled, total; |
||||
|
sylvan_table_usage(&filled, &total); |
||||
|
INFO("Level %d done, table: %0.1f%% full (%'zu nodes)\n", |
||||
|
iteration, |
||||
|
100.0*(double)filled/total, filled); |
||||
|
} else if (report_levels) { |
||||
|
INFO("Level %d done, %'0.0f states explored\n", iteration, sylvan_satcount(visited, set->variables)); |
||||
|
} else { |
||||
|
INFO("Level %d done\n", iteration); |
||||
|
} |
||||
|
iteration++; |
||||
|
} while (next_level != sylvan_false); |
||||
|
|
||||
|
set->bdd = visited; |
||||
|
|
||||
|
sylvan_unprotect(&visited); |
||||
|
sylvan_unprotect(&next_level); |
||||
|
sylvan_unprotect(&cur_level); |
||||
|
sylvan_unprotect(&deadlocks); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Extend a transition relation to a larger domain (using s=s') |
||||
|
*/ |
||||
|
#define extend_relation(rel, vars) CALL(extend_relation, rel, vars) |
||||
|
TASK_2(BDD, extend_relation, BDD, relation, BDDSET, variables) |
||||
|
{ |
||||
|
/* first determine which state BDD variables are in rel */ |
||||
|
int has[statebits]; |
||||
|
for (int i=0; i<statebits; i++) has[i] = 0; |
||||
|
BDDSET s = variables; |
||||
|
while (!sylvan_set_isempty(s)) { |
||||
|
BDDVAR v = sylvan_set_var(s); |
||||
|
if (v/2 >= (unsigned)statebits) break; // action labels |
||||
|
has[v/2] = 1; |
||||
|
s = sylvan_set_next(s); |
||||
|
} |
||||
|
|
||||
|
/* create "s=s'" for all variables not in rel */ |
||||
|
BDD eq = sylvan_true; |
||||
|
for (int i=statebits-1; i>=0; i--) { |
||||
|
if (has[i]) continue; |
||||
|
BDD low = sylvan_makenode(2*i+1, eq, sylvan_false); |
||||
|
bdd_refs_push(low); |
||||
|
BDD high = sylvan_makenode(2*i+1, sylvan_false, eq); |
||||
|
bdd_refs_pop(1); |
||||
|
eq = sylvan_makenode(2*i, low, high); |
||||
|
} |
||||
|
|
||||
|
bdd_refs_push(eq); |
||||
|
BDD result = sylvan_and(relation, eq); |
||||
|
bdd_refs_pop(1); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Compute \BigUnion ( sets[i] ) |
||||
|
*/ |
||||
|
#define big_union(first, count) CALL(big_union, first, count) |
||||
|
TASK_2(BDD, big_union, int, first, int, count) |
||||
|
{ |
||||
|
if (count == 1) return next[first]->bdd; |
||||
|
|
||||
|
bdd_refs_spawn(SPAWN(big_union, first, count/2)); |
||||
|
BDD right = bdd_refs_push(CALL(big_union, first+count/2, count-count/2)); |
||||
|
BDD left = bdd_refs_push(bdd_refs_sync(SYNC(big_union))); |
||||
|
BDD result = sylvan_or(left, right); |
||||
|
bdd_refs_pop(2); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
static void |
||||
|
print_matrix(BDD vars) |
||||
|
{ |
||||
|
for (int i=0; i<vector_size; i++) { |
||||
|
if (sylvan_set_isempty(vars)) { |
||||
|
fprintf(stdout, "-"); |
||||
|
} else { |
||||
|
BDDVAR next_s = 2*((i+1)*bits_per_integer); |
||||
|
if (sylvan_set_var(vars) < next_s) { |
||||
|
fprintf(stdout, "+"); |
||||
|
for (;;) { |
||||
|
vars = sylvan_set_next(vars); |
||||
|
if (sylvan_set_isempty(vars)) break; |
||||
|
if (sylvan_set_var(vars) >= next_s) break; |
||||
|
} |
||||
|
} else { |
||||
|
fprintf(stdout, "-"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_0(gc_start) |
||||
|
{ |
||||
|
INFO("(GC) Starting garbage collection...\n"); |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_0(gc_end) |
||||
|
{ |
||||
|
INFO("(GC) Garbage collection done.\n"); |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
main(int argc, char **argv) |
||||
|
{ |
||||
|
argp_parse(&argp, argc, argv, 0, 0, 0); |
||||
|
setlocale(LC_NUMERIC, "en_US.utf-8"); |
||||
|
t_start = wctime(); |
||||
|
|
||||
|
FILE *f = fopen(model_filename, "r"); |
||||
|
if (f == NULL) { |
||||
|
fprintf(stderr, "Cannot open file '%s'!\n", model_filename); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
// Init Lace |
||||
|
lace_init(workers, 1000000); // auto-detect number of workers, use a 1,000,000 size task queue |
||||
|
lace_startup(0, NULL, NULL); // auto-detect program stack, do not use a callback for startup |
||||
|
|
||||
|
LACE_ME; |
||||
|
|
||||
|
// Init Sylvan |
||||
|
// Nodes table size: 24 bytes * 2**N_nodes |
||||
|
// Cache table size: 36 bytes * 2**N_cache |
||||
|
// With: N_nodes=25, N_cache=24: 1.3 GB memory |
||||
|
sylvan_init_package(1LL<<21, 1LL<<27, 1LL<<20, 1LL<<26); |
||||
|
sylvan_init_bdd(6); // granularity 6 is decent default value - 1 means "use cache for every operation" |
||||
|
sylvan_gc_add_mark(0, TASK(gc_start)); |
||||
|
sylvan_gc_add_mark(40, TASK(gc_end)); |
||||
|
|
||||
|
/* Load domain information */ |
||||
|
if ((fread(&vector_size, sizeof(int), 1, f) != 1) || |
||||
|
(fread(&statebits, sizeof(int), 1, f) != 1) || |
||||
|
(fread(&actionbits, sizeof(int), 1, f) != 1)) { |
||||
|
Abort("Invalid input file!\n"); |
||||
|
} |
||||
|
|
||||
|
bits_per_integer = statebits; |
||||
|
statebits *= vector_size; |
||||
|
|
||||
|
// Read initial state |
||||
|
set_t states = set_load(f); |
||||
|
|
||||
|
// Read transitions |
||||
|
if (fread(&next_count, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n"); |
||||
|
next = (rel_t*)malloc(sizeof(rel_t) * next_count); |
||||
|
|
||||
|
int i; |
||||
|
for (i=0; i<next_count; i++) { |
||||
|
next[i] = rel_load(f); |
||||
|
} |
||||
|
|
||||
|
/* Done */ |
||||
|
fclose(f); |
||||
|
|
||||
|
if (print_transition_matrix) { |
||||
|
for (i=0; i<next_count; i++) { |
||||
|
INFO(""); |
||||
|
print_matrix(next[i]->variables); |
||||
|
fprintf(stdout, "\n"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Report statistics |
||||
|
INFO("Read file '%s'\n", model_filename); |
||||
|
INFO("%d integers per state, %d bits per integer, %d transition groups\n", vector_size, bits_per_integer, next_count); |
||||
|
|
||||
|
if (merge_relations) { |
||||
|
BDD prime_variables = sylvan_set_empty(); |
||||
|
for (int i=statebits-1; i>=0; i--) { |
||||
|
bdd_refs_push(prime_variables); |
||||
|
prime_variables = sylvan_set_add(prime_variables, i*2+1); |
||||
|
bdd_refs_pop(1); |
||||
|
} |
||||
|
|
||||
|
bdd_refs_push(prime_variables); |
||||
|
|
||||
|
INFO("Extending transition relations to full domain.\n"); |
||||
|
for (int i=0; i<next_count; i++) { |
||||
|
next[i]->bdd = extend_relation(next[i]->bdd, next[i]->variables); |
||||
|
next[i]->variables = prime_variables; |
||||
|
} |
||||
|
|
||||
|
INFO("Taking union of all transition relations.\n"); |
||||
|
next[0]->bdd = big_union(0, next_count); |
||||
|
next_count = 1; |
||||
|
} |
||||
|
|
||||
|
if (report_nodes) { |
||||
|
INFO("BDD nodes:\n"); |
||||
|
INFO("Initial states: %zu BDD nodes\n", sylvan_nodecount(states->bdd)); |
||||
|
for (i=0; i<next_count; i++) { |
||||
|
INFO("Transition %d: %zu BDD nodes\n", i, sylvan_nodecount(next[i]->bdd)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#ifdef HAVE_PROFILER |
||||
|
if (profile_filename != NULL) ProfilerStart(profile_filename); |
||||
|
#endif |
||||
|
if (strategy == 1) { |
||||
|
double t1 = wctime(); |
||||
|
CALL(par, states); |
||||
|
double t2 = wctime(); |
||||
|
INFO("PAR Time: %f\n", t2-t1); |
||||
|
} else { |
||||
|
double t1 = wctime(); |
||||
|
CALL(bfs, states); |
||||
|
double t2 = wctime(); |
||||
|
INFO("BFS Time: %f\n", t2-t1); |
||||
|
} |
||||
|
#ifdef HAVE_PROFILER |
||||
|
if (profile_filename != NULL) ProfilerStop(); |
||||
|
#endif |
||||
|
|
||||
|
// Now we just have states |
||||
|
INFO("Final states: %'0.0f states\n", sylvan_satcount(states->bdd, states->variables)); |
||||
|
if (report_nodes) { |
||||
|
INFO("Final states: %'zu BDD nodes\n", sylvan_nodecount(states->bdd)); |
||||
|
} |
||||
|
|
||||
|
sylvan_stats_report(stdout, 1); |
||||
|
|
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,121 @@ |
|||||
|
#include <assert.h>
|
||||
|
#include <stdio.h>
|
||||
|
#include <stdint.h>
|
||||
|
|
||||
|
#include <sylvan.h>
|
||||
|
#include <sylvan_obj.hpp>
|
||||
|
|
||||
|
using namespace sylvan; |
||||
|
|
||||
|
VOID_TASK_0(simple_cxx) |
||||
|
{ |
||||
|
Bdd one = Bdd::bddOne(); // the True terminal
|
||||
|
Bdd zero = Bdd::bddZero(); // the False terminal
|
||||
|
|
||||
|
// check if they really are the True/False terminal
|
||||
|
assert(one.GetBDD() == sylvan_true); |
||||
|
assert(zero.GetBDD() == sylvan_false); |
||||
|
|
||||
|
Bdd a = Bdd::bddVar(0); // create a BDD variable x_0
|
||||
|
Bdd b = Bdd::bddVar(1); // create a BDD variable x_1
|
||||
|
|
||||
|
// check if a really is the Boolean formula "x_0"
|
||||
|
assert(!a.isConstant()); |
||||
|
assert(a.TopVar() == 0); |
||||
|
assert(a.Then() == one); |
||||
|
assert(a.Else() == zero); |
||||
|
|
||||
|
// check if b really is the Boolean formula "x_1"
|
||||
|
assert(!b.isConstant()); |
||||
|
assert(b.TopVar() == 1); |
||||
|
assert(b.Then() == one); |
||||
|
assert(b.Else() == zero); |
||||
|
|
||||
|
// compute !a
|
||||
|
Bdd not_a = !a; |
||||
|
|
||||
|
// check if !!a is really a
|
||||
|
assert((!not_a) == a); |
||||
|
|
||||
|
// compute a * b and !(!a + !b) and check if they are equivalent
|
||||
|
Bdd a_and_b = a * b; |
||||
|
Bdd not_not_a_or_not_b = !(!a + !b); |
||||
|
assert(a_and_b == not_not_a_or_not_b); |
||||
|
|
||||
|
// perform some simple quantification and check the results
|
||||
|
Bdd ex = a_and_b.ExistAbstract(a); // \exists a . a * b
|
||||
|
assert(ex == b); |
||||
|
Bdd andabs = a.AndAbstract(b, a); // \exists a . a * b using AndAbstract
|
||||
|
assert(ex == andabs); |
||||
|
Bdd univ = a_and_b.UnivAbstract(a); // \forall a . a * b
|
||||
|
assert(univ == zero); |
||||
|
|
||||
|
// alternative method to get the cube "ab" using bddCube
|
||||
|
BddSet variables = a * b; |
||||
|
std::vector<unsigned char> vec = {1, 1}; |
||||
|
assert(a_and_b == Bdd::bddCube(variables, vec)); |
||||
|
|
||||
|
// test the bddCube method for all combinations
|
||||
|
assert((!a * !b) == Bdd::bddCube(variables, std::vector<uint8_t>({0, 0}))); |
||||
|
assert((!a * b) == Bdd::bddCube(variables, std::vector<uint8_t>({0, 1}))); |
||||
|
assert((!a) == Bdd::bddCube(variables, std::vector<uint8_t>({0, 2}))); |
||||
|
assert((a * !b) == Bdd::bddCube(variables, std::vector<uint8_t>({1, 0}))); |
||||
|
assert((a * b) == Bdd::bddCube(variables, std::vector<uint8_t>({1, 1}))); |
||||
|
assert((a) == Bdd::bddCube(variables, std::vector<uint8_t>({1, 2}))); |
||||
|
assert((!b) == Bdd::bddCube(variables, std::vector<uint8_t>({2, 0}))); |
||||
|
assert((b) == Bdd::bddCube(variables, std::vector<uint8_t>({2, 1}))); |
||||
|
assert(one == Bdd::bddCube(variables, std::vector<uint8_t>({2, 2}))); |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_1(_main, void*, arg) |
||||
|
{ |
||||
|
// Initialize Sylvan
|
||||
|
// With starting size of the nodes table 1 << 21, and maximum size 1 << 27.
|
||||
|
// With starting size of the cache table 1 << 20, and maximum size 1 << 20.
|
||||
|
// Memory usage: 24 bytes per node, and 36 bytes per cache bucket
|
||||
|
// - 1<<24 nodes: 384 MB
|
||||
|
// - 1<<25 nodes: 768 MB
|
||||
|
// - 1<<26 nodes: 1536 MB
|
||||
|
// - 1<<27 nodes: 3072 MB
|
||||
|
// - 1<<24 cache: 576 MB
|
||||
|
// - 1<<25 cache: 1152 MB
|
||||
|
// - 1<<26 cache: 2304 MB
|
||||
|
// - 1<<27 cache: 4608 MB
|
||||
|
sylvan_init_package(1LL<<22, 1LL<<26, 1LL<<22, 1LL<<26); |
||||
|
|
||||
|
// Initialize the BDD module with granularity 1 (cache every operation)
|
||||
|
// A higher granularity (e.g. 6) often results in better performance in practice
|
||||
|
sylvan_init_bdd(1); |
||||
|
|
||||
|
// Now we can do some simple stuff using the C++ objects.
|
||||
|
CALL(simple_cxx); |
||||
|
|
||||
|
// Report statistics (if SYLVAN_STATS is 1 in the configuration)
|
||||
|
sylvan_stats_report(stdout, 1); |
||||
|
|
||||
|
// And quit, freeing memory
|
||||
|
sylvan_quit(); |
||||
|
|
||||
|
// We didn't use arg
|
||||
|
(void)arg; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
main (int argc, char *argv[]) |
||||
|
{ |
||||
|
int n_workers = 0; // automatically detect number of workers
|
||||
|
size_t deque_size = 0; // default value for the size of task deques for the workers
|
||||
|
size_t program_stack_size = 0; // default value for the program stack of each pthread
|
||||
|
|
||||
|
// Initialize the Lace framework for <n_workers> workers.
|
||||
|
lace_init(n_workers, deque_size); |
||||
|
|
||||
|
// Spawn and start all worker pthreads; suspends current thread until done.
|
||||
|
lace_startup(program_stack_size, TASK(_main), NULL); |
||||
|
|
||||
|
// The lace_startup command also exits Lace after _main is completed.
|
||||
|
|
||||
|
return 0; |
||||
|
(void)argc; // unused variable
|
||||
|
(void)argv; // unused variable
|
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
# Ignore everything in this directory |
||||
|
* |
||||
|
# Except: |
||||
|
!.gitignore |
||||
|
!m4_ax_check_compile_flag.m4 |
@ -0,0 +1,72 @@ |
|||||
|
# =========================================================================== |
||||
|
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html |
||||
|
# =========================================================================== |
||||
|
# |
||||
|
# SYNOPSIS |
||||
|
# |
||||
|
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) |
||||
|
# |
||||
|
# DESCRIPTION |
||||
|
# |
||||
|
# Check whether the given FLAG works with the current language's compiler |
||||
|
# or gives an error. (Warnings, however, are ignored) |
||||
|
# |
||||
|
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on |
||||
|
# success/failure. |
||||
|
# |
||||
|
# If EXTRA-FLAGS is defined, it is added to the current language's default |
||||
|
# flags (e.g. CFLAGS) when the check is done. The check is thus made with |
||||
|
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to |
||||
|
# force the compiler to issue an error when a bad flag is given. |
||||
|
# |
||||
|
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this |
||||
|
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. |
||||
|
# |
||||
|
# LICENSE |
||||
|
# |
||||
|
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> |
||||
|
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify it |
||||
|
# under the terms of the GNU General Public License as published by the |
||||
|
# Free Software Foundation, either version 3 of the License, or (at your |
||||
|
# option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, but |
||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
||||
|
# Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU General Public License along |
||||
|
# with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
# As a special exception, the respective Autoconf Macro's copyright owner |
||||
|
# gives unlimited permission to copy, distribute and modify the configure |
||||
|
# scripts that are the output of Autoconf when processing the Macro. You |
||||
|
# need not follow the terms of the GNU General Public License when using |
||||
|
# or distributing such scripts, even though portions of the text of the |
||||
|
# Macro appear in them. The GNU General Public License (GPL) does govern |
||||
|
# all other use of the material that constitutes the Autoconf Macro. |
||||
|
# |
||||
|
# This special exception to the GPL applies to versions of the Autoconf |
||||
|
# Macro released by the Autoconf Archive. When you make and distribute a |
||||
|
# modified version of the Autoconf Macro, you may extend this special |
||||
|
# exception to the GPL to apply to your modified version as well. |
||||
|
|
||||
|
#serial 2 |
||||
|
|
||||
|
AC_DEFUN([AX_CHECK_COMPILE_FLAG], |
||||
|
[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX |
||||
|
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl |
||||
|
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ |
||||
|
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS |
||||
|
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" |
||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], |
||||
|
[AS_VAR_SET(CACHEVAR,[yes])], |
||||
|
[AS_VAR_SET(CACHEVAR,[no])]) |
||||
|
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) |
||||
|
AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], |
||||
|
[m4_default([$2], :)], |
||||
|
[m4_default([$3], :)]) |
||||
|
AS_VAR_POPDEF([CACHEVAR])dnl |
||||
|
])dnl AX_CHECK_COMPILE_FLAGS |
@ -0,0 +1,81 @@ |
|||||
|
cmake_minimum_required(VERSION 2.6) |
||||
|
project(sylvan C CXX) |
||||
|
|
||||
|
add_library(sylvan |
||||
|
avl.h |
||||
|
lace.h |
||||
|
lace.c |
||||
|
llmsset.c |
||||
|
llmsset.h |
||||
|
refs.h |
||||
|
refs.c |
||||
|
sha2.h |
||||
|
sha2.c |
||||
|
stats.h |
||||
|
stats.c |
||||
|
sylvan.h |
||||
|
sylvan_bdd.h |
||||
|
sylvan_bdd.c |
||||
|
sylvan_cache.h |
||||
|
sylvan_cache.c |
||||
|
sylvan_config.h |
||||
|
sylvan_common.h |
||||
|
sylvan_common.c |
||||
|
sylvan_gmp.h |
||||
|
sylvan_gmp.c |
||||
|
sylvan_ldd.h |
||||
|
sylvan_ldd.c |
||||
|
sylvan_mtbdd.h |
||||
|
sylvan_mtbdd.c |
||||
|
sylvan_mtbdd_int.h |
||||
|
sylvan_obj.hpp |
||||
|
sylvan_obj.cpp |
||||
|
tls.h |
||||
|
) |
||||
|
|
||||
|
# We need to make sure that the binary is put into a folder that is independent of the |
||||
|
# build type. Otherwise -- for example when using Xcode -- the binary might end up in a |
||||
|
# sub-folder "Debug" or "Release". |
||||
|
set_target_properties(sylvan PROPERTIES |
||||
|
ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR} |
||||
|
ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}) |
||||
|
|
||||
|
target_link_libraries(sylvan m pthread) |
||||
|
|
||||
|
if(UNIX AND NOT APPLE) |
||||
|
target_link_libraries(sylvan rt) |
||||
|
endif() |
||||
|
|
||||
|
option(USE_HWLOC "Use HWLOC library if available" ON) |
||||
|
|
||||
|
if(USE_HWLOC) |
||||
|
include(CheckIncludeFiles) |
||||
|
check_include_files(hwloc.h HAVE_HWLOC) |
||||
|
if(HAVE_HWLOC) |
||||
|
set_target_properties(sylvan PROPERTIES COMPILE_DEFINITIONS "USE_HWLOC=1") |
||||
|
target_link_libraries(sylvan hwloc) |
||||
|
endif() |
||||
|
endif() |
||||
|
|
||||
|
option(SYLVAN_STATS "Collect statistics" OFF) |
||||
|
if(SYLVAN_STATS) |
||||
|
set_target_properties(sylvan PROPERTIES COMPILE_DEFINITIONS "SYLVAN_STATS") |
||||
|
endif() |
||||
|
|
||||
|
install(TARGETS |
||||
|
sylvan |
||||
|
DESTINATION "lib") |
||||
|
|
||||
|
install(FILES |
||||
|
lace.h |
||||
|
llmsset.h |
||||
|
sylvan.h |
||||
|
sylvan_cache.h |
||||
|
sylvan_common.h |
||||
|
sylvan_config.h |
||||
|
sylvan_bdd.h |
||||
|
sylvan_ldd.h |
||||
|
sylvan_mtbdd.h |
||||
|
sylvan_obj.hpp |
||||
|
tls.h |
||||
|
DESTINATION "include") |
@ -0,0 +1,39 @@ |
|||||
|
lib_LTLIBRARIES = libsylvan.la |
||||
|
|
||||
|
libsylvan_la_CFLAGS = $(AM_CFLAGS) -fno-strict-aliasing -std=gnu11 |
||||
|
|
||||
|
libsylvan_la_SOURCES = \
|
||||
|
avl.h \
|
||||
|
lace.c \
|
||||
|
lace.h \
|
||||
|
llmsset.c \
|
||||
|
llmsset.h \
|
||||
|
refs.h \
|
||||
|
refs.c \
|
||||
|
sha2.c \
|
||||
|
sha2.h \
|
||||
|
stats.h \
|
||||
|
stats.c \
|
||||
|
sylvan.h \
|
||||
|
sylvan_config.h \
|
||||
|
sylvan_bdd.h \
|
||||
|
sylvan_bdd.c \
|
||||
|
sylvan_ldd.h \
|
||||
|
sylvan_ldd.c \
|
||||
|
sylvan_cache.h \
|
||||
|
sylvan_cache.c \
|
||||
|
sylvan_common.c \
|
||||
|
sylvan_common.h \
|
||||
|
sylvan_mtbdd.h \
|
||||
|
sylvan_mtbdd.c \
|
||||
|
sylvan_mtbdd_int.h \
|
||||
|
sylvan_obj.hpp \
|
||||
|
sylvan_obj.cpp \
|
||||
|
tls.h |
||||
|
|
||||
|
libsylvan_la_LIBADD = -lm |
||||
|
|
||||
|
if HAVE_LIBHWLOC |
||||
|
libsylvan_la_LIBADD += -lhwloc |
||||
|
libsylvan_la_CFLAGS += -DUSE_HWLOC=1 |
||||
|
endif |
@ -0,0 +1,398 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2014 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* Self-balancing binary tree implemented using C macros |
||||
|
* |
||||
|
* See also: http://en.wikipedia.org/wiki/AVL_tree |
||||
|
* Data structure originally by Adelson-Velskii, Landis, 1962. |
||||
|
* |
||||
|
* Usage of this AVL implementation: |
||||
|
* |
||||
|
* AVL(some_name, some_type) |
||||
|
* { |
||||
|
* Compare some_type *left with some_type *right |
||||
|
* Return <0 when left<right, >0 when left>right, 0 when left=right |
||||
|
* } |
||||
|
* |
||||
|
* You get: |
||||
|
* - some_type *some_name_put(avl_node_t **root_node, some_type *data, int *inserted); |
||||
|
* Either insert new or retrieve existing key, <inserted> if non-NULL receives 0 or 1. |
||||
|
* - int some_name_insert(avl_node_t **root_node, some_type *data); |
||||
|
* Try to insert, return 1 if succesful, 0 if existed. |
||||
|
* - int some_name_delete(avl_node_t **root_node, some_type *data); |
||||
|
* Try to delete, return 1 if deleted, 0 if no match. |
||||
|
* - some_type *some_name_search(avl_node_t *root_node, some_type *data); |
||||
|
* Retrieve existing data, returns NULL if unsuccesful |
||||
|
* - void some_name_free(avl_node_t **root_node); |
||||
|
* Free all memory used by the AVL tree |
||||
|
* - some_type *some_name_toarray(avl_node_t *root_node); |
||||
|
* Malloc an array and put the sorted data in it... |
||||
|
* - size_t avl_count(avl_node_t *root_node); |
||||
|
* Returns the number of items in the tree |
||||
|
* |
||||
|
* For example: |
||||
|
* struct my_struct { ... }; |
||||
|
* AVL(some_name, struct my_struct) |
||||
|
* { |
||||
|
* Compare struct my_struct *left with struct my_struct *right |
||||
|
* Return <0 when left<right, >0 when left>right, 0 when left=right |
||||
|
* } |
||||
|
* |
||||
|
* avl_node_t *the_root = NULL; |
||||
|
* struct mystuff; |
||||
|
* if (!some_name_search(the_root, &mystuff)) some_name_insert(&the_root, &mystuff); |
||||
|
* some_name_free(&the_root); |
||||
|
* |
||||
|
* For questions, feedback, etc: t.vandijk@utwente.nl |
||||
|
*/ |
||||
|
|
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#ifndef __AVL_H__ |
||||
|
#define __AVL_H__ |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
typedef struct avl_node |
||||
|
{ |
||||
|
struct avl_node *left, *right; |
||||
|
unsigned int height; |
||||
|
char pad[8-sizeof(unsigned int)]; |
||||
|
char data[0]; |
||||
|
} avl_node_t; |
||||
|
|
||||
|
/* Retrieve the height of a tree */ |
||||
|
static inline int |
||||
|
avl_get_height(avl_node_t *node) |
||||
|
{ |
||||
|
return node == NULL ? 0 : node->height; |
||||
|
} |
||||
|
|
||||
|
/* Helper for rotations to update the heights of trees */ |
||||
|
static inline void |
||||
|
avl_update_height(avl_node_t *node) |
||||
|
{ |
||||
|
int h1 = avl_get_height(node->left); |
||||
|
int h2 = avl_get_height(node->right); |
||||
|
node->height = 1 + (h1 > h2 ? h1 : h2); |
||||
|
} |
||||
|
|
||||
|
/* Helper for avl_balance_tree */ |
||||
|
static inline int |
||||
|
avl_update_height_get_balance(avl_node_t *node) |
||||
|
{ |
||||
|
int h1 = avl_get_height(node->left); |
||||
|
int h2 = avl_get_height(node->right); |
||||
|
node->height = 1 + (h1 > h2 ? h1 : h2); |
||||
|
return h1 - h2; |
||||
|
} |
||||
|
|
||||
|
/* Helper for avl_check_consistent */ |
||||
|
static inline int |
||||
|
avl_verify_height(avl_node_t *node) |
||||
|
{ |
||||
|
int h1 = avl_get_height(node->left); |
||||
|
int h2 = avl_get_height(node->right); |
||||
|
int expected_height = 1 + (h1 > h2 ? h1 : h2); |
||||
|
return expected_height == avl_get_height(node); |
||||
|
} |
||||
|
|
||||
|
/* Optional consistency check */ |
||||
|
static inline int __attribute__((unused)) |
||||
|
avl_check_consistent(avl_node_t *root) |
||||
|
{ |
||||
|
if (root == NULL) return 1; |
||||
|
if (!avl_check_consistent(root->left)) return 0; |
||||
|
if (!avl_check_consistent(root->right)) return 0; |
||||
|
if (!avl_verify_height(root)) return 0; |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
/* Perform LL rotation, returns the new root */ |
||||
|
static avl_node_t* |
||||
|
avl_rotate_LL(avl_node_t *parent) |
||||
|
{ |
||||
|
avl_node_t *child = parent->left; |
||||
|
parent->left = child->right; |
||||
|
child->right = parent; |
||||
|
avl_update_height(parent); |
||||
|
avl_update_height(child); |
||||
|
return child; |
||||
|
} |
||||
|
|
||||
|
/* Perform RR rotation, returns the new root */ |
||||
|
static avl_node_t* |
||||
|
avl_rotate_RR(avl_node_t *parent) |
||||
|
{ |
||||
|
avl_node_t *child = parent->right; |
||||
|
parent->right = child->left; |
||||
|
child->left = parent; |
||||
|
avl_update_height(parent); |
||||
|
avl_update_height(child); |
||||
|
return child; |
||||
|
} |
||||
|
|
||||
|
/* Perform RL rotation, returns the new root */ |
||||
|
static avl_node_t* |
||||
|
avl_rotate_RL(avl_node_t *parent) |
||||
|
{ |
||||
|
avl_node_t *child = parent->right; |
||||
|
parent->right = avl_rotate_LL(child); |
||||
|
return avl_rotate_RR(parent); |
||||
|
} |
||||
|
|
||||
|
/* Perform LR rotation, returns the new root */ |
||||
|
static avl_node_t* |
||||
|
avl_rotate_LR(avl_node_t *parent) |
||||
|
{ |
||||
|
avl_node_t *child = parent->left; |
||||
|
parent->left = avl_rotate_RR(child); |
||||
|
return avl_rotate_LL(parent); |
||||
|
} |
||||
|
|
||||
|
/* Calculate balance factor */ |
||||
|
static inline int |
||||
|
avl_get_balance(avl_node_t *node) |
||||
|
{ |
||||
|
if (node == NULL) return 0; |
||||
|
return avl_get_height(node->left) - avl_get_height(node->right); |
||||
|
} |
||||
|
|
||||
|
/* Balance the tree */ |
||||
|
static void |
||||
|
avl_balance_tree(avl_node_t **node) |
||||
|
{ |
||||
|
int factor = avl_update_height_get_balance(*node); |
||||
|
|
||||
|
if (factor > 1) { |
||||
|
if (avl_get_balance((*node)->left) > 0) *node = avl_rotate_LL(*node); |
||||
|
else *node = avl_rotate_LR(*node); |
||||
|
} else if (factor < -1) { |
||||
|
if (avl_get_balance((*node)->right) < 0) *node = avl_rotate_RR(*node); |
||||
|
else *node = avl_rotate_RL(*node); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Get number of items in the AVL */ |
||||
|
static size_t |
||||
|
avl_count(avl_node_t *node) |
||||
|
{ |
||||
|
if (node == NULL) return 0; |
||||
|
return 1 + avl_count(node->left) + avl_count(node->right); |
||||
|
} |
||||
|
|
||||
|
/* Structure for iterator */ |
||||
|
typedef struct avl_iter |
||||
|
{ |
||||
|
size_t height; |
||||
|
avl_node_t *nodes[0]; |
||||
|
} avl_iter_t; |
||||
|
|
||||
|
/** |
||||
|
* nodes[0] = root node |
||||
|
* nodes[1] = some node |
||||
|
* nodes[2] = some node |
||||
|
* nodes[3] = leaf node (height = 4) |
||||
|
* nodes[4] = NULL (max = height + 1) |
||||
|
*/ |
||||
|
|
||||
|
/* Create a new iterator */ |
||||
|
static inline avl_iter_t* |
||||
|
avl_iter(avl_node_t *node) |
||||
|
{ |
||||
|
size_t max = node ? node->height+1 : 1; |
||||
|
avl_iter_t *result = (avl_iter_t*)malloc(sizeof(avl_iter_t) + sizeof(avl_node_t*) * max); |
||||
|
result->height = 0; |
||||
|
result->nodes[0] = node; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/* Get the next node during iteration */ |
||||
|
static inline avl_node_t* |
||||
|
avl_iter_next(avl_iter_t *iter) |
||||
|
{ |
||||
|
/* when first node is NULL, we're done */ |
||||
|
if (iter->nodes[0] == NULL) return NULL; |
||||
|
|
||||
|
/* if the head is not NULL, first entry... */ |
||||
|
while (iter->nodes[iter->height] != NULL) { |
||||
|
iter->nodes[iter->height+1] = iter->nodes[iter->height]->left; |
||||
|
iter->height++; |
||||
|
} |
||||
|
|
||||
|
/* head is now NULL, take parent as result */ |
||||
|
avl_node_t *result = iter->nodes[iter->height-1]; |
||||
|
|
||||
|
if (result->right != NULL) { |
||||
|
/* if we can go right, do that */ |
||||
|
iter->nodes[iter->height] = result->right; |
||||
|
} else { |
||||
|
/* cannot go right, backtrack */ |
||||
|
do { |
||||
|
iter->height--; |
||||
|
} while (iter->height > 0 && iter->nodes[iter->height] == iter->nodes[iter->height-1]->right); |
||||
|
iter->nodes[iter->height] = NULL; /* set head to NULL: second entry */ |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
#define AVL(NAME, TYPE) \ |
||||
|
static inline int \ |
||||
|
NAME##_AVL_compare(TYPE *left, TYPE *right); \ |
||||
|
static __attribute__((unused)) TYPE* \ |
||||
|
NAME##_put(avl_node_t **root, TYPE *data, int *inserted) \ |
||||
|
{ \ |
||||
|
if (inserted && *inserted) *inserted = 0; /* reset inserted once */ \ |
||||
|
TYPE *result; \ |
||||
|
avl_node_t *it = *root; \ |
||||
|
if (it == NULL) { \ |
||||
|
*root = it = (avl_node_t*)malloc(sizeof(struct avl_node)+sizeof(TYPE)); \ |
||||
|
it->left = it->right = NULL; \ |
||||
|
it->height = 1; \ |
||||
|
memcpy(it->data, data, sizeof(TYPE)); \ |
||||
|
result = (TYPE *)it->data; \ |
||||
|
if (inserted) *inserted = 1; \ |
||||
|
} else { \ |
||||
|
int cmp = NAME##_AVL_compare(data, (TYPE*)(it->data)); \ |
||||
|
if (cmp == 0) return (TYPE *)it->data; \ |
||||
|
if (cmp < 0) result = NAME##_put(&it->left, data, inserted); \ |
||||
|
else result = NAME##_put(&it->right, data, inserted); \ |
||||
|
avl_balance_tree(root); \ |
||||
|
} \ |
||||
|
return result; \ |
||||
|
} \ |
||||
|
static __attribute__((unused)) int \ |
||||
|
NAME##_insert(avl_node_t **root, TYPE *data) \ |
||||
|
{ \ |
||||
|
int inserted; \ |
||||
|
NAME##_put(root, data, &inserted); \ |
||||
|
return inserted; \ |
||||
|
} \ |
||||
|
static void \ |
||||
|
NAME##_exchange_and_balance(avl_node_t *target, avl_node_t **node) \ |
||||
|
{ \ |
||||
|
avl_node_t *it = *node; \ |
||||
|
if (it->left == 0) { /* leftmost node contains lowest value */ \ |
||||
|
memcpy(target->data, it->data, sizeof(TYPE)); \ |
||||
|
*node = it->right; \ |
||||
|
free(it); \ |
||||
|
} else { \ |
||||
|
NAME##_exchange_and_balance(target, &it->left); \ |
||||
|
} \ |
||||
|
avl_balance_tree(node); \ |
||||
|
} \ |
||||
|
static __attribute__((unused)) int \ |
||||
|
NAME##_delete(avl_node_t **node, TYPE *data) \ |
||||
|
{ \ |
||||
|
avl_node_t *it = *node; \ |
||||
|
if (it == NULL) return 0; \ |
||||
|
int cmp = NAME##_AVL_compare(data, (TYPE *)((*node)->data)), res; \ |
||||
|
if (cmp < 0) res = NAME##_delete(&it->left, data); \ |
||||
|
else if (cmp > 0) res = NAME##_delete(&it->right, data); \ |
||||
|
else { \ |
||||
|
int h_left = avl_get_height(it->left); \ |
||||
|
int h_right = avl_get_height(it->right); \ |
||||
|
if (h_left == 0) { \ |
||||
|
if (h_right == 0) { /* Leaf */ \ |
||||
|
*node = NULL; \ |
||||
|
free(it); \ |
||||
|
return 1; \ |
||||
|
} else { /* Only right child */ \ |
||||
|
*node = it->right; \ |
||||
|
free(it); \ |
||||
|
return 1; \ |
||||
|
} \ |
||||
|
} else if (h_right == 0) { /* Only left child */ \ |
||||
|
*node = it->left; \ |
||||
|
free(it); \ |
||||
|
return 1; \ |
||||
|
} else { /* Exchange with successor */ \ |
||||
|
NAME##_exchange_and_balance(it, &it->right); \ |
||||
|
res = 1; \ |
||||
|
} \ |
||||
|
} \ |
||||
|
if (res) avl_balance_tree(node); \ |
||||
|
return res; \ |
||||
|
} \ |
||||
|
static __attribute__((unused)) TYPE* \ |
||||
|
NAME##_search(avl_node_t *node, TYPE *data) \ |
||||
|
{ \ |
||||
|
while (node != NULL) { \ |
||||
|
int result = NAME##_AVL_compare((TYPE *)node->data, data); \ |
||||
|
if (result == 0) return (TYPE *)node->data; \ |
||||
|
if (result > 0) node = node->left; \ |
||||
|
else node = node->right; \ |
||||
|
} \ |
||||
|
return NULL; \ |
||||
|
} \ |
||||
|
static __attribute__((unused)) void \ |
||||
|
NAME##_free(avl_node_t **node) \ |
||||
|
{ \ |
||||
|
avl_node_t *it = *node; \ |
||||
|
if (it) { \ |
||||
|
NAME##_free(&it->left); \ |
||||
|
NAME##_free(&it->right); \ |
||||
|
free(it); \ |
||||
|
*node = NULL; \ |
||||
|
} \ |
||||
|
} \ |
||||
|
static void \ |
||||
|
NAME##_toarray_rec(avl_node_t *node, TYPE **ptr) \ |
||||
|
{ \ |
||||
|
if (node->left != NULL) NAME##_toarray_rec(node->left, ptr); \ |
||||
|
memcpy(*ptr, node->data, sizeof(TYPE)); \ |
||||
|
(*ptr)++; \ |
||||
|
if (node->right != NULL) NAME##_toarray_rec(node->right, ptr); \ |
||||
|
} \ |
||||
|
static __attribute__((unused)) TYPE* \ |
||||
|
NAME##_toarray(avl_node_t *node) \ |
||||
|
{ \ |
||||
|
size_t count = avl_count(node); \ |
||||
|
TYPE *arr = (TYPE *)malloc(sizeof(TYPE) * count); \ |
||||
|
TYPE *ptr = arr; \ |
||||
|
NAME##_toarray_rec(node, &ptr); \ |
||||
|
return arr; \ |
||||
|
} \ |
||||
|
static __attribute__((unused)) avl_iter_t* \ |
||||
|
NAME##_iter(avl_node_t *node) \ |
||||
|
{ \ |
||||
|
return avl_iter(node); \ |
||||
|
} \ |
||||
|
static __attribute__((unused)) TYPE* \ |
||||
|
NAME##_iter_next(avl_iter_t *iter) \ |
||||
|
{ \ |
||||
|
avl_node_t *result = avl_iter_next(iter); \ |
||||
|
if (result == NULL) return NULL; \ |
||||
|
return (TYPE*)(result->data); \ |
||||
|
} \ |
||||
|
static __attribute__((unused)) void \ |
||||
|
NAME##_iter_free(avl_iter_t *iter) \ |
||||
|
{ \ |
||||
|
free(iter); \ |
||||
|
} \ |
||||
|
static inline int \ |
||||
|
NAME##_AVL_compare(TYPE *left, TYPE *right) |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#endif |
1045
resources/3rdparty/sylvan/src/lace.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2743
resources/3rdparty/sylvan/src/lace.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,564 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#include <sylvan_config.h> |
||||
|
|
||||
|
#include <errno.h> // for errno |
||||
|
#include <stdint.h> // for uint64_t etc |
||||
|
#include <stdio.h> // for printf |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> // memset |
||||
|
#include <sys/mman.h> // for mmap |
||||
|
|
||||
|
#include <llmsset.h> |
||||
|
#include <stats.h> |
||||
|
#include <tls.h> |
||||
|
|
||||
|
#ifndef USE_HWLOC |
||||
|
#define USE_HWLOC 0 |
||||
|
#endif |
||||
|
|
||||
|
#if USE_HWLOC |
||||
|
#include <hwloc.h> |
||||
|
|
||||
|
static hwloc_topology_t topo; |
||||
|
#endif |
||||
|
|
||||
|
#ifndef MAP_ANONYMOUS |
||||
|
#define MAP_ANONYMOUS MAP_ANON |
||||
|
#endif |
||||
|
|
||||
|
#ifndef cas |
||||
|
#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new))) |
||||
|
#endif |
||||
|
|
||||
|
DECLARE_THREAD_LOCAL(my_region, uint64_t); |
||||
|
|
||||
|
VOID_TASK_0(llmsset_reset_region) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(my_region, uint64_t); |
||||
|
my_region = (uint64_t)-1; // no region |
||||
|
SET_THREAD_LOCAL(my_region, my_region); |
||||
|
} |
||||
|
|
||||
|
static uint64_t |
||||
|
claim_data_bucket(const llmsset_t dbs) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(my_region, uint64_t); |
||||
|
|
||||
|
for (;;) { |
||||
|
if (my_region != (uint64_t)-1) { |
||||
|
// find empty bucket in region <my_region> |
||||
|
uint64_t *ptr = dbs->bitmap2 + (my_region*8); |
||||
|
int i=0; |
||||
|
for (;i<8;) { |
||||
|
uint64_t v = *ptr; |
||||
|
if (v != 0xffffffffffffffffLL) { |
||||
|
int j = __builtin_clzll(~v); |
||||
|
*ptr |= (0x8000000000000000LL>>j); |
||||
|
return (8 * my_region + i) * 64 + j; |
||||
|
} |
||||
|
i++; |
||||
|
ptr++; |
||||
|
} |
||||
|
} else { |
||||
|
// special case on startup or after garbage collection |
||||
|
my_region += (lace_get_worker()->worker*(dbs->table_size/(64*8)))/lace_workers(); |
||||
|
} |
||||
|
uint64_t count = dbs->table_size/(64*8); |
||||
|
for (;;) { |
||||
|
// check if table maybe full |
||||
|
if (count-- == 0) return (uint64_t)-1; |
||||
|
|
||||
|
my_region += 1; |
||||
|
if (my_region >= (dbs->table_size/(64*8))) my_region = 0; |
||||
|
|
||||
|
// try to claim it |
||||
|
uint64_t *ptr = dbs->bitmap1 + (my_region/64); |
||||
|
uint64_t mask = 0x8000000000000000LL >> (my_region&63); |
||||
|
uint64_t v; |
||||
|
restart: |
||||
|
v = *ptr; |
||||
|
if (v & mask) continue; // taken |
||||
|
if (cas(ptr, v, v|mask)) break; |
||||
|
else goto restart; |
||||
|
} |
||||
|
SET_THREAD_LOCAL(my_region, my_region); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static void |
||||
|
release_data_bucket(const llmsset_t dbs, uint64_t index) |
||||
|
{ |
||||
|
uint64_t *ptr = dbs->bitmap2 + (index/64); |
||||
|
uint64_t mask = 0x8000000000000000LL >> (index&63); |
||||
|
*ptr &= ~mask; |
||||
|
} |
||||
|
|
||||
|
static void |
||||
|
set_custom_bucket(const llmsset_t dbs, uint64_t index, int on) |
||||
|
{ |
||||
|
uint64_t *ptr = dbs->bitmapc + (index/64); |
||||
|
uint64_t mask = 0x8000000000000000LL >> (index&63); |
||||
|
if (on) *ptr |= mask; |
||||
|
else *ptr &= ~mask; |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
get_custom_bucket(const llmsset_t dbs, uint64_t index) |
||||
|
{ |
||||
|
uint64_t *ptr = dbs->bitmapc + (index/64); |
||||
|
uint64_t mask = 0x8000000000000000LL >> (index&63); |
||||
|
return (*ptr & mask) ? 1 : 0; |
||||
|
} |
||||
|
|
||||
|
#ifndef rotl64 |
||||
|
static inline uint64_t |
||||
|
rotl64(uint64_t x, int8_t r) |
||||
|
{ |
||||
|
return ((x<<r) | (x>>(64-r))); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
uint64_t |
||||
|
llmsset_hash(const uint64_t a, const uint64_t b, const uint64_t seed) |
||||
|
{ |
||||
|
const uint64_t prime = 1099511628211; |
||||
|
|
||||
|
uint64_t hash = seed; |
||||
|
hash = hash ^ a; |
||||
|
hash = rotl64(hash, 47); |
||||
|
hash = hash * prime; |
||||
|
hash = hash ^ b; |
||||
|
hash = rotl64(hash, 31); |
||||
|
hash = hash * prime; |
||||
|
|
||||
|
return hash ^ (hash >> 32); |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* CL_MASK and CL_MASK_R are for the probe sequence calculation. |
||||
|
* With 64 bytes per cacheline, there are 8 64-bit values per cacheline. |
||||
|
*/ |
||||
|
// The LINE_SIZE is defined in lace.h |
||||
|
static const uint64_t CL_MASK = ~(((LINE_SIZE) / 8) - 1); |
||||
|
static const uint64_t CL_MASK_R = ((LINE_SIZE) / 8) - 1; |
||||
|
|
||||
|
/* 40 bits for the index, 24 bits for the hash */ |
||||
|
#define MASK_INDEX ((uint64_t)0x000000ffffffffff) |
||||
|
#define MASK_HASH ((uint64_t)0xffffff0000000000) |
||||
|
|
||||
|
static inline uint64_t |
||||
|
llmsset_lookup2(const llmsset_t dbs, uint64_t a, uint64_t b, int* created, const int custom) |
||||
|
{ |
||||
|
uint64_t hash_rehash = 14695981039346656037LLU; |
||||
|
if (custom) hash_rehash = dbs->hash_cb(a, b, hash_rehash); |
||||
|
else hash_rehash = llmsset_hash(a, b, hash_rehash); |
||||
|
|
||||
|
const uint64_t hash = hash_rehash & MASK_HASH; |
||||
|
uint64_t idx, last, cidx = 0; |
||||
|
int i=0; |
||||
|
|
||||
|
#if LLMSSET_MASK |
||||
|
last = idx = hash_rehash & dbs->mask; |
||||
|
#else |
||||
|
last = idx = hash_rehash % dbs->table_size; |
||||
|
#endif |
||||
|
|
||||
|
for (;;) { |
||||
|
volatile uint64_t *bucket = dbs->table + idx; |
||||
|
uint64_t v = *bucket; |
||||
|
|
||||
|
if (v == 0) { |
||||
|
if (cidx == 0) { |
||||
|
cidx = claim_data_bucket(dbs); |
||||
|
if (cidx == (uint64_t)-1) return 0; // failed to claim a data bucket |
||||
|
if (custom) dbs->create_cb(&a, &b); |
||||
|
uint64_t *d_ptr = ((uint64_t*)dbs->data) + 2*cidx; |
||||
|
d_ptr[0] = a; |
||||
|
d_ptr[1] = b; |
||||
|
} |
||||
|
if (cas(bucket, 0, hash | cidx)) { |
||||
|
if (custom) set_custom_bucket(dbs, cidx, custom); |
||||
|
*created = 1; |
||||
|
return cidx; |
||||
|
} else { |
||||
|
v = *bucket; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (hash == (v & MASK_HASH)) { |
||||
|
uint64_t d_idx = v & MASK_INDEX; |
||||
|
uint64_t *d_ptr = ((uint64_t*)dbs->data) + 2*d_idx; |
||||
|
if (custom) { |
||||
|
if (dbs->equals_cb(a, b, d_ptr[0], d_ptr[1])) { |
||||
|
if (cidx != 0) { |
||||
|
dbs->destroy_cb(a, b); |
||||
|
release_data_bucket(dbs, cidx); |
||||
|
} |
||||
|
*created = 0; |
||||
|
return d_idx; |
||||
|
} |
||||
|
} else { |
||||
|
if (d_ptr[0] == a && d_ptr[1] == b) { |
||||
|
if (cidx != 0) release_data_bucket(dbs, cidx); |
||||
|
*created = 0; |
||||
|
return d_idx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
sylvan_stats_count(LLMSSET_LOOKUP); |
||||
|
|
||||
|
// find next idx on probe sequence |
||||
|
idx = (idx & CL_MASK) | ((idx+1) & CL_MASK_R); |
||||
|
if (idx == last) { |
||||
|
if (++i == dbs->threshold) return 0; // failed to find empty spot in probe sequence |
||||
|
|
||||
|
// go to next cache line in probe sequence |
||||
|
if (custom) hash_rehash = dbs->hash_cb(a, b, hash_rehash); |
||||
|
else hash_rehash = llmsset_hash(a, b, hash_rehash); |
||||
|
|
||||
|
#if LLMSSET_MASK |
||||
|
last = idx = hash_rehash & dbs->mask; |
||||
|
#else |
||||
|
last = idx = hash_rehash % dbs->table_size; |
||||
|
#endif |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
uint64_t |
||||
|
llmsset_lookup(const llmsset_t dbs, const uint64_t a, const uint64_t b, int* created) |
||||
|
{ |
||||
|
return llmsset_lookup2(dbs, a, b, created, 0); |
||||
|
} |
||||
|
|
||||
|
uint64_t |
||||
|
llmsset_lookupc(const llmsset_t dbs, const uint64_t a, const uint64_t b, int* created) |
||||
|
{ |
||||
|
return llmsset_lookup2(dbs, a, b, created, 1); |
||||
|
} |
||||
|
|
||||
|
static inline int |
||||
|
llmsset_rehash_bucket(const llmsset_t dbs, uint64_t d_idx) |
||||
|
{ |
||||
|
const uint64_t * const d_ptr = ((uint64_t*)dbs->data) + 2*d_idx; |
||||
|
const uint64_t a = d_ptr[0]; |
||||
|
const uint64_t b = d_ptr[1]; |
||||
|
|
||||
|
uint64_t hash_rehash = 14695981039346656037LLU; |
||||
|
const int custom = get_custom_bucket(dbs, d_idx) ? 1 : 0; |
||||
|
if (custom) hash_rehash = dbs->hash_cb(a, b, hash_rehash); |
||||
|
else hash_rehash = llmsset_hash(a, b, hash_rehash); |
||||
|
const uint64_t new_v = (hash_rehash & MASK_HASH) | d_idx; |
||||
|
int i=0; |
||||
|
|
||||
|
uint64_t idx, last; |
||||
|
#if LLMSSET_MASK |
||||
|
last = idx = hash_rehash & dbs->mask; |
||||
|
#else |
||||
|
last = idx = hash_rehash % dbs->table_size; |
||||
|
#endif |
||||
|
|
||||
|
for (;;) { |
||||
|
volatile uint64_t *bucket = &dbs->table[idx]; |
||||
|
if (*bucket == 0 && cas(bucket, 0, new_v)) return 1; |
||||
|
|
||||
|
// find next idx on probe sequence |
||||
|
idx = (idx & CL_MASK) | ((idx+1) & CL_MASK_R); |
||||
|
if (idx == last) { |
||||
|
if (++i == dbs->threshold) return 0; // failed to find empty spot in probe sequence |
||||
|
|
||||
|
// go to next cache line in probe sequence |
||||
|
if (custom) hash_rehash = dbs->hash_cb(a, b, hash_rehash); |
||||
|
else hash_rehash = llmsset_hash(a, b, hash_rehash); |
||||
|
|
||||
|
#if LLMSSET_MASK |
||||
|
last = idx = hash_rehash & dbs->mask; |
||||
|
#else |
||||
|
last = idx = hash_rehash % dbs->table_size; |
||||
|
#endif |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
llmsset_t |
||||
|
llmsset_create(size_t initial_size, size_t max_size) |
||||
|
{ |
||||
|
#if USE_HWLOC |
||||
|
hwloc_topology_init(&topo); |
||||
|
hwloc_topology_load(topo); |
||||
|
#endif |
||||
|
|
||||
|
llmsset_t dbs = NULL; |
||||
|
if (posix_memalign((void**)&dbs, LINE_SIZE, sizeof(struct llmsset)) != 0) { |
||||
|
fprintf(stderr, "llmsset_create: Unable to allocate memory!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
#if LLMSSET_MASK |
||||
|
/* Check if initial_size and max_size are powers of 2 */ |
||||
|
if (__builtin_popcountll(initial_size) != 1) { |
||||
|
fprintf(stderr, "llmsset_create: initial_size is not a power of 2!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
if (__builtin_popcountll(max_size) != 1) { |
||||
|
fprintf(stderr, "llmsset_create: max_size is not a power of 2!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
if (initial_size > max_size) { |
||||
|
fprintf(stderr, "llmsset_create: initial_size > max_size!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
// minimum size is now 512 buckets (region size, but of course, n_workers * 512 is suggested as minimum) |
||||
|
|
||||
|
if (initial_size < 512) { |
||||
|
fprintf(stderr, "llmsset_create: initial_size too small!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
dbs->max_size = max_size; |
||||
|
llmsset_set_size(dbs, initial_size); |
||||
|
|
||||
|
/* This implementation of "resizable hash table" allocates the max_size table in virtual memory, |
||||
|
but only uses the "actual size" part in real memory */ |
||||
|
|
||||
|
dbs->table = (uint64_t*)mmap(0, dbs->max_size * 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
||||
|
dbs->data = (uint8_t*)mmap(0, dbs->max_size * 16, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
||||
|
|
||||
|
/* Also allocate bitmaps. Each region is 64*8 = 512 buckets. |
||||
|
Overhead of bitmap1: 1 bit per 4096 bucket. |
||||
|
Overhead of bitmap2: 1 bit per bucket. |
||||
|
Overhead of bitmapc: 1 bit per bucket. */ |
||||
|
|
||||
|
dbs->bitmap1 = (uint64_t*)mmap(0, dbs->max_size / (512*8), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
||||
|
dbs->bitmap2 = (uint64_t*)mmap(0, dbs->max_size / 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
||||
|
dbs->bitmapc = (uint64_t*)mmap(0, dbs->max_size / 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
||||
|
|
||||
|
if (dbs->table == (uint64_t*)-1 || dbs->data == (uint8_t*)-1 || dbs->bitmap1 == (uint64_t*)-1 || dbs->bitmap2 == (uint64_t*)-1 || dbs->bitmapc == (uint64_t*)-1) { |
||||
|
fprintf(stderr, "llmsset_create: Unable to allocate memory: %s!\n", strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
#if defined(madvise) && defined(MADV_RANDOM) |
||||
|
madvise(dbs->table, dbs->max_size * 8, MADV_RANDOM); |
||||
|
#endif |
||||
|
|
||||
|
#if USE_HWLOC |
||||
|
hwloc_set_area_membind(topo, dbs->table, dbs->max_size * 8, hwloc_topology_get_allowed_cpuset(topo), HWLOC_MEMBIND_INTERLEAVE, 0); |
||||
|
hwloc_set_area_membind(topo, dbs->data, dbs->max_size * 16, hwloc_topology_get_allowed_cpuset(topo), HWLOC_MEMBIND_FIRSTTOUCH, 0); |
||||
|
hwloc_set_area_membind(topo, dbs->bitmap1, dbs->max_size / (512*8), hwloc_topology_get_allowed_cpuset(topo), HWLOC_MEMBIND_INTERLEAVE, 0); |
||||
|
hwloc_set_area_membind(topo, dbs->bitmap2, dbs->max_size / 8, hwloc_topology_get_allowed_cpuset(topo), HWLOC_MEMBIND_FIRSTTOUCH, 0); |
||||
|
hwloc_set_area_membind(topo, dbs->bitmapc, dbs->max_size / 8, hwloc_topology_get_allowed_cpuset(topo), HWLOC_MEMBIND_FIRSTTOUCH, 0); |
||||
|
#endif |
||||
|
|
||||
|
// forbid first two positions (index 0 and 1) |
||||
|
dbs->bitmap2[0] = 0xc000000000000000LL; |
||||
|
|
||||
|
dbs->hash_cb = NULL; |
||||
|
dbs->equals_cb = NULL; |
||||
|
dbs->create_cb = NULL; |
||||
|
dbs->destroy_cb = NULL; |
||||
|
|
||||
|
// yes, ugly. for now, we use a global thread-local value. |
||||
|
// that is a problem with multiple tables. |
||||
|
// so, for now, do NOT use multiple tables!! |
||||
|
|
||||
|
LACE_ME; |
||||
|
INIT_THREAD_LOCAL(my_region); |
||||
|
TOGETHER(llmsset_reset_region); |
||||
|
|
||||
|
return dbs; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
llmsset_free(llmsset_t dbs) |
||||
|
{ |
||||
|
munmap(dbs->table, dbs->max_size * 8); |
||||
|
munmap(dbs->data, dbs->max_size * 16); |
||||
|
munmap(dbs->bitmap1, dbs->max_size / (512*8)); |
||||
|
munmap(dbs->bitmap2, dbs->max_size / 8); |
||||
|
munmap(dbs->bitmapc, dbs->max_size / 8); |
||||
|
free(dbs); |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_IMPL_1(llmsset_clear, llmsset_t, dbs) |
||||
|
{ |
||||
|
// just reallocate... |
||||
|
if (mmap(dbs->table, dbs->max_size * 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != (void*)-1) { |
||||
|
#if defined(madvise) && defined(MADV_RANDOM) |
||||
|
madvise(dbs->table, sizeof(uint64_t[dbs->max_size]), MADV_RANDOM); |
||||
|
#endif |
||||
|
#if USE_HWLOC |
||||
|
hwloc_set_area_membind(topo, dbs->table, sizeof(uint64_t[dbs->max_size]), hwloc_topology_get_allowed_cpuset(topo), HWLOC_MEMBIND_INTERLEAVE, 0); |
||||
|
#endif |
||||
|
} else { |
||||
|
// reallocate failed... expensive fallback |
||||
|
memset(dbs->table, 0, dbs->max_size * 8); |
||||
|
} |
||||
|
|
||||
|
if (mmap(dbs->bitmap1, dbs->max_size / (512*8), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != (void*)-1) { |
||||
|
#if USE_HWLOC |
||||
|
hwloc_set_area_membind(topo, dbs->bitmap1, dbs->max_size / (512*8), hwloc_topology_get_allowed_cpuset(topo), HWLOC_MEMBIND_INTERLEAVE, 0); |
||||
|
#endif |
||||
|
} else { |
||||
|
memset(dbs->bitmap1, 0, dbs->max_size / (512*8)); |
||||
|
} |
||||
|
|
||||
|
if (mmap(dbs->bitmap2, dbs->max_size / 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != (void*)-1) { |
||||
|
#if USE_HWLOC |
||||
|
hwloc_set_area_membind(topo, dbs->bitmap2, dbs->max_size / 8, hwloc_topology_get_allowed_cpuset(topo), HWLOC_MEMBIND_FIRSTTOUCH, 0); |
||||
|
#endif |
||||
|
} else { |
||||
|
memset(dbs->bitmap2, 0, dbs->max_size / 8); |
||||
|
} |
||||
|
|
||||
|
// forbid first two positions (index 0 and 1) |
||||
|
dbs->bitmap2[0] = 0xc000000000000000LL; |
||||
|
|
||||
|
TOGETHER(llmsset_reset_region); |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
llmsset_is_marked(const llmsset_t dbs, uint64_t index) |
||||
|
{ |
||||
|
volatile uint64_t *ptr = dbs->bitmap2 + (index/64); |
||||
|
uint64_t mask = 0x8000000000000000LL >> (index&63); |
||||
|
return (*ptr & mask) ? 1 : 0; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
llmsset_mark(const llmsset_t dbs, uint64_t index) |
||||
|
{ |
||||
|
volatile uint64_t *ptr = dbs->bitmap2 + (index/64); |
||||
|
uint64_t mask = 0x8000000000000000LL >> (index&63); |
||||
|
for (;;) { |
||||
|
uint64_t v = *ptr; |
||||
|
if (v & mask) return 0; |
||||
|
if (cas(ptr, v, v|mask)) return 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_3(llmsset_rehash_par, llmsset_t, dbs, size_t, first, size_t, count) |
||||
|
{ |
||||
|
if (count > 512) { |
||||
|
size_t split = count/2; |
||||
|
SPAWN(llmsset_rehash_par, dbs, first, split); |
||||
|
CALL(llmsset_rehash_par, dbs, first + split, count - split); |
||||
|
SYNC(llmsset_rehash_par); |
||||
|
} else { |
||||
|
uint64_t *ptr = dbs->bitmap2 + (first / 64); |
||||
|
uint64_t mask = 0x8000000000000000LL >> (first & 63); |
||||
|
for (size_t k=0; k<count; k++) { |
||||
|
if (*ptr & mask) llmsset_rehash_bucket(dbs, first+k); |
||||
|
mask >>= 1; |
||||
|
if (mask == 0) { |
||||
|
ptr++; |
||||
|
mask = 0x8000000000000000LL; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_IMPL_1(llmsset_rehash, llmsset_t, dbs) |
||||
|
{ |
||||
|
CALL(llmsset_rehash_par, dbs, 0, dbs->table_size); |
||||
|
} |
||||
|
|
||||
|
TASK_3(size_t, llmsset_count_marked_par, llmsset_t, dbs, size_t, first, size_t, count) |
||||
|
{ |
||||
|
if (count > 512) { |
||||
|
size_t split = count/2; |
||||
|
SPAWN(llmsset_count_marked_par, dbs, first, split); |
||||
|
size_t right = CALL(llmsset_count_marked_par, dbs, first + split, count - split); |
||||
|
size_t left = SYNC(llmsset_count_marked_par); |
||||
|
return left + right; |
||||
|
} else { |
||||
|
size_t result = 0; |
||||
|
uint64_t *ptr = dbs->bitmap2 + (first / 64); |
||||
|
if (count == 512) { |
||||
|
result += __builtin_popcountll(ptr[0]); |
||||
|
result += __builtin_popcountll(ptr[1]); |
||||
|
result += __builtin_popcountll(ptr[2]); |
||||
|
result += __builtin_popcountll(ptr[3]); |
||||
|
result += __builtin_popcountll(ptr[4]); |
||||
|
result += __builtin_popcountll(ptr[5]); |
||||
|
result += __builtin_popcountll(ptr[6]); |
||||
|
result += __builtin_popcountll(ptr[7]); |
||||
|
} else { |
||||
|
uint64_t mask = 0x8000000000000000LL >> (first & 63); |
||||
|
for (size_t k=0; k<count; k++) { |
||||
|
if (*ptr & mask) result += 1; |
||||
|
mask >>= 1; |
||||
|
if (mask == 0) { |
||||
|
ptr++; |
||||
|
mask = 0x8000000000000000LL; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_1(size_t, llmsset_count_marked, llmsset_t, dbs) |
||||
|
{ |
||||
|
return CALL(llmsset_count_marked_par, dbs, 0, dbs->table_size); |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_3(llmsset_destroy_par, llmsset_t, dbs, size_t, first, size_t, count) |
||||
|
{ |
||||
|
if (count > 1024) { |
||||
|
size_t split = count/2; |
||||
|
SPAWN(llmsset_destroy_par, dbs, first, split); |
||||
|
CALL(llmsset_destroy_par, dbs, first + split, count - split); |
||||
|
SYNC(llmsset_destroy_par); |
||||
|
} else { |
||||
|
for (size_t k=first; k<first+count; k++) { |
||||
|
volatile uint64_t *ptr2 = dbs->bitmap2 + (k/64); |
||||
|
volatile uint64_t *ptrc = dbs->bitmapc + (k/64); |
||||
|
uint64_t mask = 0x8000000000000000LL >> (k&63); |
||||
|
|
||||
|
// if not marked but is custom |
||||
|
if ((*ptr2 & mask) == 0 && (*ptrc & mask)) { |
||||
|
uint64_t *d_ptr = ((uint64_t*)dbs->data) + 2*k; |
||||
|
dbs->destroy_cb(d_ptr[0], d_ptr[1]); |
||||
|
*ptrc &= ~mask; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_IMPL_1(llmsset_destroy_unmarked, llmsset_t, dbs) |
||||
|
{ |
||||
|
if (dbs->destroy_cb == NULL) return; // no custom function |
||||
|
CALL(llmsset_destroy_par, dbs, 0, dbs->table_size); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Set custom functions |
||||
|
*/ |
||||
|
void llmsset_set_custom(const llmsset_t dbs, llmsset_hash_cb hash_cb, llmsset_equals_cb equals_cb, llmsset_create_cb create_cb, llmsset_destroy_cb destroy_cb) |
||||
|
{ |
||||
|
dbs->hash_cb = hash_cb; |
||||
|
dbs->equals_cb = equals_cb; |
||||
|
dbs->create_cb = create_cb; |
||||
|
dbs->destroy_cb = destroy_cb; |
||||
|
} |
@ -0,0 +1,202 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2014 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#include <sylvan_config.h> |
||||
|
#include <stdint.h> |
||||
|
#include <unistd.h> |
||||
|
|
||||
|
#include <lace.h> |
||||
|
|
||||
|
#ifndef LLMSSET_H |
||||
|
#define LLMSSET_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#ifndef LLMSSET_MASK |
||||
|
#define LLMSSET_MASK 0 // set to 1 to use bit mask instead of modulo |
||||
|
#endif |
||||
|
|
||||
|
/** |
||||
|
* Lockless hash table (set) to store 16-byte keys. |
||||
|
* Each unique key is associated with a 42-bit number. |
||||
|
* |
||||
|
* The set has support for stop-the-world garbage collection. |
||||
|
* Methods llmsset_clear, llmsset_mark and llmsset_rehash implement garbage collection. |
||||
|
* During their execution, llmsset_lookup is not allowed. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* hash(a, b, seed) |
||||
|
* equals(lhs_a, lhs_b, rhs_a, rhs_b) |
||||
|
* create(a, b) -- with a,b pointers, allows changing pointers on create of node, |
||||
|
* but must keep hash/equals same! |
||||
|
* destroy(a, b) |
||||
|
*/ |
||||
|
typedef uint64_t (*llmsset_hash_cb)(uint64_t, uint64_t, uint64_t); |
||||
|
typedef int (*llmsset_equals_cb)(uint64_t, uint64_t, uint64_t, uint64_t); |
||||
|
typedef void (*llmsset_create_cb)(uint64_t *, uint64_t *); |
||||
|
typedef void (*llmsset_destroy_cb)(uint64_t, uint64_t); |
||||
|
|
||||
|
typedef struct llmsset |
||||
|
{ |
||||
|
uint64_t *table; // table with hashes |
||||
|
uint8_t *data; // table with values |
||||
|
uint64_t *bitmap1; // ownership bitmap (per 512 buckets) |
||||
|
uint64_t *bitmap2; // bitmap for "contains data" |
||||
|
uint64_t *bitmapc; // bitmap for "use custom functions" |
||||
|
size_t max_size; // maximum size of the hash table (for resizing) |
||||
|
size_t table_size; // size of the hash table (number of slots) --> power of 2! |
||||
|
#if LLMSSET_MASK |
||||
|
size_t mask; // size-1 |
||||
|
#endif |
||||
|
size_t f_size; |
||||
|
llmsset_hash_cb hash_cb; // custom hash function |
||||
|
llmsset_equals_cb equals_cb; // custom equals function |
||||
|
llmsset_create_cb create_cb; // custom create function |
||||
|
llmsset_destroy_cb destroy_cb; // custom destroy function |
||||
|
int16_t threshold; // number of iterations for insertion until returning error |
||||
|
} *llmsset_t; |
||||
|
|
||||
|
/** |
||||
|
* Retrieve a pointer to the data associated with the 42-bit value. |
||||
|
*/ |
||||
|
static inline void* |
||||
|
llmsset_index_to_ptr(const llmsset_t dbs, size_t index) |
||||
|
{ |
||||
|
return dbs->data + index * 16; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Create the set. |
||||
|
* This will allocate a set of <max_size> buckets in virtual memory. |
||||
|
* The actual space used is <initial_size> buckets. |
||||
|
*/ |
||||
|
llmsset_t llmsset_create(size_t initial_size, size_t max_size); |
||||
|
|
||||
|
/** |
||||
|
* Free the set. |
||||
|
*/ |
||||
|
void llmsset_free(llmsset_t dbs); |
||||
|
|
||||
|
/** |
||||
|
* Retrieve the maximum size of the set. |
||||
|
*/ |
||||
|
static inline size_t |
||||
|
llmsset_get_max_size(const llmsset_t dbs) |
||||
|
{ |
||||
|
return dbs->max_size; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Retrieve the current size of the lockless MS set. |
||||
|
*/ |
||||
|
static inline size_t |
||||
|
llmsset_get_size(const llmsset_t dbs) |
||||
|
{ |
||||
|
return dbs->table_size; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Set the table size of the set. |
||||
|
* Typically called during garbage collection, after clear and before rehash. |
||||
|
* Returns 0 if dbs->table_size > dbs->max_size! |
||||
|
*/ |
||||
|
static inline void |
||||
|
llmsset_set_size(llmsset_t dbs, size_t size) |
||||
|
{ |
||||
|
/* check bounds (don't be rediculous) */ |
||||
|
if (size > 128 && size <= dbs->max_size) { |
||||
|
dbs->table_size = size; |
||||
|
#if LLMSSET_MASK |
||||
|
/* Warning: if size is not a power of two, you will get interesting behavior */ |
||||
|
dbs->mask = dbs->table_size - 1; |
||||
|
#endif |
||||
|
dbs->threshold = (64 - __builtin_clzll(dbs->table_size)) + 4; // doubling table_size increases threshold by 1 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Core function: find existing data or add new. |
||||
|
* Returns the unique 42-bit value associated with the data, or 0 when table is full. |
||||
|
* Also, this value will never equal 0 or 1. |
||||
|
* Note: garbage collection during lookup strictly forbidden |
||||
|
*/ |
||||
|
uint64_t llmsset_lookup(const llmsset_t dbs, const uint64_t a, const uint64_t b, int *created); |
||||
|
|
||||
|
/** |
||||
|
* Same as lookup, but use the custom functions |
||||
|
*/ |
||||
|
uint64_t llmsset_lookupc(const llmsset_t dbs, const uint64_t a, const uint64_t b, int *created); |
||||
|
|
||||
|
/** |
||||
|
* To perform garbage collection, the user is responsible that no lookups are performed during the process. |
||||
|
* |
||||
|
* 1) call llmsset_clear |
||||
|
* 2) call llmsset_mark for every bucket to rehash |
||||
|
* 3) call llmsset_rehash |
||||
|
*/ |
||||
|
VOID_TASK_DECL_1(llmsset_clear, llmsset_t); |
||||
|
#define llmsset_clear(dbs) CALL(llmsset_clear, dbs) |
||||
|
|
||||
|
/** |
||||
|
* Check if a certain data bucket is marked (in use). |
||||
|
*/ |
||||
|
int llmsset_is_marked(const llmsset_t dbs, uint64_t index); |
||||
|
|
||||
|
/** |
||||
|
* During garbage collection, buckets are marked (for rehashing) with this function. |
||||
|
* Returns 0 if the node was already marked, or non-zero if it was not marked. |
||||
|
* May also return non-zero if multiple workers marked at the same time. |
||||
|
*/ |
||||
|
int llmsset_mark(const llmsset_t dbs, uint64_t index); |
||||
|
|
||||
|
/** |
||||
|
* Rehash all marked buckets. |
||||
|
*/ |
||||
|
VOID_TASK_DECL_1(llmsset_rehash, llmsset_t); |
||||
|
#define llmsset_rehash(dbs) CALL(llmsset_rehash, dbs) |
||||
|
|
||||
|
/** |
||||
|
* Retrieve number of marked buckets. |
||||
|
*/ |
||||
|
TASK_DECL_1(size_t, llmsset_count_marked, llmsset_t); |
||||
|
#define llmsset_count_marked(dbs) CALL(llmsset_count_marked, dbs) |
||||
|
|
||||
|
/** |
||||
|
* During garbage collection, this method calls the destroy callback |
||||
|
* for all 'custom' data that is not kept. |
||||
|
*/ |
||||
|
VOID_TASK_DECL_1(llmsset_destroy_unmarked, llmsset_t); |
||||
|
#define llmsset_destroy_unmarked(dbs) CALL(llmsset_destroy_unmarked, dbs) |
||||
|
|
||||
|
/** |
||||
|
* Set custom functions |
||||
|
*/ |
||||
|
void llmsset_set_custom(const llmsset_t dbs, llmsset_hash_cb hash_cb, llmsset_equals_cb equals_cb, llmsset_create_cb create_cb, llmsset_destroy_cb destroy_cb); |
||||
|
|
||||
|
/** |
||||
|
* Default hashing function |
||||
|
*/ |
||||
|
uint64_t llmsset_hash(const uint64_t a, const uint64_t b, const uint64_t seed); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
|
||||
|
#endif |
@ -0,0 +1,598 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#include <sylvan_config.h> |
||||
|
|
||||
|
#include <assert.h> // for assert |
||||
|
#include <errno.h> // for errno |
||||
|
#include <stdio.h> // for fprintf |
||||
|
#include <stdint.h> // for uint32_t etc |
||||
|
#include <stdlib.h> // for exit |
||||
|
#include <string.h> // for strerror |
||||
|
#include <sys/mman.h> // for mmap |
||||
|
|
||||
|
#include <refs.h> |
||||
|
|
||||
|
#ifndef compiler_barrier |
||||
|
#define compiler_barrier() { asm volatile("" ::: "memory"); } |
||||
|
#endif |
||||
|
|
||||
|
#ifndef cas |
||||
|
#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new))) |
||||
|
#endif |
||||
|
|
||||
|
/** |
||||
|
* Implementation of external references |
||||
|
* Based on a hash table for 40-bit non-null values, linear probing |
||||
|
* Use tombstones for deleting, higher bits for reference count |
||||
|
*/ |
||||
|
static const uint64_t refs_ts = 0x7fffffffffffffff; // tombstone |
||||
|
|
||||
|
/* FNV-1a 64-bit hash */ |
||||
|
static inline uint64_t |
||||
|
fnv_hash(uint64_t a) |
||||
|
{ |
||||
|
const uint64_t prime = 1099511628211; |
||||
|
uint64_t hash = 14695981039346656037LLU; |
||||
|
hash = (hash ^ a) * prime; |
||||
|
hash = (hash ^ ((a << 25) | (a >> 39))) * prime; |
||||
|
return hash ^ (hash >> 32); |
||||
|
} |
||||
|
|
||||
|
// Count number of unique entries (not number of references) |
||||
|
size_t |
||||
|
refs_count(refs_table_t *tbl) |
||||
|
{ |
||||
|
size_t count = 0; |
||||
|
uint64_t *bucket = tbl->refs_table; |
||||
|
uint64_t * const end = bucket + tbl->refs_size; |
||||
|
while (bucket != end) { |
||||
|
if (*bucket != 0 && *bucket != refs_ts) count++; |
||||
|
bucket++; |
||||
|
} |
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
refs_rehash(refs_table_t *tbl, uint64_t v) |
||||
|
{ |
||||
|
if (v == 0) return; // do not rehash empty value |
||||
|
if (v == refs_ts) return; // do not rehash tombstone |
||||
|
|
||||
|
volatile uint64_t *bucket = tbl->refs_table + (fnv_hash(v & 0x000000ffffffffff) % tbl->refs_size); |
||||
|
uint64_t * const end = tbl->refs_table + tbl->refs_size; |
||||
|
|
||||
|
int i = 128; // try 128 times linear probing |
||||
|
while (i--) { |
||||
|
if (*bucket == 0) { if (cas(bucket, 0, v)) return; } |
||||
|
if (++bucket == end) bucket = tbl->refs_table; |
||||
|
} |
||||
|
|
||||
|
// assert(0); // impossible! |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Called internally to assist resize operations |
||||
|
* Returns 1 for retry, 0 for done |
||||
|
*/ |
||||
|
static int |
||||
|
refs_resize_help(refs_table_t *tbl) |
||||
|
{ |
||||
|
if (0 == (tbl->refs_control & 0xf0000000)) return 0; // no resize in progress (anymore) |
||||
|
if (tbl->refs_control & 0x80000000) return 1; // still waiting for preparation |
||||
|
|
||||
|
if (tbl->refs_resize_part >= tbl->refs_resize_size / 128) return 1; // all parts claimed |
||||
|
size_t part = __sync_fetch_and_add(&tbl->refs_resize_part, 1); |
||||
|
if (part >= tbl->refs_resize_size/128) return 1; // all parts claimed |
||||
|
|
||||
|
// rehash all |
||||
|
int i; |
||||
|
volatile uint64_t *bucket = tbl->refs_resize_table + part * 128; |
||||
|
for (i=0; i<128; i++) refs_rehash(tbl, *bucket++); |
||||
|
|
||||
|
__sync_fetch_and_add(&tbl->refs_resize_done, 1); |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
static void |
||||
|
refs_resize(refs_table_t *tbl) |
||||
|
{ |
||||
|
while (1) { |
||||
|
uint32_t v = tbl->refs_control; |
||||
|
if (v & 0xf0000000) { |
||||
|
// someone else started resize |
||||
|
// just rehash blocks until done |
||||
|
while (refs_resize_help(tbl)) continue; |
||||
|
return; |
||||
|
} |
||||
|
if (cas(&tbl->refs_control, v, 0x80000000 | v)) { |
||||
|
// wait until all users gone |
||||
|
while (tbl->refs_control != 0x80000000) continue; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
tbl->refs_resize_table = tbl->refs_table; |
||||
|
tbl->refs_resize_size = tbl->refs_size; |
||||
|
tbl->refs_resize_part = 0; |
||||
|
tbl->refs_resize_done = 0; |
||||
|
|
||||
|
// calculate new size |
||||
|
size_t new_size = tbl->refs_size; |
||||
|
size_t count = refs_count(tbl); |
||||
|
if (count*4 > tbl->refs_size) new_size *= 2; |
||||
|
|
||||
|
// allocate new table |
||||
|
uint64_t *new_table = (uint64_t*)mmap(0, new_size * sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); |
||||
|
if (new_table == (uint64_t*)-1) { |
||||
|
fprintf(stderr, "refs: Unable to allocate memory: %s!\n", strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
// set new data and go |
||||
|
tbl->refs_table = new_table; |
||||
|
tbl->refs_size = new_size; |
||||
|
compiler_barrier(); |
||||
|
tbl->refs_control = 0x40000000; |
||||
|
|
||||
|
// until all parts are done, rehash blocks |
||||
|
while (tbl->refs_resize_done != tbl->refs_resize_size/128) refs_resize_help(tbl); |
||||
|
|
||||
|
// done! |
||||
|
compiler_barrier(); |
||||
|
tbl->refs_control = 0; |
||||
|
|
||||
|
// unmap old table |
||||
|
munmap(tbl->refs_resize_table, tbl->refs_resize_size * sizeof(uint64_t)); |
||||
|
} |
||||
|
|
||||
|
/* Enter refs_modify */ |
||||
|
static inline void |
||||
|
refs_enter(refs_table_t *tbl) |
||||
|
{ |
||||
|
for (;;) { |
||||
|
uint32_t v = tbl->refs_control; |
||||
|
if (v & 0xf0000000) { |
||||
|
while (refs_resize_help(tbl)) continue; |
||||
|
} else { |
||||
|
if (cas(&tbl->refs_control, v, v+1)) return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Leave refs_modify */ |
||||
|
static inline void |
||||
|
refs_leave(refs_table_t *tbl) |
||||
|
{ |
||||
|
for (;;) { |
||||
|
uint32_t v = tbl->refs_control; |
||||
|
if (cas(&tbl->refs_control, v, v-1)) return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static inline int |
||||
|
refs_modify(refs_table_t *tbl, const uint64_t a, const int dir) |
||||
|
{ |
||||
|
volatile uint64_t *bucket; |
||||
|
volatile uint64_t *ts_bucket; |
||||
|
uint64_t v, new_v; |
||||
|
int res, i; |
||||
|
|
||||
|
refs_enter(tbl); |
||||
|
|
||||
|
ref_retry: |
||||
|
bucket = tbl->refs_table + (fnv_hash(a) & (tbl->refs_size - 1)); |
||||
|
ts_bucket = NULL; // tombstone |
||||
|
i = 128; // try 128 times linear probing |
||||
|
|
||||
|
while (i--) { |
||||
|
ref_restart: |
||||
|
v = *bucket; |
||||
|
if (v == refs_ts) { |
||||
|
if (ts_bucket == NULL) ts_bucket = bucket; |
||||
|
} else if (v == 0) { |
||||
|
// not found |
||||
|
res = 0; |
||||
|
if (dir < 0) goto ref_exit; |
||||
|
if (ts_bucket != NULL) { |
||||
|
bucket = ts_bucket; |
||||
|
ts_bucket = NULL; |
||||
|
v = refs_ts; |
||||
|
} |
||||
|
new_v = a | (1ULL << 40); |
||||
|
goto ref_mod; |
||||
|
} else if ((v & 0x000000ffffffffff) == a) { |
||||
|
// found |
||||
|
res = 1; |
||||
|
uint64_t count = v >> 40; |
||||
|
if (count == 0x7fffff) goto ref_exit; |
||||
|
count += dir; |
||||
|
if (count == 0) new_v = refs_ts; |
||||
|
else new_v = a | (count << 40); |
||||
|
goto ref_mod; |
||||
|
} |
||||
|
|
||||
|
if (++bucket == tbl->refs_table + tbl->refs_size) bucket = tbl->refs_table; |
||||
|
} |
||||
|
|
||||
|
// not found after linear probing |
||||
|
if (dir < 0) { |
||||
|
res = 0; |
||||
|
goto ref_exit; |
||||
|
} else if (ts_bucket != NULL) { |
||||
|
bucket = ts_bucket; |
||||
|
ts_bucket = NULL; |
||||
|
v = refs_ts; |
||||
|
new_v = a | (1ULL << 40); |
||||
|
if (!cas(bucket, v, new_v)) goto ref_retry; |
||||
|
res = 1; |
||||
|
goto ref_exit; |
||||
|
} else { |
||||
|
// hash table full |
||||
|
refs_leave(tbl); |
||||
|
refs_resize(tbl); |
||||
|
return refs_modify(tbl, a, dir); |
||||
|
} |
||||
|
|
||||
|
ref_mod: |
||||
|
if (!cas(bucket, v, new_v)) goto ref_restart; |
||||
|
|
||||
|
ref_exit: |
||||
|
refs_leave(tbl); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
refs_up(refs_table_t *tbl, uint64_t a) |
||||
|
{ |
||||
|
refs_modify(tbl, a, 1); |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
refs_down(refs_table_t *tbl, uint64_t a) |
||||
|
{ |
||||
|
#ifdef NDEBUG |
||||
|
refs_modify(tbl, a, -1); |
||||
|
#else |
||||
|
int res = refs_modify(tbl, a, -1); |
||||
|
assert(res != 0); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
uint64_t* |
||||
|
refs_iter(refs_table_t *tbl, size_t first, size_t end) |
||||
|
{ |
||||
|
// assert(first < tbl->refs_size); |
||||
|
// assert(end <= tbl->refs_size); |
||||
|
|
||||
|
uint64_t *bucket = tbl->refs_table + first; |
||||
|
while (bucket != tbl->refs_table + end) { |
||||
|
if (*bucket != 0 && *bucket != refs_ts) return bucket; |
||||
|
bucket++; |
||||
|
} |
||||
|
return NULL; |
||||
|
} |
||||
|
|
||||
|
uint64_t |
||||
|
refs_next(refs_table_t *tbl, uint64_t **_bucket, size_t end) |
||||
|
{ |
||||
|
uint64_t *bucket = *_bucket; |
||||
|
// assert(bucket != NULL); |
||||
|
// assert(end <= tbl->refs_size); |
||||
|
uint64_t result = *bucket & 0x000000ffffffffff; |
||||
|
bucket++; |
||||
|
while (bucket != tbl->refs_table + end) { |
||||
|
if (*bucket != 0 && *bucket != refs_ts) { |
||||
|
*_bucket = bucket; |
||||
|
return result; |
||||
|
} |
||||
|
bucket++; |
||||
|
} |
||||
|
*_bucket = NULL; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
refs_create(refs_table_t *tbl, size_t _refs_size) |
||||
|
{ |
||||
|
if (__builtin_popcountll(_refs_size) != 1) { |
||||
|
fprintf(stderr, "refs: Table size must be a power of 2!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
tbl->refs_size = _refs_size; |
||||
|
tbl->refs_table = (uint64_t*)mmap(0, tbl->refs_size * sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); |
||||
|
if (tbl->refs_table == (uint64_t*)-1) { |
||||
|
fprintf(stderr, "refs: Unable to allocate memory: %s!\n", strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
refs_free(refs_table_t *tbl) |
||||
|
{ |
||||
|
munmap(tbl->refs_table, tbl->refs_size * sizeof(uint64_t)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Simple implementation of a 64-bit resizable hash-table |
||||
|
* No idea if this is scalable... :( but it seems thread-safe |
||||
|
*/ |
||||
|
|
||||
|
// Count number of unique entries (not number of references) |
||||
|
size_t |
||||
|
protect_count(refs_table_t *tbl) |
||||
|
{ |
||||
|
size_t count = 0; |
||||
|
uint64_t *bucket = tbl->refs_table; |
||||
|
uint64_t * const end = bucket + tbl->refs_size; |
||||
|
while (bucket != end) { |
||||
|
if (*bucket != 0 && *bucket != refs_ts) count++; |
||||
|
bucket++; |
||||
|
} |
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
protect_rehash(refs_table_t *tbl, uint64_t v) |
||||
|
{ |
||||
|
if (v == 0) return; // do not rehash empty value |
||||
|
if (v == refs_ts) return; // do not rehash tombstone |
||||
|
|
||||
|
volatile uint64_t *bucket = tbl->refs_table + (fnv_hash(v) % tbl->refs_size); |
||||
|
uint64_t * const end = tbl->refs_table + tbl->refs_size; |
||||
|
|
||||
|
int i = 128; // try 128 times linear probing |
||||
|
while (i--) { |
||||
|
if (*bucket == 0 && cas(bucket, 0, v)) return; |
||||
|
if (++bucket == end) bucket = tbl->refs_table; |
||||
|
} |
||||
|
|
||||
|
assert(0); // whoops! |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Called internally to assist resize operations |
||||
|
* Returns 1 for retry, 0 for done |
||||
|
*/ |
||||
|
static int |
||||
|
protect_resize_help(refs_table_t *tbl) |
||||
|
{ |
||||
|
if (0 == (tbl->refs_control & 0xf0000000)) return 0; // no resize in progress (anymore) |
||||
|
if (tbl->refs_control & 0x80000000) return 1; // still waiting for preparation |
||||
|
if (tbl->refs_resize_part >= tbl->refs_resize_size / 128) return 1; // all parts claimed |
||||
|
size_t part = __sync_fetch_and_add(&tbl->refs_resize_part, 1); |
||||
|
if (part >= tbl->refs_resize_size/128) return 1; // all parts claimed |
||||
|
|
||||
|
// rehash all |
||||
|
int i; |
||||
|
volatile uint64_t *bucket = tbl->refs_resize_table + part * 128; |
||||
|
for (i=0; i<128; i++) protect_rehash(tbl, *bucket++); |
||||
|
|
||||
|
__sync_fetch_and_add(&tbl->refs_resize_done, 1); |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
static void |
||||
|
protect_resize(refs_table_t *tbl) |
||||
|
{ |
||||
|
while (1) { |
||||
|
uint32_t v = tbl->refs_control; |
||||
|
if (v & 0xf0000000) { |
||||
|
// someone else started resize |
||||
|
// just rehash blocks until done |
||||
|
while (protect_resize_help(tbl)) continue; |
||||
|
return; |
||||
|
} |
||||
|
if (cas(&tbl->refs_control, v, 0x80000000 | v)) { |
||||
|
// wait until all users gone |
||||
|
while (tbl->refs_control != 0x80000000) continue; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
tbl->refs_resize_table = tbl->refs_table; |
||||
|
tbl->refs_resize_size = tbl->refs_size; |
||||
|
tbl->refs_resize_part = 0; |
||||
|
tbl->refs_resize_done = 0; |
||||
|
|
||||
|
// calculate new size |
||||
|
size_t new_size = tbl->refs_size; |
||||
|
size_t count = refs_count(tbl); |
||||
|
if (count*4 > tbl->refs_size) new_size *= 2; |
||||
|
|
||||
|
// allocate new table |
||||
|
uint64_t *new_table = (uint64_t*)mmap(0, new_size * sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); |
||||
|
if (new_table == (uint64_t*)-1) { |
||||
|
fprintf(stderr, "refs: Unable to allocate memory: %s!\n", strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
// set new data and go |
||||
|
tbl->refs_table = new_table; |
||||
|
tbl->refs_size = new_size; |
||||
|
compiler_barrier(); |
||||
|
tbl->refs_control = 0x40000000; |
||||
|
|
||||
|
// until all parts are done, rehash blocks |
||||
|
while (tbl->refs_resize_done < tbl->refs_resize_size/128) protect_resize_help(tbl); |
||||
|
|
||||
|
// done! |
||||
|
compiler_barrier(); |
||||
|
tbl->refs_control = 0; |
||||
|
|
||||
|
// unmap old table |
||||
|
munmap(tbl->refs_resize_table, tbl->refs_resize_size * sizeof(uint64_t)); |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
protect_enter(refs_table_t *tbl) |
||||
|
{ |
||||
|
for (;;) { |
||||
|
uint32_t v = tbl->refs_control; |
||||
|
if (v & 0xf0000000) { |
||||
|
while (protect_resize_help(tbl)) continue; |
||||
|
} else { |
||||
|
if (cas(&tbl->refs_control, v, v+1)) return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
protect_leave(refs_table_t *tbl) |
||||
|
{ |
||||
|
for (;;) { |
||||
|
uint32_t v = tbl->refs_control; |
||||
|
if (cas(&tbl->refs_control, v, v-1)) return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
protect_up(refs_table_t *tbl, uint64_t a) |
||||
|
{ |
||||
|
volatile uint64_t *bucket; |
||||
|
volatile uint64_t *ts_bucket; |
||||
|
uint64_t v; |
||||
|
int i; |
||||
|
|
||||
|
protect_enter(tbl); |
||||
|
|
||||
|
ref_retry: |
||||
|
bucket = tbl->refs_table + (fnv_hash(a) & (tbl->refs_size - 1)); |
||||
|
ts_bucket = NULL; // tombstone |
||||
|
i = 128; // try 128 times linear probing |
||||
|
|
||||
|
while (i--) { |
||||
|
ref_restart: |
||||
|
v = *bucket; |
||||
|
if (v == refs_ts) { |
||||
|
if (ts_bucket == NULL) ts_bucket = bucket; |
||||
|
} else if (v == 0) { |
||||
|
// go go go |
||||
|
if (ts_bucket != NULL) { |
||||
|
if (cas(ts_bucket, refs_ts, a)) { |
||||
|
protect_leave(tbl); |
||||
|
return; |
||||
|
} else { |
||||
|
goto ref_retry; |
||||
|
} |
||||
|
} else { |
||||
|
if (cas(bucket, 0, a)) { |
||||
|
protect_leave(tbl); |
||||
|
return; |
||||
|
} else { |
||||
|
goto ref_restart; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (++bucket == tbl->refs_table + tbl->refs_size) bucket = tbl->refs_table; |
||||
|
} |
||||
|
|
||||
|
// not found after linear probing |
||||
|
if (ts_bucket != NULL) { |
||||
|
if (cas(ts_bucket, refs_ts, a)) { |
||||
|
protect_leave(tbl); |
||||
|
return; |
||||
|
} else { |
||||
|
goto ref_retry; |
||||
|
} |
||||
|
} else { |
||||
|
// hash table full |
||||
|
protect_leave(tbl); |
||||
|
protect_resize(tbl); |
||||
|
protect_enter(tbl); |
||||
|
goto ref_retry; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
protect_down(refs_table_t *tbl, uint64_t a) |
||||
|
{ |
||||
|
volatile uint64_t *bucket; |
||||
|
protect_enter(tbl); |
||||
|
|
||||
|
bucket = tbl->refs_table + (fnv_hash(a) & (tbl->refs_size - 1)); |
||||
|
int i = 128; // try 128 times linear probing |
||||
|
|
||||
|
while (i--) { |
||||
|
if (*bucket == a) { |
||||
|
*bucket = refs_ts; |
||||
|
protect_leave(tbl); |
||||
|
return; |
||||
|
} |
||||
|
if (++bucket == tbl->refs_table + tbl->refs_size) bucket = tbl->refs_table; |
||||
|
} |
||||
|
|
||||
|
// not found after linear probing |
||||
|
assert(0); |
||||
|
} |
||||
|
|
||||
|
uint64_t* |
||||
|
protect_iter(refs_table_t *tbl, size_t first, size_t end) |
||||
|
{ |
||||
|
// assert(first < tbl->refs_size); |
||||
|
// assert(end <= tbl->refs_size); |
||||
|
|
||||
|
uint64_t *bucket = tbl->refs_table + first; |
||||
|
while (bucket != tbl->refs_table + end) { |
||||
|
if (*bucket != 0 && *bucket != refs_ts) return bucket; |
||||
|
bucket++; |
||||
|
} |
||||
|
return NULL; |
||||
|
} |
||||
|
|
||||
|
uint64_t |
||||
|
protect_next(refs_table_t *tbl, uint64_t **_bucket, size_t end) |
||||
|
{ |
||||
|
uint64_t *bucket = *_bucket; |
||||
|
// assert(bucket != NULL); |
||||
|
// assert(end <= tbl->refs_size); |
||||
|
uint64_t result = *bucket; |
||||
|
bucket++; |
||||
|
while (bucket != tbl->refs_table + end) { |
||||
|
if (*bucket != 0 && *bucket != refs_ts) { |
||||
|
*_bucket = bucket; |
||||
|
return result; |
||||
|
} |
||||
|
bucket++; |
||||
|
} |
||||
|
*_bucket = NULL; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
protect_create(refs_table_t *tbl, size_t _refs_size) |
||||
|
{ |
||||
|
if (__builtin_popcountll(_refs_size) != 1) { |
||||
|
fprintf(stderr, "refs: Table size must be a power of 2!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
tbl->refs_size = _refs_size; |
||||
|
tbl->refs_table = (uint64_t*)mmap(0, tbl->refs_size * sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); |
||||
|
if (tbl->refs_table == (uint64_t*)-1) { |
||||
|
fprintf(stderr, "refs: Unable to allocate memory: %s!\n", strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
protect_free(refs_table_t *tbl) |
||||
|
{ |
||||
|
munmap(tbl->refs_table, tbl->refs_size * sizeof(uint64_t)); |
||||
|
tbl->refs_table = 0; |
||||
|
} |
@ -0,0 +1,77 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#include <sylvan_config.h> |
||||
|
#include <stdint.h> // for uint32_t etc |
||||
|
|
||||
|
#ifndef REFS_INLINE_H |
||||
|
#define REFS_INLINE_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
/** |
||||
|
* Implementation of external references |
||||
|
* Based on a hash table for 40-bit non-null values, linear probing |
||||
|
* Use tombstones for deleting, higher bits for reference count |
||||
|
*/ |
||||
|
typedef struct |
||||
|
{ |
||||
|
uint64_t *refs_table; // table itself |
||||
|
size_t refs_size; // number of buckets |
||||
|
|
||||
|
/* helpers during resize operation */ |
||||
|
volatile uint32_t refs_control; // control field |
||||
|
uint64_t *refs_resize_table; // previous table |
||||
|
size_t refs_resize_size; // size of previous table |
||||
|
size_t refs_resize_part; // which part is next |
||||
|
size_t refs_resize_done; // how many parts are done |
||||
|
} refs_table_t; |
||||
|
|
||||
|
// Count number of unique entries (not number of references) |
||||
|
size_t refs_count(refs_table_t *tbl); |
||||
|
|
||||
|
// Increase or decrease reference to 40-bit value a |
||||
|
// Will fail (assertion) if more down than up are called for a |
||||
|
void refs_up(refs_table_t *tbl, uint64_t a); |
||||
|
void refs_down(refs_table_t *tbl, uint64_t a); |
||||
|
|
||||
|
// Return a bucket or NULL to start iterating |
||||
|
uint64_t *refs_iter(refs_table_t *tbl, size_t first, size_t end); |
||||
|
|
||||
|
// Continue iterating, set bucket to next bucket or NULL |
||||
|
uint64_t refs_next(refs_table_t *tbl, uint64_t **bucket, size_t end); |
||||
|
|
||||
|
// User must supply a pointer, refs_create and refs_free handle initialization/destruction |
||||
|
void refs_create(refs_table_t *tbl, size_t _refs_size); |
||||
|
void refs_free(refs_table_t *tbl); |
||||
|
|
||||
|
// The same, but now for 64-bit values ("protect pointers") |
||||
|
size_t protect_count(refs_table_t *tbl); |
||||
|
void protect_up(refs_table_t *tbl, uint64_t a); |
||||
|
void protect_down(refs_table_t *tbl, uint64_t a); |
||||
|
uint64_t *protect_iter(refs_table_t *tbl, size_t first, size_t end); |
||||
|
uint64_t protect_next(refs_table_t *tbl, uint64_t **bucket, size_t end); |
||||
|
void protect_create(refs_table_t *tbl, size_t _refs_size); |
||||
|
void protect_free(refs_table_t *tbl); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
|
||||
|
#endif |
1067
resources/3rdparty/sylvan/src/sha2.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,197 @@ |
|||||
|
/* |
||||
|
* FILE: sha2.h |
||||
|
* AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/ |
||||
|
* |
||||
|
* Copyright (c) 2000-2001, Aaron D. Gifford |
||||
|
* All rights reserved. |
||||
|
* |
||||
|
* Redistribution and use in source and binary forms, with or without |
||||
|
* modification, are permitted provided that the following conditions |
||||
|
* are met: |
||||
|
* 1. Redistributions of source code must retain the above copyright |
||||
|
* notice, this list of conditions and the following disclaimer. |
||||
|
* 2. Redistributions in binary form must reproduce the above copyright |
||||
|
* notice, this list of conditions and the following disclaimer in the |
||||
|
* documentation and/or other materials provided with the distribution. |
||||
|
* 3. Neither the name of the copyright holder nor the names of contributors |
||||
|
* may be used to endorse or promote products derived from this software |
||||
|
* without specific prior written permission. |
||||
|
* |
||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND |
||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE |
||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||
|
* SUCH DAMAGE. |
||||
|
* |
||||
|
* $Id: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $ |
||||
|
*/ |
||||
|
|
||||
|
#ifndef __SHA2_H__ |
||||
|
#define __SHA2_H__ |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif |
||||
|
|
||||
|
|
||||
|
/* |
||||
|
* Import u_intXX_t size_t type definitions from system headers. You |
||||
|
* may need to change this, or define these things yourself in this |
||||
|
* file. |
||||
|
*/ |
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#ifdef SHA2_USE_INTTYPES_H |
||||
|
|
||||
|
#include <inttypes.h> |
||||
|
|
||||
|
#endif /* SHA2_USE_INTTYPES_H */ |
||||
|
|
||||
|
|
||||
|
/*** SHA-256/384/512 Various Length Definitions ***********************/ |
||||
|
#define SHA256_BLOCK_LENGTH 64 |
||||
|
#define SHA256_DIGEST_LENGTH 32 |
||||
|
#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) |
||||
|
#define SHA384_BLOCK_LENGTH 128 |
||||
|
#define SHA384_DIGEST_LENGTH 48 |
||||
|
#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) |
||||
|
#define SHA512_BLOCK_LENGTH 128 |
||||
|
#define SHA512_DIGEST_LENGTH 64 |
||||
|
#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) |
||||
|
|
||||
|
|
||||
|
/*** SHA-256/384/512 Context Structures *******************************/ |
||||
|
/* NOTE: If your architecture does not define either u_intXX_t types or |
||||
|
* uintXX_t (from inttypes.h), you may need to define things by hand |
||||
|
* for your system: |
||||
|
*/ |
||||
|
#if 0 |
||||
|
typedef unsigned char u_int8_t; /* 1-byte (8-bits) */ |
||||
|
typedef unsigned int u_int32_t; /* 4-bytes (32-bits) */ |
||||
|
typedef unsigned long long u_int64_t; /* 8-bytes (64-bits) */ |
||||
|
#endif |
||||
|
/* |
||||
|
* Most BSD systems already define u_intXX_t types, as does Linux. |
||||
|
* Some systems, however, like Compaq's Tru64 Unix instead can use |
||||
|
* uintXX_t types defined by very recent ANSI C standards and included |
||||
|
* in the file: |
||||
|
* |
||||
|
* #include <inttypes.h> |
||||
|
* |
||||
|
* If you choose to use <inttypes.h> then please define: |
||||
|
* |
||||
|
* #define SHA2_USE_INTTYPES_H |
||||
|
* |
||||
|
* Or on the command line during compile: |
||||
|
* |
||||
|
* cc -DSHA2_USE_INTTYPES_H ... |
||||
|
*/ |
||||
|
#ifdef SHA2_USE_INTTYPES_H |
||||
|
|
||||
|
typedef struct _SHA256_CTX { |
||||
|
uint32_t state[8]; |
||||
|
uint64_t bitcount; |
||||
|
uint8_t buffer[SHA256_BLOCK_LENGTH]; |
||||
|
} SHA256_CTX; |
||||
|
typedef struct _SHA512_CTX { |
||||
|
uint64_t state[8]; |
||||
|
uint64_t bitcount[2]; |
||||
|
uint8_t buffer[SHA512_BLOCK_LENGTH]; |
||||
|
} SHA512_CTX; |
||||
|
|
||||
|
#else /* SHA2_USE_INTTYPES_H */ |
||||
|
|
||||
|
typedef struct _SHA256_CTX { |
||||
|
u_int32_t state[8]; |
||||
|
u_int64_t bitcount; |
||||
|
u_int8_t buffer[SHA256_BLOCK_LENGTH]; |
||||
|
} SHA256_CTX; |
||||
|
typedef struct _SHA512_CTX { |
||||
|
u_int64_t state[8]; |
||||
|
u_int64_t bitcount[2]; |
||||
|
u_int8_t buffer[SHA512_BLOCK_LENGTH]; |
||||
|
} SHA512_CTX; |
||||
|
|
||||
|
#endif /* SHA2_USE_INTTYPES_H */ |
||||
|
|
||||
|
typedef SHA512_CTX SHA384_CTX; |
||||
|
|
||||
|
|
||||
|
/*** SHA-256/384/512 Function Prototypes ******************************/ |
||||
|
#ifndef NOPROTO |
||||
|
#ifdef SHA2_USE_INTTYPES_H |
||||
|
|
||||
|
void SHA256_Init(SHA256_CTX *); |
||||
|
void SHA256_Update(SHA256_CTX*, const uint8_t*, size_t); |
||||
|
void SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*); |
||||
|
char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]); |
||||
|
char* SHA256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]); |
||||
|
|
||||
|
void SHA384_Init(SHA384_CTX*); |
||||
|
void SHA384_Update(SHA384_CTX*, const uint8_t*, size_t); |
||||
|
void SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*); |
||||
|
char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]); |
||||
|
char* SHA384_Data(const uint8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]); |
||||
|
|
||||
|
void SHA512_Init(SHA512_CTX*); |
||||
|
void SHA512_Update(SHA512_CTX*, const uint8_t*, size_t); |
||||
|
void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*); |
||||
|
char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]); |
||||
|
char* SHA512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]); |
||||
|
|
||||
|
#else /* SHA2_USE_INTTYPES_H */ |
||||
|
|
||||
|
void SHA256_Init(SHA256_CTX *); |
||||
|
void SHA256_Update(SHA256_CTX*, const u_int8_t*, size_t); |
||||
|
void SHA256_Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*); |
||||
|
char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]); |
||||
|
char* SHA256_Data(const u_int8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]); |
||||
|
|
||||
|
void SHA384_Init(SHA384_CTX*); |
||||
|
void SHA384_Update(SHA384_CTX*, const u_int8_t*, size_t); |
||||
|
void SHA384_Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*); |
||||
|
char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]); |
||||
|
char* SHA384_Data(const u_int8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]); |
||||
|
|
||||
|
void SHA512_Init(SHA512_CTX*); |
||||
|
void SHA512_Update(SHA512_CTX*, const u_int8_t*, size_t); |
||||
|
void SHA512_Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*); |
||||
|
char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]); |
||||
|
char* SHA512_Data(const u_int8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]); |
||||
|
|
||||
|
#endif /* SHA2_USE_INTTYPES_H */ |
||||
|
|
||||
|
#else /* NOPROTO */ |
||||
|
|
||||
|
void SHA256_Init(); |
||||
|
void SHA256_Update(); |
||||
|
void SHA256_Final(); |
||||
|
char* SHA256_End(); |
||||
|
char* SHA256_Data(); |
||||
|
|
||||
|
void SHA384_Init(); |
||||
|
void SHA384_Update(); |
||||
|
void SHA384_Final(); |
||||
|
char* SHA384_End(); |
||||
|
char* SHA384_Data(); |
||||
|
|
||||
|
void SHA512_Init(); |
||||
|
void SHA512_Update(); |
||||
|
void SHA512_Final(); |
||||
|
char* SHA512_End(); |
||||
|
char* SHA512_Data(); |
||||
|
|
||||
|
#endif /* NOPROTO */ |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#endif /* __SHA2_H__ */ |
||||
|
|
@ -0,0 +1,245 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2014 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#include <errno.h> // for errno |
||||
|
#include <string.h> // memset |
||||
|
#include <stats.h> |
||||
|
#include <sys/mman.h> |
||||
|
#include <inttypes.h> |
||||
|
#include <sylvan.h> // for nodes table |
||||
|
|
||||
|
#if SYLVAN_STATS |
||||
|
|
||||
|
#ifdef __ELF__ |
||||
|
__thread sylvan_stats_t sylvan_stats; |
||||
|
#else |
||||
|
pthread_key_t sylvan_stats_key; |
||||
|
#endif |
||||
|
|
||||
|
#ifndef USE_HWLOC |
||||
|
#define USE_HWLOC 0 |
||||
|
#endif |
||||
|
|
||||
|
#if USE_HWLOC |
||||
|
#include <hwloc.h> |
||||
|
static hwloc_topology_t topo; |
||||
|
#endif |
||||
|
|
||||
|
VOID_TASK_0(sylvan_stats_reset_perthread) |
||||
|
{ |
||||
|
#ifdef __ELF__ |
||||
|
for (int i=0; i<SYLVAN_COUNTER_COUNTER; i++) { |
||||
|
sylvan_stats.counters[i] = 0; |
||||
|
} |
||||
|
for (int i=0; i<SYLVAN_TIMER_COUNTER; i++) { |
||||
|
sylvan_stats.timers[i] = 0; |
||||
|
} |
||||
|
#else |
||||
|
sylvan_stats_t *sylvan_stats = pthread_getspecific(sylvan_stats_key); |
||||
|
if (sylvan_stats == NULL) { |
||||
|
sylvan_stats = mmap(0, sizeof(sylvan_stats_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); |
||||
|
if (sylvan_stats == (sylvan_stats_t *)-1) { |
||||
|
fprintf(stderr, "sylvan_stats: Unable to allocate memory: %s!\n", strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
#if USE_HWLOC |
||||
|
// Ensure the stats object is on our pu |
||||
|
hwloc_obj_t pu = hwloc_get_obj_by_type(topo, HWLOC_OBJ_PU, LACE_WORKER_PU); |
||||
|
hwloc_set_area_membind(topo, sylvan_stats, sizeof(sylvan_stats_t), pu->cpuset, HWLOC_MEMBIND_BIND, 0); |
||||
|
#endif |
||||
|
pthread_setspecific(sylvan_stats_key, sylvan_stats); |
||||
|
} |
||||
|
for (int i=0; i<SYLVAN_COUNTER_COUNTER; i++) { |
||||
|
sylvan_stats->counters[i] = 0; |
||||
|
} |
||||
|
for (int i=0; i<SYLVAN_TIMER_COUNTER; i++) { |
||||
|
sylvan_stats->timers[i] = 0; |
||||
|
} |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_IMPL_0(sylvan_stats_init) |
||||
|
{ |
||||
|
#ifndef __ELF__ |
||||
|
pthread_key_create(&sylvan_stats_key, NULL); |
||||
|
#endif |
||||
|
#if USE_HWLOC |
||||
|
hwloc_topology_init(&topo); |
||||
|
hwloc_topology_load(topo); |
||||
|
#endif |
||||
|
TOGETHER(sylvan_stats_reset_perthread); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reset all counters (for statistics) |
||||
|
*/ |
||||
|
VOID_TASK_IMPL_0(sylvan_stats_reset) |
||||
|
{ |
||||
|
TOGETHER(sylvan_stats_reset_perthread); |
||||
|
} |
||||
|
|
||||
|
#define BLACK "\33[22;30m" |
||||
|
#define GRAY "\33[01;30m" |
||||
|
#define RED "\33[22;31m" |
||||
|
#define LRED "\33[01;31m" |
||||
|
#define GREEN "\33[22;32m" |
||||
|
#define LGREEN "\33[01;32m" |
||||
|
#define BLUE "\33[22;34m" |
||||
|
#define LBLUE "\33[01;34m" |
||||
|
#define BROWN "\33[22;33m" |
||||
|
#define YELLOW "\33[01;33m" |
||||
|
#define CYAN "\33[22;36m" |
||||
|
#define LCYAN "\33[22;36m" |
||||
|
#define MAGENTA "\33[22;35m" |
||||
|
#define LMAGENTA "\33[01;35m" |
||||
|
#define NC "\33[0m" |
||||
|
#define BOLD "\33[1m" |
||||
|
#define ULINE "\33[4m" //underline |
||||
|
#define BLINK "\33[5m" |
||||
|
#define INVERT "\33[7m" |
||||
|
|
||||
|
VOID_TASK_1(sylvan_stats_sum, sylvan_stats_t*, target) |
||||
|
{ |
||||
|
#ifdef __ELF__ |
||||
|
for (int i=0; i<SYLVAN_COUNTER_COUNTER; i++) { |
||||
|
__sync_fetch_and_add(&target->counters[i], sylvan_stats.counters[i]); |
||||
|
} |
||||
|
for (int i=0; i<SYLVAN_TIMER_COUNTER; i++) { |
||||
|
__sync_fetch_and_add(&target->timers[i], sylvan_stats.timers[i]); |
||||
|
} |
||||
|
#else |
||||
|
sylvan_stats_t *sylvan_stats = pthread_getspecific(sylvan_stats_key); |
||||
|
if (sylvan_stats != NULL) { |
||||
|
for (int i=0; i<SYLVAN_COUNTER_COUNTER; i++) { |
||||
|
__sync_fetch_and_add(&target->counters[i], sylvan_stats->counters[i]); |
||||
|
} |
||||
|
for (int i=0; i<SYLVAN_TIMER_COUNTER; i++) { |
||||
|
__sync_fetch_and_add(&target->timers[i], sylvan_stats->timers[i]); |
||||
|
} |
||||
|
} |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
sylvan_stats_report(FILE *target, int color) |
||||
|
{ |
||||
|
#if !SYLVAN_STATS |
||||
|
(void)target; |
||||
|
(void)color; |
||||
|
return; |
||||
|
#else |
||||
|
(void)color; |
||||
|
|
||||
|
sylvan_stats_t totals; |
||||
|
memset(&totals, 0, sizeof(sylvan_stats_t)); |
||||
|
|
||||
|
LACE_ME; |
||||
|
TOGETHER(sylvan_stats_sum, &totals); |
||||
|
|
||||
|
// fix timers for MACH |
||||
|
#ifdef __MACH__ |
||||
|
mach_timebase_info_data_t timebase; |
||||
|
mach_timebase_info(&timebase); |
||||
|
uint64_t c = timebase.numer/timebase.denom; |
||||
|
for (int i=0;i<SYLVAN_TIMER_COUNTER;i++) totals.timers[i]*=c; |
||||
|
#endif |
||||
|
|
||||
|
if (color) fprintf(target, LRED "*** " BOLD "Sylvan stats" NC LRED " ***" NC); |
||||
|
else fprintf(target, "*** Sylvan stats ***"); |
||||
|
|
||||
|
if (totals.counters[BDD_NODES_CREATED]) { |
||||
|
if (color) fprintf(target, ULINE LBLUE); |
||||
|
fprintf(target, "\nBDD operations count (cache reuse, cache put)\n"); |
||||
|
if (color) fprintf(target, NC); |
||||
|
if (totals.counters[BDD_ITE]) fprintf(target, "ITE: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_ITE], totals.counters[BDD_ITE_CACHED], totals.counters[BDD_ITE_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_AND]) fprintf(target, "AND: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_AND], totals.counters[BDD_AND_CACHED], totals.counters[BDD_AND_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_XOR]) fprintf(target, "XOR: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_XOR], totals.counters[BDD_XOR_CACHED], totals.counters[BDD_XOR_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_EXISTS]) fprintf(target, "Exists: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_EXISTS], totals.counters[BDD_EXISTS_CACHED], totals.counters[BDD_EXISTS_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_AND_EXISTS]) fprintf(target, "AndExists: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_AND_EXISTS], totals.counters[BDD_AND_EXISTS_CACHED], totals.counters[BDD_AND_EXISTS_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_RELNEXT]) fprintf(target, "RelNext: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_RELNEXT], totals.counters[BDD_RELNEXT_CACHED], totals.counters[BDD_RELNEXT_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_RELPREV]) fprintf(target, "RelPrev: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_RELPREV], totals.counters[BDD_RELPREV_CACHED], totals.counters[BDD_RELPREV_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_CLOSURE]) fprintf(target, "Closure: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_CLOSURE], totals.counters[BDD_CLOSURE_CACHED], totals.counters[BDD_CLOSURE_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_COMPOSE]) fprintf(target, "Compose: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_COMPOSE], totals.counters[BDD_COMPOSE_CACHED], totals.counters[BDD_COMPOSE_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_RESTRICT]) fprintf(target, "Restrict: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_RESTRICT], totals.counters[BDD_RESTRICT_CACHED], totals.counters[BDD_RESTRICT_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_CONSTRAIN]) fprintf(target, "Constrain: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_CONSTRAIN], totals.counters[BDD_CONSTRAIN_CACHED], totals.counters[BDD_CONSTRAIN_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_SUPPORT]) fprintf(target, "Support: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_SUPPORT], totals.counters[BDD_SUPPORT_CACHED], totals.counters[BDD_SUPPORT_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_SATCOUNT]) fprintf(target, "SatCount: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_SATCOUNT], totals.counters[BDD_SATCOUNT_CACHED], totals.counters[BDD_SATCOUNT_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_PATHCOUNT]) fprintf(target, "PathCount: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_PATHCOUNT], totals.counters[BDD_PATHCOUNT_CACHED], totals.counters[BDD_PATHCOUNT_CACHEDPUT]); |
||||
|
if (totals.counters[BDD_ISBDD]) fprintf(target, "IsBDD: %'"PRIu64 " (%'"PRIu64", %'"PRIu64 ")\n", totals.counters[BDD_ISBDD], totals.counters[BDD_ISBDD_CACHED], totals.counters[BDD_ISBDD_CACHEDPUT]); |
||||
|
fprintf(target, "BDD Nodes created: %'"PRIu64"\n", totals.counters[BDD_NODES_CREATED]); |
||||
|
fprintf(target, "BDD Nodes reused: %'"PRIu64"\n", totals.counters[BDD_NODES_REUSED]); |
||||
|
} |
||||
|
|
||||
|
if (totals.counters[LDD_NODES_CREATED]) { |
||||
|
if (color) fprintf(target, ULINE LBLUE); |
||||
|
fprintf(target, "\nLDD operations count (cache reuse, cache put)\n"); |
||||
|
if (color) fprintf(target, NC); |
||||
|
if (totals.counters[LDD_UNION]) fprintf(target, "Union: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_UNION], totals.counters[LDD_UNION_CACHED], totals.counters[LDD_UNION_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_MINUS]) fprintf(target, "Minus: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_MINUS], totals.counters[LDD_MINUS_CACHED], totals.counters[LDD_MINUS_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_INTERSECT]) fprintf(target, "Intersect: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_INTERSECT], totals.counters[LDD_INTERSECT_CACHED], totals.counters[LDD_INTERSECT_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_RELPROD]) fprintf(target, "RelProd: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_RELPROD], totals.counters[LDD_RELPROD_CACHED], totals.counters[LDD_RELPROD_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_RELPREV]) fprintf(target, "RelPrev: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_RELPREV], totals.counters[LDD_RELPREV_CACHED], totals.counters[LDD_RELPREV_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_PROJECT]) fprintf(target, "Project: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_PROJECT], totals.counters[LDD_PROJECT_CACHED], totals.counters[LDD_PROJECT_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_JOIN]) fprintf(target, "Join: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_JOIN], totals.counters[LDD_JOIN_CACHED], totals.counters[LDD_JOIN_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_MATCH]) fprintf(target, "Match: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_MATCH], totals.counters[LDD_MATCH_CACHED], totals.counters[LDD_MATCH_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_SATCOUNT]) fprintf(target, "SatCount: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_SATCOUNT], totals.counters[LDD_SATCOUNT_CACHED], totals.counters[LDD_SATCOUNT_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_SATCOUNTL]) fprintf(target, "SatCountL: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_SATCOUNTL], totals.counters[LDD_SATCOUNTL_CACHED], totals.counters[LDD_SATCOUNTL_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_ZIP]) fprintf(target, "Zip: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_ZIP], totals.counters[LDD_ZIP_CACHED], totals.counters[LDD_ZIP_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_RELPROD_UNION]) fprintf(target, "RelProdUnion: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_RELPROD_UNION], totals.counters[LDD_RELPROD_UNION_CACHED], totals.counters[LDD_RELPROD_UNION_CACHEDPUT]); |
||||
|
if (totals.counters[LDD_PROJECT_MINUS]) fprintf(target, "ProjectMinus: %'"PRIu64 " (%'"PRIu64", %"PRIu64")\n", totals.counters[LDD_PROJECT_MINUS], totals.counters[LDD_PROJECT_MINUS_CACHED], totals.counters[LDD_PROJECT_MINUS_CACHEDPUT]); |
||||
|
fprintf(target, "LDD Nodes created: %'"PRIu64"\n", totals.counters[LDD_NODES_CREATED]); |
||||
|
fprintf(target, "LDD Nodes reused: %'"PRIu64"\n", totals.counters[LDD_NODES_REUSED]); |
||||
|
} |
||||
|
|
||||
|
if (color) fprintf(target, ULINE LBLUE); |
||||
|
fprintf(target, "\nGarbage collection\n"); |
||||
|
if (color) fprintf(target, NC); |
||||
|
fprintf(target, "Number of GC executions: %'"PRIu64"\n", totals.counters[SYLVAN_GC_COUNT]); |
||||
|
fprintf(target, "Total time spent: %'.6Lf sec.\n", (long double)totals.timers[SYLVAN_GC]/1000000000); |
||||
|
|
||||
|
if (color) fprintf(target, ULINE LBLUE); |
||||
|
fprintf(target, "\nTables\n"); |
||||
|
if (color) fprintf(target, NC); |
||||
|
fprintf(target, "Unique nodes table: %'zu of %'zu buckets filled.\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); |
||||
|
fprintf(target, "Operation cache: %'zu of %'zu buckets filled.\n", cache_getused(), cache_getsize()); |
||||
|
|
||||
|
if (color) fprintf(target, ULINE LBLUE); |
||||
|
fprintf(target, "\nUnique table\n"); |
||||
|
if (color) fprintf(target, NC); |
||||
|
fprintf(target, "Number of lookup iterations: %'"PRIu64"\n", totals.counters[LLMSSET_LOOKUP]); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
#else |
||||
|
|
||||
|
VOID_TASK_IMPL_0(sylvan_stats_init) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_IMPL_0(sylvan_stats_reset) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
sylvan_stats_report(FILE* target, int color) |
||||
|
{ |
||||
|
(void)target; |
||||
|
(void)color; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
#endif |
@ -0,0 +1,262 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#include <lace.h> |
||||
|
#include <sylvan_config.h> |
||||
|
|
||||
|
#ifndef SYLVAN_STATS_H |
||||
|
#define SYLVAN_STATS_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
typedef enum { |
||||
|
BDD_ITE, |
||||
|
BDD_AND, |
||||
|
BDD_XOR, |
||||
|
BDD_EXISTS, |
||||
|
BDD_AND_EXISTS, |
||||
|
BDD_RELNEXT, |
||||
|
BDD_RELPREV, |
||||
|
BDD_SATCOUNT, |
||||
|
BDD_COMPOSE, |
||||
|
BDD_RESTRICT, |
||||
|
BDD_CONSTRAIN, |
||||
|
BDD_CLOSURE, |
||||
|
BDD_ISBDD, |
||||
|
BDD_SUPPORT, |
||||
|
BDD_PATHCOUNT, |
||||
|
BDD_ITE_CACHEDPUT, |
||||
|
BDD_AND_CACHEDPUT, |
||||
|
BDD_XOR_CACHEDPUT, |
||||
|
BDD_EXISTS_CACHEDPUT, |
||||
|
BDD_AND_EXISTS_CACHEDPUT, |
||||
|
BDD_RELNEXT_CACHEDPUT, |
||||
|
BDD_RELPREV_CACHEDPUT, |
||||
|
BDD_SATCOUNT_CACHEDPUT, |
||||
|
BDD_COMPOSE_CACHEDPUT, |
||||
|
BDD_RESTRICT_CACHEDPUT, |
||||
|
BDD_CONSTRAIN_CACHEDPUT, |
||||
|
BDD_CLOSURE_CACHEDPUT, |
||||
|
BDD_ISBDD_CACHEDPUT, |
||||
|
BDD_SUPPORT_CACHEDPUT, |
||||
|
BDD_PATHCOUNT_CACHEDPUT, |
||||
|
BDD_ITE_CACHED, |
||||
|
BDD_AND_CACHED, |
||||
|
BDD_XOR_CACHED, |
||||
|
BDD_EXISTS_CACHED, |
||||
|
BDD_AND_EXISTS_CACHED, |
||||
|
BDD_RELNEXT_CACHED, |
||||
|
BDD_RELPREV_CACHED, |
||||
|
BDD_SATCOUNT_CACHED, |
||||
|
BDD_COMPOSE_CACHED, |
||||
|
BDD_RESTRICT_CACHED, |
||||
|
BDD_CONSTRAIN_CACHED, |
||||
|
BDD_CLOSURE_CACHED, |
||||
|
BDD_ISBDD_CACHED, |
||||
|
BDD_SUPPORT_CACHED, |
||||
|
BDD_PATHCOUNT_CACHED, |
||||
|
BDD_NODES_CREATED, |
||||
|
BDD_NODES_REUSED, |
||||
|
|
||||
|
LDD_UNION, |
||||
|
LDD_MINUS, |
||||
|
LDD_INTERSECT, |
||||
|
LDD_RELPROD, |
||||
|
LDD_RELPREV, |
||||
|
LDD_PROJECT, |
||||
|
LDD_JOIN, |
||||
|
LDD_MATCH, |
||||
|
LDD_SATCOUNT, |
||||
|
LDD_SATCOUNTL, |
||||
|
LDD_ZIP, |
||||
|
LDD_RELPROD_UNION, |
||||
|
LDD_PROJECT_MINUS, |
||||
|
LDD_UNION_CACHEDPUT, |
||||
|
LDD_MINUS_CACHEDPUT, |
||||
|
LDD_INTERSECT_CACHEDPUT, |
||||
|
LDD_RELPROD_CACHEDPUT, |
||||
|
LDD_RELPREV_CACHEDPUT, |
||||
|
LDD_PROJECT_CACHEDPUT, |
||||
|
LDD_JOIN_CACHEDPUT, |
||||
|
LDD_MATCH_CACHEDPUT, |
||||
|
LDD_SATCOUNT_CACHEDPUT, |
||||
|
LDD_SATCOUNTL_CACHEDPUT, |
||||
|
LDD_ZIP_CACHEDPUT, |
||||
|
LDD_RELPROD_UNION_CACHEDPUT, |
||||
|
LDD_PROJECT_MINUS_CACHEDPUT, |
||||
|
LDD_UNION_CACHED, |
||||
|
LDD_MINUS_CACHED, |
||||
|
LDD_INTERSECT_CACHED, |
||||
|
LDD_RELPROD_CACHED, |
||||
|
LDD_RELPREV_CACHED, |
||||
|
LDD_PROJECT_CACHED, |
||||
|
LDD_JOIN_CACHED, |
||||
|
LDD_MATCH_CACHED, |
||||
|
LDD_SATCOUNT_CACHED, |
||||
|
LDD_SATCOUNTL_CACHED, |
||||
|
LDD_ZIP_CACHED, |
||||
|
LDD_RELPROD_UNION_CACHED, |
||||
|
LDD_PROJECT_MINUS_CACHED, |
||||
|
LDD_NODES_CREATED, |
||||
|
LDD_NODES_REUSED, |
||||
|
|
||||
|
LLMSSET_LOOKUP, |
||||
|
|
||||
|
SYLVAN_GC_COUNT, |
||||
|
SYLVAN_COUNTER_COUNTER |
||||
|
} Sylvan_Counters; |
||||
|
|
||||
|
typedef enum |
||||
|
{ |
||||
|
SYLVAN_GC, |
||||
|
SYLVAN_TIMER_COUNTER |
||||
|
} Sylvan_Timers; |
||||
|
|
||||
|
/** |
||||
|
* Initialize stats system (done by sylvan_init_package) |
||||
|
*/ |
||||
|
#define sylvan_stats_init() CALL(sylvan_stats_init) |
||||
|
VOID_TASK_DECL_0(sylvan_stats_init) |
||||
|
|
||||
|
/** |
||||
|
* Reset all counters (for statistics) |
||||
|
*/ |
||||
|
#define sylvan_stats_reset() CALL(sylvan_stats_reset) |
||||
|
VOID_TASK_DECL_0(sylvan_stats_reset) |
||||
|
|
||||
|
/** |
||||
|
* Write statistic report to file (stdout, stderr, etc) |
||||
|
*/ |
||||
|
void sylvan_stats_report(FILE* target, int color); |
||||
|
|
||||
|
#if SYLVAN_STATS |
||||
|
|
||||
|
/* Infrastructure for internal markings */ |
||||
|
typedef struct |
||||
|
{ |
||||
|
uint64_t counters[SYLVAN_COUNTER_COUNTER]; |
||||
|
uint64_t timers[SYLVAN_TIMER_COUNTER]; |
||||
|
uint64_t timers_startstop[SYLVAN_TIMER_COUNTER]; |
||||
|
} sylvan_stats_t; |
||||
|
|
||||
|
#ifdef __MACH__ |
||||
|
#include <mach/mach_time.h> |
||||
|
#define getabstime() mach_absolute_time() |
||||
|
#else |
||||
|
#include <time.h> |
||||
|
static uint64_t |
||||
|
getabstime() |
||||
|
{ |
||||
|
struct timespec ts; |
||||
|
clock_gettime(CLOCK_MONOTONIC, &ts); |
||||
|
uint64_t t = ts.tv_sec; |
||||
|
t *= 1000000000UL; |
||||
|
t += ts.tv_nsec; |
||||
|
return t; |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
#ifdef __ELF__ |
||||
|
extern __thread sylvan_stats_t sylvan_stats; |
||||
|
#else |
||||
|
#include <pthread.h> |
||||
|
extern pthread_key_t sylvan_stats_key; |
||||
|
#endif |
||||
|
|
||||
|
static inline void |
||||
|
sylvan_stats_count(size_t counter) |
||||
|
{ |
||||
|
#ifdef __ELF__ |
||||
|
sylvan_stats.counters[counter]++; |
||||
|
#else |
||||
|
sylvan_stats_t *sylvan_stats = (sylvan_stats_t*)pthread_getspecific(sylvan_stats_key); |
||||
|
sylvan_stats->counters[counter]++; |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
sylvan_stats_add(size_t counter, size_t amount) |
||||
|
{ |
||||
|
#ifdef __ELF__ |
||||
|
sylvan_stats.counters[counter]+=amount; |
||||
|
#else |
||||
|
sylvan_stats_t *sylvan_stats = (sylvan_stats_t*)pthread_getspecific(sylvan_stats_key); |
||||
|
sylvan_stats->counters[counter]+=amount; |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
sylvan_timer_start(size_t timer) |
||||
|
{ |
||||
|
uint64_t t = getabstime(); |
||||
|
|
||||
|
#ifdef __ELF__ |
||||
|
sylvan_stats.timers_startstop[timer] = t; |
||||
|
#else |
||||
|
sylvan_stats_t *sylvan_stats = (sylvan_stats_t*)pthread_getspecific(sylvan_stats_key); |
||||
|
sylvan_stats->timers_startstop[timer] = t; |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
sylvan_timer_stop(size_t timer) |
||||
|
{ |
||||
|
uint64_t t = getabstime(); |
||||
|
|
||||
|
#ifdef __ELF__ |
||||
|
sylvan_stats.timers[timer] += (t - sylvan_stats.timers_startstop[timer]); |
||||
|
#else |
||||
|
sylvan_stats_t *sylvan_stats = (sylvan_stats_t*)pthread_getspecific(sylvan_stats_key); |
||||
|
sylvan_stats->timers[timer] += (t - sylvan_stats->timers_startstop[timer]); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
#else |
||||
|
|
||||
|
static inline void |
||||
|
sylvan_stats_count(size_t counter) |
||||
|
{ |
||||
|
(void)counter; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
sylvan_stats_add(size_t counter, size_t amount) |
||||
|
{ |
||||
|
(void)counter; |
||||
|
(void)amount; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
sylvan_timer_start(size_t timer) |
||||
|
{ |
||||
|
(void)timer; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
sylvan_timer_stop(size_t timer) |
||||
|
{ |
||||
|
(void)timer; |
||||
|
} |
||||
|
|
||||
|
#endif |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#endif |
@ -0,0 +1,182 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* Sylvan: parallel BDD/ListDD package. |
||||
|
* |
||||
|
* This is a multi-core implementation of BDDs with complement edges. |
||||
|
* |
||||
|
* This package requires parallel the work-stealing framework Lace. |
||||
|
* Lace must be initialized before initializing Sylvan |
||||
|
* |
||||
|
* This package uses explicit referencing. |
||||
|
* Use sylvan_ref and sylvan_deref to manage external references. |
||||
|
* |
||||
|
* Garbage collection requires all workers to cooperate. Garbage collection is either initiated |
||||
|
* by the user (calling sylvan_gc) or when the nodes table is full. All Sylvan operations |
||||
|
* check whether they need to cooperate on garbage collection. Garbage collection cannot occur |
||||
|
* otherwise. This means that it is perfectly fine to do this: |
||||
|
* BDD a = sylvan_ref(sylvan_and(b, c)); |
||||
|
* since it is not possible that garbage collection occurs between the two calls. |
||||
|
* |
||||
|
* To temporarily disable garbage collection, use sylvan_gc_disable() and sylvan_gc_enable(). |
||||
|
*/ |
||||
|
|
||||
|
#include <sylvan_config.h> |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
#include <stdio.h> // for FILE |
||||
|
#include <stdlib.h> |
||||
|
#include <lace.h> // for definitions |
||||
|
|
||||
|
#include <sylvan_cache.h> |
||||
|
#include <llmsset.h> |
||||
|
#include <stats.h> |
||||
|
|
||||
|
#ifndef SYLVAN_H |
||||
|
#define SYLVAN_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#ifndef SYLVAN_SIZE_FIBONACCI |
||||
|
#define SYLVAN_SIZE_FIBONACCI 0 |
||||
|
#endif |
||||
|
|
||||
|
// For now, only support 64-bit systems |
||||
|
typedef char __sylvan_check_size_t_is_8_bytes[(sizeof(uint64_t) == sizeof(size_t))?1:-1]; |
||||
|
|
||||
|
/** |
||||
|
* Initialize the Sylvan parallel decision diagrams package. |
||||
|
* |
||||
|
* After initialization, call sylvan_init_bdd and/or sylvan_init_ldd if you want to use |
||||
|
* the BDD and/or LDD functionality. |
||||
|
* |
||||
|
* BDDs and LDDs share a common node table and operations cache. |
||||
|
* |
||||
|
* The node table is resizable. |
||||
|
* The table is resized automatically when >50% of the table is filled during garbage collection. |
||||
|
* This behavior can be customized by overriding the gc hook. |
||||
|
* |
||||
|
* Memory usage: |
||||
|
* Every node requires 24 bytes memory. (16 bytes data + 8 bytes overhead) |
||||
|
* Every operation cache entry requires 36 bytes memory. (32 bytes data + 4 bytes overhead) |
||||
|
* |
||||
|
* Reasonable defaults: datasize of 1L<<26 (2048 MB), cachesize of 1L<<25 (1152 MB) |
||||
|
*/ |
||||
|
void sylvan_init_package(size_t initial_tablesize, size_t max_tablesize, size_t initial_cachesize, size_t max_cachesize); |
||||
|
|
||||
|
/** |
||||
|
* Frees all Sylvan data (also calls the quit() functions of BDD/MDD parts) |
||||
|
*/ |
||||
|
void sylvan_quit(); |
||||
|
|
||||
|
/** |
||||
|
* Return number of occupied buckets in nodes table and total number of buckets. |
||||
|
*/ |
||||
|
VOID_TASK_DECL_2(sylvan_table_usage, size_t*, size_t*); |
||||
|
#define sylvan_table_usage(filled, total) (CALL(sylvan_table_usage, filled, total)) |
||||
|
|
||||
|
/** |
||||
|
* Perform garbage collection. |
||||
|
* |
||||
|
* Garbage collection is performed in a new Lace frame, interrupting all ongoing work |
||||
|
* until garbage collection is completed. |
||||
|
* |
||||
|
* Garbage collection procedure: |
||||
|
* 1) The operation cache is cleared and the hash table is reset. |
||||
|
* 2) All live nodes are marked (to be rehashed). This is done by the "mark" callbacks. |
||||
|
* 3) The "hook" callback is called. |
||||
|
* By default, this doubles the hash table size when it is >50% full. |
||||
|
* 4) All live nodes are rehashed into the hash table. |
||||
|
* |
||||
|
* The behavior of garbage collection can be customized by adding "mark" callbacks and |
||||
|
* replacing the "hook" callback. |
||||
|
*/ |
||||
|
VOID_TASK_DECL_0(sylvan_gc); |
||||
|
#define sylvan_gc() (CALL(sylvan_gc)) |
||||
|
|
||||
|
/** |
||||
|
* Enable or disable garbage collection. |
||||
|
* |
||||
|
* This affects both automatic and manual garbage collection, i.e., |
||||
|
* calling sylvan_gc() while garbage collection is disabled does not have any effect. |
||||
|
*/ |
||||
|
void sylvan_gc_enable(); |
||||
|
void sylvan_gc_disable(); |
||||
|
|
||||
|
/** |
||||
|
* Add a "mark" callback to the list of callbacks. |
||||
|
* |
||||
|
* These are called during garbage collection to recursively mark nodes. |
||||
|
* |
||||
|
* Default "mark" functions that mark external references (via sylvan_ref) and internal |
||||
|
* references (inside operations) are added by sylvan_init_bdd/sylvan_init_bdd. |
||||
|
* |
||||
|
* Functions are called in order. |
||||
|
* level 10: marking functions of Sylvan (external/internal references) |
||||
|
* level 20: call the hook function (for resizing) |
||||
|
* level 30: rehashing |
||||
|
*/ |
||||
|
LACE_TYPEDEF_CB(void, gc_mark_cb); |
||||
|
void sylvan_gc_add_mark(int order, gc_mark_cb callback); |
||||
|
|
||||
|
/** |
||||
|
* Set "hook" callback. There can be only one. |
||||
|
* |
||||
|
* The hook is called after the "mark" phase and before the "rehash" phase. |
||||
|
* This allows users to perform certain actions, such as resizing the nodes table |
||||
|
* and the operation cache. Also, dynamic resizing could be performed then. |
||||
|
*/ |
||||
|
LACE_TYPEDEF_CB(void, gc_hook_cb); |
||||
|
void sylvan_gc_set_hook(gc_hook_cb new_hook); |
||||
|
|
||||
|
/** |
||||
|
* One of the hooks for resizing behavior. |
||||
|
* Default if SYLVAN_AGGRESSIVE_RESIZE is set. |
||||
|
* Always double size on gc() until maximum reached. |
||||
|
*/ |
||||
|
VOID_TASK_DECL_0(sylvan_gc_aggressive_resize); |
||||
|
|
||||
|
/** |
||||
|
* One of the hooks for resizing behavior. |
||||
|
* Default if SYLVAN_AGGRESSIVE_RESIZE is not set. |
||||
|
* Double size on gc() whenever >50% is used. |
||||
|
*/ |
||||
|
VOID_TASK_DECL_0(sylvan_gc_default_hook); |
||||
|
|
||||
|
/** |
||||
|
* Set "notify on dead" callback for the nodes table. |
||||
|
* See also documentation in llmsset.h |
||||
|
*/ |
||||
|
#define sylvan_set_ondead(cb, ctx) llmsset_set_ondead(nodes, cb, ctx) |
||||
|
|
||||
|
/** |
||||
|
* Global variables (number of workers, nodes table) |
||||
|
*/ |
||||
|
|
||||
|
extern llmsset_t nodes; |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#include <sylvan_bdd.h> |
||||
|
#include <sylvan_ldd.h> |
||||
|
#include <sylvan_mtbdd.h> |
||||
|
|
||||
|
#endif |
2820
resources/3rdparty/sylvan/src/sylvan_bdd.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,423 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
/* Do not include this file directly. Instead, include sylvan.h */ |
||||
|
|
||||
|
#include <tls.h> |
||||
|
|
||||
|
#ifndef SYLVAN_BDD_H |
||||
|
#define SYLVAN_BDD_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
typedef uint64_t BDD; // low 40 bits used for index, highest bit for complement, rest 0 |
||||
|
// BDDSET uses the BDD node hash table. A BDDSET is an ordered BDD. |
||||
|
typedef uint64_t BDDSET; // encodes a set of variables (e.g. for exists etc.) |
||||
|
// BDDMAP also uses the BDD node hash table. A BDDMAP is *not* an ordered BDD. |
||||
|
typedef uint64_t BDDMAP; // encodes a function of variable->BDD (e.g. for substitute) |
||||
|
typedef uint32_t BDDVAR; // low 24 bits only |
||||
|
|
||||
|
#define sylvan_complement ((uint64_t)0x8000000000000000) |
||||
|
#define sylvan_false ((BDD)0x0000000000000000) |
||||
|
#define sylvan_true (sylvan_false|sylvan_complement) |
||||
|
#define sylvan_invalid ((BDD)0x7fffffffffffffff) |
||||
|
|
||||
|
#define sylvan_isconst(bdd) (bdd == sylvan_true || bdd == sylvan_false) |
||||
|
#define sylvan_isnode(bdd) (bdd != sylvan_true && bdd != sylvan_false) |
||||
|
|
||||
|
/** |
||||
|
* Initialize BDD functionality. |
||||
|
* |
||||
|
* Granularity (BDD only) determines usage of operation cache. Smallest value is 1: use the operation cache always. |
||||
|
* Higher values mean that the cache is used less often. Variables are grouped such that |
||||
|
* the cache is used when going to the next group, i.e., with granularity=3, variables [0,1,2] are in the |
||||
|
* first group, [3,4,5] in the next, etc. Then no caching occur between 0->1, 1->2, 0->2. Caching occurs |
||||
|
* on 0->3, 1->4, 2->3, etc. |
||||
|
* |
||||
|
* A reasonable default is a granularity of 4-16, strongly depending on the structure of the BDDs. |
||||
|
*/ |
||||
|
void sylvan_init_bdd(int granularity); |
||||
|
|
||||
|
/* Create a BDD representing just <var> or the negation of <var> */ |
||||
|
BDD sylvan_ithvar(BDDVAR var); |
||||
|
static inline BDD sylvan_nithvar(BDD var) { return sylvan_ithvar(var) ^ sylvan_complement; } |
||||
|
|
||||
|
/* Retrieve the <var> of the BDD node <bdd> */ |
||||
|
BDDVAR sylvan_var(BDD bdd); |
||||
|
|
||||
|
/* Follow <low> and <high> edges */ |
||||
|
BDD sylvan_low(BDD bdd); |
||||
|
BDD sylvan_high(BDD bdd); |
||||
|
|
||||
|
/* Add or remove external reference to BDD */ |
||||
|
BDD sylvan_ref(BDD a); |
||||
|
void sylvan_deref(BDD a); |
||||
|
|
||||
|
/* For use in custom mark functions */ |
||||
|
VOID_TASK_DECL_1(sylvan_gc_mark_rec, BDD); |
||||
|
#define sylvan_gc_mark_rec(mdd) CALL(sylvan_gc_mark_rec, mdd) |
||||
|
|
||||
|
/* Return the number of external references */ |
||||
|
size_t sylvan_count_refs(); |
||||
|
|
||||
|
/* Add or remove BDD pointers to protect (indirect external references) */ |
||||
|
void sylvan_protect(BDD* ptr); |
||||
|
void sylvan_unprotect(BDD* ptr); |
||||
|
|
||||
|
/* Return the number of protected BDD pointers */ |
||||
|
size_t sylvan_count_protected(); |
||||
|
|
||||
|
/* Mark BDD for "notify on dead" */ |
||||
|
#define sylvan_notify_ondead(bdd) llmsset_notify_ondead(nodes, bdd&~sylvan_complement) |
||||
|
|
||||
|
/* Unary, binary and if-then-else operations */ |
||||
|
#define sylvan_not(a) (((BDD)a)^sylvan_complement) |
||||
|
TASK_DECL_4(BDD, sylvan_ite, BDD, BDD, BDD, BDDVAR); |
||||
|
#define sylvan_ite(a,b,c) (CALL(sylvan_ite,a,b,c,0)) |
||||
|
TASK_DECL_3(BDD, sylvan_and, BDD, BDD, BDDVAR); |
||||
|
#define sylvan_and(a,b) (CALL(sylvan_and,a,b,0)) |
||||
|
TASK_DECL_3(BDD, sylvan_xor, BDD, BDD, BDDVAR); |
||||
|
#define sylvan_xor(a,b) (CALL(sylvan_xor,a,b,0)) |
||||
|
/* Do not use nested calls for xor/equiv parameter b! */ |
||||
|
#define sylvan_equiv(a,b) sylvan_not(sylvan_xor(a,b)) |
||||
|
#define sylvan_or(a,b) sylvan_not(sylvan_and(sylvan_not(a),sylvan_not(b))) |
||||
|
#define sylvan_nand(a,b) sylvan_not(sylvan_and(a,b)) |
||||
|
#define sylvan_nor(a,b) sylvan_not(sylvan_or(a,b)) |
||||
|
#define sylvan_imp(a,b) sylvan_not(sylvan_and(a,sylvan_not(b))) |
||||
|
#define sylvan_invimp(a,b) sylvan_not(sylvan_and(sylvan_not(a),b)) |
||||
|
#define sylvan_biimp sylvan_equiv |
||||
|
#define sylvan_diff(a,b) sylvan_and(a,sylvan_not(b)) |
||||
|
#define sylvan_less(a,b) sylvan_and(sylvan_not(a),b) |
||||
|
|
||||
|
/* Existential and Universal quantifiers */ |
||||
|
TASK_DECL_3(BDD, sylvan_exists, BDD, BDD, BDDVAR); |
||||
|
#define sylvan_exists(a, vars) (CALL(sylvan_exists, a, vars, 0)) |
||||
|
#define sylvan_forall(a, vars) (sylvan_not(CALL(sylvan_exists, sylvan_not(a), vars, 0))) |
||||
|
|
||||
|
/** |
||||
|
* Compute \exists v: A(...) \and B(...) |
||||
|
* Parameter vars is the cube (conjunction) of all v variables. |
||||
|
*/ |
||||
|
TASK_DECL_4(BDD, sylvan_and_exists, BDD, BDD, BDDSET, BDDVAR); |
||||
|
#define sylvan_and_exists(a,b,vars) CALL(sylvan_and_exists,a,b,vars,0) |
||||
|
|
||||
|
/** |
||||
|
* Compute R(s,t) = \exists x: A(s,x) \and B(x,t) |
||||
|
* or R(s) = \exists x: A(s,x) \and B(x) |
||||
|
* Assumes s,t are interleaved with s even and t odd (s+1). |
||||
|
* Parameter vars is the cube of all s and/or t variables. |
||||
|
* Other variables in A are "ignored" (existential quantification) |
||||
|
* Other variables in B are kept |
||||
|
* Alternatively, vars=false means all variables are in vars |
||||
|
* |
||||
|
* Use this function to concatenate two relations --> --> |
||||
|
* or to take the 'previous' of a set --> S |
||||
|
*/ |
||||
|
TASK_DECL_4(BDD, sylvan_relprev, BDD, BDD, BDDSET, BDDVAR); |
||||
|
#define sylvan_relprev(a,b,vars) CALL(sylvan_relprev,a,b,vars,0) |
||||
|
|
||||
|
/** |
||||
|
* Compute R(s) = \exists x: A(x) \and B(x,s) |
||||
|
* with support(result) = s, support(A) = s, support(B) = s+t |
||||
|
* Assumes s,t are interleaved with s even and t odd (s+1). |
||||
|
* Parameter vars is the cube of all s and/or t variables. |
||||
|
* Other variables in A are kept |
||||
|
* Other variables in B are "ignored" (existential quantification) |
||||
|
* Alternatively, vars=false means all variables are in vars |
||||
|
* |
||||
|
* Use this function to take the 'next' of a set S --> |
||||
|
*/ |
||||
|
TASK_DECL_4(BDD, sylvan_relnext, BDD, BDD, BDDSET, BDDVAR); |
||||
|
#define sylvan_relnext(a,b,vars) CALL(sylvan_relnext,a,b,vars,0) |
||||
|
|
||||
|
/** |
||||
|
* Computes the transitive closure by traversing the BDD recursively. |
||||
|
* See Y. Matsunaga, P. C. McGeer, R. K. Brayton |
||||
|
* On Computing the Transitive Closure of a State Transition Relation |
||||
|
* 30th ACM Design Automation Conference, 1993. |
||||
|
* |
||||
|
* The input BDD must be a transition relation that only has levels of s,t |
||||
|
* with s,t interleaved with s even and t odd, i.e. |
||||
|
* s level 0,2,4 matches with t level 1,3,5 and so forth. |
||||
|
*/ |
||||
|
TASK_DECL_2(BDD, sylvan_closure, BDD, BDDVAR); |
||||
|
#define sylvan_closure(a) CALL(sylvan_closure,a,0); |
||||
|
|
||||
|
/** |
||||
|
* Calculate a@b (a constrain b), such that (b -> a@b) = (b -> a) |
||||
|
* Special cases: |
||||
|
* - a@0 = 0 |
||||
|
* - a@1 = f |
||||
|
* - 0@b = 0 |
||||
|
* - 1@b = 1 |
||||
|
* - a@a = 1 |
||||
|
* - a@not(a) = 0 |
||||
|
*/ |
||||
|
TASK_DECL_3(BDD, sylvan_constrain, BDD, BDD, BDDVAR); |
||||
|
#define sylvan_constrain(f,c) (CALL(sylvan_constrain, (f), (c), 0)) |
||||
|
|
||||
|
TASK_DECL_3(BDD, sylvan_restrict, BDD, BDD, BDDVAR); |
||||
|
#define sylvan_restrict(f,c) (CALL(sylvan_restrict, (f), (c), 0)) |
||||
|
|
||||
|
TASK_DECL_3(BDD, sylvan_compose, BDD, BDDMAP, BDDVAR); |
||||
|
#define sylvan_compose(f,m) (CALL(sylvan_compose, (f), (m), 0)) |
||||
|
|
||||
|
/** |
||||
|
* Calculate the support of a BDD. |
||||
|
* A variable v is in the support of a Boolean function f iff f[v<-0] != f[v<-1] |
||||
|
* It is also the set of all variables in the BDD nodes of the BDD. |
||||
|
*/ |
||||
|
TASK_DECL_1(BDD, sylvan_support, BDD); |
||||
|
#define sylvan_support(bdd) (CALL(sylvan_support, bdd)) |
||||
|
|
||||
|
/** |
||||
|
* A set of BDD variables is a cube (conjunction) of variables in their positive form. |
||||
|
* Note 2015-06-10: This used to be a union (disjunction) of variables in their positive form. |
||||
|
*/ |
||||
|
// empty bddset |
||||
|
#define sylvan_set_empty() sylvan_true |
||||
|
#define sylvan_set_isempty(set) (set == sylvan_true) |
||||
|
// add variables to the bddset |
||||
|
#define sylvan_set_add(set, var) sylvan_and(set, sylvan_ithvar(var)) |
||||
|
#define sylvan_set_addall(set, set_to_add) sylvan_and(set, set_to_add) |
||||
|
// remove variables from the bddset |
||||
|
#define sylvan_set_remove(set, var) sylvan_exists(set, var) |
||||
|
#define sylvan_set_removeall(set, set_to_remove) sylvan_exists(set, set_to_remove) |
||||
|
// iterate through all variables |
||||
|
#define sylvan_set_var(set) (sylvan_var(set)) |
||||
|
#define sylvan_set_next(set) (sylvan_high(set)) |
||||
|
int sylvan_set_in(BDDSET set, BDDVAR var); |
||||
|
size_t sylvan_set_count(BDDSET set); |
||||
|
void sylvan_set_toarray(BDDSET set, BDDVAR *arr); |
||||
|
// variables in arr should be ordered |
||||
|
TASK_DECL_2(BDDSET, sylvan_set_fromarray, BDDVAR*, size_t); |
||||
|
#define sylvan_set_fromarray(arr, length) ( CALL(sylvan_set_fromarray, arr, length) ) |
||||
|
void sylvan_test_isset(BDDSET set); |
||||
|
|
||||
|
/** |
||||
|
* BDDMAP maps BDDVAR-->BDD, implemented using BDD nodes. |
||||
|
* Based on disjunction of variables, but with high edges to BDDs instead of True terminals. |
||||
|
*/ |
||||
|
// empty bddmap |
||||
|
static inline BDDMAP sylvan_map_empty() { return sylvan_false; } |
||||
|
static inline int sylvan_map_isempty(BDDMAP map) { return map == sylvan_false ? 1 : 0; } |
||||
|
// add key-value pairs to the bddmap |
||||
|
BDDMAP sylvan_map_add(BDDMAP map, BDDVAR key, BDD value); |
||||
|
BDDMAP sylvan_map_addall(BDDMAP map_1, BDDMAP map_2); |
||||
|
// remove key-value pairs from the bddmap |
||||
|
BDDMAP sylvan_map_remove(BDDMAP map, BDDVAR key); |
||||
|
BDDMAP sylvan_map_removeall(BDDMAP map, BDDSET toremove); |
||||
|
// iterate through all pairs |
||||
|
static inline BDDVAR sylvan_map_key(BDDMAP map) { return sylvan_var(map); } |
||||
|
static inline BDD sylvan_map_value(BDDMAP map) { return sylvan_high(map); } |
||||
|
static inline BDDMAP sylvan_map_next(BDDMAP map) { return sylvan_low(map); } |
||||
|
// is a key in the map |
||||
|
int sylvan_map_in(BDDMAP map, BDDVAR key); |
||||
|
// count number of keys |
||||
|
size_t sylvan_map_count(BDDMAP map); |
||||
|
// convert a BDDSET (cube of variables) to a map, with all variables pointing on the value |
||||
|
BDDMAP sylvan_set_to_map(BDDSET set, BDD value); |
||||
|
|
||||
|
/** |
||||
|
* Node creation primitive. |
||||
|
* Careful: does not check ordering! |
||||
|
* Preferably only use when debugging! |
||||
|
*/ |
||||
|
BDD sylvan_makenode(BDDVAR level, BDD low, BDD high); |
||||
|
|
||||
|
/** |
||||
|
* Write a DOT representation of a BDD |
||||
|
*/ |
||||
|
void sylvan_printdot(BDD bdd); |
||||
|
void sylvan_fprintdot(FILE *out, BDD bdd); |
||||
|
|
||||
|
/** |
||||
|
* Write a DOT representation of a BDD. |
||||
|
* This variant does not print complement edges. |
||||
|
*/ |
||||
|
void sylvan_printdot_nc(BDD bdd); |
||||
|
void sylvan_fprintdot_nc(FILE *out, BDD bdd); |
||||
|
|
||||
|
void sylvan_print(BDD bdd); |
||||
|
void sylvan_fprint(FILE *f, BDD bdd); |
||||
|
|
||||
|
void sylvan_printsha(BDD bdd); |
||||
|
void sylvan_fprintsha(FILE *f, BDD bdd); |
||||
|
void sylvan_getsha(BDD bdd, char *target); // target must be at least 65 bytes... |
||||
|
|
||||
|
/** |
||||
|
* Calculate number of satisfying variable assignments. |
||||
|
* The set of variables must be >= the support of the BDD. |
||||
|
*/ |
||||
|
|
||||
|
TASK_DECL_3(double, sylvan_satcount, BDD, BDDSET, BDDVAR); |
||||
|
#define sylvan_satcount(bdd, variables) CALL(sylvan_satcount, bdd, variables, 0) |
||||
|
|
||||
|
/** |
||||
|
* Create a BDD cube representing the conjunction of variables in their positive or negative |
||||
|
* form depending on whether the cube[idx] equals 0 (negative), 1 (positive) or 2 (any). |
||||
|
* CHANGED 2014/09/19: vars is now a BDDSET (ordered!) |
||||
|
*/ |
||||
|
BDD sylvan_cube(BDDSET variables, uint8_t *cube); |
||||
|
TASK_DECL_3(BDD, sylvan_union_cube, BDD, BDDSET, uint8_t*); |
||||
|
#define sylvan_union_cube(bdd, variables, cube) CALL(sylvan_union_cube, bdd, variables, cube) |
||||
|
|
||||
|
/** |
||||
|
* Pick one satisfying variable assignment randomly for which <bdd> is true. |
||||
|
* The <variables> set must include all variables in the support of <bdd>. |
||||
|
* |
||||
|
* The function will set the values of str, such that |
||||
|
* str[index] where index is the index in the <variables> set is set to |
||||
|
* 0 when the variable is negative, 1 when positive, or 2 when it could be either. |
||||
|
* |
||||
|
* This implies that str[i] will be set in the variable ordering as in <variables>. |
||||
|
* |
||||
|
* Returns 1 when succesful, or 0 when no assignment is found (i.e. bdd==sylvan_false). |
||||
|
*/ |
||||
|
int sylvan_sat_one(BDD bdd, BDDSET variables, uint8_t* str); |
||||
|
|
||||
|
/** |
||||
|
* Pick one satisfying variable assignment randomly from the given <bdd>. |
||||
|
* Functionally equivalent to performing sylvan_cube on the result of sylvan_sat_one. |
||||
|
* For the result: sylvan_and(res, bdd) = res. |
||||
|
*/ |
||||
|
BDD sylvan_sat_one_bdd(BDD bdd); |
||||
|
#define sylvan_pick_cube sylvan_sat_one_bdd |
||||
|
|
||||
|
/** |
||||
|
* Enumerate all satisfying variable assignments from the given <bdd> using variables <vars>. |
||||
|
* Calls <cb> with four parameters: a user-supplied context, the array of BDD variables in <vars>, |
||||
|
* the cube (array of values 0 and 1 for each variables in <vars>) and the length of the two arrays. |
||||
|
*/ |
||||
|
LACE_TYPEDEF_CB(void, enum_cb, void*, BDDVAR*, uint8_t*, int); |
||||
|
VOID_TASK_DECL_4(sylvan_enum, BDD, BDDSET, enum_cb, void*); |
||||
|
#define sylvan_enum(bdd, vars, cb, context) CALL(sylvan_enum, bdd, vars, cb, context) |
||||
|
VOID_TASK_DECL_4(sylvan_enum_par, BDD, BDDSET, enum_cb, void*); |
||||
|
#define sylvan_enum_par(bdd, vars, cb, context) CALL(sylvan_enum_par, bdd, vars, cb, context) |
||||
|
|
||||
|
/** |
||||
|
* Enumerate all satisfyable variable assignments of the given <bdd> using variables <vars>. |
||||
|
* Calls <cb> with two parameters: a user-supplied context and the cube (array of |
||||
|
* values 0 and 1 for each variable in <vars>). |
||||
|
* The BDD that <cb> returns is pair-wise merged (using or) and returned. |
||||
|
*/ |
||||
|
LACE_TYPEDEF_CB(BDD, sylvan_collect_cb, void*, uint8_t*); |
||||
|
TASK_DECL_4(BDD, sylvan_collect, BDD, BDDSET, sylvan_collect_cb, void*); |
||||
|
#define sylvan_collect(bdd, vars, cb, context) CALL(sylvan_collect, bdd, vars, cb, context) |
||||
|
|
||||
|
/** |
||||
|
* Compute the number of distinct paths to sylvan_true in the BDD |
||||
|
*/ |
||||
|
TASK_DECL_2(double, sylvan_pathcount, BDD, BDDVAR); |
||||
|
#define sylvan_pathcount(bdd) (CALL(sylvan_pathcount, bdd, 0)) |
||||
|
|
||||
|
/** |
||||
|
* Compute the number of BDD nodes in the BDD |
||||
|
*/ |
||||
|
size_t sylvan_nodecount(BDD a); |
||||
|
|
||||
|
/** |
||||
|
* SAVING: |
||||
|
* use sylvan_serialize_add on every BDD you want to store |
||||
|
* use sylvan_serialize_get to retrieve the key of every stored BDD |
||||
|
* use sylvan_serialize_tofile |
||||
|
* |
||||
|
* LOADING: |
||||
|
* use sylvan_serialize_fromfile (implies sylvan_serialize_reset) |
||||
|
* use sylvan_serialize_get_reversed for every key |
||||
|
* |
||||
|
* MISC: |
||||
|
* use sylvan_serialize_reset to free all allocated structures |
||||
|
* use sylvan_serialize_totext to write a textual list of tuples of all BDDs. |
||||
|
* format: [(<key>,<level>,<key_low>,<key_high>,<complement_high>),...] |
||||
|
* |
||||
|
* for the old sylvan_print functions, use sylvan_serialize_totext |
||||
|
*/ |
||||
|
size_t sylvan_serialize_add(BDD bdd); |
||||
|
size_t sylvan_serialize_get(BDD bdd); |
||||
|
BDD sylvan_serialize_get_reversed(size_t value); |
||||
|
void sylvan_serialize_reset(); |
||||
|
void sylvan_serialize_totext(FILE *out); |
||||
|
void sylvan_serialize_tofile(FILE *out); |
||||
|
void sylvan_serialize_fromfile(FILE *in); |
||||
|
|
||||
|
/** |
||||
|
* For debugging |
||||
|
* if (part of) the BDD is not 'marked' in the nodes table, assertion fails |
||||
|
* if the BDD is not ordered, returns 0 |
||||
|
* if nicely ordered, returns 1 |
||||
|
*/ |
||||
|
TASK_DECL_1(int, sylvan_test_isbdd, BDD); |
||||
|
#define sylvan_test_isbdd(bdd) CALL(sylvan_test_isbdd, bdd) |
||||
|
|
||||
|
/* Infrastructure for internal markings */ |
||||
|
typedef struct bdd_refs_internal |
||||
|
{ |
||||
|
size_t r_size, r_count; |
||||
|
size_t s_size, s_count; |
||||
|
BDD *results; |
||||
|
Task **spawns; |
||||
|
} *bdd_refs_internal_t; |
||||
|
|
||||
|
extern DECLARE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); |
||||
|
|
||||
|
static inline BDD |
||||
|
bdd_refs_push(BDD bdd) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); |
||||
|
if (bdd_refs_key->r_count >= bdd_refs_key->r_size) { |
||||
|
bdd_refs_key->r_size *= 2; |
||||
|
bdd_refs_key->results = (BDD*)realloc(bdd_refs_key->results, sizeof(BDD) * bdd_refs_key->r_size); |
||||
|
} |
||||
|
bdd_refs_key->results[bdd_refs_key->r_count++] = bdd; |
||||
|
return bdd; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
bdd_refs_pop(int amount) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); |
||||
|
bdd_refs_key->r_count-=amount; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
bdd_refs_spawn(Task *t) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); |
||||
|
if (bdd_refs_key->s_count >= bdd_refs_key->s_size) { |
||||
|
bdd_refs_key->s_size *= 2; |
||||
|
bdd_refs_key->spawns = (Task**)realloc(bdd_refs_key->spawns, sizeof(Task*) * bdd_refs_key->s_size); |
||||
|
} |
||||
|
bdd_refs_key->spawns[bdd_refs_key->s_count++] = t; |
||||
|
} |
||||
|
|
||||
|
static inline BDD |
||||
|
bdd_refs_sync(BDD result) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); |
||||
|
bdd_refs_key->s_count--; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
#include "sylvan_bdd_storm.h" |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#endif |
@ -0,0 +1,3 @@ |
|||||
|
#define bdd_isnegated(dd) ((dd & sylvan_complement) ? 1 : 0) |
||||
|
#define bdd_regular(dd) (dd & ~sylvan_complement) |
||||
|
#define bdd_isterminal(dd) (dd == sylvan_false || dd == sylvan_true) |
@ -0,0 +1,220 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#include <errno.h> // for errno |
||||
|
#include <stdio.h> // for fprintf |
||||
|
#include <stdint.h> // for uint32_t etc |
||||
|
#include <stdlib.h> // for exit |
||||
|
#include <string.h> // for strerror |
||||
|
#include <sys/mman.h> // for mmap |
||||
|
|
||||
|
#include <sylvan_cache.h> |
||||
|
|
||||
|
#ifndef MAP_ANONYMOUS |
||||
|
#define MAP_ANONYMOUS MAP_ANON |
||||
|
#endif |
||||
|
|
||||
|
#ifndef compiler_barrier |
||||
|
#define compiler_barrier() { asm volatile("" ::: "memory"); } |
||||
|
#endif |
||||
|
|
||||
|
#ifndef cas |
||||
|
#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new))) |
||||
|
#endif |
||||
|
|
||||
|
/** |
||||
|
* This cache is designed to store a,b,c->res, with a,b,c,res 64-bit integers. |
||||
|
* |
||||
|
* Each cache bucket takes 32 bytes, 2 per cache line. |
||||
|
* Each cache status bucket takes 4 bytes, 16 per cache line. |
||||
|
* Therefore, size 2^N = 36*(2^N) bytes. |
||||
|
*/ |
||||
|
|
||||
|
struct __attribute__((packed)) cache_entry { |
||||
|
uint64_t a; |
||||
|
uint64_t b; |
||||
|
uint64_t c; |
||||
|
uint64_t res; |
||||
|
}; |
||||
|
|
||||
|
static size_t cache_size; // power of 2 |
||||
|
static size_t cache_max; // power of 2 |
||||
|
#if CACHE_MASK |
||||
|
static size_t cache_mask; // cache_size-1 |
||||
|
#endif |
||||
|
static cache_entry_t cache_table; |
||||
|
static uint32_t* cache_status; |
||||
|
|
||||
|
static uint64_t next_opid; |
||||
|
|
||||
|
uint64_t |
||||
|
cache_next_opid() |
||||
|
{ |
||||
|
return __sync_fetch_and_add(&next_opid, 1LL<<40); |
||||
|
} |
||||
|
|
||||
|
// status: 0x80000000 - bitlock |
||||
|
// 0x7fff0000 - hash (part of the 64-bit hash not used to position) |
||||
|
// 0x0000ffff - tag (every put increases tag field) |
||||
|
|
||||
|
/* Rotating 64-bit FNV-1a hash */ |
||||
|
static uint64_t |
||||
|
cache_hash(uint64_t a, uint64_t b, uint64_t c) |
||||
|
{ |
||||
|
const uint64_t prime = 1099511628211; |
||||
|
uint64_t hash = 14695981039346656037LLU; |
||||
|
hash = (hash ^ (a>>32)); |
||||
|
hash = (hash ^ a) * prime; |
||||
|
hash = (hash ^ b) * prime; |
||||
|
hash = (hash ^ c) * prime; |
||||
|
return hash; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
cache_get(uint64_t a, uint64_t b, uint64_t c, uint64_t *res) |
||||
|
{ |
||||
|
const uint64_t hash = cache_hash(a, b, c); |
||||
|
#if CACHE_MASK |
||||
|
volatile uint32_t *s_bucket = cache_status + (hash & cache_mask); |
||||
|
cache_entry_t bucket = cache_table + (hash & cache_mask); |
||||
|
#else |
||||
|
volatile uint32_t *s_bucket = cache_status + (hash % cache_size); |
||||
|
cache_entry_t bucket = cache_table + (hash % cache_size); |
||||
|
#endif |
||||
|
const uint32_t s = *s_bucket; |
||||
|
compiler_barrier(); |
||||
|
// abort if locked |
||||
|
if (s & 0x80000000) return 0; |
||||
|
// abort if different hash |
||||
|
if ((s ^ (hash>>32)) & 0x7fff0000) return 0; |
||||
|
// abort if key different |
||||
|
if (bucket->a != a || bucket->b != b || bucket->c != c) return 0; |
||||
|
*res = bucket->res; |
||||
|
compiler_barrier(); |
||||
|
// abort if status field changed after compiler_barrier() |
||||
|
return *s_bucket == s ? 1 : 0; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
cache_put(uint64_t a, uint64_t b, uint64_t c, uint64_t res) |
||||
|
{ |
||||
|
const uint64_t hash = cache_hash(a, b, c); |
||||
|
#if CACHE_MASK |
||||
|
volatile uint32_t *s_bucket = cache_status + (hash & cache_mask); |
||||
|
cache_entry_t bucket = cache_table + (hash & cache_mask); |
||||
|
#else |
||||
|
volatile uint32_t *s_bucket = cache_status + (hash % cache_size); |
||||
|
cache_entry_t bucket = cache_table + (hash % cache_size); |
||||
|
#endif |
||||
|
const uint32_t s = *s_bucket; |
||||
|
// abort if locked |
||||
|
if (s & 0x80000000) return 0; |
||||
|
// abort if hash identical -> no: in iscasmc this occasionally causes timeouts?! |
||||
|
const uint32_t hash_mask = (hash>>32) & 0x7fff0000; |
||||
|
// if ((s & 0x7fff0000) == hash_mask) return 0; |
||||
|
// use cas to claim bucket |
||||
|
const uint32_t new_s = ((s+1) & 0x0000ffff) | hash_mask; |
||||
|
if (!cas(s_bucket, s, new_s | 0x80000000)) return 0; |
||||
|
// cas succesful: write data |
||||
|
bucket->a = a; |
||||
|
bucket->b = b; |
||||
|
bucket->c = c; |
||||
|
bucket->res = res; |
||||
|
compiler_barrier(); |
||||
|
// after compiler_barrier(), unlock status field |
||||
|
*s_bucket = new_s; |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
cache_create(size_t _cache_size, size_t _max_size) |
||||
|
{ |
||||
|
#if CACHE_MASK |
||||
|
// Cache size must be a power of 2 |
||||
|
if (__builtin_popcountll(_cache_size) != 1 || __builtin_popcountll(_max_size) != 1) { |
||||
|
fprintf(stderr, "cache_create: Table size must be a power of 2!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
cache_size = _cache_size; |
||||
|
cache_max = _max_size; |
||||
|
#if CACHE_MASK |
||||
|
cache_mask = cache_size - 1; |
||||
|
#endif |
||||
|
|
||||
|
if (cache_size > cache_max) { |
||||
|
fprintf(stderr, "cache_create: Table size must be <= max size!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
cache_table = (cache_entry_t)mmap(0, cache_max * sizeof(struct cache_entry), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
||||
|
cache_status = (uint32_t*)mmap(0, cache_max * sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
||||
|
|
||||
|
if (cache_table == (cache_entry_t)-1 || cache_status == (uint32_t*)-1) { |
||||
|
fprintf(stderr, "cache_create: Unable to allocate memory: %s!\n", strerror(errno)); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
next_opid = 512LL << 40; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
cache_free() |
||||
|
{ |
||||
|
munmap(cache_table, cache_max * sizeof(struct cache_entry)); |
||||
|
munmap(cache_status, cache_max * sizeof(uint32_t)); |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
cache_clear() |
||||
|
{ |
||||
|
// a bit silly, but this works just fine, and does not require writing 0 everywhere... |
||||
|
cache_free(); |
||||
|
cache_create(cache_size, cache_max); |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
cache_setsize(size_t size) |
||||
|
{ |
||||
|
// easy solution |
||||
|
cache_free(); |
||||
|
cache_create(size, cache_max); |
||||
|
} |
||||
|
|
||||
|
size_t |
||||
|
cache_getsize() |
||||
|
{ |
||||
|
return cache_size; |
||||
|
} |
||||
|
|
||||
|
size_t |
||||
|
cache_getused() |
||||
|
{ |
||||
|
size_t result = 0; |
||||
|
for (size_t i=0;i<cache_size;i++) { |
||||
|
uint32_t s = cache_status[i]; |
||||
|
if (s & 0x80000000) fprintf(stderr, "cache_getuser: cache in use during cache_getused()\n"); |
||||
|
if (s) result++; |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
size_t |
||||
|
cache_getmaxsize() |
||||
|
{ |
||||
|
return cache_max; |
||||
|
} |
@ -0,0 +1,113 @@ |
|||||
|
#include <stdint.h> // for uint32_t etc |
||||
|
|
||||
|
#include <sylvan_config.h> |
||||
|
|
||||
|
#ifndef CACHE_H |
||||
|
#define CACHE_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#ifndef CACHE_MASK |
||||
|
#define CACHE_MASK 1 |
||||
|
#endif |
||||
|
|
||||
|
/** |
||||
|
* Operation cache |
||||
|
* |
||||
|
* Use cache_next_opid() at initialization time to generate unique "operation identifiers". |
||||
|
* You should store these operation identifiers as static globals in your implementation .C/.CPP file. |
||||
|
* |
||||
|
* For custom operations, just use the following functions: |
||||
|
* - cache_get3/cache_put3 for any operation with 1 BDD and 2 other values (that can be BDDs) |
||||
|
* int success = cache_get3(opid, dd1, value2, value3, &result); |
||||
|
* int success = cache_put3(opid, dd1, value2, value3, result); |
||||
|
* - cache_get4/cache_put4 for any operation with 4 BDDs |
||||
|
* int success = cache_get4(opid, dd1, dd2, dd3, dd4, &result); |
||||
|
* int success = cache_get4(opid, dd1, dd2, dd3, dd4, result); |
||||
|
* |
||||
|
* Notes: |
||||
|
* - The "result" is any 64-bit value |
||||
|
* - Use "0" for unused parameters |
||||
|
*/ |
||||
|
|
||||
|
typedef struct cache_entry *cache_entry_t; |
||||
|
|
||||
|
/** |
||||
|
* Primitives for cache get/put |
||||
|
*/ |
||||
|
int cache_get(uint64_t a, uint64_t b, uint64_t c, uint64_t *res); |
||||
|
int cache_put(uint64_t a, uint64_t b, uint64_t c, uint64_t res); |
||||
|
|
||||
|
/** |
||||
|
* Helper function to get next 'operation id' (during initialization of modules) |
||||
|
*/ |
||||
|
uint64_t cache_next_opid(); |
||||
|
|
||||
|
/** |
||||
|
* dd must be MTBDD, d2/d3 can be anything |
||||
|
*/ |
||||
|
static inline int __attribute__((unused)) |
||||
|
cache_get3(uint64_t opid, uint64_t dd, uint64_t d2, uint64_t d3, uint64_t *res) |
||||
|
{ |
||||
|
return cache_get(dd | opid, d2, d3, res); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* dd/dd2/dd3/dd4 must be MTBDDs |
||||
|
*/ |
||||
|
static inline int __attribute__((unused)) |
||||
|
cache_get4(uint64_t opid, uint64_t dd, uint64_t dd2, uint64_t dd3, uint64_t dd4, uint64_t *res) |
||||
|
{ |
||||
|
uint64_t p2 = dd2 | ((dd4 & 0x00000000000fffff) << 40); // 20 bits and complement bit |
||||
|
if (dd4 & 0x8000000000000000) p2 |= 0x4000000000000000; |
||||
|
uint64_t p3 = dd3 | ((dd4 & 0x000000fffff00000) << 20); // 20 bits |
||||
|
|
||||
|
return cache_get3(opid, dd, p2, p3, res); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* dd must be MTBDD, d2/d3 can be anything |
||||
|
*/ |
||||
|
static inline int __attribute__((unused)) |
||||
|
cache_put3(uint64_t opid, uint64_t dd, uint64_t d2, uint64_t d3, uint64_t res) |
||||
|
{ |
||||
|
return cache_put(dd | opid, d2, d3, res); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* dd/dd2/dd3/dd4 must be MTBDDs |
||||
|
*/ |
||||
|
static inline int __attribute__((unused)) |
||||
|
cache_put4(uint64_t opid, uint64_t dd, uint64_t dd2, uint64_t dd3, uint64_t dd4, uint64_t res) |
||||
|
{ |
||||
|
uint64_t p2 = dd2 | ((dd4 & 0x00000000000fffff) << 40); // 20 bits and complement bit |
||||
|
if (dd4 & 0x8000000000000000) p2 |= 0x4000000000000000; |
||||
|
uint64_t p3 = dd3 | ((dd4 & 0x000000fffff00000) << 20); // 20 bits |
||||
|
|
||||
|
return cache_put3(opid, dd, p2, p3, res); |
||||
|
} |
||||
|
/** |
||||
|
* Functions for Sylvan for cache management |
||||
|
*/ |
||||
|
|
||||
|
void cache_create(size_t _cache_size, size_t _max_size); |
||||
|
|
||||
|
void cache_free(); |
||||
|
|
||||
|
void cache_clear(); |
||||
|
|
||||
|
void cache_setsize(size_t size); |
||||
|
|
||||
|
size_t cache_getused(); |
||||
|
|
||||
|
size_t cache_getsize(); |
||||
|
|
||||
|
size_t cache_getmaxsize(); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#endif |
@ -0,0 +1,304 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#include <sylvan_config.h> |
||||
|
|
||||
|
#include <sylvan.h> |
||||
|
#include <sylvan_common.h> |
||||
|
|
||||
|
#ifndef cas |
||||
|
#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new))) |
||||
|
#endif |
||||
|
|
||||
|
/** |
||||
|
* Static global variables |
||||
|
*/ |
||||
|
|
||||
|
llmsset_t nodes; |
||||
|
|
||||
|
/** |
||||
|
* Retrieve nodes |
||||
|
*/ |
||||
|
|
||||
|
llmsset_t |
||||
|
__sylvan_get_internal_data() |
||||
|
{ |
||||
|
return nodes; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Calculate table usage (in parallel) |
||||
|
*/ |
||||
|
VOID_TASK_IMPL_2(sylvan_table_usage, size_t*, filled, size_t*, total) |
||||
|
{ |
||||
|
size_t tot = llmsset_get_size(nodes); |
||||
|
if (filled != NULL) *filled = llmsset_count_marked(nodes); |
||||
|
if (total != NULL) *total = tot; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Implementation of garbage collection |
||||
|
*/ |
||||
|
static int gc_enabled = 1; |
||||
|
static volatile int gc; // variable used in cas switch to ensure only one gc at a time |
||||
|
|
||||
|
struct reg_gc_mark_entry |
||||
|
{ |
||||
|
struct reg_gc_mark_entry *next; |
||||
|
gc_mark_cb cb; |
||||
|
int order; |
||||
|
}; |
||||
|
|
||||
|
static struct reg_gc_mark_entry *gc_mark_register = NULL; |
||||
|
|
||||
|
void |
||||
|
sylvan_gc_add_mark(int order, gc_mark_cb cb) |
||||
|
{ |
||||
|
struct reg_gc_mark_entry *e = (struct reg_gc_mark_entry*)malloc(sizeof(struct reg_gc_mark_entry)); |
||||
|
e->cb = cb; |
||||
|
e->order = order; |
||||
|
if (gc_mark_register == NULL || gc_mark_register->order>order) { |
||||
|
e->next = gc_mark_register; |
||||
|
gc_mark_register = e; |
||||
|
return; |
||||
|
} |
||||
|
struct reg_gc_mark_entry *f = gc_mark_register; |
||||
|
for (;;) { |
||||
|
if (f->next == NULL) { |
||||
|
e->next = NULL; |
||||
|
f->next = e; |
||||
|
return; |
||||
|
} |
||||
|
if (f->next->order > order) { |
||||
|
e->next = f->next; |
||||
|
f->next = e; |
||||
|
return; |
||||
|
} |
||||
|
f = f->next; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static gc_hook_cb gc_hook; |
||||
|
|
||||
|
void |
||||
|
sylvan_gc_set_hook(gc_hook_cb new_hook) |
||||
|
{ |
||||
|
gc_hook = new_hook; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
sylvan_gc_enable() |
||||
|
{ |
||||
|
gc_enabled = 1; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
sylvan_gc_disable() |
||||
|
{ |
||||
|
gc_enabled = 0; |
||||
|
} |
||||
|
|
||||
|
/* Mark hook for cache */ |
||||
|
VOID_TASK_0(sylvan_gc_mark_cache) |
||||
|
{ |
||||
|
/* We simply clear the cache. |
||||
|
* Alternatively, we could implement for example some strategy |
||||
|
* where part of the cache is cleared and part is marked |
||||
|
*/ |
||||
|
cache_clear(); |
||||
|
} |
||||
|
|
||||
|
/* Default hook */ |
||||
|
|
||||
|
size_t |
||||
|
next_size(size_t n) |
||||
|
{ |
||||
|
#if SYLVAN_SIZE_FIBONACCI |
||||
|
size_t f1=1, f2=1; |
||||
|
for (;;) { |
||||
|
f2 += f1; |
||||
|
if (f2 > n) return f2; |
||||
|
f1 += f2; |
||||
|
if (f1 > n) return f1; |
||||
|
} |
||||
|
#else |
||||
|
return n*2; |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_IMPL_0(sylvan_gc_aggressive_resize) |
||||
|
{ |
||||
|
/** |
||||
|
* Always resize when gc called |
||||
|
*/ |
||||
|
size_t max_size = llmsset_get_max_size(nodes); |
||||
|
size_t size = llmsset_get_size(nodes); |
||||
|
if (size < max_size) { |
||||
|
size_t new_size = next_size(size); |
||||
|
if (new_size > max_size) new_size = max_size; |
||||
|
llmsset_set_size(nodes, new_size); |
||||
|
size_t cache_size = cache_getsize(); |
||||
|
size_t cache_max = cache_getmaxsize(); |
||||
|
if (cache_size < cache_max) { |
||||
|
new_size = next_size(cache_size); |
||||
|
if (new_size > cache_max) new_size = cache_max; |
||||
|
cache_setsize(new_size); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_IMPL_0(sylvan_gc_default_hook) |
||||
|
{ |
||||
|
/** |
||||
|
* Default behavior: |
||||
|
* if we can resize the nodes set, and if we use more than 50%, then increase size |
||||
|
*/ |
||||
|
size_t max_size = llmsset_get_max_size(nodes); |
||||
|
size_t size = llmsset_get_size(nodes); |
||||
|
if (size < max_size) { |
||||
|
size_t marked = llmsset_count_marked(nodes); |
||||
|
if (marked*2 > size) { |
||||
|
size_t new_size = next_size(size); |
||||
|
if (new_size > max_size) new_size = max_size; |
||||
|
llmsset_set_size(nodes, new_size); |
||||
|
size_t cache_size = cache_getsize(); |
||||
|
size_t cache_max = cache_getmaxsize(); |
||||
|
if (cache_size < cache_max) { |
||||
|
new_size = next_size(cache_size); |
||||
|
if (new_size > cache_max) new_size = cache_max; |
||||
|
cache_setsize(new_size); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_0(sylvan_gc_call_hook) |
||||
|
{ |
||||
|
// call hook function (resizing, reordering, etc) |
||||
|
WRAP(gc_hook); |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_0(sylvan_gc_rehash) |
||||
|
{ |
||||
|
// rehash marked nodes |
||||
|
llmsset_rehash(nodes); |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_0(sylvan_gc_destroy_unmarked) |
||||
|
{ |
||||
|
llmsset_destroy_unmarked(nodes); |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_0(sylvan_gc_go) |
||||
|
{ |
||||
|
sylvan_stats_count(SYLVAN_GC_COUNT); |
||||
|
sylvan_timer_start(SYLVAN_GC); |
||||
|
|
||||
|
// clear hash array |
||||
|
llmsset_clear(nodes); |
||||
|
|
||||
|
// call mark functions, hook and rehash |
||||
|
struct reg_gc_mark_entry *e = gc_mark_register; |
||||
|
while (e != NULL) { |
||||
|
WRAP(e->cb); |
||||
|
e = e->next; |
||||
|
} |
||||
|
|
||||
|
sylvan_timer_stop(SYLVAN_GC); |
||||
|
} |
||||
|
|
||||
|
/* Perform garbage collection */ |
||||
|
VOID_TASK_IMPL_0(sylvan_gc) |
||||
|
{ |
||||
|
if (!gc_enabled) return; |
||||
|
if (cas(&gc, 0, 1)) { |
||||
|
NEWFRAME(sylvan_gc_go); |
||||
|
gc = 0; |
||||
|
} else { |
||||
|
/* wait for new frame to appear */ |
||||
|
while (*(Task* volatile*)&(lace_newframe.t) == 0) {} |
||||
|
lace_yield(__lace_worker, __lace_dq_head); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Package init and quit functions |
||||
|
*/ |
||||
|
void |
||||
|
sylvan_init_package(size_t tablesize, size_t maxsize, size_t cachesize, size_t max_cachesize) |
||||
|
{ |
||||
|
if (tablesize > maxsize) tablesize = maxsize; |
||||
|
if (cachesize > max_cachesize) cachesize = max_cachesize; |
||||
|
|
||||
|
if (maxsize > 0x000003ffffffffff) { |
||||
|
fprintf(stderr, "sylvan_init_package error: tablesize must be <= 42 bits!\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
nodes = llmsset_create(tablesize, maxsize); |
||||
|
cache_create(cachesize, max_cachesize); |
||||
|
|
||||
|
gc = 0; |
||||
|
#if SYLVAN_AGGRESSIVE_RESIZE |
||||
|
gc_hook = TASK(sylvan_gc_aggressive_resize); |
||||
|
#else |
||||
|
gc_hook = TASK(sylvan_gc_default_hook); |
||||
|
#endif |
||||
|
sylvan_gc_add_mark(10, TASK(sylvan_gc_mark_cache)); |
||||
|
sylvan_gc_add_mark(19, TASK(sylvan_gc_destroy_unmarked)); |
||||
|
sylvan_gc_add_mark(20, TASK(sylvan_gc_call_hook)); |
||||
|
sylvan_gc_add_mark(30, TASK(sylvan_gc_rehash)); |
||||
|
|
||||
|
LACE_ME; |
||||
|
sylvan_stats_init(); |
||||
|
} |
||||
|
|
||||
|
struct reg_quit_entry |
||||
|
{ |
||||
|
struct reg_quit_entry *next; |
||||
|
quit_cb cb; |
||||
|
}; |
||||
|
|
||||
|
static struct reg_quit_entry *quit_register = NULL; |
||||
|
|
||||
|
void |
||||
|
sylvan_register_quit(quit_cb cb) |
||||
|
{ |
||||
|
struct reg_quit_entry *e = (struct reg_quit_entry*)malloc(sizeof(struct reg_quit_entry)); |
||||
|
e->next = quit_register; |
||||
|
e->cb = cb; |
||||
|
quit_register = e; |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
sylvan_quit() |
||||
|
{ |
||||
|
while (quit_register != NULL) { |
||||
|
struct reg_quit_entry *e = quit_register; |
||||
|
quit_register = e->next; |
||||
|
e->cb(); |
||||
|
free(e); |
||||
|
} |
||||
|
|
||||
|
while (gc_mark_register != NULL) { |
||||
|
struct reg_gc_mark_entry *e = gc_mark_register; |
||||
|
gc_mark_register = e->next; |
||||
|
free(e); |
||||
|
} |
||||
|
|
||||
|
cache_free(); |
||||
|
llmsset_free(nodes); |
||||
|
} |
@ -0,0 +1,85 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef SYLVAN_COMMON_H |
||||
|
#define SYLVAN_COMMON_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
/* Garbage collection test task - t */ |
||||
|
#define sylvan_gc_test() YIELD_NEWFRAME() |
||||
|
|
||||
|
// BDD operations |
||||
|
#define CACHE_BDD_ITE (0LL<<40) |
||||
|
#define CACHE_BDD_AND (1LL<<40) |
||||
|
#define CACHE_BDD_XOR (2LL<<40) |
||||
|
#define CACHE_BDD_EXISTS (3LL<<40) |
||||
|
#define CACHE_BDD_AND_EXISTS (4LL<<40) |
||||
|
#define CACHE_BDD_RELNEXT (5LL<<40) |
||||
|
#define CACHE_BDD_RELPREV (6LL<<40) |
||||
|
#define CACHE_BDD_SATCOUNT (7LL<<40) |
||||
|
#define CACHE_BDD_COMPOSE (8LL<<40) |
||||
|
#define CACHE_BDD_RESTRICT (9LL<<40) |
||||
|
#define CACHE_BDD_CONSTRAIN (10LL<<40) |
||||
|
#define CACHE_BDD_CLOSURE (11LL<<40) |
||||
|
#define CACHE_BDD_ISBDD (12LL<<40) |
||||
|
#define CACHE_BDD_SUPPORT (13LL<<40) |
||||
|
#define CACHE_BDD_PATHCOUNT (14LL<<40) |
||||
|
|
||||
|
// MDD operations |
||||
|
#define CACHE_MDD_RELPROD (20LL<<40) |
||||
|
#define CACHE_MDD_MINUS (21LL<<40) |
||||
|
#define CACHE_MDD_UNION (22LL<<40) |
||||
|
#define CACHE_MDD_INTERSECT (23LL<<40) |
||||
|
#define CACHE_MDD_PROJECT (24LL<<40) |
||||
|
#define CACHE_MDD_JOIN (25LL<<40) |
||||
|
#define CACHE_MDD_MATCH (26LL<<40) |
||||
|
#define CACHE_MDD_RELPREV (27LL<<40) |
||||
|
#define CACHE_MDD_SATCOUNT (28LL<<40) |
||||
|
#define CACHE_MDD_SATCOUNTL1 (29LL<<40) |
||||
|
#define CACHE_MDD_SATCOUNTL2 (30LL<<40) |
||||
|
|
||||
|
// MTBDD operations |
||||
|
#define CACHE_MTBDD_APPLY (40LL<<40) |
||||
|
#define CACHE_MTBDD_UAPPLY (41LL<<40) |
||||
|
#define CACHE_MTBDD_ABSTRACT (42LL<<40) |
||||
|
#define CACHE_MTBDD_ITE (43LL<<40) |
||||
|
#define CACHE_MTBDD_AND_EXISTS (44LL<<40) |
||||
|
#define CACHE_MTBDD_SUPPORT (45LL<<40) |
||||
|
#define CACHE_MTBDD_COMPOSE (46LL<<40) |
||||
|
#define CACHE_MTBDD_EQUAL_NORM (47LL<<40) |
||||
|
#define CACHE_MTBDD_EQUAL_NORM_REL (48LL<<40) |
||||
|
#define CACHE_MTBDD_MINIMUM (49LL<<40) |
||||
|
#define CACHE_MTBDD_MAXIMUM (50LL<<40) |
||||
|
#define CACHE_MTBDD_LEQ (51LL<<40) |
||||
|
#define CACHE_MTBDD_LESS (52LL<<40) |
||||
|
#define CACHE_MTBDD_GEQ (53LL<<40) |
||||
|
#define CACHE_MTBDD_GREATER (54LL<<40) |
||||
|
#define CACHE_MTBDD_NONZERO_COUNT (55LL<<40) |
||||
|
|
||||
|
/** |
||||
|
* Registration of quit functions |
||||
|
*/ |
||||
|
typedef void (*quit_cb)(); |
||||
|
void sylvan_register_quit(quit_cb cb); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#endif |
@ -0,0 +1,30 @@ |
|||||
|
/* Operation cache: use bitmasks for module (size must be power of 2!) */ |
||||
|
#ifndef CACHE_MASK |
||||
|
#define CACHE_MASK 1 |
||||
|
#endif |
||||
|
|
||||
|
/* Nodes table: use bitmasks for module (size must be power of 2!) */ |
||||
|
#ifndef LLMSSET_MASK |
||||
|
#define LLMSSET_MASK 1 |
||||
|
#endif |
||||
|
|
||||
|
/** |
||||
|
* Use Fibonacci sequence as resizing strategy. |
||||
|
* This MAY result in more conservative memory consumption, but is not |
||||
|
* great for performance. |
||||
|
* By default, powers of 2 should be used. |
||||
|
* If you set this, then set CACHE_MASK and LLMSSET_MASK to 0. |
||||
|
*/ |
||||
|
#ifndef SYLVAN_SIZE_FIBONACCI |
||||
|
#define SYLVAN_SIZE_FIBONACCI 0 |
||||
|
#endif |
||||
|
|
||||
|
/* Enable/disable counters and timers */ |
||||
|
#ifndef SYLVAN_STATS |
||||
|
#define SYLVAN_STATS 0 |
||||
|
#endif |
||||
|
|
||||
|
/* Aggressive or conservative resizing strategy */ |
||||
|
#ifndef SYLVAN_AGGRESSIVE_RESIZE |
||||
|
#define SYLVAN_AGGRESSIVE_RESIZE 1 |
||||
|
#endif |
@ -0,0 +1,595 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#include <sylvan_config.h> |
||||
|
|
||||
|
#include <assert.h> |
||||
|
#include <inttypes.h> |
||||
|
#include <math.h> |
||||
|
#include <stdint.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#include <sylvan.h> |
||||
|
#include <sylvan_common.h> |
||||
|
#include <sylvan_mtbdd_int.h> |
||||
|
#include <sylvan_gmp.h> |
||||
|
#include <gmp.h> |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* helper function for hash |
||||
|
*/ |
||||
|
#ifndef rotl64 |
||||
|
static inline uint64_t |
||||
|
rotl64(uint64_t x, int8_t r) |
||||
|
{ |
||||
|
return ((x<<r) | (x>>(64-r))); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
static uint64_t |
||||
|
gmp_hash(const uint64_t v, const uint64_t seed) |
||||
|
{ |
||||
|
/* Hash the mpq in pointer v |
||||
|
* A simpler way would be to hash the result of mpq_get_d. |
||||
|
* We just hash on the contents of the memory */ |
||||
|
|
||||
|
mpq_ptr x = (mpq_ptr)(size_t)v; |
||||
|
|
||||
|
const uint64_t prime = 1099511628211; |
||||
|
uint64_t hash = seed; |
||||
|
mp_limb_t *limbs; |
||||
|
|
||||
|
// hash "numerator" limbs |
||||
|
limbs = x[0]._mp_num._mp_d; |
||||
|
for (int i=0; i<x[0]._mp_num._mp_size; i++) { |
||||
|
hash = hash ^ limbs[i]; |
||||
|
hash = rotl64(hash, 47); |
||||
|
hash = hash * prime; |
||||
|
} |
||||
|
|
||||
|
// hash "denominator" limbs |
||||
|
limbs = x[0]._mp_den._mp_d; |
||||
|
for (int i=0; i<x[0]._mp_den._mp_size; i++) { |
||||
|
hash = hash ^ limbs[i]; |
||||
|
hash = rotl64(hash, 31); |
||||
|
hash = hash * prime; |
||||
|
} |
||||
|
|
||||
|
return hash ^ (hash >> 32); |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
gmp_equals(const uint64_t left, const uint64_t right) |
||||
|
{ |
||||
|
/* This function is called by the unique table when comparing a new |
||||
|
leaf with an existing leaf */ |
||||
|
mpq_ptr x = (mpq_ptr)(size_t)left; |
||||
|
mpq_ptr y = (mpq_ptr)(size_t)right; |
||||
|
|
||||
|
/* Just compare x and y */ |
||||
|
return mpq_equal(x, y) ? 1 : 0; |
||||
|
} |
||||
|
|
||||
|
static void |
||||
|
gmp_create(uint64_t *val) |
||||
|
{ |
||||
|
/* This function is called by the unique table when a leaf does not yet exist. |
||||
|
We make a copy, which will be stored in the hash table. */ |
||||
|
mpq_ptr x = (mpq_ptr)malloc(sizeof(__mpq_struct)); |
||||
|
mpq_init(x); |
||||
|
mpq_set(x, *(mpq_ptr*)val); |
||||
|
*(mpq_ptr*)val = x; |
||||
|
} |
||||
|
|
||||
|
static void |
||||
|
gmp_destroy(uint64_t val) |
||||
|
{ |
||||
|
/* This function is called by the unique table |
||||
|
when a leaf is removed during garbage collection. */ |
||||
|
mpq_clear((mpq_ptr)val); |
||||
|
free((void*)val); |
||||
|
} |
||||
|
|
||||
|
static uint32_t gmp_type; |
||||
|
static uint64_t CACHE_GMP_AND_EXISTS; |
||||
|
|
||||
|
/** |
||||
|
* Initialize gmp custom leaves |
||||
|
*/ |
||||
|
void |
||||
|
gmp_init() |
||||
|
{ |
||||
|
/* Register custom leaf 3 */ |
||||
|
gmp_type = mtbdd_register_custom_leaf(gmp_hash, gmp_equals, gmp_create, gmp_destroy); |
||||
|
CACHE_GMP_AND_EXISTS = cache_next_opid(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Create GMP mpq leaf |
||||
|
*/ |
||||
|
MTBDD |
||||
|
mtbdd_gmp(mpq_t val) |
||||
|
{ |
||||
|
mpq_canonicalize(val); |
||||
|
return mtbdd_makeleaf(gmp_type, (size_t)val); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Operation "plus" for two mpq MTBDDs |
||||
|
* Interpret partial function as "0" |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, gmp_op_plus, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
/* Check for partial functions */ |
||||
|
if (a == mtbdd_false) return b; |
||||
|
if (b == mtbdd_false) return a; |
||||
|
|
||||
|
/* If both leaves, compute plus */ |
||||
|
if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { |
||||
|
mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); |
||||
|
mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); |
||||
|
|
||||
|
mpq_t mres; |
||||
|
mpq_init(mres); |
||||
|
mpq_add(mres, ma, mb); |
||||
|
MTBDD res = mtbdd_gmp(mres); |
||||
|
mpq_clear(mres); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
/* Commutative, so swap a,b for better cache performance */ |
||||
|
if (a < b) { |
||||
|
*pa = b; |
||||
|
*pb = a; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Operation "minus" for two mpq MTBDDs |
||||
|
* Interpret partial function as "0" |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, gmp_op_minus, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
/* Check for partial functions */ |
||||
|
if (a == mtbdd_false) return gmp_neg(b); |
||||
|
if (b == mtbdd_false) return a; |
||||
|
|
||||
|
/* If both leaves, compute plus */ |
||||
|
if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { |
||||
|
mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); |
||||
|
mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); |
||||
|
|
||||
|
mpq_t mres; |
||||
|
mpq_init(mres); |
||||
|
mpq_sub(mres, ma, mb); |
||||
|
MTBDD res = mtbdd_gmp(mres); |
||||
|
mpq_clear(mres); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Operation "times" for two mpq MTBDDs. |
||||
|
* One of the parameters can be a BDD, then it is interpreted as a filter. |
||||
|
* For partial functions, domain is intersection |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, gmp_op_times, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
/* Check for partial functions and for Boolean (filter) */ |
||||
|
if (a == mtbdd_false || b == mtbdd_false) return mtbdd_false; |
||||
|
|
||||
|
/* If one of Boolean, interpret as filter */ |
||||
|
if (a == mtbdd_true) return b; |
||||
|
if (b == mtbdd_true) return a; |
||||
|
|
||||
|
/* Handle multiplication of leaves */ |
||||
|
if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { |
||||
|
mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); |
||||
|
mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); |
||||
|
|
||||
|
// compute result |
||||
|
mpq_t mres; |
||||
|
mpq_init(mres); |
||||
|
mpq_mul(mres, ma, mb); |
||||
|
MTBDD res = mtbdd_gmp(mres); |
||||
|
mpq_clear(mres); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
/* Commutative, so make "a" the lowest for better cache performance */ |
||||
|
if (a < b) { |
||||
|
*pa = b; |
||||
|
*pb = a; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Operation "divide" for two mpq MTBDDs. |
||||
|
* For partial functions, domain is intersection |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, gmp_op_divide, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
/* Check for partial functions */ |
||||
|
if (a == mtbdd_false || b == mtbdd_false) return mtbdd_false; |
||||
|
|
||||
|
/* Handle division of leaves */ |
||||
|
if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { |
||||
|
mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); |
||||
|
mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); |
||||
|
|
||||
|
// compute result |
||||
|
mpq_t mres; |
||||
|
mpq_init(mres); |
||||
|
mpq_div(mres, ma, mb); |
||||
|
MTBDD res = mtbdd_gmp(mres); |
||||
|
mpq_clear(mres); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Operation "min" for two mpq MTBDDs. |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, gmp_op_min, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
/* Handle partial functions */ |
||||
|
if (a == mtbdd_false) return b; |
||||
|
if (b == mtbdd_false) return a; |
||||
|
|
||||
|
/* Handle trivial case */ |
||||
|
if (a == b) return a; |
||||
|
|
||||
|
/* Compute result for leaves */ |
||||
|
if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { |
||||
|
mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); |
||||
|
mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); |
||||
|
int cmp = mpq_cmp(ma, mb); |
||||
|
return cmp < 0 ? a : b; |
||||
|
} |
||||
|
|
||||
|
/* For cache performance */ |
||||
|
if (a < b) { |
||||
|
*pa = b; |
||||
|
*pb = a; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Operation "max" for two mpq MTBDDs. |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, gmp_op_max, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
/* Handle partial functions */ |
||||
|
if (a == mtbdd_false) return b; |
||||
|
if (b == mtbdd_false) return a; |
||||
|
|
||||
|
/* Handle trivial case */ |
||||
|
if (a == b) return a; |
||||
|
|
||||
|
/* Compute result for leaves */ |
||||
|
if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { |
||||
|
mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); |
||||
|
mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); |
||||
|
int cmp = mpq_cmp(ma, mb); |
||||
|
return cmp > 0 ? a : b; |
||||
|
} |
||||
|
|
||||
|
/* For cache performance */ |
||||
|
if (a < b) { |
||||
|
*pa = b; |
||||
|
*pb = a; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Operation "neg" for one mpq MTBDD |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, gmp_op_neg, MTBDD, dd, size_t, p) |
||||
|
{ |
||||
|
/* Handle partial functions */ |
||||
|
if (dd == mtbdd_false) return mtbdd_false; |
||||
|
|
||||
|
/* Compute result for leaf */ |
||||
|
if (mtbdd_isleaf(dd)) { |
||||
|
mpq_ptr m = (mpq_ptr)mtbdd_getvalue(dd); |
||||
|
|
||||
|
mpq_t mres; |
||||
|
mpq_init(mres); |
||||
|
mpq_neg(mres, m); |
||||
|
MTBDD res = mtbdd_gmp(mres); |
||||
|
mpq_clear(mres); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
(void)p; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Operation "abs" for one mpq MTBDD |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, gmp_op_abs, MTBDD, dd, size_t, p) |
||||
|
{ |
||||
|
/* Handle partial functions */ |
||||
|
if (dd == mtbdd_false) return mtbdd_false; |
||||
|
|
||||
|
/* Compute result for leaf */ |
||||
|
if (mtbdd_isleaf(dd)) { |
||||
|
mpq_ptr m = (mpq_ptr)mtbdd_getvalue(dd); |
||||
|
|
||||
|
mpq_t mres; |
||||
|
mpq_init(mres); |
||||
|
mpq_abs(mres, m); |
||||
|
MTBDD res = mtbdd_gmp(mres); |
||||
|
mpq_clear(mres); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
(void)p; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* The abstraction operators are called in either of two ways: |
||||
|
* - with k=0, then just calculate "a op b" |
||||
|
* - with k<>0, then just calculate "a := a op a", k times |
||||
|
*/ |
||||
|
|
||||
|
TASK_IMPL_3(MTBDD, gmp_abstract_op_plus, MTBDD, a, MTBDD, b, int, k) |
||||
|
{ |
||||
|
if (k==0) { |
||||
|
return mtbdd_apply(a, b, TASK(gmp_op_plus)); |
||||
|
} else { |
||||
|
MTBDD res = a; |
||||
|
for (int i=0; i<k; i++) { |
||||
|
mtbdd_refs_push(res); |
||||
|
res = mtbdd_apply(res, res, TASK(gmp_op_plus)); |
||||
|
mtbdd_refs_pop(1); |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_3(MTBDD, gmp_abstract_op_times, MTBDD, a, MTBDD, b, int, k) |
||||
|
{ |
||||
|
if (k==0) { |
||||
|
return mtbdd_apply(a, b, TASK(gmp_op_times)); |
||||
|
} else { |
||||
|
MTBDD res = a; |
||||
|
for (int i=0; i<k; i++) { |
||||
|
mtbdd_refs_push(res); |
||||
|
res = mtbdd_apply(res, res, TASK(gmp_op_times)); |
||||
|
mtbdd_refs_pop(1); |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_3(MTBDD, gmp_abstract_op_min, MTBDD, a, MTBDD, b, int, k) |
||||
|
{ |
||||
|
if (k == 0) { |
||||
|
return mtbdd_apply(a, b, TASK(gmp_op_min)); |
||||
|
} else { |
||||
|
// nothing to do: min(a, a) = a |
||||
|
return a; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_3(MTBDD, gmp_abstract_op_max, MTBDD, a, MTBDD, b, int, k) |
||||
|
{ |
||||
|
if (k == 0) { |
||||
|
return mtbdd_apply(a, b, TASK(gmp_op_max)); |
||||
|
} else { |
||||
|
// nothing to do: max(a, a) = a |
||||
|
return a; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Convert to Boolean MTBDD, terminals >= value (double) to True, or False otherwise. |
||||
|
*/ |
||||
|
TASK_2(MTBDD, gmp_op_threshold_d, MTBDD, a, size_t, svalue) |
||||
|
{ |
||||
|
/* Handle partial function */ |
||||
|
if (a == mtbdd_false) return mtbdd_false; |
||||
|
|
||||
|
/* Compute result */ |
||||
|
if (mtbdd_isleaf(a)) { |
||||
|
double value = *(double*)&svalue; |
||||
|
mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); |
||||
|
return mpq_get_d(ma) >= value ? mtbdd_true : mtbdd_false; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Convert to Boolean MTBDD, terminals > value (double) to True, or False otherwise. |
||||
|
*/ |
||||
|
TASK_2(MTBDD, gmp_op_strict_threshold_d, MTBDD, a, size_t, svalue) |
||||
|
{ |
||||
|
/* Handle partial function */ |
||||
|
if (a == mtbdd_false) return mtbdd_false; |
||||
|
|
||||
|
/* Compute result */ |
||||
|
if (mtbdd_isleaf(a)) { |
||||
|
double value = *(double*)&svalue; |
||||
|
mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); |
||||
|
return mpq_get_d(ma) > value ? mtbdd_true : mtbdd_false; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_2(MTBDD, gmp_threshold_d, MTBDD, dd, double, d) |
||||
|
{ |
||||
|
return mtbdd_uapply(dd, TASK(gmp_op_threshold_d), *(size_t*)&d); |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_2(MTBDD, gmp_strict_threshold_d, MTBDD, dd, double, d) |
||||
|
{ |
||||
|
return mtbdd_uapply(dd, TASK(gmp_op_strict_threshold_d), *(size_t*)&d); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Operation "threshold" for mpq MTBDDs. |
||||
|
* The second parameter must be a mpq leaf. |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, gmp_op_threshold, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
/* Check for partial functions */ |
||||
|
if (a == mtbdd_false) return mtbdd_false; |
||||
|
|
||||
|
/* Handle comparison of leaves */ |
||||
|
if (mtbdd_isleaf(a)) { |
||||
|
mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); |
||||
|
mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); |
||||
|
int cmp = mpq_cmp(ma, mb); |
||||
|
return cmp >= 0 ? mtbdd_true : mtbdd_false; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Operation "strict threshold" for mpq MTBDDs. |
||||
|
* The second parameter must be a mpq leaf. |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, gmp_op_strict_threshold, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
/* Check for partial functions */ |
||||
|
if (a == mtbdd_false) return mtbdd_false; |
||||
|
|
||||
|
/* Handle comparison of leaves */ |
||||
|
if (mtbdd_isleaf(a)) { |
||||
|
mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); |
||||
|
mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); |
||||
|
int cmp = mpq_cmp(ma, mb); |
||||
|
return cmp > 0 ? mtbdd_true : mtbdd_false; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Multiply <a> and <b>, and abstract variables <vars> using summation. |
||||
|
* This is similar to the "and_exists" operation in BDDs. |
||||
|
*/ |
||||
|
TASK_IMPL_3(MTBDD, gmp_and_exists, MTBDD, a, MTBDD, b, MTBDD, v) |
||||
|
{ |
||||
|
/* Check terminal cases */ |
||||
|
|
||||
|
/* If v == true, then <vars> is an empty set */ |
||||
|
if (v == mtbdd_true) return mtbdd_apply(a, b, TASK(gmp_op_times)); |
||||
|
|
||||
|
/* Try the times operator on a and b */ |
||||
|
MTBDD result = CALL(gmp_op_times, &a, &b); |
||||
|
if (result != mtbdd_invalid) { |
||||
|
/* Times operator successful, store reference (for garbage collection) */ |
||||
|
mtbdd_refs_push(result); |
||||
|
/* ... and perform abstraction */ |
||||
|
result = mtbdd_abstract(result, v, TASK(gmp_abstract_op_plus)); |
||||
|
mtbdd_refs_pop(1); |
||||
|
/* Note that the operation cache is used in mtbdd_abstract */ |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/* Maybe perform garbage collection */ |
||||
|
sylvan_gc_test(); |
||||
|
|
||||
|
/* Check cache. Note that we do this now, since the times operator might swap a and b (commutative) */ |
||||
|
if (cache_get3(CACHE_GMP_AND_EXISTS, a, b, v, &result)) return result; |
||||
|
|
||||
|
/* Now, v is not a constant, and either a or b is not a constant */ |
||||
|
|
||||
|
/* Get top variable */ |
||||
|
int la = mtbdd_isleaf(a); |
||||
|
int lb = mtbdd_isleaf(b); |
||||
|
mtbddnode_t na = la ? 0 : GETNODE(a); |
||||
|
mtbddnode_t nb = lb ? 0 : GETNODE(b); |
||||
|
uint32_t va = la ? 0xffffffff : mtbddnode_getvariable(na); |
||||
|
uint32_t vb = lb ? 0xffffffff : mtbddnode_getvariable(nb); |
||||
|
uint32_t var = va < vb ? va : vb; |
||||
|
|
||||
|
mtbddnode_t nv = GETNODE(v); |
||||
|
uint32_t vv = mtbddnode_getvariable(nv); |
||||
|
|
||||
|
if (vv < var) { |
||||
|
/* Recursive, then abstract result */ |
||||
|
result = CALL(gmp_and_exists, a, b, node_gethigh(v, nv)); |
||||
|
mtbdd_refs_push(result); |
||||
|
result = mtbdd_apply(result, result, TASK(gmp_op_plus)); |
||||
|
mtbdd_refs_pop(1); |
||||
|
} else { |
||||
|
/* Get cofactors */ |
||||
|
MTBDD alow, ahigh, blow, bhigh; |
||||
|
alow = (!la && va == var) ? node_getlow(a, na) : a; |
||||
|
ahigh = (!la && va == var) ? node_gethigh(a, na) : a; |
||||
|
blow = (!lb && vb == var) ? node_getlow(b, nb) : b; |
||||
|
bhigh = (!lb && vb == var) ? node_gethigh(b, nb) : b; |
||||
|
|
||||
|
if (vv == var) { |
||||
|
/* Recursive, then abstract result */ |
||||
|
mtbdd_refs_spawn(SPAWN(gmp_and_exists, ahigh, bhigh, node_gethigh(v, nv))); |
||||
|
MTBDD low = mtbdd_refs_push(CALL(gmp_and_exists, alow, blow, node_gethigh(v, nv))); |
||||
|
MTBDD high = mtbdd_refs_push(mtbdd_refs_sync(SYNC(gmp_and_exists))); |
||||
|
result = CALL(mtbdd_apply, low, high, TASK(gmp_op_plus)); |
||||
|
mtbdd_refs_pop(2); |
||||
|
} else /* vv > v */ { |
||||
|
/* Recursive, then create node */ |
||||
|
mtbdd_refs_spawn(SPAWN(gmp_and_exists, ahigh, bhigh, v)); |
||||
|
MTBDD low = mtbdd_refs_push(CALL(gmp_and_exists, alow, blow, v)); |
||||
|
MTBDD high = mtbdd_refs_sync(SYNC(gmp_and_exists)); |
||||
|
mtbdd_refs_pop(1); |
||||
|
result = mtbdd_makenode(var, low, high); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Store in cache */ |
||||
|
cache_put3(CACHE_GMP_AND_EXISTS, a, b, v, result); |
||||
|
return result; |
||||
|
} |
@ -0,0 +1,182 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* This is an implementation of GMP mpq custom leaves of MTBDDs |
||||
|
*/ |
||||
|
|
||||
|
#ifndef SYLVAN_GMP_H |
||||
|
#define SYLVAN_GMP_H |
||||
|
|
||||
|
#include <sylvan.h> |
||||
|
#include <gmp.h> |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
/** |
||||
|
* Initialize GMP custom leaves |
||||
|
*/ |
||||
|
void gmp_init(); |
||||
|
|
||||
|
/** |
||||
|
* Create MPQ leaf |
||||
|
*/ |
||||
|
MTBDD mtbdd_gmp(mpq_t val); |
||||
|
|
||||
|
/** |
||||
|
* Operation "plus" for two mpq MTBDDs |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_op_plus, MTBDD*, MTBDD*); |
||||
|
TASK_DECL_3(MTBDD, gmp_abstract_op_plus, MTBDD, MTBDD, int); |
||||
|
|
||||
|
/** |
||||
|
* Operation "minus" for two mpq MTBDDs |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_op_minus, MTBDD*, MTBDD*); |
||||
|
|
||||
|
/** |
||||
|
* Operation "times" for two mpq MTBDDs |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_op_times, MTBDD*, MTBDD*); |
||||
|
TASK_DECL_3(MTBDD, gmp_abstract_op_times, MTBDD, MTBDD, int); |
||||
|
|
||||
|
/** |
||||
|
* Operation "divide" for two mpq MTBDDs |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_op_divide, MTBDD*, MTBDD*); |
||||
|
|
||||
|
/** |
||||
|
* Operation "min" for two mpq MTBDDs |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_op_min, MTBDD*, MTBDD*); |
||||
|
TASK_DECL_3(MTBDD, gmp_abstract_op_min, MTBDD, MTBDD, int); |
||||
|
|
||||
|
/** |
||||
|
* Operation "max" for two mpq MTBDDs |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_op_max, MTBDD*, MTBDD*); |
||||
|
TASK_DECL_3(MTBDD, gmp_abstract_op_max, MTBDD, MTBDD, int); |
||||
|
|
||||
|
/** |
||||
|
* Operation "negate" for one mpq MTBDD |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_op_neg, MTBDD, size_t); |
||||
|
|
||||
|
/** |
||||
|
* Operation "abs" for one mpq MTBDD |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_op_abs, MTBDD, size_t); |
||||
|
|
||||
|
/** |
||||
|
* Compute a + b |
||||
|
*/ |
||||
|
#define gmp_plus(a, b) mtbdd_apply(a, b, TASK(gmp_op_plus)) |
||||
|
|
||||
|
/** |
||||
|
* Compute a + b |
||||
|
*/ |
||||
|
#define gmp_minus(a, b) mtbdd_apply(a, b, TASK(gmp_op_minus)) |
||||
|
|
||||
|
/** |
||||
|
* Compute a * b |
||||
|
*/ |
||||
|
#define gmp_times(a, b) mtbdd_apply(a, b, TASK(gmp_op_times)) |
||||
|
|
||||
|
/** |
||||
|
* Compute a * b |
||||
|
*/ |
||||
|
#define gmp_divide(a, b) mtbdd_apply(a, b, TASK(gmp_op_divide)) |
||||
|
|
||||
|
/** |
||||
|
* Compute min(a, b) |
||||
|
*/ |
||||
|
#define gmp_min(a, b) mtbdd_apply(a, b, TASK(gmp_op_min)) |
||||
|
|
||||
|
/** |
||||
|
* Compute max(a, b) |
||||
|
*/ |
||||
|
#define gmp_max(a, b) mtbdd_apply(a, b, TASK(gmp_op_max)) |
||||
|
|
||||
|
/** |
||||
|
* Compute -a |
||||
|
*/ |
||||
|
#define gmp_neg(a) mtbdd_uapply(a, TASK(gmp_op_neg), 0); |
||||
|
|
||||
|
/** |
||||
|
* Compute abs(a) |
||||
|
*/ |
||||
|
#define gmp_abs(a) mtbdd_uapply(a, TASK(gmp_op_abs), 0); |
||||
|
|
||||
|
/** |
||||
|
* Abstract the variables in <v> from <a> by taking the sum of all values |
||||
|
*/ |
||||
|
#define gmp_abstract_plus(dd, v) mtbdd_abstract(dd, v, TASK(gmp_abstract_op_plus)) |
||||
|
|
||||
|
/** |
||||
|
* Abstract the variables in <v> from <a> by taking the product of all values |
||||
|
*/ |
||||
|
#define gmp_abstract_times(dd, v) mtbdd_abstract(dd, v, TASK(gmp_abstract_op_times)) |
||||
|
|
||||
|
/** |
||||
|
* Abstract the variables in <v> from <a> by taking the minimum of all values |
||||
|
*/ |
||||
|
#define gmp_abstract_min(dd, v) mtbdd_abstract(dd, v, TASK(gmp_abstract_op_min)) |
||||
|
|
||||
|
/** |
||||
|
* Abstract the variables in <v> from <a> by taking the maximum of all values |
||||
|
*/ |
||||
|
#define gmp_abstract_max(dd, v) mtbdd_abstract(dd, v, TASK(gmp_abstract_op_max)) |
||||
|
|
||||
|
/** |
||||
|
* Multiply <a> and <b>, and abstract variables <vars> using summation. |
||||
|
* This is similar to the "and_exists" operation in BDDs. |
||||
|
*/ |
||||
|
TASK_DECL_3(MTBDD, gmp_and_exists, MTBDD, MTBDD, MTBDD); |
||||
|
#define gmp_and_exists(a, b, vars) CALL(gmp_and_exists, a, b, vars) |
||||
|
|
||||
|
/** |
||||
|
* Convert to a Boolean MTBDD, translate terminals >= value to 1 and to 0 otherwise; |
||||
|
* Parameter <dd> is the MTBDD to convert; parameter <value> is an GMP mpq leaf |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_op_threshold, MTBDD*, MTBDD*); |
||||
|
#define gmp_threshold(dd, value) mtbdd_apply(dd, value, TASK(gmp_op_threshold)); |
||||
|
|
||||
|
/** |
||||
|
* Convert to a Boolean MTBDD, translate terminals > value to 1 and to 0 otherwise; |
||||
|
* Parameter <dd> is the MTBDD to convert; parameter <value> is an GMP mpq leaf |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_op_strict_threshold, MTBDD*, MTBDD*); |
||||
|
#define gmp_strict_threshold(dd, value) mtbdd_apply(dd, value, TASK(gmp_op_strict_threshold)); |
||||
|
|
||||
|
/** |
||||
|
* Convert to a Boolean MTBDD, translate terminals >= value to 1 and to 0 otherwise; |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_threshold_d, MTBDD, double); |
||||
|
#define gmp_threshold_d(dd, value) CALL(gmp_threshold_d, dd, value) |
||||
|
|
||||
|
/** |
||||
|
* Convert to a Boolean MTBDD, translate terminals > value to 1 and to 0 otherwise; |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, gmp_strict_threshold_d, MTBDD, double); |
||||
|
#define gmp_strict_threshold_d(dd, value) CALL(gmp_strict_threshold_d, dd, value) |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#endif |
2560
resources/3rdparty/sylvan/src/sylvan_ldd.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,288 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2014 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
/* Do not include this file directly. Instead, include sylvan.h */ |
||||
|
|
||||
|
#ifndef SYLVAN_LDD_H |
||||
|
#define SYLVAN_LDD_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
|
||||
|
typedef uint64_t MDD; // Note: low 40 bits only |
||||
|
|
||||
|
#define lddmc_false ((MDD)0) |
||||
|
#define lddmc_true ((MDD)1) |
||||
|
|
||||
|
/* Initialize LDD functionality */ |
||||
|
void sylvan_init_ldd(); |
||||
|
|
||||
|
/* Primitives */ |
||||
|
MDD lddmc_makenode(uint32_t value, MDD ifeq, MDD ifneq); |
||||
|
MDD lddmc_extendnode(MDD mdd, uint32_t value, MDD ifeq); |
||||
|
uint32_t lddmc_getvalue(MDD mdd); |
||||
|
MDD lddmc_getdown(MDD mdd); |
||||
|
MDD lddmc_getright(MDD mdd); |
||||
|
MDD lddmc_follow(MDD mdd, uint32_t value); |
||||
|
|
||||
|
/** |
||||
|
* Copy nodes in relations. |
||||
|
* A copy node represents 'read x, then write x' for every x. |
||||
|
* In a read-write relation, use copy nodes twice, once on read level, once on write level. |
||||
|
* Copy nodes are only supported by relprod, relprev and union. |
||||
|
*/ |
||||
|
|
||||
|
/* Primitive for special 'copy node' (for relprod/relprev) */ |
||||
|
MDD lddmc_make_copynode(MDD ifeq, MDD ifneq); |
||||
|
int lddmc_iscopy(MDD mdd); |
||||
|
MDD lddmc_followcopy(MDD mdd); |
||||
|
|
||||
|
/* Add or remove external reference to MDD */ |
||||
|
MDD lddmc_ref(MDD a); |
||||
|
void lddmc_deref(MDD a); |
||||
|
|
||||
|
/* For use in custom mark functions */ |
||||
|
VOID_TASK_DECL_1(lddmc_gc_mark_rec, MDD) |
||||
|
#define lddmc_gc_mark_rec(mdd) CALL(lddmc_gc_mark_rec, mdd) |
||||
|
|
||||
|
/* Return the number of external references */ |
||||
|
size_t lddmc_count_refs(); |
||||
|
|
||||
|
/* Mark MDD for "notify on dead" */ |
||||
|
#define lddmc_notify_ondead(mdd) llmsset_notify_ondead(nodes, mdd) |
||||
|
|
||||
|
/* Sanity check - returns depth of MDD including 'true' terminal or 0 for empty set */ |
||||
|
#ifndef NDEBUG |
||||
|
size_t lddmc_test_ismdd(MDD mdd); |
||||
|
#endif |
||||
|
|
||||
|
/* Operations for model checking */ |
||||
|
TASK_DECL_2(MDD, lddmc_union, MDD, MDD); |
||||
|
#define lddmc_union(a, b) CALL(lddmc_union, a, b) |
||||
|
|
||||
|
TASK_DECL_2(MDD, lddmc_minus, MDD, MDD); |
||||
|
#define lddmc_minus(a, b) CALL(lddmc_minus, a, b) |
||||
|
|
||||
|
TASK_DECL_3(MDD, lddmc_zip, MDD, MDD, MDD*); |
||||
|
#define lddmc_zip(a, b, res) CALL(lddmc_zip, a, b, res) |
||||
|
|
||||
|
TASK_DECL_2(MDD, lddmc_intersect, MDD, MDD); |
||||
|
#define lddmc_intersect(a, b) CALL(lddmc_intersect, a, b) |
||||
|
|
||||
|
TASK_DECL_3(MDD, lddmc_match, MDD, MDD, MDD); |
||||
|
#define lddmc_match(a, b, proj) CALL(lddmc_match, a, b, proj) |
||||
|
|
||||
|
MDD lddmc_union_cube(MDD a, uint32_t* values, size_t count); |
||||
|
int lddmc_member_cube(MDD a, uint32_t* values, size_t count); |
||||
|
MDD lddmc_cube(uint32_t* values, size_t count); |
||||
|
|
||||
|
MDD lddmc_union_cube_copy(MDD a, uint32_t* values, int* copy, size_t count); |
||||
|
int lddmc_member_cube_copy(MDD a, uint32_t* values, int* copy, size_t count); |
||||
|
MDD lddmc_cube_copy(uint32_t* values, int* copy, size_t count); |
||||
|
|
||||
|
TASK_DECL_3(MDD, lddmc_relprod, MDD, MDD, MDD); |
||||
|
#define lddmc_relprod(a, b, proj) CALL(lddmc_relprod, a, b, proj) |
||||
|
|
||||
|
TASK_DECL_4(MDD, lddmc_relprod_union, MDD, MDD, MDD, MDD); |
||||
|
#define lddmc_relprod_union(a, b, meta, un) CALL(lddmc_relprod_union, a, b, meta, un) |
||||
|
|
||||
|
/** |
||||
|
* Calculate all predecessors to a in uni according to rel[proj] |
||||
|
* <proj> follows the same semantics as relprod |
||||
|
* i.e. 0 (not in rel), 1 (read+write), 2 (read), 3 (write), -1 (end; rest=0) |
||||
|
*/ |
||||
|
TASK_DECL_4(MDD, lddmc_relprev, MDD, MDD, MDD, MDD); |
||||
|
#define lddmc_relprev(a, rel, proj, uni) CALL(lddmc_relprev, a, rel, proj, uni) |
||||
|
|
||||
|
// so: proj: -2 (end; quantify rest), -1 (end; keep rest), 0 (quantify), 1 (keep) |
||||
|
TASK_DECL_2(MDD, lddmc_project, MDD, MDD); |
||||
|
#define lddmc_project(mdd, proj) CALL(lddmc_project, mdd, proj) |
||||
|
|
||||
|
TASK_DECL_3(MDD, lddmc_project_minus, MDD, MDD, MDD); |
||||
|
#define lddmc_project_minus(mdd, proj, avoid) CALL(lddmc_project_minus, mdd, proj, avoid) |
||||
|
|
||||
|
TASK_DECL_4(MDD, lddmc_join, MDD, MDD, MDD, MDD); |
||||
|
#define lddmc_join(a, b, a_proj, b_proj) CALL(lddmc_join, a, b, a_proj, b_proj) |
||||
|
|
||||
|
/* Write a DOT representation */ |
||||
|
void lddmc_printdot(MDD mdd); |
||||
|
void lddmc_fprintdot(FILE *out, MDD mdd); |
||||
|
|
||||
|
void lddmc_fprint(FILE *out, MDD mdd); |
||||
|
void lddmc_print(MDD mdd); |
||||
|
|
||||
|
void lddmc_printsha(MDD mdd); |
||||
|
void lddmc_fprintsha(FILE *out, MDD mdd); |
||||
|
void lddmc_getsha(MDD mdd, char *target); // at least 65 bytes... |
||||
|
|
||||
|
/** |
||||
|
* Calculate number of satisfying variable assignments. |
||||
|
* The set of variables must be >= the support of the MDD. |
||||
|
* (i.e. all variables in the MDD must be in variables) |
||||
|
* |
||||
|
* The cached version uses the operation cache, but is limited to 64-bit floating point numbers. |
||||
|
*/ |
||||
|
|
||||
|
typedef double lddmc_satcount_double_t; |
||||
|
// if this line below gives an error, modify the above typedef until fixed ;) |
||||
|
typedef char __lddmc_check_float_is_8_bytes[(sizeof(lddmc_satcount_double_t) == sizeof(uint64_t))?1:-1]; |
||||
|
|
||||
|
TASK_DECL_1(lddmc_satcount_double_t, lddmc_satcount_cached, MDD); |
||||
|
#define lddmc_satcount_cached(mdd) CALL(lddmc_satcount_cached, mdd) |
||||
|
|
||||
|
TASK_DECL_1(long double, lddmc_satcount, MDD); |
||||
|
#define lddmc_satcount(mdd) CALL(lddmc_satcount, mdd) |
||||
|
|
||||
|
/** |
||||
|
* A callback for enumerating functions like sat_all_par, collect and match |
||||
|
* Example: |
||||
|
* TASK_3(void*, my_function, uint32_t*, values, size_t, count, void*, context) ... |
||||
|
* For collect, use: |
||||
|
* TASK_3(MDD, ...) |
||||
|
*/ |
||||
|
LACE_TYPEDEF_CB(void, lddmc_enum_cb, uint32_t*, size_t, void*); |
||||
|
LACE_TYPEDEF_CB(MDD, lddmc_collect_cb, uint32_t*, size_t, void*); |
||||
|
|
||||
|
VOID_TASK_DECL_5(lddmc_sat_all_par, MDD, lddmc_enum_cb, void*, uint32_t*, size_t); |
||||
|
#define lddmc_sat_all_par(mdd, cb, context) CALL(lddmc_sat_all_par, mdd, cb, context, 0, 0) |
||||
|
|
||||
|
VOID_TASK_DECL_3(lddmc_sat_all_nopar, MDD, lddmc_enum_cb, void*); |
||||
|
#define lddmc_sat_all_nopar(mdd, cb, context) CALL(lddmc_sat_all_nopar, mdd, cb, context) |
||||
|
|
||||
|
TASK_DECL_5(MDD, lddmc_collect, MDD, lddmc_collect_cb, void*, uint32_t*, size_t); |
||||
|
#define lddmc_collect(mdd, cb, context) CALL(lddmc_collect, mdd, cb, context, 0, 0) |
||||
|
|
||||
|
VOID_TASK_DECL_5(lddmc_match_sat_par, MDD, MDD, MDD, lddmc_enum_cb, void*); |
||||
|
#define lddmc_match_sat_par(mdd, match, proj, cb, context) CALL(lddmc_match_sat_par, mdd, match, proj, cb, context) |
||||
|
|
||||
|
int lddmc_sat_one(MDD mdd, uint32_t *values, size_t count); |
||||
|
MDD lddmc_sat_one_mdd(MDD mdd); |
||||
|
#define lddmc_pick_cube lddmc_sat_one_mdd |
||||
|
|
||||
|
/** |
||||
|
* Callback functions for visiting nodes. |
||||
|
* lddmc_visit_seq sequentially visits nodes, down first, then right. |
||||
|
* lddmc_visit_par visits nodes in parallel (down || right) |
||||
|
*/ |
||||
|
LACE_TYPEDEF_CB(int, lddmc_visit_pre_cb, MDD, void*); // int pre(MDD, context) |
||||
|
LACE_TYPEDEF_CB(void, lddmc_visit_post_cb, MDD, void*); // void post(MDD, context) |
||||
|
LACE_TYPEDEF_CB(void, lddmc_visit_init_context_cb, void*, void*, int); // void init_context(context, parent, is_down) |
||||
|
|
||||
|
typedef struct lddmc_visit_node_callbacks { |
||||
|
lddmc_visit_pre_cb lddmc_visit_pre; |
||||
|
lddmc_visit_post_cb lddmc_visit_post; |
||||
|
lddmc_visit_init_context_cb lddmc_visit_init_context; |
||||
|
} lddmc_visit_callbacks_t; |
||||
|
|
||||
|
VOID_TASK_DECL_4(lddmc_visit_par, MDD, lddmc_visit_callbacks_t*, size_t, void*); |
||||
|
#define lddmc_visit_par(mdd, cbs, ctx_size, context) CALL(lddmc_visit_par, mdd, cbs, ctx_size, context); |
||||
|
|
||||
|
VOID_TASK_DECL_4(lddmc_visit_seq, MDD, lddmc_visit_callbacks_t*, size_t, void*); |
||||
|
#define lddmc_visit_seq(mdd, cbs, ctx_size, context) CALL(lddmc_visit_seq, mdd, cbs, ctx_size, context); |
||||
|
|
||||
|
size_t lddmc_nodecount(MDD mdd); |
||||
|
void lddmc_nodecount_levels(MDD mdd, size_t *variables); |
||||
|
|
||||
|
/** |
||||
|
* Functional composition |
||||
|
* For every node at depth <depth>, call function cb (MDD -> MDD). |
||||
|
* and replace the node by the result of the function |
||||
|
*/ |
||||
|
LACE_TYPEDEF_CB(MDD, lddmc_compose_cb, MDD, void*); |
||||
|
TASK_DECL_4(MDD, lddmc_compose, MDD, lddmc_compose_cb, void*, int); |
||||
|
#define lddmc_compose(mdd, cb, context, depth) CALL(lddmc_compose, mdd, cb, context, depth) |
||||
|
|
||||
|
/** |
||||
|
* SAVING: |
||||
|
* use lddmc_serialize_add on every MDD you want to store |
||||
|
* use lddmc_serialize_get to retrieve the key of every stored MDD |
||||
|
* use lddmc_serialize_tofile |
||||
|
* |
||||
|
* LOADING: |
||||
|
* use lddmc_serialize_fromfile (implies lddmc_serialize_reset) |
||||
|
* use lddmc_serialize_get_reversed for every key |
||||
|
* |
||||
|
* MISC: |
||||
|
* use lddmc_serialize_reset to free all allocated structures |
||||
|
* use lddmc_serialize_totext to write a textual list of tuples of all MDDs. |
||||
|
* format: [(<key>,<level>,<key_low>,<key_high>,<complement_high>),...] |
||||
|
* |
||||
|
* for the old lddmc_print functions, use lddmc_serialize_totext |
||||
|
*/ |
||||
|
size_t lddmc_serialize_add(MDD mdd); |
||||
|
size_t lddmc_serialize_get(MDD mdd); |
||||
|
MDD lddmc_serialize_get_reversed(size_t value); |
||||
|
void lddmc_serialize_reset(); |
||||
|
void lddmc_serialize_totext(FILE *out); |
||||
|
void lddmc_serialize_tofile(FILE *out); |
||||
|
void lddmc_serialize_fromfile(FILE *in); |
||||
|
|
||||
|
/* Infrastructure for internal markings */ |
||||
|
typedef struct lddmc_refs_internal |
||||
|
{ |
||||
|
size_t r_size, r_count; |
||||
|
size_t s_size, s_count; |
||||
|
MDD *results; |
||||
|
Task **spawns; |
||||
|
} *lddmc_refs_internal_t; |
||||
|
|
||||
|
extern DECLARE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); |
||||
|
|
||||
|
static inline MDD |
||||
|
lddmc_refs_push(MDD ldd) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); |
||||
|
if (lddmc_refs_key->r_count >= lddmc_refs_key->r_size) { |
||||
|
lddmc_refs_key->r_size *= 2; |
||||
|
lddmc_refs_key->results = (MDD*)realloc(lddmc_refs_key->results, sizeof(MDD) * lddmc_refs_key->r_size); |
||||
|
} |
||||
|
lddmc_refs_key->results[lddmc_refs_key->r_count++] = ldd; |
||||
|
return ldd; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
lddmc_refs_pop(int amount) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); |
||||
|
lddmc_refs_key->r_count-=amount; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
lddmc_refs_spawn(Task *t) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); |
||||
|
if (lddmc_refs_key->s_count >= lddmc_refs_key->s_size) { |
||||
|
lddmc_refs_key->s_size *= 2; |
||||
|
lddmc_refs_key->spawns = (Task**)realloc(lddmc_refs_key->spawns, sizeof(Task*) * lddmc_refs_key->s_size); |
||||
|
} |
||||
|
lddmc_refs_key->spawns[lddmc_refs_key->s_count++] = t; |
||||
|
} |
||||
|
|
||||
|
static inline MDD |
||||
|
lddmc_refs_sync(MDD result) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); |
||||
|
lddmc_refs_key->s_count--; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#endif |
2542
resources/3rdparty/sylvan/src/sylvan_mtbdd.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,608 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* This is an implementation of Multi-Terminal Binary Decision Diagrams. |
||||
|
* They encode functions on Boolean variables to any domain. |
||||
|
* |
||||
|
* Three domains are supported by default: Boolean, Integer and Real. |
||||
|
* Boolean MTBDDs are identical to BDDs (as supported by the bdd subpackage). |
||||
|
* Integer MTBDDs are encoded using "int64_t" terminals. |
||||
|
* Real MTBDDs are encoded using "double" terminals. |
||||
|
* |
||||
|
* Labels of Boolean variables of MTBDD nodes are 24-bit integers. |
||||
|
* |
||||
|
* Custom terminals are supported. |
||||
|
* |
||||
|
* Terminal type "0" is the Integer type, type "1" is the Real type. |
||||
|
* Type "2" is the Fraction type, consisting of two 32-bit integers (numerator and denominator) |
||||
|
* For non-Boolean MTBDDs, mtbdd_false is used for partial functions, i.e. mtbdd_false |
||||
|
* indicates that the function is not defined for a certain input. |
||||
|
*/ |
||||
|
|
||||
|
/* Do not include this file directly. Instead, include sylvan.h */ |
||||
|
|
||||
|
#ifndef SYLVAN_MTBDD_H |
||||
|
#define SYLVAN_MTBDD_H |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
/** |
||||
|
* An MTBDD is a 64-bit value. The low 40 bits are an index into the unique table. |
||||
|
* The highest 1 bit is the complement edge, indicating negation. |
||||
|
* For Boolean MTBDDs, this means "not X", for Integer and Real MTBDDs, this means "-X". |
||||
|
*/ |
||||
|
typedef uint64_t MTBDD; |
||||
|
typedef MTBDD MTBDDMAP; |
||||
|
|
||||
|
/** |
||||
|
* mtbdd_true is only used in Boolean MTBDDs. mtbdd_false has multiple roles (see above). |
||||
|
*/ |
||||
|
#define mtbdd_complement ((MTBDD)0x8000000000000000LL) |
||||
|
#define mtbdd_false ((MTBDD)0) |
||||
|
#define mtbdd_true (mtbdd_false|mtbdd_complement) |
||||
|
#define mtbdd_invalid ((MTBDD)0xffffffffffffffffLL) |
||||
|
|
||||
|
/** |
||||
|
* Initialize MTBDD functionality. |
||||
|
* This initializes internal and external referencing datastructures, |
||||
|
* and registers them in the garbage collection framework. |
||||
|
*/ |
||||
|
void sylvan_init_mtbdd(); |
||||
|
|
||||
|
/** |
||||
|
* Create a MTBDD terminal of type <type> and value <value>. |
||||
|
* For custom types, the value could be a pointer to some external struct. |
||||
|
*/ |
||||
|
MTBDD mtbdd_makeleaf(uint32_t type, uint64_t value); |
||||
|
|
||||
|
/** |
||||
|
* Create an internal MTBDD node of Boolean variable <var>, with low edge <low> and high edge <high>. |
||||
|
* <var> is a 24-bit integer. |
||||
|
*/ |
||||
|
MTBDD mtbdd_makenode(uint32_t var, MTBDD low, MTBDD high); |
||||
|
|
||||
|
/** |
||||
|
* Returns 1 is the MTBDD is a terminal, or 0 otherwise. |
||||
|
*/ |
||||
|
int mtbdd_isleaf(MTBDD mtbdd); |
||||
|
#define mtbdd_isnode(mtbdd) (mtbdd_isleaf(mtbdd) ? 0 : 1) |
||||
|
|
||||
|
/** |
||||
|
* For MTBDD terminals, returns <type> and <value> |
||||
|
*/ |
||||
|
uint32_t mtbdd_gettype(MTBDD terminal); |
||||
|
uint64_t mtbdd_getvalue(MTBDD terminal); |
||||
|
|
||||
|
/** |
||||
|
* For internal MTBDD nodes, returns <var>, <low> and <high> |
||||
|
*/ |
||||
|
uint32_t mtbdd_getvar(MTBDD node); |
||||
|
MTBDD mtbdd_getlow(MTBDD node); |
||||
|
MTBDD mtbdd_gethigh(MTBDD node); |
||||
|
|
||||
|
/** |
||||
|
* Compute the complement of the MTBDD. |
||||
|
* For Boolean MTBDDs, this means "not X". |
||||
|
*/ |
||||
|
#define mtbdd_hascomp(dd) ((dd & mtbdd_complement) ? 1 : 0) |
||||
|
#define mtbdd_comp(dd) (dd ^ mtbdd_complement) |
||||
|
#define mtbdd_not(dd) (dd ^ mtbdd_complement) |
||||
|
|
||||
|
/** |
||||
|
* Create terminals representing int64_t (type 0), double (type 1), or fraction (type 2) values |
||||
|
*/ |
||||
|
MTBDD mtbdd_int64(int64_t value); |
||||
|
MTBDD mtbdd_double(double value); |
||||
|
MTBDD mtbdd_fraction(int64_t numer, uint64_t denom); |
||||
|
|
||||
|
/** |
||||
|
* Get the value of a terminal (for Integer, Real and Fraction terminals, types 0, 1 and 2) |
||||
|
*/ |
||||
|
int64_t mtbdd_getint64(MTBDD terminal); |
||||
|
double mtbdd_getdouble(MTBDD terminal); |
||||
|
#define mtbdd_getnumer(terminal) ((int32_t)(mtbdd_getvalue(terminal)>>32)) |
||||
|
#define mtbdd_getdenom(terminal) ((uint32_t)(mtbdd_getvalue(terminal)&0xffffffff)) |
||||
|
|
||||
|
/** |
||||
|
* Create the conjunction of variables in arr. |
||||
|
* I.e. arr[0] \and arr[1] \and ... \and arr[length-1] |
||||
|
*/ |
||||
|
MTBDD mtbdd_fromarray(uint32_t* arr, size_t length); |
||||
|
|
||||
|
/** |
||||
|
* Create a MTBDD cube representing the conjunction of variables in their positive or negative |
||||
|
* form depending on whether the cube[idx] equals 0 (negative), 1 (positive) or 2 (any). |
||||
|
* Use cube[idx]==3 for "s=s'" in interleaved variables (matches with next variable) |
||||
|
* <variables> is the cube of variables (var1 \and var2 \and ... \and varn) |
||||
|
*/ |
||||
|
MTBDD mtbdd_cube(MTBDD variables, uint8_t *cube, MTBDD terminal); |
||||
|
|
||||
|
/** |
||||
|
* Same as mtbdd_cube, but extends <mtbdd> with the assignment <cube> \to <terminal>. |
||||
|
* If <mtbdd> already assigns a value to the cube, the new value <terminal> is taken. |
||||
|
* Does not support cube[idx]==3. |
||||
|
*/ |
||||
|
#define mtbdd_union_cube(mtbdd, variables, cube, terminal) CALL(mtbdd_union_cube, mtbdd, variables, cube, terminal) |
||||
|
TASK_DECL_4(BDD, mtbdd_union_cube, MTBDD, MTBDD, uint8_t*, MTBDD); |
||||
|
|
||||
|
/** |
||||
|
* Count the number of satisfying assignments (minterms) leading to a non-false leaf |
||||
|
*/ |
||||
|
TASK_DECL_2(double, mtbdd_satcount, MTBDD, size_t); |
||||
|
#define mtbdd_satcount(dd, nvars) CALL(mtbdd_satcount, dd, nvars) |
||||
|
|
||||
|
/** |
||||
|
* Count the number of MTBDD leaves (excluding mtbdd_false and mtbdd_true) in the MTBDD |
||||
|
*/ |
||||
|
size_t mtbdd_leafcount(MTBDD mtbdd); |
||||
|
|
||||
|
/** |
||||
|
* Count the number of MTBDD nodes and terminals (excluding mtbdd_false and mtbdd_true) in a MTBDD |
||||
|
*/ |
||||
|
size_t mtbdd_nodecount(MTBDD mtbdd); |
||||
|
|
||||
|
/** |
||||
|
* Callback function types for binary ("dyadic") and unary ("monadic") operations. |
||||
|
* The callback function returns either the MTBDD that is the result of applying op to the MTBDDs, |
||||
|
* or mtbdd_invalid if op cannot be applied. |
||||
|
* The binary function may swap the two parameters (if commutative) to improve caching. |
||||
|
* The unary function is allowed an extra parameter (be careful of caching) |
||||
|
*/ |
||||
|
LACE_TYPEDEF_CB(MTBDD, mtbdd_apply_op, MTBDD*, MTBDD*); |
||||
|
LACE_TYPEDEF_CB(MTBDD, mtbdd_applyp_op, MTBDD*, MTBDD*, size_t); |
||||
|
LACE_TYPEDEF_CB(MTBDD, mtbdd_uapply_op, MTBDD, size_t); |
||||
|
|
||||
|
/** |
||||
|
* Apply a binary operation <op> to <a> and <b>. |
||||
|
* Callback <op> is consulted before the cache, thus the application to terminals is not cached. |
||||
|
*/ |
||||
|
TASK_DECL_3(MTBDD, mtbdd_apply, MTBDD, MTBDD, mtbdd_apply_op); |
||||
|
#define mtbdd_apply(a, b, op) CALL(mtbdd_apply, a, b, op) |
||||
|
|
||||
|
/** |
||||
|
* Apply a binary operation <op> with id <opid> to <a> and <b> with parameter <p> |
||||
|
* Callback <op> is consulted before the cache, thus the application to terminals is not cached. |
||||
|
*/ |
||||
|
TASK_DECL_5(MTBDD, mtbdd_applyp, MTBDD, MTBDD, size_t, mtbdd_applyp_op, uint64_t); |
||||
|
#define mtbdd_applyp(a, b, p, op, opid) CALL(mtbdd_applyp, a, b, p, op, opid) |
||||
|
|
||||
|
/** |
||||
|
* Apply a unary operation <op> to <dd>. |
||||
|
* Callback <op> is consulted after the cache, thus the application to a terminal is cached. |
||||
|
*/ |
||||
|
TASK_DECL_3(MTBDD, mtbdd_uapply, MTBDD, mtbdd_uapply_op, size_t); |
||||
|
#define mtbdd_uapply(dd, op, param) CALL(mtbdd_uapply, dd, op, param) |
||||
|
|
||||
|
/** |
||||
|
* Callback function types for abstraction. |
||||
|
* MTBDD mtbdd_abstract_op(MTBDD a, MTBDD b, int k). |
||||
|
* The function is either called with k==0 (apply to two arguments) or k>0 (k skipped BDD variables) |
||||
|
* k == 0 => res := apply op to a and b |
||||
|
* k > 0 => res := apply op to op(a, a, k-1) and op(a, a, k-1) |
||||
|
*/ |
||||
|
LACE_TYPEDEF_CB(MTBDD, mtbdd_abstract_op, MTBDD, MTBDD, int); |
||||
|
|
||||
|
/** |
||||
|
* Abstract the variables in <v> from <a> using the binary operation <op>. |
||||
|
*/ |
||||
|
TASK_DECL_3(MTBDD, mtbdd_abstract, MTBDD, MTBDD, mtbdd_abstract_op); |
||||
|
#define mtbdd_abstract(a, v, op) CALL(mtbdd_abstract, a, v, op) |
||||
|
|
||||
|
/** |
||||
|
* Unary operation Negate. |
||||
|
* Supported domains: Integer, Real, Fraction |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_negate, MTBDD, size_t); |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Plus (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDDs, mtbdd_false is interpreted as "0" or "0.0". |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_plus, MTBDD*, MTBDD*); |
||||
|
TASK_DECL_3(MTBDD, mtbdd_abstract_op_plus, MTBDD, MTBDD, int); |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Minus (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDDs, mtbdd_false is interpreted as "0" or "0.0". |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_minus, MTBDD*, MTBDD*); |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Times (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is mtbdd_false (i.e. not defined). |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_times, MTBDD*, MTBDD*); |
||||
|
TASK_DECL_3(MTBDD, mtbdd_abstract_op_times, MTBDD, MTBDD, int); |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Minimum (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is the other operand. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_min, MTBDD*, MTBDD*); |
||||
|
TASK_DECL_3(MTBDD, mtbdd_abstract_op_min, MTBDD, MTBDD, int); |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Maximum (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is the other operand. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_max, MTBDD*, MTBDD*); |
||||
|
TASK_DECL_3(MTBDD, mtbdd_abstract_op_max, MTBDD, MTBDD, int); |
||||
|
|
||||
|
/** |
||||
|
* Compute -a |
||||
|
*/ |
||||
|
#define mtbdd_negate(a) mtbdd_uapply(a, TASK(mtbdd_op_negate), 0) |
||||
|
|
||||
|
/** |
||||
|
* Compute a + b |
||||
|
*/ |
||||
|
#define mtbdd_plus(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_plus)) |
||||
|
|
||||
|
/** |
||||
|
* Compute a - b |
||||
|
*/ |
||||
|
#define mtbdd_minus(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_minus)) |
||||
|
|
||||
|
/** |
||||
|
* Compute a * b |
||||
|
*/ |
||||
|
#define mtbdd_times(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_times)) |
||||
|
|
||||
|
/** |
||||
|
* Compute min(a, b) |
||||
|
*/ |
||||
|
#define mtbdd_min(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_min)) |
||||
|
|
||||
|
/** |
||||
|
* Compute max(a, b) |
||||
|
*/ |
||||
|
#define mtbdd_max(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_max)) |
||||
|
|
||||
|
/** |
||||
|
* Abstract the variables in <v> from <a> by taking the sum of all values |
||||
|
*/ |
||||
|
#define mtbdd_abstract_plus(dd, v) mtbdd_abstract(dd, v, TASK(mtbdd_abstract_op_plus)) |
||||
|
|
||||
|
/** |
||||
|
* Abstract the variables in <v> from <a> by taking the product of all values |
||||
|
*/ |
||||
|
#define mtbdd_abstract_times(dd, v) mtbdd_abstract(dd, v, TASK(mtbdd_abstract_op_times)) |
||||
|
|
||||
|
/** |
||||
|
* Abstract the variables in <v> from <a> by taking the minimum of all values |
||||
|
*/ |
||||
|
#define mtbdd_abstract_min(dd, v) mtbdd_abstract(dd, v, TASK(mtbdd_abstract_op_min)) |
||||
|
|
||||
|
/** |
||||
|
* Abstract the variables in <v> from <a> by taking the maximum of all values |
||||
|
*/ |
||||
|
#define mtbdd_abstract_max(dd, v) mtbdd_abstract(dd, v, TASK(mtbdd_abstract_op_max)) |
||||
|
|
||||
|
/** |
||||
|
* Compute IF <f> THEN <g> ELSE <h>. |
||||
|
* <f> must be a Boolean MTBDD (or standard BDD). |
||||
|
*/ |
||||
|
TASK_DECL_3(MTBDD, mtbdd_ite, MTBDD, MTBDD, MTBDD); |
||||
|
#define mtbdd_ite(f, g, h) CALL(mtbdd_ite, f, g, h); |
||||
|
|
||||
|
/** |
||||
|
* Multiply <a> and <b>, and abstract variables <vars> using summation. |
||||
|
* This is similar to the "and_exists" operation in BDDs. |
||||
|
*/ |
||||
|
TASK_DECL_3(MTBDD, mtbdd_and_exists, MTBDD, MTBDD, MTBDD); |
||||
|
#define mtbdd_and_exists(a, b, vars) CALL(mtbdd_and_exists, a, b, vars) |
||||
|
|
||||
|
/** |
||||
|
* Monad that converts double to a Boolean MTBDD, translate terminals >= value to 1 and to 0 otherwise; |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_threshold_double, MTBDD, size_t) |
||||
|
|
||||
|
/** |
||||
|
* Monad that converts double to a Boolean MTBDD, translate terminals > value to 1 and to 0 otherwise; |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_strict_threshold_double, MTBDD, size_t) |
||||
|
|
||||
|
/** |
||||
|
* Convert double to a Boolean MTBDD, translate terminals >= value to 1 and to 0 otherwise; |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_threshold_double, MTBDD, double); |
||||
|
#define mtbdd_threshold_double(dd, value) CALL(mtbdd_threshold_double, dd, value) |
||||
|
|
||||
|
/** |
||||
|
* Convert double to a Boolean MTBDD, translate terminals > value to 1 and to 0 otherwise; |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_strict_threshold_double, MTBDD, double); |
||||
|
#define mtbdd_strict_threshold_double(dd, value) CALL(mtbdd_strict_threshold_double, dd, value) |
||||
|
|
||||
|
/** |
||||
|
* For two Double MTBDDs, calculate whether they are equal module some value epsilon |
||||
|
* i.e. abs(a-b) < e |
||||
|
*/ |
||||
|
TASK_DECL_3(MTBDD, mtbdd_equal_norm_d, MTBDD, MTBDD, double); |
||||
|
#define mtbdd_equal_norm_d(a, b, epsilon) CALL(mtbdd_equal_norm_d, a, b, epsilon) |
||||
|
|
||||
|
/** |
||||
|
* For two Double MTBDDs, calculate whether they are equal modulo some value epsilon |
||||
|
* This version computes the relative difference vs the value in a. |
||||
|
* i.e. abs((a-b)/a) < e |
||||
|
*/ |
||||
|
TASK_DECL_3(MTBDD, mtbdd_equal_norm_rel_d, MTBDD, MTBDD, double); |
||||
|
#define mtbdd_equal_norm_rel_d(a, b, epsilon) CALL(mtbdd_equal_norm_rel_d, a, b, epsilon) |
||||
|
|
||||
|
/** |
||||
|
* For two MTBDDs a, b, return mtbdd_true if all common assignments a(s) <= b(s), mtbdd_false otherwise. |
||||
|
* For domains not in a / b, assume True. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_leq, MTBDD, MTBDD); |
||||
|
#define mtbdd_leq(a, b) CALL(mtbdd_leq, a, b) |
||||
|
|
||||
|
/** |
||||
|
* For two MTBDDs a, b, return mtbdd_true if all common assignments a(s) < b(s), mtbdd_false otherwise. |
||||
|
* For domains not in a / b, assume True. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_less, MTBDD, MTBDD); |
||||
|
#define mtbdd_less(a, b) CALL(mtbdd_less, a, b) |
||||
|
|
||||
|
/** |
||||
|
* For two MTBDDs a, b, return mtbdd_true if all common assignments a(s) >= b(s), mtbdd_false otherwise. |
||||
|
* For domains not in a / b, assume True. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_geq, MTBDD, MTBDD); |
||||
|
#define mtbdd_geq(a, b) CALL(mtbdd_geq, a, b) |
||||
|
|
||||
|
/** |
||||
|
* For two MTBDDs a, b, return mtbdd_true if all common assignments a(s) > b(s), mtbdd_false otherwise. |
||||
|
* For domains not in a / b, assume True. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_greater, MTBDD, MTBDD); |
||||
|
#define mtbdd_greater(a, b) CALL(mtbdd_greater, a, b) |
||||
|
|
||||
|
/** |
||||
|
* Calculate the support of a MTBDD, i.e. the cube of all variables that appear in the MTBDD nodes. |
||||
|
*/ |
||||
|
TASK_DECL_1(MTBDD, mtbdd_support, MTBDD); |
||||
|
#define mtbdd_support(dd) CALL(mtbdd_support, dd) |
||||
|
|
||||
|
/** |
||||
|
* Function composition, for each node with variable <key> which has a <key,value> pair in <map>, |
||||
|
* replace the node by the result of mtbdd_ite(<value>, <low>, <high>). |
||||
|
* Each <value> in <map> must be a Boolean MTBDD. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_compose, MTBDD, MTBDDMAP); |
||||
|
#define mtbdd_compose(dd, map) CALL(mtbdd_compose, dd, map) |
||||
|
|
||||
|
/** |
||||
|
* Compute minimal leaf in the MTBDD (for Integer, Double, Rational MTBDDs) |
||||
|
*/ |
||||
|
TASK_DECL_1(MTBDD, mtbdd_minimum, MTBDD); |
||||
|
#define mtbdd_minimum(dd) CALL(mtbdd_minimum, dd) |
||||
|
|
||||
|
/** |
||||
|
* Compute maximal leaf in the MTBDD (for Integer, Double, Rational MTBDDs) |
||||
|
*/ |
||||
|
TASK_DECL_1(MTBDD, mtbdd_maximum, MTBDD); |
||||
|
#define mtbdd_maximum(dd) CALL(mtbdd_maximum, dd) |
||||
|
|
||||
|
/** |
||||
|
* Given a MTBDD <dd> and a cube of variables <variables> expected in <dd>, |
||||
|
* mtbdd_enum_first and mtbdd_enum_next enumerates the unique paths in <dd> that lead to a non-False leaf. |
||||
|
* |
||||
|
* The function returns the leaf (or mtbdd_false if no new path is found) and encodes the path |
||||
|
* in the supplied array <arr>: 0 for a low edge, 1 for a high edge, and 2 if the variable is skipped. |
||||
|
* |
||||
|
* The supplied array <arr> must be large enough for all variables in <variables>. |
||||
|
* |
||||
|
* Usage: |
||||
|
* MTBDD leaf = mtbdd_enum_first(dd, variables, arr, NULL); |
||||
|
* while (leaf != mtbdd_false) { |
||||
|
* .... // do something with arr/leaf |
||||
|
* leaf = mtbdd_enum_next(dd, variables, arr, NULL); |
||||
|
* } |
||||
|
* |
||||
|
* The callback is an optional function that returns 0 when the given terminal node should be skipped. |
||||
|
*/ |
||||
|
typedef int (*mtbdd_enum_filter_cb)(MTBDD); |
||||
|
MTBDD mtbdd_enum_first(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb filter_cb); |
||||
|
MTBDD mtbdd_enum_next(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb filter_cb); |
||||
|
|
||||
|
/** |
||||
|
* For debugging. |
||||
|
* Tests if all nodes in the MTBDD are correctly ``marked'' in the nodes table. |
||||
|
* Tests if variables in the internal nodes appear in-order. |
||||
|
* In Debug mode, this will cause assertion failures instead of returning 0. |
||||
|
* Returns 1 if all is fine, or 0 otherwise. |
||||
|
*/ |
||||
|
TASK_DECL_1(int, mtbdd_test_isvalid, MTBDD); |
||||
|
#define mtbdd_test_isvalid(mtbdd) CALL(mtbdd_test_isvalid, mtbdd) |
||||
|
|
||||
|
/** |
||||
|
* Write a DOT representation of a MTBDD |
||||
|
* The callback function is required for custom terminals. |
||||
|
*/ |
||||
|
typedef void (*print_terminal_label_cb)(FILE *out, uint32_t type, uint64_t value); |
||||
|
void mtbdd_fprintdot(FILE *out, MTBDD mtbdd, print_terminal_label_cb cb); |
||||
|
#define mtbdd_printdot(mtbdd, cb) mtbdd_fprintdot(stdout, mtbdd, cb) |
||||
|
|
||||
|
/** |
||||
|
* MTBDDMAP, maps uint32_t variables to MTBDDs. |
||||
|
* A MTBDDMAP node has variable level, low edge going to the next MTBDDMAP, high edge to the mapped MTBDD |
||||
|
*/ |
||||
|
#define mtbdd_map_empty() mtbdd_false |
||||
|
#define mtbdd_map_isempty(map) (map == mtbdd_false ? 1 : 0) |
||||
|
#define mtbdd_map_key(map) mtbdd_getvar(map) |
||||
|
#define mtbdd_map_value(map) mtbdd_gethigh(map) |
||||
|
#define mtbdd_map_next(map) mtbdd_getlow(map) |
||||
|
|
||||
|
/** |
||||
|
* Return 1 if the map contains the key, 0 otherwise. |
||||
|
*/ |
||||
|
int mtbdd_map_contains(MTBDDMAP map, uint32_t key); |
||||
|
|
||||
|
/** |
||||
|
* Retrieve the number of keys in the map. |
||||
|
*/ |
||||
|
size_t mtbdd_map_count(MTBDDMAP map); |
||||
|
|
||||
|
/** |
||||
|
* Add the pair <key,value> to the map, overwrites if key already in map. |
||||
|
*/ |
||||
|
MTBDDMAP mtbdd_map_add(MTBDDMAP map, uint32_t key, MTBDD value); |
||||
|
|
||||
|
/** |
||||
|
* Add all values from map2 to map1, overwrites if key already in map1. |
||||
|
*/ |
||||
|
MTBDDMAP mtbdd_map_addall(MTBDDMAP map1, MTBDDMAP map2); |
||||
|
|
||||
|
/** |
||||
|
* Remove the key <key> from the map and return the result |
||||
|
*/ |
||||
|
MTBDDMAP mtbdd_map_remove(MTBDDMAP map, uint32_t key); |
||||
|
|
||||
|
/** |
||||
|
* Remove all keys in the cube <variables> from the map and return the result |
||||
|
*/ |
||||
|
MTBDDMAP mtbdd_map_removeall(MTBDDMAP map, MTBDD variables); |
||||
|
|
||||
|
/** |
||||
|
* Custom node types |
||||
|
* Overrides standard hash/equality/notify_on_dead behavior |
||||
|
* hash(value, seed) return hash version |
||||
|
* equals(value1, value2) return 1 if equal, 0 if not equal |
||||
|
* create(&value) replace value by new value for object allocation |
||||
|
* destroy(value) |
||||
|
* NOTE: equals(value1, value2) must imply: hash(value1, seed) == hash(value2,seed) |
||||
|
* NOTE: new value of create must imply: equals(old, new) |
||||
|
*/ |
||||
|
typedef uint64_t (*mtbdd_hash_cb)(uint64_t, uint64_t); |
||||
|
typedef int (*mtbdd_equals_cb)(uint64_t, uint64_t); |
||||
|
typedef void (*mtbdd_create_cb)(uint64_t*); |
||||
|
typedef void (*mtbdd_destroy_cb)(uint64_t); |
||||
|
|
||||
|
/** |
||||
|
* Registry callback handlers for <type>. |
||||
|
*/ |
||||
|
uint32_t mtbdd_register_custom_leaf(mtbdd_hash_cb hash_cb, mtbdd_equals_cb equals_cb, mtbdd_create_cb create_cb, mtbdd_destroy_cb destroy_cb); |
||||
|
|
||||
|
/** |
||||
|
* Garbage collection |
||||
|
* Sylvan supplies two default methods to handle references to nodes, but the user |
||||
|
* is encouraged to implement custom handling. Simply add a handler using sylvan_gc_add_mark |
||||
|
* and let the handler call mtbdd_gc_mark_rec for every MTBDD that should be saved |
||||
|
* during garbage collection. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* Call mtbdd_gc_mark_rec for every mtbdd you want to keep in your custom mark functions. |
||||
|
*/ |
||||
|
VOID_TASK_DECL_1(mtbdd_gc_mark_rec, MTBDD); |
||||
|
#define mtbdd_gc_mark_rec(mtbdd) CALL(mtbdd_gc_mark_rec, mtbdd) |
||||
|
|
||||
|
/** |
||||
|
* Default external referencing. During garbage collection, MTBDDs marked with mtbdd_ref will |
||||
|
* be kept in the forest. |
||||
|
* It is recommended to prefer mtbdd_protect and mtbdd_unprotect. |
||||
|
*/ |
||||
|
MTBDD mtbdd_ref(MTBDD a); |
||||
|
void mtbdd_deref(MTBDD a); |
||||
|
size_t mtbdd_count_refs(); |
||||
|
|
||||
|
/** |
||||
|
* Default external pointer referencing. During garbage collection, the pointers are followed and the MTBDD |
||||
|
* that they refer to are kept in the forest. |
||||
|
*/ |
||||
|
void mtbdd_protect(MTBDD* ptr); |
||||
|
void mtbdd_unprotect(MTBDD* ptr); |
||||
|
size_t mtbdd_count_protected(); |
||||
|
|
||||
|
/** |
||||
|
* If sylvan_set_ondead is set to a callback, then this function marks MTBDDs (terminals). |
||||
|
* When they are dead after the mark phase in garbage collection, the callback is called for marked MTBDDs. |
||||
|
* The ondead callback can either perform cleanup or resurrect dead terminals. |
||||
|
*/ |
||||
|
#define mtbdd_notify_ondead(dd) llmsset_notify_ondead(nodes, dd&~mtbdd_complement) |
||||
|
|
||||
|
/** |
||||
|
* Infrastructure for internal references (per-thread, e.g. during MTBDD operations) |
||||
|
* Use mtbdd_refs_push and mtbdd_refs_pop to put MTBDDs on a thread-local reference stack. |
||||
|
* Use mtbdd_refs_spawn and mtbdd_refs_sync around SPAWN and SYNC operations when the result |
||||
|
* of the spawned Task is a MTBDD that must be kept during garbage collection. |
||||
|
*/ |
||||
|
typedef struct mtbdd_refs_internal |
||||
|
{ |
||||
|
size_t r_size, r_count; |
||||
|
size_t s_size, s_count; |
||||
|
MTBDD *results; |
||||
|
Task **spawns; |
||||
|
} *mtbdd_refs_internal_t; |
||||
|
|
||||
|
extern DECLARE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); |
||||
|
|
||||
|
static inline MTBDD |
||||
|
mtbdd_refs_push(MTBDD mtbdd) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); |
||||
|
if (mtbdd_refs_key->r_count >= mtbdd_refs_key->r_size) { |
||||
|
mtbdd_refs_key->r_size *= 2; |
||||
|
mtbdd_refs_key->results = (MTBDD*)realloc(mtbdd_refs_key->results, sizeof(MTBDD) * mtbdd_refs_key->r_size); |
||||
|
} |
||||
|
mtbdd_refs_key->results[mtbdd_refs_key->r_count++] = mtbdd; |
||||
|
return mtbdd; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
mtbdd_refs_pop(int amount) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); |
||||
|
mtbdd_refs_key->r_count-=amount; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
mtbdd_refs_spawn(Task *t) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); |
||||
|
if (mtbdd_refs_key->s_count >= mtbdd_refs_key->s_size) { |
||||
|
mtbdd_refs_key->s_size *= 2; |
||||
|
mtbdd_refs_key->spawns = (Task**)realloc(mtbdd_refs_key->spawns, sizeof(Task*) * mtbdd_refs_key->s_size); |
||||
|
} |
||||
|
mtbdd_refs_key->spawns[mtbdd_refs_key->s_count++] = t; |
||||
|
} |
||||
|
|
||||
|
static inline MTBDD |
||||
|
mtbdd_refs_sync(MTBDD result) |
||||
|
{ |
||||
|
LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); |
||||
|
mtbdd_refs_key->s_count--; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
#include "sylvan_mtbdd_storm.h" |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif /* __cplusplus */ |
||||
|
|
||||
|
#endif |
@ -0,0 +1,128 @@ |
|||||
|
/* |
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* Internals for MTBDDs |
||||
|
*/ |
||||
|
|
||||
|
#ifndef SYLVAN_MTBDD_INT_H |
||||
|
#define SYLVAN_MTBDD_INT_H |
||||
|
|
||||
|
/** |
||||
|
* MTBDD node structure |
||||
|
*/ |
||||
|
typedef struct __attribute__((packed)) mtbddnode { |
||||
|
uint64_t a, b; |
||||
|
} * mtbddnode_t; // 16 bytes |
||||
|
|
||||
|
#define GETNODE(mtbdd) ((mtbddnode_t)llmsset_index_to_ptr(nodes, mtbdd&0x000000ffffffffff)) |
||||
|
|
||||
|
/** |
||||
|
* Complement handling macros |
||||
|
*/ |
||||
|
#define MTBDD_HASMARK(s) (s&mtbdd_complement?1:0) |
||||
|
#define MTBDD_TOGGLEMARK(s) (s^mtbdd_complement) |
||||
|
#define MTBDD_STRIPMARK(s) (s&~mtbdd_complement) |
||||
|
#define MTBDD_TRANSFERMARK(from, to) (to ^ (from & mtbdd_complement)) |
||||
|
// Equal under mark |
||||
|
#define MTBDD_EQUALM(a, b) ((((a)^(b))&(~mtbdd_complement))==0) |
||||
|
|
||||
|
// Leaf: a = L=1, M, type; b = value |
||||
|
// Node: a = L=0, C, M, high; b = variable, low |
||||
|
// Only complement edge on "high" |
||||
|
|
||||
|
static inline int |
||||
|
mtbddnode_isleaf(mtbddnode_t n) |
||||
|
{ |
||||
|
return n->a & 0x4000000000000000 ? 1 : 0; |
||||
|
} |
||||
|
|
||||
|
static inline uint32_t |
||||
|
mtbddnode_gettype(mtbddnode_t n) |
||||
|
{ |
||||
|
return n->a & 0x00000000ffffffff; |
||||
|
} |
||||
|
|
||||
|
static inline uint64_t |
||||
|
mtbddnode_getvalue(mtbddnode_t n) |
||||
|
{ |
||||
|
return n->b; |
||||
|
} |
||||
|
|
||||
|
static inline int |
||||
|
mtbddnode_getcomp(mtbddnode_t n) |
||||
|
{ |
||||
|
return n->a & 0x8000000000000000 ? 1 : 0; |
||||
|
} |
||||
|
|
||||
|
static inline uint64_t |
||||
|
mtbddnode_getlow(mtbddnode_t n) |
||||
|
{ |
||||
|
return n->b & 0x000000ffffffffff; // 40 bits |
||||
|
} |
||||
|
|
||||
|
static inline uint64_t |
||||
|
mtbddnode_gethigh(mtbddnode_t n) |
||||
|
{ |
||||
|
return n->a & 0x800000ffffffffff; // 40 bits plus high bit of first |
||||
|
} |
||||
|
|
||||
|
static inline uint32_t |
||||
|
mtbddnode_getvariable(mtbddnode_t n) |
||||
|
{ |
||||
|
return (uint32_t)(n->b >> 40); |
||||
|
} |
||||
|
|
||||
|
static inline int |
||||
|
mtbddnode_getmark(mtbddnode_t n) |
||||
|
{ |
||||
|
return n->a & 0x2000000000000000 ? 1 : 0; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
mtbddnode_setmark(mtbddnode_t n, int mark) |
||||
|
{ |
||||
|
if (mark) n->a |= 0x2000000000000000; |
||||
|
else n->a &= 0xdfffffffffffffff; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
mtbddnode_makeleaf(mtbddnode_t n, uint32_t type, uint64_t value) |
||||
|
{ |
||||
|
n->a = 0x4000000000000000 | (uint64_t)type; |
||||
|
n->b = value; |
||||
|
} |
||||
|
|
||||
|
static inline void |
||||
|
mtbddnode_makenode(mtbddnode_t n, uint32_t var, uint64_t low, uint64_t high) |
||||
|
{ |
||||
|
n->a = high; |
||||
|
n->b = ((uint64_t)var)<<40 | low; |
||||
|
} |
||||
|
|
||||
|
static MTBDD |
||||
|
node_getlow(MTBDD mtbdd, mtbddnode_t node) |
||||
|
{ |
||||
|
return MTBDD_TRANSFERMARK(mtbdd, mtbddnode_getlow(node)); |
||||
|
} |
||||
|
|
||||
|
static MTBDD |
||||
|
node_gethigh(MTBDD mtbdd, mtbddnode_t node) |
||||
|
{ |
||||
|
return MTBDD_TRANSFERMARK(mtbdd, mtbddnode_gethigh(node)); |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,514 @@ |
|||||
|
/** |
||||
|
* Generate SHA2 structural hashes. |
||||
|
* Hashes are independent of location. |
||||
|
* Mainly useful for debugging purposes. |
||||
|
*/ |
||||
|
static void |
||||
|
mtbdd_sha2_rec(MTBDD mtbdd, SHA256_CTX *ctx) |
||||
|
{ |
||||
|
if (mtbdd == sylvan_true || mtbdd == sylvan_false) { |
||||
|
SHA256_Update(ctx, (void*)&mtbdd, sizeof(MTBDD)); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
mtbddnode_t node = GETNODE(mtbdd); |
||||
|
if (mtbddnode_isleaf(node)) { |
||||
|
uint64_t val = mtbddnode_getvalue(node); |
||||
|
SHA256_Update(ctx, (void*)&val, sizeof(uint64_t)); |
||||
|
} else if (mtbddnode_getmark(node) == 0) { |
||||
|
mtbddnode_setmark(node, 1); |
||||
|
uint32_t level = mtbddnode_getvariable(node); |
||||
|
if (MTBDD_STRIPMARK(mtbddnode_gethigh(node))) level |= 0x80000000; |
||||
|
SHA256_Update(ctx, (void*)&level, sizeof(uint32_t)); |
||||
|
mtbdd_sha2_rec(mtbddnode_gethigh(node), ctx); |
||||
|
mtbdd_sha2_rec(mtbddnode_getlow(node), ctx); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
mtbdd_getsha(MTBDD mtbdd, char *target) |
||||
|
{ |
||||
|
SHA256_CTX ctx; |
||||
|
SHA256_Init(&ctx); |
||||
|
mtbdd_sha2_rec(mtbdd, &ctx); |
||||
|
if (mtbdd != sylvan_true && mtbdd != sylvan_false) mtbdd_unmark_rec(mtbdd); |
||||
|
SHA256_End(&ctx, target); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Times (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Integer or Double. |
||||
|
* If either operand is mtbdd_false (not defined), |
||||
|
* then the result is mtbdd_false (i.e. not defined). |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_divide, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
if (a == mtbdd_false || b == mtbdd_false) return mtbdd_false; |
||||
|
|
||||
|
// Do not handle Boolean MTBDDs... |
||||
|
|
||||
|
mtbddnode_t na = GETNODE(a); |
||||
|
mtbddnode_t nb = GETNODE(b); |
||||
|
|
||||
|
if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { |
||||
|
uint64_t val_a = mtbddnode_getvalue(na); |
||||
|
uint64_t val_b = mtbddnode_getvalue(nb); |
||||
|
if (mtbddnode_gettype(na) == 0 && mtbddnode_gettype(nb) == 0) { |
||||
|
int64_t va = *(int64_t*)(&val_a); |
||||
|
int64_t vb = *(int64_t*)(&val_b); |
||||
|
|
||||
|
if (va == 0) return a; |
||||
|
else if (vb == 0) return b; |
||||
|
else { |
||||
|
MTBDD result; |
||||
|
if (va == 1) result = b; |
||||
|
else if (vb == 1) result = a; |
||||
|
else result = mtbdd_int64(va*vb); |
||||
|
return result; |
||||
|
} |
||||
|
} else if (mtbddnode_gettype(na) == 1 && mtbddnode_gettype(nb) == 1) { |
||||
|
// both double |
||||
|
double vval_a = *(double*)&val_a; |
||||
|
double vval_b = *(double*)&val_b; |
||||
|
if (vval_a == 0.0) return a; |
||||
|
else if (vval_b == 0.0) return b; |
||||
|
else { |
||||
|
MTBDD result; |
||||
|
if (vval_a == 0.0 || vval_b == 1.0) result = a; |
||||
|
result = mtbdd_double(vval_a / vval_b); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
else if (mtbddnode_gettype(na) == 2 && mtbddnode_gettype(nb) == 2) { |
||||
|
// both fraction |
||||
|
uint64_t nom_a = val_a>>32; |
||||
|
uint64_t nom_b = val_b>>32; |
||||
|
uint64_t denom_a = val_a&0xffffffff; |
||||
|
uint64_t denom_b = val_b&0xffffffff; |
||||
|
// multiply! |
||||
|
uint32_t c = gcd(denom_b, denom_a); |
||||
|
uint32_t d = gcd(nom_a, nom_b); |
||||
|
nom_a /= d; |
||||
|
denom_a /= c; |
||||
|
nom_a *= (denom_b/c); |
||||
|
denom_a *= (nom_b/d); |
||||
|
// compute result |
||||
|
MTBDD result = mtbdd_fraction(nom_a, denom_a); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Equals (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is mtbdd_false (i.e. not defined). |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_equals, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
if (a == mtbdd_false && b == mtbdd_false) return mtbdd_true; |
||||
|
if (a == mtbdd_true && b == mtbdd_true) return mtbdd_true; |
||||
|
|
||||
|
mtbddnode_t na = GETNODE(a); |
||||
|
mtbddnode_t nb = GETNODE(b); |
||||
|
|
||||
|
if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { |
||||
|
uint64_t val_a = mtbddnode_getvalue(na); |
||||
|
uint64_t val_b = mtbddnode_getvalue(nb); |
||||
|
if (mtbddnode_gettype(na) == 0 && mtbddnode_gettype(nb) == 0) { |
||||
|
int64_t va = *(int64_t*)(&val_a); |
||||
|
int64_t vb = *(int64_t*)(&val_b); |
||||
|
if (va == vb) return mtbdd_true; |
||||
|
return mtbdd_false; |
||||
|
} else if (mtbddnode_gettype(na) == 1 && mtbddnode_gettype(nb) == 1) { |
||||
|
// both double |
||||
|
double vval_a = *(double*)&val_a; |
||||
|
double vval_b = *(double*)&val_b; |
||||
|
if (vval_a == vval_b) return mtbdd_true; |
||||
|
return mtbdd_false; |
||||
|
} else if (mtbddnode_gettype(na) == 2 && mtbddnode_gettype(nb) == 2) { |
||||
|
// both fraction |
||||
|
uint64_t nom_a = val_a>>32; |
||||
|
uint64_t nom_b = val_b>>32; |
||||
|
uint64_t denom_a = val_a&0xffffffff; |
||||
|
uint64_t denom_b = val_b&0xffffffff; |
||||
|
if (nom_a == nom_b && denom_a == denom_b) return mtbdd_true; |
||||
|
return mtbdd_false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (a < b) { |
||||
|
*pa = b; |
||||
|
*pb = a; |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Equals (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is mtbdd_false (i.e. not defined). |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_less, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
if (a == mtbdd_false && b == mtbdd_false) return mtbdd_true; |
||||
|
if (a == mtbdd_true && b == mtbdd_true) return mtbdd_true; |
||||
|
|
||||
|
mtbddnode_t na = GETNODE(a); |
||||
|
mtbddnode_t nb = GETNODE(b); |
||||
|
|
||||
|
if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { |
||||
|
uint64_t val_a = mtbddnode_getvalue(na); |
||||
|
uint64_t val_b = mtbddnode_getvalue(nb); |
||||
|
if (mtbddnode_gettype(na) == 0 && mtbddnode_gettype(nb) == 0) { |
||||
|
int64_t va = *(int64_t*)(&val_a); |
||||
|
int64_t vb = *(int64_t*)(&val_b); |
||||
|
if (va < vb) return mtbdd_true; |
||||
|
return mtbdd_false; |
||||
|
} else if (mtbddnode_gettype(na) == 1 && mtbddnode_gettype(nb) == 1) { |
||||
|
// both double |
||||
|
double vval_a = *(double*)&val_a; |
||||
|
double vval_b = *(double*)&val_b; |
||||
|
if (vval_a < vval_b) return mtbdd_true; |
||||
|
return mtbdd_false; |
||||
|
} else if (mtbddnode_gettype(na) == 2 && mtbddnode_gettype(nb) == 2) { |
||||
|
// both fraction |
||||
|
uint64_t nom_a = val_a>>32; |
||||
|
uint64_t nom_b = val_b>>32; |
||||
|
uint64_t denom_a = val_a&0xffffffff; |
||||
|
uint64_t denom_b = val_b&0xffffffff; |
||||
|
return nom_a * denom_b < nom_b * denom_a ? mtbdd_true : mtbdd_false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Equals (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is mtbdd_false (i.e. not defined). |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_less_or_equal, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
if (a == mtbdd_false && b == mtbdd_false) return mtbdd_true; |
||||
|
if (a == mtbdd_true && b == mtbdd_true) return mtbdd_true; |
||||
|
|
||||
|
mtbddnode_t na = GETNODE(a); |
||||
|
mtbddnode_t nb = GETNODE(b); |
||||
|
|
||||
|
if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { |
||||
|
uint64_t val_a = mtbddnode_getvalue(na); |
||||
|
uint64_t val_b = mtbddnode_getvalue(nb); |
||||
|
if (mtbddnode_gettype(na) == 0 && mtbddnode_gettype(nb) == 0) { |
||||
|
int64_t va = *(int64_t*)(&val_a); |
||||
|
int64_t vb = *(int64_t*)(&val_b); |
||||
|
return va <= vb ? mtbdd_true : mtbdd_false; |
||||
|
} else if (mtbddnode_gettype(na) == 1 && mtbddnode_gettype(nb) == 1) { |
||||
|
// both double |
||||
|
double vval_a = *(double*)&val_a; |
||||
|
double vval_b = *(double*)&val_b; |
||||
|
if (vval_a <= vval_b) return mtbdd_true; |
||||
|
return mtbdd_false; |
||||
|
} else if (mtbddnode_gettype(na) == 2 && mtbddnode_gettype(nb) == 2) { |
||||
|
// both fraction |
||||
|
uint64_t nom_a = val_a>>32; |
||||
|
uint64_t nom_b = val_b>>32; |
||||
|
uint64_t denom_a = val_a&0xffffffff; |
||||
|
uint64_t denom_b = val_b&0xffffffff; |
||||
|
nom_a *= denom_b; |
||||
|
nom_b *= denom_a; |
||||
|
return nom_a <= nom_b ? mtbdd_true : mtbdd_false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Pow (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is mtbdd_false (i.e. not defined). |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_pow, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
mtbddnode_t na = GETNODE(a); |
||||
|
mtbddnode_t nb = GETNODE(b); |
||||
|
|
||||
|
if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { |
||||
|
uint64_t val_a = mtbddnode_getvalue(na); |
||||
|
uint64_t val_b = mtbddnode_getvalue(nb); |
||||
|
if (mtbddnode_gettype(na) == 0 && mtbddnode_gettype(nb) == 0) { |
||||
|
assert(0); |
||||
|
} else if (mtbddnode_gettype(na) == 1 && mtbddnode_gettype(nb) == 1) { |
||||
|
// both double |
||||
|
double vval_a = *(double*)&val_a; |
||||
|
double vval_b = *(double*)&val_b; |
||||
|
return mtbdd_double(pow(vval_a, vval_b)); |
||||
|
} else if (mtbddnode_gettype(na) == 2 && mtbddnode_gettype(nb) == 2) { |
||||
|
assert(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Mod (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is mtbdd_false (i.e. not defined). |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_mod, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
mtbddnode_t na = GETNODE(a); |
||||
|
mtbddnode_t nb = GETNODE(b); |
||||
|
|
||||
|
if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { |
||||
|
uint64_t val_a = mtbddnode_getvalue(na); |
||||
|
uint64_t val_b = mtbddnode_getvalue(nb); |
||||
|
if (mtbddnode_gettype(na) == 0 && mtbddnode_gettype(nb) == 0) { |
||||
|
assert(0); |
||||
|
} else if (mtbddnode_gettype(na) == 1 && mtbddnode_gettype(nb) == 1) { |
||||
|
// both double |
||||
|
double vval_a = *(double*)&val_a; |
||||
|
double vval_b = *(double*)&val_b; |
||||
|
return mtbdd_double(fmod(vval_a, vval_b)); |
||||
|
} else if (mtbddnode_gettype(na) == 2 && mtbddnode_gettype(nb) == 2) { |
||||
|
assert(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Log (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is mtbdd_false (i.e. not defined). |
||||
|
*/ |
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_logxy, MTBDD*, pa, MTBDD*, pb) |
||||
|
{ |
||||
|
MTBDD a = *pa, b = *pb; |
||||
|
|
||||
|
mtbddnode_t na = GETNODE(a); |
||||
|
mtbddnode_t nb = GETNODE(b); |
||||
|
|
||||
|
if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { |
||||
|
uint64_t val_a = mtbddnode_getvalue(na); |
||||
|
uint64_t val_b = mtbddnode_getvalue(nb); |
||||
|
if (mtbddnode_gettype(na) == 0 && mtbddnode_gettype(nb) == 0) { |
||||
|
assert(0); |
||||
|
} else if (mtbddnode_gettype(na) == 1 && mtbddnode_gettype(nb) == 1) { |
||||
|
// both double |
||||
|
double vval_a = *(double*)&val_a; |
||||
|
double vval_b = *(double*)&val_b; |
||||
|
return mtbdd_double(log(vval_a) / log(vval_b)); |
||||
|
} else if (mtbddnode_gettype(na) == 2 && mtbddnode_gettype(nb) == 2) { |
||||
|
assert(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_not_zero, MTBDD, a, size_t, v) |
||||
|
{ |
||||
|
/* We only expect "double" terminals, or false */ |
||||
|
if (a == mtbdd_false) return mtbdd_false; |
||||
|
if (a == mtbdd_true) return mtbdd_true; |
||||
|
|
||||
|
// a != constant |
||||
|
mtbddnode_t na = GETNODE(a); |
||||
|
|
||||
|
if (mtbddnode_isleaf(na)) { |
||||
|
if (mtbddnode_gettype(na) == 0) { |
||||
|
return mtbdd_getint64(a) != 0 ? mtbdd_true : mtbdd_false; |
||||
|
} else if (mtbddnode_gettype(na) == 1) { |
||||
|
return mtbdd_getdouble(a) != 0.0 ? mtbdd_true : mtbdd_false; |
||||
|
} else if (mtbddnode_gettype(na) == 2) { |
||||
|
return mtbdd_getnumer(a) != 0 ? mtbdd_true : mtbdd_false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Ugly hack to get rid of the error "unused variable v" (because there is no version of uapply without a parameter). |
||||
|
(void)v; |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_1(MTBDD, mtbdd_not_zero, MTBDD, dd) |
||||
|
{ |
||||
|
return mtbdd_uapply(dd, TASK(mtbdd_op_not_zero), 0); |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_floor, MTBDD, a, size_t, v) |
||||
|
{ |
||||
|
/* We only expect "double" terminals, or false */ |
||||
|
if (a == mtbdd_false) return mtbdd_false; |
||||
|
if (a == mtbdd_true) return mtbdd_true; |
||||
|
|
||||
|
// a != constant |
||||
|
mtbddnode_t na = GETNODE(a); |
||||
|
|
||||
|
if (mtbddnode_isleaf(na)) { |
||||
|
if (mtbddnode_gettype(na) == 0) { |
||||
|
return a; |
||||
|
} else if (mtbddnode_gettype(na) == 1) { |
||||
|
MTBDD result = mtbdd_double(floor(mtbdd_getdouble(a))); |
||||
|
return result; |
||||
|
} else if (mtbddnode_gettype(na) == 2) { |
||||
|
MTBDD result = mtbdd_fraction(mtbdd_getnumer(a) / mtbdd_getdenom(a), 1); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Ugly hack to get rid of the error "unused variable v" (because there is no version of uapply without a parameter). |
||||
|
(void)v; |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_1(MTBDD, mtbdd_floor, MTBDD, dd) |
||||
|
{ |
||||
|
return mtbdd_uapply(dd, TASK(mtbdd_op_floor), 0); |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_ceil, MTBDD, a, size_t, v) |
||||
|
{ |
||||
|
/* We only expect "double" terminals, or false */ |
||||
|
if (a == mtbdd_false) return mtbdd_false; |
||||
|
if (a == mtbdd_true) return mtbdd_true; |
||||
|
|
||||
|
// a != constant |
||||
|
mtbddnode_t na = GETNODE(a); |
||||
|
|
||||
|
if (mtbddnode_isleaf(na)) { |
||||
|
if (mtbddnode_gettype(na) == 0) { |
||||
|
return a; |
||||
|
} else if (mtbddnode_gettype(na) == 1) { |
||||
|
MTBDD result = mtbdd_double(ceil(mtbdd_getdouble(a))); |
||||
|
return result; |
||||
|
} else if (mtbddnode_gettype(na) == 2) { |
||||
|
MTBDD result = mtbdd_fraction(mtbdd_getnumer(a) / mtbdd_getdenom(a) + 1, 1); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Ugly hack to get rid of the error "unused variable v" (because there is no version of uapply without a parameter). |
||||
|
(void)v; |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_1(MTBDD, mtbdd_ceil, MTBDD, dd) |
||||
|
{ |
||||
|
return mtbdd_uapply(dd, TASK(mtbdd_op_ceil), 0); |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_bool_to_double, MTBDD, a, size_t, v) |
||||
|
{ |
||||
|
/* We only expect "double" terminals, or false */ |
||||
|
if (a == mtbdd_false) return mtbdd_double(0); |
||||
|
if (a == mtbdd_true) return mtbdd_double(1.0); |
||||
|
|
||||
|
// Ugly hack to get rid of the error "unused variable v" (because there is no version of uapply without a parameter). |
||||
|
(void)v; |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_1(MTBDD, mtbdd_bool_to_double, MTBDD, dd) |
||||
|
{ |
||||
|
return mtbdd_uapply(dd, TASK(mtbdd_op_bool_to_double), 0); |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_2(MTBDD, mtbdd_op_bool_to_int64, MTBDD, a, size_t, v) |
||||
|
{ |
||||
|
/* We only expect "double" terminals, or false */ |
||||
|
if (a == mtbdd_false) return mtbdd_int64(0); |
||||
|
if (a == mtbdd_true) return mtbdd_int64(1); |
||||
|
|
||||
|
// Ugly hack to get rid of the error "unused variable v" (because there is no version of uapply without a parameter). |
||||
|
(void)v; |
||||
|
|
||||
|
return mtbdd_invalid; |
||||
|
} |
||||
|
|
||||
|
TASK_IMPL_1(MTBDD, mtbdd_bool_to_int64, MTBDD, dd) |
||||
|
{ |
||||
|
return mtbdd_uapply(dd, TASK(mtbdd_op_bool_to_int64), 0); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Calculate the number of satisfying variable assignments according to <variables>. |
||||
|
*/ |
||||
|
TASK_IMPL_2(double, mtbdd_non_zero_count, MTBDD, dd, size_t, nvars) |
||||
|
{ |
||||
|
/* Trivial cases */ |
||||
|
if (dd == mtbdd_false) return 0.0; |
||||
|
|
||||
|
mtbddnode_t na = GETNODE(dd); |
||||
|
|
||||
|
if (mtbdd_isleaf(dd)) { |
||||
|
if (mtbddnode_gettype(na) == 0) { |
||||
|
return mtbdd_getint64(dd) != 0 ? powl(2.0L, nvars) : 0.0; |
||||
|
} else if (mtbddnode_gettype(na) == 1) { |
||||
|
return mtbdd_getdouble(dd) != 0 ? powl(2.0L, nvars) : 0.0; |
||||
|
} else if (mtbddnode_gettype(na) == 2) { |
||||
|
return mtbdd_getnumer(dd) != 0 ? powl(2.0L, nvars) : 0.0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Perhaps execute garbage collection */ |
||||
|
sylvan_gc_test(); |
||||
|
|
||||
|
union { |
||||
|
double d; |
||||
|
uint64_t s; |
||||
|
} hack; |
||||
|
|
||||
|
/* Consult cache */ |
||||
|
if (cache_get3(CACHE_MTBDD_NONZERO_COUNT, dd, 0, nvars, &hack.s)) { |
||||
|
sylvan_stats_count(CACHE_MTBDD_NONZERO_COUNT); |
||||
|
return hack.d; |
||||
|
} |
||||
|
|
||||
|
SPAWN(mtbdd_non_zero_count, mtbdd_gethigh(dd), nvars-1); |
||||
|
double low = CALL(mtbdd_non_zero_count, mtbdd_getlow(dd), nvars-1); |
||||
|
hack.d = low + SYNC(mtbdd_non_zero_count); |
||||
|
|
||||
|
cache_put3(CACHE_MTBDD_NONZERO_COUNT, dd, 0, nvars, hack.s); |
||||
|
return hack.d; |
||||
|
} |
||||
|
|
||||
|
int mtbdd_iszero(MTBDD dd) { |
||||
|
if (mtbdd_gettype(dd) == 0) { |
||||
|
return mtbdd_getint64(dd) == 0; |
||||
|
} else if (mtbdd_gettype(dd) == 1) { |
||||
|
return mtbdd_getdouble(dd) == 0; |
||||
|
} else if (mtbdd_gettype(dd) == 2) { |
||||
|
return mtbdd_getnumer(dd) == 0; |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int mtbdd_isnonzero(MTBDD dd) { |
||||
|
return mtbdd_iszero(dd) ? 0 : 1; |
||||
|
} |
@ -0,0 +1,111 @@ |
|||||
|
void mtbdd_getsha(MTBDD mtbdd, char *target); // target must be at least 65 bytes... |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Divide (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where all leaves are Integer or Double. |
||||
|
* If either operand is mtbdd_false (not defined), |
||||
|
* then the result is mtbdd_false (i.e. not defined). |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_divide, MTBDD*, MTBDD*); |
||||
|
#define mtbdd_divide(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_divide)) |
||||
|
|
||||
|
/** |
||||
|
* Binary operation equals (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is the other operand. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_equals, MTBDD*, MTBDD*); |
||||
|
#define mtbdd_equals(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_equals)) |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Less (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is the other operand. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_less, MTBDD*, MTBDD*); |
||||
|
#define mtbdd_less_as_bdd(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_less)) |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Less (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is the other operand. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_less_or_equal, MTBDD*, MTBDD*); |
||||
|
#define mtbdd_less_or_equal_as_bdd(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_less_or_equal)) |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Pow (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Integer, Double or a Fraction. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is the other operand. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_pow, MTBDD*, MTBDD*); |
||||
|
#define mtbdd_pow(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_pow)) |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Mod (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Integer, Double or a Fraction. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is the other operand. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_mod, MTBDD*, MTBDD*); |
||||
|
#define mtbdd_mod(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_mod)) |
||||
|
|
||||
|
/** |
||||
|
* Binary operation Log (for MTBDDs of same type) |
||||
|
* Only for MTBDDs where either all leaves are Double or a Fraction. |
||||
|
* For Integer/Double MTBDD, if either operand is mtbdd_false (not defined), |
||||
|
* then the result is the other operand. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_logxy, MTBDD*, MTBDD*); |
||||
|
#define mtbdd_logxy(a, b) mtbdd_apply(a, b, TASK(mtbdd_op_logxy)) |
||||
|
|
||||
|
/** |
||||
|
* Monad that converts double to a Boolean MTBDD, translate terminals != 0 to 1 and to 0 otherwise; |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_not_zero, MTBDD, size_t) |
||||
|
TASK_DECL_1(MTBDD, mtbdd_not_zero, MTBDD) |
||||
|
#define mtbdd_not_zero(dd) CALL(mtbdd_not_zero, dd) |
||||
|
|
||||
|
/** |
||||
|
* Monad that floors all values Double and Fraction values. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_floor, MTBDD, size_t) |
||||
|
TASK_DECL_1(MTBDD, mtbdd_floor, MTBDD) |
||||
|
#define mtbdd_floor(dd) CALL(mtbdd_floor, dd) |
||||
|
|
||||
|
/** |
||||
|
* Monad that ceils all values Double and Fraction values. |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_ceil, MTBDD, size_t) |
||||
|
TASK_DECL_1(MTBDD, mtbdd_ceil, MTBDD) |
||||
|
#define mtbdd_ceil(dd) CALL(mtbdd_ceil, dd) |
||||
|
|
||||
|
/** |
||||
|
* Monad that converts Boolean to a Double MTBDD, translate terminals true to 1 and to 0 otherwise; |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_bool_to_double, MTBDD, size_t) |
||||
|
TASK_DECL_1(MTBDD, mtbdd_bool_to_double, MTBDD) |
||||
|
#define mtbdd_bool_to_double(dd) CALL(mtbdd_bool_to_double, dd) |
||||
|
|
||||
|
/** |
||||
|
* Monad that converts Boolean to a uint MTBDD, translate terminals true to 1 and to 0 otherwise; |
||||
|
*/ |
||||
|
TASK_DECL_2(MTBDD, mtbdd_op_bool_to_int64, MTBDD, size_t) |
||||
|
TASK_DECL_1(MTBDD, mtbdd_bool_to_int64, MTBDD) |
||||
|
#define mtbdd_bool_to_int64(dd) CALL(mtbdd_bool_to_int64, dd) |
||||
|
|
||||
|
/** |
||||
|
* Count the number of assignments (minterms) leading to a non-zero |
||||
|
*/ |
||||
|
TASK_DECL_2(double, mtbdd_non_zero_count, MTBDD, size_t); |
||||
|
#define mtbdd_non_zero_count(dd, nvars) CALL(mtbdd_non_zero_count, dd, nvars) |
||||
|
|
||||
|
// Checks whether the given MTBDD (does represents a zero leaf. |
||||
|
int mtbdd_iszero(MTBDD); |
||||
|
int mtbdd_isnonzero(MTBDD); |
||||
|
|
||||
|
#define mtbdd_regular(dd) (dd & ~mtbdd_complement) |
1039
resources/3rdparty/sylvan/src/sylvan_obj.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,855 @@ |
|||||
|
/*
|
||||
|
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
||||
|
* |
||||
|
* 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. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef SYLVAN_OBJ_H
|
||||
|
#define SYLVAN_OBJ_H
|
||||
|
|
||||
|
#include <string>
|
||||
|
#include <vector>
|
||||
|
|
||||
|
#include <lace.h>
|
||||
|
#include <sylvan.h>
|
||||
|
|
||||
|
namespace sylvan { |
||||
|
|
||||
|
class BddSet; |
||||
|
class BddMap; |
||||
|
class Mtbdd; |
||||
|
|
||||
|
class Bdd { |
||||
|
friend class Sylvan; |
||||
|
friend class BddSet; |
||||
|
friend class BddMap; |
||||
|
friend class Mtbdd; |
||||
|
|
||||
|
public: |
||||
|
Bdd() { bdd = sylvan_false; sylvan_protect(&bdd); } |
||||
|
Bdd(const BDD from) : bdd(from) { sylvan_protect(&bdd); } |
||||
|
Bdd(const Bdd &from) : bdd(from.bdd) { sylvan_protect(&bdd); } |
||||
|
Bdd(const uint32_t var) { bdd = sylvan_ithvar(var); sylvan_protect(&bdd); } |
||||
|
~Bdd() { sylvan_unprotect(&bdd); } |
||||
|
|
||||
|
/**
|
||||
|
* @brief Creates a Bdd representing just the variable index in its positive form |
||||
|
* The variable index must be a 0<=index<=2^23 (we use 24 bits internally) |
||||
|
*/ |
||||
|
static Bdd bddVar(uint32_t index); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the Bdd representing "True" |
||||
|
*/ |
||||
|
static Bdd bddOne(); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the Bdd representing "False" |
||||
|
*/ |
||||
|
static Bdd bddZero(); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the Bdd representing a cube of variables, according to the given values. |
||||
|
* @param variables the variables that will be in the cube in their positive or negative form |
||||
|
* @param values a character array describing how the variables will appear in the result |
||||
|
* The length of string must be equal to the number of variables in the cube. |
||||
|
* For every ith char in string, if it is 0, the corresponding variable will appear in its negative form, |
||||
|
* if it is 1, it will appear in its positive form, and if it is 2, it will appear as "any", thus it will |
||||
|
* be skipped. |
||||
|
*/ |
||||
|
static Bdd bddCube(const BddSet &variables, unsigned char *values); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the Bdd representing a cube of variables, according to the given values. |
||||
|
* @param variables the variables that will be in the cube in their positive or negative form |
||||
|
* @param string a character array describing how the variables will appear in the result |
||||
|
* The length of string must be equal to the number of variables in the cube. |
||||
|
* For every ith char in string, if it is 0, the corresponding variable will appear in its negative form, |
||||
|
* if it is 1, it will appear in its positive form, and if it is 2, it will appear as "any", thus it will |
||||
|
* be skipped. |
||||
|
*/ |
||||
|
static Bdd bddCube(const BddSet &variables, std::vector<uint8_t> values); |
||||
|
|
||||
|
int operator==(const Bdd& other) const; |
||||
|
int operator!=(const Bdd& other) const; |
||||
|
Bdd operator=(const Bdd& right); |
||||
|
int operator<=(const Bdd& other) const; |
||||
|
int operator>=(const Bdd& other) const; |
||||
|
int operator<(const Bdd& other) const; |
||||
|
int operator>(const Bdd& other) const; |
||||
|
Bdd operator!() const; |
||||
|
Bdd operator~() const; |
||||
|
Bdd operator*(const Bdd& other) const; |
||||
|
Bdd operator*=(const Bdd& other); |
||||
|
Bdd operator&(const Bdd& other) const; |
||||
|
Bdd operator&=(const Bdd& other); |
||||
|
Bdd operator+(const Bdd& other) const; |
||||
|
Bdd operator+=(const Bdd& other); |
||||
|
Bdd operator|(const Bdd& other) const; |
||||
|
Bdd operator|=(const Bdd& other); |
||||
|
Bdd operator^(const Bdd& other) const; |
||||
|
Bdd operator^=(const Bdd& other); |
||||
|
Bdd operator-(const Bdd& other) const; |
||||
|
Bdd operator-=(const Bdd& other); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns non-zero if this Bdd is bddOne() or bddZero() |
||||
|
*/ |
||||
|
int isConstant() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns non-zero if this Bdd is bddOne() or bddZero() |
||||
|
*/ |
||||
|
int isTerminal() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns non-zero if this Bdd is bddOne() |
||||
|
*/ |
||||
|
int isOne() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns non-zero if this Bdd is bddZero() |
||||
|
*/ |
||||
|
int isZero() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the top variable index of this Bdd (the variable in the root node) |
||||
|
*/ |
||||
|
uint32_t TopVar() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Follows the high edge ("then") of the root node of this Bdd |
||||
|
*/ |
||||
|
Bdd Then() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Follows the low edge ("else") of the root node of this Bdd |
||||
|
*/ |
||||
|
Bdd Else() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes \exists cube: f \and g |
||||
|
*/ |
||||
|
Bdd AndAbstract(const Bdd& g, const BddSet& cube) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes \exists cube: f |
||||
|
*/ |
||||
|
Bdd ExistAbstract(const BddSet& cube) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes \forall cube: f |
||||
|
*/ |
||||
|
Bdd UnivAbstract(const BddSet& cube) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes if f then g else h |
||||
|
*/ |
||||
|
Bdd Ite(const Bdd& g, const Bdd& h) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes f \and g |
||||
|
*/ |
||||
|
Bdd And(const Bdd& g) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes f \or g |
||||
|
*/ |
||||
|
Bdd Or(const Bdd& g) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes \not (f \and g) |
||||
|
*/ |
||||
|
Bdd Nand(const Bdd& g) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes \not (f \or g) |
||||
|
*/ |
||||
|
Bdd Nor(const Bdd& g) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes f \xor g |
||||
|
*/ |
||||
|
Bdd Xor(const Bdd& g) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes \not (f \xor g), i.e. f \equiv g |
||||
|
*/ |
||||
|
Bdd Xnor(const Bdd& g) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns whether all elements in f are also in g |
||||
|
*/ |
||||
|
int Leq(const Bdd& g) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes the reverse application of a transition relation to this set. |
||||
|
* @param relation the transition relation to apply |
||||
|
* @param cube the variables that are in the transition relation |
||||
|
* This function assumes that s,t are interleaved with s even and t odd (s+1). |
||||
|
* Other variables in the relation are ignored (by existential quantification) |
||||
|
* Set cube to "false" (illegal cube) to assume all encountered variables are in s,t |
||||
|
* |
||||
|
* Use this function to concatenate two relations --> --> |
||||
|
* or to take the 'previous' of a set --> S |
||||
|
*/ |
||||
|
Bdd RelPrev(const Bdd& relation, const BddSet& cube) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes the application of a transition relation to this set. |
||||
|
* @param relation the transition relation to apply |
||||
|
* @param cube the variables that are in the transition relation |
||||
|
* This function assumes that s,t are interleaved with s even and t odd (s+1). |
||||
|
* Other variables in the relation are ignored (by existential quantification) |
||||
|
* Set cube to "false" (illegal cube) to assume all encountered variables are in s,t |
||||
|
* |
||||
|
* Use this function to take the 'next' of a set S --> |
||||
|
*/ |
||||
|
Bdd RelNext(const Bdd& relation, const BddSet& cube) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes the transitive closure by traversing the BDD recursively. |
||||
|
* See Y. Matsunaga, P. C. McGeer, R. K. Brayton |
||||
|
* On Computing the Transitive Closre of a State Transition Relation |
||||
|
* 30th ACM Design Automation Conference, 1993. |
||||
|
*/ |
||||
|
Bdd Closure() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes the constrain f @ c |
||||
|
*/ |
||||
|
Bdd Constrain(const Bdd &c) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes the BDD restrict according to Coudert and Madre's algorithm (ICCAD90). |
||||
|
*/ |
||||
|
Bdd Restrict(const Bdd &c) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Functional composition. Whenever a variable v in the map m is found in the BDD, |
||||
|
* it is substituted by the associated function. |
||||
|
* You can also use this function to implement variable reordering. |
||||
|
*/ |
||||
|
Bdd Compose(const BddMap &m) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Substitute all variables in the array from by the corresponding variables in to. |
||||
|
*/ |
||||
|
Bdd Permute(const std::vector<uint32_t>& from, const std::vector<uint32_t>& to) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes the support of a Bdd. |
||||
|
*/ |
||||
|
Bdd Support() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Gets the BDD of this Bdd (for C functions) |
||||
|
*/ |
||||
|
BDD GetBDD() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Writes .dot file of this Bdd. Not thread-safe! |
||||
|
*/ |
||||
|
void PrintDot(FILE *out) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Gets a SHA2 hash that describes the structure of this Bdd. |
||||
|
* @param string a character array of at least 65 characters (includes zero-termination) |
||||
|
* This hash is 64 characters long and is independent of the memory locations of BDD nodes. |
||||
|
*/ |
||||
|
void GetShaHash(char *string) const; |
||||
|
|
||||
|
std::string GetShaHash() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes the number of satisfying variable assignments, using variables in cube. |
||||
|
*/ |
||||
|
double SatCount(const BddSet &cube) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Compute the number of satisfying variable assignments, using the given number of variables. |
||||
|
*/ |
||||
|
double SatCount(const size_t nvars) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Gets one satisfying assignment according to the variables. |
||||
|
* @param variables The set of variables to be assigned, must include the support of the Bdd. |
||||
|
*/ |
||||
|
void PickOneCube(const BddSet &variables, uint8_t *string) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Gets one satisfying assignment according to the variables. |
||||
|
* @param variables The set of variables to be assigned, must include the support of the Bdd. |
||||
|
* Returns an empty vector when either this Bdd equals bddZero() or the cube is empty. |
||||
|
*/ |
||||
|
std::vector<bool> PickOneCube(const BddSet &variables) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Gets a cube that satisfies this Bdd. |
||||
|
*/ |
||||
|
Bdd PickOneCube() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Faster version of: *this + Sylvan::bddCube(variables, values); |
||||
|
*/ |
||||
|
Bdd UnionCube(const BddSet &variables, uint8_t *values) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Faster version of: *this + Sylvan::bddCube(variables, values); |
||||
|
*/ |
||||
|
Bdd UnionCube(const BddSet &variables, std::vector<uint8_t> values) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Generate a cube representing a set of variables |
||||
|
*/ |
||||
|
static Bdd VectorCube(const std::vector<Bdd> variables); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Generate a cube representing a set of variables |
||||
|
* @param variables An sorted set of variable indices |
||||
|
*/ |
||||
|
static Bdd VariablesCube(const std::vector<uint32_t> variables); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Gets the number of nodes in this Bdd. Not thread-safe! |
||||
|
*/ |
||||
|
size_t NodeCount() const; |
||||
|
|
||||
|
#include "sylvan_obj_bdd_storm.hpp"
|
||||
|
|
||||
|
private: |
||||
|
BDD bdd; |
||||
|
}; |
||||
|
|
||||
|
class BddSet |
||||
|
{ |
||||
|
friend class Bdd; |
||||
|
friend class Mtbdd; |
||||
|
Bdd set; |
||||
|
|
||||
|
public: |
||||
|
/**
|
||||
|
* @brief Create a new empty set. |
||||
|
*/ |
||||
|
BddSet() : set(Bdd::bddOne()) {} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Wrap the BDD cube <other> in a set. |
||||
|
*/ |
||||
|
BddSet(const Bdd &other) : set(other) {} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Create a copy of the set <other>. |
||||
|
*/ |
||||
|
BddSet(const BddSet &other) : set(other.set) {} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Add the variable <variable> to this set. |
||||
|
*/ |
||||
|
void add(uint32_t variable) { |
||||
|
set *= Bdd::bddVar(variable); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Add all variables in the set <other> to this set. |
||||
|
*/ |
||||
|
void add(BddSet &other) { |
||||
|
set *= other.set; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Remove the variable <variable> from this set. |
||||
|
*/ |
||||
|
void remove(uint32_t variable) { |
||||
|
set = set.ExistAbstract(Bdd::bddVar(variable)); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Remove all variables in the set <other> from this set. |
||||
|
*/ |
||||
|
void remove(BddSet &other) { |
||||
|
set = set.ExistAbstract(other.set); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Retrieve the head of the set. (The first variable.) |
||||
|
*/ |
||||
|
uint32_t TopVar() const { |
||||
|
return set.TopVar(); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Retrieve the tail of the set. (The set containing all but the first variables.) |
||||
|
*/ |
||||
|
BddSet Next() const { |
||||
|
Bdd then = set.Then(); |
||||
|
return BddSet(then); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Return true if this set is empty, or false otherwise. |
||||
|
*/ |
||||
|
bool isEmpty() const { |
||||
|
return set.isOne(); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Return true if this set contains the variable <variable>, or false otherwise. |
||||
|
*/ |
||||
|
bool contains(uint32_t variable) const { |
||||
|
if (isEmpty()) return false; |
||||
|
else if (TopVar() == variable) return true; |
||||
|
else return Next().contains(variable); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Return the number of variables in this set. |
||||
|
*/ |
||||
|
size_t size() const { |
||||
|
if (isEmpty()) return 0; |
||||
|
else return 1 + Next().size(); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Create a set containing the <length> variables in <arr>. |
||||
|
* It is advised to have the variables in <arr> in ascending order. |
||||
|
*/ |
||||
|
static BddSet fromArray(BDDVAR *arr, size_t length) { |
||||
|
BddSet set; |
||||
|
for (size_t i = 0; i < length; i++) { |
||||
|
set.add(arr[length-i-1]); |
||||
|
} |
||||
|
return set; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Create a set containing the variables in <variables>. |
||||
|
* It is advised to have the variables in <arr> in ascending order. |
||||
|
*/ |
||||
|
static BddSet fromVector(const std::vector<Bdd> variables) { |
||||
|
BddSet set; |
||||
|
for (int i=variables.size()-1; i>=0; i--) { |
||||
|
set.set *= variables[i]; |
||||
|
} |
||||
|
return set; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Create a set containing the variables in <variables>. |
||||
|
* It is advised to have the variables in <arr> in ascending order. |
||||
|
*/ |
||||
|
static BddSet fromVector(const std::vector<uint32_t> variables) { |
||||
|
BddSet set; |
||||
|
for (int i=variables.size()-1; i>=0; i--) { |
||||
|
set.add(variables[i]); |
||||
|
} |
||||
|
return set; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Write all variables in this set to <arr>. |
||||
|
* @param arr An array of at least size this.size(). |
||||
|
*/ |
||||
|
void toArray(BDDVAR *arr) const { |
||||
|
if (!isEmpty()) { |
||||
|
*arr = TopVar(); |
||||
|
Next().toArray(arr+1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* @brief Return the vector of all variables in this set. |
||||
|
*/ |
||||
|
std::vector<uint32_t> toVector() const { |
||||
|
std::vector<uint32_t> result; |
||||
|
Bdd x = set; |
||||
|
while (!x.isOne()) { |
||||
|
result.push_back(x.TopVar()); |
||||
|
x = x.Then(); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
class BddMap |
||||
|
{ |
||||
|
friend class Bdd; |
||||
|
BDD bdd; |
||||
|
BddMap(const BDD from) : bdd(from) { sylvan_protect(&bdd); } |
||||
|
BddMap(const Bdd &from) : bdd(from.bdd) { sylvan_protect(&bdd); } |
||||
|
public: |
||||
|
BddMap() : bdd(sylvan_map_empty()) { sylvan_protect(&bdd); } |
||||
|
~BddMap() { sylvan_unprotect(&bdd); } |
||||
|
|
||||
|
BddMap(uint32_t key_variable, const Bdd value); |
||||
|
|
||||
|
BddMap operator+(const Bdd& other) const; |
||||
|
BddMap operator+=(const Bdd& other); |
||||
|
BddMap operator-(const Bdd& other) const; |
||||
|
BddMap operator-=(const Bdd& other); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Adds a key-value pair to the map |
||||
|
*/ |
||||
|
void put(uint32_t key, Bdd value); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Removes a key-value pair from the map |
||||
|
*/ |
||||
|
void removeKey(uint32_t key); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the number of key-value pairs in this map |
||||
|
*/ |
||||
|
size_t size() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns non-zero when this map is empty |
||||
|
*/ |
||||
|
int isEmpty() const; |
||||
|
}; |
||||
|
|
||||
|
class MtbddMap; |
||||
|
|
||||
|
class Mtbdd { |
||||
|
friend class Sylvan; |
||||
|
friend class MtbddMap; |
||||
|
|
||||
|
public: |
||||
|
Mtbdd() { mtbdd = sylvan_false; mtbdd_protect(&mtbdd); } |
||||
|
Mtbdd(const MTBDD from) : mtbdd(from) { mtbdd_protect(&mtbdd); } |
||||
|
Mtbdd(const Mtbdd &from) : mtbdd(from.mtbdd) { mtbdd_protect(&mtbdd); } |
||||
|
Mtbdd(const Bdd &from) : mtbdd(from.bdd) { mtbdd_protect(&mtbdd); } |
||||
|
~Mtbdd() { mtbdd_unprotect(&mtbdd); } |
||||
|
|
||||
|
/**
|
||||
|
* @brief Creates a Mtbdd leaf representing the int64 value <value> |
||||
|
*/ |
||||
|
static Mtbdd int64Terminal(int64_t value); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Creates a Mtbdd leaf representing the floating-point value <value> |
||||
|
*/ |
||||
|
static Mtbdd doubleTerminal(double value); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Creates a Mtbdd leaf representing the fraction value <nominator>/<denominator> |
||||
|
* Internally, Sylvan uses 32-bit values and reports overflows to stderr. |
||||
|
*/ |
||||
|
static Mtbdd fractionTerminal(int64_t nominator, uint64_t denominator); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Creates a Mtbdd leaf of type <type> holding value <value> |
||||
|
* This is useful for custom Mtbdd types. |
||||
|
*/ |
||||
|
static Mtbdd terminal(uint32_t type, uint64_t value); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Creates a Boolean Mtbdd representing jsut the variable index in its positive form |
||||
|
* The variable index must be 0<=index<=2^23 (Sylvan uses 24 bits internally) |
||||
|
*/ |
||||
|
static Mtbdd mtbddVar(uint32_t variable); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the Boolean Mtbdd representing "True" |
||||
|
*/ |
||||
|
static Mtbdd mtbddOne(); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the Boolean Mtbdd representing "False" |
||||
|
*/ |
||||
|
static Mtbdd mtbddZero(); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the Mtbdd representing a cube of variables, according to the given values. |
||||
|
* @param variables the variables that will be in the cube in their positive or negative form |
||||
|
* @param values a character array describing how the variables will appear in the result |
||||
|
* @param terminal the leaf of the cube |
||||
|
* The length of string must be equal to the number of variables in the cube. |
||||
|
* For every ith char in string, if it is 0, the corresponding variable will appear in its negative form, |
||||
|
* if it is 1, it will appear in its positive form, and if it is 2, it will appear as "any", thus it will |
||||
|
* be skipped. |
||||
|
*/ |
||||
|
static Mtbdd mtbddCube(const BddSet &variables, unsigned char *values, const Mtbdd &terminal); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the Mtbdd representing a cube of variables, according to the given values. |
||||
|
* @param variables the variables that will be in the cube in their positive or negative form |
||||
|
* @param values a character array describing how the variables will appear in the result |
||||
|
* @param terminal the leaf of the cube |
||||
|
* The length of string must be equal to the number of variables in the cube. |
||||
|
* For every ith char in string, if it is 0, the corresponding variable will appear in its negative form, |
||||
|
* if it is 1, it will appear in its positive form, and if it is 2, it will appear as "any", thus it will |
||||
|
* be skipped. |
||||
|
*/ |
||||
|
static Mtbdd mtbddCube(const BddSet &variables, std::vector<uint8_t> values, const Mtbdd &terminal); |
||||
|
|
||||
|
int operator==(const Mtbdd& other) const; |
||||
|
int operator!=(const Mtbdd& other) const; |
||||
|
Mtbdd operator=(const Mtbdd& right); |
||||
|
Mtbdd operator!() const; |
||||
|
Mtbdd operator~() const; |
||||
|
Mtbdd operator*(const Mtbdd& other) const; |
||||
|
Mtbdd operator*=(const Mtbdd& other); |
||||
|
Mtbdd operator+(const Mtbdd& other) const; |
||||
|
Mtbdd operator+=(const Mtbdd& other); |
||||
|
Mtbdd operator-(const Mtbdd& other) const; |
||||
|
Mtbdd operator-=(const Mtbdd& other); |
||||
|
|
||||
|
// not implemented (compared to Bdd): <=, >=, <, >, &, &=, |, |=, ^, ^=
|
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns non-zero if this Mtbdd is a leaf |
||||
|
*/ |
||||
|
int isTerminal() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns non-zero if this Mtbdd is a leaf |
||||
|
*/ |
||||
|
int isLeaf() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns non-zero if this Mtbdd is mtbddOne() |
||||
|
*/ |
||||
|
int isOne() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns non-zero if this Mtbdd is mtbddZero() |
||||
|
*/ |
||||
|
int isZero() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the top variable index of this Mtbdd (the variable in the root node) |
||||
|
*/ |
||||
|
uint32_t TopVar() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Follows the high edge ("then") of the root node of this Mtbdd |
||||
|
*/ |
||||
|
Mtbdd Then() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Follows the low edge ("else") of the root node of this Mtbdd |
||||
|
*/ |
||||
|
Mtbdd Else() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the negation of the MTBDD (every terminal negative) |
||||
|
* Do not use this for Boolean MTBDDs, only for Integer/Double/Fraction MTBDDs. |
||||
|
*/ |
||||
|
Mtbdd Negate() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Applies the binary operation <op> |
||||
|
*/ |
||||
|
Mtbdd Apply(const Mtbdd &other, mtbdd_apply_op op) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Applies the unary operation <op> with parameter <param> |
||||
|
*/ |
||||
|
Mtbdd UApply(mtbdd_uapply_op op, size_t param) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computers the abstraction on variables <variables> using operator <op>. |
||||
|
* See also: AbstractPlus, AbstractTimes, AbstractMin, AbstractMax |
||||
|
*/ |
||||
|
Mtbdd Abstract(const BddSet &variables, mtbdd_abstract_op op) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes if f then g else h |
||||
|
* This Mtbdd must be a Boolean Mtbdd |
||||
|
*/ |
||||
|
Mtbdd Ite(const Mtbdd &g, const Mtbdd &h) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes f + g |
||||
|
*/ |
||||
|
Mtbdd Plus(const Mtbdd &other) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes f * g |
||||
|
*/ |
||||
|
Mtbdd Times(const Mtbdd &other) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes min(f, g) |
||||
|
*/ |
||||
|
Mtbdd Min(const Mtbdd &other) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes max(f, g) |
||||
|
*/ |
||||
|
Mtbdd Max(const Mtbdd &other) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes abstraction by summation (existential quantification) |
||||
|
*/ |
||||
|
Mtbdd AbstractPlus(const BddSet &variables) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes abstraction by multiplication (universal quantification) |
||||
|
*/ |
||||
|
Mtbdd AbstractTimes(const BddSet &variables) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes abstraction by minimum |
||||
|
*/ |
||||
|
Mtbdd AbstractMin(const BddSet &variables) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes abstraction by maximum |
||||
|
*/ |
||||
|
Mtbdd AbstractMax(const BddSet &variables) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes abstraction by summation of f \times g |
||||
|
*/ |
||||
|
Mtbdd AndExists(const Mtbdd &other, const BddSet &variables) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Convert floating-point/fraction Mtbdd to a Boolean Mtbdd, leaf >= value ? true : false |
||||
|
*/ |
||||
|
Mtbdd MtbddThreshold(double value) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Convert floating-point/fraction Mtbdd to a Boolean Mtbdd, leaf > value ? true : false |
||||
|
*/ |
||||
|
Mtbdd MtbddStrictThreshold(double value) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Convert floating-point/fraction Mtbdd to a Boolean Mtbdd, leaf >= value ? true : false |
||||
|
* Same as MtbddThreshold (Bdd = Boolean Mtbdd) |
||||
|
*/ |
||||
|
Bdd BddThreshold(double value) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Convert floating-point/fraction Mtbdd to a Boolean Mtbdd, leaf > value ? true : false |
||||
|
* Same as MtbddStrictThreshold (Bdd = Boolean Mtbdd) |
||||
|
*/ |
||||
|
Bdd BddStrictThreshold(double value) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes the support of a Mtbdd. |
||||
|
*/ |
||||
|
Mtbdd Support() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Gets the MTBDD of this Mtbdd (for C functions) |
||||
|
*/ |
||||
|
MTBDD GetMTBDD() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Functional composition. Whenever a variable v in the map m is found in the MTBDD, |
||||
|
* it is substituted by the associated function (which should be a Boolean MTBDD) |
||||
|
* You can also use this function to implement variable reordering. |
||||
|
*/ |
||||
|
Mtbdd Compose(MtbddMap &m) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Substitute all variables in the array from by the corresponding variables in to. |
||||
|
*/ |
||||
|
Mtbdd Permute(const std::vector<uint32_t>& from, const std::vector<uint32_t>& to) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Compute the number of satisfying variable assignments, using variables in cube. |
||||
|
*/ |
||||
|
double SatCount(const BddSet &variables) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Compute the number of satisfying variable assignments, using the given number of variables. |
||||
|
*/ |
||||
|
double SatCount(const size_t nvars) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Gets the number of nodes in this Bdd. Not thread-safe! |
||||
|
*/ |
||||
|
size_t NodeCount() const; |
||||
|
|
||||
|
#include "sylvan_obj_mtbdd_storm.hpp"
|
||||
|
|
||||
|
private: |
||||
|
MTBDD mtbdd; |
||||
|
}; |
||||
|
|
||||
|
class MtbddMap |
||||
|
{ |
||||
|
friend class Mtbdd; |
||||
|
MTBDD mtbdd; |
||||
|
MtbddMap(MTBDD from) : mtbdd(from) { mtbdd_protect(&mtbdd); } |
||||
|
MtbddMap(Mtbdd &from) : mtbdd(from.mtbdd) { mtbdd_protect(&mtbdd); } |
||||
|
public: |
||||
|
MtbddMap() : mtbdd(mtbdd_map_empty()) { mtbdd_protect(&mtbdd); } |
||||
|
~MtbddMap() { mtbdd_unprotect(&mtbdd); } |
||||
|
|
||||
|
MtbddMap(uint32_t key_variable, Mtbdd value); |
||||
|
|
||||
|
MtbddMap operator+(const Mtbdd& other) const; |
||||
|
MtbddMap operator+=(const Mtbdd& other); |
||||
|
MtbddMap operator-(const Mtbdd& other) const; |
||||
|
MtbddMap operator-=(const Mtbdd& other); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Adds a key-value pair to the map |
||||
|
*/ |
||||
|
void put(uint32_t key, Mtbdd value); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Removes a key-value pair from the map |
||||
|
*/ |
||||
|
void removeKey(uint32_t key); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns the number of key-value pairs in this map |
||||
|
*/ |
||||
|
size_t size(); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Returns non-zero when this map is empty |
||||
|
*/ |
||||
|
int isEmpty(); |
||||
|
}; |
||||
|
|
||||
|
class Sylvan { |
||||
|
public: |
||||
|
/**
|
||||
|
* @brief Initializes the Sylvan framework, call this only once in your program. |
||||
|
* @param initialTableSize The initial size of the nodes table. Must be a power of two. |
||||
|
* @param maxTableSize The maximum size of the nodes table. Must be a power of two. |
||||
|
* @param initialCacheSize The initial size of the operation cache. Must be a power of two. |
||||
|
* @param maxCacheSize The maximum size of the operation cache. Must be a power of two. |
||||
|
*/ |
||||
|
static void initPackage(size_t initialTableSize, size_t maxTableSize, size_t initialCacheSize, size_t maxCacheSize); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Initializes the BDD module of the Sylvan framework. |
||||
|
* @param granularity determins operation cache behavior; for higher values (2+) it will use the operation cache less often. |
||||
|
* Values of 3-7 may result in better performance, since occasionally not using the operation cache is fine in practice. |
||||
|
* A granularity of 1 means that every BDD operation will be cached at every variable level. |
||||
|
*/ |
||||
|
static void initBdd(int granularity); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Initializes the MTBDD module of the Sylvan framework. |
||||
|
*/ |
||||
|
static void initMtbdd(); |
||||
|
|
||||
|
/**
|
||||
|
* @brief Frees all memory in use by Sylvan. |
||||
|
* Warning: if you have any Bdd objects which are not bddZero() or bddOne() after this, your program may crash! |
||||
|
*/ |
||||
|
static void quitPackage(); |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#endif
|
@ -0,0 +1,3 @@ |
|||||
|
Mtbdd toDoubleMtbdd() const; |
||||
|
Mtbdd toInt64Mtbdd() const; |
||||
|
Mtbdd Ite(Mtbdd const& thenDd, Mtbdd const& elseDd) const; |
@ -0,0 +1,51 @@ |
|||||
|
/**
|
||||
|
* @brief Computes f - g |
||||
|
*/ |
||||
|
Mtbdd Minus(const Mtbdd &other) const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Computes f / g |
||||
|
*/ |
||||
|
Mtbdd Divide(const Mtbdd &other) const; |
||||
|
|
||||
|
Bdd NotZero() const; |
||||
|
|
||||
|
Bdd Equals(const Mtbdd& other) const; |
||||
|
|
||||
|
Bdd Less(const Mtbdd& other) const; |
||||
|
|
||||
|
Bdd LessOrEqual(const Mtbdd& other) const; |
||||
|
|
||||
|
Mtbdd Minimum() const; |
||||
|
|
||||
|
Mtbdd Maximum() const; |
||||
|
|
||||
|
bool EqualNorm(const Mtbdd& other, double epsilon) const; |
||||
|
|
||||
|
bool EqualNormRel(const Mtbdd& other, double epsilon) const; |
||||
|
|
||||
|
Mtbdd Floor() const; |
||||
|
|
||||
|
Mtbdd Ceil() const; |
||||
|
|
||||
|
Mtbdd Pow(const Mtbdd& other) const; |
||||
|
|
||||
|
Mtbdd Mod(const Mtbdd& other) const; |
||||
|
|
||||
|
Mtbdd Logxy(const Mtbdd& other) const; |
||||
|
|
||||
|
size_t CountLeaves() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Compute the number of non-zero variable assignments, using variables in cube. |
||||
|
*/ |
||||
|
double NonZeroCount(size_t variableCount) const; |
||||
|
|
||||
|
bool isValid() const; |
||||
|
|
||||
|
/**
|
||||
|
* @brief Writes .dot file of this Bdd. Not thread-safe! |
||||
|
*/ |
||||
|
void PrintDot(FILE *out) const; |
||||
|
|
||||
|
std::string GetShaHash() const; |
@ -0,0 +1,141 @@ |
|||||
|
Mtbdd |
||||
|
Bdd::toDoubleMtbdd() const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_bool_to_double(bdd); |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Bdd::toInt64Mtbdd() const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_bool_to_int64(bdd); |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Bdd::Ite(Mtbdd const& thenDd, Mtbdd const& elseDd) const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_ite(bdd, thenDd.GetMTBDD(), elseDd.GetMTBDD()); |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Mtbdd::Minus(const Mtbdd &other) const |
||||
|
{ |
||||
|
LACE_ME; |
||||
|
return mtbdd_minus(mtbdd, other.mtbdd); |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Mtbdd::Divide(const Mtbdd &other) const |
||||
|
{ |
||||
|
LACE_ME; |
||||
|
return mtbdd_divide(mtbdd, other.mtbdd); |
||||
|
} |
||||
|
|
||||
|
Bdd |
||||
|
Mtbdd::NotZero() const |
||||
|
{ |
||||
|
LACE_ME; |
||||
|
return mtbdd_not_zero(mtbdd); |
||||
|
} |
||||
|
|
||||
|
Bdd |
||||
|
Mtbdd::Equals(const Mtbdd& other) const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_equals(mtbdd, other.mtbdd); |
||||
|
} |
||||
|
|
||||
|
Bdd |
||||
|
Mtbdd::Less(const Mtbdd& other) const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_less_as_bdd(mtbdd, other.mtbdd); |
||||
|
} |
||||
|
|
||||
|
Bdd |
||||
|
Mtbdd::LessOrEqual(const Mtbdd& other) const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_less_or_equal_as_bdd(mtbdd, other.mtbdd); |
||||
|
} |
||||
|
|
||||
|
bool |
||||
|
Mtbdd::EqualNorm(const Mtbdd& other, double epsilon) const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_equal_norm_d(mtbdd, other.mtbdd, epsilon); |
||||
|
} |
||||
|
|
||||
|
bool |
||||
|
Mtbdd::EqualNormRel(const Mtbdd& other, double epsilon) const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_equal_norm_rel_d(mtbdd, other.mtbdd, epsilon); |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Mtbdd::Floor() const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_floor(mtbdd); |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Mtbdd::Ceil() const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_ceil(mtbdd); |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Mtbdd::Pow(const Mtbdd& other) const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_pow(mtbdd, other.mtbdd); |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Mtbdd::Mod(const Mtbdd& other) const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_mod(mtbdd, other.mtbdd); |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Mtbdd::Logxy(const Mtbdd& other) const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_logxy(mtbdd, other.mtbdd); |
||||
|
} |
||||
|
|
||||
|
size_t |
||||
|
Mtbdd::CountLeaves() const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_leafcount(mtbdd); |
||||
|
} |
||||
|
|
||||
|
double |
||||
|
Mtbdd::NonZeroCount(size_t variableCount) const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_non_zero_count(mtbdd, variableCount); |
||||
|
} |
||||
|
|
||||
|
bool |
||||
|
Mtbdd::isValid() const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_test_isvalid(mtbdd) == 1; |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Mtbdd::Minimum() const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_minimum(mtbdd); |
||||
|
} |
||||
|
|
||||
|
Mtbdd |
||||
|
Mtbdd::Maximum() const { |
||||
|
LACE_ME; |
||||
|
return mtbdd_maximum(mtbdd); |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
Mtbdd::PrintDot(FILE *out) const { |
||||
|
mtbdd_fprintdot(out, mtbdd, NULL); |
||||
|
} |
||||
|
|
||||
|
std::string |
||||
|
Mtbdd::GetShaHash() const { |
||||
|
char buf[65]; |
||||
|
mtbdd_getsha(mtbdd, buf); |
||||
|
return std::string(buf); |
||||
|
} |
||||
|
|
@ -0,0 +1,35 @@ |
|||||
|
/* |
||||
|
* Written by Josh Dybnis and released to the public domain, as explained at |
||||
|
* http://creativecommons.org/licenses/publicdomain |
||||
|
* |
||||
|
* A platform independant wrapper around thread-local storage. On platforms that don't support |
||||
|
* __thread variables (e.g. Mac OS X), we have to use the pthreads library for thread-local storage |
||||
|
*/ |
||||
|
#include <assert.h> |
||||
|
|
||||
|
#ifndef TLS_H |
||||
|
#define TLS_H |
||||
|
|
||||
|
#ifdef __ELF__ // use gcc thread-local storage (i.e. __thread variables) |
||||
|
#define DECLARE_THREAD_LOCAL(name, type) __thread type name |
||||
|
#define INIT_THREAD_LOCAL(name) |
||||
|
#define SET_THREAD_LOCAL(name, value) name = value |
||||
|
#define LOCALIZE_THREAD_LOCAL(name, type) |
||||
|
|
||||
|
#else//!__ELF__ |
||||
|
|
||||
|
#include <pthread.h> |
||||
|
|
||||
|
#define DECLARE_THREAD_LOCAL(name, type) pthread_key_t name##_KEY |
||||
|
|
||||
|
#define INIT_THREAD_LOCAL(name) \ |
||||
|
do { \ |
||||
|
if (pthread_key_create(&name##_KEY, NULL) != 0) { assert(0); } \ |
||||
|
} while (0) |
||||
|
|
||||
|
#define SET_THREAD_LOCAL(name, value) pthread_setspecific(name##_KEY, (void *)(size_t)value); |
||||
|
|
||||
|
#define LOCALIZE_THREAD_LOCAL(name, type) type name = (type)(size_t)pthread_getspecific(name##_KEY) |
||||
|
|
||||
|
#endif//__ELF__ |
||||
|
#endif//TLS_H |
@ -0,0 +1,5 @@ |
|||||
|
test |
||||
|
cmake_install.cmake |
||||
|
CMakeFiles |
||||
|
*.o |
||||
|
.libs |
@ -0,0 +1,15 @@ |
|||||
|
cmake_minimum_required(VERSION 2.6) |
||||
|
project(sylvan C CXX) |
||||
|
enable_testing() |
||||
|
|
||||
|
add_executable(sylvan_test main.c) |
||||
|
target_link_libraries(sylvan_test sylvan) |
||||
|
|
||||
|
add_executable(test_basic test_basic.c) |
||||
|
target_link_libraries(test_basic sylvan) |
||||
|
|
||||
|
add_executable(test_cxx test_cxx.cpp) |
||||
|
target_link_libraries(test_cxx sylvan stdc++) |
||||
|
|
||||
|
add_test(test_cxx test_cxx) |
||||
|
add_test(test_basic test_basic) |
@ -0,0 +1,350 @@ |
|||||
|
#include <stdio.h> |
||||
|
#include <stdint.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <pthread.h> |
||||
|
#include <unistd.h> |
||||
|
#include <time.h> |
||||
|
#include <sys/types.h> |
||||
|
#include <sys/time.h> |
||||
|
#include <inttypes.h> |
||||
|
|
||||
|
#include <assert.h> |
||||
|
#include "test_assert.h" |
||||
|
|
||||
|
#include "llmsset.h" |
||||
|
#include "sylvan.h" |
||||
|
|
||||
|
#define BLACK "\33[22;30m" |
||||
|
#define GRAY "\33[01;30m" |
||||
|
#define RED "\33[22;31m" |
||||
|
#define LRED "\33[01;31m" |
||||
|
#define GREEN "\33[22;32m" |
||||
|
#define LGREEN "\33[01;32m" |
||||
|
#define BLUE "\33[22;34m" |
||||
|
#define LBLUE "\33[01;34m" |
||||
|
#define BROWN "\33[22;33m" |
||||
|
#define YELLOW "\33[01;33m" |
||||
|
#define CYAN "\33[22;36m" |
||||
|
#define LCYAN "\33[22;36m" |
||||
|
#define MAGENTA "\33[22;35m" |
||||
|
#define LMAGENTA "\33[01;35m" |
||||
|
#define NC "\33[0m" |
||||
|
#define BOLD "\33[1m" |
||||
|
#define ULINE "\33[4m" //underline |
||||
|
#define BLINK "\33[5m" |
||||
|
#define INVERT "\33[7m" |
||||
|
|
||||
|
__thread uint64_t seed = 1; |
||||
|
|
||||
|
uint64_t |
||||
|
xorshift_rand(void) |
||||
|
{ |
||||
|
uint64_t x = seed; |
||||
|
if (seed == 0) seed = rand(); |
||||
|
x ^= x >> 12; |
||||
|
x ^= x << 25; |
||||
|
x ^= x >> 27; |
||||
|
seed = x; |
||||
|
return x * 2685821657736338717LL; |
||||
|
} |
||||
|
|
||||
|
double |
||||
|
uniform_deviate(uint64_t seed) |
||||
|
{ |
||||
|
return seed * (1.0 / (0xffffffffffffffffL + 1.0)); |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
rng(int low, int high) |
||||
|
{ |
||||
|
return low + uniform_deviate(xorshift_rand()) * (high-low); |
||||
|
} |
||||
|
|
||||
|
static inline BDD |
||||
|
make_random(int i, int j) |
||||
|
{ |
||||
|
if (i == j) return rng(0, 2) ? sylvan_true : sylvan_false; |
||||
|
|
||||
|
BDD yes = make_random(i+1, j); |
||||
|
BDD no = make_random(i+1, j); |
||||
|
BDD result = sylvan_invalid; |
||||
|
|
||||
|
switch(rng(0, 4)) { |
||||
|
case 0: |
||||
|
result = no; |
||||
|
sylvan_deref(yes); |
||||
|
break; |
||||
|
case 1: |
||||
|
result = yes; |
||||
|
sylvan_deref(no); |
||||
|
break; |
||||
|
case 2: |
||||
|
result = sylvan_ref(sylvan_makenode(i, yes, no)); |
||||
|
sylvan_deref(no); |
||||
|
sylvan_deref(yes); |
||||
|
break; |
||||
|
case 3: |
||||
|
default: |
||||
|
result = sylvan_ref(sylvan_makenode(i, no, yes)); |
||||
|
sylvan_deref(no); |
||||
|
sylvan_deref(yes); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** GC testing */ |
||||
|
VOID_TASK_2(gctest_fill, int, levels, int, width) |
||||
|
{ |
||||
|
if (levels > 1) { |
||||
|
int i; |
||||
|
for (i=0; i<width; i++) { SPAWN(gctest_fill, levels-1, width); } |
||||
|
for (i=0; i<width; i++) { SYNC(gctest_fill); } |
||||
|
} else { |
||||
|
sylvan_deref(make_random(0, 10)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void report_table() |
||||
|
{ |
||||
|
llmsset_t __sylvan_get_internal_data(); |
||||
|
llmsset_t tbl = __sylvan_get_internal_data(); |
||||
|
LACE_ME; |
||||
|
size_t filled = llmsset_count_marked(tbl); |
||||
|
size_t total = llmsset_get_size(tbl); |
||||
|
printf("done, table: %0.1f%% full (%zu nodes).\n", 100.0*(double)filled/total, filled); |
||||
|
} |
||||
|
|
||||
|
int test_gc(int threads) |
||||
|
{ |
||||
|
LACE_ME; |
||||
|
int N_canaries = 16; |
||||
|
BDD canaries[N_canaries]; |
||||
|
char* hashes[N_canaries]; |
||||
|
char* hashes2[N_canaries]; |
||||
|
int i,j; |
||||
|
for (i=0;i<N_canaries;i++) { |
||||
|
canaries[i] = make_random(0, 10); |
||||
|
hashes[i] = (char*)malloc(80); |
||||
|
hashes2[i] = (char*)malloc(80); |
||||
|
sylvan_getsha(canaries[i], hashes[i]); |
||||
|
sylvan_test_isbdd(canaries[i]); |
||||
|
} |
||||
|
test_assert(sylvan_count_refs() == (size_t)N_canaries); |
||||
|
for (j=0;j<10*threads;j++) { |
||||
|
CALL(gctest_fill, 6, 5); |
||||
|
for (i=0;i<N_canaries;i++) { |
||||
|
sylvan_test_isbdd(canaries[i]); |
||||
|
sylvan_getsha(canaries[i], hashes2[i]); |
||||
|
test_assert(strcmp(hashes[i], hashes2[i]) == 0); |
||||
|
} |
||||
|
} |
||||
|
test_assert(sylvan_count_refs() == (size_t)N_canaries); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
TASK_2(MDD, random_ldd, int, depth, int, count) |
||||
|
{ |
||||
|
uint32_t n[depth]; |
||||
|
|
||||
|
MDD result = lddmc_false; |
||||
|
|
||||
|
int i, j; |
||||
|
for (i=0; i<count; i++) { |
||||
|
for (j=0; j<depth; j++) { |
||||
|
n[j] = rng(0, 10); |
||||
|
} |
||||
|
//MDD old = result; |
||||
|
result = lddmc_union_cube(result, n, depth); |
||||
|
//assert(lddmc_cube(n, depth) != lddmc_true); |
||||
|
//assert(result == lddmc_union(old, lddmc_cube(n, depth))); |
||||
|
//assert(result != lddmc_true); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
VOID_TASK_3(enumer, uint32_t*, values, size_t, count, void*, context) |
||||
|
{ |
||||
|
return; |
||||
|
(void)values; |
||||
|
(void)count; |
||||
|
(void)context; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
test_lddmc() |
||||
|
{ |
||||
|
LACE_ME; |
||||
|
|
||||
|
sylvan_init_package(1LL<<24, 1LL<<24, 1LL<<24, 1LL<<24); |
||||
|
sylvan_init_ldd(); |
||||
|
sylvan_gc_disable(); |
||||
|
|
||||
|
MDD a, b, c; |
||||
|
|
||||
|
// Test union, union_cube, member_cube, satcount |
||||
|
|
||||
|
a = lddmc_cube((uint32_t[]){1,2,3,5,4,3}, 6); |
||||
|
a = lddmc_union(a,lddmc_cube((uint32_t[]){2,2,3,5,4,3}, 6)); |
||||
|
c = b = a = lddmc_union_cube(a, (uint32_t[]){2,2,3,5,4,2}, 6); |
||||
|
a = lddmc_union_cube(a, (uint32_t[]){2,3,3,5,4,3}, 6); |
||||
|
a = lddmc_union(a, lddmc_cube((uint32_t[]){2,3,4,4,4,3}, 6)); |
||||
|
|
||||
|
test_assert(lddmc_member_cube(a, (uint32_t[]){2,3,3,5,4,3}, 6)); |
||||
|
test_assert(lddmc_member_cube(a, (uint32_t[]){1,2,3,5,4,3}, 6)); |
||||
|
test_assert(lddmc_member_cube(a, (uint32_t[]){2,2,3,5,4,3}, 6)); |
||||
|
test_assert(lddmc_member_cube(a, (uint32_t[]){2,2,3,5,4,2}, 6)); |
||||
|
|
||||
|
test_assert(lddmc_satcount(a) == 5); |
||||
|
|
||||
|
lddmc_sat_all_par(a, TASK(enumer), NULL); |
||||
|
|
||||
|
// Test minus, member_cube, satcount |
||||
|
|
||||
|
a = lddmc_minus(a, b); |
||||
|
test_assert(lddmc_member_cube(a, (uint32_t[]){2,3,3,5,4,3}, 6)); |
||||
|
test_assert(!lddmc_member_cube(a, (uint32_t[]){1,2,3,5,4,3}, 6)); |
||||
|
test_assert(!lddmc_member_cube(a, (uint32_t[]){2,2,3,5,4,3}, 6)); |
||||
|
test_assert(!lddmc_member_cube(a, (uint32_t[]){2,2,3,5,4,2}, 6)); |
||||
|
test_assert(lddmc_member_cube(a, (uint32_t[]){2,3,4,4,4,3}, 6)); |
||||
|
|
||||
|
test_assert(lddmc_satcount(a) == 2); |
||||
|
|
||||
|
// Test intersect |
||||
|
|
||||
|
test_assert(lddmc_satcount(lddmc_intersect(a,b)) == 0); |
||||
|
test_assert(lddmc_intersect(b,c)==lddmc_intersect(c,b)); |
||||
|
test_assert(lddmc_intersect(b,c)==c); |
||||
|
|
||||
|
// Test project, project_minus |
||||
|
a = lddmc_cube((uint32_t[]){1,2,3,5,4,3}, 6); |
||||
|
a = lddmc_union_cube(a, (uint32_t[]){2,2,3,5,4,3}, 6); |
||||
|
a = lddmc_union_cube(a, (uint32_t[]){2,2,3,5,4,2}, 6); |
||||
|
a = lddmc_union_cube(a, (uint32_t[]){2,3,3,5,4,3}, 6); |
||||
|
a = lddmc_union_cube(a, (uint32_t[]){2,3,4,4,4,3}, 6); |
||||
|
// a = {<1,2,3,5,4,3>,<2,2,3,5,4,3>,<2,2,3,5,4,2>,<2,3,3,5,4,3>,<2,3,4,4,4,3>} |
||||
|
MDD proj = lddmc_cube((uint32_t[]){1,1,-2},3); |
||||
|
b = lddmc_cube((uint32_t[]){1,2}, 2); |
||||
|
b = lddmc_union_cube(b, (uint32_t[]){2,2}, 2); |
||||
|
b = lddmc_union_cube(b, (uint32_t[]){2,3}, 2); |
||||
|
test_assert(lddmc_project(a, proj)==b); |
||||
|
test_assert(lddmc_project_minus(a, proj, lddmc_false)==b); |
||||
|
test_assert(lddmc_project_minus(a, proj, b)==lddmc_false); |
||||
|
|
||||
|
// Test relprod |
||||
|
|
||||
|
a = lddmc_cube((uint32_t[]){1},1); |
||||
|
b = lddmc_cube((uint32_t[]){1,2},2); |
||||
|
proj = lddmc_cube((uint32_t[]){1,2,-1}, 3); |
||||
|
test_assert(lddmc_cube((uint32_t[]){2},1) == lddmc_relprod(a, b, proj)); |
||||
|
test_assert(lddmc_cube((uint32_t[]){3},1) == lddmc_relprod(a, lddmc_cube((uint32_t[]){1,3},2), proj)); |
||||
|
a = lddmc_union_cube(a, (uint32_t[]){2},1); |
||||
|
test_assert(lddmc_satcount(a) == 2); |
||||
|
test_assert(lddmc_cube((uint32_t[]){2},1) == lddmc_relprod(a, b, proj)); |
||||
|
b = lddmc_union_cube(b, (uint32_t[]){2,2},2); |
||||
|
test_assert(lddmc_cube((uint32_t[]){2},1) == lddmc_relprod(a, b, proj)); |
||||
|
b = lddmc_union_cube(b, (uint32_t[]){2,3},2); |
||||
|
test_assert(lddmc_satcount(lddmc_relprod(a, b, proj)) == 2); |
||||
|
test_assert(lddmc_union(lddmc_cube((uint32_t[]){2},1),lddmc_cube((uint32_t[]){3},1)) == lddmc_relprod(a, b, proj)); |
||||
|
|
||||
|
// Test relprev |
||||
|
MDD universe = lddmc_union(lddmc_cube((uint32_t[]){1},1), lddmc_cube((uint32_t[]){2},1)); |
||||
|
a = lddmc_cube((uint32_t[]){2},1); |
||||
|
b = lddmc_cube((uint32_t[]){1,2},2); |
||||
|
test_assert(lddmc_cube((uint32_t[]){1},1) == lddmc_relprev(a, b, proj, universe)); |
||||
|
test_assert(lddmc_cube((uint32_t[]){1},1) == lddmc_relprev(a, b, proj, lddmc_cube((uint32_t[]){1},1))); |
||||
|
a = lddmc_cube((uint32_t[]){1},1); |
||||
|
MDD next = lddmc_relprod(a, b, proj); |
||||
|
test_assert(lddmc_relprev(next, b, proj, a) == a); |
||||
|
|
||||
|
// Random tests |
||||
|
|
||||
|
MDD rnd1, rnd2; |
||||
|
|
||||
|
int i; |
||||
|
for (i=0; i<200; i++) { |
||||
|
int depth = rng(1, 20); |
||||
|
rnd1 = CALL(random_ldd, depth, rng(0, 30)); |
||||
|
rnd2 = CALL(random_ldd, depth, rng(0, 30)); |
||||
|
test_assert(rnd1 != lddmc_true); |
||||
|
test_assert(rnd2 != lddmc_true); |
||||
|
test_assert(lddmc_intersect(rnd1,rnd2) == lddmc_intersect(rnd2,rnd1)); |
||||
|
test_assert(lddmc_union(rnd1,rnd2) == lddmc_union(rnd2,rnd1)); |
||||
|
MDD tmp = lddmc_union(lddmc_minus(rnd1, rnd2), lddmc_minus(rnd2, rnd1)); |
||||
|
test_assert(lddmc_intersect(tmp, lddmc_intersect(rnd1, rnd2)) == lddmc_false); |
||||
|
test_assert(lddmc_union(tmp, lddmc_intersect(rnd1, rnd2)) == lddmc_union(rnd1, rnd2)); |
||||
|
test_assert(lddmc_minus(rnd1,rnd2) == lddmc_minus(rnd1, lddmc_intersect(rnd1,rnd2))); |
||||
|
} |
||||
|
|
||||
|
// Test file stuff |
||||
|
for (i=0; i<10; i++) { |
||||
|
FILE *f = fopen("__lddmc_test_bdd", "w+"); |
||||
|
int N = 20; |
||||
|
MDD rnd[N]; |
||||
|
size_t a[N]; |
||||
|
char sha[N][65]; |
||||
|
int j; |
||||
|
for (j=0;j<N;j++) rnd[j] = CALL(random_ldd, 5, 500); |
||||
|
for (j=0;j<N;j++) lddmc_getsha(rnd[j], sha[j]); |
||||
|
for (j=0;j<N;j++) { a[j] = lddmc_serialize_add(rnd[j]); lddmc_serialize_tofile(f); } |
||||
|
for (j=0;j<N;j++) test_assert(a[j] == lddmc_serialize_get(rnd[j])); |
||||
|
for (j=0;j<N;j++) test_assert(rnd[j] == lddmc_serialize_get_reversed(a[j])); |
||||
|
fseek(f, 0, SEEK_SET); |
||||
|
lddmc_serialize_reset(); |
||||
|
|
||||
|
sylvan_quit(); |
||||
|
sylvan_init_package(1LL<<24, 1LL<<24, 1LL<<24, 1LL<<24); |
||||
|
sylvan_init_ldd(); |
||||
|
sylvan_gc_disable(); |
||||
|
|
||||
|
for (j=0;j<N;j++) lddmc_serialize_fromfile(f); |
||||
|
fclose(f); |
||||
|
unlink("__lddmc_test_bdd"); |
||||
|
|
||||
|
for (j=0;j<N;j++) rnd[j] = lddmc_serialize_get_reversed(a[j]); |
||||
|
char sha2[N][65]; |
||||
|
for (j=0;j<N;j++) lddmc_getsha(rnd[j], sha2[j]); |
||||
|
for (j=0;j<N;j++) test_assert(memcmp(sha[j], sha2[j], 64)==0); |
||||
|
|
||||
|
lddmc_serialize_reset(); |
||||
|
} |
||||
|
|
||||
|
sylvan_quit(); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int runtests(int threads) |
||||
|
{ |
||||
|
lace_init(threads, 100000); |
||||
|
lace_startup(0, NULL, NULL); |
||||
|
|
||||
|
printf(BOLD "Testing LDDMC... "); |
||||
|
fflush(stdout); |
||||
|
if (test_lddmc()) return 1; |
||||
|
printf(LGREEN "success" NC "!\n"); |
||||
|
|
||||
|
printf(NC "Testing garbage collection... "); |
||||
|
fflush(stdout); |
||||
|
sylvan_init_package(1LL<<14, 1LL<<14, 1LL<<20, 1LL<<20); |
||||
|
sylvan_init_bdd(1); |
||||
|
sylvan_gc_enable(); |
||||
|
if (test_gc(threads)) return 1; |
||||
|
sylvan_quit(); |
||||
|
printf(LGREEN "success" NC "!\n"); |
||||
|
|
||||
|
lace_exit(); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int main(int argc, char **argv) |
||||
|
{ |
||||
|
int threads = 2; |
||||
|
if (argc > 1) sscanf(argv[1], "%d", &threads); |
||||
|
|
||||
|
if (runtests(threads)) exit(1); |
||||
|
printf(NC); |
||||
|
exit(0); |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
#ifndef test_assert |
||||
|
#define test_assert(expr) do { \ |
||||
|
if (!(expr)) \ |
||||
|
{ \ |
||||
|
fprintf(stderr, \ |
||||
|
"file %s: line %d (%s): precondition `%s' failed.\n", \ |
||||
|
__FILE__, \ |
||||
|
__LINE__, \ |
||||
|
__PRETTY_FUNCTION__, \ |
||||
|
#expr); \ |
||||
|
return 1; \ |
||||
|
} } while(0) |
||||
|
#endif |
@ -0,0 +1,331 @@ |
|||||
|
#include <stdio.h> |
||||
|
#include <stdint.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <pthread.h> |
||||
|
#include <unistd.h> |
||||
|
#include <time.h> |
||||
|
#include <sys/types.h> |
||||
|
#include <sys/time.h> |
||||
|
#include <inttypes.h> |
||||
|
|
||||
|
#include "llmsset.h" |
||||
|
#include "sylvan.h" |
||||
|
#include "test_assert.h" |
||||
|
|
||||
|
__thread uint64_t seed = 1; |
||||
|
|
||||
|
uint64_t |
||||
|
xorshift_rand(void) |
||||
|
{ |
||||
|
uint64_t x = seed; |
||||
|
if (seed == 0) seed = rand(); |
||||
|
x ^= x >> 12; |
||||
|
x ^= x << 25; |
||||
|
x ^= x >> 27; |
||||
|
seed = x; |
||||
|
return x * 2685821657736338717LL; |
||||
|
} |
||||
|
|
||||
|
double |
||||
|
uniform_deviate(uint64_t seed) |
||||
|
{ |
||||
|
return seed * (1.0 / (0xffffffffffffffffL + 1.0)); |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
rng(int low, int high) |
||||
|
{ |
||||
|
return low + uniform_deviate(xorshift_rand()) * (high-low); |
||||
|
} |
||||
|
|
||||
|
static inline BDD |
||||
|
make_random(int i, int j) |
||||
|
{ |
||||
|
if (i == j) return rng(0, 2) ? sylvan_true : sylvan_false; |
||||
|
|
||||
|
BDD yes = make_random(i+1, j); |
||||
|
BDD no = make_random(i+1, j); |
||||
|
BDD result = sylvan_invalid; |
||||
|
|
||||
|
switch(rng(0, 4)) { |
||||
|
case 0: |
||||
|
result = no; |
||||
|
sylvan_deref(yes); |
||||
|
break; |
||||
|
case 1: |
||||
|
result = yes; |
||||
|
sylvan_deref(no); |
||||
|
break; |
||||
|
case 2: |
||||
|
result = sylvan_ref(sylvan_makenode(i, yes, no)); |
||||
|
sylvan_deref(no); |
||||
|
sylvan_deref(yes); |
||||
|
break; |
||||
|
case 3: |
||||
|
default: |
||||
|
result = sylvan_ref(sylvan_makenode(i, no, yes)); |
||||
|
sylvan_deref(no); |
||||
|
sylvan_deref(yes); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
int testEqual(BDD a, BDD b) |
||||
|
{ |
||||
|
if (a == b) return 1; |
||||
|
|
||||
|
if (a == sylvan_invalid) { |
||||
|
fprintf(stderr, "a is invalid!\n"); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
if (b == sylvan_invalid) { |
||||
|
fprintf(stderr, "b is invalid!\n"); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
fprintf(stderr, "a and b are not equal!\n"); |
||||
|
|
||||
|
sylvan_fprint(stderr, a);fprintf(stderr, "\n"); |
||||
|
sylvan_fprint(stderr, b);fprintf(stderr, "\n"); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
test_bdd() |
||||
|
{ |
||||
|
test_assert(sylvan_makenode(sylvan_ithvar(1), sylvan_true, sylvan_true) == sylvan_not(sylvan_makenode(sylvan_ithvar(1), sylvan_false, sylvan_false))); |
||||
|
test_assert(sylvan_makenode(sylvan_ithvar(1), sylvan_false, sylvan_true) == sylvan_not(sylvan_makenode(sylvan_ithvar(1), sylvan_true, sylvan_false))); |
||||
|
test_assert(sylvan_makenode(sylvan_ithvar(1), sylvan_true, sylvan_false) == sylvan_not(sylvan_makenode(sylvan_ithvar(1), sylvan_false, sylvan_true))); |
||||
|
test_assert(sylvan_makenode(sylvan_ithvar(1), sylvan_false, sylvan_false) == sylvan_not(sylvan_makenode(sylvan_ithvar(1), sylvan_true, sylvan_true))); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
test_cube() |
||||
|
{ |
||||
|
LACE_ME; |
||||
|
BDDSET vars = sylvan_set_fromarray(((BDDVAR[]){1,2,3,4,6,8}), 6); |
||||
|
|
||||
|
uint8_t cube[6], check[6]; |
||||
|
int i, j; |
||||
|
for (i=0;i<6;i++) cube[i] = rng(0,3); |
||||
|
BDD bdd = sylvan_cube(vars, cube); |
||||
|
|
||||
|
sylvan_sat_one(bdd, vars, check); |
||||
|
for (i=0; i<6;i++) test_assert(cube[i] == check[i] || (cube[i] == 2 && check[i] == 0)); |
||||
|
|
||||
|
BDD picked = sylvan_pick_cube(bdd); |
||||
|
test_assert(testEqual(sylvan_and(picked, bdd), picked)); |
||||
|
|
||||
|
BDD t1 = sylvan_cube(vars, ((uint8_t[]){1,1,2,2,0,0})); |
||||
|
BDD t2 = sylvan_cube(vars, ((uint8_t[]){1,1,1,0,0,2})); |
||||
|
test_assert(testEqual(sylvan_union_cube(t1, vars, ((uint8_t[]){1,1,1,0,0,2})), sylvan_or(t1, t2))); |
||||
|
t2 = sylvan_cube(vars, ((uint8_t[]){2,2,2,1,1,0})); |
||||
|
test_assert(testEqual(sylvan_union_cube(t1, vars, ((uint8_t[]){2,2,2,1,1,0})), sylvan_or(t1, t2))); |
||||
|
t2 = sylvan_cube(vars, ((uint8_t[]){1,1,1,0,0,0})); |
||||
|
test_assert(testEqual(sylvan_union_cube(t1, vars, ((uint8_t[]){1,1,1,0,0,0})), sylvan_or(t1, t2))); |
||||
|
|
||||
|
bdd = make_random(1, 16); |
||||
|
for (j=0;j<10;j++) { |
||||
|
for (i=0;i<6;i++) cube[i] = rng(0,3); |
||||
|
BDD c = sylvan_cube(vars, cube); |
||||
|
test_assert(sylvan_union_cube(bdd, vars, cube) == sylvan_or(bdd, c)); |
||||
|
} |
||||
|
|
||||
|
for (i=0;i<10;i++) { |
||||
|
picked = sylvan_pick_cube(bdd); |
||||
|
test_assert(testEqual(sylvan_and(picked, bdd), picked)); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
test_operators() |
||||
|
{ |
||||
|
// We need to test: xor, and, or, nand, nor, imp, biimp, invimp, diff, less |
||||
|
LACE_ME; |
||||
|
|
||||
|
//int i; |
||||
|
BDD a = sylvan_ithvar(1); |
||||
|
BDD b = sylvan_ithvar(2); |
||||
|
BDD one = make_random(1, 12); |
||||
|
BDD two = make_random(6, 24); |
||||
|
|
||||
|
// Test or |
||||
|
test_assert(testEqual(sylvan_or(a, b), sylvan_makenode(1, b, sylvan_true))); |
||||
|
test_assert(testEqual(sylvan_or(a, b), sylvan_or(b, a))); |
||||
|
test_assert(testEqual(sylvan_or(one, two), sylvan_or(two, one))); |
||||
|
|
||||
|
// Test and |
||||
|
test_assert(testEqual(sylvan_and(a, b), sylvan_makenode(1, sylvan_false, b))); |
||||
|
test_assert(testEqual(sylvan_and(a, b), sylvan_and(b, a))); |
||||
|
test_assert(testEqual(sylvan_and(one, two), sylvan_and(two, one))); |
||||
|
|
||||
|
// Test xor |
||||
|
test_assert(testEqual(sylvan_xor(a, b), sylvan_makenode(1, b, sylvan_not(b)))); |
||||
|
test_assert(testEqual(sylvan_xor(a, b), sylvan_xor(a, b))); |
||||
|
test_assert(testEqual(sylvan_xor(a, b), sylvan_xor(b, a))); |
||||
|
test_assert(testEqual(sylvan_xor(one, two), sylvan_xor(two, one))); |
||||
|
test_assert(testEqual(sylvan_xor(a, b), sylvan_ite(a, sylvan_not(b), b))); |
||||
|
|
||||
|
// Test diff |
||||
|
test_assert(testEqual(sylvan_diff(a, b), sylvan_diff(a, b))); |
||||
|
test_assert(testEqual(sylvan_diff(a, b), sylvan_diff(a, sylvan_and(a, b)))); |
||||
|
test_assert(testEqual(sylvan_diff(a, b), sylvan_and(a, sylvan_not(b)))); |
||||
|
test_assert(testEqual(sylvan_diff(a, b), sylvan_ite(b, sylvan_false, a))); |
||||
|
test_assert(testEqual(sylvan_diff(one, two), sylvan_diff(one, two))); |
||||
|
test_assert(testEqual(sylvan_diff(one, two), sylvan_diff(one, sylvan_and(one, two)))); |
||||
|
test_assert(testEqual(sylvan_diff(one, two), sylvan_and(one, sylvan_not(two)))); |
||||
|
test_assert(testEqual(sylvan_diff(one, two), sylvan_ite(two, sylvan_false, one))); |
||||
|
|
||||
|
// Test biimp |
||||
|
test_assert(testEqual(sylvan_biimp(a, b), sylvan_makenode(1, sylvan_not(b), b))); |
||||
|
test_assert(testEqual(sylvan_biimp(a, b), sylvan_biimp(b, a))); |
||||
|
test_assert(testEqual(sylvan_biimp(one, two), sylvan_biimp(two, one))); |
||||
|
|
||||
|
// Test nand / and |
||||
|
test_assert(testEqual(sylvan_not(sylvan_and(a, b)), sylvan_nand(b, a))); |
||||
|
test_assert(testEqual(sylvan_not(sylvan_and(one, two)), sylvan_nand(two, one))); |
||||
|
|
||||
|
// Test nor / or |
||||
|
test_assert(testEqual(sylvan_not(sylvan_or(a, b)), sylvan_nor(b, a))); |
||||
|
test_assert(testEqual(sylvan_not(sylvan_or(one, two)), sylvan_nor(two, one))); |
||||
|
|
||||
|
// Test xor / biimp |
||||
|
test_assert(testEqual(sylvan_xor(a, b), sylvan_not(sylvan_biimp(b, a)))); |
||||
|
test_assert(testEqual(sylvan_xor(one, two), sylvan_not(sylvan_biimp(two, one)))); |
||||
|
|
||||
|
// Test imp |
||||
|
test_assert(testEqual(sylvan_imp(a, b), sylvan_ite(a, b, sylvan_true))); |
||||
|
test_assert(testEqual(sylvan_imp(one, two), sylvan_ite(one, two, sylvan_true))); |
||||
|
test_assert(testEqual(sylvan_imp(one, two), sylvan_not(sylvan_diff(one, two)))); |
||||
|
test_assert(testEqual(sylvan_invimp(one, two), sylvan_not(sylvan_less(one, two)))); |
||||
|
test_assert(testEqual(sylvan_imp(a, b), sylvan_invimp(b, a))); |
||||
|
test_assert(testEqual(sylvan_imp(one, two), sylvan_invimp(two, one))); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
test_relprod() |
||||
|
{ |
||||
|
LACE_ME; |
||||
|
|
||||
|
BDDVAR vars[] = {0,2,4}; |
||||
|
BDDVAR all_vars[] = {0,1,2,3,4,5}; |
||||
|
|
||||
|
BDDSET vars_set = sylvan_set_fromarray(vars, 3); |
||||
|
BDDSET all_vars_set = sylvan_set_fromarray(all_vars, 6); |
||||
|
|
||||
|
BDD s, t, next, prev; |
||||
|
BDD zeroes, ones; |
||||
|
|
||||
|
// transition relation: 000 --> 111 and !000 --> 000 |
||||
|
t = sylvan_false; |
||||
|
t = sylvan_union_cube(t, all_vars_set, ((uint8_t[]){0,1,0,1,0,1})); |
||||
|
t = sylvan_union_cube(t, all_vars_set, ((uint8_t[]){1,0,2,0,2,0})); |
||||
|
t = sylvan_union_cube(t, all_vars_set, ((uint8_t[]){2,0,1,0,2,0})); |
||||
|
t = sylvan_union_cube(t, all_vars_set, ((uint8_t[]){2,0,2,0,1,0})); |
||||
|
|
||||
|
s = sylvan_cube(vars_set, (uint8_t[]){0,0,1}); |
||||
|
zeroes = sylvan_cube(vars_set, (uint8_t[]){0,0,0}); |
||||
|
ones = sylvan_cube(vars_set, (uint8_t[]){1,1,1}); |
||||
|
|
||||
|
next = sylvan_relnext(s, t, all_vars_set); |
||||
|
prev = sylvan_relprev(t, next, all_vars_set); |
||||
|
test_assert(next == zeroes); |
||||
|
test_assert(prev == sylvan_not(zeroes)); |
||||
|
|
||||
|
next = sylvan_relnext(next, t, all_vars_set); |
||||
|
prev = sylvan_relprev(t, next, all_vars_set); |
||||
|
test_assert(next == ones); |
||||
|
test_assert(prev == zeroes); |
||||
|
|
||||
|
t = sylvan_cube(all_vars_set, (uint8_t[]){0,0,0,0,0,1}); |
||||
|
test_assert(sylvan_relprev(t, s, all_vars_set) == zeroes); |
||||
|
test_assert(sylvan_relprev(t, sylvan_not(s), all_vars_set) == sylvan_false); |
||||
|
test_assert(sylvan_relnext(s, t, all_vars_set) == sylvan_false); |
||||
|
test_assert(sylvan_relnext(zeroes, t, all_vars_set) == s); |
||||
|
|
||||
|
t = sylvan_cube(all_vars_set, (uint8_t[]){0,0,0,0,0,2}); |
||||
|
test_assert(sylvan_relprev(t, s, all_vars_set) == zeroes); |
||||
|
test_assert(sylvan_relprev(t, zeroes, all_vars_set) == zeroes); |
||||
|
test_assert(sylvan_relnext(sylvan_not(zeroes), t, all_vars_set) == sylvan_false); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
test_compose() |
||||
|
{ |
||||
|
LACE_ME; |
||||
|
|
||||
|
BDD a = sylvan_ithvar(1); |
||||
|
BDD b = sylvan_ithvar(2); |
||||
|
|
||||
|
BDD a_or_b = sylvan_or(a, b); |
||||
|
|
||||
|
BDD one = make_random(3, 16); |
||||
|
BDD two = make_random(8, 24); |
||||
|
|
||||
|
BDDMAP map = sylvan_map_empty(); |
||||
|
|
||||
|
map = sylvan_map_add(map, 1, one); |
||||
|
map = sylvan_map_add(map, 2, two); |
||||
|
|
||||
|
test_assert(sylvan_map_key(map) == 1); |
||||
|
test_assert(sylvan_map_value(map) == one); |
||||
|
test_assert(sylvan_map_key(sylvan_map_next(map)) == 2); |
||||
|
test_assert(sylvan_map_value(sylvan_map_next(map)) == two); |
||||
|
|
||||
|
test_assert(testEqual(one, sylvan_compose(a, map))); |
||||
|
test_assert(testEqual(two, sylvan_compose(b, map))); |
||||
|
|
||||
|
test_assert(testEqual(sylvan_or(one, two), sylvan_compose(a_or_b, map))); |
||||
|
|
||||
|
map = sylvan_map_add(map, 2, one); |
||||
|
test_assert(testEqual(sylvan_compose(a_or_b, map), one)); |
||||
|
|
||||
|
map = sylvan_map_add(map, 1, two); |
||||
|
test_assert(testEqual(sylvan_or(one, two), sylvan_compose(a_or_b, map))); |
||||
|
|
||||
|
test_assert(testEqual(sylvan_and(one, two), sylvan_compose(sylvan_and(a, b), map))); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int runtests() |
||||
|
{ |
||||
|
// we are not testing garbage collection |
||||
|
sylvan_gc_disable(); |
||||
|
|
||||
|
if (test_bdd()) return 1; |
||||
|
for (int j=0;j<10;j++) if (test_cube()) return 1; |
||||
|
for (int j=0;j<10;j++) if (test_relprod()) return 1; |
||||
|
for (int j=0;j<10;j++) if (test_compose()) return 1; |
||||
|
for (int j=0;j<10;j++) if (test_operators()) return 1; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int main() |
||||
|
{ |
||||
|
// Standard Lace initialization with 1 worker |
||||
|
lace_init(1, 0); |
||||
|
lace_startup(0, NULL, NULL); |
||||
|
|
||||
|
// Simple Sylvan initialization, also initialize BDD support |
||||
|
sylvan_init_package(1LL<<20, 1LL<<20, 1LL<<16, 1LL<<16); |
||||
|
sylvan_init_bdd(1); |
||||
|
|
||||
|
int res = runtests(); |
||||
|
|
||||
|
sylvan_quit(); |
||||
|
lace_exit(); |
||||
|
|
||||
|
return res; |
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
/**
|
||||
|
* Just a small test file to ensure that Sylvan can compile in C++ |
||||
|
*/ |
||||
|
|
||||
|
#include <assert.h>
|
||||
|
#include <sylvan.h>
|
||||
|
#include <sylvan_obj.hpp>
|
||||
|
|
||||
|
#include "test_assert.h"
|
||||
|
|
||||
|
using namespace sylvan; |
||||
|
|
||||
|
int runtest() |
||||
|
{ |
||||
|
Bdd one = Bdd::bddOne(); |
||||
|
Bdd zero = Bdd::bddZero(); |
||||
|
|
||||
|
test_assert(one != zero); |
||||
|
test_assert(one == !zero); |
||||
|
|
||||
|
Bdd v1 = Bdd::bddVar(1); |
||||
|
Bdd v2 = Bdd::bddVar(2); |
||||
|
|
||||
|
Bdd t = v1 + v2; |
||||
|
|
||||
|
BddMap map; |
||||
|
map.put(2, t); |
||||
|
|
||||
|
test_assert(v2.Compose(map) == (v1 + v2)); |
||||
|
test_assert((t * v2) == v2); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int main() |
||||
|
{ |
||||
|
// Standard Lace initialization with 1 worker
|
||||
|
lace_init(1, 0); |
||||
|
lace_startup(0, NULL, NULL); |
||||
|
|
||||
|
// Simple Sylvan initialization, also initialize BDD support
|
||||
|
sylvan_init_package(1LL<<16, 1LL<<16, 1LL<<16, 1LL<<16); |
||||
|
sylvan_init_bdd(1); |
||||
|
|
||||
|
int res = runtest(); |
||||
|
|
||||
|
sylvan_quit(); |
||||
|
lace_exit(); |
||||
|
|
||||
|
return res; |
||||
|
} |
Reference in new issue
xxxxxxxxxx