105 changed files with 7402 additions and 4191 deletions
-
5resources/3rdparty/CMakeLists.txt
-
36resources/3rdparty/sylvan/.gitignore
-
82resources/3rdparty/sylvan/.travis.yml
-
53resources/3rdparty/sylvan/CHANGELOG.md
-
86resources/3rdparty/sylvan/CMakeLists.txt
-
0resources/3rdparty/sylvan/LICENSE
-
5resources/3rdparty/sylvan/Makefile.am
-
111resources/3rdparty/sylvan/README.md
-
68resources/3rdparty/sylvan/cmake/FindGMP.cmake
-
24resources/3rdparty/sylvan/cmake/FindHwloc.cmake
-
72resources/3rdparty/sylvan/cmake/FindSphinx.cmake
-
76resources/3rdparty/sylvan/cmake/UpdateGHPages.cmake
-
21resources/3rdparty/sylvan/configure.ac
-
58resources/3rdparty/sylvan/docs/conf.py.in
-
282resources/3rdparty/sylvan/docs/index.rst
-
20resources/3rdparty/sylvan/examples/CMakeLists.txt
-
0resources/3rdparty/sylvan/examples/getrss.c
-
0resources/3rdparty/sylvan/examples/getrss.h
-
777resources/3rdparty/sylvan/examples/ldd2bdd.c
-
83resources/3rdparty/sylvan/examples/lddmc.c
-
23resources/3rdparty/sylvan/examples/mc.c
-
328resources/3rdparty/sylvan/examples/nqueens.c
-
7resources/3rdparty/sylvan/examples/simple.cpp
-
127resources/3rdparty/sylvan/examples/storm.cpp
-
5resources/3rdparty/sylvan/m4/.gitignore
-
72resources/3rdparty/sylvan/m4/m4_ax_check_compile_flag.m4
-
0resources/3rdparty/sylvan/models/at.5.8-rgs.bdd
-
0resources/3rdparty/sylvan/models/at.6.8-rgs.bdd
-
0resources/3rdparty/sylvan/models/at.7.8-rgs.bdd
-
0resources/3rdparty/sylvan/models/blocks.2.ldd
-
0resources/3rdparty/sylvan/models/blocks.4.ldd
-
0resources/3rdparty/sylvan/models/collision.4.9-rgs.bdd
-
0resources/3rdparty/sylvan/models/collision.5.9-rgs.bdd
-
0resources/3rdparty/sylvan/models/schedule_world.2.8-rgs.bdd
-
0resources/3rdparty/sylvan/models/schedule_world.3.8-rgs.bdd
-
111resources/3rdparty/sylvan/src/CMakeLists.txt
-
39resources/3rdparty/sylvan/src/Makefile.am
-
5resources/3rdparty/sylvan/src/avl.h
-
352resources/3rdparty/sylvan/src/lace.c
-
139resources/3rdparty/sylvan/src/lace.h
-
15resources/3rdparty/sylvan/src/sha2.c
-
0resources/3rdparty/sylvan/src/sha2.h
-
245resources/3rdparty/sylvan/src/stats.c
-
11resources/3rdparty/sylvan/src/storm_function_wrapper.cpp
-
2resources/3rdparty/sylvan/src/storm_function_wrapper.h
-
162resources/3rdparty/sylvan/src/sylvan.h
-
1478resources/3rdparty/sylvan/src/sylvan_bdd.c
-
277resources/3rdparty/sylvan/src/sylvan_bdd.h
-
87resources/3rdparty/sylvan/src/sylvan_bdd_int.h
-
6resources/3rdparty/sylvan/src/sylvan_bdd_storm.c
-
101resources/3rdparty/sylvan/src/sylvan_cache.c
-
37resources/3rdparty/sylvan/src/sylvan_cache.h
-
415resources/3rdparty/sylvan/src/sylvan_common.c
-
242resources/3rdparty/sylvan/src/sylvan_common.h
-
0resources/3rdparty/sylvan/src/sylvan_config.h
-
211resources/3rdparty/sylvan/src/sylvan_gmp.c
-
16resources/3rdparty/sylvan/src/sylvan_gmp.h
-
106resources/3rdparty/sylvan/src/sylvan_int.h
-
491resources/3rdparty/sylvan/src/sylvan_ldd.c
-
9resources/3rdparty/sylvan/src/sylvan_ldd.h
-
125resources/3rdparty/sylvan/src/sylvan_ldd_int.h
-
266resources/3rdparty/sylvan/src/sylvan_mt.c
-
132resources/3rdparty/sylvan/src/sylvan_mt.h
-
1437resources/3rdparty/sylvan/src/sylvan_mtbdd.c
-
408resources/3rdparty/sylvan/src/sylvan_mtbdd.h
-
74resources/3rdparty/sylvan/src/sylvan_mtbdd_int.h
-
134resources/3rdparty/sylvan/src/sylvan_mtbdd_storm.c
-
3resources/3rdparty/sylvan/src/sylvan_mtbdd_storm.h
-
68resources/3rdparty/sylvan/src/sylvan_obj.cpp
-
59resources/3rdparty/sylvan/src/sylvan_obj.hpp
-
6resources/3rdparty/sylvan/src/sylvan_obj_mtbdd_storm.hpp
-
19resources/3rdparty/sylvan/src/sylvan_obj_storm.cpp
-
1resources/3rdparty/sylvan/src/sylvan_obj_sylvan_storm.hpp
-
5resources/3rdparty/sylvan/src/sylvan_refs.c
-
3resources/3rdparty/sylvan/src/sylvan_refs.h
-
172resources/3rdparty/sylvan/src/sylvan_sl.c
-
70resources/3rdparty/sylvan/src/sylvan_sl.h
-
296resources/3rdparty/sylvan/src/sylvan_stats.c
-
179resources/3rdparty/sylvan/src/sylvan_stats.h
-
42resources/3rdparty/sylvan/src/sylvan_storm_rational_function.c
-
87resources/3rdparty/sylvan/src/sylvan_table.c
-
23resources/3rdparty/sylvan/src/sylvan_table.h
-
0resources/3rdparty/sylvan/src/sylvan_tls.h
-
10resources/3rdparty/sylvan/sylvan.pc.cmake.in
-
5resources/3rdparty/sylvan/test/.gitignore
-
7resources/3rdparty/sylvan/test/CMakeLists.txt
-
350resources/3rdparty/sylvan/test/main.c
-
0resources/3rdparty/sylvan/test/test_assert.h
-
260resources/3rdparty/sylvan/test/test_basic.c
-
13resources/3rdparty/sylvan/test/test_cxx.cpp
-
2src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp
-
2src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h
-
2src/storm/solver/SymbolicEliminationLinearEquationSolver.cpp
-
1src/storm/storage/bisimulation/BisimulationDecomposition.h
-
24src/storm/storage/dd/Bdd.cpp
-
21src/storm/storage/dd/Bdd.h
-
53src/storm/storage/dd/cudd/InternalCuddBdd.cpp
-
36src/storm/storage/dd/cudd/InternalCuddBdd.h
-
52src/storm/storage/dd/sylvan/InternalSylvanBdd.cpp
-
35src/storm/storage/dd/sylvan/InternalSylvanBdd.h
@ -0,0 +1,53 @@ |
|||
# Change Log |
|||
All notable changes to Sylvan will be documented in this file. |
|||
|
|||
## [Unreleased] |
|||
### Added |
|||
- The embedded work-stealing framework now explicitly checks for stack overflows and aborts with an appropriate error message written to stderr. |
|||
- New functions `sylvan_project` and `sylvan_and_project` for BDDs, a dual of existential quantification, where instead of the variables to remove, the given set of variables are the variables to keep. |
|||
|
|||
### Changed |
|||
- Rewritten initialization of Sylvan. Before the call to `sylvan_init_package`, table sizes must be initialized either using `sylvan_set_sizes` or with the new function `sylvan_set_limits`. This new function allows the user to set a maximum number of bytes allocated for the nodes table and for the operation cache. |
|||
|
|||
## [1.2.0] - 2017-02-03 |
|||
### Added |
|||
- Added documentation in the docs directory using Sphinx. Some documentation is removed from the README.md file. |
|||
|
|||
### Changed |
|||
- The custom terminal/leaf API is slightly modified. The `read_binary_cb` has a different signature to remove the dependency upon MTBDD functionality. |
|||
- The custom terminal/leaf API functions have been renamed and moved to a separate file. |
|||
- Lace has been updated with a new version. The new version has rewritten the hardware locality code that pins worker threads and memory. |
|||
|
|||
### Fixed |
|||
- A bug in `mtbdd_reader_readbinary` has been fixed. |
|||
|
|||
## [1.1.2] - 2017-01-11 |
|||
### Fixed |
|||
- The pkg-config file is slightly improved. |
|||
- A critical bug in `sylvan_collect` has been fixed. |
|||
|
|||
## [1.1.1] - 2017-01-10 |
|||
### Fixed |
|||
- The pkg-config file now includes hwloc as a requirement |
|||
|
|||
## [1.1.0] - 2017-01-09 |
|||
### Added |
|||
- This CHANGELOG file. |
|||
- Custom leaves can now implement custom callbacks for writing/reading to/from files. |
|||
- Implemented GMP leaf writing/reading to/from file. |
|||
- Method `mtbdd_eval_compose` for proper function composition (after partial evaluation). |
|||
- Method `mtbdd_enum_par_*` for parallel path enumeration. |
|||
- LDD methods `relprod` and `relprev` now support action labels (meta 5). |
|||
- Examples program `ldd2bdd` now converts LDD transition systems to BDDs transition systems. |
|||
- Methods `cache_get6` and `cache_put6` for operation cache entries that require two buckets. |
|||
- File `sylvan.pc` for pkg-config. |
|||
|
|||
### Changed |
|||
- The API to register a custom MTBDD leaf now requires multiple calls, which is better design for future extensions. |
|||
- When rehashing during garbage collection fails (due to finite length probe sequences), Sylvan now increases the probe sequence length instead of aborting with an error message. However, Sylvan will probably still abort due to the table being full, since this error is typically triggered when garbage collection does not remove many dead nodes. |
|||
|
|||
### Fixed |
|||
- Methods `mtbdd_enum_all_*` fixed and rewritten. |
|||
|
|||
### Removed |
|||
- We no longer use both autoconf makefiles and CMake. Instead, we removed the autoconf files and rely solely on CMake now. |
@ -1,5 +0,0 @@ |
|||
ACLOCAL_AMFLAGS = -I m4 |
|||
|
|||
AM_CFLAGS = -g -O2 -Wall -Wextra -Werror -std=gnu11 |
|||
|
|||
SUBDIRS = src |
@ -1,97 +1,40 @@ |
|||
Sylvan [](https://travis-ci.org/trolando/sylvan) |
|||
====== |
|||
Sylvan is a parallel (multi-core) BDD library in C. Sylvan allows both sequential and parallel BDD-based algorithms to benefit from parallelism. Sylvan uses the work-stealing framework Lace and a scalable lockless hashtable to implement scalable multi-core BDD operations. |
|||
Sylvan is a parallel (multi-core) MTBDD library written in C. Sylvan |
|||
implements parallelized operations on BDDs, MTBDDs and LDDs. Both |
|||
sequential and parallel BDD-based algorithms can benefit from |
|||
parallelism. Sylvan uses the work-stealing framework Lace and parallel |
|||
datastructures to implement scalable multi-core operations on decision |
|||
diagrams. |
|||
|
|||
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. |
|||
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 |
|||
was funded by NWO, and (© 2016-2017) by the [Formal Methods and Verification](http://fmv.jku.at/) |
|||
group at the Johannes Kepler University Linz as part of the RiSE project. |
|||
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. |
|||
The main author of Sylvan is Tom van Dijk who can be reached via <tom@tvandijk.nl>. |
|||
Please let us know if you use Sylvan in your projects and if you need |
|||
decision diagram operations that are currently not implemented in Sylvan. |
|||
|
|||
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 |
|||
The main repository of Sylvan is https://github.com/trolando/sylvan. A |
|||
mirror is available at https://github.com/utwente-fmt/sylvan. |
|||
|
|||
Publications |
|||
------------ |
|||
T. van Dijk and J. van de Pol (2015) [Sylvan: Multi-core Decision Diagrams](http://dx.doi.org/10.1007/978-3-662-46681-0_60). In: TACAS 2015, LNCS 9035. Springer. |
|||
|
|||
T. van Dijk and A.W. Laarman and J. van de Pol (2012) [Multi-Core BDD Operations for Symbolic Reachability](http://eprints.eemcs.utwente.nl/22166/). In: PDMC 2012, ENTCS. Elsevier. |
|||
|
|||
Usage |
|||
----- |
|||
Simple examples can be found in the `examples` subdirectory. The file `simple.cpp` contains a toy program that |
|||
uses the C++ objects to perform basic BDD manipulation. |
|||
The `mc.c` and `lddmc.c` programs are more advanced examples of symbolic model checking (with example models in the `models` subdirectory). |
|||
|
|||
Sylvan depends on the [work-stealing framework Lace](http://fmt.ewi.utwente.nl/tools/lace) for its implementation. Lace is embedded in the Sylvan distribution. |
|||
To use Sylvan, Lace must be initialized first. |
|||
For more details, see the comments in `src/sylvan.h`. |
|||
|
|||
### Basic functionality |
|||
|
|||
To create new BDDs, you can use: |
|||
- `sylvan_true`: representation of constant `true`. |
|||
- `sylvan_false`: representation of constant `false`. |
|||
- `sylvan_ithvar(var)`: representation of literal <var> (negated: `sylvan_nithvar(var)`) |
|||
|
|||
To follow the BDD edges and obtain the variable at the root of a BDD, you can use: |
|||
- `sylvan_var(bdd)`: obtain variable of the root node of <bdd> - requires that <bdd> is not constant `true` or `false`. |
|||
- `sylvan_high(bdd)`: follow high edge of <bdd>. |
|||
- `sylvan_low(bdd)`: follow low edge of <bdd>. |
|||
Bindings for other languages than C/C++ also exist: |
|||
|
|||
You need to manually reference BDDs that you want to keep during garbage collection: |
|||
- `sylvan_ref(bdd)`: add reference to <bdd>. |
|||
- `sylvan_deref(bdd)`: remove reference to <bdd>. |
|||
- `sylvan_protect(bddptr)`: add a pointer reference to the BDD variable <bddptr> |
|||
- `sylvan_unprotect(bddptr)`: remove a pointer reference to the BDD variable <bddptr> |
|||
- Java/JNI bindings: https://github.com/utwente-fmt/jsylvan |
|||
- Haskell bindings: https://github.com/adamwalker/sylvan-haskell |
|||
- Python bindings: https://github.com/johnyf/dd |
|||
|
|||
It is recommended to use `sylvan_protect` and `sylvan_unprotect`. |
|||
The C++ objects handle this automatically. |
|||
**Documentation** is available [at GitHub Pages](https://trolando.github.com/sylvan). |
|||
|
|||
The following 'primitives' are implemented: |
|||
- `sylvan_not(bdd)`: negation of <bdd>. |
|||
- `sylvan_ite(a,b,c)`: calculate 'if <a> then <b> else <c>'. |
|||
- `sylvan_and(a, b)`: calculate a and b |
|||
- `sylvan_or(a, b)`: calculate a or b |
|||
- `sylvan_nand(a, b)`: calculate not (a and b) |
|||
- `sylvan_nor(a, b)`: calculate not (a or b) |
|||
- `sylvan_imp(a, b)`: calculate a implies b |
|||
- `sylvan_invimp(a, b)`: calculate implies a |
|||
- `sylvan_xor(a, b)`: calculate a xor b |
|||
- `sylvan_equiv(a, b)`: calculate a = b |
|||
- `sylvan_diff(a, b)`: calculate a and not b |
|||
- `sylvan_less(a, b)`: calculate b and not a |
|||
- `sylvan_exists(bdd, vars)`: existential quantification of <bdd> with respect to variables <vars>. Here, <vars> is a conjunction of literals. |
|||
- `sylvan_forall(bdd, vars)`: universal quantification of <bdd> with respect to variables <vars>. Here, <vars> is a conjunction of literals. |
|||
|
|||
### Other BDD operations |
|||
|
|||
See `src/sylvan_bdd.h`, `src/sylvan_mtbdd.h` and `src/sylvan_ldd.h` for other implemented operations. |
|||
See `src/sylvan_obj.hpp` for the C++ interface. |
|||
|
|||
### Garbage collection |
|||
|
|||
Garbage collection is triggered when trying to insert a new node and no new bucket can be found within a reasonable upper bound. |
|||
Garbage collection is stop-the-world and all workers must cooperate on garbage collection. (Beware of deadlocks if you use Sylvan operations in critical sections!) |
|||
- `sylvan_gc()`: manually trigger garbage collection. |
|||
- `sylvan_gc_enable()`: enable garbage collection. |
|||
- `sylvan_gc_disable()`: disable garbage collection. |
|||
|
|||
### Table resizing |
|||
|
|||
During garbage collection, it is possible to resize the nodes table and the cache. |
|||
Sylvan provides two default implementations: an agressive version that resizes every time garbage collection is performed, |
|||
and a less agressive version that only resizes when at least half the table is full. |
|||
This can be configured in `src/sylvan_config.h` |
|||
It is not possible to decrease the size of the nodes table and the cache. |
|||
Publications |
|||
------------ |
|||
T. van Dijk (2016) [Sylvan: Multi-core Decision Diagrams](http://dx.doi.org/10.3990/1.9789036541602). PhD Thesis. |
|||
|
|||
### Dynamic reordering |
|||
T. van Dijk and J.C. van de Pol (2016) [Sylvan: Multi-core Framework for Decision Diagrams](http://dx.doi.org/10.1007/s10009-016-0433-2>). In: STTT (Special Issue), Springer. |
|||
|
|||
Dynamic reordening is currently not supported. |
|||
For now, we suggest users find a good static variable ordering. |
|||
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. |
|||
|
|||
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. |
|||
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. |
|||
|
|||
### 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`. |
@ -1,56 +1,24 @@ |
|||
# FindGMP.cmake can be found at https://code.google.com/p/origin/source/browse/trunk/cmake/FindGMP.cmake |
|||
# Copyright (c) 2008-2010 Kent State University |
|||
# Copyright (c) 2011-2012 Texas A&M University |
|||
# |
|||
# This file is distributed under the MIT License. See the accompanying file |
|||
# LICENSE.txt or http://www.opensource.org/licenses/mit-license.php for terms |
|||
# and conditions. |
|||
# Modified by David Korzeniewski to also find MPIR as an alternative. |
|||
# Try to find GMP |
|||
# Once done this will define: |
|||
# - GMP_FOUND - True if the system has GMP |
|||
# - GMP_INCLUDE_DIRS - include directories for compiling |
|||
# - GMP_LIBRARIES - libraries for linking |
|||
# - GMP_DEFINITIONS - cflags suggested by pkg-config |
|||
|
|||
# FIXME: How do I find the version of GMP that I want to use? |
|||
# What versions are available? |
|||
find_package(PkgConfig) |
|||
pkg_check_modules(PC_GMP QUIET gmp) |
|||
|
|||
# NOTE: GMP prefix is understood to be the path to the root of the GMP |
|||
# installation library. |
|||
set(GMP_PREFIX "" CACHE PATH "The path to the prefix of a GMP installation") |
|||
set(GMP_DEFINITIONS ${PC_GMP_CFLAGS_OTHER}) |
|||
|
|||
find_path(GMP_INCLUDE_DIR gmp.h |
|||
HINTS ${PC_GMP_INCLUDEDIR} ${PC_GMP_INCLUDE_DIRS}) |
|||
|
|||
find_path(GMP_INCLUDE_DIR gmp.h |
|||
PATHS ${GMP_PREFIX}/include /usr/include /usr/local/include) |
|||
find_library(GMP_LIBRARIES NAMES gmp libgmp |
|||
HINTS ${PC_GMP_LIBDIR} ${PC_GMP_LIBRARY_DIRS}) |
|||
|
|||
find_library(GMP_LIBRARY NAMES gmp |
|||
PATHS ${GMP_PREFIX}/lib /usr/lib /usr/local/lib) |
|||
include(FindPackageHandleStandardArgs) |
|||
# handle the QUIETLY and REQUIRED arguments and set GMP_FOUND to TRUE |
|||
# if all listed variables are TRUE |
|||
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARIES GMP_INCLUDE_DIR) |
|||
|
|||
find_library(GMP_MPIR_LIBRARY NAMES mpir |
|||
PATHS ${GMP_PREFIX}/lib /usr/lib /usr/local/lib) |
|||
|
|||
find_library(GMP_MPIRXX_LIBRARY NAMES mpirxx |
|||
PATHS ${GMP_PREFIX}/lib /usr/lib /usr/local/lib) |
|||
|
|||
|
|||
if(GMP_INCLUDE_DIR AND GMP_LIBRARY) |
|||
get_filename_component(GMP_LIBRARY_DIR ${GMP_LIBRARY} PATH) |
|||
set(GMP_FOUND TRUE) |
|||
endif() |
|||
|
|||
if(GMP_INCLUDE_DIR AND GMP_MPIR_LIBRARY AND GMP_MPIRXX_LIBRARY) |
|||
get_filename_component(GMP_MPIR_LIBRARY_DIR ${GMP_MPIR_LIBRARY} PATH) |
|||
get_filename_component(GMP_MPIRXX_LIBRARY_DIR ${GMP_MPIRXX_LIBRARY} PATH) |
|||
set(MPIR_FOUND TRUE) |
|||
endif() |
|||
|
|||
if(GMP_FOUND) |
|||
if(NOT GMP_FIND_QUIETLY) |
|||
MESSAGE(STATUS "Found GMP: ${GMP_LIBRARY}") |
|||
endif() |
|||
elseif(MPIR_FOUND) |
|||
if(NOT GMP_FIND_QUIETLY) |
|||
MESSAGE(STATUS "Found GMP alternative MPIR: ${GMP_MPIR_LIBRARY} and ${GMP_MPIRXX_LIBRARY}") |
|||
endif() |
|||
elseif(GMP_FOUND) |
|||
if(GMP_FIND_REQUIRED) |
|||
message(FATAL_ERROR "Could not find GMP") |
|||
endif() |
|||
endif() |
|||
|
|||
MARK_AS_ADVANCED(GMP_MPIRXX_LIBRARY GMP_MPIR_LIBRARY GMP_INCLUDE_DIR GMP_LIBRARY) |
|||
mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARIES) |
@ -0,0 +1,24 @@ |
|||
# Try to find HWLOC |
|||
# Once done this will define: |
|||
# - HWLOC_FOUND - True if the system has HWLOC |
|||
# - HWLOC_INCLUDE_DIRS - include directories for compiling |
|||
# - HWLOC_LIBRARIES - libraries for linking |
|||
# - HWLOC_DEFINITIONS - cflags suggested by pkg-config |
|||
|
|||
find_package(PkgConfig) |
|||
pkg_check_modules(PC_HWLOC QUIET hwloc) |
|||
|
|||
set(HWLOC_DEFINITIONS ${PC_HWLOC_FLAGS_OTHER}) |
|||
|
|||
find_path(HWLOC_INCLUDE_DIR hwloc.h |
|||
HINTS ${PC_HWLOC_INCLUDEDIR} ${PC_HWLOC_INCLUDE_DIRS}) |
|||
|
|||
find_library(HWLOC_LIBRARIES NAMES hwloc |
|||
HINTS ${PC_HWLOC_LIBDIR} ${PC_HWLOC_LIBRARY_DIRS}) |
|||
|
|||
include(FindPackageHandleStandardArgs) |
|||
# handle the QUIETLY and REQUIRED arguments and set HWLOC_FOUND to TRUE |
|||
# if all listed variables are TRUE |
|||
find_package_handle_standard_args(HWLOC DEFAULT_MSG HWLOC_LIBRARIES HWLOC_INCLUDE_DIR) |
|||
|
|||
mark_as_advanced(HWLOC_INCLUDE_DIR HWLOC_LIBRARIES) |
@ -0,0 +1,72 @@ |
|||
# This modules defines |
|||
# SPHINX_EXECUTABLE |
|||
# SPHINX_FOUND |
|||
|
|||
find_program(SPHINX_EXECUTABLE |
|||
NAMES sphinx-build sphinx-build2 |
|||
HINTS $ENV{SPHINX_DIR} |
|||
PATHS |
|||
/usr/bin |
|||
/usr/local/bin |
|||
/opt/local/bin |
|||
DOC "Sphinx documentation generator" |
|||
) |
|||
|
|||
include(FindPackageHandleStandardArgs) |
|||
|
|||
find_package_handle_standard_args(Sphinx DEFAULT_MSG SPHINX_EXECUTABLE) |
|||
|
|||
option( SPHINX_HTML_OUTPUT "Build a single HTML with the whole content." ON ) |
|||
option( SPHINX_EPUB_OUTPUT "Build HTML pages with additional information for building a documentation collection in epub." OFF ) |
|||
option( SPHINX_LATEX_OUTPUT "Build LaTeX sources that can be compiled to a PDF document using pdflatex." OFF ) |
|||
option( SPHINX_MAN_OUTPUT "Build manual pages in groff format for UNIX systems." OFF ) |
|||
option( SPHINX_TEXT_OUTPUT "Build plain text files." OFF ) |
|||
|
|||
|
|||
mark_as_advanced( |
|||
SPHINX_EXECUTABLE |
|||
SPHINX_HTML_OUTPUT |
|||
SPHINX_EPUB_OUTPUT |
|||
SPHINX_LATEX_OUTPUT |
|||
SPHINX_MAN_OUTPUT |
|||
SPHINX_TEXT_OUTPUT |
|||
) |
|||
|
|||
function( Sphinx_add_target target_name builder conf source destination ) |
|||
add_custom_target( ${target_name} ALL |
|||
COMMAND ${SPHINX_EXECUTABLE} -b ${builder} |
|||
-c ${conf} |
|||
${source} |
|||
${destination} |
|||
COMMENT "Generating sphinx documentation: ${builder}" |
|||
) |
|||
|
|||
set_property( |
|||
DIRECTORY APPEND PROPERTY |
|||
ADDITIONAL_MAKE_CLEAN_FILES |
|||
${destination} |
|||
) |
|||
endfunction() |
|||
|
|||
# Target dependencies can be optionally listed at the end. |
|||
function( Sphinx_add_targets target_base_name conf source base_destination ) |
|||
if( ${SPHINX_HTML_OUTPUT} ) |
|||
Sphinx_add_target( ${target_base_name}_html html ${conf} ${source} ${base_destination}/html ) |
|||
endif() |
|||
|
|||
if( ${SPHINX_EPUB_OUTPUT} ) |
|||
Sphinx_add_target( ${target_base_name}_epub epub ${conf} ${source} ${base_destination}/epub ) |
|||
endif() |
|||
|
|||
if( ${SPHINX_LATEX_OUTPUT} ) |
|||
Sphinx_add_target( ${target_base_name}_latex latex ${conf} ${source} ${base_destination}/latex ) |
|||
endif() |
|||
|
|||
if( ${SPHINX_MAN_OUTPUT} ) |
|||
Sphinx_add_target( ${target_base_name}_man man ${conf} ${source} ${base_destination}/man ) |
|||
endif() |
|||
|
|||
if( ${SPHINX_TEXT_OUTPUT} ) |
|||
Sphinx_add_target( ${target_base_name}_text text ${conf} ${source} ${base_destination}/text ) |
|||
endif() |
|||
endfunction() |
@ -0,0 +1,76 @@ |
|||
# Copyright (c) 2011-2013 Thomas Heller |
|||
# Modified by Tom van Dijk |
|||
# |
|||
# Distributed under the Boost Software License, Version 1.0. (See accompanying |
|||
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
|||
|
|||
find_package(Git) |
|||
|
|||
if(NOT GIT_FOUND) |
|||
message(FATAL_ERROR "Git not found!") |
|||
endif() |
|||
|
|||
if(NOT GHPAGES_REPOSITORY) |
|||
set(GHPAGES_REPOSITORY git@github.com:trolando/sylvan.git --branch gh-pages) |
|||
endif() |
|||
|
|||
if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/gh-pages") |
|||
execute_process( |
|||
COMMAND "${GIT_EXECUTABLE}" pull --rebase |
|||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/gh-pages" |
|||
RESULT_VARIABLE git_pull_result) |
|||
if(NOT "${git_pull_result}" EQUAL "0") |
|||
message(FATAL_ERROR "Updating the GitHub pages branch failed.") |
|||
endif() |
|||
else() |
|||
execute_process( |
|||
COMMAND "${GIT_EXECUTABLE}" clone ${GHPAGES_REPOSITORY} gh-pages |
|||
RESULT_VARIABLE git_clone_result) |
|||
if(NOT "${git_clone_result}" EQUAL "0") |
|||
message(FATAL_ERROR "Cloning the GitHub pages branch failed. Trying to clone ${GHPAGES_REPOSITORY}") |
|||
endif() |
|||
endif() |
|||
|
|||
# first delete all files |
|||
file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/gh-pages/*") |
|||
|
|||
# copy all documentation files to target branch |
|||
file(COPY "${CMAKE_CURRENT_BINARY_DIR}/html/" |
|||
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gh-pages" |
|||
PATTERN ".doctrees" EXCLUDE |
|||
PATTERN ".buildinfo" EXCLUDE |
|||
) |
|||
|
|||
# git add -A * |
|||
execute_process( |
|||
COMMAND "${GIT_EXECUTABLE}" add -A * |
|||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/gh-pages" |
|||
RESULT_VARIABLE git_add_result) |
|||
if(NOT "${git_add_result}" EQUAL "0") |
|||
message(FATAL_ERROR "Adding files to the GitHub pages branch failed.") |
|||
endif() |
|||
|
|||
# check if there are changes to commit |
|||
execute_process( |
|||
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD |
|||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/gh-pages" |
|||
RESULT_VARIABLE git_diff_index_result) |
|||
if(NOT "${git_diff_index_result}" EQUAL "0") |
|||
# commit changes |
|||
execute_process( |
|||
COMMAND "${GIT_EXECUTABLE}" commit -m "Updated documentation" |
|||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/gh-pages" |
|||
RESULT_VARIABLE git_commit_result) |
|||
if(NOT "${git_commit_result}" EQUAL "0") |
|||
message(FATAL_ERROR "Commiting to the GitHub pages branch failed.") |
|||
endif() |
|||
|
|||
# push everything up to github |
|||
execute_process( |
|||
COMMAND "${GIT_EXECUTABLE}" push |
|||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/gh-pages" |
|||
RESULT_VARIABLE git_push_result) |
|||
if(NOT "${git_push_result}" EQUAL "0") |
|||
message(FATAL_ERROR "Pushing to the GitHub pages branch failed.") |
|||
endif() |
|||
endif() |
@ -1,21 +0,0 @@ |
|||
AC_PREREQ([2.60]) |
|||
AC_INIT([sylvan], [1.0]) |
|||
AC_CONFIG_MACRO_DIR([m4]) |
|||
AC_CONFIG_AUX_DIR([tools]) |
|||
AM_INIT_AUTOMAKE([foreign]) |
|||
|
|||
AC_PROG_CC |
|||
AX_CHECK_COMPILE_FLAG([-std=c11],,[AC_MSG_FAILURE([no acceptable C11 compiler found.])]) |
|||
AC_PROG_CXX |
|||
LT_INIT |
|||
|
|||
AC_CHECKING([for any suitable hwloc installation]) |
|||
AC_CHECK_LIB([hwloc], [hwloc_topology_init], [AC_CHECK_HEADER([hwloc.h], [hwloc=yes])]) |
|||
AM_CONDITIONAL([HAVE_LIBHWLOC], [test "$hwloc" = "yes"]) |
|||
|
|||
AC_CANONICAL_HOST |
|||
AM_CONDITIONAL([DARWIN], [case $host_os in darwin*) true;; *) false;; esac]) |
|||
# test x$(uname) == "xDarwin"]) |
|||
|
|||
AC_CONFIG_FILES([Makefile src/Makefile]) |
|||
AC_OUTPUT |
@ -0,0 +1,58 @@ |
|||
#!/usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
extensions = ['sphinx.ext.todo', 'sphinx.ext.imgmath', 'sphinx.ext.githubpages'] |
|||
|
|||
# templates_path = ['_templates'] |
|||
source_suffix = '.rst' |
|||
master_doc = 'index' |
|||
|
|||
# General information about the project. |
|||
project = 'Sylvan' |
|||
copyright = '2017, Tom van Dijk' |
|||
author = 'Tom van Dijk' |
|||
version = '@PROJECT_VERSION@' |
|||
release = '@PROJECT_VERSION@' |
|||
|
|||
language = None |
|||
exclude_patterns = ['Thumbs.db', '.DS_Store'] |
|||
|
|||
# The name of the Pygments (syntax highlighting) style to use. |
|||
pygments_style = 'sphinx' |
|||
todo_include_todos = True |
|||
|
|||
# -- Options for HTML output ---------------------------------------------- |
|||
|
|||
# html_theme = 'alabaster' |
|||
# html_theme = 'default' |
|||
import os |
|||
if os.environ.get('READTHEDOCS', None) != 'True': |
|||
import sphinx_rtd_theme |
|||
html_theme = 'sphinx_rtd_theme' |
|||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] |
|||
|
|||
# -- Options for LaTeX output --------------------------------------------- |
|||
|
|||
latex_elements = { |
|||
'papersize': 'a4paper', |
|||
'pointsize': '11pt', |
|||
} |
|||
|
|||
# Grouping the document tree into LaTeX files. List of tuples |
|||
# (source start file, target name, title, |
|||
# author, documentclass [howto, manual, or own class]). |
|||
latex_documents = [ |
|||
(master_doc, 'Sylvan.tex', 'Sylvan Documentation', |
|||
'Tom van Dijk', 'manual'), |
|||
] |
|||
|
|||
|
|||
# -- Options for manual page output --------------------------------------- |
|||
|
|||
# One entry per manual page. List of tuples |
|||
# (source start file, name, description, authors, manual section). |
|||
man_pages = [ |
|||
(master_doc, 'sylvan', 'Sylvan Documentation', |
|||
[author], 1) |
|||
] |
|||
|
@ -0,0 +1,282 @@ |
|||
Sylvan |
|||
===================== |
|||
|
|||
Sylvan is a parallel (multi-core) MTBDD library written in C. Sylvan |
|||
implements parallelized operations on BDDs, MTBDDs and LDDs. Both |
|||
sequential and parallel BDD-based algorithms can benefit from |
|||
parallelism. Sylvan uses the work-stealing framework Lace and parallel |
|||
datastructures to implement scalable multi-core operations on decision |
|||
diagrams. |
|||
|
|||
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, and (© 2016-2017) |
|||
by the `Formal Methods and Verification <http://fmv.jku.at/>`__ group at |
|||
the Johannes Kepler University Linz as part of the RiSE project. Sylvan |
|||
is licensed with the Apache 2.0 license. |
|||
The main author of the project is Tom van Dijk who can be reached via |
|||
tom@tvandijk.nl. |
|||
Please let us know if you use Sylvan in your projects and if you need |
|||
decision diagram operations that are currently not implemented in Sylvan. |
|||
|
|||
The main repository of Sylvan is https://github.com/trolando/sylvan. A |
|||
mirror is available at https://github.com/utwente-fmt/sylvan. |
|||
|
|||
Bindings for other languages than C/C++ also exist: |
|||
|
|||
- Java/JNI bindings: https://github.com/utwente-fmt/jsylvan |
|||
- Haskell bindings: https://github.com/adamwalker/sylvan-haskell |
|||
- Python bindings: https://github.com/johnyf/dd |
|||
|
|||
Dependencies |
|||
------------ |
|||
|
|||
Sylvan has the following required dependencies: |
|||
|
|||
- **CMake** for compiling. |
|||
- **gmp** (``libgmp-dev``) for the GMP leaves in MTBDDs. |
|||
- **hwloc** (``libhwloc-dev``) for pinning worker threads to processors. |
|||
|
|||
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. |
|||
|
|||
Building |
|||
-------- |
|||
|
|||
It is recommended to build Sylvan in a separate build directory: |
|||
|
|||
.. code:: bash |
|||
|
|||
mkdir build |
|||
cd build |
|||
cmake .. |
|||
make && make test && make install |
|||
|
|||
It is recommended to use ``ccmake`` to configure the build settings of Sylvan. For example, |
|||
you can choose whether you want shared/static libraries, whether you want to enable |
|||
statistics gathering and whether you want a ``Debug`` or a ``Release`` build. |
|||
|
|||
Using Sylvan |
|||
------------ |
|||
|
|||
To use Sylvan, the library and its dependency Lace must be initialized: |
|||
|
|||
.. code:: c |
|||
|
|||
#include <sylvan.h> |
|||
|
|||
main() { |
|||
int n_workers = 0; // auto-detect |
|||
lace_init(n_workers, 0); |
|||
lace_startup(0, NULL, NULL); |
|||
|
|||
size_t nodes_minsize = 1LL<<22; |
|||
size_t nodes_maxsize = 1LL<<26; |
|||
size_t cache_minsize = 1LL<<23; |
|||
size_t cache_maxsize = 1LL<<27; |
|||
sylvan_init_package(nodes_minsize, nodes_maxsize, cache_minsize, cache_maxsize); |
|||
sylvan_init_mtbdd(); |
|||
|
|||
... |
|||
|
|||
sylvan_stats_report(stdout); |
|||
sylvan_quit(); |
|||
lace_exit(); |
|||
} |
|||
|
|||
The call to ``lace_init`` initializes the Lace framework, which sets up the data structures |
|||
for work-stealing. The parameter ``n_workers`` can be set to 0 for auto-detection. The |
|||
function ``lace_startup`` then creates all other worker threads. The worker threads run |
|||
until ``lace_exit`` is called. Lace must be started before Sylvan can be initialized. |
|||
|
|||
Sylvan is initialized with a call to ``sylvan_init_package``. Here we choose the initial |
|||
and maximum sizes of the nodes table and the operation cache. In the example, we choose a maximum |
|||
nodes table size of 2^26 and a maximum cache size of 2^27. The initial sizes are |
|||
set to 2^22 and 2^23, respectively. The sizes must be powers of 2. |
|||
Sylvan allocates memory for the maximum sizes *in virtual memory* but only uses the space |
|||
needed for the initial sizes. The sizes are doubled during garbage collection, until the maximum |
|||
size has been reached. |
|||
|
|||
After ``sylvan_init_package``, the subpackages ``mtbdd`` and ``ldd`` can be initialized with |
|||
``sylvan_init_mtbdd`` and ``sylvan_init_ldd``. This mainly allocates auxiliary datastructures for |
|||
garbage collection. |
|||
|
|||
If you enable statistics generation (via CMake) then you can use ``sylvan_stats_report`` to report |
|||
the obtained statistics to a given ``FILE*``. |
|||
|
|||
The Lace framework |
|||
~~~~~~~~~~~~~~~~~~ |
|||
|
|||
Sylvan uses the Lace framework to offer 'automatic' parallelization of decision diagram operations. |
|||
Many functions in Sylvan are Lace tasks. To call a Lace task, the variables |
|||
``__lace_worker`` and ``__lace_dq_head`` must be initialized **locally**. |
|||
Use the macro ``LACE_ME`` to initialize the variables in every function that calls Sylvan functions |
|||
and is not itself a Lace task. |
|||
|
|||
Garbage collection and referencing nodes |
|||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
Like all decision diagram implementations, Sylvan performs garbage collection. |
|||
Garbage collection is triggered when trying to insert a new node and no |
|||
empty space can be found in the table within a reasonable upper bound. |
|||
|
|||
To ensure that no decision diagram nodes are overwritten, you must ensure that |
|||
Sylvan knows which decision diagrams you care about. |
|||
The easiest way to do this is with ``sylvan_protect`` and ``sylvan_unprotect`` to protect |
|||
a given pointer. |
|||
These functions protect the decision diagram referenced to by that pointer at the time |
|||
that garbage collection is performed. |
|||
Unlike some other implementations of decision diagrams, |
|||
you can modify the variable between the calls to ``sylvan_protect`` and ``sylvan_unprotect`` |
|||
without explicitly changing the reference. |
|||
|
|||
To manually trigger garbage collection, call ``sylvan_gc``. |
|||
You can use ``sylvan_gc_disable`` and ``sylvan_gc_enable`` to disable garbage collection or |
|||
enable it again. If garbage collection is disabled, the program will abort when the nodes table |
|||
is full. |
|||
**Warning**: Sylvan is a multi-threaded library and all workers must cooperate for garbage collection. If you use locking mechanisms in your code, beware of deadlocks! |
|||
|
|||
Basic BDD functionality |
|||
~~~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
To create new BDDs, you can use: |
|||
|
|||
- ``sylvan_true``: representation of constant ``true``. |
|||
- ``sylvan_false``: representation of constant ``false``. |
|||
- ``sylvan_ithvar(var)``: representation of literal <var> (negated: ``sylvan_nithvar(var)``) |
|||
|
|||
To follow the BDD edges and obtain the variable at the root of a BDD, |
|||
you can use (only for internal nodes, not for leaves ``sylvan_true`` and ``sylvan_false``): |
|||
|
|||
- ``sylvan_var(bdd)``: obtain the variable of the root node of <bdd>. |
|||
- ``sylvan_high(bdd)``: follow the high edge of <bdd>. |
|||
- ``sylvan_low(bdd)``: follow the low edge of <bdd>. |
|||
|
|||
You need to manually reference BDDs that you want to keep during garbage |
|||
collection: |
|||
|
|||
- ``sylvan_protect(bddptr)``: add a pointer reference to <bddptr>. |
|||
- ``sylvan_unprotect(bddptr)``: remove a pointer reference to <bddptr>. |
|||
- ``sylvan_ref(bdd)``: add a reference to <bdd>. |
|||
- ``sylvan_deref(bdd)``: remove a reference to <bdd>. |
|||
|
|||
It is recommended to use ``sylvan_protect`` and ``sylvan_unprotect``. |
|||
The C++ objects (defined in ``sylvan_obj.hpp``) handle this automatically. |
|||
|
|||
The following basic operations are implemented: |
|||
|
|||
- ``sylvan_not(bdd)``: compute the negation of <bdd>. |
|||
- ``sylvan_ite(a,b,c)``: compute 'if <a> then <b> else <c>'. |
|||
- ``sylvan_and(a, b)``: compute '<a> and <b>' |
|||
- ``sylvan_or(a, b)``: compute '<a> or <b>' |
|||
- ``sylvan_nand(a, b)``: compute 'not (<a> and <b>)' |
|||
- ``sylvan_nor(a, b)``: compute 'not (<a> or <b>)' |
|||
- ``sylvan_imp(a, b)``: compute '<a> then <b>' |
|||
- ``sylvan_invimp(a, b)``: compute '<b> then <a>' |
|||
- ``sylvan_xor(a, b)``: compute '<a> xor <b>' |
|||
- ``sylvan_equiv(a, b)``: compute '<a> = <b>' |
|||
- ``sylvan_diff(a, b)``: compute '<a> and not <b>' |
|||
- ``sylvan_less(a, b)``: compute '<b> and not <a>' |
|||
- ``sylvan_exists(bdd, vars)``: existential quantification of <bdd> with respect to variables <vars>. |
|||
- ``sylvan_forall(bdd, vars)``: universal quantification of <bdd> with respect to variables <vars>. |
|||
|
|||
A set of variables (like <vars> above) is a BDD representing the conjunction of the variables. |
|||
|
|||
Other BDD operations |
|||
~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
See ``src/sylvan_bdd.h`` for other operations on BDDs, especially operations |
|||
that are relevant for model checking. |
|||
|
|||
Basic MTBDD functionality |
|||
~~~~~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
See ``src/sylvan_mtbdd.h`` for operations on multi-terminal BDDs. |
|||
|
|||
Basic LDD functionality |
|||
~~~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
See ``src/sylvan_ldd.h`` for operations on List DDs. |
|||
|
|||
Support for C++ |
|||
~~~~~~~~~~~~~~~ |
|||
|
|||
See ``src/sylvan_obj.hpp`` for the C++ interface. |
|||
|
|||
.. Adding custom decision diagram operations |
|||
.. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
Table resizing |
|||
~~~~~~~~~~~~~~ |
|||
|
|||
During garbage collection, it is possible to resize the nodes table and |
|||
the cache. Sylvan provides two default implementations: an aggressive |
|||
version that resizes every time garbage collection is performed, and a |
|||
less aggressive 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 not yet supported. For now, we suggest users |
|||
find a good static variable ordering. |
|||
|
|||
Examples |
|||
-------- |
|||
|
|||
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). |
|||
|
|||
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``. |
|||
|
|||
I get errors about ``__lace_worker`` and ``__lace_dq_head`` |
|||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
Many Sylvan operations are implemented as Lace tasks. To call a Lace |
|||
task, the variables ``__lace_worker`` and ``__lace_dq_head`` must be |
|||
initialized. Use the macro ``LACE_ME`` to do this. Only use ``LACE_ME`` |
|||
locally (in a function), never globally! |
|||
|
|||
Publications |
|||
------------ |
|||
|
|||
T. van Dijk (2016) `Sylvan: Multi-core Decision |
|||
Diagrams <http://dx.doi.org/10.3990/1.9789036541602>`__. PhD Thesis. |
|||
|
|||
T. van Dijk and J.C. van de Pol (2016) `Sylvan: Multi-core Framework |
|||
for Decision Diagrams <http://dx.doi.org/10.1007/s10009-016-0433-2>`__. |
|||
In: STTT (Special Issue), Springer. |
|||
|
|||
T. van Dijk and J.C. 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.C. van de Pol (2012) `Multi-Core BDD |
|||
Operations for Symbolic |
|||
Reachability <http://eprints.eemcs.utwente.nl/22166/>`__. In: PDMC 2012, |
|||
ENTCS. Elsevier. |
|||
|
|||
|
@ -0,0 +1,777 @@ |
|||
#include <argp.h> |
|||
#include <assert.h> |
|||
#include <inttypes.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <sys/time.h> |
|||
|
|||
#include <sylvan_int.h> |
|||
|
|||
/* Configuration */ |
|||
static int workers = 0; // autodetect |
|||
static int verbose = 0; |
|||
static char* model_filename = NULL; // filename of model |
|||
static char* bdd_filename = NULL; // filename of output BDD |
|||
static char* sizes = "22,27,21,26"; // default sizes |
|||
static int check_results = 0; |
|||
|
|||
/* argp configuration */ |
|||
static struct argp_option options[] = |
|||
{ |
|||
{"workers", 'w', "<workers>", 0, "Number of workers (default=0: autodetect)", 0}, |
|||
{"table-sizes", 1, "<tablesize>,<tablemax>,<cachesize>,<cachemax>", 0, "Sizes of nodes table and operation cache as powers of 2", 0}, |
|||
{"check-results", 2, 0, 0, "Check new transition relations ", 0}, |
|||
{"verbose", 'v', 0, 0, "Set verbose", 0}, |
|||
{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 'v': |
|||
verbose = 1; |
|||
break; |
|||
case 1: |
|||
sizes = arg; |
|||
break; |
|||
case 2: |
|||
check_results = 1; |
|||
break; |
|||
case ARGP_KEY_ARG: |
|||
if (state->arg_num == 0) model_filename = arg; |
|||
if (state->arg_num == 1) bdd_filename = arg; |
|||
if (state->arg_num >= 2) argp_usage(state); |
|||
break; |
|||
case ARGP_KEY_END: |
|||
if (state->arg_num < 2) argp_usage(state); |
|||
break; |
|||
default: |
|||
return ARGP_ERR_UNKNOWN; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
static struct argp argp = { options, parse_opt, "<model> [<output-bdd>]", 0, 0, 0, 0 }; |
|||
|
|||
/* Globals */ |
|||
typedef struct set |
|||
{ |
|||
MDD mdd; |
|||
MDD proj; |
|||
} *set_t; |
|||
|
|||
typedef struct relation |
|||
{ |
|||
MDD mdd; |
|||
MDD meta; |
|||
} *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 |
|||
static int actionbits = 0; |
|||
static int has_actions = 0; |
|||
|
|||
#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) |
|||
{ |
|||
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"); |
|||
|
|||
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)); |
|||
|
|||
return set; |
|||
} |
|||
|
|||
/* Load a relation from file */ |
|||
#define rel_load(f) CALL(rel_load, f) |
|||
TASK_1(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"); |
|||
|
|||
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)); |
|||
|
|||
return rel; |
|||
} |
|||
|
|||
/** |
|||
* Compute the highest value for each variable level. |
|||
* This method is called for the set of reachable states. |
|||
*/ |
|||
static uint64_t compute_highest_id; |
|||
#define compute_highest(dd, arr) CALL(compute_highest, dd, arr) |
|||
VOID_TASK_2(compute_highest, MDD, dd, uint32_t*, arr) |
|||
{ |
|||
if (dd == lddmc_true || dd == lddmc_false) return; |
|||
|
|||
uint64_t result = 1; |
|||
if (cache_get3(compute_highest_id, dd, 0, 0, &result)) return; |
|||
cache_put3(compute_highest_id, dd, 0, 0, result); |
|||
|
|||
mddnode_t n = LDD_GETNODE(dd); |
|||
|
|||
SPAWN(compute_highest, mddnode_getright(n), arr); |
|||
CALL(compute_highest, mddnode_getdown(n), arr+1); |
|||
SYNC(compute_highest); |
|||
|
|||
if (!mddnode_getcopy(n)) { |
|||
const uint32_t v = mddnode_getvalue(n); |
|||
while (1) { |
|||
const uint32_t cur = *(volatile uint32_t*)arr; |
|||
if (v <= cur) break; |
|||
if (__sync_bool_compare_and_swap(arr, cur, v)) break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Compute the highest value for the action label. |
|||
* This method is called for each transition relation. |
|||
*/ |
|||
static uint64_t compute_highest_action_id; |
|||
#define compute_highest_action(dd, meta, arr) CALL(compute_highest_action, dd, meta, arr) |
|||
VOID_TASK_3(compute_highest_action, MDD, dd, MDD, meta, uint32_t*, target) |
|||
{ |
|||
if (dd == lddmc_true || dd == lddmc_false) return; |
|||
if (meta == lddmc_true) return; |
|||
|
|||
uint64_t result = 1; |
|||
if (cache_get3(compute_highest_action_id, dd, meta, 0, &result)) return; |
|||
cache_put3(compute_highest_action_id, dd, meta, 0, result); |
|||
|
|||
/* meta: |
|||
* 0 is skip |
|||
* 1 is read |
|||
* 2 is write |
|||
* 3 is only-read |
|||
* 4 is only-write |
|||
* 5 is action label (at end, before -1) |
|||
* -1 is end |
|||
*/ |
|||
|
|||
const mddnode_t n = LDD_GETNODE(dd); |
|||
const mddnode_t nmeta = LDD_GETNODE(meta); |
|||
const uint32_t vmeta = mddnode_getvalue(nmeta); |
|||
if (vmeta == (uint32_t)-1) return; |
|||
|
|||
SPAWN(compute_highest_action, mddnode_getright(n), meta, target); |
|||
CALL(compute_highest_action, mddnode_getdown(n), mddnode_getdown(nmeta), target); |
|||
SYNC(compute_highest_action); |
|||
|
|||
if (vmeta == 5) { |
|||
has_actions = 1; |
|||
const uint32_t v = mddnode_getvalue(n); |
|||
while (1) { |
|||
const uint32_t cur = *(volatile uint32_t*)target; |
|||
if (v <= cur) break; |
|||
if (__sync_bool_compare_and_swap(target, cur, v)) break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Compute the BDD equivalent of the LDD of a set of states. |
|||
*/ |
|||
static uint64_t bdd_from_ldd_id; |
|||
#define bdd_from_ldd(dd, bits, firstvar) CALL(bdd_from_ldd, dd, bits, firstvar) |
|||
TASK_3(MTBDD, bdd_from_ldd, MDD, dd, MDD, bits_mdd, uint32_t, firstvar) |
|||
{ |
|||
/* simple for leaves */ |
|||
if (dd == lddmc_false) return mtbdd_false; |
|||
if (dd == lddmc_true) return mtbdd_true; |
|||
|
|||
MTBDD result; |
|||
/* get from cache */ |
|||
/* note: some assumptions about the encoding... */ |
|||
if (cache_get3(bdd_from_ldd_id, dd, bits_mdd, firstvar, &result)) return result; |
|||
|
|||
mddnode_t n = LDD_GETNODE(dd); |
|||
mddnode_t nbits = LDD_GETNODE(bits_mdd); |
|||
int bits = (int)mddnode_getvalue(nbits); |
|||
|
|||
/* spawn right, same bits_mdd and firstvar */ |
|||
mtbdd_refs_spawn(SPAWN(bdd_from_ldd, mddnode_getright(n), bits_mdd, firstvar)); |
|||
|
|||
/* call down, with next bits_mdd and firstvar */ |
|||
MTBDD down = CALL(bdd_from_ldd, mddnode_getdown(n), mddnode_getdown(nbits), firstvar + 2*bits); |
|||
|
|||
/* encode current value */ |
|||
uint32_t val = mddnode_getvalue(n); |
|||
for (int i=0; i<bits; i++) { |
|||
/* encode with high bit first */ |
|||
int bit = bits-i-1; |
|||
if (val & (1LL<<i)) down = mtbdd_makenode(firstvar + 2*bit, mtbdd_false, down); |
|||
else down = mtbdd_makenode(firstvar + 2*bit, down, mtbdd_false); |
|||
} |
|||
|
|||
/* sync right */ |
|||
mtbdd_refs_push(down); |
|||
MTBDD right = mtbdd_refs_sync(SYNC(bdd_from_ldd)); |
|||
|
|||
/* take union of current and right */ |
|||
mtbdd_refs_push(right); |
|||
result = sylvan_or(down, right); |
|||
mtbdd_refs_pop(2); |
|||
|
|||
/* put in cache */ |
|||
cache_put3(bdd_from_ldd_id, dd, bits_mdd, firstvar, result); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* Compute the BDD equivalent of an LDD transition relation. |
|||
*/ |
|||
static uint64_t bdd_from_ldd_rel_id; |
|||
#define bdd_from_ldd_rel(dd, bits, firstvar, meta) CALL(bdd_from_ldd_rel, dd, bits, firstvar, meta) |
|||
TASK_4(MTBDD, bdd_from_ldd_rel, MDD, dd, MDD, bits_mdd, uint32_t, firstvar, MDD, meta) |
|||
{ |
|||
if (dd == lddmc_false) return mtbdd_false; |
|||
if (dd == lddmc_true) return mtbdd_true; |
|||
assert(meta != lddmc_false && meta != lddmc_true); |
|||
|
|||
/* meta: |
|||
* -1 is end |
|||
* 0 is skip |
|||
* 1 is read |
|||
* 2 is write |
|||
* 3 is only-read |
|||
* 4 is only-write |
|||
*/ |
|||
|
|||
MTBDD result; |
|||
/* note: assumptions */ |
|||
if (cache_get4(bdd_from_ldd_rel_id, dd, bits_mdd, firstvar, meta, &result)) return result; |
|||
|
|||
const mddnode_t n = LDD_GETNODE(dd); |
|||
const mddnode_t nmeta = LDD_GETNODE(meta); |
|||
const mddnode_t nbits = LDD_GETNODE(bits_mdd); |
|||
const int bits = (int)mddnode_getvalue(nbits); |
|||
|
|||
const uint32_t vmeta = mddnode_getvalue(nmeta); |
|||
assert(vmeta != (uint32_t)-1); |
|||
|
|||
if (vmeta == 0) { |
|||
/* skip level */ |
|||
result = bdd_from_ldd_rel(dd, mddnode_getdown(nbits), firstvar + 2*bits, mddnode_getdown(nmeta)); |
|||
} else if (vmeta == 1) { |
|||
/* read level */ |
|||
assert(!mddnode_getcopy(n)); // do not process read copy nodes for now |
|||
assert(mddnode_getright(n) != mtbdd_true); |
|||
|
|||
/* spawn right */ |
|||
mtbdd_refs_spawn(SPAWN(bdd_from_ldd_rel, mddnode_getright(n), bits_mdd, firstvar, meta)); |
|||
|
|||
/* compute down with same bits / firstvar */ |
|||
MTBDD down = bdd_from_ldd_rel(mddnode_getdown(n), bits_mdd, firstvar, mddnode_getdown(nmeta)); |
|||
mtbdd_refs_push(down); |
|||
|
|||
/* encode read value */ |
|||
uint32_t val = mddnode_getvalue(n); |
|||
MTBDD part = mtbdd_true; |
|||
for (int i=0; i<bits; i++) { |
|||
/* encode with high bit first */ |
|||
int bit = bits-i-1; |
|||
if (val & (1LL<<i)) part = mtbdd_makenode(firstvar + 2*bit, mtbdd_false, part); |
|||
else part = mtbdd_makenode(firstvar + 2*bit, part, mtbdd_false); |
|||
} |
|||
|
|||
/* intersect read value with down result */ |
|||
mtbdd_refs_push(part); |
|||
down = sylvan_and(part, down); |
|||
mtbdd_refs_pop(2); |
|||
|
|||
/* sync right */ |
|||
mtbdd_refs_push(down); |
|||
MTBDD right = mtbdd_refs_sync(SYNC(bdd_from_ldd_rel)); |
|||
|
|||
/* take union of current and right */ |
|||
mtbdd_refs_push(right); |
|||
result = sylvan_or(down, right); |
|||
mtbdd_refs_pop(2); |
|||
} else if (vmeta == 2 || vmeta == 4) { |
|||
/* write or only-write level */ |
|||
|
|||
/* spawn right */ |
|||
assert(mddnode_getright(n) != mtbdd_true); |
|||
mtbdd_refs_spawn(SPAWN(bdd_from_ldd_rel, mddnode_getright(n), bits_mdd, firstvar, meta)); |
|||
|
|||
/* get recursive result */ |
|||
MTBDD down = CALL(bdd_from_ldd_rel, mddnode_getdown(n), mddnode_getdown(nbits), firstvar + 2*bits, mddnode_getdown(nmeta)); |
|||
|
|||
if (mddnode_getcopy(n)) { |
|||
/* encode a copy node */ |
|||
for (int i=0; i<bits; i++) { |
|||
int bit = bits-i-1; |
|||
MTBDD low = mtbdd_makenode(firstvar + 2*bit + 1, down, mtbdd_false); |
|||
mtbdd_refs_push(low); |
|||
MTBDD high = mtbdd_makenode(firstvar + 2*bit + 1, mtbdd_false, down); |
|||
mtbdd_refs_pop(1); |
|||
down = mtbdd_makenode(firstvar + 2*bit, low, high); |
|||
} |
|||
} else { |
|||
/* encode written value */ |
|||
uint32_t val = mddnode_getvalue(n); |
|||
for (int i=0; i<bits; i++) { |
|||
/* encode with high bit first */ |
|||
int bit = bits-i-1; |
|||
if (val & (1LL<<i)) down = mtbdd_makenode(firstvar + 2*bit + 1, mtbdd_false, down); |
|||
else down = mtbdd_makenode(firstvar + 2*bit + 1, down, mtbdd_false); |
|||
} |
|||
} |
|||
|
|||
/* sync right */ |
|||
mtbdd_refs_push(down); |
|||
MTBDD right = mtbdd_refs_sync(SYNC(bdd_from_ldd_rel)); |
|||
|
|||
/* take union of current and right */ |
|||
mtbdd_refs_push(right); |
|||
result = sylvan_or(down, right); |
|||
mtbdd_refs_pop(2); |
|||
} else if (vmeta == 3) { |
|||
/* only-read level */ |
|||
assert(!mddnode_getcopy(n)); // do not process read copy nodes |
|||
|
|||
/* spawn right */ |
|||
mtbdd_refs_spawn(SPAWN(bdd_from_ldd_rel, mddnode_getright(n), bits_mdd, firstvar, meta)); |
|||
|
|||
/* get recursive result */ |
|||
MTBDD down = CALL(bdd_from_ldd_rel, mddnode_getdown(n), mddnode_getdown(nbits), firstvar + 2*bits, mddnode_getdown(nmeta)); |
|||
|
|||
/* encode read value */ |
|||
uint32_t val = mddnode_getvalue(n); |
|||
for (int i=0; i<bits; i++) { |
|||
/* encode with high bit first */ |
|||
int bit = bits-i-1; |
|||
/* only-read, so write same value */ |
|||
if (val & (1LL<<i)) down = mtbdd_makenode(firstvar + 2*bit + 1, mtbdd_false, down); |
|||
else down = mtbdd_makenode(firstvar + 2*bit + 1, down, mtbdd_false); |
|||
if (val & (1LL<<i)) down = mtbdd_makenode(firstvar + 2*bit, mtbdd_false, down); |
|||
else down = mtbdd_makenode(firstvar + 2*bit, down, mtbdd_false); |
|||
} |
|||
|
|||
/* sync right */ |
|||
mtbdd_refs_push(down); |
|||
MTBDD right = mtbdd_refs_sync(SYNC(bdd_from_ldd_rel)); |
|||
|
|||
/* take union of current and right */ |
|||
mtbdd_refs_push(right); |
|||
result = sylvan_or(down, right); |
|||
mtbdd_refs_pop(2); |
|||
} else if (vmeta == 5) { |
|||
assert(!mddnode_getcopy(n)); // not allowed! |
|||
|
|||
/* we assume this is the last value */ |
|||
result = mtbdd_true; |
|||
|
|||
/* encode action value */ |
|||
uint32_t val = mddnode_getvalue(n); |
|||
for (int i=0; i<actionbits; i++) { |
|||
/* encode with high bit first */ |
|||
int bit = actionbits-i-1; |
|||
/* only-read, so write same value */ |
|||
if (val & (1LL<<i)) result = mtbdd_makenode(1000000 + bit, mtbdd_false, result); |
|||
else result = mtbdd_makenode(1000000 + bit, result, mtbdd_false); |
|||
} |
|||
} else { |
|||
assert(vmeta <= 5); |
|||
} |
|||
|
|||
cache_put4(bdd_from_ldd_rel_id, dd, bits_mdd, firstvar, meta, result); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* Compute the BDD equivalent of the meta variable (to a variables cube) |
|||
*/ |
|||
MTBDD |
|||
meta_to_bdd(MDD meta, MDD bits_mdd, uint32_t firstvar) |
|||
{ |
|||
if (meta == lddmc_false || meta == lddmc_true) return mtbdd_true; |
|||
|
|||
/* meta: |
|||
* -1 is end |
|||
* 0 is skip (no variables) |
|||
* 1 is read (variables added by write) |
|||
* 2 is write |
|||
* 3 is only-read |
|||
* 4 is only-write |
|||
*/ |
|||
|
|||
const mddnode_t nmeta = LDD_GETNODE(meta); |
|||
const uint32_t vmeta = mddnode_getvalue(nmeta); |
|||
if (vmeta == (uint32_t)-1) return mtbdd_true; |
|||
|
|||
if (vmeta == 1) { |
|||
/* return recursive result, don't go down on bits */ |
|||
return meta_to_bdd(mddnode_getdown(nmeta), bits_mdd, firstvar); |
|||
} |
|||
|
|||
const mddnode_t nbits = LDD_GETNODE(bits_mdd); |
|||
const int bits = (int)mddnode_getvalue(nbits); |
|||
|
|||
/* compute recursive result */ |
|||
MTBDD res = meta_to_bdd(mddnode_getdown(nmeta), mddnode_getdown(nbits), firstvar + 2*bits); |
|||
|
|||
/* add our variables if meta is 2,3,4 */ |
|||
if (vmeta != 0 && vmeta != 5) { |
|||
for (int i=0; i<bits; i++) { |
|||
res = mtbdd_makenode(firstvar + 2*(bits-i-1) + 1, mtbdd_false, res); |
|||
res = mtbdd_makenode(firstvar + 2*(bits-i-1), mtbdd_false, res); |
|||
} |
|||
} |
|||
|
|||
return res; |
|||
} |
|||
|
|||
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; |
|||
} |
|||
|
|||
VOID_TASK_0(gc_start) |
|||
{ |
|||
printf("Starting garbage collection\n"); |
|||
} |
|||
|
|||
VOID_TASK_0(gc_end) |
|||
{ |
|||
printf("Garbage collection done\n"); |
|||
} |
|||
|
|||
int |
|||
main(int argc, char **argv) |
|||
{ |
|||
argp_parse(&argp, argc, argv, 0, 0, 0); |
|||
|
|||
// Parse table sizes |
|||
int tablesize, maxtablesize, cachesize, maxcachesize; |
|||
if (sscanf(sizes, "%d,%d,%d,%d", &tablesize, &maxtablesize, &cachesize, &maxcachesize) != 4) { |
|||
Abort("Invalid string for --table-sizes, try e.g. --table-sizes=23,28,22,27"); |
|||
} |
|||
if (tablesize < 10 || maxtablesize < 10 || cachesize < 10 || maxcachesize < 10 || |
|||
tablesize > 40 || maxtablesize > 40 || cachesize > 40 || maxcachesize > 40) { |
|||
Abort("Invalid string for --table-sizes, must be between 10 and 40"); |
|||
} |
|||
if (tablesize > maxtablesize) { |
|||
Abort("Invalid string for --table-sizes, tablesize is larger than maxtablesize"); |
|||
} |
|||
if (cachesize > maxcachesize) { |
|||
Abort("Invalid string for --table-sizes, cachesize is larger than maxcachesize"); |
|||
} |
|||
|
|||
// Report table sizes |
|||
char buf[32]; |
|||
to_h((1ULL<<maxtablesize)*24+(1ULL<<maxcachesize)*36, buf); |
|||
printf("Sylvan allocates %s virtual memory for nodes table and operation cache.\n", buf); |
|||
to_h((1ULL<<tablesize)*24+(1ULL<<cachesize)*36, buf); |
|||
printf("Initial nodes table and operation cache requires %s.\n", buf); |
|||
|
|||
// 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 |
|||
sylvan_set_sizes(1LL<<21, 1LL<<27, 1LL<<20, 1LL<<26); |
|||
sylvan_init_package(); |
|||
sylvan_init_ldd(); |
|||
sylvan_init_mtbdd(); |
|||
sylvan_gc_hook_pregc(TASK(gc_start)); |
|||
sylvan_gc_hook_postgc(TASK(gc_end)); |
|||
|
|||
// Obtain operation ids for the operation cache |
|||
compute_highest_id = cache_next_opid(); |
|||
compute_highest_action_id = cache_next_opid(); |
|||
bdd_from_ldd_id = cache_next_opid(); |
|||
bdd_from_ldd_rel_id = cache_next_opid(); |
|||
|
|||
// Open file |
|||
FILE *f = fopen(model_filename, "r"); |
|||
if (f == NULL) Abort("Cannot open file '%s'!\n", model_filename); |
|||
|
|||
// Read integers per vector |
|||
if (fread(&vector_size, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n"); |
|||
|
|||
// Read initial state |
|||
if (verbose) { |
|||
printf("Loading initial state... "); |
|||
fflush(stdout); |
|||
} |
|||
set_t initial = set_load(f); |
|||
if (verbose) printf("done.\n"); |
|||
|
|||
// Read number of transitions |
|||
if (fread(&next_count, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n"); |
|||
next = (rel_t*)malloc(sizeof(rel_t) * next_count); |
|||
|
|||
// Read transitions |
|||
if (verbose) { |
|||
printf("Loading transition relations... "); |
|||
fflush(stdout); |
|||
} |
|||
int i; |
|||
for (i=0; i<next_count; i++) { |
|||
next[i] = rel_load(f); |
|||
if (verbose) { |
|||
printf("%d, ", i); |
|||
fflush(stdout); |
|||
} |
|||
} |
|||
if (verbose) printf("done.\n"); |
|||
|
|||
// Read whether reachable states are stored |
|||
int has_reachable = 0; |
|||
if (fread(&has_reachable, sizeof(int), 1, f) != 1) Abort("Input file missing reachable states!\n"); |
|||
if (has_reachable == 0) Abort("Input file missing reachable states!\n"); |
|||
|
|||
// Read reachable states |
|||
if (verbose) { |
|||
printf("Loading reachable states... "); |
|||
fflush(stdout); |
|||
} |
|||
set_t states = set_load(f); |
|||
if (verbose) printf("done.\n"); |
|||
|
|||
// Read number of action labels |
|||
int action_labels_count = 0; |
|||
if (fread(&action_labels_count, sizeof(int), 1, f) != 1) Abort("Input file missing action label count!\n"); |
|||
|
|||
// Read action labels |
|||
char *action_labels[action_labels_count]; |
|||
for (int i=0; i<action_labels_count; i++) { |
|||
uint32_t len; |
|||
if (fread(&len, sizeof(uint32_t), 1, f) != 1) Abort("Invalid input file!\n"); |
|||
action_labels[i] = (char*)malloc(sizeof(char[len+1])); |
|||
if (fread(action_labels[i], sizeof(char), len, f) != len) Abort("Invalid input file!\n"); |
|||
action_labels[i][len] = 0; |
|||
} |
|||
|
|||
// Close file |
|||
fclose(f); |
|||
|
|||
// Report that we have read the input file |
|||
printf("Read file %s.\n", argv[1]); |
|||
|
|||
// Report statistics |
|||
if (verbose) { |
|||
printf("%zu integers per state, %d transition groups\n", vector_size, next_count); |
|||
printf("LDD nodes:\n"); |
|||
printf("Initial states: %zu LDD nodes\n", lddmc_nodecount(initial->mdd)); |
|||
for (i=0; i<next_count; i++) { |
|||
printf("Transition %d: %zu LDD nodes\n", i, lddmc_nodecount(next[i]->mdd)); |
|||
} |
|||
} |
|||
|
|||
// Report that we prepare BDD conversion |
|||
if (verbose) printf("Preparing conversion to BDD...\n"); |
|||
|
|||
// Compute highest value at each level (from reachable states) |
|||
uint32_t highest[vector_size]; |
|||
for (size_t i=0; i<vector_size; i++) highest[i] = 0; |
|||
compute_highest(states->mdd, highest); |
|||
|
|||
// Compute highest action label value (from transition relations) |
|||
uint32_t highest_action = 0; |
|||
for (int i=0; i<next_count; i++) { |
|||
compute_highest_action(next[i]->mdd, next[i]->meta, &highest_action); |
|||
} |
|||
|
|||
// Report highest integers |
|||
/* |
|||
printf("Highest integer per level: "); |
|||
for (size_t i=0; i<vector_size; i++) { |
|||
if (i>0) printf(", "); |
|||
printf("%u", highest[i]); |
|||
} |
|||
printf("\n"); |
|||
*/ |
|||
|
|||
// Compute number of bits for each level |
|||
int bits[vector_size]; |
|||
for (size_t i=0; i<vector_size; i++) { |
|||
bits[i] = 0; |
|||
while (highest[i] != 0) { |
|||
bits[i]++; |
|||
highest[i]>>=1; |
|||
} |
|||
if (bits[i] == 0) bits[i] = 1; |
|||
} |
|||
|
|||
// Compute number of bits for action label |
|||
actionbits = 0; |
|||
while (highest_action != 0) { |
|||
actionbits++; |
|||
highest_action>>=1; |
|||
} |
|||
if (actionbits == 0 && has_actions) actionbits = 1; |
|||
|
|||
// Report number of bits |
|||
if (verbose) { |
|||
printf("Bits per level: "); |
|||
for (size_t i=0; i<vector_size; i++) { |
|||
if (i>0) printf(", "); |
|||
printf("%d", bits[i]); |
|||
} |
|||
printf("\n"); |
|||
printf("Action bits: %d.\n", actionbits); |
|||
} |
|||
|
|||
// Compute bits MDD |
|||
MDD bits_mdd = lddmc_true; |
|||
for (size_t i=0; i<vector_size; i++) { |
|||
bits_mdd = lddmc_makenode(bits[vector_size-i-1], bits_mdd, lddmc_false); |
|||
} |
|||
lddmc_ref(bits_mdd); |
|||
|
|||
// Compute total number of bits |
|||
int totalbits = 0; |
|||
for (size_t i=0; i<vector_size; i++) { |
|||
totalbits += bits[i]; |
|||
} |
|||
|
|||
// Compute state variables |
|||
MTBDD state_vars = mtbdd_true; |
|||
for (int i=0; i<totalbits; i++) { |
|||
state_vars = mtbdd_makenode(2*(totalbits-i-1), mtbdd_false, state_vars); |
|||
} |
|||
mtbdd_protect(&state_vars); |
|||
|
|||
// Report that we begin the actual conversion |
|||
if (verbose) printf("Converting to BDD...\n"); |
|||
|
|||
// Create BDD file |
|||
f = fopen(bdd_filename, "w"); |
|||
if (f == NULL) Abort("Cannot open file '%s'!\n", bdd_filename); |
|||
|
|||
// Write domain... |
|||
int vector_size = 1; |
|||
fwrite(&totalbits, sizeof(int), 1, f); // use number of bits as vector size |
|||
fwrite(&vector_size, sizeof(int), 1, f); // set each to 1 |
|||
fwrite(&actionbits, sizeof(int), 1, f); |
|||
|
|||
// Write initial state... |
|||
MTBDD new_initial = bdd_from_ldd(initial->mdd, bits_mdd, 0); |
|||
assert((size_t)mtbdd_satcount(new_initial, totalbits) == (size_t)lddmc_satcount_cached(initial->mdd)); |
|||
mtbdd_refs_push(new_initial); |
|||
{ |
|||
size_t a = sylvan_serialize_add(new_initial); |
|||
size_t b = sylvan_serialize_add(state_vars); |
|||
size_t s = totalbits; |
|||
sylvan_serialize_tofile(f); |
|||
fwrite(&a, sizeof(size_t), 1, f); |
|||
fwrite(&s, sizeof(size_t), 1, f); |
|||
fwrite(&b, sizeof(size_t), 1, f); |
|||
} |
|||
|
|||
// Custom operation that converts to BDD given number of bits for each level |
|||
MTBDD new_states = bdd_from_ldd(states->mdd, bits_mdd, 0); |
|||
assert((size_t)mtbdd_satcount(new_states, totalbits) == (size_t)lddmc_satcount_cached(states->mdd)); |
|||
mtbdd_refs_push(new_states); |
|||
|
|||
// Report size of BDD |
|||
if (verbose) { |
|||
printf("Initial states: %zu BDD nodes\n", mtbdd_nodecount(new_initial)); |
|||
printf("Reachable states: %zu BDD nodes\n", mtbdd_nodecount(new_states)); |
|||
} |
|||
|
|||
// Write number of transitions |
|||
fwrite(&next_count, sizeof(int), 1, f); |
|||
|
|||
// Write transitions |
|||
for (int i=0; i<next_count; i++) { |
|||
// Compute new transition relation |
|||
MTBDD new_rel = bdd_from_ldd_rel(next[i]->mdd, bits_mdd, 0, next[i]->meta); |
|||
mtbdd_refs_push(new_rel); |
|||
|
|||
// Compute new <variables> for the current transition relation |
|||
MTBDD new_vars = meta_to_bdd(next[i]->meta, bits_mdd, 0); |
|||
mtbdd_refs_push(new_vars); |
|||
|
|||
if (check_results) { |
|||
// Test if the transition is correctly converted |
|||
MTBDD test = sylvan_relnext(new_states, new_rel, new_vars); |
|||
mtbdd_refs_push(test); |
|||
MDD succ = lddmc_relprod(states->mdd, next[i]->mdd, next[i]->meta); |
|||
lddmc_refs_push(succ); |
|||
MTBDD test2 = bdd_from_ldd(succ, bits_mdd, 0); |
|||
if (test != test2) Abort("Conversion error!\n"); |
|||
mtbdd_refs_pop(1); |
|||
lddmc_refs_pop(1); |
|||
} |
|||
|
|||
// Report number of nodes |
|||
if (verbose) printf("Transition %d: %zu BDD nodes\n", i, mtbdd_nodecount(new_rel)); |
|||
|
|||
size_t a = sylvan_serialize_add(new_rel); |
|||
size_t b = sylvan_serialize_add(new_vars); |
|||
sylvan_serialize_tofile(f); |
|||
fwrite(&a, sizeof(size_t), 1, f); |
|||
fwrite(&b, sizeof(size_t), 1, f); |
|||
} |
|||
|
|||
// Write reachable states |
|||
has_reachable = 1; |
|||
fwrite(&has_reachable, sizeof(int), 1, f); |
|||
|
|||
{ |
|||
size_t a = sylvan_serialize_add(new_states); |
|||
size_t b = sylvan_serialize_add(state_vars); |
|||
size_t s = totalbits; |
|||
sylvan_serialize_tofile(f); |
|||
fwrite(&a, sizeof(size_t), 1, f); |
|||
fwrite(&s, sizeof(size_t), 1, f); |
|||
fwrite(&b, sizeof(size_t), 1, f); |
|||
} |
|||
|
|||
// Write action labels |
|||
fwrite(&action_labels_count, sizeof(int), 1, f); |
|||
for (int i=0; i<action_labels_count; i++) { |
|||
uint32_t len = strlen(action_labels[i]); |
|||
fwrite(&len, sizeof(uint32_t), 1, f); |
|||
fwrite(action_labels[i], sizeof(char), len, f); |
|||
} |
|||
|
|||
// Close the file |
|||
fclose(f); |
|||
|
|||
// Report to the user |
|||
printf("Written file %s.\n", bdd_filename); |
|||
|
|||
// Report Sylvan statistics (if SYLVAN_STATS is set) |
|||
if (verbose) sylvan_stats_report(stdout); |
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,328 @@ |
|||
/** |
|||
* N-queens example. |
|||
* Based on work by Robert Meolic, released by him into the public domain. |
|||
*/ |
|||
|
|||
#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 <sylvan_table.h> |
|||
|
|||
/* Configuration */ |
|||
static int report_minterms = 0; // report minterms at every major step |
|||
static int report_minor = 0; // report minor steps |
|||
static int report_stats = 0; // report stats at end |
|||
static int workers = 0; // autodetect number of workers by default |
|||
static size_t size = 0; // will be set by caller |
|||
#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}, |
|||
#ifdef HAVE_PROFILER |
|||
{"profiler", 'p', "<filename>", 0, "Filename for profiling", 0}, |
|||
#endif |
|||
{"report-minterms", 1, 0, 0, "Report #minterms at every major step", 1}, |
|||
{"report-minor", 2, 0, 0, "Report minor steps", 1}, |
|||
{"report-stats", 3, 0, 0, "Report statistics at end", 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 1: |
|||
report_minterms = 1; |
|||
break; |
|||
case 2: |
|||
report_minor = 1; |
|||
break; |
|||
case 3: |
|||
report_stats = 1; |
|||
break; |
|||
#ifdef HAVE_PROFILER |
|||
case 'p': |
|||
profile_filename = arg; |
|||
break; |
|||
#endif |
|||
case ARGP_KEY_ARG: |
|||
if (state->arg_num >= 1) argp_usage(state); |
|||
size = atoi(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, "<size>", 0, 0, 0, 0 }; |
|||
|
|||
/* 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); } |
|||
|
|||
VOID_TASK_0(gc_start) |
|||
{ |
|||
if (report_minor) { |
|||
printf("\n"); |
|||
} |
|||
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(); |
|||
|
|||
// 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 is initialized, now set local variables |
|||
LACE_ME; |
|||
|
|||
// Init Sylvan |
|||
// Nodes table size of 1LL<<20 is 1048576 entries |
|||
// Cache size of 1LL<<18 is 262144 entries |
|||
// Nodes table size: 24 bytes * nodes |
|||
// Cache table size: 36 bytes * cache entries |
|||
// With 2^20 nodes and 2^18 cache entries, that's 33 MB |
|||
// With 2^24 nodes and 2^22 cache entries, that's 528 MB |
|||
sylvan_set_sizes(1LL<<20, 1LL<<24, 1LL<<18, 1LL<<22); |
|||
sylvan_init_package(); |
|||
sylvan_set_granularity(3); // granularity 3 is decent value for this small problem - 1 means "use cache for every operation" |
|||
sylvan_init_bdd(); |
|||
|
|||
// Before and after garbage collection, call gc_start and gc_end |
|||
sylvan_gc_hook_pregc(TASK(gc_start)); |
|||
sylvan_gc_hook_postgc(TASK(gc_end)); |
|||
|
|||
#ifdef HAVE_PROFILER |
|||
if (profile_filename != NULL) ProfilerStart(profile_filename); |
|||
#endif |
|||
double t1 = wctime(); |
|||
|
|||
BDD zero = sylvan_false; |
|||
BDD one = sylvan_true; |
|||
|
|||
// Variables 0 ... (SIZE*SIZE-1) |
|||
|
|||
BDD board[size*size]; |
|||
for (size_t i=0; i<size*size; i++) { |
|||
board[i] = sylvan_ithvar(i); |
|||
sylvan_protect(board+i); |
|||
} |
|||
|
|||
BDD res = one, temp = one; |
|||
|
|||
// we use sylvan's "protect" marking mechanism... |
|||
// that means we hardly need to do manual ref/deref when the variables change |
|||
sylvan_protect(&res); |
|||
sylvan_protect(&temp); |
|||
|
|||
// Old satcount function still requires a silly variables cube |
|||
BDD vars = one; |
|||
sylvan_protect(&vars); |
|||
for (size_t i=0; i<size*size; i++) vars = sylvan_and(vars, board[i]); |
|||
|
|||
INFO("Initialisation complete!\n"); |
|||
|
|||
if (report_minor) { |
|||
INFO("Encoding rows... "); |
|||
} else { |
|||
INFO("Encoding rows...\n"); |
|||
} |
|||
|
|||
for (size_t i=0; i<size; i++) { |
|||
if (report_minor) { |
|||
printf("%zu... ", i); |
|||
fflush(stdout); |
|||
} |
|||
|
|||
for (size_t j=0; j<size; j++) { |
|||
// compute "\BigAnd (!board[i][k]) \or !board[i][j]" with k != j |
|||
temp = one; |
|||
for (size_t k=0; k<size; k++) { |
|||
if (j==k) continue; |
|||
temp = sylvan_and(temp, sylvan_not(board[i*size+k])); |
|||
} |
|||
temp = sylvan_or(temp, sylvan_not(board[i*size+j])); |
|||
// add cube to "res" |
|||
res = sylvan_and(res, temp); |
|||
} |
|||
} |
|||
|
|||
if (report_minor) { |
|||
printf("\n"); |
|||
} |
|||
if (report_minterms) { |
|||
INFO("We have %.0f minterms\n", sylvan_satcount(res, vars)); |
|||
} |
|||
if (report_minor) { |
|||
INFO("Encoding columns... "); |
|||
} else { |
|||
INFO("Encoding columns...\n"); |
|||
} |
|||
|
|||
for (size_t j=0; j<size; j++) { |
|||
if (report_minor) { |
|||
printf("%zu... ", j); |
|||
fflush(stdout); |
|||
} |
|||
|
|||
for (size_t i=0; i<size; i++) { |
|||
// compute "\BigAnd (!board[k][j]) \or !board[i][j]" with k != i |
|||
temp = one; |
|||
for (size_t k=0; k<size; k++) { |
|||
if (i==k) continue; |
|||
temp = sylvan_and(temp, sylvan_not(board[k*size+j])); |
|||
} |
|||
temp = sylvan_or(temp, sylvan_not(board[i*size+j])); |
|||
// add cube to "res" |
|||
res = sylvan_and(res, temp); |
|||
} |
|||
} |
|||
|
|||
if (report_minor) { |
|||
printf("\n"); |
|||
} |
|||
if (report_minterms) { |
|||
INFO("We have %.0f minterms\n", sylvan_satcount(res, vars)); |
|||
} |
|||
if (report_minor) { |
|||
INFO("Encoding rising diagonals... "); |
|||
} else { |
|||
INFO("Encoding rising diagonals...\n"); |
|||
} |
|||
|
|||
for (size_t i=0; i<size; i++) { |
|||
if (report_minor) { |
|||
printf("%zu... ", i); |
|||
fflush(stdout); |
|||
} |
|||
|
|||
for (size_t j=0; j<size; j++) { |
|||
temp = one; |
|||
for (size_t k=0; k<size; k++) { |
|||
// if (j+k-i >= 0 && j+k-i < size && k != i) |
|||
if (j+k >= i && j+k < size+i && k != i) { |
|||
temp = sylvan_and(temp, sylvan_not(board[k*size + (j+k-i)])); |
|||
} |
|||
} |
|||
temp = sylvan_or(temp, sylvan_not(board[i*size+j])); |
|||
// add cube to "res" |
|||
res = sylvan_and(res, temp); |
|||
} |
|||
} |
|||
|
|||
if (report_minor) { |
|||
printf("\n"); |
|||
} |
|||
if (report_minterms) { |
|||
INFO("We have %.0f minterms\n", sylvan_satcount(res, vars)); |
|||
} |
|||
if (report_minor) { |
|||
INFO("Encoding falling diagonals... "); |
|||
} else { |
|||
INFO("Encoding falling diagonals...\n"); |
|||
} |
|||
|
|||
for (size_t i=0; i<size; i++) { |
|||
if (report_minor) { |
|||
printf("%zu... ", i); |
|||
fflush(stdout); |
|||
} |
|||
|
|||
for (size_t j=0; j<size; j++) { |
|||
temp = one; |
|||
for (size_t k=0; k<size; k++) { |
|||
// if (j+i-k >= 0 && j+i-k < size && k != i) |
|||
if (j+i >= k && j+i < size+k && k != i) { |
|||
temp = sylvan_and(temp, sylvan_not(board[k*size + (j+i-k)])); |
|||
} |
|||
} |
|||
temp = sylvan_or(temp, sylvan_not(board[i*size + j])); |
|||
// add cube to "res" |
|||
res = sylvan_and(res, temp); |
|||
} |
|||
} |
|||
|
|||
if (report_minor) { |
|||
printf("\n"); |
|||
} |
|||
if (report_minterms) { |
|||
INFO("We have %.0f minterms\n", sylvan_satcount(res, vars)); |
|||
} |
|||
if (report_minor) { |
|||
INFO("Final computation to place a queen on every row... "); |
|||
} else { |
|||
INFO("Final computation to place a queen on every row...\n"); |
|||
} |
|||
|
|||
for (size_t i=0; i<size; i++) { |
|||
if (report_minor) { |
|||
printf("%zu... ", i); |
|||
fflush(stdout); |
|||
} |
|||
|
|||
temp = zero; |
|||
for (size_t j=0; j<size; j++) { |
|||
temp = sylvan_or(temp, board[i*size+j]); |
|||
} |
|||
res = sylvan_and(res, temp); |
|||
} |
|||
|
|||
if (report_minor) { |
|||
printf("\n"); |
|||
} |
|||
|
|||
double t2 = wctime(); |
|||
#ifdef HAVE_PROFILER |
|||
if (profile_filename != NULL) ProfilerStop(); |
|||
#endif |
|||
|
|||
INFO("Result: NQueens(%zu) has %.0f solutions.\n", size, sylvan_satcount(res, vars)); |
|||
INFO("Result BDD has %zu nodes.\n", sylvan_nodecount(res)); |
|||
INFO("Computation time: %f sec.\n", t2-t1); |
|||
|
|||
if (report_stats) { |
|||
sylvan_stats_report(stdout); |
|||
} |
|||
|
|||
sylvan_quit(); |
|||
lace_exit(); |
|||
} |
@ -1,127 +0,0 @@ |
|||
#ifdef NDEBUG
|
|||
#undef NDEBUG
|
|||
#endif
|
|||
|
|||
#include <assert.h>
|
|||
#include <stdio.h>
|
|||
#include <stdint.h>
|
|||
|
|||
#include <sylvan.h>
|
|||
#include <sylvan_obj.hpp>
|
|||
#include <storm_function_wrapper.h>
|
|||
#include <sylvan_storm_rational_function.h>
|
|||
|
|||
using namespace sylvan; |
|||
|
|||
VOID_TASK_0(storm_rf) |
|||
{ |
|||
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(storm_rf); |
|||
|
|||
// 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
|
|||
} |
@ -1,5 +0,0 @@ |
|||
# Ignore everything in this directory |
|||
* |
|||
# Except: |
|||
!.gitignore |
|||
!m4_ax_check_compile_flag.m4 |
@ -1,72 +0,0 @@ |
|||
# =========================================================================== |
|||
# 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 |
@ -1,90 +1,79 @@ |
|||
cmake_minimum_required(VERSION 2.6) |
|||
project(sylvan C CXX) |
|||
|
|||
add_library(sylvan |
|||
avl.h |
|||
lace.h |
|||
set(SOURCES |
|||
lace.c |
|||
llmsset.c |
|||
llmsset.h |
|||
refs.h |
|||
refs.c |
|||
sha2.h |
|||
sha2.c |
|||
stats.h |
|||
stats.c |
|||
storm_function_wrapper.h |
|||
sylvan_bdd.c |
|||
sylvan_cache.c |
|||
sylvan_common.c |
|||
sylvan_gmp.c |
|||
sylvan_ldd.c |
|||
sylvan_mt.c |
|||
sylvan_mtbdd.c |
|||
sylvan_obj.cpp |
|||
sylvan_refs.c |
|||
sylvan_sl.c |
|||
sylvan_stats.c |
|||
sylvan_table.c |
|||
storm_function_wrapper.cpp |
|||
sylvan_storm_rational_function.c |
|||
) |
|||
|
|||
set(HEADERS |
|||
lace.h |
|||
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_int.h |
|||
sylvan_ldd.h |
|||
sylvan_ldd.c |
|||
sylvan_ldd_int.h |
|||
sylvan_mt.h |
|||
sylvan_mtbdd.h |
|||
sylvan_mtbdd.c |
|||
sylvan_mtbdd_int.h |
|||
sylvan_obj.hpp |
|||
sylvan_obj.cpp |
|||
sylvan_stats.h |
|||
sylvan_table.h |
|||
sylvan_tls.h |
|||
storm_function_wrapper.h |
|||
sylvan_storm_rational_function.h |
|||
sylvan_storm_rational_function.c |
|||
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}) |
|||
option(BUILD_SHARED_LIBS "Enable/disable creation of shared libraries" ON) |
|||
option(BUILD_STATIC_LIBS "Enable/disable creation of static libraries" ON) |
|||
|
|||
target_link_libraries(sylvan m pthread gmp) |
|||
add_library(sylvan ${SOURCES}) |
|||
|
|||
if(USE_CARL) |
|||
message(STATUS "Sylvan - linking CARL.") |
|||
target_link_libraries(sylvan ${carl_LIBRARIES}) |
|||
endif(USE_CARL) |
|||
find_package(GMP REQUIRED) |
|||
find_package(Hwloc REQUIRED) |
|||
|
|||
include_directories(sylvan ${HWLOC_INCLUDE_DIR} ${GMP_INCLUDE_DIR}) |
|||
target_link_libraries(sylvan m pthread ${GMP_LIBRARIES} ${HWLOC_LIBRARIES}) |
|||
|
|||
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() |
|||
set_target_properties(sylvan PROPERTIES COMPILE_DEFINITIONS "STORM_SILENCE_WARNINGS") |
|||
|
|||
install(TARGETS |
|||
sylvan |
|||
DESTINATION "lib") |
|||
install(TARGETS sylvan DESTINATION "${CMAKE_INSTALL_LIBDIR}") |
|||
install(FILES ${HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") |
|||
|
|||
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") |
|||
|
|||
# MODIFICATIONS NEEDED MADE FOR STORM |
|||
|
|||
# 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}) |
|||
|
|||
if(USE_CARL) |
|||
message(STATUS "Sylvan - linking CArL.") |
|||
target_link_libraries(sylvan ${carl_LIBRARIES}) |
|||
endif(USE_CARL) |
@ -1,39 +0,0 @@ |
|||
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 |
@ -1,245 +0,0 @@ |
|||
/* |
|||
* 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 |
1478
resources/3rdparty/sylvan/src/sylvan_bdd.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,87 +0,0 @@ |
|||
/* |
|||
* 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 BDDs |
|||
*/ |
|||
|
|||
#ifndef SYLVAN_BDD_INT_H |
|||
#define SYLVAN_BDD_INT_H |
|||
|
|||
/** |
|||
* Complement handling macros |
|||
*/ |
|||
#define BDD_HASMARK(s) (s&sylvan_complement?1:0) |
|||
#define BDD_TOGGLEMARK(s) (s^sylvan_complement) |
|||
#define BDD_STRIPMARK(s) (s&~sylvan_complement) |
|||
#define BDD_TRANSFERMARK(from, to) (to ^ (from & sylvan_complement)) |
|||
// Equal under mark |
|||
#define BDD_EQUALM(a, b) ((((a)^(b))&(~sylvan_complement))==0) |
|||
|
|||
/** |
|||
* BDD node structure |
|||
*/ |
|||
typedef struct __attribute__((packed)) bddnode { |
|||
uint64_t a, b; |
|||
} * bddnode_t; // 16 bytes |
|||
|
|||
#define BDD_GETNODE(bdd) ((bddnode_t)llmsset_index_to_ptr(nodes, bdd&0x000000ffffffffff)) |
|||
|
|||
static inline int __attribute__((unused)) |
|||
bddnode_getcomp(bddnode_t n) |
|||
{ |
|||
return n->a & 0x8000000000000000 ? 1 : 0; |
|||
} |
|||
|
|||
static inline uint64_t |
|||
bddnode_getlow(bddnode_t n) |
|||
{ |
|||
return n->b & 0x000000ffffffffff; // 40 bits |
|||
} |
|||
|
|||
static inline uint64_t |
|||
bddnode_gethigh(bddnode_t n) |
|||
{ |
|||
return n->a & 0x800000ffffffffff; // 40 bits plus high bit of first |
|||
} |
|||
|
|||
static inline uint32_t |
|||
bddnode_getvariable(bddnode_t n) |
|||
{ |
|||
return (uint32_t)(n->b >> 40); |
|||
} |
|||
|
|||
static inline int |
|||
bddnode_getmark(bddnode_t n) |
|||
{ |
|||
return n->a & 0x2000000000000000 ? 1 : 0; |
|||
} |
|||
|
|||
static inline void |
|||
bddnode_setmark(bddnode_t n, int mark) |
|||
{ |
|||
if (mark) n->a |= 0x2000000000000000; |
|||
else n->a &= 0xdfffffffffffffff; |
|||
} |
|||
|
|||
static inline void |
|||
bddnode_makenode(bddnode_t n, uint32_t var, uint64_t low, uint64_t high) |
|||
{ |
|||
n->a = high; |
|||
n->b = ((uint64_t)var)<<40 | low; |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,106 @@ |
|||
/* |
|||
* Copyright 2011-2016 Formal Methods and Tools, University of Twente |
|||
* Copyright 2016 Tom van Dijk, Johannes Kepler University Linz |
|||
* |
|||
* 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 of Sylvan |
|||
*/ |
|||
|
|||
#include <sylvan.h> |
|||
|
|||
#include <sylvan_cache.h> |
|||
#include <sylvan_table.h> |
|||
#include <sylvan_stats.h> |
|||
|
|||
#ifndef SYLVAN_INT_H |
|||
#define SYLVAN_INT_H |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif /* __cplusplus */ |
|||
|
|||
/** |
|||
* Nodes table. |
|||
*/ |
|||
extern llmsset_t nodes; |
|||
|
|||
/** |
|||
* Macros for all operation identifiers for the operation cache |
|||
*/ |
|||
|
|||
// 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_PROJECT (4LL<<40) |
|||
#define CACHE_BDD_AND_EXISTS (5LL<<40) |
|||
#define CACHE_BDD_AND_PROJECT (6LL<<40) |
|||
#define CACHE_BDD_RELNEXT (7LL<<40) |
|||
#define CACHE_BDD_RELPREV (8LL<<40) |
|||
#define CACHE_BDD_SATCOUNT (9LL<<40) |
|||
#define CACHE_BDD_COMPOSE (10LL<<40) |
|||
#define CACHE_BDD_RESTRICT (11LL<<40) |
|||
#define CACHE_BDD_CONSTRAIN (12LL<<40) |
|||
#define CACHE_BDD_CLOSURE (13LL<<40) |
|||
#define CACHE_BDD_ISBDD (14LL<<40) |
|||
#define CACHE_BDD_SUPPORT (15LL<<40) |
|||
#define CACHE_BDD_PATHCOUNT (16LL<<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_ABSTRACT_PLUS (44LL<<40) |
|||
#define CACHE_MTBDD_AND_ABSTRACT_MAX (45LL<<40) |
|||
#define CACHE_MTBDD_SUPPORT (46LL<<40) |
|||
#define CACHE_MTBDD_COMPOSE (47LL<<40) |
|||
#define CACHE_MTBDD_EQUAL_NORM (48LL<<40) |
|||
#define CACHE_MTBDD_EQUAL_NORM_REL (49LL<<40) |
|||
#define CACHE_MTBDD_MINIMUM (50LL<<40) |
|||
#define CACHE_MTBDD_MAXIMUM (51LL<<40) |
|||
#define CACHE_MTBDD_LEQ (52LL<<40) |
|||
#define CACHE_MTBDD_LESS (53LL<<40) |
|||
#define CACHE_MTBDD_GEQ (54LL<<40) |
|||
#define CACHE_MTBDD_GREATER (55LL<<40) |
|||
#define CACHE_MTBDD_EVAL_COMPOSE (56LL<<40) |
|||
#define CACHE_MTBDD_NONZERO_COUNT (57LL<<40) |
|||
#define CACHE_MTBDD_AND_EXISTS_RF (58LL<<40) |
|||
#define CACHE_MTBDD_MINIMUM_RF (59LL<<40) |
|||
#define CACHE_MTBDD_MAXIMUM_RF (60LL<<40) |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif /* __cplusplus */ |
|||
|
|||
#include <sylvan_mtbdd_int.h> |
|||
#include <sylvan_ldd_int.h> |
|||
|
|||
#endif |
491
resources/3rdparty/sylvan/src/sylvan_ldd.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,125 @@ |
|||
/* |
|||
* Copyright 2011-2016 Formal Methods and Tools, University of Twente |
|||
* Copyright 2016 Tom van Dijk, Johannes Kepler University Linz |
|||
* |
|||
* 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 <pthread.h> |
|||
#include <stdint.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
|
|||
#include <sylvan.h> |
|||
#include <sylvan_int.h> |
|||
|
|||
#include <avl.h> |
|||
#include <sylvan_refs.h> |
|||
#include <sha2.h> |
|||
*/ |
|||
|
|||
/** |
|||
* Internals for LDDs |
|||
*/ |
|||
|
|||
#ifndef SYLVAN_LDD_INT_H |
|||
#define SYLVAN_LDD_INT_H |
|||
|
|||
/** |
|||
* LDD node structure |
|||
* |
|||
* RmRR RRRR RRRR VVVV | VVVV DcDD DDDD DDDD (little endian - in memory) |
|||
* VVVV RRRR RRRR RRRm | DDDD DDDD DDDc VVVV (big endian) |
|||
*/ |
|||
typedef struct __attribute__((packed)) mddnode { |
|||
uint64_t a, b; |
|||
} * mddnode_t; // 16 bytes |
|||
|
|||
#define LDD_GETNODE(mdd) ((mddnode_t)llmsset_index_to_ptr(nodes, mdd)) |
|||
|
|||
static inline uint32_t __attribute__((unused)) |
|||
mddnode_getvalue(mddnode_t n) |
|||
{ |
|||
return *(uint32_t*)((uint8_t*)n+6); |
|||
} |
|||
|
|||
static inline uint8_t __attribute__((unused)) |
|||
mddnode_getmark(mddnode_t n) |
|||
{ |
|||
return n->a & 1; |
|||
} |
|||
|
|||
static inline uint8_t __attribute__((unused)) |
|||
mddnode_getcopy(mddnode_t n) |
|||
{ |
|||
return n->b & 0x10000 ? 1 : 0; |
|||
} |
|||
|
|||
static inline uint64_t __attribute__((unused)) |
|||
mddnode_getright(mddnode_t n) |
|||
{ |
|||
return (n->a & 0x0000ffffffffffff) >> 1; |
|||
} |
|||
|
|||
static inline uint64_t __attribute__((unused)) |
|||
mddnode_getdown(mddnode_t n) |
|||
{ |
|||
return n->b >> 17; |
|||
} |
|||
|
|||
static inline void __attribute__((unused)) |
|||
mddnode_setvalue(mddnode_t n, uint32_t value) |
|||
{ |
|||
*(uint32_t*)((uint8_t*)n+6) = value; |
|||
} |
|||
|
|||
static inline void __attribute__((unused)) |
|||
mddnode_setmark(mddnode_t n, uint8_t mark) |
|||
{ |
|||
n->a = (n->a & 0xfffffffffffffffe) | (mark ? 1 : 0); |
|||
} |
|||
|
|||
static inline void __attribute__((unused)) |
|||
mddnode_setright(mddnode_t n, uint64_t right) |
|||
{ |
|||
n->a = (n->a & 0xffff000000000001) | (right << 1); |
|||
} |
|||
|
|||
static inline void __attribute__((unused)) |
|||
mddnode_setdown(mddnode_t n, uint64_t down) |
|||
{ |
|||
n->b = (n->b & 0x000000000001ffff) | (down << 17); |
|||
} |
|||
|
|||
static inline void __attribute__((unused)) |
|||
mddnode_make(mddnode_t n, uint32_t value, uint64_t right, uint64_t down) |
|||
{ |
|||
n->a = right << 1; |
|||
n->b = down << 17; |
|||
*(uint32_t*)((uint8_t*)n+6) = value; |
|||
} |
|||
|
|||
static inline void __attribute__((unused)) |
|||
mddnode_makecopy(mddnode_t n, uint64_t right, uint64_t down) |
|||
{ |
|||
n->a = right << 1; |
|||
n->b = ((down << 1) | 1) << 16; |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,266 @@ |
|||
/* |
|||
* Copyright 2011-2016 Formal Methods and Tools, University of Twente |
|||
* Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz |
|||
* |
|||
* 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 <stdlib.h> |
|||
#include <string.h> |
|||
|
|||
#include <sylvan_mt.h> |
|||
#include <sylvan_int.h> // for llmsset*, nodes, sylvan_register_quit |
|||
|
|||
/** |
|||
* Handling of custom leaves "registry" |
|||
*/ |
|||
|
|||
typedef struct |
|||
{ |
|||
sylvan_mt_hash_cb hash_cb; |
|||
sylvan_mt_equals_cb equals_cb; |
|||
sylvan_mt_create_cb create_cb; |
|||
sylvan_mt_destroy_cb destroy_cb; |
|||
sylvan_mt_to_str_cb to_str_cb; |
|||
sylvan_mt_write_binary_cb write_binary_cb; |
|||
sylvan_mt_read_binary_cb read_binary_cb; |
|||
} customleaf_t; |
|||
|
|||
static customleaf_t *cl_registry; |
|||
static size_t cl_registry_count; |
|||
static size_t cl_registry_size; |
|||
|
|||
/** |
|||
* Implementation of hooks for llmsset |
|||
*/ |
|||
|
|||
/** |
|||
* Internal helper function |
|||
*/ |
|||
static inline customleaf_t* |
|||
sylvan_mt_from_node(uint64_t a, uint64_t b) |
|||
{ |
|||
uint32_t type = a & 0xffffffff; |
|||
assert(type < cl_registry_count); |
|||
return cl_registry + type; |
|||
(void)b; |
|||
} |
|||
|
|||
static void |
|||
_sylvan_create_cb(uint64_t *a, uint64_t *b) |
|||
{ |
|||
customleaf_t *c = sylvan_mt_from_node(*a, *b); |
|||
if (c->create_cb != NULL) c->create_cb(b); |
|||
} |
|||
|
|||
static void |
|||
_sylvan_destroy_cb(uint64_t a, uint64_t b) |
|||
{ |
|||
// for leaf |
|||
customleaf_t *c = sylvan_mt_from_node(a, b); |
|||
if (c->destroy_cb != NULL) c->destroy_cb(b); |
|||
} |
|||
|
|||
static uint64_t |
|||
_sylvan_hash_cb(uint64_t a, uint64_t b, uint64_t seed) |
|||
{ |
|||
customleaf_t *c = sylvan_mt_from_node(a, b); |
|||
if (c->hash_cb != NULL) return c->hash_cb(b, seed ^ a); |
|||
else return llmsset_hash(a, b, seed); |
|||
} |
|||
|
|||
static int |
|||
_sylvan_equals_cb(uint64_t a, uint64_t b, uint64_t aa, uint64_t bb) |
|||
{ |
|||
if (a != aa) return 0; |
|||
customleaf_t *c = sylvan_mt_from_node(a, b); |
|||
if (c->equals_cb != NULL) return c->equals_cb(b, bb); |
|||
else return b == bb ? 1 : 0; |
|||
} |
|||
|
|||
uint32_t |
|||
sylvan_mt_create_type() |
|||
{ |
|||
if (cl_registry_count == cl_registry_size) { |
|||
// resize registry array |
|||
cl_registry_size += 8; |
|||
cl_registry = (customleaf_t *)realloc(cl_registry, sizeof(customleaf_t) * (cl_registry_size)); |
|||
memset(cl_registry + cl_registry_count, 0, sizeof(customleaf_t) * (cl_registry_size-cl_registry_count)); |
|||
} |
|||
return cl_registry_count++; |
|||
} |
|||
|
|||
void sylvan_mt_set_hash(uint32_t type, sylvan_mt_hash_cb hash_cb) |
|||
{ |
|||
customleaf_t *c = cl_registry + type; |
|||
c->hash_cb = hash_cb; |
|||
} |
|||
|
|||
void sylvan_mt_set_equals(uint32_t type, sylvan_mt_equals_cb equals_cb) |
|||
{ |
|||
customleaf_t *c = cl_registry + type; |
|||
c->equals_cb = equals_cb; |
|||
} |
|||
|
|||
void sylvan_mt_set_create(uint32_t type, sylvan_mt_create_cb create_cb) |
|||
{ |
|||
customleaf_t *c = cl_registry + type; |
|||
c->create_cb = create_cb; |
|||
} |
|||
|
|||
void sylvan_mt_set_destroy(uint32_t type, sylvan_mt_destroy_cb destroy_cb) |
|||
{ |
|||
customleaf_t *c = cl_registry + type; |
|||
c->destroy_cb = destroy_cb; |
|||
} |
|||
|
|||
void sylvan_mt_set_to_str(uint32_t type, sylvan_mt_to_str_cb to_str_cb) |
|||
{ |
|||
customleaf_t *c = cl_registry + type; |
|||
c->to_str_cb = to_str_cb; |
|||
} |
|||
|
|||
void sylvan_mt_set_write_binary(uint32_t type, sylvan_mt_write_binary_cb write_binary_cb) |
|||
{ |
|||
customleaf_t *c = cl_registry + type; |
|||
c->write_binary_cb = write_binary_cb; |
|||
} |
|||
|
|||
void sylvan_mt_set_read_binary(uint32_t type, sylvan_mt_read_binary_cb read_binary_cb) |
|||
{ |
|||
customleaf_t *c = cl_registry + type; |
|||
c->read_binary_cb = read_binary_cb; |
|||
} |
|||
|
|||
/** |
|||
* Initialize and quit functions |
|||
*/ |
|||
|
|||
static int mt_initialized = 0; |
|||
|
|||
static void |
|||
sylvan_mt_quit() |
|||
{ |
|||
if (mt_initialized == 0) return; |
|||
mt_initialized = 0; |
|||
|
|||
free(cl_registry); |
|||
cl_registry = NULL; |
|||
cl_registry_count = 0; |
|||
cl_registry_size = 0; |
|||
} |
|||
|
|||
void |
|||
sylvan_init_mt() |
|||
{ |
|||
if (mt_initialized) return; |
|||
mt_initialized = 1; |
|||
|
|||
// Register quit handler to free structures |
|||
sylvan_register_quit(sylvan_mt_quit); |
|||
|
|||
// Tell llmsset to use our custom hooks |
|||
llmsset_set_custom(nodes, _sylvan_hash_cb, _sylvan_equals_cb, _sylvan_create_cb, _sylvan_destroy_cb); |
|||
|
|||
// Initialize data structures |
|||
cl_registry_size = 8; |
|||
cl_registry = (customleaf_t *)calloc(sizeof(customleaf_t), cl_registry_size); |
|||
cl_registry_count = 3; // 0, 1, 2 are taken |
|||
} |
|||
|
|||
/** |
|||
* Return 1 if the given <type> has a custom hash callback, or 0 otherwise. |
|||
*/ |
|||
int |
|||
sylvan_mt_has_custom_hash(uint32_t type) |
|||
{ |
|||
assert(type < cl_registry_count); |
|||
customleaf_t *c = cl_registry + type; |
|||
return c->hash_cb != NULL ? 1 : 0; |
|||
} |
|||
|
|||
/** |
|||
* Convert a leaf (possibly complemented) to a string representation. |
|||
* If it does not fit in <buf> of size <buflen>, returns a freshly allocated char* array. |
|||
*/ |
|||
char* |
|||
sylvan_mt_to_str(int complement, uint32_t type, uint64_t value, char* buf, size_t buflen) |
|||
{ |
|||
assert(type < cl_registry_count); |
|||
customleaf_t *c = cl_registry + type; |
|||
if (type == 0) { |
|||
size_t required = (size_t)snprintf(NULL, 0, "%" PRId64, (int64_t)value); |
|||
char *ptr = buf; |
|||
if (buflen < required) { |
|||
ptr = (char*)malloc(required); |
|||
buflen = required; |
|||
} |
|||
if (ptr != NULL) snprintf(ptr, buflen, "%" PRId64, (int64_t)value); |
|||
return ptr; |
|||
} else if (type == 1) { |
|||
size_t required = (size_t)snprintf(NULL, 0, "%f", *(double*)&value); |
|||
char *ptr = buf; |
|||
if (buflen < required) { |
|||
ptr = (char*)malloc(required); |
|||
buflen = required; |
|||
} |
|||
if (ptr != NULL) snprintf(ptr, buflen, "%f", *(double*)&value); |
|||
return ptr; |
|||
} else if (type == 2) { |
|||
int32_t num = (int32_t)(value>>32); |
|||
uint32_t denom = value&0xffffffff; |
|||
size_t required = (size_t)snprintf(NULL, 0, "%" PRId32 "/%" PRIu32, num, denom); |
|||
char *ptr = buf; |
|||
if (buflen < required) { |
|||
ptr = (char*)malloc(required); |
|||
buflen = required; |
|||
} |
|||
if (ptr != NULL) snprintf(ptr, buflen, "%" PRId32 "/%" PRIu32, num, denom); |
|||
return ptr; |
|||
} else if (c->to_str_cb != NULL) { |
|||
return c->to_str_cb(complement, value, buf, buflen); |
|||
} else { |
|||
return NULL; |
|||
} |
|||
} |
|||
|
|||
uint64_t |
|||
sylvan_mt_hash(uint32_t type, uint64_t value, uint64_t seed) |
|||
{ |
|||
assert(type < cl_registry_count); |
|||
customleaf_t *c = cl_registry + type; |
|||
if (c->hash_cb != NULL) return c->hash_cb(value, seed); |
|||
else return llmsset_hash((uint64_t)type, value, seed); |
|||
} |
|||
|
|||
int |
|||
sylvan_mt_write_binary(uint32_t type, uint64_t value, FILE *out) |
|||
{ |
|||
assert(type < cl_registry_count); |
|||
customleaf_t *c = cl_registry + type; |
|||
if (c->write_binary_cb != NULL) return c->write_binary_cb(out, value); |
|||
else return 0; |
|||
} |
|||
|
|||
int |
|||
sylvan_mt_read_binary(uint32_t type, uint64_t *value, FILE *in) |
|||
{ |
|||
assert(type < cl_registry_count); |
|||
customleaf_t *c = cl_registry + type; |
|||
if (c->read_binary_cb != NULL) return c->read_binary_cb(in, value); |
|||
else return 0; |
|||
} |
@ -0,0 +1,132 @@ |
|||
/* |
|||
* Copyright 2011-2016 Formal Methods and Tools, University of Twente |
|||
* Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz |
|||
* |
|||
* 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 file contains declarations for custom Multi-Terminal support. |
|||
*/ |
|||
|
|||
#ifndef SYLVAN_MT_H |
|||
#define SYLVAN_MT_H |
|||
|
|||
#include <stddef.h> |
|||
#include <stdint.h> |
|||
#include <stdio.h> |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif /* __cplusplus */ |
|||
|
|||
/** |
|||
* Helper implementation for custom terminal (multi-terminal support) |
|||
* Types can implement the following callback functions: |
|||
* |
|||
* hash(value, seed) |
|||
* return hash of value with given seed |
|||
* equals(value1, value2) |
|||
* return 1 if equal, 0 if not equal |
|||
* create(&value) |
|||
* optionally allocate object and update value with the pointer |
|||
* destroy(value) |
|||
* optionally destroy/free value if value points to an allocated object |
|||
* to_str(complemented, value, buf, bufsize) |
|||
* return string representation of (complemented) value to buf if bufsize large enough, |
|||
* otherwise return newly allocated string |
|||
* write_binary(fp, value) |
|||
* write binary representation of a leaf to given FILE* fp |
|||
* return 0 if successful |
|||
* read_binary(fp, &value) |
|||
* read binary representation of a leaf from given FILE* fp |
|||
* treat allocated objects like create (and destroy) |
|||
* return 0 if successful |
|||
* |
|||
* If the 64-byte value already completely describes the leaf, then the functions |
|||
* write_binary and read_binary should be set to NULL. |
|||
* |
|||
* If the 64-byte value is also already a canonical representation, then the functions |
|||
* hash, equals, create and destroy should be set to NULL. |
|||
* |
|||
* Two values are equal (with equals) iff they have the same hash (with hash) |
|||
* |
|||
* A value v obtained due to create must be equal to the original value (with equals): |
|||
* create(v) => equals(\old(v), \new(v)) |
|||
* |
|||
* NOTE ON EXPECTED LEAF NODE STRUCTURE: the implementation expects leaves in a specific format: |
|||
* - 16-byte node { uint64_t a, b; } |
|||
* - type == a & 0x00000000ffffffff |
|||
* - value == b |
|||
*/ |
|||
typedef uint64_t (*sylvan_mt_hash_cb)(uint64_t, uint64_t); |
|||
typedef int (*sylvan_mt_equals_cb)(uint64_t, uint64_t); |
|||
typedef void (*sylvan_mt_create_cb)(uint64_t*); |
|||
typedef void (*sylvan_mt_destroy_cb)(uint64_t); |
|||
typedef char* (*sylvan_mt_to_str_cb)(int, uint64_t, char*, size_t); |
|||
typedef int (*sylvan_mt_write_binary_cb)(FILE*, uint64_t); |
|||
typedef int (*sylvan_mt_read_binary_cb)(FILE*, uint64_t*); |
|||
|
|||
/** |
|||
* Initialize the multi-terminal subsystem |
|||
*/ |
|||
void sylvan_init_mt(void); |
|||
|
|||
/** |
|||
* Register a new leaf type. |
|||
*/ |
|||
uint32_t sylvan_mt_create_type(void); |
|||
|
|||
/** |
|||
* Set the callback handlers for <type> |
|||
*/ |
|||
void sylvan_mt_set_hash(uint32_t type, sylvan_mt_hash_cb hash_cb); |
|||
void sylvan_mt_set_equals(uint32_t type, sylvan_mt_equals_cb equals_cb); |
|||
void sylvan_mt_set_create(uint32_t type, sylvan_mt_create_cb create_cb); |
|||
void sylvan_mt_set_destroy(uint32_t type, sylvan_mt_destroy_cb destroy_cb); |
|||
void sylvan_mt_set_to_str(uint32_t type, sylvan_mt_to_str_cb to_str_cb); |
|||
void sylvan_mt_set_write_binary(uint32_t type, sylvan_mt_write_binary_cb write_binary_cb); |
|||
void sylvan_mt_set_read_binary(uint32_t type, sylvan_mt_read_binary_cb read_binary_cb); |
|||
|
|||
/** |
|||
* Returns 1 if the given type implements hash, or 0 otherwise. |
|||
* (used when inserting into the unique table) |
|||
*/ |
|||
int sylvan_mt_has_custom_hash(uint32_t type); |
|||
|
|||
/** |
|||
* Get a hash for given value (calls hash callback of type). |
|||
* If the type does not implement hash, then this is the same hash as used by the unique table. |
|||
*/ |
|||
uint64_t sylvan_mt_hash(uint32_t type, uint64_t value, uint64_t seed); |
|||
|
|||
/** |
|||
* Get text representation of leaf (calls to_str callback of type). |
|||
*/ |
|||
char *sylvan_mt_to_str(int complement, uint32_t type, uint64_t value, char *buf, size_t buflen); |
|||
|
|||
/** |
|||
* Write a leaf in binary form (calls write_binary callback of type). |
|||
*/ |
|||
int sylvan_mt_write_binary(uint32_t type, uint64_t value, FILE *out); |
|||
|
|||
/** |
|||
* Read a leaf in binary form (calls read_binary callback of type). |
|||
*/ |
|||
int sylvan_mt_read_binary(uint32_t type, uint64_t *value, FILE *in); |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif /* __cplusplus */ |
|||
|
|||
#endif |
1437
resources/3rdparty/sylvan/src/sylvan_mtbdd.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1 @@ |
|||
static void initCustomMtbdd(); |
@ -1,5 +1,6 @@ |
|||
/* |
|||
* Copyright 2011-2015 Formal Methods and Tools, University of Twente |
|||
* Copyright 2011-2016 Formal Methods and Tools, University of Twente |
|||
* Copyright 2016 Tom van Dijk, Johannes Kepler University Linz |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
@ -0,0 +1,172 @@ |
|||
/* |
|||
* Copyright 2016 Tom van Dijk, Johannes Kepler University Linz |
|||
* |
|||
* 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 <assert.h> |
|||
#include <stdio.h> |
|||
#include <sys/mman.h> // for mmap, munmap, etc |
|||
|
|||
#include <sylvan.h> |
|||
#include <sylvan_sl.h> |
|||
|
|||
/* A SL_DEPTH of 6 means 32 bytes per bucket, of 14 means 64 bytes per bucket. |
|||
However, there is a very large performance drop with only 6 levels. */ |
|||
#define SL_DEPTH 14 |
|||
|
|||
typedef struct |
|||
{ |
|||
BDD dd; |
|||
uint32_t next[SL_DEPTH]; |
|||
} sl_bucket; |
|||
|
|||
struct sylvan_skiplist |
|||
{ |
|||
sl_bucket *buckets; |
|||
size_t size; |
|||
size_t next; |
|||
}; |
|||
|
|||
#ifndef cas |
|||
#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new))) |
|||
#endif |
|||
|
|||
sylvan_skiplist_t |
|||
sylvan_skiplist_alloc(size_t size) |
|||
{ |
|||
if (size >= 0x80000000) { |
|||
fprintf(stderr, "sylvan: Trying to allocate a skiplist >= 0x80000000 buckets!\n"); |
|||
exit(1); |
|||
} |
|||
sylvan_skiplist_t l = malloc(sizeof(struct sylvan_skiplist)); |
|||
l->buckets = (sl_bucket*)mmap(0, sizeof(sl_bucket)*size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); |
|||
if (l->buckets == (sl_bucket*)-1) { |
|||
fprintf(stderr, "sylvan: Unable to allocate virtual memory (%'zu bytes) for the skiplist!\n", size*sizeof(sl_bucket)); |
|||
exit(1); |
|||
} |
|||
l->size = size; |
|||
l->next = 1; |
|||
return l; |
|||
} |
|||
|
|||
void |
|||
sylvan_skiplist_free(sylvan_skiplist_t l) |
|||
{ |
|||
munmap(l->buckets, sizeof(sl_bucket)*l->size); |
|||
free(l); |
|||
} |
|||
|
|||
/** |
|||
* Return the assigned number of the given dd, |
|||
* or 0 if not found. |
|||
*/ |
|||
uint64_t |
|||
sylvan_skiplist_get(sylvan_skiplist_t l, MTBDD dd) |
|||
{ |
|||
if (dd == mtbdd_false || dd == mtbdd_true) return 0; |
|||
|
|||
uint32_t loc = 0, k = SL_DEPTH-1; |
|||
for (;;) { |
|||
/* invariant: [loc].dd < dd */ |
|||
/* note: this is always true for loc==0 */ |
|||
sl_bucket *e = l->buckets + loc; |
|||
uint32_t loc_next = (*(volatile uint32_t*)&e->next[k]) & 0x7fffffff; |
|||
if (loc_next != 0 && l->buckets[loc_next].dd == dd) { |
|||
/* found */ |
|||
return loc_next; |
|||
} else if (loc_next != 0 && l->buckets[loc_next].dd < dd) { |
|||
/* go right */ |
|||
loc = loc_next; |
|||
} else if (k > 0) { |
|||
/* go down */ |
|||
k--; |
|||
} else { |
|||
return 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
VOID_TASK_IMPL_2(sylvan_skiplist_assign_next, sylvan_skiplist_t, l, MTBDD, dd) |
|||
{ |
|||
if (dd == mtbdd_false || dd == mtbdd_true) return; |
|||
|
|||
uint32_t trace[SL_DEPTH]; |
|||
uint32_t loc = 0, loc_next = 0, k = SL_DEPTH-1; |
|||
for (;;) { |
|||
/* invariant: [loc].dd < dd */ |
|||
/* note: this is always true for loc==0 */ |
|||
sl_bucket *e = l->buckets + loc; |
|||
loc_next = (*(volatile uint32_t*)&e->next[k]) & 0x7fffffff; |
|||
if (loc_next != 0 && l->buckets[loc_next].dd == dd) { |
|||
/* found */ |
|||
return; |
|||
} else if (loc_next != 0 && l->buckets[loc_next].dd < dd) { |
|||
/* go right */ |
|||
loc = loc_next; |
|||
} else if (k > 0) { |
|||
/* go down */ |
|||
trace[k] = loc; |
|||
k--; |
|||
} else if (!(e->next[0] & 0x80000000) && cas(&e->next[0], loc_next, loc_next|0x80000000)) { |
|||
/* locked */ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/* claim next item */ |
|||
const uint64_t next = __sync_fetch_and_add(&l->next, 1); |
|||
if (next >= l->size) { |
|||
fprintf(stderr, "Out of cheese exception, no more blocks available\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
/* fill next item */ |
|||
sl_bucket *a = l->buckets + next; |
|||
a->dd = dd; |
|||
a->next[0] = loc_next; |
|||
compiler_barrier(); |
|||
l->buckets[loc].next[0] = next; |
|||
|
|||
/* determine height */ |
|||
uint64_t h = 1 + __builtin_clz(LACE_TRNG) / 2; |
|||
if (h > SL_DEPTH) h = SL_DEPTH; |
|||
|
|||
/* go up and create links */ |
|||
for (k=1;k<h;k++) { |
|||
loc = trace[k]; |
|||
for (;;) { |
|||
sl_bucket *e = l->buckets + loc; |
|||
/* note, at k>0, no locks on edges */ |
|||
uint32_t loc_next = *(volatile uint32_t*)&e->next[k]; |
|||
if (loc_next != 0 && l->buckets[loc_next].dd < dd) { |
|||
loc = loc_next; |
|||
} else { |
|||
a->next[k] = loc_next; |
|||
if (cas(&e->next[k], loc_next, next)) break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
size_t |
|||
sylvan_skiplist_count(sylvan_skiplist_t l) |
|||
{ |
|||
return l->next - 1; |
|||
} |
|||
|
|||
MTBDD |
|||
sylvan_skiplist_getr(sylvan_skiplist_t l, uint64_t index) |
|||
{ |
|||
return l->buckets[index].dd; |
|||
} |
@ -0,0 +1,70 @@ |
|||
/* |
|||
* Copyright 2016 Tom van Dijk, Johannes Kepler University Linz |
|||
* |
|||
* 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_SKIPLIST_H |
|||
#define SYLVAN_SKIPLIST_H |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif /* __cplusplus */ |
|||
|
|||
/** |
|||
* Implementation of a simple limited-depth skiplist. |
|||
* The skiplist is used by the serialization mechanism in Sylvan. |
|||
* Each stored MTBDD is assigned a number starting with 1. |
|||
* Each bucket takes 32 bytes. |
|||
*/ |
|||
|
|||
typedef struct sylvan_skiplist *sylvan_skiplist_t; |
|||
|
|||
/** |
|||
* Allocate a new skiplist of maximum size <size>. |
|||
* Only supports at most 0x7fffffff (max int32) buckets |
|||
*/ |
|||
sylvan_skiplist_t sylvan_skiplist_alloc(size_t size); |
|||
|
|||
/** |
|||
* Free the given skiplist. |
|||
*/ |
|||
void sylvan_skiplist_free(sylvan_skiplist_t sl); |
|||
|
|||
/** |
|||
* Get the number assigned to the given node <dd>. |
|||
* Returns 0 if no number was assigned. |
|||
*/ |
|||
uint64_t sylvan_skiplist_get(sylvan_skiplist_t sl, MTBDD dd); |
|||
|
|||
/** |
|||
* Assign the next number (starting at 1) to the given node <dd>. |
|||
*/ |
|||
VOID_TASK_DECL_2(sylvan_skiplist_assign_next, sylvan_skiplist_t, MTBDD); |
|||
#define sylvan_skiplist_assign_next(sl, dd) CALL(sylvan_skiplist_assign_next, sl, dd) |
|||
|
|||
/** |
|||
* Give the number of assigned nodes. (numbers 1,2,...,N) |
|||
*/ |
|||
size_t sylvan_skiplist_count(sylvan_skiplist_t sl); |
|||
|
|||
/** |
|||
* Get the MTBDD assigned to the number <index>, with the index 1,...,count. |
|||
*/ |
|||
MTBDD sylvan_skiplist_getr(sylvan_skiplist_t sl, uint64_t index); |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif /* __cplusplus */ |
|||
|
|||
#endif |
@ -0,0 +1,296 @@ |
|||
/* |
|||
* Copyright 2011-2016 Formal Methods and Tools, University of Twente |
|||
* Copyright 2016 Tom van Dijk, Johannes Kepler University Linz |
|||
* |
|||
* 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 <sylvan_stats.h> |
|||
#include <sys/mman.h> |
|||
#include <inttypes.h> |
|||
|
|||
#include <sylvan_int.h> |
|||
|
|||
#if SYLVAN_STATS |
|||
|
|||
#ifdef __ELF__ |
|||
__thread sylvan_stats_t sylvan_stats; |
|||
#else |
|||
pthread_key_t sylvan_stats_key; |
|||
#endif |
|||
|
|||
#include <hwloc.h> |
|||
static hwloc_topology_t topo; |
|||
|
|||
/** |
|||
* Instructions for sylvan_stats_report |
|||
*/ |
|||
struct |
|||
{ |
|||
int type; /* 0 for print line, 1 for simple counter, 2 for operation with CACHED and CACHEDPUT */ |
|||
/* 3 for timer, 4 for report table data */ |
|||
int id; |
|||
const char *key; |
|||
} sylvan_report_info[] = |
|||
{ |
|||
{0, 0, "Tables"}, |
|||
{1, BDD_NODES_CREATED, "MTBDD nodes created"}, |
|||
{1, BDD_NODES_REUSED, "MTBDD nodes reused"}, |
|||
{1, LDD_NODES_CREATED, "LDD nodes created"}, |
|||
{1, LDD_NODES_REUSED, "LDD nodes reused"}, |
|||
{1, LLMSSET_LOOKUP, "Lookup iterations"}, |
|||
{4, 0, NULL}, /* trigger to report unique nodes and operation cache */ |
|||
|
|||
{0, 0, "Operation Count Cache get Cache put"}, |
|||
{2, BDD_AND, "BDD and"}, |
|||
{2, BDD_XOR, "BDD xor"}, |
|||
{2, BDD_ITE, "BDD ite"}, |
|||
{2, BDD_EXISTS, "BDD exists"}, |
|||
{2, BDD_PROJECT, "BDD project"}, |
|||
{2, BDD_AND_EXISTS, "BDD andexists"}, |
|||
{2, BDD_AND_PROJECT, "BDD andproject"}, |
|||
{2, BDD_RELNEXT, "BDD relnext"}, |
|||
{2, BDD_RELPREV, "BDD relprev"}, |
|||
{2, BDD_CLOSURE, "BDD closure"}, |
|||
{2, BDD_COMPOSE, "BDD compose"}, |
|||
{2, BDD_RESTRICT, "BDD restrict"}, |
|||
{2, BDD_CONSTRAIN, "BDD constrain"}, |
|||
{2, BDD_SUPPORT, "BDD support"}, |
|||
{2, BDD_SATCOUNT, "BDD satcount"}, |
|||
{2, BDD_PATHCOUNT, "BDD pathcount"}, |
|||
{2, BDD_ISBDD, "BDD isbdd"}, |
|||
|
|||
{2, MTBDD_APPLY, "MTBDD binary apply"}, |
|||
{2, MTBDD_UAPPLY, "MTBDD unary apply"}, |
|||
{2, MTBDD_ABSTRACT, "MTBDD abstract"}, |
|||
{2, MTBDD_ITE, "MTBDD ite"}, |
|||
{2, MTBDD_EQUAL_NORM, "MTBDD eq norm"}, |
|||
{2, MTBDD_EQUAL_NORM_REL, "MTBDD eq norm rel"}, |
|||
{2, MTBDD_LEQ, "MTBDD leq"}, |
|||
{2, MTBDD_LESS, "MTBDD less"}, |
|||
{2, MTBDD_GEQ, "MTBDD geq"}, |
|||
{2, MTBDD_GREATER, "MTBDD greater"}, |
|||
{2, MTBDD_AND_ABSTRACT_PLUS, "MTBDD and_abs_plus"}, |
|||
{2, MTBDD_AND_ABSTRACT_MAX, "MTBDD and_abs_max"}, |
|||
{2, MTBDD_COMPOSE, "MTBDD compose"}, |
|||
{2, MTBDD_MINIMUM, "MTBDD minimum"}, |
|||
{2, MTBDD_MAXIMUM, "MTBDD maximum"}, |
|||
{2, MTBDD_EVAL_COMPOSE, "MTBDD eval_compose"}, |
|||
|
|||
{2, LDD_UNION, "LDD union"}, |
|||
{2, LDD_MINUS, "LDD minus"}, |
|||
{2, LDD_INTERSECT, "LDD intersect"}, |
|||
{2, LDD_RELPROD, "LDD relprod"}, |
|||
{2, LDD_RELPREV, "LDD relprev"}, |
|||
{2, LDD_PROJECT, "LDD project"}, |
|||
{2, LDD_JOIN, "LDD join"}, |
|||
{2, LDD_MATCH, "LDD match"}, |
|||
{2, LDD_SATCOUNT, "LDD satcount"}, |
|||
{2, LDD_SATCOUNTL, "LDD satcountl"}, |
|||
{2, LDD_ZIP, "LDD zip"}, |
|||
{2, LDD_RELPROD_UNION, "LDD relprod_union"}, |
|||
{2, LDD_PROJECT_MINUS, "LDD project_minus"}, |
|||
|
|||
{0, 0, "Garbage collection"}, |
|||
{1, SYLVAN_GC_COUNT, "GC executions"}, |
|||
{3, SYLVAN_GC, "Total time spent"}, |
|||
|
|||
{-1, -1, NULL}, |
|||
}; |
|||
|
|||
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); |
|||
} |
|||
// 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); |
|||
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 |
|||
hwloc_topology_init(&topo); |
|||
hwloc_topology_load(topo); |
|||
TOGETHER(sylvan_stats_reset_perthread); |
|||
} |
|||
|
|||
/** |
|||
* Reset all counters (for statistics) |
|||
*/ |
|||
VOID_TASK_IMPL_0(sylvan_stats_reset) |
|||
{ |
|||
TOGETHER(sylvan_stats_reset_perthread); |
|||
} |
|||
|
|||
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_TASK_IMPL_1(sylvan_stats_snapshot, sylvan_stats_t*, target) |
|||
{ |
|||
memset(target, 0, sizeof(sylvan_stats_t)); |
|||
TOGETHER(sylvan_stats_sum, target); |
|||
} |
|||
|
|||
#define BLACK "\33[22;30m" |
|||
#define GRAY "\33[1;30m" |
|||
#define RED "\33[22;31m" |
|||
#define LRED "\33[1;31m" |
|||
#define GREEN "\33[22;32m" |
|||
#define LGREEN "\33[1;32m" |
|||
#define BROWN "\33[22;33m" |
|||
#define YELLOW "\33[1;33m" |
|||
#define BLUE "\33[22;34m" |
|||
#define LBLUE "\33[1;34m" |
|||
#define MAGENTA "\33[22;35m" |
|||
#define LMAGENTA "\33[1;35m" |
|||
#define CYAN "\33[22;36m" |
|||
#define LCYAN "\33[1;36m" |
|||
#define LGRAY "\33[22;37m" |
|||
#define WHITE "\33[1;37m" |
|||
#define NC "\33[m" |
|||
#define BOLD "\33[1m" |
|||
#define ULINE "\33[4m" |
|||
#define PINK "\33[38;5;200m" |
|||
|
|||
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; |
|||
} |
|||
|
|||
void |
|||
sylvan_stats_report(FILE *target) |
|||
{ |
|||
LACE_ME; |
|||
sylvan_stats_t totals; |
|||
sylvan_stats_snapshot(&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 |
|||
|
|||
int color = isatty(fileno(target)) ? 1 : 0; |
|||
if (color) fprintf(target, ULINE WHITE "Sylvan statistics\n" NC); |
|||
else fprintf(target, "Sylvan statistics\n"); |
|||
|
|||
int i=0; |
|||
for (;;) { |
|||
if (sylvan_report_info[i].id == -1) break; |
|||
int id = sylvan_report_info[i].id; |
|||
int type = sylvan_report_info[i].type; |
|||
if (type == 0) { |
|||
if (color) fprintf(target, WHITE "\n%s\n" NC, sylvan_report_info[i].key); |
|||
else fprintf(target, "\n%s\n", sylvan_report_info[i].key); |
|||
} else if (type == 1) { |
|||
if (totals.counters[id] > 0) { |
|||
fprintf(target, "%-20s %'-16"PRIu64"\n", sylvan_report_info[i].key, totals.counters[id]); |
|||
} |
|||
} else if (type == 2) { |
|||
if (totals.counters[id] > 0) { |
|||
fprintf(target, "%-20s %'-16"PRIu64 " %'-16"PRIu64" %'-16"PRIu64 "\n", sylvan_report_info[i].key, totals.counters[id], totals.counters[id+1], totals.counters[id+2]); |
|||
} |
|||
} else if (type == 3) { |
|||
if (totals.timers[id] > 0) { |
|||
fprintf(target, "%-20s %'.6Lf sec.\n", sylvan_report_info[i].key, (long double)totals.timers[id]/1000000000); |
|||
} |
|||
} else if (type == 4) { |
|||
fprintf(target, "%-20s %'zu of %'zu buckets filled.\n", "Unique nodes table", llmsset_count_marked(nodes), llmsset_get_size(nodes)); |
|||
fprintf(target, "%-20s %'zu of %'zu buckets filled.\n", "Operation cache", cache_getused(), cache_getsize()); |
|||
char buf[64], buf2[64]; |
|||
to_h(24ULL * llmsset_get_size(nodes), buf); |
|||
to_h(24ULL * llmsset_get_max_size(nodes), buf2); |
|||
fprintf(target, "%-20s %s (max real) of %s (allocated virtual memory).\n", "Memory (nodes)", buf, buf2); |
|||
to_h(36ULL * cache_getsize(), buf); |
|||
to_h(36ULL * cache_getmaxsize(), buf2); |
|||
fprintf(target, "%-20s %s (max real) of %s (allocated virtual memory).\n", "Memory (cache)", buf, buf2); |
|||
} |
|||
i++; |
|||
} |
|||
} |
|||
|
|||
#else |
|||
|
|||
VOID_TASK_IMPL_0(sylvan_stats_init) |
|||
{ |
|||
} |
|||
|
|||
VOID_TASK_IMPL_0(sylvan_stats_reset) |
|||
{ |
|||
} |
|||
|
|||
VOID_TASK_IMPL_1(sylvan_stats_snapshot, sylvan_stats_t*, target) |
|||
{ |
|||
memset(target, 0, sizeof(sylvan_stats_t)); |
|||
} |
|||
|
|||
void |
|||
sylvan_stats_report(FILE* target) |
|||
{ |
|||
(void)target; |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,10 @@ |
|||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@ |
|||
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ |
|||
|
|||
Name: Sylvan |
|||
Description: @PROJECT_DESCRIPTION@ |
|||
URL: @PROJECT_URL@ |
|||
Version: @PROJECT_VERSION@ |
|||
Cflags: -I${includedir} |
|||
Libs: -L${libdir} -lsylvan -lgmp -lpthread -lm |
|||
Requires: hwloc |
@ -1,5 +0,0 @@ |
|||
test |
|||
cmake_install.cmake |
|||
CMakeFiles |
|||
*.o |
|||
.libs |
@ -1,350 +0,0 @@ |
|||
#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); |
|||
} |
Some files were not shown because too many files changed in this diff
Reference in new issue
xxxxxxxxxx