Browse Source

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
  1. 43
      .gitignore
  2. 100
      .travis.yml
  3. 50
      CMakeLists.txt
  4. 202
      LICENSE
  5. 5
      Makefile.am
  6. 97
      README.md
  7. 20
      cmake/FindGMP.cmake
  8. 21
      configure.ac
  9. 32
      examples/CMakeLists.txt
  10. 68
      examples/getrss.c
  11. 26
      examples/getrss.h
  12. 499
      examples/lddmc.c
  13. 616
      examples/mc.c
  14. 121
      examples/simple.cpp
  15. 5
      m4/.gitignore
  16. 72
      m4/m4_ax_check_compile_flag.m4
  17. BIN
      models/at.5.8-rgs.bdd
  18. BIN
      models/at.6.8-rgs.bdd
  19. BIN
      models/at.7.8-rgs.bdd
  20. BIN
      models/blocks.2.ldd
  21. BIN
      models/blocks.4.ldd
  22. BIN
      models/collision.4.9-rgs.bdd
  23. BIN
      models/collision.5.9-rgs.bdd
  24. BIN
      models/schedule_world.2.8-rgs.bdd
  25. BIN
      models/schedule_world.3.8-rgs.bdd
  26. 81
      src/CMakeLists.txt
  27. 39
      src/Makefile.am
  28. 398
      src/avl.h
  29. 1045
      src/lace.c
  30. 2743
      src/lace.h
  31. 564
      src/llmsset.c
  32. 202
      src/llmsset.h
  33. 598
      src/refs.c
  34. 77
      src/refs.h
  35. 1067
      src/sha2.c
  36. 197
      src/sha2.h
  37. 245
      src/stats.c
  38. 262
      src/stats.h
  39. 182
      src/sylvan.h
  40. 2820
      src/sylvan_bdd.c
  41. 423
      src/sylvan_bdd.h
  42. 3
      src/sylvan_bdd_storm.h
  43. 220
      src/sylvan_cache.c
  44. 113
      src/sylvan_cache.h
  45. 304
      src/sylvan_common.c
  46. 85
      src/sylvan_common.h
  47. 30
      src/sylvan_config.h
  48. 595
      src/sylvan_gmp.c
  49. 182
      src/sylvan_gmp.h
  50. 2560
      src/sylvan_ldd.c
  51. 288
      src/sylvan_ldd.h
  52. 2542
      src/sylvan_mtbdd.c
  53. 608
      src/sylvan_mtbdd.h
  54. 128
      src/sylvan_mtbdd_int.h
  55. 514
      src/sylvan_mtbdd_storm.c
  56. 111
      src/sylvan_mtbdd_storm.h
  57. 1039
      src/sylvan_obj.cpp
  58. 855
      src/sylvan_obj.hpp
  59. 3
      src/sylvan_obj_bdd_storm.hpp
  60. 51
      src/sylvan_obj_mtbdd_storm.hpp
  61. 141
      src/sylvan_obj_storm.cpp
  62. 35
      src/tls.h
  63. 5
      test/.gitignore
  64. 15
      test/CMakeLists.txt
  65. 350
      test/main.c
  66. 13
      test/test_assert.h
  67. 331
      test/test_basic.c
  68. 51
      test/test_cxx.cpp

43
.gitignore

@ -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

100
.travis.yml

@ -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

50
CMakeLists.txt

@ -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()

202
LICENSE

@ -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.

5
Makefile.am

@ -0,0 +1,5 @@
ACLOCAL_AMFLAGS = -I m4
AM_CFLAGS = -g -O2 -Wall -Wextra -Werror -std=gnu11
SUBDIRS = src

97
README.md

