Browse Source
Squashed 'resources/3rdparty/sylvan/' content from commit d91f6ac
Squashed 'resources/3rdparty/sylvan/' content from commit d91f6ac
git-subtree-dir: resources/3rdparty/sylvan
git-subtree-split: d91f6acb55
tempestpy_adaptions
dehnert
9 years ago
commit
5934a42898
68 changed files with 24092 additions and 0 deletions
-
43.gitignore
-
100.travis.yml
-
50CMakeLists.txt
-
202LICENSE
-
5Makefile.am
-
97README.md
-
20cmake/FindGMP.cmake
-
21configure.ac
-
32examples/CMakeLists.txt
-
68examples/getrss.c
-
26examples/getrss.h
-
499examples/lddmc.c
-
616examples/mc.c
-
121examples/simple.cpp
-
5m4/.gitignore
-
72m4/m4_ax_check_compile_flag.m4
-
BINmodels/at.5.8-rgs.bdd
-
BINmodels/at.6.8-rgs.bdd
-
BINmodels/at.7.8-rgs.bdd
-
BINmodels/blocks.2.ldd
-
BINmodels/blocks.4.ldd
-
BINmodels/collision.4.9-rgs.bdd
-
BINmodels/collision.5.9-rgs.bdd
-
BINmodels/schedule_world.2.8-rgs.bdd
-
BINmodels/schedule_world.3.8-rgs.bdd
-
81src/CMakeLists.txt
-
39src/Makefile.am
-
398src/avl.h
-
1045src/lace.c
-
2743src/lace.h
-
564src/llmsset.c
-
202src/llmsset.h
-
598src/refs.c
-
77src/refs.h
-
1067src/sha2.c
-
197src/sha2.h
-
245src/stats.c
-
262src/stats.h
-
182src/sylvan.h
-
2820src/sylvan_bdd.c
-
423src/sylvan_bdd.h
-
3src/sylvan_bdd_storm.h
-
220src/sylvan_cache.c
-
113src/sylvan_cache.h
-
304src/sylvan_common.c
-
85src/sylvan_common.h
-
30src/sylvan_config.h
-
595src/sylvan_gmp.c
-
182src/sylvan_gmp.h
-
2560src/sylvan_ldd.c
-
288src/sylvan_ldd.h
-
2542src/sylvan_mtbdd.c
-
608src/sylvan_mtbdd.h
-
128src/sylvan_mtbdd_int.h
-
514src/sylvan_mtbdd_storm.c
-
111src/sylvan_mtbdd_storm.h
-
1039src/sylvan_obj.cpp
-
855src/sylvan_obj.hpp
-
3src/sylvan_obj_bdd_storm.hpp
-
51src/sylvan_obj_mtbdd_storm.hpp
-
141src/sylvan_obj_storm.cpp
-
35src/tls.h
-
5test/.gitignore
-
15test/CMakeLists.txt
-
350test/main.c
-
13test/test_assert.h
-
331test/test_basic.c
-
51test/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 [![Build Status](https://travis-ci.org/trolando/sylvan.svg?branch=master)](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
src/lace.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2743
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
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
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
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
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
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; |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue