Browse Source

moved to new sparsepp version and made the appropriate changes

main
dehnert 8 years ago
parent
commit
8f42bd2ec0
  1. 31
      resources/3rdparty/CMakeLists.txt
  2. 29
      resources/3rdparty/sparsepp/README.md
  3. 17
      resources/3rdparty/sparsepp/makefile
  4. 5578
      resources/3rdparty/sparsepp/sparsepp.h
  5. 2982
      resources/3rdparty/sparsepp/spp_test.cc
  6. 318
      resources/3rdparty/sparsepp/spp_utils.h
  7. 4
      resources/3rdparty/sylvan/src/sylvan_storm_rational_function.c
  8. 4
      resources/3rdparty/sylvan/src/sylvan_storm_rational_number.c
  9. 5
      src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp
  10. 3
      src/storm/builder/jit/ExplicitJitJaniModelBuilder.h
  11. 2
      src/storm/settings/modules/BisimulationSettings.cpp
  12. 2
      src/storm/settings/modules/BisimulationSettings.h
  13. 19
      src/storm/storage/dd/Add.cpp
  14. 13
      src/storm/storage/dd/Add.h
  15. 5
      src/storm/storage/dd/Bdd.cpp
  16. 23
      src/storm/storage/dd/Bdd.h
  17. 45
      src/storm/storage/dd/BisimulationDecomposition.cpp
  18. 14
      src/storm/storage/dd/DdManager.cpp
  19. 8
      src/storm/storage/dd/DdManager.h
  20. 64
      src/storm/storage/dd/bisimulation/Partition.cpp
  21. 39
      src/storm/storage/dd/bisimulation/Partition.h
  22. 32
      src/storm/storage/dd/bisimulation/SignatureComputer.cpp
  23. 29
      src/storm/storage/dd/bisimulation/SignatureComputer.h
  24. 328
      src/storm/storage/dd/bisimulation/SignatureRefiner.cpp
  25. 9
      src/storm/storage/dd/cudd/InternalCuddAdd.cpp
  26. 12
      src/storm/storage/dd/cudd/InternalCuddAdd.h
  27. 9
      src/storm/storage/dd/cudd/InternalCuddDdManager.cpp
  28. 10
      src/storm/storage/dd/cudd/InternalCuddDdManager.h
  29. 40
      src/storm/storage/dd/sylvan/InternalSylvanAdd.cpp
  30. 12
      src/storm/storage/dd/sylvan/InternalSylvanAdd.h
  31. 27
      src/storm/storage/dd/sylvan/InternalSylvanBdd.h
  32. 49
      src/storm/storage/dd/sylvan/InternalSylvanDdManager.cpp
  33. 8
      src/storm/storage/dd/sylvan/InternalSylvanDdManager.h
  34. 2
      src/storm/storage/geometry/NativePolytope.cpp

31
resources/3rdparty/CMakeLists.txt

@ -99,16 +99,24 @@ list(APPEND STORM_DEP_TARGETS ExprTk)
# Use the shipped version of Sparsepp
message (STATUS "Storm - Including Sparsepp.")
include_directories("${PROJECT_SOURCE_DIR}/resources/3rdparty/sparsepp")
# Add sparsepp.h to the headers that are copied to the include directory in thebuild directory.
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/include/resources/3rdparty/sparsepp/sparsepp.h
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include/resources/3rdparty/sparsepp
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/resources/3rdparty/sparsepp/sparsepp.h ${CMAKE_BINARY_DIR}/include/resources/3rdparty/sparsepp/sparsepp.h
DEPENDS ${PROJECT_SOURCE_DIR}/resources/3rdparty/sparsepp/sparsepp.h
)
list(APPEND STORM_RESOURCES_HEADERS "${CMAKE_BINARY_DIR}/include/resources/3rdparty/sparsepp/sparsepp.h")
set(SPARSEPP_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/resources/3rdparty/sparsepp/sparsepp")
file(GLOB SPARSEPP_HEADERS "${SPARSEPP_INCLUDE_DIR}/*.h")
# Add the sparsepp headers to the headers that are copied to the include directory in the build directory.
set(SPARSEPP_BINDIR_DIR ${CMAKE_BINARY_DIR}/include/resources/3rdparty/sparsepp)
include_directories("${SPARSEPP_BINDIR_DIR}")
foreach(HEADER ${SPARSEPP_HEADERS})
string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/src/?" "" RELATIVE_HEADER_PATH ${HEADER})
string(REGEX MATCH "(.*)[/\\]" RELATIVE_DIRECTORY ${RELATIVE_HEADER_PATH})
string(REGEX REPLACE "${RELATIVE_DIRECTORY}/?" "" HEADER_FILENAME ${RELATIVE_HEADER_PATH})
add_custom_command(
OUTPUT ${SPARSEPP_BINDIR_DIR}/sparsepp/${HEADER_FILENAME}
COMMAND ${CMAKE_COMMAND} -E make_directory ${SPARSEPP_BINDIR_DIR}/sparsepp
COMMAND ${CMAKE_COMMAND} -E copy ${HEADER} ${SPARSEPP_BINDIR_DIR}/sparsepp/${HEADER_FILENAME}
DEPENDS ${SPARSEPP_INCLUDE_DIR}/${HEADER_FILENAME}
)
list(APPEND SPARSEPP_BINDIR_HEADERS ${SPARSEPP_BINDIR_DIR}/sparsepp/${HEADER_FILENAME})
endforeach()
#############################################################
##
@ -380,7 +388,6 @@ ExternalProject_Add(
LOG_CONFIGURE ON
LOG_BUILD ON
BUILD_BYPRODUCTS ${STORM_3RDPARTY_BINARY_DIR}/sylvan/src/libsylvan${STATIC_EXT}
BUILD_ALWAYS 1
)
ExternalProject_Get_Property(sylvan source_dir)
@ -626,4 +633,4 @@ if(ENABLE_CUDA)
include_directories("${PROJECT_SOURCE_DIR}/cuda/kernels/")
endif()
add_custom_target(copy_resources_headers DEPENDS ${CMAKE_BINARY_DIR}/include/resources/3rdparty/sparsepp/sparsepp.h ${CMAKE_BINARY_DIR}/include/resources/3rdparty/sparsepp/sparsepp.h)
add_custom_target(copy_resources_headers DEPENDS ${SPARSEPP_BINDIR_HEADERS})

29
resources/3rdparty/sparsepp/README.md

@ -8,7 +8,7 @@ Sparsepp is derived from Google's excellent [sparsehash](https://github.com/spar
- **Extremely low memory usage** (typically about one byte overhead per entry).
- **Very efficient**, typically faster than your compiler's unordered map/set or Boost's.
- **C++11 support** (if supported by compiler).
- **Single header** implementation - just copy `sparsepp.h` to your project and include it.
- ~~Single header~~ not anymore
- **Tested** on Windows (vs2010-2015, g++), linux (g++, clang++) and MacOS (clang++).
We believe Sparsepp provides an unparalleled combination of performance and memory usage, and will outperform your compiler's unordered_map on both counts. Only Google's `dense_hash_map` is consistently faster, at the cost of much greater memory usage (especially when the final size of the map is not known in advance).
@ -20,7 +20,7 @@ For a detailed comparison of various hash implementations, including Sparsepp, p
```c++
#include <iostream>
#include <string>
#include <sparsepp.h>
#include <sparsepp/spp.h>
using spp::sparse_hash_map;
@ -50,9 +50,7 @@ int main()
## Installation
Since the full Sparsepp implementation is contained in a single header file `sparsepp.h`, the installation consist in copying this header file wherever it will be convenient to include in your project(s).
Optionally, a second header file `spp_utils.h` is provided, which implements only the spp::hash_combine() functionality. This is useful when we want to specify a hash function for a user-defined class in an header file, without including the full `sparsepp.h` header (this is demonstrated in [example 2](#example-2---providing-a-hash-function-for-a-user-defined-class) below).
No compilation is needed, as this is a header-only library. The installation consist in copying the sparsepp directory wherever it will be convenient to include in your project(s). Also make the path to this directory is provided to the compiler with the `-I` option.
## Warning - iterator invalidation on erase/insert
@ -62,7 +60,7 @@ Optionally, a second header file `spp_utils.h` is provided, which implements onl
## Usage
As shown in the example above, you need to include the header file: `#include <sparsepp.h>`
As shown in the example above, you need to include the header file: `#include <sparsepp/spp.h>`
This provides the implementation for the following classes:
@ -107,7 +105,7 @@ These classes provide the same interface as std::unordered_map and std::unordere
Of course, the user of sparsepp may provide its own hash function, as shown below:
```c++
#include <sparsepp.h>
#include <sparsepp/spp.h>
struct Hash64 {
size_t operator()(uint64_t k) const { return (k ^ 14695981039346656037ULL) * 1099511628211ULL; }
@ -125,7 +123,7 @@ These classes provide the same interface as std::unordered_map and std::unordere
```
2. When the user provides its own hash function, for example when inserting custom classes into a hash map, sometimes the resulting hash keys have similar low order bits and cause many collisions, decreasing the efficiency of the hash map. To address this use case, sparsepp provides an optional 'mixing' of the hash key (see [Integer Hash Function](https://gist.github.com/badboy/6267743) which can be enabled by defining the proprocessor macro: SPP_HASH_MIX.
2. When the user provides its own hash function, for example when inserting custom classes into a hash map, sometimes the resulting hash keys have similar low order bits and cause many collisions, decreasing the efficiency of the hash map. To address this use case, sparsepp provides an optional 'mixing' of the hash key (see [Integer Hash Function](https://gist.github.com/badboy/6267743) which can be enabled by defining the proprocessor macro: SPP_MIX_HASH.
## Example 2 - providing a hash function for a user-defined class
@ -135,7 +133,7 @@ In order to use a sparse_hash_set or sparse_hash_map, a hash function should be
#include <iostream>
#include <functional>
#include <string>
#include "sparsepp.h"
#include <sparsepp/spp.h>
using std::string;
@ -179,11 +177,11 @@ int main()
The `std::hash` specialization for `Person` combines the hash values for both first and last name using the convenient spp::hash_combine function, and returns the combined hash value.
spp::hash_combine is provided by the header `sparsepp.h`. However, class definitions often appear in header files, and it is desirable to limit the size of headers included in such header files, so we provide the very small header `spp_utils.h` for that purpose:
spp::hash_combine is provided by the header `sparsepp/spp.h`. However, class definitions often appear in header files, and it is desirable to limit the size of headers included in such header files, so we provide the very small header `sparsepp/spp_utils.h` for that purpose:
```c++
#include <string>
#include "spp_utils.h"
#include <sparsepp/spp_utils.h>
using std::string;
@ -236,7 +234,7 @@ The following example demontrates how a simple sparse_hash_map can be written to
```c++
#include <cstdio>
#include "sparsepp.h"
#include <sparsepp/spp.h>
using spp::sparse_hash_map;
using namespace std;
@ -319,5 +317,12 @@ int main(int argc, char* argv[])
}
```
## Thread safety
Sparsepp follows the trade safety rules of the Standard C++ library. In Particular:
- A single sparsepp hash table is thread safe for reading from multiple threads. For example, given a hash table A, it is safe to read A from thread 1 and from thread 2 simultaneously.
- If a single hash table is being written to by one thread, then all reads and writes to that hash table on the same or other threads must be protected. For example, given a hash table A, if thread 1 is writing to A, then thread 2 must be prevented from reading from or writing to A.
- It is safe to read and write to one instance of a type even if another thread is reading or writing to a different instance of the same type. For example, given hash tables A and B of the same type, it is safe if A is being written in thread 1 and B is being read in thread 2.

17
resources/3rdparty/sparsepp/makefile

@ -1,17 +0,0 @@
all: spp_test
clean:
/bin/rm spp_test
test:
./spp_test
spp_test: spp_test.cc sparsepp.h makefile
$(CXX) -O2 -std=c++0x -Wall -pedantic -Wextra -D_XOPEN_SOURCE=700 -D_CRT_SECURE_NO_WARNINGS spp_test.cc -o spp_test
spp_alloc_test: spp_alloc_test.cc spp_alloc.h spp_bitset.h sparsepp.h makefile
$(CXX) -O2 -DNDEBUG -std=c++11 spp_alloc_test.cc -o spp_alloc_test
perftest1: perftest1.cc sparsepp.h makefile
$(CXX) -O2 -DNDEBUG -std=c++11 perftest1.cc -o perftest1

5578
resources/3rdparty/sparsepp/sparsepp.h
File diff suppressed because it is too large
View File

2982
resources/3rdparty/sparsepp/spp_test.cc
File diff suppressed because it is too large
View File

318
resources/3rdparty/sparsepp/spp_utils.h

@ -1,318 +0,0 @@
// ----------------------------------------------------------------------
// Copyright (c) 2016, Steven Gregory Popovitch - greg7mdp@gmail.com
// All rights reserved.
//
// Code derived derived from Boost libraries.
// Boost software licence reproduced below.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * The name of Steven Gregory Popovitch may not be used to
// endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// ----------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Boost Software License - Version 1.0 - August 17th, 2003
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// ---------------------------------------------------------------------------
// ----------------------------------------------------------------------
// H A S H F U N C T I O N S
// ----------------------------
//
// Implements spp::spp_hash() and spp::hash_combine()
//
// The exact same code is duplicated in sparsepp.h.
//
// WARNING: Any change here has to be duplicated in sparsepp.h.
// ----------------------------------------------------------------------
#if !defined(spp_utils_h_guard_)
#define spp_utils_h_guard_
#if defined(_MSC_VER)
#if (_MSC_VER >= 1600 ) // vs2010 (1900 is vs2015)
#include <functional>
#define SPP_HASH_CLASS std::hash
#else
#include <hash_map>
#define SPP_HASH_CLASS stdext::hash_compare
#endif
#if (_MSC_FULL_VER < 190021730)
#define SPP_NO_CXX11_NOEXCEPT
#endif
#elif defined(__GNUC__)
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
#include <functional>
#define SPP_HASH_CLASS std::hash
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 40600
#define SPP_NO_CXX11_NOEXCEPT
#endif
#else
#include <tr1/unordered_map>
#define SPP_HASH_CLASS std::tr1::hash
#define SPP_NO_CXX11_NOEXCEPT
#endif
#elif defined __clang__
#include <functional>
#define SPP_HASH_CLASS std::hash
#if !__has_feature(cxx_noexcept)
#define SPP_NO_CXX11_NOEXCEPT
#endif
#else
#include <functional>
#define SPP_HASH_CLASS std::hash
#endif
#ifdef SPP_NO_CXX11_NOEXCEPT
#define SPP_NOEXCEPT
#else
#define SPP_NOEXCEPT noexcept
#endif
#ifdef SPP_NO_CXX11_CONSTEXPR
#define SPP_CONSTEXPR
#else
#define SPP_CONSTEXPR constexpr
#endif
#define SPP_INLINE
#ifndef SPP_NAMESPACE
#define SPP_NAMESPACE spp
#endif
namespace SPP_NAMESPACE
{
template <class T>
struct spp_hash
{
SPP_INLINE size_t operator()(const T &__v) const SPP_NOEXCEPT
{
SPP_HASH_CLASS<T> hasher;
return hasher(__v);
}
};
template <class T>
struct spp_hash<T *>
{
static size_t spp_log2 (size_t val) SPP_NOEXCEPT
{
size_t res = 0;
while (val > 1)
{
val >>= 1;
res++;
}
return res;
}
SPP_INLINE size_t operator()(const T *__v) const SPP_NOEXCEPT
{
static const size_t shift = 3; // spp_log2(1 + sizeof(T)); // T might be incomplete!
return static_cast<size_t>((*(reinterpret_cast<const uintptr_t *>(&__v))) >> shift);
}
};
// from http://burtleburtle.net/bob/hash/integer.html
// fast and efficient for power of two table sizes where we always
// consider the last bits.
// ---------------------------------------------------------------
inline size_t spp_mix_32(uint32_t a)
{
a = a ^ (a >> 4);
a = (a ^ 0xdeadbeef) + (a << 5);
a = a ^ (a >> 11);
return static_cast<size_t>(a);
}
// Maybe we should do a more thorough scrambling as described in
// https://gist.github.com/badboy/6267743
// -------------------------------------------------------------
inline size_t spp_mix_64(uint64_t a)
{
a = a ^ (a >> 4);
a = (a ^ 0xdeadbeef) + (a << 5);
a = a ^ (a >> 11);
return a;
}
template <>
struct spp_hash<bool> : public std::unary_function<bool, size_t>
{
SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
};
template <>
struct spp_hash<char> : public std::unary_function<char, size_t>
{
SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
};
template <>
struct spp_hash<signed char> : public std::unary_function<signed char, size_t>
{
SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
};
template <>
struct spp_hash<unsigned char> : public std::unary_function<unsigned char, size_t>
{
SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
};
template <>
struct spp_hash<wchar_t> : public std::unary_function<wchar_t, size_t>
{
SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT
{ return static_cast<size_t>(__v); }
};
template <>
struct spp_hash<int16_t> : public std::unary_function<int16_t, size_t>
{
SPP_INLINE size_t operator()(int16_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
};
template <>
struct spp_hash<uint16_t> : public std::unary_function<uint16_t, size_t>
{
SPP_INLINE size_t operator()(uint16_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
};
template <>
struct spp_hash<int32_t> : public std::unary_function<int32_t, size_t>
{
SPP_INLINE size_t operator()(int32_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
};
template <>
struct spp_hash<uint32_t> : public std::unary_function<uint32_t, size_t>
{
SPP_INLINE size_t operator()(uint32_t __v) const SPP_NOEXCEPT
{ return spp_mix_32(static_cast<uint32_t>(__v)); }
};
template <>
struct spp_hash<int64_t> : public std::unary_function<int64_t, size_t>
{
SPP_INLINE size_t operator()(int64_t __v) const SPP_NOEXCEPT
{ return spp_mix_64(static_cast<uint64_t>(__v)); }
};
template <>
struct spp_hash<uint64_t> : public std::unary_function<uint64_t, size_t>
{
SPP_INLINE size_t operator()(uint64_t __v) const SPP_NOEXCEPT
{ return spp_mix_64(static_cast<uint64_t>(__v)); }
};
template <>
struct spp_hash<float> : public std::unary_function<float, size_t>
{
SPP_INLINE size_t operator()(float __v) const SPP_NOEXCEPT
{
// -0.0 and 0.0 should return same hash
uint32_t *as_int = reinterpret_cast<uint32_t *>(&__v);
return (__v == 0) ? static_cast<size_t>(0) : spp_mix_32(*as_int);
}
};
template <>
struct spp_hash<double> : public std::unary_function<double, size_t>
{
SPP_INLINE size_t operator()(double __v) const SPP_NOEXCEPT
{
// -0.0 and 0.0 should return same hash
uint64_t *as_int = reinterpret_cast<uint64_t *>(&__v);
return (__v == 0) ? static_cast<size_t>(0) : spp_mix_64(*as_int);
}
};
template <class T, int sz> struct Combiner
{
inline void operator()(T& seed, T value);
};
template <class T> struct Combiner<T, 4>
{
inline void operator()(T& seed, T value)
{
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
};
template <class T> struct Combiner<T, 8>
{
inline void operator()(T& seed, T value)
{
seed ^= value + T(0xc6a4a7935bd1e995) + (seed << 6) + (seed >> 2);
}
};
template <class T>
inline void hash_combine(std::size_t& seed, T const& v)
{
spp::spp_hash<T> hasher;
Combiner<std::size_t, sizeof(std::size_t)> combiner;
combiner(seed, hasher(v));
}
}
#endif // spp_utils_h_guard_

4
resources/3rdparty/sylvan/src/sylvan_storm_rational_function.c

@ -114,6 +114,8 @@ TASK_IMPL_2(MTBDD, sylvan_storm_rational_function_op_plus, MTBDD*, pa, MTBDD*, p
/* Check for partial functions */
if (a == mtbdd_false) return b;
if (b == mtbdd_false) return a;
if (a == mtbdd_true || b == mtbdd_true) return mtbdd_true;
/* If both leaves, compute plus */
if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) {
@ -163,6 +165,8 @@ TASK_IMPL_2(MTBDD, sylvan_storm_rational_function_op_times, MTBDD*, pa, MTBDD*,
/* Check for partial functions */
if (a == mtbdd_false || b == mtbdd_false) return mtbdd_false;
if (a == mtbdd_true) return b;
if (b == mtbdd_true) return a;
/* If both leaves, compute multiplication */
if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) {

4
resources/3rdparty/sylvan/src/sylvan_storm_rational_number.c

@ -114,6 +114,8 @@ TASK_IMPL_2(MTBDD, sylvan_storm_rational_number_op_plus, MTBDD*, pa, MTBDD*, pb)
/* Check for partial functions */
if (a == mtbdd_false) return b;
if (b == mtbdd_false) return a;
if (a == mtbdd_true || b == mtbdd_true) return mtbdd_true;
/* If both leaves, compute plus */
if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) {
@ -163,6 +165,8 @@ TASK_IMPL_2(MTBDD, sylvan_storm_rational_number_op_times, MTBDD*, pa, MTBDD*, pb
/* Check for partial functions */
if (a == mtbdd_false || b == mtbdd_false) return mtbdd_false;
if (a == mtbdd_true) return b;
if (b == mtbdd_true) return a;
/* If both leaves, compute multiplication */
if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) {

5
src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp

@ -92,6 +92,7 @@ namespace storm {
} else {
carlIncludeDirectory = STORM_CARL_INCLUDE_DIR;
}
sparseppIncludeDirectory = STORM_BUILD_DIR "/include/resources/3rdparty/sparsepp/";
// Register all transient variables as transient.
for (auto const& variable : this->model.getGlobalVariables().getTransientVariables()) {
@ -1648,7 +1649,7 @@ namespace storm {
#include "storm/adapters/CarlAdapter.h"
{% endif %}
#include "resources/3rdparty/sparsepp/sparsepp.h"
#include <sparsepp/spp.h>
#include "storm/builder/jit/StateSet.h"
#include "storm/builder/jit/JitModelBuilderInterface.h"
@ -2405,7 +2406,7 @@ namespace storm {
dynamicLibraryPath += DYLIB_EXTENSION;
std::string dynamicLibraryFilename = boost::filesystem::absolute(dynamicLibraryPath).string();
std::string command = compiler + " " + sourceFilename + " " + compilerFlags + " -I" + stormIncludeDirectory + " -I" + boostIncludeDirectory + " -I" + carlIncludeDirectory + " -o " + dynamicLibraryFilename;
std::string command = compiler + " " + sourceFilename + " " + compilerFlags + " -I" + stormIncludeDirectory + " -I" + sparseppIncludeDirectory + " -I" + boostIncludeDirectory + " -I" + carlIncludeDirectory + " -o " + dynamicLibraryFilename;
boost::optional<std::string> error = execute(command);
if (error) {

3
src/storm/builder/jit/ExplicitJitJaniModelBuilder.h

@ -192,6 +192,9 @@ namespace storm {
/// The include directory of carl.
std::string carlIncludeDirectory;
/// The include directory of sparsepp.
std::string sparseppIncludeDirectory;
/// A cache that is used by carl.
std::shared_ptr<carl::Cache<carl::PolynomialFactorizationPair<RawPolynomial>>> cache;
};

2
src/storm/settings/modules/BisimulationSettings.cpp

@ -6,6 +6,8 @@
#include "storm/settings/Argument.h"
#include "storm/settings/SettingsManager.h"
#include "storm/exceptions/InvalidSettingsException.h"
namespace storm {
namespace settings {
namespace modules {

2
src/storm/settings/modules/BisimulationSettings.h

@ -15,6 +15,8 @@ namespace storm {
// An enumeration of all available bisimulation types.
enum class BisimulationType { Strong, Weak };
enum class CachingStrategy { FullDirect, FullLate, Granularity, Minimal };
/*!
* Creates a new set of bisimulation settings.
*/

19
src/storm/storage/dd/Add.cpp

@ -241,7 +241,24 @@ namespace storm {
template<DdType LibraryType, typename ValueType>
Add<LibraryType, ValueType> Add<LibraryType, ValueType>::multiplyMatrix(Add<LibraryType, ValueType> const& otherMatrix, std::set<storm::expressions::Variable> const& summationMetaVariables) const {
// Create the CUDD summation variables.
// Create the summation variables.
std::vector<InternalBdd<LibraryType>> summationDdVariables;
for (auto const& metaVariable : summationMetaVariables) {
for (auto const& ddVariable : this->getDdManager().getMetaVariable(metaVariable).getDdVariables()) {
summationDdVariables.push_back(ddVariable);
}
}
std::set<storm::expressions::Variable> unionOfMetaVariables = Dd<LibraryType>::joinMetaVariables(*this, otherMatrix);
std::set<storm::expressions::Variable> containedMetaVariables;
std::set_difference(unionOfMetaVariables.begin(), unionOfMetaVariables.end(), summationMetaVariables.begin(), summationMetaVariables.end(), std::inserter(containedMetaVariables, containedMetaVariables.begin()));
return Add<LibraryType, ValueType>(this->getDdManager(), internalAdd.multiplyMatrix(otherMatrix, summationDdVariables), containedMetaVariables);
}
template<DdType LibraryType, typename ValueType>
Add<LibraryType, ValueType> Add<LibraryType, ValueType>::multiplyMatrix(Bdd<LibraryType> const& otherMatrix, std::set<storm::expressions::Variable> const& summationMetaVariables) const {
// Create the summation variables.
std::vector<InternalBdd<LibraryType>> summationDdVariables;
for (auto const& metaVariable : summationMetaVariables) {
for (auto const& ddVariable : this->getDdManager().getMetaVariable(metaVariable).getDdVariables()) {

13
src/storm/storage/dd/Add.h

@ -339,7 +339,18 @@ namespace storm {
* @return An ADD representing the result of the matrix-matrix multiplication.
*/
Add<LibraryType, ValueType> multiplyMatrix(Add<LibraryType, ValueType> const& otherMatrix, std::set<storm::expressions::Variable> const& summationMetaVariables) const;
/*!
* Multiplies the current ADD (representing a matrix) with the given matrix (given by a BDD) by summing over
* the given meta variables.
*
* @param otherMatrix The matrix with which to multiply.
* @param summationMetaVariables The names of the meta variables over which to sum during the matrix-
* matrix multiplication.
* @return An ADD representing the result of the matrix-matrix multiplication.
*/
Add<LibraryType, ValueType> multiplyMatrix(Bdd<LibraryType> const& otherMatrix, std::set<storm::expressions::Variable> const& summationMetaVariables) const;
/*!
* Computes a BDD that represents the function in which all assignments with a function value strictly
* larger than the given value are mapped to one and all others to zero.

5
src/storm/storage/dd/Bdd.cpp

@ -356,6 +356,11 @@ namespace storm {
return internalBdd.createOdd(this->getSortedVariableIndices());
}
template<DdType LibraryType>
InternalBdd<LibraryType> const& Bdd<LibraryType>::getInternalBdd() const {
return internalBdd;
}
template<DdType LibraryType>
template<typename ValueType>
std::vector<ValueType> Bdd<LibraryType>::filterExplicitVector(Odd const& odd, std::vector<ValueType> const& values) const {

23
src/storm/storage/dd/Bdd.h

@ -29,6 +29,15 @@ namespace storm {
template<DdType LibraryTypePrime, typename ValueTypePrime>
friend class Add;
/*!
* Creates a DD that encapsulates the given internal BDD.
*
* @param ddManager The manager responsible for this DD.
* @param internalBdd The internal BDD to store.
* @param containedMetaVariables The meta variables that appear in the DD.
*/
Bdd(DdManager<LibraryType> const& ddManager, InternalBdd<LibraryType> const& internalBdd, std::set<storm::expressions::Variable> const& containedMetaVariables = std::set<storm::expressions::Variable>());
// Instantiate all copy/move constructors/assignments with the default implementation.
Bdd() = default;
Bdd(Bdd<LibraryType> const& other) = default;
@ -353,6 +362,11 @@ namespace storm {
*/
storm::storage::BitVector filterExplicitVector(Odd const& odd, storm::storage::BitVector const& values) const;
/*!
* Retrieves the internal BDD.
*/
InternalBdd<LibraryType> const& getInternalBdd() const;
friend struct std::hash<storm::dd::Bdd<LibraryType>>;
template<DdType LibraryTypePrime, typename ValueType>
@ -364,15 +378,6 @@ namespace storm {
*/
operator InternalBdd<LibraryType>() const;
/*!
* Creates a DD that encapsulates the given internal BDD.
*
* @param ddManager The manager responsible for this DD.
* @param internalBdd The internal BDD to store.
* @param containedMetaVariables The meta variables that appear in the DD.
*/
Bdd(DdManager<LibraryType> const& ddManager, InternalBdd<LibraryType> const& internalBdd, std::set<storm::expressions::Variable> const& containedMetaVariables = std::set<storm::expressions::Variable>());
// The internal BDD that depends on the chosen library.
InternalBdd<LibraryType> internalBdd;
};

45
src/storm/storage/dd/BisimulationDecomposition.cpp

@ -1,5 +1,6 @@
#include "storm/storage/dd/BisimulationDecomposition.h"
#include "storm/storage/dd/bisimulation/SignatureComputer.h"
#include "storm/storage/dd/bisimulation/SignatureRefiner.h"
#include "storm/utility/macros.h"
@ -26,46 +27,52 @@ namespace storm {
template <storm::dd::DdType DdType, typename ValueType>
void BisimulationDecomposition<DdType, ValueType>::compute() {
// LACE_ME;
auto partitionRefinementStart = std::chrono::high_resolution_clock::now();
this->status = Status::InComputation;
auto start = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::duration totalSignatureTime(0);
std::chrono::high_resolution_clock::duration totalRefinementTime(0);
STORM_LOG_TRACE("Initial partition has " << currentPartition.getNumberOfBlocks() << " blocks.");
#ifndef NDEBUG
STORM_LOG_TRACE("Initial partition ADD has " << currentPartition.getPartitionAdd().getNodeCount() << " nodes.");
STORM_LOG_TRACE("Initial partition has " << currentPartition.getNodeCount() << " nodes.");
#endif
SignatureRefiner<DdType, ValueType> refiner(model.getManager(), currentPartition.getBlockVariable(), model.getRowVariables());
SignatureComputer<DdType, ValueType> signatureComputer(model);
bool done = false;
uint64_t iterations = 0;
while (!done) {
// currentPartition.getPartitionAdd().exportToDot("part" + std::to_string(iterations) + ".dot");
Signature<DdType, ValueType> signature(model.getTransitionMatrix().multiplyMatrix(currentPartition.getPartitionAdd(), model.getColumnVariables()));
// signature.getSignatureAdd().exportToDot("sig" + std::to_string(iterations) + ".dot");
#ifndef NDEBUG
STORM_LOG_TRACE("Computed signature ADD with " << signature.getSignatureAdd().getNodeCount() << " nodes.");
#endif
++iterations;
auto iterationStart = std::chrono::high_resolution_clock::now();
auto signatureStart = std::chrono::high_resolution_clock::now();
Signature<DdType, ValueType> signature = signatureComputer.compute(currentPartition);
auto signatureEnd = std::chrono::high_resolution_clock::now();
totalSignatureTime += (signatureEnd - signatureStart);
auto refinementStart = std::chrono::high_resolution_clock::now();
Partition<DdType, ValueType> newPartition = refiner.refine(currentPartition, signature);
STORM_LOG_TRACE("New partition has " << newPartition.getNumberOfBlocks() << " blocks.");
#ifndef NDEBUG
STORM_LOG_TRACE("Computed new partition ADD with " << newPartition.getPartitionAdd().getNodeCount() << " nodes.");
#endif
// STORM_LOG_TRACE("Current #nodes in table " << llmsset_count_marked(nodes) << " of " << llmsset_get_size(nodes) << " BDD nodes.");
// newPartition.getPartitionAdd().exportToDot("part" + std::to_string(iterations) + ".dot");
auto refinementEnd = std::chrono::high_resolution_clock::now();
totalRefinementTime += (refinementEnd - refinementStart);
auto signatureTime = std::chrono::duration_cast<std::chrono::milliseconds>(signatureEnd - signatureStart).count();
auto refinementTime = std::chrono::duration_cast<std::chrono::milliseconds>(refinementEnd - refinementStart).count();
auto iterationTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - iterationStart).count();
STORM_LOG_DEBUG("Iteration " << iterations << " produced " << newPartition.getNumberOfBlocks() << " blocks and was completed in " << iterationTime << "ms (signature: " << signatureTime << "ms, refinement: " << refinementTime << "ms). Signature DD has " << signature.getSignatureAdd().getNodeCount() << " nodes and partition DD has " << currentPartition.getNodeCount() << " nodes.");
if (currentPartition == newPartition) {
done = true;
} else {
currentPartition = newPartition;
}
++iterations;
}
this->status = Status::FixedPoint;
auto partitionRefinementEnd = std::chrono::high_resolution_clock::now();
STORM_LOG_DEBUG("Partition refinement completed in " << std::chrono::duration_cast<std::chrono::milliseconds>(partitionRefinementEnd - partitionRefinementStart).count() << "ms (" << iterations << " iterations).");
auto end = std::chrono::high_resolution_clock::now();
auto totalSignatureTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(totalSignatureTime).count();
auto totalRefinementTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(totalRefinementTime).count();
STORM_LOG_DEBUG("Partition refinement completed in " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms (" << iterations << " iterations, signature: " << totalSignatureTimeInMilliseconds << "ms, refinement: " << totalRefinementTimeInMilliseconds << "ms).");
}
template <storm::dd::DdType DdType, typename ValueType>

14
src/storm/storage/dd/DdManager.cpp

@ -42,6 +42,12 @@ namespace storm {
Add<LibraryType, ValueType> DdManager<LibraryType>::getAddZero() const {
return Add<LibraryType, ValueType>(*this, internalDdManager.template getAddZero<ValueType>());
}
template<DdType LibraryType>
template<typename ValueType>
Add<LibraryType, ValueType> DdManager<LibraryType>::getAddUndefined() const {
return Add<LibraryType, ValueType>(*this, internalDdManager.template getAddUndefined<ValueType>());
}
template<DdType LibraryType>
template<typename ValueType>
@ -460,7 +466,6 @@ namespace storm {
template Add<DdType::CUDD, double> DdManager<DdType::CUDD>::getIdentity(storm::expressions::Variable const& variable) const;
template Add<DdType::CUDD, uint_fast64_t> DdManager<DdType::CUDD>::getIdentity(storm::expressions::Variable const& variable) const;
template class DdManager<DdType::Sylvan>;
template Add<DdType::Sylvan, double> DdManager<DdType::Sylvan>::getAddZero() const;
@ -470,6 +475,13 @@ namespace storm {
template Add<DdType::Sylvan, storm::RationalFunction> DdManager<DdType::Sylvan>::getAddZero() const;
#endif
template Add<DdType::Sylvan, double> DdManager<DdType::Sylvan>::getAddUndefined() const;
template Add<DdType::Sylvan, uint_fast64_t> DdManager<DdType::Sylvan>::getAddUndefined() const;
#ifdef STORM_HAVE_CARL
template Add<DdType::Sylvan, storm::RationalNumber> DdManager<DdType::Sylvan>::getAddUndefined() const;
template Add<DdType::Sylvan, storm::RationalFunction> DdManager<DdType::Sylvan>::getAddUndefined() const;
#endif
template Add<DdType::Sylvan, double> DdManager<DdType::Sylvan>::getAddOne() const;
template Add<DdType::Sylvan, uint_fast64_t> DdManager<DdType::Sylvan>::getAddOne() const;
#ifdef STORM_HAVE_CARL

8
src/storm/storage/dd/DdManager.h

@ -72,6 +72,14 @@ namespace storm {
template<typename ValueType>
Add<LibraryType, ValueType> getAddZero() const;
/*!
* Retrieves an ADD representing the an undefined value.
*
* @return An ADD representing an undefined value.
*/
template<typename ValueType>
Add<LibraryType, ValueType> getAddUndefined() const;
/*!
* Retrieves an ADD representing the constant infinity function.
*

64
src/storm/storage/dd/bisimulation/Partition.cpp

@ -7,20 +7,30 @@ namespace storm {
namespace bisimulation {
template<storm::dd::DdType DdType, typename ValueType>
Partition<DdType, ValueType>::Partition(storm::dd::Add<DdType, ValueType> const& partitionAdd, storm::expressions::Variable const& blockVariable, uint64_t nextFreeBlockIndex) : partitionAdd(partitionAdd), blockVariable(blockVariable), nextFreeBlockIndex(nextFreeBlockIndex) {
Partition<DdType, ValueType>::Partition(storm::dd::Add<DdType, ValueType> const& partitionAdd, storm::expressions::Variable const& blockVariable, uint64_t nextFreeBlockIndex) : partition(partitionAdd), blockVariable(blockVariable), nextFreeBlockIndex(nextFreeBlockIndex) {
// Intentionally left empty.
}
template<storm::dd::DdType DdType, typename ValueType>
Partition<DdType, ValueType>::Partition(storm::dd::Bdd<DdType> const& partitionBdd, storm::expressions::Variable const& blockVariable, uint64_t nextFreeBlockIndex) : partition(partitionBdd), blockVariable(blockVariable), nextFreeBlockIndex(nextFreeBlockIndex) {
// Intentionally left empty.
}
template<storm::dd::DdType DdType, typename ValueType>
bool Partition<DdType, ValueType>::operator==(Partition<DdType, ValueType> const& other) {
return this->partitionAdd == other.partitionAdd && this->blockVariable == other.blockVariable && this->nextFreeBlockIndex == other.nextFreeBlockIndex;
return this->partition == other.partition && this->blockVariable == other.blockVariable && this->nextFreeBlockIndex == other.nextFreeBlockIndex;
}
template<storm::dd::DdType DdType, typename ValueType>
Partition<DdType, ValueType> Partition<DdType, ValueType>::replacePartitionAdd(storm::dd::Add<DdType, ValueType> const& newPartitionAdd, uint64_t nextFreeBlockIndex) const {
Partition<DdType, ValueType> Partition<DdType, ValueType>::replacePartition(storm::dd::Add<DdType, ValueType> const& newPartitionAdd, uint64_t nextFreeBlockIndex) const {
return Partition<DdType, ValueType>(newPartitionAdd, blockVariable, nextFreeBlockIndex);
}
template<storm::dd::DdType DdType, typename ValueType>
Partition<DdType, ValueType> Partition<DdType, ValueType>::replacePartition(storm::dd::Bdd<DdType> const& newPartitionBdd, uint64_t nextFreeBlockIndex) const {
return Partition<DdType, ValueType>(newPartitionBdd, blockVariable, nextFreeBlockIndex);
}
template<storm::dd::DdType DdType, typename ValueType>
Partition<DdType, ValueType> Partition<DdType, ValueType>::create(storm::models::symbolic::Model<DdType, ValueType> const& model) {
return create(model, model.getLabels());
@ -45,8 +55,14 @@ namespace storm {
}
storm::expressions::Variable blockVariable = createBlockVariable(manager, model.getReachableStates().getNonZeroCount());
std::pair<storm::dd::Add<DdType, ValueType>, uint64_t> partitionAddAndBlockCount = createPartitionAdd(manager, model, stateSets, blockVariable);
return Partition<DdType, ValueType>(partitionAddAndBlockCount.first, blockVariable, partitionAddAndBlockCount.second);
std::pair<storm::dd::Bdd<DdType>, uint64_t> partitionBddAndBlockCount = createPartitionBdd(manager, model, stateSets, blockVariable);
// Store the partition as an ADD only in the case of CUDD.
if (DdType == storm::dd::DdType::CUDD) {
return Partition<DdType, ValueType>(partitionBddAndBlockCount.first.template toAdd<ValueType>(), blockVariable, partitionBddAndBlockCount.second);
} else {
return Partition<DdType, ValueType>(partitionBddAndBlockCount.first, blockVariable, partitionBddAndBlockCount.second);
}
}
template<storm::dd::DdType DdType, typename ValueType>
@ -55,8 +71,23 @@ namespace storm {
}
template<storm::dd::DdType DdType, typename ValueType>
storm::dd::Add<DdType, ValueType> const& Partition<DdType, ValueType>::getPartitionAdd() const {
return partitionAdd;
bool Partition<DdType, ValueType>::storedAsAdd() const {
return partition.which() == 1;
}
template<storm::dd::DdType DdType, typename ValueType>
bool Partition<DdType, ValueType>::storedAsBdd() const {
return partition.which() == 0;
}
template<storm::dd::DdType DdType, typename ValueType>
storm::dd::Add<DdType, ValueType> const& Partition<DdType, ValueType>::asAdd() const {
return boost::get<storm::dd::Add<DdType, ValueType>>(partition);
}
template<storm::dd::DdType DdType, typename ValueType>
storm::dd::Bdd<DdType> const& Partition<DdType, ValueType>::asBdd() const {
return boost::get<storm::dd::Bdd<DdType>>(partition);
}
template<storm::dd::DdType DdType, typename ValueType>
@ -69,6 +100,15 @@ namespace storm {
return nextFreeBlockIndex;
}
template<storm::dd::DdType DdType, typename ValueType>
uint64_t Partition<DdType, ValueType>::getNodeCount() const {
if (this->storedAsBdd()) {
return asBdd().getNodeCount();
} else {
return asAdd().getNodeCount();
}
}
template<storm::dd::DdType DdType>
void enumerateBlocksRec(std::vector<storm::dd::Bdd<DdType>> const& stateSets, storm::dd::Bdd<DdType> const& currentStateSet, uint64_t offset, storm::expressions::Variable const& blockVariable, std::function<void (storm::dd::Bdd<DdType> const&)> const& callback) {
if (currentStateSet.isZero()) {
@ -83,14 +123,14 @@ namespace storm {
}
template<storm::dd::DdType DdType, typename ValueType>
std::pair<storm::dd::Add<DdType, ValueType>, uint64_t> Partition<DdType, ValueType>::createPartitionAdd(storm::dd::DdManager<DdType> const& manager, storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::dd::Bdd<DdType>> const& stateSets, storm::expressions::Variable const& blockVariable) {
std::pair<storm::dd::Bdd<DdType>, uint64_t> Partition<DdType, ValueType>::createPartitionBdd(storm::dd::DdManager<DdType> const& manager, storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::dd::Bdd<DdType>> const& stateSets, storm::expressions::Variable const& blockVariable) {
uint64_t blockCount = 0;
storm::dd::Add<DdType, ValueType> partitionAdd = manager.template getAddZero<ValueType>();
storm::dd::Bdd<DdType> partitionAdd = manager.getBddZero();
// Enumerate all realizable blocks.
enumerateBlocksRec<DdType>(stateSets, model.getReachableStates(), 0, blockVariable, [&manager, &partitionAdd, &blockVariable, &blockCount](storm::dd::Bdd<DdType> const& stateSet) {
stateSet.template toAdd<ValueType>().exportToDot("states_" + std::to_string(blockCount) + ".dot");
partitionAdd += (stateSet && manager.getEncoding(blockVariable, blockCount, false)).template toAdd<ValueType>();
partitionAdd |= (stateSet && manager.getEncoding(blockVariable, blockCount, false));
blockCount++;
} );

39
src/storm/storage/dd/bisimulation/Partition.h

@ -1,7 +1,10 @@
#pragma once
#include <boost/variant.hpp>
#include "storm/storage/dd/DdType.h"
#include "storm/storage/dd/Add.h"
#include "storm/storage/dd/Bdd.h"
#include "storm/models/symbolic/Model.h"
@ -26,10 +29,24 @@ namespace storm {
*/
Partition(storm::dd::Add<DdType, ValueType> const& partitionAdd, storm::expressions::Variable const& blockVariable, uint64_t nextFreeBlockIndex);
bool operator==(Partition<DdType, ValueType> const& other);
/*!
* Creates a new partition from the given data.
*
* @param partitionBdd A BDD that maps encoding over the state/row variables and the block variable to
* true iff the state is in the block.
* @param blockVariable The variable to use for the block encoding. Its range must be [0, x] where x is
* the number of states in the partition.
* @param nextFreeBlockIndex The next free block index. The existing blocks must be encoded with indices
* between 0 and this number.
*/
Partition(storm::dd::Bdd<DdType> const& partitionBdd, storm::expressions::Variable const& blockVariable, uint64_t nextFreeBlockIndex);
Partition<DdType, ValueType> replacePartitionAdd(storm::dd::Add<DdType, ValueType> const& newPartitionAdd, uint64_t nextFreeBlockIndex) const;
bool operator==(Partition<DdType, ValueType> const& other);
Partition<DdType, ValueType> replacePartition(storm::dd::Add<DdType, ValueType> const& newPartitionAdd, uint64_t nextFreeBlockIndex) const;
Partition<DdType, ValueType> replacePartition(storm::dd::Bdd<DdType> const& newPartitionBdd, uint64_t nextFreeBlockIndex) const;
/*!
* Creates a partition from the given model that respects all labels.
*/
@ -47,24 +64,30 @@ namespace storm {
uint64_t getNumberOfBlocks() const;
storm::dd::Add<DdType, ValueType> const& getPartitionAdd() const;
bool storedAsAdd() const;
bool storedAsBdd() const;
storm::dd::Add<DdType, ValueType> const& asAdd() const;
storm::dd::Bdd<DdType> const& asBdd() const;
storm::expressions::Variable const& getBlockVariable() const;
uint64_t getNextFreeBlockIndex() const;
uint64_t getNodeCount() const;
private:
static std::pair<storm::dd::Add<DdType, ValueType>, uint64_t> createPartitionAdd(storm::dd::DdManager<DdType> const& manager, storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::dd::Bdd<DdType>> const& stateSets, storm::expressions::Variable const& blockVariable);
static std::pair<storm::dd::Bdd<DdType>, uint64_t> createPartitionBdd(storm::dd::DdManager<DdType> const& manager, storm::models::symbolic::Model<DdType, ValueType> const& model, std::vector<storm::dd::Bdd<DdType>> const& stateSets, storm::expressions::Variable const& blockVariable);
static storm::expressions::Variable createBlockVariable(storm::dd::DdManager<DdType>& manager, uint64_t numberOfStates);
// The ADD representing the partition. The ADD is over the row variables of the model and the block variable.
storm::dd::Add<DdType, ValueType> partitionAdd;
/// The DD representing the partition. The DD is over the row variables of the model and the block variable.
boost::variant<storm::dd::Bdd<DdType>, storm::dd::Add<DdType, ValueType>> partition;
// The meta variable used to encode the block of each state in this partition.
/// The meta variable used to encode the block of each state in this partition.
storm::expressions::Variable blockVariable;
// The next free block index.
/// The next free block index.
uint64_t nextFreeBlockIndex;
};

32
src/storm/storage/dd/bisimulation/SignatureComputer.cpp

@ -0,0 +1,32 @@
#include "storm/storage/dd/bisimulation/SignatureComputer.h"
#include "storm/storage/dd/DdManager.h"
namespace storm {
namespace dd {
namespace bisimulation {
template<storm::dd::DdType DdType, typename ValueType>
SignatureComputer<DdType, ValueType>::SignatureComputer(storm::models::symbolic::Model<DdType, ValueType> const& model) : model(model), transitionMatrix(model.getTransitionMatrix()) {
if (DdType == storm::dd::DdType::Sylvan) {
this->transitionMatrix = this->transitionMatrix.notZero().ite(this->transitionMatrix, this->transitionMatrix.getDdManager().template getAddUndefined<ValueType>());
}
}
template<storm::dd::DdType DdType, typename ValueType>
Signature<DdType, ValueType> SignatureComputer<DdType, ValueType>::compute(Partition<DdType, ValueType> const& partition) {
if (partition.storedAsBdd()) {
return Signature<DdType, ValueType>(this->transitionMatrix.multiplyMatrix(partition.asBdd(), model.getColumnVariables()));
} else {
return Signature<DdType, ValueType>(this->transitionMatrix.multiplyMatrix(partition.asAdd(), model.getColumnVariables()));
}
}
template class SignatureComputer<storm::dd::DdType::CUDD, double>;
template class SignatureComputer<storm::dd::DdType::Sylvan, double>;
template class SignatureComputer<storm::dd::DdType::Sylvan, storm::RationalNumber>;
template class SignatureComputer<storm::dd::DdType::Sylvan, storm::RationalFunction>;
}
}
}

29
src/storm/storage/dd/bisimulation/SignatureComputer.h

@ -0,0 +1,29 @@
#pragma once
#include "storm/storage/dd/DdType.h"
#include "storm/storage/dd/bisimulation/Signature.h"
#include "storm/storage/dd/bisimulation/Partition.h"
#include "storm/models/symbolic/Model.h"
namespace storm {
namespace dd {
namespace bisimulation {
template<storm::dd::DdType DdType, typename ValueType>
class SignatureComputer {
public:
SignatureComputer(storm::models::symbolic::Model<DdType, ValueType> const& model);
Signature<DdType, ValueType> compute(Partition<DdType, ValueType> const& partition);
private:
storm::models::symbolic::Model<DdType, ValueType> const& model;
storm::dd::Add<DdType, ValueType> transitionMatrix;
};
}
}
}

328
src/storm/storage/dd/bisimulation/SignatureRefiner.cpp

@ -1,15 +1,24 @@
#include "storm/storage/dd/bisimulation/SignatureRefiner.h"
#include <unordered_map>
#include <boost/container/flat_map.hpp>
#include "storm/storage/dd/DdManager.h"
#include "storm/storage/dd/cudd/InternalCuddDdManager.h"
#include "storm/utility/macros.h"
#include "storm/exceptions/InvalidSettingsException.h"
#include "storm/exceptions/NotImplementedException.h"
#include "resources/3rdparty/sparsepp/sparsepp.h"
#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/BisimulationSettings.h"
#include <sparsepp/spp.h>
#include "sylvan_cache.h"
#include "sylvan_table.h"
#include "sylvan_int.h"
namespace storm {
namespace dd {
@ -18,7 +27,7 @@ namespace storm {
struct CuddPointerPairHash {
std::size_t operator()(std::pair<DdNode const*, DdNode const*> const& pair) const {
std::size_t seed = std::hash<DdNode const*>()(pair.first);
boost::hash_combine(seed, pair.second);
spp::hash_combine(seed, pair.second);
return seed;
}
};
@ -26,40 +35,75 @@ namespace storm {
struct SylvanMTBDDPairHash {
std::size_t operator()(std::pair<MTBDD, MTBDD> const& pair) const {
std::size_t seed = std::hash<MTBDD>()(pair.first);
boost::hash_combine(seed, pair.second);
spp::hash_combine(seed, pair.second);
return seed;
}
};
struct SylvanMTBDDPairLess {
std::size_t operator()(std::pair<MTBDD, MTBDD> const& a, std::pair<MTBDD, MTBDD> const& b) const {
if (a.first < b.first) {
return true;
} else if (a.first == b.first && a.second < b.second) {
return true;
}
return false;
}
};
template<storm::dd::DdType DdType, typename ValueType>
class InternalSignatureRefiner;
class ReuseWrapper {
public:
ReuseWrapper() : ReuseWrapper(false) {
// Intentionally left empty.
}
ReuseWrapper(bool value) : value(value) {
// Intentionally left empty.
}
bool isReused() const {
return value;
}
void setReused() {
value = true;
}
private:
bool value;
};
template<typename ValueType>
class InternalSignatureRefiner<storm::dd::DdType::CUDD, ValueType> {
public:
InternalSignatureRefiner(storm::dd::DdManager<storm::dd::DdType::CUDD> const& manager, storm::expressions::Variable const& blockVariable, uint64_t lastStateLevel) : manager(manager), internalDdManager(manager.getInternalDdManager()), blockVariable(blockVariable), lastStateLevel(lastStateLevel), nextFreeBlockIndex(0), numberOfRefinements(0) {
InternalSignatureRefiner(storm::dd::DdManager<storm::dd::DdType::CUDD> const& manager, storm::expressions::Variable const& blockVariable, uint64_t lastStateLevel) : manager(manager), internalDdManager(manager.getInternalDdManager()), blockVariable(blockVariable), lastStateLevel(lastStateLevel), nextFreeBlockIndex(0), numberOfRefinements(0), lastNumberOfVisitedNodes(10000), signatureCache(lastNumberOfVisitedNodes), reuseBlocksCache(lastNumberOfVisitedNodes) {
// Intentionally left empty.
}
Partition<storm::dd::DdType::CUDD, ValueType> refine(Partition<storm::dd::DdType::CUDD, ValueType> const& oldPartition, Signature<storm::dd::DdType::CUDD, ValueType> const& signature) {
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> newPartitionAdd = refine(oldPartition, signature.getSignatureAdd());
++numberOfRefinements;
return oldPartition.replacePartitionAdd(newPartitionAdd, nextFreeBlockIndex);
return oldPartition.replacePartition(newPartitionAdd, nextFreeBlockIndex);
}
private:
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> refine(Partition<storm::dd::DdType::CUDD, ValueType> const& oldPartition, storm::dd::Add<storm::dd::DdType::CUDD, ValueType> const& signatureAdd) {
STORM_LOG_ASSERT(oldPartition.storedAsAdd(), "Expecting partition to be stored as ADD for CUDD.");
// Clear the caches.
signatureCache.clear();
reuseBlocksCache.clear();
nextFreeBlockIndex = oldPartition.getNextFreeBlockIndex();
// Perform the actual recursive refinement step.
DdNodePtr result = refine(oldPartition.getPartitionAdd().getInternalAdd().getCuddDdNode(), signatureAdd.getInternalAdd().getCuddDdNode());
DdNodePtr result = refine(oldPartition.asAdd().getInternalAdd().getCuddDdNode(), signatureAdd.getInternalAdd().getCuddDdNode());
// Construct resulting ADD from the obtained node and the meta information.
storm::dd::InternalAdd<storm::dd::DdType::CUDD, ValueType> internalNewPartitionAdd(&internalDdManager, cudd::ADD(internalDdManager.getCuddManager(), result));
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> newPartitionAdd(oldPartition.getPartitionAdd().getDdManager(), internalNewPartitionAdd, oldPartition.getPartitionAdd().getContainedMetaVariables());
storm::dd::Add<storm::dd::DdType::CUDD, ValueType> newPartitionAdd(oldPartition.asAdd().getDdManager(), internalNewPartitionAdd, oldPartition.asAdd().getContainedMetaVariables());
return newPartitionAdd;
}
@ -67,13 +111,22 @@ namespace storm {
DdNodePtr refine(DdNode* partitionNode, DdNode* signatureNode) {
::DdManager* ddman = internalDdManager.getCuddManager().getManager();
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
// all states to be non-deadlock).
if (partitionNode == Cudd_ReadZero(ddman)) {
return partitionNode;
}
// Check the cache whether we have seen the same node before.
auto sigCacheIt = signatureCache.find(std::make_pair(signatureNode, partitionNode));
if (sigCacheIt != signatureCache.end()) {
std::unique_ptr<DdNode*>& sigCacheEntrySmartPtr = signatureCache[std::make_pair(signatureNode, partitionNode)];
if (sigCacheEntrySmartPtr) {
// If so, we return the corresponding result.
return sigCacheIt->second;
return *sigCacheEntrySmartPtr;
}
DdNode** newEntryPtr = new DdNode*;
sigCacheEntrySmartPtr.reset(newEntryPtr);
// Determine the levels in the DDs.
uint64_t partitionVariable = Cudd_NodeReadIndex(partitionNode) - 1;
uint64_t signatureVariable = Cudd_NodeReadIndex(signatureNode);
@ -103,43 +156,38 @@ namespace storm {
elseResult = refine(Cudd_E(partitionNode), Cudd_E(signatureNode));
Cudd_Ref(elseResult);
}
DdNode* result;
if (thenResult == elseResult) {
Cudd_Deref(thenResult);
Cudd_Deref(elseResult);
return thenResult;
result = thenResult;
} else {
// Get the node to connect the subresults.
DdNode* var = Cudd_addIthVar(ddman, topVariable + 1);
Cudd_Ref(var);
result = Cudd_addIte(ddman, var, thenResult, elseResult);
Cudd_Ref(result);
Cudd_RecursiveDeref(ddman, var);
Cudd_Deref(thenResult);
Cudd_Deref(elseResult);
}
// Get the node to connect the subresults.
DdNode* var = Cudd_addIthVar(ddman, topVariable + 1);
Cudd_Ref(var);
DdNode* result = Cudd_addIte(ddman, var, thenResult, elseResult);
Cudd_Ref(result);
Cudd_RecursiveDeref(ddman, var);
Cudd_Deref(thenResult);
Cudd_Deref(elseResult);
// Store the result in the cache.
signatureCache[std::make_pair(signatureNode, partitionNode)] = result;
*newEntryPtr = result;
Cudd_Deref(result);
return result;
} else {
// If we are not within the state encoding any more, we hit the signature itself.
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
// all states to be non-deadlock).
if (signatureNode == Cudd_ReadZero(ddman)) {
return signatureNode;
}
// If this is the first time (in this traversal) that we encounter this signature, we check
// whether we can assign the old block number to it.
auto reuseCacheIt = reuseBlocksCache.find(partitionNode);
if (reuseCacheIt == reuseBlocksCache.end()) {
reuseBlocksCache.emplace(partitionNode, true);
signatureCache[std::make_pair(signatureNode, partitionNode)] = partitionNode;
auto& reuseEntry = reuseBlocksCache[partitionNode];
if (!reuseEntry.isReused()) {
reuseEntry.setReused();
*newEntryPtr = partitionNode;
return partitionNode;
} else {
DdNode* result;
@ -149,7 +197,7 @@ namespace storm {
result = blockEncoding.getInternalAdd().getCuddDdNode();
Cudd_Ref(result);
}
signatureCache[std::make_pair(signatureNode, partitionNode)] = result;
*newEntryPtr = result;
Cudd_Deref(result);
return result;
}
@ -169,124 +217,191 @@ namespace storm {
// The number of completed refinements.
uint64_t numberOfRefinements;
// The number of nodes visited in the last refinement operation.
uint64_t lastNumberOfVisitedNodes;
// The cache used to identify states with identical signature.
spp::sparse_hash_map<std::pair<DdNode const*, DdNode const*>, DdNode*, CuddPointerPairHash> signatureCache;
spp::sparse_hash_map<std::pair<DdNode const*, DdNode const*>, std::unique_ptr<DdNode*>, CuddPointerPairHash> signatureCache;
// The cache used to identify which old block numbers have already been reused.
spp::sparse_hash_map<DdNode const*, bool> reuseBlocksCache;
spp::sparse_hash_map<DdNode const*, ReuseWrapper> reuseBlocksCache;
};
template<typename ValueType>
class InternalSignatureRefiner<storm::dd::DdType::Sylvan, ValueType> {
public:
InternalSignatureRefiner(storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager, storm::expressions::Variable const& blockVariable, uint64_t lastStateLevel) : manager(manager), internalDdManager(manager.getInternalDdManager()), blockVariable(blockVariable), lastStateLevel(lastStateLevel), nextFreeBlockIndex(0), numberOfRefinements(0) {
// Intentionally left empty.
InternalSignatureRefiner(storm::dd::DdManager<storm::dd::DdType::Sylvan> const& manager, storm::expressions::Variable const& blockVariable, uint64_t lastStateLevel) : manager(manager), internalDdManager(manager.getInternalDdManager()), blockVariable(blockVariable), numberOfBlockVariables(manager.getMetaVariable(blockVariable).getNumberOfDdVariables()), blockCube(manager.getMetaVariable(blockVariable).getCube()), lastStateLevel(lastStateLevel), nextFreeBlockIndex(0), numberOfRefinements(0), signatureCache() {
// Perform garbage collection to clean up stuff not needed anymore.
LACE_ME;
sylvan_gc();
}
Partition<storm::dd::DdType::Sylvan, ValueType> refine(Partition<storm::dd::DdType::Sylvan, ValueType> const& oldPartition, Signature<storm::dd::DdType::Sylvan, ValueType> const& signature) {
storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> newPartitionAdd = refine(oldPartition, signature.getSignatureAdd());
++numberOfRefinements;
return oldPartition.replacePartitionAdd(newPartitionAdd, nextFreeBlockIndex);
storm::dd::Bdd<storm::dd::DdType::Sylvan> newPartitionBdd = refine(oldPartition, signature.getSignatureAdd());
return oldPartition.replacePartition(newPartitionBdd, nextFreeBlockIndex);
}
private:
storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> refine(Partition<storm::dd::DdType::Sylvan, ValueType> const& oldPartition, storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> const& signatureAdd) {
storm::dd::Bdd<storm::dd::DdType::Sylvan> refine(Partition<storm::dd::DdType::Sylvan, ValueType> const& oldPartition, storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> const& signatureAdd) {
STORM_LOG_ASSERT(oldPartition.storedAsBdd(), "Expecting partition to be stored as BDD for Sylvan.");
LACE_ME;
// Set up next refinement.
++numberOfRefinements;
// Clear the caches.
std::size_t oldSize = signatureCache.size();
signatureCache.clear();
signatureCache.reserve(3 * oldSize);
reuseBlocksCache.clear();
reuseBlocksCache.reserve(3 * oldPartition.getNumberOfBlocks());
nextFreeBlockIndex = oldPartition.getNextFreeBlockIndex();
// Clear performance counters.
// signatureCacheLookups = 0;
// signatureCacheHits = 0;
// numberOfVisitedNodes = 0;
// totalSignatureCacheLookupTime = std::chrono::high_resolution_clock::duration(0);
// totalSignatureCacheStoreTime = std::chrono::high_resolution_clock::duration(0);
// totalReuseBlocksLookupTime = std::chrono::high_resolution_clock::duration(0);
// totalLevelLookupTime = std::chrono::high_resolution_clock::duration(0);
// totalBlockEncodingTime = std::chrono::high_resolution_clock::duration(0);
// totalMakeNodeTime = std::chrono::high_resolution_clock::duration(0);
// Perform the actual recursive refinement step.
MTBDD result = refine(oldPartition.getPartitionAdd().getInternalAdd().getSylvanMtbdd().GetMTBDD(), signatureAdd.getInternalAdd().getSylvanMtbdd().GetMTBDD());
BDD result = refine(oldPartition.asBdd().getInternalBdd().getSylvanBdd().GetBDD(), signatureAdd.getInternalAdd().getSylvanMtbdd().GetMTBDD());
// Construct resulting BDD from the obtained node and the meta information.
storm::dd::InternalBdd<storm::dd::DdType::Sylvan> internalNewPartitionBdd(&internalDdManager, sylvan::Bdd(result));
storm::dd::Bdd<storm::dd::DdType::Sylvan> newPartitionBdd(oldPartition.asBdd().getDdManager(), internalNewPartitionBdd, oldPartition.asBdd().getContainedMetaVariables());
// Construct resulting ADD from the obtained node and the meta information.
storm::dd::InternalAdd<storm::dd::DdType::Sylvan, ValueType> internalNewPartitionAdd(&internalDdManager, sylvan::Mtbdd(result));
storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> newPartitionAdd(oldPartition.getPartitionAdd().getDdManager(), internalNewPartitionAdd, oldPartition.getPartitionAdd().getContainedMetaVariables());
// // Display some statistics.
// STORM_LOG_TRACE("Refinement visited " << numberOfVisitedNodes << " nodes.");
// STORM_LOG_TRACE("Current #nodes in table: " << llmsset_count_marked(nodes) << " of " << llmsset_get_size(nodes) << ", cache: " << cache_getused() << " of " << cache_getsize() << ".");
// STORM_LOG_TRACE("Signature cache hits: " << signatureCacheHits << ", misses: " << (signatureCacheLookups - signatureCacheHits) << ".");
// STORM_LOG_TRACE("Signature cache lookup time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalSignatureCacheLookupTime).count() << "ms");
// STORM_LOG_TRACE("Signature cache store time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalSignatureCacheStoreTime).count() << "ms");
// STORM_LOG_TRACE("Signature cache total time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalSignatureCacheStoreTime + totalSignatureCacheLookupTime).count() << "ms");
// STORM_LOG_TRACE("Reuse blocks lookup time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalReuseBlocksLookupTime).count() << "ms");
// STORM_LOG_TRACE("Level lookup time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalLevelLookupTime).count() << "ms");
// STORM_LOG_TRACE("Block encoding time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalBlockEncodingTime).count() << "ms");
// STORM_LOG_TRACE("Make node time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalMakeNodeTime).count() << "ms");
return newPartitionAdd;
return newPartitionBdd;
}
BDD encodeBlock(uint64_t blockIndex) {
std::vector<uint8_t> e(numberOfBlockVariables);
for (uint64_t i = 0; i < numberOfBlockVariables; ++i) {
e[i] = blockIndex & 1 ? 1 : 0;
blockIndex >>= 1;
}
return sylvan_cube(blockCube.getInternalBdd().getSylvanBdd().GetBDD(), e.data());
}
MTBDD refine(MTBDD partitionNode, MTBDD signatureNode) {
BDD refine(BDD partitionNode, MTBDD signatureNode) {
LACE_ME;
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
// all states to be non-deadlock).
if (partitionNode == sylvan_false) {
return partitionNode;
}
STORM_LOG_ASSERT(partitionNode != mtbdd_false, "Expected non-false node.");
// ++numberOfVisitedNodes;
// Check the cache whether we have seen the same node before.
auto sigCacheIt = signatureCache.find(std::make_pair(signatureNode, partitionNode));
if (sigCacheIt != signatureCache.end()) {
// ++signatureCacheLookups;
// auto start = std::chrono::high_resolution_clock::now();
std::unique_ptr<MTBDD>& sigCacheEntrySmartPtr = signatureCache[std::make_pair(signatureNode, partitionNode)];
if (sigCacheEntrySmartPtr) {
// ++signatureCacheHits;
// If so, we return the corresponding result.
return sigCacheIt->second;
// auto end = std::chrono::high_resolution_clock::now();
// totalSignatureCacheLookupTime += end - start;
return *sigCacheEntrySmartPtr;
}
// auto end = std::chrono::high_resolution_clock::now();
// totalSignatureCacheLookupTime += end - start;
MTBDD* newEntryPtr = new MTBDD;
sigCacheEntrySmartPtr.reset(newEntryPtr);
sylvan_gc_test();
// Determine levels in the DDs.
BDDVAR signatureVariable = mtbdd_isleaf(signatureNode) ? 0xffffffff : sylvan_var(signatureNode);
BDDVAR partitionVariable = mtbdd_isleaf(signatureNode) ? 0xffffffff : sylvan_var(partitionNode) - 1;
// start = std::chrono::high_resolution_clock::now();
BDDVAR signatureVariable = sylvan_isconst(signatureNode) ? 0xffffffff : sylvan_var(signatureNode);
BDDVAR partitionVariable = sylvan_var(partitionNode) - 1;
BDDVAR topVariable = std::min(signatureVariable, partitionVariable);
// end = std::chrono::high_resolution_clock::now();
// totalLevelLookupTime += end - start;
// Check whether the top variable is still within the state encoding.
if (topVariable <= lastStateLevel) {
// Determine subresults by recursive descent.
MTBDD thenResult;
MTBDD elseResult;
BDD thenResult;
BDD elseResult;
if (partitionVariable < signatureVariable) {
thenResult = refine(sylvan_high(partitionNode), signatureNode);
sylvan_protect(&thenResult);
elseResult = refine(sylvan_low(partitionNode), signatureNode);
sylvan_protect(&elseResult);
thenResult = refine(sylvan_high(partitionNode), signatureNode);
} else if (partitionVariable > signatureVariable) {
thenResult = refine(partitionNode, sylvan_high(signatureNode));
sylvan_protect(&thenResult);
elseResult = refine(partitionNode, sylvan_low(signatureNode));
sylvan_protect(&elseResult);
thenResult = refine(partitionNode, sylvan_high(signatureNode));
} else {
thenResult = refine(sylvan_high(partitionNode), sylvan_high(signatureNode));
sylvan_protect(&thenResult);
elseResult = refine(sylvan_low(partitionNode), sylvan_low(signatureNode));
sylvan_protect(&elseResult);
thenResult = refine(sylvan_high(partitionNode), sylvan_high(signatureNode));
}
BDD result;
if (thenResult == elseResult) {
sylvan_unprotect(&thenResult);
sylvan_unprotect(&elseResult);
return thenResult;
result = thenResult;
} else {
// Get the node to connect the subresults.
// start = std::chrono::high_resolution_clock::now();
result = sylvan_makenode(topVariable + 1, elseResult, thenResult);
// end = std::chrono::high_resolution_clock::now();
// totalMakeNodeTime += end - start;
}
// Get the node to connect the subresults.
MTBDD result = sylvan_makenode(topVariable + 1, elseResult, thenResult);// mtbdd_ite(sylvan_ithvar(topVariable), thenResult, elseResult);
sylvan_protect(&result);
sylvan_unprotect(&thenResult);
sylvan_unprotect(&elseResult);
// Store the result in the cache.
signatureCache[std::make_pair(signatureNode, partitionNode)] = result;
// start = std::chrono::high_resolution_clock::now();
*newEntryPtr = result;
// end = std::chrono::high_resolution_clock::now();
// totalSignatureCacheStoreTime += end - start;
sylvan_unprotect(&result);
return result;
} else {
// If we are not within the state encoding any more, we hit the signature itself.
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
// all states to be non-deadlock).
if (mtbdd_iszero(signatureNode)) {
return signatureNode;
}
// If this is the first time (in this traversal) that we encounter this signature, we check
// whether we can assign the old block number to it.
auto reuseCacheIt = reuseBlocksCache.find(partitionNode);
if (reuseCacheIt == reuseBlocksCache.end()) {
// start = std::chrono::high_resolution_clock::now();
auto& reuseBlockEntry = reuseBlocksCache[partitionNode];
// end = std::chrono::high_resolution_clock::now();
// totalReuseBlocksLookupTime += end - start;
if (!reuseBlockEntry.isReused()) {
reuseBlockEntry.setReused();
reuseBlocksCache.emplace(partitionNode, true);
signatureCache[std::make_pair(signatureNode, partitionNode)] = partitionNode;
// start = std::chrono::high_resolution_clock::now();
*newEntryPtr = partitionNode;
// end = std::chrono::high_resolution_clock::now();
// totalSignatureCacheStoreTime += end - start;
return partitionNode;
} else {
MTBDD result;
{
storm::dd::Add<storm::dd::DdType::Sylvan, ValueType> blockEncoding = manager.getEncoding(blockVariable, nextFreeBlockIndex, false).template toAdd<ValueType>();
++nextFreeBlockIndex;
result = blockEncoding.getInternalAdd().getSylvanMtbdd().GetMTBDD();
sylvan_protect(&result);
}
signatureCache[std::make_pair(signatureNode, partitionNode)] = result;
sylvan_unprotect(&result);
// start = std::chrono::high_resolution_clock::now();
BDD result = encodeBlock(nextFreeBlockIndex++);
// end = std::chrono::high_resolution_clock::now();
// totalBlockEncodingTime += end - start;
// start = std::chrono::high_resolution_clock::now();
*newEntryPtr = result;
// end = std::chrono::high_resolution_clock::now();
// totalSignatureCacheStoreTime += end - start;
return result;
}
}
@ -296,6 +411,10 @@ namespace storm {
storm::dd::InternalDdManager<storm::dd::DdType::Sylvan> const& internalDdManager;
storm::expressions::Variable const& blockVariable;
uint64_t numberOfBlockVariables;
storm::dd::Bdd<storm::dd::DdType::Sylvan> blockCube;
// The last level that belongs to the state encoding in the DDs.
uint64_t lastStateLevel;
@ -306,10 +425,22 @@ namespace storm {
uint64_t numberOfRefinements;
// The cache used to identify states with identical signature.
spp::sparse_hash_map<std::pair<MTBDD, MTBDD>, MTBDD, SylvanMTBDDPairHash> signatureCache;
spp::sparse_hash_map<std::pair<MTBDD, MTBDD>, std::unique_ptr<MTBDD>, SylvanMTBDDPairHash> signatureCache;
// The cache used to identify which old block numbers have already been reused.
spp::sparse_hash_map<MTBDD, bool> reuseBlocksCache;
spp::sparse_hash_map<MTBDD, ReuseWrapper> reuseBlocksCache;
// Performance counters.
// uint64_t signatureCacheLookups;
// uint64_t signatureCacheHits;
// uint64_t numberOfVisitedNodes;
// std::chrono::high_resolution_clock::duration totalSignatureCacheLookupTime;
// std::chrono::high_resolution_clock::duration totalSignatureCacheStoreTime;
// std::chrono::high_resolution_clock::duration totalReuseBlocksLookupTime;
// std::chrono::high_resolution_clock::duration totalLevelLookupTime;
// std::chrono::high_resolution_clock::duration totalBlockEncodingTime;
// std::chrono::high_resolution_clock::duration totalMakeNodeTime;
};
template<storm::dd::DdType DdType, typename ValueType>
@ -336,7 +467,6 @@ namespace storm {
template class SignatureRefiner<storm::dd::DdType::Sylvan, storm::RationalNumber>;
template class SignatureRefiner<storm::dd::DdType::Sylvan, storm::RationalFunction>;
}
}
}

9
src/storm/storage/dd/cudd/InternalCuddAdd.cpp

@ -209,11 +209,16 @@ namespace storm {
summationAdds.push_back(ddVariable.toAdd<ValueType>().getCuddAdd());
}
return InternalAdd<DdType::CUDD, ValueType>(ddManager, this->getCuddAdd().TimesPlus(otherMatrix.getCuddAdd(), summationAdds));
return InternalAdd<DdType::CUDD, ValueType>(ddManager, this->getCuddAdd().Triangle(otherMatrix.getCuddAdd(), summationAdds));
// return InternalAdd<DdType::CUDD, ValueType>(ddManager, this->getCuddAdd().TimesPlus(otherMatrix.getCuddAdd(), summationAdds));
// return InternalAdd<DdType::CUDD, ValueType>(ddManager, this->getCuddAdd().Triangle(otherMatrix.getCuddAdd(), summationAdds));
return InternalAdd<DdType::CUDD, ValueType>(ddManager, this->getCuddAdd().MatrixMultiply(otherMatrix.getCuddAdd(), summationAdds));
}
template<typename ValueType>
InternalAdd<DdType::CUDD, ValueType> InternalAdd<DdType::CUDD, ValueType>::multiplyMatrix(InternalBdd<DdType::CUDD> const& otherMatrix, std::vector<InternalBdd<DdType::CUDD>> const& summationDdVariables) const {
return this->multiplyMatrix(otherMatrix.template toAdd<ValueType>(), summationDdVariables);
}
template<typename ValueType>
InternalBdd<DdType::CUDD> InternalAdd<DdType::CUDD, ValueType>::greater(ValueType const& value) const {
return InternalBdd<DdType::CUDD>(ddManager, this->getCuddAdd().BddStrictThreshold(value));

12
src/storm/storage/dd/cudd/InternalCuddAdd.h

@ -328,7 +328,17 @@ namespace storm {
* @return An ADD representing the result of the matrix-matrix multiplication.
*/
InternalAdd<DdType::CUDD, ValueType> multiplyMatrix(InternalAdd<DdType::CUDD, ValueType> const& otherMatrix, std::vector<InternalBdd<DdType::CUDD>> const& summationDdVariables) const;
/*!
* Multiplies the current ADD (representing a matrix) with the given matrix by summing over the given meta
* variables.
*
* @param otherMatrix The matrix with which to multiply.
* @param summationDdVariables The DD variables (represented as ADDs) over which to sum.
* @return An ADD representing the result of the matrix-matrix multiplication.
*/
InternalAdd<DdType::CUDD, ValueType> multiplyMatrix(InternalBdd<DdType::CUDD> const& otherMatrix, std::vector<InternalBdd<DdType::CUDD>> const& summationDdVariables) const;
/*!
* Computes a BDD that represents the function in which all assignments with a function value strictly
* larger than the given value are mapped to one and all others to zero.

9
src/storm/storage/dd/cudd/InternalCuddDdManager.cpp

@ -3,6 +3,8 @@
#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/CuddSettings.h"
#include "storm/exceptions/NotSupportedException.h"
namespace storm {
namespace dd {
@ -55,7 +57,12 @@ namespace storm {
InternalAdd<DdType::CUDD, ValueType> InternalDdManager<DdType::CUDD>::getAddZero() const {
return InternalAdd<DdType::CUDD, ValueType>(this, cuddManager.addZero());
}
template<typename ValueType>
InternalAdd<DdType::CUDD, ValueType> InternalDdManager<DdType::CUDD>::getAddUndefined() const {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Undefined values are not supported by CUDD.");
}
template<typename ValueType>
InternalAdd<DdType::CUDD, ValueType> InternalDdManager<DdType::CUDD>::getConstant(ValueType const& value) const {
return InternalAdd<DdType::CUDD, ValueType>(this, cuddManager.constant(value));

10
src/storm/storage/dd/cudd/InternalCuddDdManager.h

@ -66,7 +66,15 @@ namespace storm {
*/
template<typename ValueType>
InternalAdd<DdType::CUDD, ValueType> getAddZero() const;
/*!
* Retrieves an ADD representing an undefined value.
*
* @return An ADD representing an undefined value.
*/
template<typename ValueType>
InternalAdd<DdType::CUDD, ValueType> getAddUndefined() const;
/*!
* Retrieves an ADD representing the constant function with the given value.
*

40
src/storm/storage/dd/sylvan/InternalSylvanAdd.cpp

@ -581,6 +581,18 @@ namespace storm {
return InternalAdd<DdType::Sylvan, ValueType>(ddManager, this->sylvanMtbdd.AndExists(otherMatrix.sylvanMtbdd, summationVariables.getSylvanBdd()));
}
#ifdef STORM_HAVE_CARL
template<>
InternalAdd<DdType::Sylvan, storm::RationalFunction> InternalAdd<DdType::Sylvan, storm::RationalFunction>::multiplyMatrix(InternalAdd<DdType::Sylvan, storm::RationalFunction> const& otherMatrix, std::vector<InternalBdd<DdType::Sylvan>> const& summationDdVariables) const {
InternalBdd<DdType::Sylvan> summationVariables = ddManager->getBddOne();
for (auto const& ddVariable : summationDdVariables) {
summationVariables &= ddVariable;
}
return InternalAdd<DdType::Sylvan, storm::RationalFunction>(ddManager, this->sylvanMtbdd.AndExistsRF(otherMatrix.sylvanMtbdd, summationVariables.getSylvanBdd()));
}
#endif
template<>
InternalAdd<DdType::Sylvan, storm::RationalNumber> InternalAdd<DdType::Sylvan, storm::RationalNumber>::multiplyMatrix(InternalAdd<DdType::Sylvan, storm::RationalNumber> const& otherMatrix, std::vector<InternalBdd<DdType::Sylvan>> const& summationDdVariables) const {
@ -591,19 +603,39 @@ namespace storm {
return InternalAdd<DdType::Sylvan, storm::RationalNumber>(ddManager, this->sylvanMtbdd.AndExistsRN(otherMatrix.sylvanMtbdd, summationVariables.getSylvanBdd()));
}
template<typename ValueType>
InternalAdd<DdType::Sylvan, ValueType> InternalAdd<DdType::Sylvan, ValueType>::multiplyMatrix(InternalBdd<DdType::Sylvan> const& otherMatrix, std::vector<InternalBdd<DdType::Sylvan>> const& summationDdVariables) const {
InternalBdd<DdType::Sylvan> summationVariables = ddManager->getBddOne();
for (auto const& ddVariable : summationDdVariables) {
summationVariables &= ddVariable;
}
return InternalAdd<DdType::Sylvan, ValueType>(ddManager, this->sylvanMtbdd.AndExists(otherMatrix.getSylvanBdd().GetBDD(), summationVariables.getSylvanBdd()));
}
#ifdef STORM_HAVE_CARL
template<>
InternalAdd<DdType::Sylvan, storm::RationalFunction> InternalAdd<DdType::Sylvan, storm::RationalFunction>::multiplyMatrix(InternalAdd<DdType::Sylvan, storm::RationalFunction> const& otherMatrix, std::vector<InternalBdd<DdType::Sylvan>> const& summationDdVariables) const {
InternalAdd<DdType::Sylvan, storm::RationalFunction> InternalAdd<DdType::Sylvan, storm::RationalFunction>::multiplyMatrix(InternalBdd<DdType::Sylvan> const& otherMatrix, std::vector<InternalBdd<DdType::Sylvan>> const& summationDdVariables) const {
InternalBdd<DdType::Sylvan> summationVariables = ddManager->getBddOne();
for (auto const& ddVariable : summationDdVariables) {
summationVariables &= ddVariable;
}
return InternalAdd<DdType::Sylvan, storm::RationalFunction>(ddManager, this->sylvanMtbdd.AndExistsRF(otherMatrix.sylvanMtbdd, summationVariables.getSylvanBdd()));
return InternalAdd<DdType::Sylvan, storm::RationalFunction>(ddManager, this->sylvanMtbdd.AndExistsRF(otherMatrix.getSylvanBdd().GetBDD(), summationVariables.getSylvanBdd()));
}
#endif
template<>
InternalAdd<DdType::Sylvan, storm::RationalNumber> InternalAdd<DdType::Sylvan, storm::RationalNumber>::multiplyMatrix(InternalBdd<DdType::Sylvan> const& otherMatrix, std::vector<InternalBdd<DdType::Sylvan>> const& summationDdVariables) const {
InternalBdd<DdType::Sylvan> summationVariables = ddManager->getBddOne();
for (auto const& ddVariable : summationDdVariables) {
summationVariables &= ddVariable;
}
return InternalAdd<DdType::Sylvan, storm::RationalNumber>(ddManager, this->sylvanMtbdd.AndExistsRN(otherMatrix.getSylvanBdd().GetBDD(), summationVariables.getSylvanBdd()));
}
template<typename ValueType>
InternalBdd<DdType::Sylvan> InternalAdd<DdType::Sylvan, ValueType>::greater(ValueType const& value) const {
return InternalBdd<DdType::Sylvan>(ddManager, this->sylvanMtbdd.BddStrictThreshold(value));

12
src/storm/storage/dd/sylvan/InternalSylvanAdd.h

@ -331,7 +331,17 @@ namespace storm {
* @return An ADD representing the result of the matrix-matrix multiplication.
*/
InternalAdd<DdType::Sylvan, ValueType> multiplyMatrix(InternalAdd<DdType::Sylvan, ValueType> const& otherMatrix, std::vector<InternalBdd<DdType::Sylvan>> const& summationDdVariables) const;
/*!
* Multiplies the current ADD (representing a matrix) with the given matrix by summing over the given meta
* variables.
*
* @param otherMatrix The matrix with which to multiply.
* @param summationDdVariables The DD variables (represented as ADDs) over which to sum.
* @return An ADD representing the result of the matrix-matrix multiplication.
*/
InternalAdd<DdType::Sylvan, ValueType> multiplyMatrix(InternalBdd<DdType::Sylvan> const& otherMatrix, std::vector<InternalBdd<DdType::Sylvan>> const& summationDdVariables) const;
/*!
* Computes a BDD that represents the function in which all assignments with a function value strictly
* larger than the given value are mapped to one and all others to zero.

27
src/storm/storage/dd/sylvan/InternalSylvanBdd.h

@ -376,6 +376,20 @@ namespace storm {
friend struct std::hash<storm::dd::InternalBdd<storm::dd::DdType::Sylvan>>;
/*!
* Retrieves the sylvan BDD.
*
* @return The sylvan BDD.
*/
sylvan::Bdd& getSylvanBdd();
/*!
* Retrieves the sylvan BDD.
*
* @return The sylvan BDD.
*/
sylvan::Bdd const& getSylvanBdd() const;
private:
/*!
* Builds a BDD representing the values that make the given filter function evaluate to true.
@ -474,19 +488,6 @@ namespace storm {
*/
static storm::expressions::Variable toExpressionRec(BDD dd, storm::expressions::ExpressionManager& manager, std::vector<storm::expressions::Expression>& expressions, std::unordered_map<uint_fast64_t, storm::expressions::Variable>& indexToVariableMap, std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, storm::expressions::Variable>& countIndexToVariablePair, std::unordered_map<BDD, uint_fast64_t>& nodeToCounterMap, std::vector<uint_fast64_t>& nextCounterForIndex);
/*!
* Retrieves the sylvan BDD.
*
* @return The sylvan BDD.
*/
sylvan::Bdd& getSylvanBdd();
/*!
* Retrieves the sylvan BDD.
*
* @return The sylvan BDD.
*/
sylvan::Bdd const& getSylvanBdd() const;
// The internal manager responsible for this BDD.
InternalDdManager<DdType::Sylvan> const* ddManager;

49
src/storm/storage/dd/sylvan/InternalSylvanDdManager.cpp

@ -17,6 +17,17 @@
namespace storm {
namespace dd {
#ifndef NDEBUG
VOID_TASK_0(gc_start) {
STORM_LOG_TRACE("Starting sylvan garbage collection...");
}
VOID_TASK_0(gc_end) {
STORM_LOG_TRACE("Sylvan garbage collection done.");
}
#endif
uint_fast64_t InternalDdManager<DdType::Sylvan>::numberOfInstances = 0;
// It is important that the variable pairs start at an even offset, because sylvan assumes this to be true for
@ -50,15 +61,27 @@ namespace storm {
STORM_LOG_THROW(powerOfTwo >= 16, storm::exceptions::InvalidSettingsException, "Too little memory assigned to sylvan.");
if ((((1ull << powerOfTwo) + (1ull << (powerOfTwo - 1)))) < totalNodesToStore) {
sylvan::Sylvan::initPackage(1ull << powerOfTwo, 1ull << powerOfTwo, 1ull << (powerOfTwo - 1), 1ull << (powerOfTwo - 1));
} else {
sylvan::Sylvan::initPackage(1ull << (powerOfTwo - 1), 1ull << (powerOfTwo - 1), 1ull << (powerOfTwo - 1), 1ull << (powerOfTwo - 1));
uint64_t maxTableSize = 1ull << powerOfTwo;
uint64_t maxCacheSize = 1ull << (powerOfTwo - 1);
if (maxTableSize + maxCacheSize > totalNodesToStore) {
maxTableSize >>= 1;
}
sylvan::Sylvan::setGranularity(3);
uint64_t initialTableSize = 1ull << std::max(powerOfTwo - 4, 16ull);
uint64_t initialCacheSize = 1ull << std::max(powerOfTwo - 4, 16ull);
STORM_LOG_DEBUG("Initializing sylvan. Initial/max table size: " << initialTableSize << "/" << maxTableSize << ", initial/max cache size: " << initialCacheSize << "/" << maxCacheSize << ".");
sylvan::Sylvan::initPackage(initialTableSize, maxTableSize, initialCacheSize, maxCacheSize);
sylvan::Sylvan::initBdd();
sylvan::Sylvan::initMtbdd();
sylvan::Sylvan::initCustomMtbdd();
#ifndef NDEBUG
sylvan_gc_hook_pregc(TASK(gc_start));
sylvan_gc_hook_postgc(TASK(gc_end));
#endif
}
++numberOfInstances;
}
@ -127,7 +150,12 @@ namespace storm {
return InternalAdd<DdType::Sylvan, storm::RationalFunction>(this, sylvan::Mtbdd::stormRationalFunctionTerminal(storm::utility::zero<storm::RationalFunction>()));
}
#endif
template<typename ValueType>
InternalAdd<DdType::Sylvan, ValueType> InternalDdManager<DdType::Sylvan>::getAddUndefined() const {
return InternalAdd<DdType::Sylvan, ValueType>(this, sylvan::Mtbdd(sylvan::Bdd::bddZero()));
}
template<>
InternalAdd<DdType::Sylvan, double> InternalDdManager<DdType::Sylvan>::getConstant(double const& value) const {
return InternalAdd<DdType::Sylvan, double>(this, sylvan::Mtbdd::doubleTerminal(value));
@ -200,6 +228,15 @@ namespace storm {
#ifdef STORM_HAVE_CARL
template InternalAdd<DdType::Sylvan, storm::RationalFunction> InternalDdManager<DdType::Sylvan>::getAddZero() const;
#endif
template InternalAdd<DdType::Sylvan, double> InternalDdManager<DdType::Sylvan>::getAddUndefined() const;
template InternalAdd<DdType::Sylvan, uint_fast64_t> InternalDdManager<DdType::Sylvan>::getAddUndefined() const;
template InternalAdd<DdType::Sylvan, storm::RationalNumber> InternalDdManager<DdType::Sylvan>::getAddUndefined() const;
#ifdef STORM_HAVE_CARL
template InternalAdd<DdType::Sylvan, storm::RationalFunction> InternalDdManager<DdType::Sylvan>::getAddUndefined() const;
#endif
template InternalAdd<DdType::Sylvan, double> InternalDdManager<DdType::Sylvan>::getConstant(double const& value) const;
template InternalAdd<DdType::Sylvan, uint_fast64_t> InternalDdManager<DdType::Sylvan>::getConstant(uint_fast64_t const& value) const;

8
src/storm/storage/dd/sylvan/InternalSylvanDdManager.h

@ -68,6 +68,14 @@ namespace storm {
template<typename ValueType>
InternalAdd<DdType::Sylvan, ValueType> getAddZero() const;
/*!
* Retrieves an ADD representing an undefined value.
*
* @return An ADD representing an undefined value.
*/
template<typename ValueType>
InternalAdd<DdType::Sylvan, ValueType> getAddUndefined() const;
/*!
* Retrieves an ADD representing the constant function with the given value.
*

2
src/storm/storage/geometry/NativePolytope.cpp

@ -25,7 +25,7 @@ namespace storm {
uint_fast64_t maxCol = halfspaces.front().normalVector().size();
uint_fast64_t maxRow = halfspaces.size();
A = EigenMatrix(maxRow, maxCol);
b = EigenVector(maxRow);
b = EigenVector(static_cast<unsigned long int>(maxRow));
for (int_fast64_t row = 0; row < A.rows(); ++row ){
assert(halfspaces[row].normalVector().size() == maxCol);
b(row) = halfspaces[row].offset();

Loading…
Cancel
Save