@ -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 &lt;var&gt; (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 &lt;bdd&gt; - requires that &lt;bdd&gt; is not constant `true` or `false`.
- `sylvan_high(bdd)`: follow high edge of &lt;bdd&gt;.
- `sylvan_low(bdd)`: follow low edge of &lt;bdd&gt;.
You need to manually reference BDDs that you want to keep during garbage collection:
- `sylvan_ref(bdd)`: add reference to &lt;bdd&gt;.
- `sylvan_deref(bdd)`: remove reference to &lt;bdd&gt;.
- `sylvan_protect(bddptr)`: add a pointer reference to the BDD variable &lt;bddptr&gt;
- `sylvan_unprotect(bddptr)`: remove a pointer reference to the BDD variable &lt;bddptr&gt;
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 &lt;bdd&gt;.
- `sylvan_ite(a,b,c)`: calculate 'if &lt;a&gt; then &lt;b&gt; else &lt;c&gt;'.
- `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 &lt;bdd&gt; with respect to variables &lt;vars&gt;. Here, &lt;vars&gt; is a conjunction of literals.
- `sylvan_forall(bdd, vars)`: universal quantification of &lt;bdd&gt; with respect to variables &lt;vars&gt;. Here, &lt;vars&gt; 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`.

20
cmake/FindGMP.cmake

@ -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)

21
configure.ac

@ -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

32
examples/CMakeLists.txt

@ -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()

68
examples/getrss.c

@ -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
}

26
examples/getrss.h

@ -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

499
examples/lddmc.c

@ -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;
}

616
examples/mc.c

@ -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;
}

121
examples/simple.cpp

@ -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
}

5
m4/.gitignore

@ -0,0 +1,5 @@
# Ignore everything in this directory
*
# Except:
!.gitignore
!m4_ax_check_compile_flag.m4

72
m4/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

BIN
models/at.5.8-rgs.bdd

BIN
models/at.6.8-rgs.bdd

BIN
models/at.7.8-rgs.bdd

BIN
models/blocks.2.ldd

BIN
models/blocks.4.ldd

BIN
models/collision.4.9-rgs.bdd

BIN
models/collision.5.9-rgs.bdd

BIN
models/schedule_world.2.8-rgs.bdd

BIN
models/schedule_world.3.8-rgs.bdd

81
src/CMakeLists.txt

@ -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")

39
src/Makefile.am

@ -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

398
src/avl.h

@ -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

2743
src/lace.h
File diff suppressed because it is too large
View File

564
src/llmsset.c

@ -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;
}

202
src/llmsset.h

@ -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

598
src/refs.c

@ -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;
}

77
src/refs.h

@ -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

197
src/sha2.h

@ -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__ */

245
src/stats.c

@ -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

262
src/stats.h

@ -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

182
src/sylvan.h

@ -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

423
src/sylvan_bdd.h

@ -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

3
src/sylvan_bdd_storm.h

@ -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)

220
src/sylvan_cache.c

@ -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;
}

113
src/sylvan_cache.h

@ -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

304
src/sylvan_common.c

@ -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);
}

85
src/sylvan_common.h

@ -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

30
src/sylvan_config.h

@ -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

595
src/sylvan_gmp.c

@ -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;
}

182
src/sylvan_gmp.h

@ -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

288
src/sylvan_ldd.h

@ -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

608
src/sylvan_mtbdd.h

@ -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

128
src/sylvan_mtbdd_int.h

@ -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

514
src/sylvan_mtbdd_storm.c

@ -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;
}

111
src/sylvan_mtbdd_storm.h

@ -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

855
src/sylvan_obj.hpp

@ -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

3
src/sylvan_obj_bdd_storm.hpp

@ -0,0 +1,3 @@
Mtbdd toDoubleMtbdd() const;
Mtbdd toInt64Mtbdd() const;
Mtbdd Ite(Mtbdd const& thenDd, Mtbdd const& elseDd) const;

51
src/sylvan_obj_mtbdd_storm.hpp

@ -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;

141
src/sylvan_obj_storm.cpp

@ -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);
}

35
src/tls.h

@ -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

5
test/.gitignore

@ -0,0 +1,5 @@
test
cmake_install.cmake
CMakeFiles
*.o
.libs

15
test/CMakeLists.txt

@ -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)

350
test/main.c

@ -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);
}

13
test/test_assert.h

@ -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

331
test/test_basic.c

@ -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;
}

51
test/test_cxx.cpp

@ -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;
}
Loading…
Cancel
Save