diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 92b35f18f..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "resources/3rdparty/cusplibrary"] - path = resources/3rdparty/cusplibrary - url = https://github.com/cusplibrary/cusplibrary.git diff --git a/.travis.yml b/.travis.yml index 2b5f50657..a9b1577af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ branches: only: - master - stable +sudo: required dist: trusty language: cpp @@ -20,7 +21,6 @@ cache: # Enable docker support services: - docker -sudo: required notifications: email: @@ -43,7 +43,7 @@ jobs: - stage: Build (1st run) os: osx compiler: clang - env: CONFIG=DefaultDebug COMPILER=clang-4.0 STL=libc++ + env: CONFIG=DefaultDebug COMPILER=clang STL=libc++ install: - rm -rf build - travis/install_osx.sh @@ -54,7 +54,7 @@ jobs: - stage: Build (1st run) os: osx compiler: clang - env: CONFIG=DefaultRelease COMPILER=clang-4.0 STL=libc++ + env: CONFIG=DefaultRelease COMPILER=clang STL=libc++ install: - rm -rf build - travis/install_osx.sh @@ -66,7 +66,7 @@ jobs: - stage: Build (1st run) os: linux compiler: gcc - env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc-6 + env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc install: - rm -rf build - travis/install_linux.sh @@ -79,7 +79,7 @@ jobs: - stage: Build (1st run) os: linux compiler: gcc - env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc-6 + env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc install: - rm -rf build - travis/install_linux.sh @@ -98,7 +98,7 @@ jobs: - stage: Build (2nd run) os: osx compiler: clang - env: CONFIG=DefaultDebug COMPILER=clang-4.0 STL=libc++ + env: CONFIG=DefaultDebug COMPILER=clang STL=libc++ install: - travis/install_osx.sh script: @@ -108,7 +108,7 @@ jobs: - stage: Build (2nd run) os: osx compiler: clang - env: CONFIG=DefaultRelease COMPILER=clang-4.0 STL=libc++ + env: CONFIG=DefaultRelease COMPILER=clang STL=libc++ install: - travis/install_osx.sh script: @@ -119,7 +119,7 @@ jobs: - stage: Build (2nd run) os: linux compiler: gcc - env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc-6 + env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc install: - travis/install_linux.sh script: @@ -131,7 +131,7 @@ jobs: - stage: Build (2nd run) os: linux compiler: gcc - env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc-6 + env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc install: - travis/install_linux.sh script: @@ -149,7 +149,7 @@ jobs: - stage: Build (3rd run) os: osx compiler: clang - env: CONFIG=DefaultDebug COMPILER=clang-4.0 STL=libc++ + env: CONFIG=DefaultDebug COMPILER=clang STL=libc++ install: - travis/install_osx.sh script: @@ -159,7 +159,7 @@ jobs: - stage: Build (3rd run) os: osx compiler: clang - env: CONFIG=DefaultRelease COMPILER=clang-4.0 STL=libc++ + env: CONFIG=DefaultRelease COMPILER=clang STL=libc++ install: - travis/install_osx.sh script: @@ -170,7 +170,7 @@ jobs: - stage: Build (3rd run) os: linux compiler: gcc - env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc-6 + env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc install: - travis/install_linux.sh script: @@ -182,7 +182,7 @@ jobs: - stage: Build (3rd run) os: linux compiler: gcc - env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc-6 + env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc install: - travis/install_linux.sh script: @@ -200,68 +200,17 @@ jobs: - stage: Build (4th run) os: osx compiler: clang - env: CONFIG=DefaultDebug COMPILER=clang-4.0 STL=libc++ - install: - - travis/install_osx.sh - script: - - travis/build.sh Build4 - after_failure: - - find build -iname '*err*.log' -type f -print -exec cat {} \; - - stage: Build (4th run) - os: osx - compiler: clang - env: CONFIG=DefaultRelease COMPILER=clang-4.0 STL=libc++ - install: - - travis/install_osx.sh - script: - - travis/build.sh Build4 - after_failure: - - find build -iname '*err*.log' -type f -print -exec cat {} \; - # ubuntu-16.10 - - stage: Build (4th run) - os: linux - compiler: gcc - env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc-6 - install: - - travis/install_linux.sh - script: - - travis/build.sh Build4 - before_cache: - - docker cp storm:/storm/. . - after_failure: - - find build -iname '*err*.log' -type f -print -exec cat {} \; - - stage: Build (4th run) - os: linux - compiler: gcc - env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc-6 - install: - - travis/install_linux.sh - script: - - travis/build.sh Build4 - before_cache: - - docker cp storm:/storm/. . - after_failure: - - find build -iname '*err*.log' -type f -print -exec cat {} \; - - ### - # Stage: Build (5th run) - ### - - # osx - - stage: Build (5th run) - os: osx - compiler: clang - env: CONFIG=DefaultDebug COMPILER=clang-4.0 STL=libc++ + env: CONFIG=DefaultDebug COMPILER=clang STL=libc++ install: - travis/install_osx.sh script: - travis/build.sh BuildLast after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - - stage: Build (5th run) + - stage: Build (4th run) os: osx compiler: clang - env: CONFIG=DefaultRelease COMPILER=clang-4.0 STL=libc++ + env: CONFIG=DefaultRelease COMPILER=clang STL=libc++ install: - travis/install_osx.sh script: @@ -269,10 +218,10 @@ jobs: after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; # ubuntu-16.10 - - stage: Build (5th run) + - stage: Build (4th run) os: linux compiler: gcc - env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc-6 + env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc install: - travis/install_linux.sh script: @@ -281,10 +230,10 @@ jobs: - docker cp storm:/storm/. . after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - - stage: Build (5th run) + - stage: Build (4th run) os: linux compiler: gcc - env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc-6 + env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc install: - travis/install_linux.sh script: @@ -302,7 +251,7 @@ jobs: - stage: Test all os: osx compiler: clang - env: CONFIG=DefaultDebug COMPILER=clang-4.0 STL=libc++ + env: CONFIG=DefaultDebug COMPILER=clang STL=libc++ install: - travis/install_osx.sh script: @@ -312,7 +261,7 @@ jobs: - stage: Test all os: osx compiler: clang - env: CONFIG=DefaultRelease COMPILER=clang-4.0 STL=libc++ + env: CONFIG=DefaultRelease COMPILER=clang STL=libc++ install: - travis/install_osx.sh script: @@ -323,7 +272,7 @@ jobs: - stage: Test all os: linux compiler: gcc - env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc-6 + env: CONFIG=DefaultDebug LINUX=ubuntu-16.10 COMPILER=gcc install: - travis/install_linux.sh script: @@ -335,7 +284,7 @@ jobs: - stage: Test all os: linux compiler: gcc - env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc-6 + env: CONFIG=DefaultRelease LINUX=ubuntu-16.10 COMPILER=gcc install: - travis/install_linux.sh script: diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e971c46e..8e4c84799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Version 1.1.x - storm-pars: support for welldefinedness constraints in mdps. - symbolic (MT/BDD) bisimulation - Fixed issue related to variable names that can not be used in Exprtk. +- DRN parser improved ### Version 1.1.0 (2017/8) diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index fd321e147..000000000 --- a/Jenkinsfile +++ /dev/null @@ -1,44 +0,0 @@ -node { - def cmakeTool - stage('Preparation') { - // Get some code from a GitHub repository - checkout scm - - cmakeTool = tool name: 'InSearchPath', type: 'hudson.plugins.cmake.CmakeTool' - - sh "rm -rf build" - sh "mkdir -p build" - } - stage('Configure') { - dir("build") { - sh "${cmakeTool} .." - } - - } - - stage('Build') { - dir("build") { - sh "make storm" - } - - } - - stage('Build Tests') { - dir("build") { - sh "make -j 4 tests" - } - - } - - stage('Test') { - dir("build") { - sh "make check-verbose" - } - } - - stage('Archive') { - archiveArtifacts artifacts: 'build/bin/*', onlyIfSuccessful: true - archiveArtifacts artifacts: 'build/lib/*', onlyIfSuccessful: true - archiveArtifacts artifacts: 'build/include/*', onlyIfSuccessful: true - } -} diff --git a/doc/build.md b/doc/build.md deleted file mode 100644 index 725794034..000000000 --- a/doc/build.md +++ /dev/null @@ -1,37 +0,0 @@ -# Building Storm - - -## Requirements -CMake >= 3.2 - CMake is required as it is used to generate the Makefiles or Projects/Solutions required to build StoRM. - -### Compiler: - A C++11 compliant compiler is required to build StoRM. It is tested and known to work with the following compilers: - - GCC 5.3 - - Clang 3.5.0 - - Other versions or compilers might work, but are not tested. - - The following Compilers are known NOT to work: - - Microsoft Visual Studio versions older than 2013, - - GCC versions 4.9.1 and older. - -Prerequisites: - Boost >= 1.61 - Build using the Boost Build system, for x64 use "bjam address-model=64" or "bjam.exe address-model=64 --build-type=complete" - -## Instructions - -### General - -```bash -mkdir build -cd build -``` - -It is recommended to make an out-of-source build, meaning that the folder in which CMake generates its Cache, Makefiles and output files should not be the Project Root nor its Source Directory. -A typical build layout is to create a folder "build" in the project root alongside the CMakeLists.txt file, change into this folder and execute "cmake .." as this will leave all source files untouched -and makes cleaning up the build tree very easy. -There are several options available for the CMake Script as to control behaviour and included components. -If no error occured during the last CMake Configure round, press Generate. -Now you can build StoRM using the generated project/makefiles in the Build folder you selected. \ No newline at end of file diff --git a/doc/dependencies.md b/doc/dependencies.md deleted file mode 100644 index 34660fdc0..000000000 --- a/doc/dependencies.md +++ /dev/null @@ -1,37 +0,0 @@ -# Dependencies - -## Included Dependencies: -- Carl 1.0 -- CUDD 3.0.0 - CUDD is included in the StoRM Sources under /resources/3rdparty/cudd-2.5.0 and builds automatically alongside StoRM. - Its Sourced where heavily modified as to incorporate newer Versions of Boost, changes in C++ (TR1 to C++11) and - to remove components only available under UNIX. -- Eigen 3.3 beta1 - Eigen is included in the StoRM Sources under /resources/3rdparty/eigen and builds automatically alongside StoRM. - - -- GTest 1.7.0 - GTest is included in the StoRM Sources under /resources/3rdparty/gtest-1.7.0 and builds automatically alongside StoRM -- GMM >= 4.2 - GMM is included in the StoRM Sources under /resources/3rdparty/gmm-4.2 and builds automatically alongside StoRM. - -## Optional: -- Gurobi >= 5.6.2 - Specify the path to the gurobi root dir using -DGUROBI_ROOT=/your/path/to/gurobi -- Z3 >= 4.3.2 - Specify the path to the z3 root dir using -DZ3_ROOT=/your/path/to/z3 -- MathSAT >= 5.2.11 - Specify the path to the mathsat root dir using -DMSAT_ROOT=/your/path/to/mathsat -- MPIR >= 2.7.0 - MSVC only and only if linked with MathSAT - Specify the path to the gmp-include directory -DGMP_INCLUDE_DIR=/your/path/to/mathsat - Specify the path to the mpir.lib directory -DGMP_MPIR_LIBRARY=/your/path/to/mpir.lib - Specify the path to the mpirxx.lib directory -DGMP_MPIRXX_LIBRARY=/your/path/to/mpirxx.lib -- GMP - clang and gcc only -- CUDA Toolkit >= 6.5 - Specify the path to the cuda toolkit root dir using -DCUDA_ROOT=/your/path/to/cuda -- CUSP >= 0.4.0 - Only of built with CUDA Toolkit - CUSP is included in the StoRM Sources as a git-submodule unter /resources/3rdparty/cusplibrary - diff --git a/doc/getting-started.md b/doc/getting-started.md deleted file mode 100644 index 8b1378917..000000000 --- a/doc/getting-started.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/install.sh b/install.sh deleted file mode 100755 index 384bbb2e9..000000000 --- a/install.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -pip install -ve stormpy diff --git a/resources/3rdparty/CMakeLists.txt b/resources/3rdparty/CMakeLists.txt index 34e38bbf7..cb7e064ec 100644 --- a/resources/3rdparty/CMakeLists.txt +++ b/resources/3rdparty/CMakeLists.txt @@ -227,6 +227,10 @@ if(carl_FOUND AND NOT STORM_FORCE_SHIPPED_CARL) else() message(SEND_ERROR "File ${carlLOCATION} does not exist, did you build carl?") endif() + if("${carl_MINORYEARVERSION}" STREQUAL "" OR "${carl_MINORMONTHVERSION}" STREQUAL "" OR "${carl_MAINTENANCEVERSION}" STREQUAL "") + # don't have detailed version information, probably an old version of carl + message(FATAL_ERROR "Carl at ${carlLOCATION} outdated, require ${CARL_MINYEAR}.${CARL_MINMONTH}.${CARL_MINPATCH}, have ${carl_VERSION}") + endif() if(${carl_MINORYEARVERSION} LESS ${CARL_MINYEAR}) message(SEND_ERROR "Carl outdated, require ${CARL_MINYEAR}.${CARL_MINMONTH}.${CARL_MINPATCH}, have ${carl_VERSION}") elseif(${carl_MINORYEARVERSION} EQUAL ${CARL_MINYEAR}) @@ -391,9 +395,9 @@ endif() set(STORM_HAVE_MSAT ${ENABLE_MSAT}) if (ENABLE_MSAT) message (STATUS "Storm - Linking with MathSAT.") - link_directories("${MSAT_ROOT}/lib") - include_directories("${MSAT_ROOT}/include") - list(APPEND STORM_LINK_LIBRARIES "mathsat") + find_library(MSAT_LIB mathsat PATHS "${MSAT_ROOT}/lib") + add_imported_library(msat SHARED ${MSAT_LIB} "${MSAT_ROOT}/include") + list(APPEND STORM_DEP_TARGETS msat_SHARED) endif(ENABLE_MSAT) ############################################################# diff --git a/resources/3rdparty/sylvan/src/lace.c b/resources/3rdparty/sylvan/src/lace.c index 08e9f8843..64443e23b 100755 --- a/resources/3rdparty/sylvan/src/lace.c +++ b/resources/3rdparty/sylvan/src/lace.c @@ -1088,6 +1088,12 @@ void lace_exit() lace_resume(); lace_barrier(); + // Free the memory of the workers. + for (unsigned int i=0; i<n_workers; i++) munmap(workers_memory[i], workers_memory_size); + free(workers_memory); + free(workers); + free(workers_p); + // finally, destroy the barriers lace_barrier_destroy(); pthread_barrier_destroy(&suspend_barrier); diff --git a/src/storm-cli-utilities/cli.cpp b/src/storm-cli-utilities/cli.cpp index cb33c3205..3ff4d4593 100644 --- a/src/storm-cli-utilities/cli.cpp +++ b/src/storm-cli-utilities/cli.cpp @@ -64,7 +64,7 @@ namespace storm { } - void printHeader(std::string const& name, const int argc, const char* argv[]) { + void printHeader(std::string const& name, const int argc, const char** argv) { STORM_PRINT(name << " " << storm::utility::StormVersion::shortVersionString() << std::endl << std::endl); // "Compute" the command line argument string with which storm was invoked. diff --git a/src/storm-cli-utilities/cli.h b/src/storm-cli-utilities/cli.h index 4decbb02c..e8d8601e9 100644 --- a/src/storm-cli-utilities/cli.h +++ b/src/storm-cli-utilities/cli.h @@ -11,7 +11,7 @@ namespace storm { */ int64_t process(const int argc, const char** argv); - void printHeader(std::string const& name, const int argc, const char* argv[]); + void printHeader(std::string const& name, const int argc, const char** argv); void printVersion(std::string const& name); @@ -27,7 +27,8 @@ namespace storm { bool parseOptions(const int argc, const char* argv[]); void processOptions(); - + + void setUrgentOptions(); } } diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h index f702667b7..d91d84479 100644 --- a/src/storm-cli-utilities/model-handling.h +++ b/src/storm-cli-utilities/model-handling.h @@ -232,7 +232,9 @@ namespace storm { std::shared_ptr<storm::models::sparse::Model<ValueType>> result = model; model->close(); if (model->hasOnlyTrivialNondeterminism()) { - result = model->convertToCTMC(); + STORM_LOG_WARN_COND(false, "Non-deterministic choices in MA seem to be unnecessary. Consider using a CTMC instead."); + // Activate again if transformation is correct + //result = model->convertToCTMC(); } return result; } diff --git a/src/storm-dft-cli/CMakeLists.txt b/src/storm-dft-cli/CMakeLists.txt index b8cdbbd27..4780eef4d 100644 --- a/src/storm-dft-cli/CMakeLists.txt +++ b/src/storm-dft-cli/CMakeLists.txt @@ -1,9 +1,9 @@ # Create storm-dft. -add_executable(storm-dft-cli ${PROJECT_SOURCE_DIR}/src/storm-dft-cli/storm-dyftee.cpp) +add_executable(storm-dft-cli ${PROJECT_SOURCE_DIR}/src/storm-dft-cli/storm-dft.cpp) target_link_libraries(storm-dft-cli storm-dft storm-cli-utilities) # Adding headers for xcode set_target_properties(storm-dft-cli PROPERTIES OUTPUT_NAME "storm-dft") add_dependencies(binaries storm-dft-cli) # installation -install(TARGETS storm-dft-cli RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) \ No newline at end of file +install(TARGETS storm-dft-cli RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-dft-cli/storm-dyftee.cpp b/src/storm-dft-cli/storm-dft.cpp similarity index 64% rename from src/storm-dft-cli/storm-dyftee.cpp rename to src/storm-dft-cli/storm-dft.cpp index 8eef6f0b5..3e69fb217 100644 --- a/src/storm-dft-cli/storm-dyftee.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -1,20 +1,14 @@ -#include "storm/logic/Formula.h" -#include "storm/utility/initialize.h" -#include "storm/api/storm.h" -#include "storm-cli-utilities/cli.h" -#include "storm/exceptions/BaseException.h" - -#include "storm/logic/Formula.h" +#include "storm-dft/settings/DftSettings.h" +#include "storm-dft/settings/modules/DftIOSettings.h" +#include "storm-dft/settings/modules/FaultTreeSettings.h" #include "storm/settings/modules/IOSettings.h" -#include "storm/settings/modules/CoreSettings.h" -#include "storm/settings/modules/DebugSettings.h" -#include "storm/settings/modules/GmmxxEquationSolverSettings.h" -#include "storm/settings/modules/MinMaxEquationSolverSettings.h" -#include "storm/settings/modules/NativeEquationSolverSettings.h" -#include "storm/settings/modules/EliminationSettings.h" #include "storm/settings/modules/ResourceSettings.h" +#include "storm/utility/initialize.h" +#include "storm/api/storm.h" +#include "storm-cli-utilities/cli.h" + #include "storm-dft/parser/DFTGalileoParser.h" #include "storm-dft/parser/DFTJsonParser.h" #include "storm-dft/modelchecker/dft/DFTModelChecker.h" @@ -22,13 +16,8 @@ #include "storm-dft/transformations/DftToGspnTransformator.h" #include "storm-dft/storage/dft/DftJsonExporter.h" -#include "storm-dft/settings/modules/DFTSettings.h" - #include "storm-gspn/storage/gspn/GSPN.h" #include "storm-gspn/storm-gspn.h" -#include "storm/settings/modules/GSPNSettings.h" -#include "storm/settings/modules/GSPNExportSettings.h" - #include <boost/lexical_cast.hpp> #include <memory> @@ -45,18 +34,18 @@ */ template <typename ValueType> void analyzeDFT(std::vector<std::string> const& properties, bool symred, bool allowModularisation, bool enableDC, double approximationError) { - storm::settings::modules::DFTSettings const& dftSettings = storm::settings::getModule<storm::settings::modules::DFTSettings>(); + storm::settings::modules::DftIOSettings const& dftIOSettings = storm::settings::getModule<storm::settings::modules::DftIOSettings>(); std::shared_ptr<storm::storage::DFT<ValueType>> dft; // Build DFT from given file. - if (dftSettings.isDftJsonFileSet()) { + if (dftIOSettings.isDftJsonFileSet()) { storm::parser::DFTJsonParser<ValueType> parser; - std::cout << "Loading DFT from file " << dftSettings.getDftJsonFilename() << std::endl; - dft = std::make_shared<storm::storage::DFT<ValueType>>(parser.parseJson(dftSettings.getDftJsonFilename())); + std::cout << "Loading DFT from file " << dftIOSettings.getDftJsonFilename() << std::endl; + dft = std::make_shared<storm::storage::DFT<ValueType>>(parser.parseJson(dftIOSettings.getDftJsonFilename())); } else { storm::parser::DFTGalileoParser<ValueType> parser; - std::cout << "Loading DFT from file " << dftSettings.getDftFilename() << std::endl; - dft = std::make_shared<storm::storage::DFT<ValueType>>(parser.parseDFT(dftSettings.getDftFilename())); + std::cout << "Loading DFT from file " << dftIOSettings.getDftFilename() << std::endl; + dft = std::make_shared<storm::storage::DFT<ValueType>>(parser.parseDFT(dftIOSettings.getDftFilename())); } // Build properties @@ -92,83 +81,39 @@ void analyzeWithSMT(std::string filename) { //std::cout << "SMT result: " << sat << std::endl; } +void processOptions() { + // Start by setting some urgent options (log levels, resources, etc.) + storm::cli::setUrgentOptions(); -/*! - * Initialize the settings manager. - */ -void initializeSettings() { - storm::settings::mutableManager().setName("Storm-DyFTeE", "storm-dft"); - - // Register all known settings modules. - storm::settings::addModule<storm::settings::modules::GeneralSettings>(); - storm::settings::addModule<storm::settings::modules::DFTSettings>(); - storm::settings::addModule<storm::settings::modules::CoreSettings>(); - storm::settings::addModule<storm::settings::modules::DebugSettings>(); - storm::settings::addModule<storm::settings::modules::IOSettings>(); - //storm::settings::addModule<storm::settings::modules::CounterexampleGeneratorSettings>(); - //storm::settings::addModule<storm::settings::modules::CuddSettings>(); - //storm::settings::addModule<storm::settings::modules::SylvanSettings>(); - storm::settings::addModule<storm::settings::modules::GmmxxEquationSolverSettings>(); - storm::settings::addModule<storm::settings::modules::MinMaxEquationSolverSettings>(); - storm::settings::addModule<storm::settings::modules::NativeEquationSolverSettings>(); - //storm::settings::addModule<storm::settings::modules::BisimulationSettings>(); - //storm::settings::addModule<storm::settings::modules::GlpkSettings>(); - //storm::settings::addModule<storm::settings::modules::GurobiSettings>(); - //storm::settings::addModule<storm::settings::modules::TopologicalValueIterationEquationSolverSettings>(); - //storm::settings::addModule<storm::settings::modules::ParametricSettings>(); - storm::settings::addModule<storm::settings::modules::EliminationSettings>(); - storm::settings::addModule<storm::settings::modules::ResourceSettings>(); - - // For translation into JANI via GSPN. - storm::settings::addModule<storm::settings::modules::GSPNSettings>(); - storm::settings::addModule<storm::settings::modules::GSPNExportSettings>(); - storm::settings::addModule<storm::settings::modules::JaniExportSettings>(); -} - -/*! - * Entry point for the DyFTeE backend. - * - * @param argc The argc argument of main(). - * @param argv The argv argument of main(). - * @return Return code, 0 if successfull, not 0 otherwise. - */ -int main(const int argc, const char** argv) { - try { - storm::utility::setUp(); - storm::cli::printHeader("Storm-DyFTeE", argc, argv); - initializeSettings(); + storm::cli::processOptions(); - bool optionsCorrect = storm::cli::parseOptions(argc, argv); - if (!optionsCorrect) { - return -1; - } - - storm::settings::modules::DFTSettings const& dftSettings = storm::settings::getModule<storm::settings::modules::DFTSettings>(); + storm::settings::modules::DftIOSettings const& dftIOSettings = storm::settings::getModule<storm::settings::modules::DftIOSettings>(); + storm::settings::modules::FaultTreeSettings const& faultTreeSettings = storm::settings::getModule<storm::settings::modules::FaultTreeSettings>(); storm::settings::modules::GeneralSettings const& generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>(); storm::settings::modules::IOSettings const& ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>(); - if (!dftSettings.isDftFileSet() && !dftSettings.isDftJsonFileSet()) { + if (!dftIOSettings.isDftFileSet() && !dftIOSettings.isDftJsonFileSet()) { STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "No input model."); } - if (dftSettings.isExportToJson()) { - STORM_LOG_THROW(dftSettings.isDftFileSet(), storm::exceptions::InvalidSettingsException, "No input model in Galileo format given."); + if (dftIOSettings.isExportToJson()) { + STORM_LOG_THROW(dftIOSettings.isDftFileSet(), storm::exceptions::InvalidSettingsException, "No input model in Galileo format given."); storm::parser::DFTGalileoParser<double> parser; - storm::storage::DFT<double> dft = parser.parseDFT(dftSettings.getDftFilename()); + storm::storage::DFT<double> dft = parser.parseDFT(dftIOSettings.getDftFilename()); // Export to json - storm::storage::DftJsonExporter<double>::toFile(dft, dftSettings.getExportJsonFilename()); + storm::storage::DftJsonExporter<double>::toFile(dft, dftIOSettings.getExportJsonFilename()); storm::utility::cleanUp(); - return 0; + return; } - if (dftSettings.isTransformToGspn()) { + if (dftIOSettings.isTransformToGspn()) { std::shared_ptr<storm::storage::DFT<double>> dft; - if (dftSettings.isDftJsonFileSet()) { + if (dftIOSettings.isDftJsonFileSet()) { storm::parser::DFTJsonParser<double> parser; - dft = std::make_shared<storm::storage::DFT<double>>(parser.parseJson(dftSettings.getDftJsonFilename())); + dft = std::make_shared<storm::storage::DFT<double>>(parser.parseJson(dftIOSettings.getDftJsonFilename())); } else { storm::parser::DFTGalileoParser<double> parser(true, false); - dft = std::make_shared<storm::storage::DFT<double>>(parser.parseDFT(dftSettings.getDftFilename())); + dft = std::make_shared<storm::storage::DFT<double>>(parser.parseDFT(dftIOSettings.getDftFilename())); } storm::transformations::dft::DftToGspnTransformator<double> gspnTransformator(*dft); gspnTransformator.transform(); @@ -199,7 +144,7 @@ int main(const int argc, const char** argv) { delete model; delete gspn; storm::utility::cleanUp(); - return 0; + return; } bool parametric = false; @@ -208,22 +153,22 @@ int main(const int argc, const char** argv) { #endif #ifdef STORM_HAVE_Z3 - if (dftSettings.solveWithSMT()) { + if (faultTreeSettings.solveWithSMT()) { // Solve with SMT if (parametric) { // analyzeWithSMT<storm::RationalFunction>(dftSettings.getDftFilename()); } else { - analyzeWithSMT<double>(dftSettings.getDftFilename()); + analyzeWithSMT<double>(dftIOSettings.getDftFilename()); } storm::utility::cleanUp(); - return 0; + return; } #endif // Set min or max std::string optimizationDirection = "min"; - if (dftSettings.isComputeMaximalValue()) { - STORM_LOG_THROW(!dftSettings.isComputeMinimalValue(), storm::exceptions::InvalidSettingsException, "Cannot compute minimal and maximal values at the same time."); + if (dftIOSettings.isComputeMaximalValue()) { + STORM_LOG_THROW(!dftIOSettings.isComputeMinimalValue(), storm::exceptions::InvalidSettingsException, "Cannot compute minimal and maximal values at the same time."); optimizationDirection = "max"; } @@ -233,19 +178,19 @@ int main(const int argc, const char** argv) { if (ioSettings.isPropertySet()) { properties.push_back(ioSettings.getProperty()); } - if (dftSettings.usePropExpectedTime()) { + if (dftIOSettings.usePropExpectedTime()) { properties.push_back("T" + optimizationDirection + "=? [F \"failed\"]"); } - if (dftSettings.usePropProbability()) { + if (dftIOSettings.usePropProbability()) { properties.push_back("P" + optimizationDirection + "=? [F \"failed\"]"); } - if (dftSettings.usePropTimebound()) { + if (dftIOSettings.usePropTimebound()) { std::stringstream stream; - stream << "P" << optimizationDirection << "=? [F<=" << dftSettings.getPropTimebound() << " \"failed\"]"; + stream << "P" << optimizationDirection << "=? [F<=" << dftIOSettings.getPropTimebound() << " \"failed\"]"; properties.push_back(stream.str()); } - if (dftSettings.usePropTimepoints()) { - for (double timepoint : dftSettings.getPropTimepoints()) { + if (dftIOSettings.usePropTimepoints()) { + for (double timepoint : dftIOSettings.getPropTimepoints()) { std::stringstream stream; stream << "P" << optimizationDirection << "=? [F<=" << timepoint << " \"failed\"]"; properties.push_back(stream.str()); @@ -258,21 +203,51 @@ int main(const int argc, const char** argv) { // Set possible approximation error double approximationError = 0.0; - if (dftSettings.isApproximationErrorSet()) { - approximationError = dftSettings.getApproximationError(); + if (faultTreeSettings.isApproximationErrorSet()) { + approximationError = faultTreeSettings.getApproximationError(); } // From this point on we are ready to carry out the actual computations. if (parametric) { #ifdef STORM_HAVE_CARL - analyzeDFT<storm::RationalFunction>(properties, dftSettings.useSymmetryReduction(), dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError); + analyzeDFT<storm::RationalFunction>(properties, faultTreeSettings.useSymmetryReduction(), faultTreeSettings.useModularisation(), !faultTreeSettings.isDisableDC(), approximationError); #else STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameters are not supported in this build."); #endif } else { - analyzeDFT<double>(properties, dftSettings.useSymmetryReduction(), dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError); + analyzeDFT<double>(properties, faultTreeSettings.useSymmetryReduction(), faultTreeSettings.useModularisation(), !faultTreeSettings.isDisableDC(), approximationError); } - +} + +/*! + * Entry point for the DyFTeE backend. + * + * @param argc The argc argument of main(). + * @param argv The argv argument of main(). + * @return Return code, 0 if successfull, not 0 otherwise. + */ +/*! + * Main entry point of the executable storm-pars. + */ +int main(const int argc, const char** argv) { + try { + storm::utility::setUp(); + storm::cli::printHeader("Storm-dft", argc, argv); + storm::settings::initializeDftSettings("Storm-dft", "storm-dft"); + + storm::utility::Stopwatch totalTimer(true); + if (!storm::cli::parseOptions(argc, argv)) { + return -1; + } + + processOptions(); + //storm::pars::processOptions(); + + totalTimer.stop(); + if (storm::settings::getModule<storm::settings::modules::ResourceSettings>().isPrintTimeAndMemorySet()) { + storm::cli::printTimeAndMemoryStatistics(totalTimer.getTimeInMilliseconds()); + } + // All operations have now been performed, so we clean up everything and terminate. storm::utility::cleanUp(); return 0; diff --git a/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp b/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp index 4a29b0652..2b74432ad 100644 --- a/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp +++ b/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp @@ -6,456 +6,824 @@ #include "storm/models/sparse/Ctmc.h" #include "storm/utility/constants.h" #include "storm/utility/vector.h" +#include "storm/utility/bitoperations.h" #include "storm/exceptions/UnexpectedException.h" #include "storm/settings/SettingsManager.h" - -#include "storm-dft/settings/modules/DFTSettings.h" +#include "storm/logic/AtomicLabelFormula.h" +#include "storm-dft/settings/modules/FaultTreeSettings.h" namespace storm { namespace builder { - template <typename ValueType> - ExplicitDFTModelBuilder<ValueType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), markovianStates(), exitRates(), choiceLabeling() { + template<typename ValueType, typename StateType> + ExplicitDFTModelBuilder<ValueType, StateType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), markovianStates(), exitRates(), choiceLabeling() { // Intentionally left empty. } - - template <typename ValueType> - ExplicitDFTModelBuilder<ValueType>::ExplicitDFTModelBuilder(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : mDft(dft), mStates(((mDft.stateVectorSize() / 64) + 1) * 64, INITIAL_BUCKETSIZE), enableDC(enableDC) { - // stateVectorSize is bound for size of bitvector - mStateGenerationInfo = std::make_shared<storm::storage::DFTStateGenerationInfo>(mDft.buildStateGenerationInfo(symmetries)); + template<typename ValueType, typename StateType> + ExplicitDFTModelBuilder<ValueType, StateType>::MatrixBuilder::MatrixBuilder(bool canHaveNondeterminism) : mappingOffset(0), stateRemapping(), currentRowGroup(0), currentRow(0), canHaveNondeterminism((canHaveNondeterminism)) { + // Create matrix builder + builder = storm::storage::SparseMatrixBuilder<ValueType>(0, 0, 0, false, canHaveNondeterminism, 0); } + template<typename ValueType, typename StateType> + ExplicitDFTModelBuilder<ValueType, StateType>::LabelOptions::LabelOptions(std::vector<std::shared_ptr<const storm::logic::Formula>> properties, bool buildAllLabels) : buildFailLabel(true), buildFailSafeLabel(false), buildAllLabels(buildAllLabels) { + // Get necessary labels from properties + std::vector<std::shared_ptr<storm::logic::AtomicLabelFormula const>> atomicLabels; + for (auto property : properties) { + property->gatherAtomicLabelFormulas(atomicLabels); + } + // Set usage of necessary labels + for (auto atomic : atomicLabels) { + std::string label = atomic->getLabel(); + std::size_t foundIndex = label.find("_fail"); + if (foundIndex != std::string::npos) { + elementLabels.insert(label.substr(0, foundIndex)); + } else if (label.compare("failed") == 0) { + buildFailLabel = true; + } else if (label.compare("failsafe") == 0) { + buildFailSafeLabel = true; + } else { + STORM_LOG_WARN("Label '" << label << "' not known."); + } + } + } + + template<typename ValueType, typename StateType> + ExplicitDFTModelBuilder<ValueType, StateType>::ExplicitDFTModelBuilder(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : + dft(dft), + stateGenerationInfo(std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))), + enableDC(enableDC), + usedHeuristic(storm::settings::getModule<storm::settings::modules::FaultTreeSettings>().getApproximationHeuristic()), + generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), + matrixBuilder(!generator.isDeterministicModel()), + stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), + // TODO Matthias: make choosable + //explorationQueue(dft.nrElements()+1, 0, 1) + explorationQueue(200, 0, 0.9) + { + // Intentionally left empty. + // TODO Matthias: remove again + usedHeuristic = storm::builder::ApproximationHeuristic::DEPTH; + + // Compute independent subtrees + if (dft.topLevelType() == storm::storage::DFTElementType::OR) { + // We only support this for approximation with top level element OR + for (auto const& child : dft.getGate(dft.getTopLevelIndex())->children()) { + // Consider all children of the top level gate + std::vector<size_t> isubdft; + if (child->nrParents() > 1 || child->hasOutgoingDependencies()) { + STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation."); + isubdft.clear(); + } else if (dft.isGate(child->id())) { + isubdft = dft.getGate(child->id())->independentSubDft(false); + } else { + STORM_LOG_ASSERT(dft.isBasicElement(child->id()), "Child is no BE."); + if(dft.getBasicElement(child->id())->hasIngoingDependencies()) { + STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation."); + isubdft.clear(); + } else { + isubdft = {child->id()}; + } + } + if(isubdft.empty()) { + subtreeBEs.clear(); + break; + } else { + // Add new independent subtree + std::vector<size_t> subtree; + for (size_t id : isubdft) { + if (dft.isBasicElement(id)) { + subtree.push_back(id); + } + } + subtreeBEs.push_back(subtree); + } + } + } + if (subtreeBEs.empty()) { + // Consider complete tree + std::vector<size_t> subtree; + for (size_t id = 0; id < dft.nrElements(); ++id) { + if (dft.isBasicElement(id)) { + subtree.push_back(id); + } + } + subtreeBEs.push_back(subtree); + } - template <typename ValueType> - std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilder<ValueType>::buildModel(LabelOptions const& labelOpts) { - // Initialize - bool deterministicModel = false; - size_t rowOffset = 0; - ModelComponents modelComponents; - std::vector<uint_fast64_t> tmpMarkovianStates; - storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !deterministicModel, 0); - - if(mergeFailedStates) { - // Introduce explicit fail state - failedIndex = newIndex; - newIndex++; - transitionMatrixBuilder.newRowGroup(failedIndex); - transitionMatrixBuilder.addNextValue(failedIndex, failedIndex, storm::utility::one<ValueType>()); - STORM_LOG_TRACE("Introduce fail state with id: " << failedIndex); - modelComponents.exitRates.push_back(storm::utility::one<ValueType>()); - tmpMarkovianStates.push_back(failedIndex); - } - - // Explore state space - DFTStatePointer state = std::make_shared<storm::storage::DFTState<ValueType>>(mDft, *mStateGenerationInfo, newIndex); - auto exploreResult = exploreStates(state, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); - initialStateIndex = exploreResult.first; - bool deterministic = exploreResult.second; - - // Before ending the exploration check for pseudo states which are not initialized yet - for (auto & pseudoStatePair : mPseudoStatesMapping) { - if (pseudoStatePair.first == 0) { - // Create state from pseudo state and explore - STORM_LOG_ASSERT(mStates.contains(pseudoStatePair.second), "Pseudo state not contained."); - STORM_LOG_ASSERT(mStates.getValue(pseudoStatePair.second) >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); - STORM_LOG_TRACE("Create pseudo state from bit vector " << pseudoStatePair.second); - DFTStatePointer pseudoState = std::make_shared<storm::storage::DFTState<ValueType>>(pseudoStatePair.second, mDft, *mStateGenerationInfo, newIndex); - pseudoState->construct(); - STORM_LOG_ASSERT(pseudoStatePair.second == pseudoState->status(), "Pseudo states do not coincide."); - STORM_LOG_TRACE("Explore pseudo state " << mDft.getStateString(pseudoState) << " with id " << pseudoState->getId()); - auto exploreResult = exploreStates(pseudoState, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); - deterministic &= exploreResult.second; - STORM_LOG_ASSERT(pseudoStatePair.first == pseudoState->getId(), "Pseudo state ids do not coincide"); - STORM_LOG_ASSERT(pseudoState->getId() == exploreResult.first, "Pseudo state ids do not coincide."); + for (auto tree : subtreeBEs) { + std::stringstream stream; + stream << "Subtree: "; + for (size_t id : tree) { + stream << id << ", "; } + STORM_LOG_TRACE(stream.str()); } - - // Replace pseudo states in matrix - std::vector<uint_fast64_t> pseudoStatesVector; - for (auto const& pseudoStatePair : mPseudoStatesMapping) { - pseudoStatesVector.push_back(pseudoStatePair.first); - } - STORM_LOG_ASSERT(std::find(pseudoStatesVector.begin(), pseudoStatesVector.end(), 0) == pseudoStatesVector.end(), "Unexplored pseudo state still contained."); - transitionMatrixBuilder.replaceColumns(pseudoStatesVector, OFFSET_PSEUDO_STATE); - - STORM_LOG_DEBUG("Generated " << mStates.size() + (mergeFailedStates ? 1 : 0) << " states"); - STORM_LOG_DEBUG("Model is " << (deterministic ? "deterministic" : "non-deterministic")); - - size_t stateSize = mStates.size() + (mergeFailedStates ? 1 : 0); - // Build Markov Automaton - modelComponents.markovianStates = storm::storage::BitVector(stateSize, tmpMarkovianStates); + } + + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilder<ValueType, StateType>::buildModel(LabelOptions const& labelOpts, size_t iteration, double approximationThreshold) { + STORM_LOG_TRACE("Generating DFT state space"); + + if (iteration < 1) { + // Initialize + modelComponents.markovianStates = storm::storage::BitVector(INITIAL_BITVECTOR_SIZE); + + if(mergeFailedStates) { + // Introduce explicit fail state + storm::generator::StateBehavior<ValueType, StateType> behavior = generator.createMergeFailedState([this] (DFTStatePointer const& state) { + this->failedStateId = newIndex++; + matrixBuilder.stateRemapping.push_back(0); + return this->failedStateId; + } ); + + matrixBuilder.setRemapping(failedStateId); + STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); + matrixBuilder.newRowGroup(); + setMarkovian(behavior.begin()->isMarkovian()); + + // Now add self loop. + // TODO Matthias: maybe use general method. + STORM_LOG_ASSERT(behavior.getNumberOfChoices() == 1, "Wrong number of choices for failed state."); + STORM_LOG_ASSERT(behavior.begin()->size() == 1, "Wrong number of transitions for failed state."); + std::pair<StateType, ValueType> stateProbabilityPair = *(behavior.begin()->begin()); + STORM_LOG_ASSERT(stateProbabilityPair.first == failedStateId, "No self loop for failed state."); + STORM_LOG_ASSERT(storm::utility::isOne<ValueType>(stateProbabilityPair.second), "Probability for failed state != 1."); + matrixBuilder.addTransition(stateProbabilityPair.first, stateProbabilityPair.second); + matrixBuilder.finishRow(); + } + + // Build initial state + this->stateStorage.initialStateIndices = generator.getInitialStates(std::bind(&ExplicitDFTModelBuilder::getOrAddStateIndex, this, std::placeholders::_1)); + STORM_LOG_ASSERT(stateStorage.initialStateIndices.size() == 1, "Only one initial state assumed."); + initialStateIndex = stateStorage.initialStateIndices[0]; + STORM_LOG_TRACE("Initial state: " << initialStateIndex); + // Initialize heuristic values for inital state + STORM_LOG_ASSERT(!statesNotExplored.at(initialStateIndex).second, "Heuristic for initial state is already initialized"); + ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(initialStateIndex); + heuristic->markExpand(); + statesNotExplored[initialStateIndex].second = heuristic; + explorationQueue.push(heuristic); + } else { + initializeNextIteration(); + } + + if (approximationThreshold > 0) { + switch (usedHeuristic) { + case storm::builder::ApproximationHeuristic::NONE: + // Do not change anything + approximationThreshold = dft.nrElements()+10; + break; + case storm::builder::ApproximationHeuristic::DEPTH: + approximationThreshold = iteration + 1; + break; + case storm::builder::ApproximationHeuristic::PROBABILITY: + case storm::builder::ApproximationHeuristic::BOUNDDIFFERENCE: + approximationThreshold = 10 * std::pow(2, iteration); + break; + } + } + exploreStateSpace(approximationThreshold); + + size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); + modelComponents.markovianStates.resize(stateSize); + modelComponents.deterministicModel = generator.isDeterministicModel(); + + // Fix the entries in the transition matrix according to the mapping of ids to row group indices + STORM_LOG_ASSERT(matrixBuilder.getRemapping(initialStateIndex) == initialStateIndex, "Initial state should not be remapped."); + // TODO Matthias: do not consider all rows? + STORM_LOG_TRACE("Remap matrix: " << matrixBuilder.stateRemapping << ", offset: " << matrixBuilder.mappingOffset); + matrixBuilder.remap(); + + STORM_LOG_TRACE("State remapping: " << matrixBuilder.stateRemapping); + STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); + STORM_LOG_DEBUG("Model has " << stateSize << " states"); + STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic")); + // Build transition matrix - modelComponents.transitionMatrix = transitionMatrixBuilder.build(stateSize, stateSize); + modelComponents.transitionMatrix = matrixBuilder.builder.build(stateSize, stateSize); if (stateSize <= 15) { STORM_LOG_TRACE("Transition matrix: " << std::endl << modelComponents.transitionMatrix); } else { STORM_LOG_TRACE("Transition matrix: too big to print"); } - STORM_LOG_TRACE("Exit rates: " << storm::utility::vector::toString<ValueType>(modelComponents.exitRates)); - STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); - + + buildLabeling(labelOpts); + } + + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilder<ValueType, StateType>::initializeNextIteration() { + STORM_LOG_TRACE("Refining DFT state space"); + + // TODO Matthias: should be easier now as skipped states all are at the end of the matrix + // Push skipped states to explore queue + // TODO Matthias: remove + for (auto const& skippedState : skippedStates) { + statesNotExplored[skippedState.second.first->getId()] = skippedState.second; + explorationQueue.push(skippedState.second.second); + } + + // Initialize matrix builder again + // TODO Matthias: avoid copy + std::vector<uint_fast64_t> copyRemapping = matrixBuilder.stateRemapping; + matrixBuilder = MatrixBuilder(!generator.isDeterministicModel()); + matrixBuilder.stateRemapping = copyRemapping; + StateType nrStates = modelComponents.transitionMatrix.getRowGroupCount(); + STORM_LOG_ASSERT(nrStates == matrixBuilder.stateRemapping.size(), "No. of states does not coincide with mapping size."); + + // Start by creating a remapping from the old indices to the new indices + std::vector<StateType> indexRemapping = std::vector<StateType>(nrStates, 0); + auto iterSkipped = skippedStates.begin(); + size_t skippedBefore = 0; + for (size_t i = 0; i < indexRemapping.size(); ++i) { + while (iterSkipped != skippedStates.end() && iterSkipped->first <= i) { + STORM_LOG_ASSERT(iterSkipped->first < indexRemapping.size(), "Skipped is too high."); + ++skippedBefore; + ++iterSkipped; + } + indexRemapping[i] = i - skippedBefore; + } + + // Set remapping + size_t nrExpandedStates = nrStates - skippedBefore; + matrixBuilder.mappingOffset = nrStates; + STORM_LOG_TRACE("# expanded states: " << nrExpandedStates); + StateType skippedIndex = nrExpandedStates; + std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> skippedStatesNew; + for (size_t id = 0; id < matrixBuilder.stateRemapping.size(); ++id) { + StateType index = matrixBuilder.getRemapping(id); + auto itFind = skippedStates.find(index); + if (itFind != skippedStates.end()) { + // Set new mapping for skipped state + matrixBuilder.stateRemapping[id] = skippedIndex; + skippedStatesNew[skippedIndex] = itFind->second; + indexRemapping[index] = skippedIndex; + ++skippedIndex; + } else { + // Set new mapping for expanded state + matrixBuilder.stateRemapping[id] = indexRemapping[index]; + } + } + STORM_LOG_TRACE("New state remapping: " << matrixBuilder.stateRemapping); + std::stringstream ss; + ss << "Index remapping:" << std::endl; + for (auto tmp : indexRemapping) { + ss << tmp << " "; + } + STORM_LOG_TRACE(ss.str()); + + // Remap markovian states + storm::storage::BitVector markovianStatesNew = storm::storage::BitVector(modelComponents.markovianStates.size(), true); + // Iterate over all not set bits + modelComponents.markovianStates.complement(); + size_t index = modelComponents.markovianStates.getNextSetIndex(0); + while (index < modelComponents.markovianStates.size()) { + markovianStatesNew.set(indexRemapping[index], false); + index = modelComponents.markovianStates.getNextSetIndex(index+1); + } + STORM_LOG_ASSERT(modelComponents.markovianStates.size() - modelComponents.markovianStates.getNumberOfSetBits() == markovianStatesNew.getNumberOfSetBits(), "Remapping of markovian states is wrong."); + STORM_LOG_ASSERT(markovianStatesNew.size() == nrStates, "No. of states does not coincide with markovian size."); + modelComponents.markovianStates = markovianStatesNew; + + // Build submatrix for expanded states + // TODO Matthias: only use row groups when necessary + for (StateType oldRowGroup = 0; oldRowGroup < modelComponents.transitionMatrix.getRowGroupCount(); ++oldRowGroup) { + if (indexRemapping[oldRowGroup] < nrExpandedStates) { + // State is expanded -> copy to new matrix + matrixBuilder.newRowGroup(); + for (StateType oldRow = modelComponents.transitionMatrix.getRowGroupIndices()[oldRowGroup]; oldRow < modelComponents.transitionMatrix.getRowGroupIndices()[oldRowGroup+1]; ++oldRow) { + for (typename storm::storage::SparseMatrix<ValueType>::const_iterator itEntry = modelComponents.transitionMatrix.begin(oldRow); itEntry != modelComponents.transitionMatrix.end(oldRow); ++itEntry) { + auto itFind = skippedStates.find(itEntry->getColumn()); + if (itFind != skippedStates.end()) { + // Set id for skipped states as we remap it later + matrixBuilder.addTransition(matrixBuilder.mappingOffset + itFind->second.first->getId(), itEntry->getValue()); + } else { + // Set newly remapped index for expanded states + matrixBuilder.addTransition(indexRemapping[itEntry->getColumn()], itEntry->getValue()); + } + } + matrixBuilder.finishRow(); + } + } + } + + skippedStates = skippedStatesNew; + + STORM_LOG_ASSERT(matrixBuilder.getCurrentRowGroup() == nrExpandedStates, "Row group size does not match."); + skippedStates.clear(); + } + + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilder<ValueType, StateType>::exploreStateSpace(double approximationThreshold) { + size_t nrExpandedStates = 0; + size_t nrSkippedStates = 0; + // TODO Matthias: do not empty queue every time but break before + while (!explorationQueue.empty()) { + // Get the first state in the queue + ExplorationHeuristicPointer currentExplorationHeuristic = explorationQueue.popTop(); + StateType currentId = currentExplorationHeuristic->getId(); + auto itFind = statesNotExplored.find(currentId); + STORM_LOG_ASSERT(itFind != statesNotExplored.end(), "Id " << currentId << " not found"); + DFTStatePointer currentState = itFind->second.first; + STORM_LOG_ASSERT(currentExplorationHeuristic == itFind->second.second, "Exploration heuristics do not match"); + STORM_LOG_ASSERT(currentState->getId() == currentId, "Ids do not match"); + // Remove it from the list of not explored states + statesNotExplored.erase(itFind); + STORM_LOG_ASSERT(stateStorage.stateToId.contains(currentState->status()), "State is not contained in state storage."); + STORM_LOG_ASSERT(stateStorage.stateToId.getValue(currentState->status()) == currentId, "Ids of states do not coincide."); + + // Get concrete state if necessary + if (currentState->isPseudoState()) { + // Create concrete state from pseudo state + currentState->construct(); + } + STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state."); + + // Remember that the current row group was actually filled with the transitions of a different state + matrixBuilder.setRemapping(currentId); + + matrixBuilder.newRowGroup(); + + // Try to explore the next state + generator.load(currentState); + + //if (approximationThreshold > 0.0 && nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) { + if (approximationThreshold > 0.0 && currentExplorationHeuristic->isSkip(approximationThreshold)) { + // Skip the current state + ++nrSkippedStates; + STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); + setMarkovian(true); + // Add transition to target state with temporary value 0 + // TODO Matthias: what to do when there is no unique target state? + matrixBuilder.addTransition(failedStateId, storm::utility::zero<ValueType>()); + // Remember skipped state + skippedStates[matrixBuilder.getCurrentRowGroup() - 1] = std::make_pair(currentState, currentExplorationHeuristic); + matrixBuilder.finishRow(); + } else { + // Explore the current state + ++nrExpandedStates; + storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(std::bind(&ExplicitDFTModelBuilder::getOrAddStateIndex, this, std::placeholders::_1)); + STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); + setMarkovian(behavior.begin()->isMarkovian()); + + // Now add all choices. + for (auto const& choice : behavior) { + // Add the probabilistic behavior to the matrix. + for (auto const& stateProbabilityPair : choice) { + STORM_LOG_ASSERT(!storm::utility::isZero(stateProbabilityPair.second), "Probability zero."); + // Set transition to state id + offset. This helps in only remapping all previously skipped states. + matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second); + // Set heuristic values for reached states + auto iter = statesNotExplored.find(stateProbabilityPair.first); + if (iter != statesNotExplored.end()) { + // Update heuristic values + DFTStatePointer state = iter->second.first; + if (!iter->second.second) { + // Initialize heuristic values + ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateProbabilityPair.first, *currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass()); + iter->second.second = heuristic; + if (state->hasFailed(dft.getTopLevelIndex()) || state->isFailsafe(dft.getTopLevelIndex()) || state->nrFailableDependencies() > 0 || (state->nrFailableDependencies() == 0 && state->nrFailableBEs() == 0)) { + // Do not skip absorbing state or if reached by dependencies + iter->second.second->markExpand(); + } + if (usedHeuristic == storm::builder::ApproximationHeuristic::BOUNDDIFFERENCE) { + // Compute bounds for heuristic now + if (state->isPseudoState()) { + // Create concrete state from pseudo state + state->construct(); + } + STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state."); + + // Initialize bounds + // TODO Mathias: avoid hack + ValueType lowerBound = getLowerBound(state); + ValueType upperBound = getUpperBound(state); + heuristic->setBounds(lowerBound, upperBound); + } + + explorationQueue.push(heuristic); + } else if (!iter->second.second->isExpand()) { + double oldPriority = iter->second.second->getPriority(); + if (iter->second.second->updateHeuristicValues(*currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass())) { + // Update priority queue + explorationQueue.update(iter->second.second, oldPriority); + } + } + } + } + matrixBuilder.finishRow(); + } + } + } // end exploration + + STORM_LOG_INFO("Expanded " << nrExpandedStates << " states"); + STORM_LOG_INFO("Skipped " << nrSkippedStates << " states"); + STORM_LOG_ASSERT(nrSkippedStates == skippedStates.size(), "Nr skipped states is wrong"); + } + + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilder<ValueType, StateType>::buildLabeling(LabelOptions const& labelOpts) { // Build state labeling - modelComponents.stateLabeling = storm::models::sparse::StateLabeling(mStates.size() + (mergeFailedStates ? 1 : 0)); - // Initial state is always first state without any failure + modelComponents.stateLabeling = storm::models::sparse::StateLabeling(modelComponents.transitionMatrix.getRowGroupCount()); + // Initial state modelComponents.stateLabeling.addLabel("init"); + STORM_LOG_ASSERT(matrixBuilder.getRemapping(initialStateIndex) == initialStateIndex, "Initial state should not be remapped."); modelComponents.stateLabeling.addLabelToState("init", initialStateIndex); // Label all states corresponding to their status (failed, failsafe, failed BE) if(labelOpts.buildFailLabel) { modelComponents.stateLabeling.addLabel("failed"); - } + } if(labelOpts.buildFailSafeLabel) { modelComponents.stateLabeling.addLabel("failsafe"); } - - // Collect labels for all BE - std::vector<std::shared_ptr<storage::DFTBE<ValueType>>> basicElements = mDft.getBasicElements(); - for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) { - if(labelOpts.beLabels.count(elem->name()) > 0) { - modelComponents.stateLabeling.addLabel(elem->name() + "_fail"); + + // Collect labels for all necessary elements + for (size_t id = 0; id < dft.nrElements(); ++id) { + std::shared_ptr<storage::DFTElement<ValueType> const> element = dft.getElement(id); + if (labelOpts.buildAllLabels || labelOpts.elementLabels.count(element->name()) > 0) { + modelComponents.stateLabeling.addLabel(element->name() + "_fail"); } } - if(mergeFailedStates) { - modelComponents.stateLabeling.addLabelToState("failed", failedIndex); + // Set labels to states + if (mergeFailedStates) { + modelComponents.stateLabeling.addLabelToState("failed", failedStateId); } - for (auto const& stateIdPair : mStates) { + for (auto const& stateIdPair : stateStorage.stateToId) { storm::storage::BitVector state = stateIdPair.first; - size_t stateId = stateIdPair.second; - if (!mergeFailedStates && labelOpts.buildFailLabel && mDft.hasFailed(state, *mStateGenerationInfo)) { + size_t stateId = matrixBuilder.getRemapping(stateIdPair.second); + if (!mergeFailedStates && labelOpts.buildFailLabel && dft.hasFailed(state, *stateGenerationInfo)) { modelComponents.stateLabeling.addLabelToState("failed", stateId); } - if (labelOpts.buildFailSafeLabel && mDft.isFailsafe(state, *mStateGenerationInfo)) { + if (labelOpts.buildFailSafeLabel && dft.isFailsafe(state, *stateGenerationInfo)) { modelComponents.stateLabeling.addLabelToState("failsafe", stateId); - }; - // Set fail status for each BE - for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) { - if (labelOpts.beLabels.count(elem->name()) > 0 && storm::storage::DFTState<ValueType>::hasFailed(state, mStateGenerationInfo->getStateIndex(elem->id())) ) { - modelComponents.stateLabeling.addLabelToState(elem->name() + "_fail", stateId); + } + // Set fail status for each necessary element + for (size_t id = 0; id < dft.nrElements(); ++id) { + std::shared_ptr<storage::DFTElement<ValueType> const> element = dft.getElement(id); + if ((labelOpts.buildAllLabels || labelOpts.elementLabels.count(element->name()) > 0) && storm::storage::DFTState<ValueType>::hasFailed(state, stateGenerationInfo->getStateIndex(element->id()))) { + modelComponents.stateLabeling.addLabelToState(element->name() + "_fail", stateId); + } + } + } + + STORM_LOG_TRACE(modelComponents.stateLabeling); + } + + template<typename ValueType, typename StateType> + std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilder<ValueType, StateType>::getModel() { + STORM_LOG_ASSERT(skippedStates.size() == 0, "Concrete model has skipped states"); + return createModel(false); + } + + template<typename ValueType, typename StateType> + std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilder<ValueType, StateType>::getModelApproximation(bool lowerBound, bool expectedTime) { + if (expectedTime) { + // TODO Matthias: handle case with no skipped states + changeMatrixBound(modelComponents.transitionMatrix, lowerBound); + return createModel(true); + } else { + // Change model for probabilities + // TODO Matthias: make nicer + storm::storage::SparseMatrix<ValueType> matrix = modelComponents.transitionMatrix; + storm::models::sparse::StateLabeling labeling = modelComponents.stateLabeling; + if (lowerBound) { + // Set self loop for lower bound + for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { + auto matrixEntry = matrix.getRow(it->first, 0).begin(); + STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); + STORM_LOG_ASSERT(!it->second.first->isPseudoState(), "State is still pseudo state."); + matrixEntry->setValue(storm::utility::one<ValueType>()); + matrixEntry->setColumn(it->first); + } + } else { + // Make skipped states failed states for upper bound + storm::storage::BitVector failedStates = modelComponents.stateLabeling.getStates("failed"); + for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { + failedStates.set(it->first); } + labeling.setStates("failed", failedStates); } + + std::shared_ptr<storm::models::sparse::Model<ValueType>> model; + if (modelComponents.deterministicModel) { + model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(matrix), std::move(labeling)); + } else { + // Build MA + // Compute exit rates + // TODO Matthias: avoid computing multiple times + modelComponents.exitRates = std::vector<ValueType>(modelComponents.markovianStates.size()); + std::vector<typename storm::storage::SparseMatrix<ValueType>::index_type> indices = matrix.getRowGroupIndices(); + for (StateType stateIndex = 0; stateIndex < modelComponents.markovianStates.size(); ++stateIndex) { + if (modelComponents.markovianStates[stateIndex]) { + modelComponents.exitRates[stateIndex] = matrix.getRowSum(indices[stateIndex]); + } else { + modelComponents.exitRates[stateIndex] = storm::utility::zero<ValueType>(); + } + } + STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); + + storm::storage::sparse::ModelComponents<ValueType> maComponents(std::move(matrix), std::move(labeling)); + maComponents.rateTransitions = true; + maComponents.markovianStates = modelComponents.markovianStates; + maComponents.exitRates = modelComponents.exitRates; + std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(maComponents)); + if (ma->hasOnlyTrivialNondeterminism()) { + // Markov automaton can be converted into CTMC + // TODO Matthias: change components which were not moved accordingly + model = ma->convertToCTMC(); + } else { + model = ma; + } + + } + + if (model->getNumberOfStates() <= 15) { + STORM_LOG_TRACE("Transition matrix: " << std::endl << model->getTransitionMatrix()); + } else { + STORM_LOG_TRACE("Transition matrix: too big to print"); + } + return model; } + } + template<typename ValueType, typename StateType> + std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilder<ValueType, StateType>::createModel(bool copy) { std::shared_ptr<storm::models::sparse::Model<ValueType>> model; - storm::storage::sparse::ModelComponents<ValueType> components(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling)); - components.exitRates = std::move(modelComponents.exitRates); - if (deterministic) { - components.transitionMatrix.makeRowGroupingTrivial(); - model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(components)); + + if (modelComponents.deterministicModel) { + // Build CTMC + if (copy) { + model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(modelComponents.transitionMatrix, modelComponents.stateLabeling); + } else { + model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling)); + } } else { - components.markovianStates = std::move(modelComponents.markovianStates); - std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(components)); + // Build MA + // Compute exit rates + // TODO Matthias: avoid computing multiple times + modelComponents.exitRates = std::vector<ValueType>(modelComponents.markovianStates.size()); + std::vector<typename storm::storage::SparseMatrix<ValueType>::index_type> indices = modelComponents.transitionMatrix.getRowGroupIndices(); + for (StateType stateIndex = 0; stateIndex < modelComponents.markovianStates.size(); ++stateIndex) { + if (modelComponents.markovianStates[stateIndex]) { + modelComponents.exitRates[stateIndex] = modelComponents.transitionMatrix.getRowSum(indices[stateIndex]); + } else { + modelComponents.exitRates[stateIndex] = storm::utility::zero<ValueType>(); + } + } + STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); + + std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma; + if (copy) { + storm::storage::sparse::ModelComponents<ValueType> maComponents(modelComponents.transitionMatrix, modelComponents.stateLabeling); + maComponents.rateTransitions = true; + maComponents.markovianStates = modelComponents.markovianStates; + maComponents.exitRates = modelComponents.exitRates; + ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(maComponents)); + } else { + storm::storage::sparse::ModelComponents<ValueType> maComponents(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling)); + maComponents.rateTransitions = true; + maComponents.markovianStates = std::move(modelComponents.markovianStates); + maComponents.exitRates = std::move(modelComponents.exitRates); + ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(maComponents)); + } if (ma->hasOnlyTrivialNondeterminism()) { // Markov automaton can be converted into CTMC + // TODO Matthias: change components which were not moved accordingly model = ma->convertToCTMC(); } else { model = ma; } } - + + if (model->getNumberOfStates() <= 15) { + STORM_LOG_TRACE("Transition matrix: " << std::endl << model->getTransitionMatrix()); + } else { + STORM_LOG_TRACE("Transition matrix: too big to print"); + } return model; } - - template <typename ValueType> - std::pair<uint_fast64_t, bool> ExplicitDFTModelBuilder<ValueType>::exploreStates(DFTStatePointer const& state, size_t& rowOffset, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<uint_fast64_t>& markovianStates, std::vector<ValueType>& exitRates) { - STORM_LOG_TRACE("Explore state: " << mDft.getStateString(state)); - - auto explorePair = checkForExploration(state); - if (!explorePair.first) { - // State does not need any exploration - return std::make_pair(explorePair.second, true); - } - - - // Initialization - // TODO Matthias: set Markovian states directly as bitvector? - std::map<uint_fast64_t, ValueType> outgoingRates; - std::vector<std::map<uint_fast64_t, ValueType>> outgoingProbabilities; - bool hasDependencies = state->nrFailableDependencies() > 0; - size_t failableCount = hasDependencies ? state->nrFailableDependencies() : state->nrFailableBEs(); - size_t smallest = 0; - ValueType exitRate = storm::utility::zero<ValueType>(); - bool deterministic = !hasDependencies; - - // Absorbing state - if (mDft.hasFailed(state) || mDft.isFailsafe(state) || state->nrFailableBEs() == 0) { - uint_fast64_t stateId = addState(state); - STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not coincide."); - - // Add self loop - transitionMatrixBuilder.newRowGroup(stateId + rowOffset); - transitionMatrixBuilder.addNextValue(stateId + rowOffset, stateId, storm::utility::one<ValueType>()); - STORM_LOG_TRACE("Added self loop for " << stateId); - exitRates.push_back(storm::utility::one<ValueType>()); - STORM_LOG_ASSERT(exitRates.size()-1 == stateId, "No. of considered states does not match state id."); - markovianStates.push_back(stateId); - // No further exploration required - return std::make_pair(stateId, true); - } - - // Let BE fail - while (smallest < failableCount) { - STORM_LOG_ASSERT(!mDft.hasFailed(state), "Dft has failed."); - - // Construct new state as copy from original one - DFTStatePointer newState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); - std::pair<std::shared_ptr<storm::storage::DFTBE<ValueType> const>, bool> nextBEPair = newState->letNextBEFail(smallest++); - std::shared_ptr<storm::storage::DFTBE<ValueType> const>& nextBE = nextBEPair.first; - STORM_LOG_ASSERT(nextBE, "NextBE is null."); - STORM_LOG_ASSERT(nextBEPair.second == hasDependencies, "Failure due to dependencies does not match."); - STORM_LOG_TRACE("With the failure of: " << nextBE->name() << " [" << nextBE->id() << "] in " << mDft.getStateString(state)); - - // Propagate failures - storm::storage::DFTStateSpaceGenerationQueues<ValueType> queues; - - for (DFTGatePointer parent : nextBE->parents()) { - if (newState->isOperational(parent->id())) { - queues.propagateFailure(parent); - } - } - for (DFTRestrictionPointer restr : nextBE->restrictions()) { - queues.checkRestrictionLater(restr); - } - while (!queues.failurePropagationDone()) { - DFTGatePointer next = queues.nextFailurePropagation(); - next->checkFails(*newState, queues); - newState->updateFailableDependencies(next->id()); - } - - while(!queues.restrictionChecksDone()) { - DFTRestrictionPointer next = queues.nextRestrictionCheck(); - next->checkFails(*newState, queues); - newState->updateFailableDependencies(next->id()); - } - - if(newState->isInvalid()) { - continue; - } - bool dftFailed = newState->hasFailed(mDft.getTopLevelIndex()); - - while (!dftFailed && !queues.failsafePropagationDone()) { - DFTGatePointer next = queues.nextFailsafePropagation(); - next->checkFailsafe(*newState, queues); + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilder<ValueType, StateType>::changeMatrixBound(storm::storage::SparseMatrix<ValueType> & matrix, bool lowerBound) const { + // Set lower bound for skipped states + for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { + auto matrixEntry = matrix.getRow(it->first, 0).begin(); + STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); + STORM_LOG_ASSERT(!it->second.first->isPseudoState(), "State is still pseudo state."); + + ExplorationHeuristicPointer heuristic = it->second.second; + if (storm::utility::isZero(heuristic->getUpperBound())) { + // Initialize bounds + ValueType lowerBound = getLowerBound(it->second.first); + ValueType upperBound = getUpperBound(it->second.first); + heuristic->setBounds(lowerBound, upperBound); } - while (!dftFailed && enableDC && !queues.dontCarePropagationDone()) { - DFTElementPointer next = queues.nextDontCarePropagation(); - next->checkDontCareAnymore(*newState, queues); - } - - // Update failable dependencies - if (!dftFailed) { - newState->updateFailableDependencies(nextBE->id()); - newState->updateDontCareDependencies(nextBE->id()); - } - - uint_fast64_t newStateId; - if(dftFailed && mergeFailedStates) { - newStateId = failedIndex; + // Change bound + if (lowerBound) { + matrixEntry->setValue(it->second.second->getLowerBound()); } else { - // Explore new state recursively - auto explorePair = exploreStates(newState, rowOffset, transitionMatrixBuilder, markovianStates, exitRates); - newStateId = explorePair.first; - deterministic &= explorePair.second; + matrixEntry->setValue(it->second.second->getUpperBound()); } + } + } - // Set transitions - if (hasDependencies) { - // Failure is due to dependency -> add non-deterministic choice - std::map<uint_fast64_t, ValueType> choiceProbabilities; - std::shared_ptr<storm::storage::DFTDependency<ValueType> const> dependency = mDft.getDependency(state->getDependencyId(smallest-1)); - choiceProbabilities.insert(std::make_pair(newStateId, dependency->probability())); - STORM_LOG_TRACE("Added transition to " << newStateId << " with probability " << dependency->probability()); - - if (!storm::utility::isOne(dependency->probability())) { - // Add transition to state where dependency was unsuccessful - DFTStatePointer unsuccessfulState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); - unsuccessfulState->letDependencyBeUnsuccessful(smallest-1); - auto explorePair = exploreStates(unsuccessfulState, rowOffset, transitionMatrixBuilder, markovianStates, exitRates); - uint_fast64_t unsuccessfulStateId = explorePair.first; - deterministic &= explorePair.second; - ValueType remainingProbability = storm::utility::one<ValueType>() - dependency->probability(); - choiceProbabilities.insert(std::make_pair(unsuccessfulStateId, remainingProbability)); - STORM_LOG_TRACE("Added transition to " << unsuccessfulStateId << " with remaining probability " << remainingProbability); - } - outgoingProbabilities.push_back(choiceProbabilities); - } else { - // Set failure rate according to activation - bool isActive = true; - if (mDft.hasRepresentant(nextBE->id())) { - // Active must be checked for the state we are coming from as this state is responsible for the - // rate and not the new state we are going to - isActive = state->isActive(mDft.getRepresentant(nextBE->id())); + template<typename ValueType, typename StateType> + ValueType ExplicitDFTModelBuilder<ValueType, StateType>::getLowerBound(DFTStatePointer const& state) const { + // Get the lower bound by considering the failure of all possible BEs + ValueType lowerBound = storm::utility::zero<ValueType>(); + for (size_t index = 0; index < state->nrFailableBEs(); ++index) { + lowerBound += state->getFailableBERate(index); + } + return lowerBound; + } + + template<typename ValueType, typename StateType> + ValueType ExplicitDFTModelBuilder<ValueType, StateType>::getUpperBound(DFTStatePointer const& state) const { + if (state->hasFailed(dft.getTopLevelIndex())) { + return storm::utility::zero<ValueType>(); + } + + // Get the upper bound by considering the failure of all BEs + ValueType upperBound = storm::utility::one<ValueType>(); + ValueType rateSum = storm::utility::zero<ValueType>(); + + // Compute for each independent subtree + for (std::vector<size_t> const& subtree : subtreeBEs) { + // Get all possible rates + std::vector<ValueType> rates; + storm::storage::BitVector coldBEs(subtree.size(), false); + for (size_t i = 0; i < subtree.size(); ++i) { + size_t id = subtree[i]; + if (state->isOperational(id)) { + // Get BE rate + ValueType rate = state->getBERate(id); + if (storm::utility::isZero<ValueType>(rate)) { + // Get active failure rate for cold BE + rate = dft.getBasicElement(id)->activeFailureRate(); + if (storm::utility::isZero<ValueType>(rate)) { + // Ignore BE which cannot fail + continue; + } + // Mark BE as cold + coldBEs.set(i, true); + } + rates.push_back(rate); + rateSum += rate; } - ValueType rate = isActive ? nextBE->activeFailureRate() : nextBE->passiveFailureRate(); - STORM_LOG_ASSERT(!storm::utility::isZero(rate), "Rate is 0."); - auto resultFind = outgoingRates.find(newStateId); - if (resultFind != outgoingRates.end()) { - // Add to existing transition - resultFind->second += rate; - STORM_LOG_TRACE("Updated transition to " << resultFind->first << " with " << (isActive ? "active" : "passive") << " rate " << rate << " to new rate " << resultFind->second); - } else { - // Insert new transition - outgoingRates.insert(std::make_pair(newStateId, rate)); - STORM_LOG_TRACE("Added transition to " << newStateId << " with " << (isActive ? "active" : "passive") << " rate " << rate); + } + + STORM_LOG_ASSERT(rates.size() > 0, "No rates failable"); + + // Sort rates + std::sort(rates.begin(), rates.end()); + std::vector<size_t> rateCount(rates.size(), 0); + size_t lastIndex = 0; + for (size_t i = 0; i < rates.size(); ++i) { + if (rates[lastIndex] != rates[i]) { + lastIndex = i; } - exitRate += rate; + ++rateCount[lastIndex]; } - } // end while failing BE - - // Add state - uint_fast64_t stateId = addState(state); - STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); - STORM_LOG_ASSERT(stateId == newIndex-1, "Id does not match no. of states."); - - if (hasDependencies) { - // Add all probability transitions - STORM_LOG_ASSERT(outgoingRates.empty(), "Outgoing transitions not empty."); - transitionMatrixBuilder.newRowGroup(stateId + rowOffset); - for (size_t i = 0; i < outgoingProbabilities.size(); ++i, ++rowOffset) { - STORM_LOG_ASSERT(outgoingProbabilities[i].size() == 1 || outgoingProbabilities[i].size() == 2, "No. of outgoing transitions is not valid."); - for (auto it = outgoingProbabilities[i].begin(); it != outgoingProbabilities[i].end(); ++it) - { - STORM_LOG_TRACE("Set transition from " << stateId << " to " << it->first << " with probability " << it->second); - transitionMatrixBuilder.addNextValue(stateId + rowOffset, it->first, it->second); + for (size_t i = 0; i < rates.size(); ++i) { + // Cold BEs cannot fail in the first step + if (!coldBEs.get(i) && rateCount[i] > 0) { + std::iter_swap(rates.begin() + i, rates.end() - 1); + // Compute AND MTTF of subtree without current rate and scale with current rate + upperBound += rates.back() * rateCount[i] * computeMTTFAnd(rates, rates.size() - 1); + // Swap back + // TODO try to avoid swapping back + std::iter_swap(rates.begin() + i, rates.end() - 1); } } - rowOffset--; // One increment too many - } else { - // Try to merge pseudo states with their instantiation - // TODO Matthias: improve? - for (auto it = outgoingRates.begin(); it != outgoingRates.end(); ) { - if (it->first >= OFFSET_PSEUDO_STATE) { - uint_fast64_t newId = it->first - OFFSET_PSEUDO_STATE; - STORM_LOG_ASSERT(newId < mPseudoStatesMapping.size(), "Id is not valid."); - if (mPseudoStatesMapping[newId].first > 0) { - // State exists already - newId = mPseudoStatesMapping[newId].first; - auto itFind = outgoingRates.find(newId); - if (itFind != outgoingRates.end()) { - // Add probability from pseudo state to instantiation - itFind->second += it->second; - STORM_LOG_TRACE("Merged pseudo state " << newId << " adding rate " << it->second << " to total rate of " << itFind->second); - } else { - // Only change id - outgoingRates.emplace(newId, it->second); - STORM_LOG_TRACE("Instantiated pseudo state " << newId << " with rate " << it->second); - } - // Remove pseudo state - it = outgoingRates.erase(it); - } else { - ++it; + } + + STORM_LOG_TRACE("Upper bound is " << (rateSum / upperBound) << " for state " << state->getId()); + STORM_LOG_ASSERT(!storm::utility::isZero(upperBound), "Upper bound is 0"); + STORM_LOG_ASSERT(!storm::utility::isZero(rateSum), "State is absorbing"); + return rateSum / upperBound; + } + + template<typename ValueType, typename StateType> + ValueType ExplicitDFTModelBuilder<ValueType, StateType>::computeMTTFAnd(std::vector<ValueType> const& rates, size_t size) const { + ValueType result = storm::utility::zero<ValueType>(); + if (size == 0) { + return result; + } + + // TODO Matthias: works only for <64 BEs! + // WARNING: this code produces wrong results for more than 32 BEs + /*for (size_t i = 1; i < 4 && i <= rates.size(); ++i) { + size_t permutation = smallestIntWithNBitsSet(static_cast<size_t>(i)); + ValueType sum = storm::utility::zero<ValueType>(); + do { + ValueType permResult = storm::utility::zero<ValueType>(); + for(size_t j = 0; j < rates.size(); ++j) { + if(permutation & static_cast<size_t>(1 << static_cast<size_t>(j))) { + // WARNING: if the first bit is set, it also recognizes the 32nd bit as set + // TODO: fix + permResult += rates[j]; } - } else { - ++it; } + permutation = nextBitPermutation(permutation); + STORM_LOG_ASSERT(!storm::utility::isZero(permResult), "PermResult is 0"); + sum += storm::utility::one<ValueType>() / permResult; + } while(permutation < (static_cast<size_t>(1) << rates.size()) && permutation != 0); + if (i % 2 == 0) { + result -= sum; + } else { + result += sum; } - - // Add all rate transitions - STORM_LOG_ASSERT(outgoingProbabilities.empty(), "Outgoing probabilities not empty."); - transitionMatrixBuilder.newRowGroup(state->getId() + rowOffset); - STORM_LOG_TRACE("Exit rate for " << state->getId() << ": " << exitRate); - for (auto it = outgoingRates.begin(); it != outgoingRates.end(); ++it) - { - ValueType probability = it->second / exitRate; // Transform rate to probability - STORM_LOG_TRACE("Set transition from " << state->getId() << " to " << it->first << " with rate " << it->second); - transitionMatrixBuilder.addNextValue(state->getId() + rowOffset, it->first, probability); + }*/ + + // Compute result with permutations of size <= 3 + ValueType one = storm::utility::one<ValueType>(); + for (size_t i1 = 0; i1 < size; ++i1) { + // + 1/a + ValueType sum = rates[i1]; + result += one / sum; + for (size_t i2 = 0; i2 < i1; ++i2) { + // - 1/(a+b) + ValueType sum2 = sum + rates[i2]; + result -= one / sum2; + for (size_t i3 = 0; i3 < i2; ++i3) { + // + 1/(a+b+c) + result += one / (sum2 + rates[i3]); + } } - - markovianStates.push_back(state->getId()); } - - STORM_LOG_TRACE("Finished exploring state: " << mDft.getStateString(state)); - exitRates.push_back(exitRate); - STORM_LOG_ASSERT(exitRates.size()-1 == state->getId(), "Id does not match no. of states."); - return std::make_pair(state->getId(), deterministic); + + STORM_LOG_ASSERT(!storm::utility::isZero(result), "UpperBound is 0"); + return result; } - - template <typename ValueType> - std::pair<bool, uint_fast64_t> ExplicitDFTModelBuilder<ValueType>::checkForExploration(DFTStatePointer const& state) { + + template<typename ValueType, typename StateType> + StateType ExplicitDFTModelBuilder<ValueType, StateType>::getOrAddStateIndex(DFTStatePointer const& state) { + StateType stateId; bool changed = false; - if (mStateGenerationInfo->hasSymmetries()) { + + if (stateGenerationInfo->hasSymmetries()) { // Order state by symmetry - STORM_LOG_TRACE("Check for symmetry: " << mDft.getStateString(state)); + STORM_LOG_TRACE("Check for symmetry: " << dft.getStateString(state)); changed = state->orderBySymmetry(); - STORM_LOG_TRACE("State " << (changed ? "changed to " : "did not change") << (changed ? mDft.getStateString(state) : "")); - } - - if (mStates.contains(state->status())) { - // State already exists - uint_fast64_t stateId = mStates.getValue(state->status()); - STORM_LOG_TRACE("State " << mDft.getStateString(state) << " with id " << stateId << " already exists"); - - if (changed || stateId < OFFSET_PSEUDO_STATE) { - // State is changed or an explored "normal" state - return std::make_pair(false, stateId); - } - - stateId -= OFFSET_PSEUDO_STATE; - STORM_LOG_ASSERT(stateId < mPseudoStatesMapping.size(), "Id not valid."); - if (mPseudoStatesMapping[stateId].first > 0) { - // Pseudo state already explored - return std::make_pair(false, mPseudoStatesMapping[stateId].first); - } - - STORM_LOG_ASSERT(mPseudoStatesMapping[stateId].second == state->status(), "States do not coincide."); - STORM_LOG_TRACE("Pseudo state " << mDft.getStateString(state) << " can be explored now"); - return std::make_pair(true, stateId); - } else { - // State does not exists - if (changed) { - // Remember state for later creation - state->setId(mPseudoStatesMapping.size() + OFFSET_PSEUDO_STATE); - mPseudoStatesMapping.push_back(std::make_pair(0, state->status())); - mStates.findOrAdd(state->status(), state->getId()); - STORM_LOG_TRACE("Remember state for later creation: " << mDft.getStateString(state)); - return std::make_pair(false, state->getId()); - } else { - // State needs exploration - return std::make_pair(true, 0); - } + STORM_LOG_TRACE("State " << (changed ? "changed to " : "did not change") << (changed ? dft.getStateString(state) : "")); } - } - template <typename ValueType> - uint_fast64_t ExplicitDFTModelBuilder<ValueType>::addState(DFTStatePointer const& state) { - uint_fast64_t stateId; - // TODO remove - bool changed = state->orderBySymmetry(); - STORM_LOG_ASSERT(!changed, "State to add has changed by applying symmetry."); - - // Check if state already exists - if (mStates.contains(state->status())) { + if (stateStorage.stateToId.contains(state->status())) { // State already exists - stateId = mStates.getValue(state->status()); - STORM_LOG_TRACE("State " << mDft.getStateString(state) << " with id " << stateId << " already exists"); - - // Check if possible pseudo state can be created now - STORM_LOG_ASSERT(stateId >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); - stateId -= OFFSET_PSEUDO_STATE; - STORM_LOG_ASSERT(stateId < mPseudoStatesMapping.size(), "Pseudo state not known."); - if (mPseudoStatesMapping[stateId].first == 0) { - // Create pseudo state now - STORM_LOG_ASSERT(mPseudoStatesMapping[stateId].second == state->status(), "Pseudo states do not coincide."); - state->setId(newIndex++); - mPseudoStatesMapping[stateId].first = state->getId(); - stateId = state->getId(); - mStates.setOrAdd(state->status(), stateId); - STORM_LOG_TRACE("Now create state " << mDft.getStateString(state) << " with id " << stateId); - return stateId; - } else { - STORM_LOG_ASSERT(false, "Pseudo state already created."); - return 0; + stateId = stateStorage.stateToId.getValue(state->status()); + STORM_LOG_TRACE("State " << dft.getStateString(state) << " with id " << stateId << " already exists"); + if (!changed) { + // Check if state is pseudo state + // If state is explored already the possible pseudo state was already constructed + auto iter = statesNotExplored.find(stateId); + if (iter != statesNotExplored.end() && iter->second.first->isPseudoState()) { + // Create pseudo state now + STORM_LOG_ASSERT(iter->second.first->getId() == stateId, "Ids do not match."); + STORM_LOG_ASSERT(iter->second.first->status() == state->status(), "Pseudo states do not coincide."); + state->setId(stateId); + // Update mapping to map to concrete state now + // TODO Matthias: just change pointer? + statesNotExplored[stateId] = std::make_pair(state, iter->second.second); + // We do not push the new state on the exploration queue as the pseudo state was already pushed + STORM_LOG_TRACE("Created pseudo state " << dft.getStateString(state)); + } } } else { + // State does not exist yet + STORM_LOG_ASSERT(state->isPseudoState() == changed, "State type (pseudo/concrete) wrong."); // Create new state state->setId(newIndex++); - stateId = mStates.findOrAdd(state->status(), state->getId()); - STORM_LOG_TRACE("New state: " << mDft.getStateString(state)); - return stateId; + stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); + STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); + // Insert state as not yet explored + ExplorationHeuristicPointer nullHeuristic; + statesNotExplored[stateId] = std::make_pair(state, nullHeuristic); + // Reserve one slot for the new state in the remapping + matrixBuilder.stateRemapping.push_back(0); + STORM_LOG_TRACE("New " << (state->isPseudoState() ? "pseudo" : "concrete") << " state: " << dft.getStateString(state)); + } + return stateId; + } + + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilder<ValueType, StateType>::setMarkovian(bool markovian) { + if (matrixBuilder.getCurrentRowGroup() > modelComponents.markovianStates.size()) { + // Resize BitVector + modelComponents.markovianStates.resize(modelComponents.markovianStates.size() + INITIAL_BITVECTOR_SIZE); + } + modelComponents.markovianStates.set(matrixBuilder.getCurrentRowGroup() - 1, markovian); + } + + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilder<ValueType, StateType>::printNotExplored() const { + std::cout << "states not explored:" << std::endl; + for (auto it : statesNotExplored) { + std::cout << it.first << " -> " << dft.getStateString(it.second.first) << std::endl; } } diff --git a/src/storm-dft/builder/ExplicitDFTModelBuilder.h b/src/storm-dft/builder/ExplicitDFTModelBuilder.h index 2913a78fa..b96dea410 100644 --- a/src/storm-dft/builder/ExplicitDFTModelBuilder.h +++ b/src/storm-dft/builder/ExplicitDFTModelBuilder.h @@ -1,40 +1,42 @@ -#pragma once +#pragma once #include <boost/container/flat_set.hpp> #include <boost/optional/optional.hpp> #include <stack> #include <unordered_set> - +#include <limits> #include "storm/models/sparse/StateLabeling.h" #include "storm/models/sparse/ChoiceLabeling.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/models/sparse/Model.h" #include "storm/storage/SparseMatrix.h" -#include "storm/storage/BitVectorHashMap.h" +#include "storm/storage/sparse/StateStorage.h" +#include "storm-dft/builder/DftExplorationHeuristic.h" +#include "storm-dft/generator/DftNextStateGenerator.h" #include "storm-dft/storage/dft/DFT.h" #include "storm-dft/storage/dft/SymmetricUnits.h" - - - - +#include "storm-dft/storage/BucketPriorityQueue.h" namespace storm { namespace builder { - template<typename ValueType> + /*! + * Build a Markov chain from DFT. + */ + template<typename ValueType, typename StateType = uint32_t> class ExplicitDFTModelBuilder { - using DFTElementPointer = std::shared_ptr<storm::storage::DFTElement<ValueType>>; - using DFTElementCPointer = std::shared_ptr<storm::storage::DFTElement<ValueType> const>; - using DFTGatePointer = std::shared_ptr<storm::storage::DFTGate<ValueType>>; using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; - using DFTRestrictionPointer = std::shared_ptr<storm::storage::DFTRestriction<ValueType>>; + // TODO Matthias: make choosable + using ExplorationHeuristic = DFTExplorationHeuristicDepth<ValueType>; + using ExplorationHeuristicPointer = std::shared_ptr<ExplorationHeuristic>; // A structure holding the individual components of a model. struct ModelComponents { + // Constructor ModelComponents(); // The transition matrix. @@ -51,52 +53,310 @@ namespace storm { // A vector that stores a labeling for each choice. boost::optional<storm::models::sparse::ChoiceLabeling> choiceLabeling; + + // A flag indicating if the model is deterministic. + bool deterministicModel; + }; + + // A class holding the information for building the transition matrix. + class MatrixBuilder { + public: + // Constructor + MatrixBuilder(bool canHaveNondeterminism); + + /*! + * Set a mapping from a state id to the index in the matrix. + * + * @param id Id of the state. + */ + void setRemapping(StateType id) { + STORM_LOG_ASSERT(id < stateRemapping.size(), "Invalid index for remapping."); + stateRemapping[id] = currentRowGroup; + } + + /*! + * Create a new row group if the model is nondeterministic. + */ + void newRowGroup() { + if (canHaveNondeterminism) { + builder.newRowGroup(currentRow); + } + ++currentRowGroup; + } + + /*! + * Add a transition from the current row. + * + * @param index Target index + * @param value Value of transition + */ + void addTransition(StateType index, ValueType value) { + builder.addNextValue(currentRow, index, value); + } + + /*! + * Finish the current row. + */ + void finishRow() { + ++currentRow; + } + + /*! + * Remap the columns in the matrix. + */ + void remap() { + builder.replaceColumns(stateRemapping, mappingOffset); + } + + /*! + * Get the current row group. + * + * @return The current row group. + */ + StateType getCurrentRowGroup() { + return currentRowGroup; + } + + /*! + * Get the remapped state for the given id. + * + * @param id State. + * + * @return Remapped index. + */ + StateType getRemapping(StateType id) { + STORM_LOG_ASSERT(id < stateRemapping.size(), "Invalid index for remapping."); + return stateRemapping[id]; + } + + // Matrix builder. + storm::storage::SparseMatrixBuilder<ValueType> builder; + + // Offset to distinguish states which will not be remapped anymore and those which will. + size_t mappingOffset; + + // A mapping from state ids to the row group indices in which they actually reside. + // TODO Matthias: avoid hack with fixed int type + std::vector<uint_fast64_t> stateRemapping; + + private: + + // Index of the current row group. + StateType currentRowGroup; + + // Index of the current row. + StateType currentRow; + + // Flag indicating if row groups are needed. + bool canHaveNondeterminism; }; - - const size_t INITIAL_BUCKETSIZE = 20000; - const uint_fast64_t OFFSET_PSEUDO_STATE = UINT_FAST64_MAX / 2; - - storm::storage::DFT<ValueType> const& mDft; - std::shared_ptr<storm::storage::DFTStateGenerationInfo> mStateGenerationInfo; - storm::storage::BitVectorHashMap<uint_fast64_t> mStates; - std::vector<std::pair<uint_fast64_t, storm::storage::BitVector>> mPseudoStatesMapping; // vector of (id to concrete state, bitvector) - size_t newIndex = 0; - bool mergeFailedStates = false; - bool enableDC = true; - size_t failedIndex = 0; - size_t initialStateIndex = 0; public: + // A structure holding the labeling options. struct LabelOptions { - bool buildFailLabel = true; - bool buildFailSafeLabel = false; - std::set<std::string> beLabels = {}; + // Constructor + LabelOptions(std::vector<std::shared_ptr<storm::logic::Formula const>> properties, bool buildAllLabels = false); + + // Flag indicating if the general fail label should be included. + bool buildFailLabel; + + // Flag indicating if the general failsafe label should be included. + bool buildFailSafeLabel; + + // Flag indicating if all possible labels should be included. + bool buildAllLabels; + + // Set of element names whose fail label to include. + std::set<std::string> elementLabels; }; - + + /*! + * Constructor. + * + * @param dft DFT. + * @param symmetries Symmetries in the dft. + * @param enableDC Flag indicating if dont care propagation should be used. + */ ExplicitDFTModelBuilder(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC); - std::shared_ptr<storm::models::sparse::Model<ValueType>> buildModel(LabelOptions const& labelOpts); + /*! + * Build model from DFT. + * + * @param labelOpts Options for labeling. + * @param iteration Current number of iteration. + * @param approximationThreshold Threshold determining when to skip exploring states. + */ + void buildModel(LabelOptions const& labelOpts, size_t iteration, double approximationThreshold = 0.0); + + /*! + * Get the built model. + * + * @return The model built from the DFT. + */ + std::shared_ptr<storm::models::sparse::Model<ValueType>> getModel(); + + /*! + * Get the built approximation model for either the lower or upper bound. + * + * @param lowerBound If true, the lower bound model is returned, else the upper bound model + * @param expectedTime If true, the bounds for expected time are computed, else the bounds for probabilities. + * + * @return The model built from the DFT. + */ + std::shared_ptr<storm::models::sparse::Model<ValueType>> getModelApproximation(bool lowerBound, bool expectedTime); private: - std::pair<uint_fast64_t, bool> exploreStates(DFTStatePointer const& state, size_t& rowOffset, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<uint_fast64_t>& markovianStates, std::vector<ValueType>& exitRates); - + /*! - * Adds a state to the explored states and handles pseudo states. + * Explore state space of DFT. + * + * @param approximationThreshold Threshold to determine when to skip states. + */ + void exploreStateSpace(double approximationThreshold); + + /*! + * Initialize the matrix for a refinement iteration. + */ + void initializeNextIteration(); + + /*! + * Build the labeling. + * + * @param labelOpts Options for labeling. + */ + void buildLabeling(LabelOptions const& labelOpts); + + /*! + * Add a state to the explored states (if not already there). It also handles pseudo states. * * @param state The state to add. - * @return Id of added state. + * + * @return Id of state. + */ + StateType getOrAddStateIndex(DFTStatePointer const& state); + + /*! + * Set markovian flag for the current state. + * + * @param markovian Flag indicating if the state is markovian. + */ + void setMarkovian(bool markovian); + + /** + * Change matrix to reflect the lower or upper approximation bound. + * + * @param matrix Matrix to change. The change are reflected here. + * @param lowerBound Flag indicating if the lower bound should be used. Otherwise the upper bound is used. */ - uint_fast64_t addState(DFTStatePointer const& state); - + void changeMatrixBound(storm::storage::SparseMatrix<ValueType> & matrix, bool lowerBound) const; + /*! - * Check if state needs an exploration and remember pseudo states for later creation. + * Get lower bound approximation for state. + * + * @param state The state. + * + * @return Lower bound approximation. + */ + ValueType getLowerBound(DFTStatePointer const& state) const; + + /*! + * Get upper bound approximation for state. + * + * @param state The state. + * + * @return Upper bound approximation. + */ + ValueType getUpperBound(DFTStatePointer const& state) const; + + /*! + * Compute the MTTF of an AND gate via a closed formula. + * The used formula is 1/( 1/a + 1/b + 1/c + ... - 1/(a+b) - 1/(a+c) - ... + 1/(a+b+c) + ... - ...) + * + * @param rates List of rates of children of AND. + * @param size Only indices < size are considered in the vector. + * @return MTTF. + */ + ValueType computeMTTFAnd(std::vector<ValueType> const& rates, size_t size) const; + + /*! + * Compares the priority of two states. + * + * @param idA Id of first state + * @param idB Id of second state * - * @param state State which might need exploration. - * @return Pair of flag indicating whether the state needs exploration now and the state id if the state already - * exists. + * @return True if the priority of the first state is greater then the priority of the second one. */ - std::pair<bool, uint_fast64_t> checkForExploration(DFTStatePointer const& state); + bool isPriorityGreater(StateType idA, StateType idB) const; + void printNotExplored() const; + + /*! + * Create the model model from the model components. + * + * @param copy If true, all elements of the model component are copied (used for approximation). If false + * they are moved to reduce the memory overhead. + * + * @return The model built from the model components. + */ + std::shared_ptr<storm::models::sparse::Model<ValueType>> createModel(bool copy); + + // Initial size of the bitvector. + const size_t INITIAL_BITVECTOR_SIZE = 20000; + // Offset used for pseudo states. + const StateType OFFSET_PSEUDO_STATE = std::numeric_limits<StateType>::max() / 2; + + // Dft + storm::storage::DFT<ValueType> const& dft; + + // General information for state generation + // TODO Matthias: use const reference + std::shared_ptr<storm::storage::DFTStateGenerationInfo> stateGenerationInfo; + + // Flag indication if dont care propagation should be used. + bool enableDC = true; + + //TODO Matthias: make changeable + const bool mergeFailedStates = true; + + // Heuristic used for approximation + storm::builder::ApproximationHeuristic usedHeuristic; + + // Current id for new state + size_t newIndex = 0; + + // Id of failed state + size_t failedStateId = 0; + + // Id of initial state + size_t initialStateIndex = 0; + + // Next state generator for exploring the state space + storm::generator::DftNextStateGenerator<ValueType, StateType> generator; + + // Structure for the components of the model. + ModelComponents modelComponents; + + // Structure for the transition matrix builder. + MatrixBuilder matrixBuilder; + + // Internal information about the states that were explored. + storm::storage::sparse::StateStorage<StateType> stateStorage; + + // A priority queue of states that still need to be explored. + storm::storage::BucketPriorityQueue<ValueType> explorationQueue; + + // A mapping of not yet explored states from the id to the tuple (state object, heuristic values). + std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> statesNotExplored; + + // Holds all skipped states which were not yet expanded. More concretely it is a mapping from matrix indices + // to the corresponding skipped states. + // Notice that we need an ordered map here to easily iterate in increasing order over state ids. + // TODO remove again + std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> skippedStates; + + // List of independent subtrees and the BEs contained in them. + std::vector<std::vector<size_t>> subtreeBEs; }; + } } diff --git a/src/storm-dft/builder/ExplicitDFTModelBuilderApprox.cpp b/src/storm-dft/builder/ExplicitDFTModelBuilderApprox.cpp deleted file mode 100644 index 0c7d69628..000000000 --- a/src/storm-dft/builder/ExplicitDFTModelBuilderApprox.cpp +++ /dev/null @@ -1,841 +0,0 @@ -#include "ExplicitDFTModelBuilderApprox.h" - -#include <map> - -#include "storm/models/sparse/MarkovAutomaton.h" -#include "storm/models/sparse/Ctmc.h" -#include "storm/utility/constants.h" -#include "storm/utility/vector.h" -#include "storm/utility/bitoperations.h" -#include "storm/exceptions/UnexpectedException.h" -#include "storm/settings/SettingsManager.h" -#include "storm/logic/AtomicLabelFormula.h" -#include "storm-dft/settings/modules/DFTSettings.h" - - -namespace storm { - namespace builder { - - template<typename ValueType, typename StateType> - ExplicitDFTModelBuilderApprox<ValueType, StateType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), markovianStates(), exitRates(), choiceLabeling() { - // Intentionally left empty. - } - - template<typename ValueType, typename StateType> - ExplicitDFTModelBuilderApprox<ValueType, StateType>::MatrixBuilder::MatrixBuilder(bool canHaveNondeterminism) : mappingOffset(0), stateRemapping(), currentRowGroup(0), currentRow(0), canHaveNondeterminism((canHaveNondeterminism)) { - // Create matrix builder - builder = storm::storage::SparseMatrixBuilder<ValueType>(0, 0, 0, false, canHaveNondeterminism, 0); - } - - template<typename ValueType, typename StateType> - ExplicitDFTModelBuilderApprox<ValueType, StateType>::LabelOptions::LabelOptions(std::vector<std::shared_ptr<const storm::logic::Formula>> properties, bool buildAllLabels) : buildFailLabel(true), buildFailSafeLabel(false), buildAllLabels(buildAllLabels) { - // Get necessary labels from properties - std::vector<std::shared_ptr<storm::logic::AtomicLabelFormula const>> atomicLabels; - for (auto property : properties) { - property->gatherAtomicLabelFormulas(atomicLabels); - } - // Set usage of necessary labels - for (auto atomic : atomicLabels) { - std::string label = atomic->getLabel(); - std::size_t foundIndex = label.find("_fail"); - if (foundIndex != std::string::npos) { - elementLabels.insert(label.substr(0, foundIndex)); - } else if (label.compare("failed") == 0) { - buildFailLabel = true; - } else if (label.compare("failsafe") == 0) { - buildFailSafeLabel = true; - } else { - STORM_LOG_WARN("Label '" << label << "' not known."); - } - } - } - - template<typename ValueType, typename StateType> - ExplicitDFTModelBuilderApprox<ValueType, StateType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : - dft(dft), - stateGenerationInfo(std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))), - enableDC(enableDC), - usedHeuristic(storm::settings::getModule<storm::settings::modules::DFTSettings>().getApproximationHeuristic()), - generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), - matrixBuilder(!generator.isDeterministicModel()), - stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), - // TODO Matthias: make choosable - //explorationQueue(dft.nrElements()+1, 0, 1) - explorationQueue(200, 0, 0.9) - { - // Intentionally left empty. - // TODO Matthias: remove again - usedHeuristic = storm::builder::ApproximationHeuristic::DEPTH; - - // Compute independent subtrees - if (dft.topLevelType() == storm::storage::DFTElementType::OR) { - // We only support this for approximation with top level element OR - for (auto const& child : dft.getGate(dft.getTopLevelIndex())->children()) { - // Consider all children of the top level gate - std::vector<size_t> isubdft; - if (child->nrParents() > 1 || child->hasOutgoingDependencies()) { - STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation."); - isubdft.clear(); - } else if (dft.isGate(child->id())) { - isubdft = dft.getGate(child->id())->independentSubDft(false); - } else { - STORM_LOG_ASSERT(dft.isBasicElement(child->id()), "Child is no BE."); - if(dft.getBasicElement(child->id())->hasIngoingDependencies()) { - STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation."); - isubdft.clear(); - } else { - isubdft = {child->id()}; - } - } - if(isubdft.empty()) { - subtreeBEs.clear(); - break; - } else { - // Add new independent subtree - std::vector<size_t> subtree; - for (size_t id : isubdft) { - if (dft.isBasicElement(id)) { - subtree.push_back(id); - } - } - subtreeBEs.push_back(subtree); - } - } - } - if (subtreeBEs.empty()) { - // Consider complete tree - std::vector<size_t> subtree; - for (size_t id = 0; id < dft.nrElements(); ++id) { - if (dft.isBasicElement(id)) { - subtree.push_back(id); - } - } - subtreeBEs.push_back(subtree); - } - - for (auto tree : subtreeBEs) { - std::stringstream stream; - stream << "Subtree: "; - for (size_t id : tree) { - stream << id << ", "; - } - STORM_LOG_TRACE(stream.str()); - } - } - - template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts, size_t iteration, double approximationThreshold) { - STORM_LOG_TRACE("Generating DFT state space"); - - if (iteration < 1) { - // Initialize - modelComponents.markovianStates = storm::storage::BitVector(INITIAL_BITVECTOR_SIZE); - - if(mergeFailedStates) { - // Introduce explicit fail state - storm::generator::StateBehavior<ValueType, StateType> behavior = generator.createMergeFailedState([this] (DFTStatePointer const& state) { - this->failedStateId = newIndex++; - matrixBuilder.stateRemapping.push_back(0); - return this->failedStateId; - } ); - - matrixBuilder.setRemapping(failedStateId); - STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); - matrixBuilder.newRowGroup(); - setMarkovian(behavior.begin()->isMarkovian()); - - // Now add self loop. - // TODO Matthias: maybe use general method. - STORM_LOG_ASSERT(behavior.getNumberOfChoices() == 1, "Wrong number of choices for failed state."); - STORM_LOG_ASSERT(behavior.begin()->size() == 1, "Wrong number of transitions for failed state."); - std::pair<StateType, ValueType> stateProbabilityPair = *(behavior.begin()->begin()); - STORM_LOG_ASSERT(stateProbabilityPair.first == failedStateId, "No self loop for failed state."); - STORM_LOG_ASSERT(storm::utility::isOne<ValueType>(stateProbabilityPair.second), "Probability for failed state != 1."); - matrixBuilder.addTransition(stateProbabilityPair.first, stateProbabilityPair.second); - matrixBuilder.finishRow(); - } - - // Build initial state - this->stateStorage.initialStateIndices = generator.getInitialStates(std::bind(&ExplicitDFTModelBuilderApprox::getOrAddStateIndex, this, std::placeholders::_1)); - STORM_LOG_ASSERT(stateStorage.initialStateIndices.size() == 1, "Only one initial state assumed."); - initialStateIndex = stateStorage.initialStateIndices[0]; - STORM_LOG_TRACE("Initial state: " << initialStateIndex); - // Initialize heuristic values for inital state - STORM_LOG_ASSERT(!statesNotExplored.at(initialStateIndex).second, "Heuristic for initial state is already initialized"); - ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(initialStateIndex); - heuristic->markExpand(); - statesNotExplored[initialStateIndex].second = heuristic; - explorationQueue.push(heuristic); - } else { - initializeNextIteration(); - } - - if (approximationThreshold > 0) { - switch (usedHeuristic) { - case storm::builder::ApproximationHeuristic::NONE: - // Do not change anything - approximationThreshold = dft.nrElements()+10; - break; - case storm::builder::ApproximationHeuristic::DEPTH: - approximationThreshold = iteration + 1; - break; - case storm::builder::ApproximationHeuristic::PROBABILITY: - case storm::builder::ApproximationHeuristic::BOUNDDIFFERENCE: - approximationThreshold = 10 * std::pow(2, iteration); - break; - } - } - exploreStateSpace(approximationThreshold); - - size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); - modelComponents.markovianStates.resize(stateSize); - modelComponents.deterministicModel = generator.isDeterministicModel(); - - // Fix the entries in the transition matrix according to the mapping of ids to row group indices - STORM_LOG_ASSERT(matrixBuilder.getRemapping(initialStateIndex) == initialStateIndex, "Initial state should not be remapped."); - // TODO Matthias: do not consider all rows? - STORM_LOG_TRACE("Remap matrix: " << matrixBuilder.stateRemapping << ", offset: " << matrixBuilder.mappingOffset); - matrixBuilder.remap(); - - STORM_LOG_TRACE("State remapping: " << matrixBuilder.stateRemapping); - STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); - STORM_LOG_DEBUG("Model has " << stateSize << " states"); - STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic")); - - // Build transition matrix - modelComponents.transitionMatrix = matrixBuilder.builder.build(stateSize, stateSize); - if (stateSize <= 15) { - STORM_LOG_TRACE("Transition matrix: " << std::endl << modelComponents.transitionMatrix); - } else { - STORM_LOG_TRACE("Transition matrix: too big to print"); - } - - buildLabeling(labelOpts); - } - - template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::initializeNextIteration() { - STORM_LOG_TRACE("Refining DFT state space"); - - // TODO Matthias: should be easier now as skipped states all are at the end of the matrix - // Push skipped states to explore queue - // TODO Matthias: remove - for (auto const& skippedState : skippedStates) { - statesNotExplored[skippedState.second.first->getId()] = skippedState.second; - explorationQueue.push(skippedState.second.second); - } - - // Initialize matrix builder again - // TODO Matthias: avoid copy - std::vector<uint_fast64_t> copyRemapping = matrixBuilder.stateRemapping; - matrixBuilder = MatrixBuilder(!generator.isDeterministicModel()); - matrixBuilder.stateRemapping = copyRemapping; - StateType nrStates = modelComponents.transitionMatrix.getRowGroupCount(); - STORM_LOG_ASSERT(nrStates == matrixBuilder.stateRemapping.size(), "No. of states does not coincide with mapping size."); - - // Start by creating a remapping from the old indices to the new indices - std::vector<StateType> indexRemapping = std::vector<StateType>(nrStates, 0); - auto iterSkipped = skippedStates.begin(); - size_t skippedBefore = 0; - for (size_t i = 0; i < indexRemapping.size(); ++i) { - while (iterSkipped != skippedStates.end() && iterSkipped->first <= i) { - STORM_LOG_ASSERT(iterSkipped->first < indexRemapping.size(), "Skipped is too high."); - ++skippedBefore; - ++iterSkipped; - } - indexRemapping[i] = i - skippedBefore; - } - - // Set remapping - size_t nrExpandedStates = nrStates - skippedBefore; - matrixBuilder.mappingOffset = nrStates; - STORM_LOG_TRACE("# expanded states: " << nrExpandedStates); - StateType skippedIndex = nrExpandedStates; - std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> skippedStatesNew; - for (size_t id = 0; id < matrixBuilder.stateRemapping.size(); ++id) { - StateType index = matrixBuilder.getRemapping(id); - auto itFind = skippedStates.find(index); - if (itFind != skippedStates.end()) { - // Set new mapping for skipped state - matrixBuilder.stateRemapping[id] = skippedIndex; - skippedStatesNew[skippedIndex] = itFind->second; - indexRemapping[index] = skippedIndex; - ++skippedIndex; - } else { - // Set new mapping for expanded state - matrixBuilder.stateRemapping[id] = indexRemapping[index]; - } - } - STORM_LOG_TRACE("New state remapping: " << matrixBuilder.stateRemapping); - std::stringstream ss; - ss << "Index remapping:" << std::endl; - for (auto tmp : indexRemapping) { - ss << tmp << " "; - } - STORM_LOG_TRACE(ss.str()); - - // Remap markovian states - storm::storage::BitVector markovianStatesNew = storm::storage::BitVector(modelComponents.markovianStates.size(), true); - // Iterate over all not set bits - modelComponents.markovianStates.complement(); - size_t index = modelComponents.markovianStates.getNextSetIndex(0); - while (index < modelComponents.markovianStates.size()) { - markovianStatesNew.set(indexRemapping[index], false); - index = modelComponents.markovianStates.getNextSetIndex(index+1); - } - STORM_LOG_ASSERT(modelComponents.markovianStates.size() - modelComponents.markovianStates.getNumberOfSetBits() == markovianStatesNew.getNumberOfSetBits(), "Remapping of markovian states is wrong."); - STORM_LOG_ASSERT(markovianStatesNew.size() == nrStates, "No. of states does not coincide with markovian size."); - modelComponents.markovianStates = markovianStatesNew; - - // Build submatrix for expanded states - // TODO Matthias: only use row groups when necessary - for (StateType oldRowGroup = 0; oldRowGroup < modelComponents.transitionMatrix.getRowGroupCount(); ++oldRowGroup) { - if (indexRemapping[oldRowGroup] < nrExpandedStates) { - // State is expanded -> copy to new matrix - matrixBuilder.newRowGroup(); - for (StateType oldRow = modelComponents.transitionMatrix.getRowGroupIndices()[oldRowGroup]; oldRow < modelComponents.transitionMatrix.getRowGroupIndices()[oldRowGroup+1]; ++oldRow) { - for (typename storm::storage::SparseMatrix<ValueType>::const_iterator itEntry = modelComponents.transitionMatrix.begin(oldRow); itEntry != modelComponents.transitionMatrix.end(oldRow); ++itEntry) { - auto itFind = skippedStates.find(itEntry->getColumn()); - if (itFind != skippedStates.end()) { - // Set id for skipped states as we remap it later - matrixBuilder.addTransition(matrixBuilder.mappingOffset + itFind->second.first->getId(), itEntry->getValue()); - } else { - // Set newly remapped index for expanded states - matrixBuilder.addTransition(indexRemapping[itEntry->getColumn()], itEntry->getValue()); - } - } - matrixBuilder.finishRow(); - } - } - } - - skippedStates = skippedStatesNew; - - STORM_LOG_ASSERT(matrixBuilder.getCurrentRowGroup() == nrExpandedStates, "Row group size does not match."); - skippedStates.clear(); - } - - template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::exploreStateSpace(double approximationThreshold) { - size_t nrExpandedStates = 0; - size_t nrSkippedStates = 0; - // TODO Matthias: do not empty queue every time but break before - while (!explorationQueue.empty()) { - // Get the first state in the queue - ExplorationHeuristicPointer currentExplorationHeuristic = explorationQueue.popTop(); - StateType currentId = currentExplorationHeuristic->getId(); - auto itFind = statesNotExplored.find(currentId); - STORM_LOG_ASSERT(itFind != statesNotExplored.end(), "Id " << currentId << " not found"); - DFTStatePointer currentState = itFind->second.first; - STORM_LOG_ASSERT(currentExplorationHeuristic == itFind->second.second, "Exploration heuristics do not match"); - STORM_LOG_ASSERT(currentState->getId() == currentId, "Ids do not match"); - // Remove it from the list of not explored states - statesNotExplored.erase(itFind); - STORM_LOG_ASSERT(stateStorage.stateToId.contains(currentState->status()), "State is not contained in state storage."); - STORM_LOG_ASSERT(stateStorage.stateToId.getValue(currentState->status()) == currentId, "Ids of states do not coincide."); - - // Get concrete state if necessary - if (currentState->isPseudoState()) { - // Create concrete state from pseudo state - currentState->construct(); - } - STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state."); - - // Remember that the current row group was actually filled with the transitions of a different state - matrixBuilder.setRemapping(currentId); - - matrixBuilder.newRowGroup(); - - // Try to explore the next state - generator.load(currentState); - - //if (approximationThreshold > 0.0 && nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) { - if (approximationThreshold > 0.0 && currentExplorationHeuristic->isSkip(approximationThreshold)) { - // Skip the current state - ++nrSkippedStates; - STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); - setMarkovian(true); - // Add transition to target state with temporary value 0 - // TODO Matthias: what to do when there is no unique target state? - matrixBuilder.addTransition(failedStateId, storm::utility::zero<ValueType>()); - // Remember skipped state - skippedStates[matrixBuilder.getCurrentRowGroup() - 1] = std::make_pair(currentState, currentExplorationHeuristic); - matrixBuilder.finishRow(); - } else { - // Explore the current state - ++nrExpandedStates; - storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(std::bind(&ExplicitDFTModelBuilderApprox::getOrAddStateIndex, this, std::placeholders::_1)); - STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); - setMarkovian(behavior.begin()->isMarkovian()); - - // Now add all choices. - for (auto const& choice : behavior) { - // Add the probabilistic behavior to the matrix. - for (auto const& stateProbabilityPair : choice) { - STORM_LOG_ASSERT(!storm::utility::isZero(stateProbabilityPair.second), "Probability zero."); - // Set transition to state id + offset. This helps in only remapping all previously skipped states. - matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second); - // Set heuristic values for reached states - auto iter = statesNotExplored.find(stateProbabilityPair.first); - if (iter != statesNotExplored.end()) { - // Update heuristic values - DFTStatePointer state = iter->second.first; - if (!iter->second.second) { - // Initialize heuristic values - ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateProbabilityPair.first, *currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass()); - iter->second.second = heuristic; - if (state->hasFailed(dft.getTopLevelIndex()) || state->isFailsafe(dft.getTopLevelIndex()) || state->nrFailableDependencies() > 0 || (state->nrFailableDependencies() == 0 && state->nrFailableBEs() == 0)) { - // Do not skip absorbing state or if reached by dependencies - iter->second.second->markExpand(); - } - if (usedHeuristic == storm::builder::ApproximationHeuristic::BOUNDDIFFERENCE) { - // Compute bounds for heuristic now - if (state->isPseudoState()) { - // Create concrete state from pseudo state - state->construct(); - } - STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state."); - - // Initialize bounds - // TODO Mathias: avoid hack - ValueType lowerBound = getLowerBound(state); - ValueType upperBound = getUpperBound(state); - heuristic->setBounds(lowerBound, upperBound); - } - - explorationQueue.push(heuristic); - } else if (!iter->second.second->isExpand()) { - double oldPriority = iter->second.second->getPriority(); - if (iter->second.second->updateHeuristicValues(*currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass())) { - // Update priority queue - explorationQueue.update(iter->second.second, oldPriority); - } - } - } - } - matrixBuilder.finishRow(); - } - } - } // end exploration - - STORM_LOG_INFO("Expanded " << nrExpandedStates << " states"); - STORM_LOG_INFO("Skipped " << nrSkippedStates << " states"); - STORM_LOG_ASSERT(nrSkippedStates == skippedStates.size(), "Nr skipped states is wrong"); - } - - template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildLabeling(LabelOptions const& labelOpts) { - // Build state labeling - modelComponents.stateLabeling = storm::models::sparse::StateLabeling(modelComponents.transitionMatrix.getRowGroupCount()); - // Initial state - modelComponents.stateLabeling.addLabel("init"); - STORM_LOG_ASSERT(matrixBuilder.getRemapping(initialStateIndex) == initialStateIndex, "Initial state should not be remapped."); - modelComponents.stateLabeling.addLabelToState("init", initialStateIndex); - // Label all states corresponding to their status (failed, failsafe, failed BE) - if(labelOpts.buildFailLabel) { - modelComponents.stateLabeling.addLabel("failed"); - } - if(labelOpts.buildFailSafeLabel) { - modelComponents.stateLabeling.addLabel("failsafe"); - } - - // Collect labels for all necessary elements - for (size_t id = 0; id < dft.nrElements(); ++id) { - std::shared_ptr<storage::DFTElement<ValueType> const> element = dft.getElement(id); - if (labelOpts.buildAllLabels || labelOpts.elementLabels.count(element->name()) > 0) { - modelComponents.stateLabeling.addLabel(element->name() + "_fail"); - } - } - - // Set labels to states - if (mergeFailedStates) { - modelComponents.stateLabeling.addLabelToState("failed", failedStateId); - } - for (auto const& stateIdPair : stateStorage.stateToId) { - storm::storage::BitVector state = stateIdPair.first; - size_t stateId = matrixBuilder.getRemapping(stateIdPair.second); - if (!mergeFailedStates && labelOpts.buildFailLabel && dft.hasFailed(state, *stateGenerationInfo)) { - modelComponents.stateLabeling.addLabelToState("failed", stateId); - } - if (labelOpts.buildFailSafeLabel && dft.isFailsafe(state, *stateGenerationInfo)) { - modelComponents.stateLabeling.addLabelToState("failsafe", stateId); - } - // Set fail status for each necessary element - for (size_t id = 0; id < dft.nrElements(); ++id) { - std::shared_ptr<storage::DFTElement<ValueType> const> element = dft.getElement(id); - if ((labelOpts.buildAllLabels || labelOpts.elementLabels.count(element->name()) > 0) && storm::storage::DFTState<ValueType>::hasFailed(state, stateGenerationInfo->getStateIndex(element->id()))) { - modelComponents.stateLabeling.addLabelToState(element->name() + "_fail", stateId); - } - } - } - - STORM_LOG_TRACE(modelComponents.stateLabeling); - } - - template<typename ValueType, typename StateType> - std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::getModel() { - STORM_LOG_ASSERT(skippedStates.size() == 0, "Concrete model has skipped states"); - return createModel(false); - } - - template<typename ValueType, typename StateType> - std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::getModelApproximation(bool lowerBound, bool expectedTime) { - if (expectedTime) { - // TODO Matthias: handle case with no skipped states - changeMatrixBound(modelComponents.transitionMatrix, lowerBound); - return createModel(true); - } else { - // Change model for probabilities - // TODO Matthias: make nicer - storm::storage::SparseMatrix<ValueType> matrix = modelComponents.transitionMatrix; - storm::models::sparse::StateLabeling labeling = modelComponents.stateLabeling; - if (lowerBound) { - // Set self loop for lower bound - for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { - auto matrixEntry = matrix.getRow(it->first, 0).begin(); - STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); - STORM_LOG_ASSERT(!it->second.first->isPseudoState(), "State is still pseudo state."); - matrixEntry->setValue(storm::utility::one<ValueType>()); - matrixEntry->setColumn(it->first); - } - } else { - // Make skipped states failed states for upper bound - storm::storage::BitVector failedStates = modelComponents.stateLabeling.getStates("failed"); - for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { - failedStates.set(it->first); - } - labeling.setStates("failed", failedStates); - } - - std::shared_ptr<storm::models::sparse::Model<ValueType>> model; - if (modelComponents.deterministicModel) { - model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(matrix), std::move(labeling)); - } else { - // Build MA - // Compute exit rates - // TODO Matthias: avoid computing multiple times - modelComponents.exitRates = std::vector<ValueType>(modelComponents.markovianStates.size()); - std::vector<typename storm::storage::SparseMatrix<ValueType>::index_type> indices = matrix.getRowGroupIndices(); - for (StateType stateIndex = 0; stateIndex < modelComponents.markovianStates.size(); ++stateIndex) { - if (modelComponents.markovianStates[stateIndex]) { - modelComponents.exitRates[stateIndex] = matrix.getRowSum(indices[stateIndex]); - } else { - modelComponents.exitRates[stateIndex] = storm::utility::zero<ValueType>(); - } - } - STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); - - storm::storage::sparse::ModelComponents<ValueType> maComponents(std::move(matrix), std::move(labeling)); - maComponents.rateTransitions = true; - maComponents.markovianStates = modelComponents.markovianStates; - maComponents.exitRates = modelComponents.exitRates; - std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(maComponents)); - if (ma->hasOnlyTrivialNondeterminism()) { - // Markov automaton can be converted into CTMC - // TODO Matthias: change components which were not moved accordingly - model = ma->convertToCTMC(); - } else { - model = ma; - } - - } - - if (model->getNumberOfStates() <= 15) { - STORM_LOG_TRACE("Transition matrix: " << std::endl << model->getTransitionMatrix()); - } else { - STORM_LOG_TRACE("Transition matrix: too big to print"); - } - return model; - } - } - - template<typename ValueType, typename StateType> - std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::createModel(bool copy) { - std::shared_ptr<storm::models::sparse::Model<ValueType>> model; - - if (modelComponents.deterministicModel) { - // Build CTMC - if (copy) { - model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(modelComponents.transitionMatrix, modelComponents.stateLabeling); - } else { - model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling)); - } - } else { - // Build MA - // Compute exit rates - // TODO Matthias: avoid computing multiple times - modelComponents.exitRates = std::vector<ValueType>(modelComponents.markovianStates.size()); - std::vector<typename storm::storage::SparseMatrix<ValueType>::index_type> indices = modelComponents.transitionMatrix.getRowGroupIndices(); - for (StateType stateIndex = 0; stateIndex < modelComponents.markovianStates.size(); ++stateIndex) { - if (modelComponents.markovianStates[stateIndex]) { - modelComponents.exitRates[stateIndex] = modelComponents.transitionMatrix.getRowSum(indices[stateIndex]); - } else { - modelComponents.exitRates[stateIndex] = storm::utility::zero<ValueType>(); - } - } - STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); - - std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma; - if (copy) { - storm::storage::sparse::ModelComponents<ValueType> maComponents(modelComponents.transitionMatrix, modelComponents.stateLabeling); - maComponents.rateTransitions = true; - maComponents.markovianStates = modelComponents.markovianStates; - maComponents.exitRates = modelComponents.exitRates; - std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(maComponents)); - } else { - storm::storage::sparse::ModelComponents<ValueType> maComponents(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling)); - maComponents.rateTransitions = true; - maComponents.markovianStates = std::move(modelComponents.markovianStates); - maComponents.exitRates = std::move(modelComponents.exitRates); - std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(maComponents)); - } - if (ma->hasOnlyTrivialNondeterminism()) { - // Markov automaton can be converted into CTMC - // TODO Matthias: change components which were not moved accordingly - model = ma->convertToCTMC(); - } else { - model = ma; - } - } - - if (model->getNumberOfStates() <= 15) { - STORM_LOG_TRACE("Transition matrix: " << std::endl << model->getTransitionMatrix()); - } else { - STORM_LOG_TRACE("Transition matrix: too big to print"); - } - return model; - } - - template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::changeMatrixBound(storm::storage::SparseMatrix<ValueType> & matrix, bool lowerBound) const { - // Set lower bound for skipped states - for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { - auto matrixEntry = matrix.getRow(it->first, 0).begin(); - STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); - STORM_LOG_ASSERT(!it->second.first->isPseudoState(), "State is still pseudo state."); - - ExplorationHeuristicPointer heuristic = it->second.second; - if (storm::utility::isZero(heuristic->getUpperBound())) { - // Initialize bounds - ValueType lowerBound = getLowerBound(it->second.first); - ValueType upperBound = getUpperBound(it->second.first); - heuristic->setBounds(lowerBound, upperBound); - } - - // Change bound - if (lowerBound) { - matrixEntry->setValue(it->second.second->getLowerBound()); - } else { - matrixEntry->setValue(it->second.second->getUpperBound()); - } - } - } - - template<typename ValueType, typename StateType> - ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getLowerBound(DFTStatePointer const& state) const { - // Get the lower bound by considering the failure of all possible BEs - ValueType lowerBound = storm::utility::zero<ValueType>(); - for (size_t index = 0; index < state->nrFailableBEs(); ++index) { - lowerBound += state->getFailableBERate(index); - } - return lowerBound; - } - - template<typename ValueType, typename StateType> - ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getUpperBound(DFTStatePointer const& state) const { - if (state->hasFailed(dft.getTopLevelIndex())) { - return storm::utility::zero<ValueType>(); - } - - // Get the upper bound by considering the failure of all BEs - ValueType upperBound = storm::utility::one<ValueType>(); - ValueType rateSum = storm::utility::zero<ValueType>(); - - // Compute for each independent subtree - for (std::vector<size_t> const& subtree : subtreeBEs) { - // Get all possible rates - std::vector<ValueType> rates; - storm::storage::BitVector coldBEs(subtree.size(), false); - for (size_t i = 0; i < subtree.size(); ++i) { - size_t id = subtree[i]; - if (state->isOperational(id)) { - // Get BE rate - ValueType rate = state->getBERate(id); - if (storm::utility::isZero<ValueType>(rate)) { - // Get active failure rate for cold BE - rate = dft.getBasicElement(id)->activeFailureRate(); - if (storm::utility::isZero<ValueType>(rate)) { - // Ignore BE which cannot fail - continue; - } - // Mark BE as cold - coldBEs.set(i, true); - } - rates.push_back(rate); - rateSum += rate; - } - } - - STORM_LOG_ASSERT(rates.size() > 0, "No rates failable"); - - // Sort rates - std::sort(rates.begin(), rates.end()); - std::vector<size_t> rateCount(rates.size(), 0); - size_t lastIndex = 0; - for (size_t i = 0; i < rates.size(); ++i) { - if (rates[lastIndex] != rates[i]) { - lastIndex = i; - } - ++rateCount[lastIndex]; - } - - for (size_t i = 0; i < rates.size(); ++i) { - // Cold BEs cannot fail in the first step - if (!coldBEs.get(i) && rateCount[i] > 0) { - std::iter_swap(rates.begin() + i, rates.end() - 1); - // Compute AND MTTF of subtree without current rate and scale with current rate - upperBound += rates.back() * rateCount[i] * computeMTTFAnd(rates, rates.size() - 1); - // Swap back - // TODO try to avoid swapping back - std::iter_swap(rates.begin() + i, rates.end() - 1); - } - } - } - - STORM_LOG_TRACE("Upper bound is " << (rateSum / upperBound) << " for state " << state->getId()); - STORM_LOG_ASSERT(!storm::utility::isZero(upperBound), "Upper bound is 0"); - STORM_LOG_ASSERT(!storm::utility::isZero(rateSum), "State is absorbing"); - return rateSum / upperBound; - } - - template<typename ValueType, typename StateType> - ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::computeMTTFAnd(std::vector<ValueType> const& rates, size_t size) const { - ValueType result = storm::utility::zero<ValueType>(); - if (size == 0) { - return result; - } - - // TODO Matthias: works only for <64 BEs! - // WARNING: this code produces wrong results for more than 32 BEs - /*for (size_t i = 1; i < 4 && i <= rates.size(); ++i) { - size_t permutation = smallestIntWithNBitsSet(static_cast<size_t>(i)); - ValueType sum = storm::utility::zero<ValueType>(); - do { - ValueType permResult = storm::utility::zero<ValueType>(); - for(size_t j = 0; j < rates.size(); ++j) { - if(permutation & static_cast<size_t>(1 << static_cast<size_t>(j))) { - // WARNING: if the first bit is set, it also recognizes the 32nd bit as set - // TODO: fix - permResult += rates[j]; - } - } - permutation = nextBitPermutation(permutation); - STORM_LOG_ASSERT(!storm::utility::isZero(permResult), "PermResult is 0"); - sum += storm::utility::one<ValueType>() / permResult; - } while(permutation < (static_cast<size_t>(1) << rates.size()) && permutation != 0); - if (i % 2 == 0) { - result -= sum; - } else { - result += sum; - } - }*/ - - // Compute result with permutations of size <= 3 - ValueType one = storm::utility::one<ValueType>(); - for (size_t i1 = 0; i1 < size; ++i1) { - // + 1/a - ValueType sum = rates[i1]; - result += one / sum; - for (size_t i2 = 0; i2 < i1; ++i2) { - // - 1/(a+b) - ValueType sum2 = sum + rates[i2]; - result -= one / sum2; - for (size_t i3 = 0; i3 < i2; ++i3) { - // + 1/(a+b+c) - result += one / (sum2 + rates[i3]); - } - } - } - - STORM_LOG_ASSERT(!storm::utility::isZero(result), "UpperBound is 0"); - return result; - } - - template<typename ValueType, typename StateType> - StateType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getOrAddStateIndex(DFTStatePointer const& state) { - StateType stateId; - bool changed = false; - - if (stateGenerationInfo->hasSymmetries()) { - // Order state by symmetry - STORM_LOG_TRACE("Check for symmetry: " << dft.getStateString(state)); - changed = state->orderBySymmetry(); - STORM_LOG_TRACE("State " << (changed ? "changed to " : "did not change") << (changed ? dft.getStateString(state) : "")); - } - - if (stateStorage.stateToId.contains(state->status())) { - // State already exists - stateId = stateStorage.stateToId.getValue(state->status()); - STORM_LOG_TRACE("State " << dft.getStateString(state) << " with id " << stateId << " already exists"); - if (!changed) { - // Check if state is pseudo state - // If state is explored already the possible pseudo state was already constructed - auto iter = statesNotExplored.find(stateId); - if (iter != statesNotExplored.end() && iter->second.first->isPseudoState()) { - // Create pseudo state now - STORM_LOG_ASSERT(iter->second.first->getId() == stateId, "Ids do not match."); - STORM_LOG_ASSERT(iter->second.first->status() == state->status(), "Pseudo states do not coincide."); - state->setId(stateId); - // Update mapping to map to concrete state now - // TODO Matthias: just change pointer? - statesNotExplored[stateId] = std::make_pair(state, iter->second.second); - // We do not push the new state on the exploration queue as the pseudo state was already pushed - STORM_LOG_TRACE("Created pseudo state " << dft.getStateString(state)); - } - } - } else { - // State does not exist yet - STORM_LOG_ASSERT(state->isPseudoState() == changed, "State type (pseudo/concrete) wrong."); - // Create new state - state->setId(newIndex++); - stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); - STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); - // Insert state as not yet explored - ExplorationHeuristicPointer nullHeuristic; - statesNotExplored[stateId] = std::make_pair(state, nullHeuristic); - // Reserve one slot for the new state in the remapping - matrixBuilder.stateRemapping.push_back(0); - STORM_LOG_TRACE("New " << (state->isPseudoState() ? "pseudo" : "concrete") << " state: " << dft.getStateString(state)); - } - return stateId; - } - - template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::setMarkovian(bool markovian) { - if (matrixBuilder.getCurrentRowGroup() > modelComponents.markovianStates.size()) { - // Resize BitVector - modelComponents.markovianStates.resize(modelComponents.markovianStates.size() + INITIAL_BITVECTOR_SIZE); - } - modelComponents.markovianStates.set(matrixBuilder.getCurrentRowGroup() - 1, markovian); - } - - template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::printNotExplored() const { - std::cout << "states not explored:" << std::endl; - for (auto it : statesNotExplored) { - std::cout << it.first << " -> " << dft.getStateString(it.second.first) << std::endl; - } - } - - - // Explicitly instantiate the class. - template class ExplicitDFTModelBuilderApprox<double>; - -#ifdef STORM_HAVE_CARL - template class ExplicitDFTModelBuilderApprox<storm::RationalFunction>; -#endif - - } // namespace builder -} // namespace storm - - diff --git a/src/storm-dft/builder/ExplicitDFTModelBuilderApprox.h b/src/storm-dft/builder/ExplicitDFTModelBuilderApprox.h deleted file mode 100644 index 802aefe1b..000000000 --- a/src/storm-dft/builder/ExplicitDFTModelBuilderApprox.h +++ /dev/null @@ -1,362 +0,0 @@ -#pragma once - -#include <boost/container/flat_set.hpp> -#include <boost/optional/optional.hpp> -#include <stack> -#include <unordered_set> -#include <limits> - -#include "storm/models/sparse/StateLabeling.h" -#include "storm/models/sparse/ChoiceLabeling.h" -#include "storm/models/sparse/StandardRewardModel.h" -#include "storm/models/sparse/Model.h" -#include "storm/storage/SparseMatrix.h" -#include "storm/storage/sparse/StateStorage.h" - -#include "storm-dft/builder/DftExplorationHeuristic.h" -#include "storm-dft/generator/DftNextStateGenerator.h" -#include "storm-dft/storage/dft/DFT.h" -#include "storm-dft/storage/dft/SymmetricUnits.h" -#include "storm-dft/storage/BucketPriorityQueue.h" - -namespace storm { - namespace builder { - - /*! - * Build a Markov chain from DFT. - */ - template<typename ValueType, typename StateType = uint32_t> - class ExplicitDFTModelBuilderApprox { - - using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; - // TODO Matthias: make choosable - using ExplorationHeuristic = DFTExplorationHeuristicDepth<ValueType>; - using ExplorationHeuristicPointer = std::shared_ptr<ExplorationHeuristic>; - - - // A structure holding the individual components of a model. - struct ModelComponents { - // Constructor - ModelComponents(); - - // The transition matrix. - storm::storage::SparseMatrix<ValueType> transitionMatrix; - - // The state labeling. - storm::models::sparse::StateLabeling stateLabeling; - - // The Markovian states. - storm::storage::BitVector markovianStates; - - // The exit rates. - std::vector<ValueType> exitRates; - - // A vector that stores a labeling for each choice. - boost::optional<storm::models::sparse::ChoiceLabeling> choiceLabeling; - - // A flag indicating if the model is deterministic. - bool deterministicModel; - }; - - // A class holding the information for building the transition matrix. - class MatrixBuilder { - public: - // Constructor - MatrixBuilder(bool canHaveNondeterminism); - - /*! - * Set a mapping from a state id to the index in the matrix. - * - * @param id Id of the state. - */ - void setRemapping(StateType id) { - STORM_LOG_ASSERT(id < stateRemapping.size(), "Invalid index for remapping."); - stateRemapping[id] = currentRowGroup; - } - - /*! - * Create a new row group if the model is nondeterministic. - */ - void newRowGroup() { - if (canHaveNondeterminism) { - builder.newRowGroup(currentRow); - } - ++currentRowGroup; - } - - /*! - * Add a transition from the current row. - * - * @param index Target index - * @param value Value of transition - */ - void addTransition(StateType index, ValueType value) { - builder.addNextValue(currentRow, index, value); - } - - /*! - * Finish the current row. - */ - void finishRow() { - ++currentRow; - } - - /*! - * Remap the columns in the matrix. - */ - void remap() { - builder.replaceColumns(stateRemapping, mappingOffset); - } - - /*! - * Get the current row group. - * - * @return The current row group. - */ - StateType getCurrentRowGroup() { - return currentRowGroup; - } - - /*! - * Get the remapped state for the given id. - * - * @param id State. - * - * @return Remapped index. - */ - StateType getRemapping(StateType id) { - STORM_LOG_ASSERT(id < stateRemapping.size(), "Invalid index for remapping."); - return stateRemapping[id]; - } - - // Matrix builder. - storm::storage::SparseMatrixBuilder<ValueType> builder; - - // Offset to distinguish states which will not be remapped anymore and those which will. - size_t mappingOffset; - - // A mapping from state ids to the row group indices in which they actually reside. - // TODO Matthias: avoid hack with fixed int type - std::vector<uint_fast64_t> stateRemapping; - - private: - - // Index of the current row group. - StateType currentRowGroup; - - // Index of the current row. - StateType currentRow; - - // Flag indicating if row groups are needed. - bool canHaveNondeterminism; - }; - - public: - // A structure holding the labeling options. - struct LabelOptions { - // Constructor - LabelOptions(std::vector<std::shared_ptr<storm::logic::Formula const>> properties, bool buildAllLabels = false); - - // Flag indicating if the general fail label should be included. - bool buildFailLabel; - - // Flag indicating if the general failsafe label should be included. - bool buildFailSafeLabel; - - // Flag indicating if all possible labels should be included. - bool buildAllLabels; - - // Set of element names whose fail label to include. - std::set<std::string> elementLabels; - }; - - /*! - * Constructor. - * - * @param dft DFT. - * @param symmetries Symmetries in the dft. - * @param enableDC Flag indicating if dont care propagation should be used. - */ - ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC); - - /*! - * Build model from DFT. - * - * @param labelOpts Options for labeling. - * @param iteration Current number of iteration. - * @param approximationThreshold Threshold determining when to skip exploring states. - */ - void buildModel(LabelOptions const& labelOpts, size_t iteration, double approximationThreshold = 0.0); - - /*! - * Get the built model. - * - * @return The model built from the DFT. - */ - std::shared_ptr<storm::models::sparse::Model<ValueType>> getModel(); - - /*! - * Get the built approximation model for either the lower or upper bound. - * - * @param lowerBound If true, the lower bound model is returned, else the upper bound model - * @param expectedTime If true, the bounds for expected time are computed, else the bounds for probabilities. - * - * @return The model built from the DFT. - */ - std::shared_ptr<storm::models::sparse::Model<ValueType>> getModelApproximation(bool lowerBound, bool expectedTime); - - private: - - /*! - * Explore state space of DFT. - * - * @param approximationThreshold Threshold to determine when to skip states. - */ - void exploreStateSpace(double approximationThreshold); - - /*! - * Initialize the matrix for a refinement iteration. - */ - void initializeNextIteration(); - - /*! - * Build the labeling. - * - * @param labelOpts Options for labeling. - */ - void buildLabeling(LabelOptions const& labelOpts); - - /*! - * Add a state to the explored states (if not already there). It also handles pseudo states. - * - * @param state The state to add. - * - * @return Id of state. - */ - StateType getOrAddStateIndex(DFTStatePointer const& state); - - /*! - * Set markovian flag for the current state. - * - * @param markovian Flag indicating if the state is markovian. - */ - void setMarkovian(bool markovian); - - /** - * Change matrix to reflect the lower or upper approximation bound. - * - * @param matrix Matrix to change. The change are reflected here. - * @param lowerBound Flag indicating if the lower bound should be used. Otherwise the upper bound is used. - */ - void changeMatrixBound(storm::storage::SparseMatrix<ValueType> & matrix, bool lowerBound) const; - - /*! - * Get lower bound approximation for state. - * - * @param state The state. - * - * @return Lower bound approximation. - */ - ValueType getLowerBound(DFTStatePointer const& state) const; - - /*! - * Get upper bound approximation for state. - * - * @param state The state. - * - * @return Upper bound approximation. - */ - ValueType getUpperBound(DFTStatePointer const& state) const; - - /*! - * Compute the MTTF of an AND gate via a closed formula. - * The used formula is 1/( 1/a + 1/b + 1/c + ... - 1/(a+b) - 1/(a+c) - ... + 1/(a+b+c) + ... - ...) - * - * @param rates List of rates of children of AND. - * @param size Only indices < size are considered in the vector. - * @return MTTF. - */ - ValueType computeMTTFAnd(std::vector<ValueType> const& rates, size_t size) const; - - /*! - * Compares the priority of two states. - * - * @param idA Id of first state - * @param idB Id of second state - * - * @return True if the priority of the first state is greater then the priority of the second one. - */ - bool isPriorityGreater(StateType idA, StateType idB) const; - - void printNotExplored() const; - - /*! - * Create the model model from the model components. - * - * @param copy If true, all elements of the model component are copied (used for approximation). If false - * they are moved to reduce the memory overhead. - * - * @return The model built from the model components. - */ - std::shared_ptr<storm::models::sparse::Model<ValueType>> createModel(bool copy); - - // Initial size of the bitvector. - const size_t INITIAL_BITVECTOR_SIZE = 20000; - // Offset used for pseudo states. - const StateType OFFSET_PSEUDO_STATE = std::numeric_limits<StateType>::max() / 2; - - // Dft - storm::storage::DFT<ValueType> const& dft; - - // General information for state generation - // TODO Matthias: use const reference - std::shared_ptr<storm::storage::DFTStateGenerationInfo> stateGenerationInfo; - - // Flag indication if dont care propagation should be used. - bool enableDC = true; - - //TODO Matthias: make changeable - const bool mergeFailedStates = true; - - // Heuristic used for approximation - storm::builder::ApproximationHeuristic usedHeuristic; - - // Current id for new state - size_t newIndex = 0; - - // Id of failed state - size_t failedStateId = 0; - - // Id of initial state - size_t initialStateIndex = 0; - - // Next state generator for exploring the state space - storm::generator::DftNextStateGenerator<ValueType, StateType> generator; - - // Structure for the components of the model. - ModelComponents modelComponents; - - // Structure for the transition matrix builder. - MatrixBuilder matrixBuilder; - - // Internal information about the states that were explored. - storm::storage::sparse::StateStorage<StateType> stateStorage; - - // A priority queue of states that still need to be explored. - storm::storage::BucketPriorityQueue<ValueType> explorationQueue; - - // A mapping of not yet explored states from the id to the tuple (state object, heuristic values). - std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> statesNotExplored; - - // Holds all skipped states which were not yet expanded. More concretely it is a mapping from matrix indices - // to the corresponding skipped states. - // Notice that we need an ordered map here to easily iterate in increasing order over state ids. - // TODO remove again - std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> skippedStates; - - // List of independent subtrees and the BEs contained in them. - std::vector<std::vector<size_t>> subtreeBEs; - }; - - } -} diff --git a/src/storm-dft/generator/DftNextStateGenerator.cpp b/src/storm-dft/generator/DftNextStateGenerator.cpp index 7b86d1faa..0002cd6a3 100644 --- a/src/storm-dft/generator/DftNextStateGenerator.cpp +++ b/src/storm-dft/generator/DftNextStateGenerator.cpp @@ -4,7 +4,7 @@ #include "storm/utility/macros.h" #include "storm/exceptions/NotImplementedException.h" #include "storm/settings/SettingsManager.h" -#include "storm-dft/settings/modules/DFTSettings.h" +#include "storm-dft/settings/modules/FaultTreeSettings.h" namespace storm { namespace generator { @@ -71,7 +71,7 @@ namespace storm { // Let BE fail while (currentFailable < failableCount) { - if (storm::settings::getModule<storm::settings::modules::DFTSettings>().isTakeFirstDependency() && hasDependencies && currentFailable > 0) { + if (storm::settings::getModule<storm::settings::modules::FaultTreeSettings>().isTakeFirstDependency() && hasDependencies && currentFailable > 0) { // We discard further exploration as we already chose one dependent event break; } diff --git a/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp b/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp index d6b7e9138..d2b435c1c 100644 --- a/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp @@ -6,9 +6,8 @@ #include "storm/utility/DirectEncodingExporter.h" #include "storm-dft/builder/ExplicitDFTModelBuilder.h" -#include "storm-dft/builder/ExplicitDFTModelBuilderApprox.h" #include "storm-dft/storage/dft/DFTIsomorphism.h" -#include "storm-dft/settings/modules/DFTSettings.h" +#include "storm-dft/settings/modules/FaultTreeSettings.h" namespace storm { @@ -192,8 +191,8 @@ namespace storm { // Build a single CTMC STORM_LOG_INFO("Building Model..."); - storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(ft, symmetries, enableDC); - typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions(properties); + storm::builder::ExplicitDFTModelBuilder<ValueType> builder(ft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilder<ValueType>::LabelOptions labeloptions(properties); builder.buildModel(labeloptions, 0, 0.0); std::shared_ptr<storm::models::sparse::Model<ValueType>> model = builder.getModel(); explorationTimer.stop(); @@ -244,9 +243,8 @@ namespace storm { // Build a single CTMC STORM_LOG_INFO("Building Model..."); - - storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); - typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions(properties); + storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilder<ValueType>::LabelOptions labeloptions(properties); builder.buildModel(labeloptions, 0, 0.0); std::shared_ptr<storm::models::sparse::Model<ValueType>> model = builder.getModel(); model->printModelInformationToStream(std::cout); @@ -277,8 +275,8 @@ namespace storm { approximation_result approxResult = std::make_pair(storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); std::shared_ptr<storm::models::sparse::Model<ValueType>> model; std::vector<ValueType> newResult; - storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); - typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions(properties); + storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilder<ValueType>::LabelOptions labeloptions(properties); // TODO Matthias: compute approximation for all properties simultaneously? std::shared_ptr<const storm::logic::Formula> property = properties[0]; @@ -342,18 +340,10 @@ namespace storm { } else { // Build a single Markov Automaton STORM_LOG_INFO("Building Model..."); - std::shared_ptr<storm::models::sparse::Model<ValueType>> model; - // TODO Matthias: use only one builder if everything works again - if (storm::settings::getModule<storm::settings::modules::DFTSettings>().isApproximationErrorSet()) { - storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); - typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions(properties, storm::settings::getModule<storm::settings::modules::IOSettings>().isExportExplicitSet()); - builder.buildModel(labeloptions, 0, 0.0); - model = builder.getModel(); - } else { - storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); - typename storm::builder::ExplicitDFTModelBuilder<ValueType>::LabelOptions labeloptions; - model = builder.buildModel(labeloptions); - } + storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilder<ValueType>::LabelOptions labeloptions(properties, storm::settings::getModule<storm::settings::modules::IOSettings>().isExportExplicitSet()); + builder.buildModel(labeloptions, 0, 0.0); + std::shared_ptr<storm::models::sparse::Model<ValueType>> model = builder.getModel(); model->printModelInformationToStream(std::cout); explorationTimer.stop(); diff --git a/src/storm-dft/settings/DftSettings.cpp b/src/storm-dft/settings/DftSettings.cpp new file mode 100644 index 000000000..ce48c6fd9 --- /dev/null +++ b/src/storm-dft/settings/DftSettings.cpp @@ -0,0 +1,51 @@ +#include "storm-dft/settings/DftSettings.h" + +#include "storm-dft/settings/modules/DftIOSettings.h" +#include "storm-dft/settings/modules/FaultTreeSettings.h" + +#include "storm/settings/SettingsManager.h" +#include "storm/settings/modules/GeneralSettings.h" +#include "storm/settings/modules/CoreSettings.h" +#include "storm/settings/modules/IOSettings.h" +#include "storm/settings/modules/DebugSettings.h" +#include "storm/settings/modules/EigenEquationSolverSettings.h" +#include "storm/settings/modules/GmmxxEquationSolverSettings.h" +#include "storm/settings/modules/NativeEquationSolverSettings.h" +#include "storm/settings/modules/EliminationSettings.h" +#include "storm/settings/modules/MinMaxEquationSolverSettings.h" +#include "storm/settings/modules/BisimulationSettings.h" +#include "storm/settings/modules/ResourceSettings.h" +#include "storm/settings/modules/JaniExportSettings.h" +#include "storm/settings/modules/GSPNSettings.h" +#include "storm/settings/modules/GSPNExportSettings.h" + + +namespace storm { + namespace settings { + void initializeDftSettings(std::string const& name, std::string const& executableName) { + storm::settings::mutableManager().setName(name, executableName); + + // Register relevant settings modules. + storm::settings::addModule<storm::settings::modules::GeneralSettings>(); + storm::settings::addModule<storm::settings::modules::DftIOSettings>(); + storm::settings::addModule<storm::settings::modules::FaultTreeSettings>(); + storm::settings::addModule<storm::settings::modules::IOSettings>(); + storm::settings::addModule<storm::settings::modules::CoreSettings>(); + + storm::settings::addModule<storm::settings::modules::DebugSettings>(); + storm::settings::addModule<storm::settings::modules::NativeEquationSolverSettings>(); + storm::settings::addModule<storm::settings::modules::GmmxxEquationSolverSettings>(); + storm::settings::addModule<storm::settings::modules::EigenEquationSolverSettings>(); + storm::settings::addModule<storm::settings::modules::EliminationSettings>(); + storm::settings::addModule<storm::settings::modules::MinMaxEquationSolverSettings>(); + // storm::settings::addModule<storm::settings::modules::BisimulationSettings>(); + storm::settings::addModule<storm::settings::modules::ResourceSettings>(); + + // For translation into JANI via GSPN. + storm::settings::addModule<storm::settings::modules::JaniExportSettings>(); + storm::settings::addModule<storm::settings::modules::GSPNSettings>(); + storm::settings::addModule<storm::settings::modules::GSPNExportSettings>(); + } + + } +} diff --git a/src/storm-dft/settings/DftSettings.h b/src/storm-dft/settings/DftSettings.h new file mode 100644 index 000000000..8f67ec0b7 --- /dev/null +++ b/src/storm-dft/settings/DftSettings.h @@ -0,0 +1,11 @@ +#pragma once + +#include <string> + +namespace storm { + namespace settings { + + void initializeDftSettings(std::string const& name, std::string const& executableName); + + } +} diff --git a/src/storm-dft/settings/modules/DFTSettings.cpp b/src/storm-dft/settings/modules/DFTSettings.cpp deleted file mode 100644 index 1a8d59d70..000000000 --- a/src/storm-dft/settings/modules/DFTSettings.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include "DFTSettings.h" - -#include "storm/settings/SettingsManager.h" -#include "storm/settings/SettingMemento.h" -#include "storm/settings/Option.h" -#include "storm/settings/OptionBuilder.h" -#include "storm/settings/ArgumentBuilder.h" -#include "storm/settings/Argument.h" - -#include "storm/exceptions/InvalidSettingsException.h" -#include "storm/exceptions/IllegalArgumentValueException.h" - -namespace storm { - namespace settings { - namespace modules { - - const std::string DFTSettings::moduleName = "dft"; - const std::string DFTSettings::dftFileOptionName = "dftfile"; - const std::string DFTSettings::dftFileOptionShortName = "dft"; - const std::string DFTSettings::dftJsonFileOptionName = "dftfile-json"; - const std::string DFTSettings::dftJsonFileOptionShortName = "dftjson"; - const std::string DFTSettings::symmetryReductionOptionName = "symmetryreduction"; - const std::string DFTSettings::symmetryReductionOptionShortName = "symred"; - const std::string DFTSettings::modularisationOptionName = "modularisation"; - const std::string DFTSettings::disableDCOptionName = "disabledc"; - const std::string DFTSettings::approximationErrorOptionName = "approximation"; - const std::string DFTSettings::approximationErrorOptionShortName = "approx"; - const std::string DFTSettings::approximationHeuristicOptionName = "approximationheuristic"; - const std::string DFTSettings::propExpectedTimeOptionName = "expectedtime"; - const std::string DFTSettings::propExpectedTimeOptionShortName = "mttf"; - const std::string DFTSettings::propProbabilityOptionName = "probability"; - const std::string DFTSettings::propTimeboundOptionName = "timebound"; - const std::string DFTSettings::propTimepointsOptionName = "timepoints"; - const std::string DFTSettings::minValueOptionName = "min"; - const std::string DFTSettings::maxValueOptionName = "max"; - const std::string DFTSettings::firstDependencyOptionName = "firstdep"; - const std::string DFTSettings::transformToGspnOptionName = "gspn"; - const std::string DFTSettings::exportToJsonOptionName = "export-json"; -#ifdef STORM_HAVE_Z3 - const std::string DFTSettings::solveWithSmtOptionName = "smt"; -#endif - - DFTSettings::DFTSettings() : ModuleSettings(moduleName) { - this->addOption(storm::settings::OptionBuilder(moduleName, dftFileOptionName, false, "Parses the model given in the Galileo format.").setShortName(dftFileOptionShortName) - .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the file from which to read the DFT model.").addValidatorString(ArgumentValidatorFactory::createExistingFileValidator()).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, dftJsonFileOptionName, false, "Parses the model given in the Cytoscape JSON format.").setShortName(dftJsonFileOptionShortName) - .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the JSON file from which to read the DFT model.").addValidatorString(ArgumentValidatorFactory::createExistingFileValidator()).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, symmetryReductionOptionName, false, "Exploit symmetric structure of model.").setShortName(symmetryReductionOptionShortName).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, modularisationOptionName, false, "Use modularisation (not applicable for expected time).").build()); - this->addOption(storm::settings::OptionBuilder(moduleName, disableDCOptionName, false, "Disable Dont Care propagation.").build()); - this->addOption(storm::settings::OptionBuilder(moduleName, firstDependencyOptionName, false, "Avoid non-determinism by always taking the first possible dependency.").build()); - this->addOption(storm::settings::OptionBuilder(moduleName, approximationErrorOptionName, false, "Approximation error allowed.").setShortName(approximationErrorOptionShortName).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("error", "The relative approximation error to use.").addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterEqualValidator(0.0)).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, approximationHeuristicOptionName, false, "Set the heuristic used for approximation.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("heuristic", "Sets which heuristic is used for approximation. Must be in {depth, probability}. Default is").setDefaultValueString("depth").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator({"depth", "rateratio"})).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, propExpectedTimeOptionName, false, "Compute expected time of system failure.").setShortName(propExpectedTimeOptionShortName).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, propProbabilityOptionName, false, "Compute probability of system failure.").build()); - this->addOption(storm::settings::OptionBuilder(moduleName, propTimeboundOptionName, false, "Compute probability of system failure up to given timebound.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("time", "The timebound to use.").addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterValidator(0.0)).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, propTimepointsOptionName, false, "Compute probability of system failure up to given timebound for a set of given timepoints [starttime, starttime+inc, starttime+2inc, ... ,endtime]").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("starttime", "The timebound to start from.").addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterEqualValidator(0.0)).build()).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("endtime", "The timebound to end with.").addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterEqualValidator(0.0)).build()).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("inc", "The value to increment with to get the next timepoint.").addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterEqualValidator(0.0)).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, minValueOptionName, false, "Compute minimal value in case of non-determinism.").build()); - this->addOption(storm::settings::OptionBuilder(moduleName, maxValueOptionName, false, "Compute maximal value in case of non-determinism.").build()); -#ifdef STORM_HAVE_Z3 - this->addOption(storm::settings::OptionBuilder(moduleName, solveWithSmtOptionName, true, "Solve the DFT with SMT.").build()); -#endif - this->addOption(storm::settings::OptionBuilder(moduleName, transformToGspnOptionName, false, "Transform DFT to GSPN.").build()); - this->addOption(storm::settings::OptionBuilder(moduleName, exportToJsonOptionName, false, "Export the model to the Cytoscape JSON format.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the JSON file to export to.").build()).build()); - } - - bool DFTSettings::isDftFileSet() const { - return this->getOption(dftFileOptionName).getHasOptionBeenSet(); - } - - std::string DFTSettings::getDftFilename() const { - return this->getOption(dftFileOptionName).getArgumentByName("filename").getValueAsString(); - } - - bool DFTSettings::isDftJsonFileSet() const { - return this->getOption(dftJsonFileOptionName).getHasOptionBeenSet(); - } - - std::string DFTSettings::getDftJsonFilename() const { - return this->getOption(dftJsonFileOptionName).getArgumentByName("filename").getValueAsString(); - } - - bool DFTSettings::useSymmetryReduction() const { - return this->getOption(symmetryReductionOptionName).getHasOptionBeenSet(); - } - - bool DFTSettings::useModularisation() const { - return this->getOption(modularisationOptionName).getHasOptionBeenSet(); - } - - bool DFTSettings::isDisableDC() const { - return this->getOption(disableDCOptionName).getHasOptionBeenSet(); - } - - bool DFTSettings::isApproximationErrorSet() const { - return this->getOption(approximationErrorOptionName).getHasOptionBeenSet(); - } - - double DFTSettings::getApproximationError() const { - return this->getOption(approximationErrorOptionName).getArgumentByName("error").getValueAsDouble(); - } - - storm::builder::ApproximationHeuristic DFTSettings::getApproximationHeuristic() const { - if (!isApproximationErrorSet() || getApproximationError() == 0.0) { - // No approximation is done - return storm::builder::ApproximationHeuristic::NONE; - } - std::string heuristicAsString = this->getOption(approximationHeuristicOptionName).getArgumentByName("heuristic").getValueAsString(); - if (heuristicAsString == "depth") { - return storm::builder::ApproximationHeuristic::DEPTH; - } else if (heuristicAsString == "probability") { - return storm::builder::ApproximationHeuristic::PROBABILITY; - } - STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Illegal value '" << heuristicAsString << "' set as heuristic for approximation."); - } - - bool DFTSettings::usePropExpectedTime() const { - return this->getOption(propExpectedTimeOptionName).getHasOptionBeenSet(); - } - - bool DFTSettings::usePropProbability() const { - return this->getOption(propProbabilityOptionName).getHasOptionBeenSet(); - } - - bool DFTSettings::usePropTimebound() const { - return this->getOption(propTimeboundOptionName).getHasOptionBeenSet(); - } - - double DFTSettings::getPropTimebound() const { - return this->getOption(propTimeboundOptionName).getArgumentByName("time").getValueAsDouble(); - } - - bool DFTSettings::usePropTimepoints() const { - return this->getOption(propTimepointsOptionName).getHasOptionBeenSet(); - } - - std::vector<double> DFTSettings::getPropTimepoints() const { - double starttime = this->getOption(propTimepointsOptionName).getArgumentByName("starttime").getValueAsDouble(); - double endtime = this->getOption(propTimepointsOptionName).getArgumentByName("endtime").getValueAsDouble(); - double inc = this->getOption(propTimepointsOptionName).getArgumentByName("inc").getValueAsDouble(); - std::vector<double> timepoints; - for (double time = starttime; time <= endtime; time += inc) { - timepoints.push_back(time); - } - return timepoints; - } - - bool DFTSettings::isComputeMinimalValue() const { - return this->getOption(minValueOptionName).getHasOptionBeenSet(); - } - - bool DFTSettings::isComputeMaximalValue() const { - return this->getOption(maxValueOptionName).getHasOptionBeenSet(); - } - - bool DFTSettings::isTakeFirstDependency() const { - return this->getOption(firstDependencyOptionName).getHasOptionBeenSet(); - } - -#ifdef STORM_HAVE_Z3 - bool DFTSettings::solveWithSMT() const { - return this->getOption(solveWithSmtOptionName).getHasOptionBeenSet(); - } -#endif - - bool DFTSettings::isTransformToGspn() const { - return this->getOption(transformToGspnOptionName).getHasOptionBeenSet(); - } - - bool DFTSettings::isExportToJson() const { - return this->getOption(exportToJsonOptionName).getHasOptionBeenSet(); - } - - std::string DFTSettings::getExportJsonFilename() const { - return this->getOption(exportToJsonOptionName).getArgumentByName("filename").getValueAsString(); - } - - void DFTSettings::finalize() { - } - - bool DFTSettings::check() const { - // Ensure that at most one of min or max is set - STORM_LOG_THROW(!isComputeMinimalValue() || !isComputeMaximalValue(), storm::exceptions::InvalidSettingsException, "Min and max can not both be set."); - return true; - } - - } // namespace modules - } // namespace settings -} // namespace storm diff --git a/src/storm-dft/settings/modules/DftIOSettings.cpp b/src/storm-dft/settings/modules/DftIOSettings.cpp new file mode 100644 index 000000000..cf656333d --- /dev/null +++ b/src/storm-dft/settings/modules/DftIOSettings.cpp @@ -0,0 +1,123 @@ +#include "DftIOSettings.h" + +#include "storm/settings/SettingsManager.h" +#include "storm/settings/SettingMemento.h" +#include "storm/settings/Option.h" +#include "storm/settings/OptionBuilder.h" +#include "storm/settings/ArgumentBuilder.h" +#include "storm/settings/Argument.h" +#include "storm/exceptions/InvalidSettingsException.h" + +namespace storm { + namespace settings { + namespace modules { + + const std::string DftIOSettings::moduleName = "dftIO"; + const std::string DftIOSettings::dftFileOptionName = "dftfile"; + const std::string DftIOSettings::dftFileOptionShortName = "dft"; + const std::string DftIOSettings::dftJsonFileOptionName = "dftfile-json"; + const std::string DftIOSettings::dftJsonFileOptionShortName = "dftjson"; + const std::string DftIOSettings::propExpectedTimeOptionName = "expectedtime"; + const std::string DftIOSettings::propExpectedTimeOptionShortName = "mttf"; + const std::string DftIOSettings::propProbabilityOptionName = "probability"; + const std::string DftIOSettings::propTimeboundOptionName = "timebound"; + const std::string DftIOSettings::propTimepointsOptionName = "timepoints"; + const std::string DftIOSettings::minValueOptionName = "min"; + const std::string DftIOSettings::maxValueOptionName = "max"; + const std::string DftIOSettings::transformToGspnOptionName = "gspn"; + const std::string DftIOSettings::exportToJsonOptionName = "export-json"; + + DftIOSettings::DftIOSettings() : ModuleSettings(moduleName) { + this->addOption(storm::settings::OptionBuilder(moduleName, dftFileOptionName, false, "Parses the model given in the Galileo format.").setShortName(dftFileOptionShortName) + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the file from which to read the DFT model.").addValidatorString(ArgumentValidatorFactory::createExistingFileValidator()).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, dftJsonFileOptionName, false, "Parses the model given in the Cytoscape JSON format.").setShortName(dftJsonFileOptionShortName) + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the JSON file from which to read the DFT model.").addValidatorString(ArgumentValidatorFactory::createExistingFileValidator()).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, propExpectedTimeOptionName, false, "Compute expected time of system failure.").setShortName(propExpectedTimeOptionShortName).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, propProbabilityOptionName, false, "Compute probability of system failure.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, propTimeboundOptionName, false, "Compute probability of system failure up to given timebound.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("time", "The timebound to use.").addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterValidator(0.0)).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, propTimepointsOptionName, false, "Compute probability of system failure up to given timebound for a set of given timepoints [starttime, starttime+inc, starttime+2inc, ... ,endtime]").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("starttime", "The timebound to start from.").addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterEqualValidator(0.0)).build()).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("endtime", "The timebound to end with.").addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterEqualValidator(0.0)).build()).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("inc", "The value to increment with to get the next timepoint.").addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterEqualValidator(0.0)).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, minValueOptionName, false, "Compute minimal value in case of non-determinism.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, maxValueOptionName, false, "Compute maximal value in case of non-determinism.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, transformToGspnOptionName, false, "Transform DFT to GSPN.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, exportToJsonOptionName, false, "Export the model to the Cytoscape JSON format.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the JSON file to export to.").build()).build()); + } + + bool DftIOSettings::isDftFileSet() const { + return this->getOption(dftFileOptionName).getHasOptionBeenSet(); + } + + std::string DftIOSettings::getDftFilename() const { + return this->getOption(dftFileOptionName).getArgumentByName("filename").getValueAsString(); + } + + bool DftIOSettings::isDftJsonFileSet() const { + return this->getOption(dftJsonFileOptionName).getHasOptionBeenSet(); + } + + std::string DftIOSettings::getDftJsonFilename() const { + return this->getOption(dftJsonFileOptionName).getArgumentByName("filename").getValueAsString(); + } + + bool DftIOSettings::usePropExpectedTime() const { + return this->getOption(propExpectedTimeOptionName).getHasOptionBeenSet(); + } + + bool DftIOSettings::usePropProbability() const { + return this->getOption(propProbabilityOptionName).getHasOptionBeenSet(); + } + + bool DftIOSettings::usePropTimebound() const { + return this->getOption(propTimeboundOptionName).getHasOptionBeenSet(); + } + + double DftIOSettings::getPropTimebound() const { + return this->getOption(propTimeboundOptionName).getArgumentByName("time").getValueAsDouble(); + } + + bool DftIOSettings::usePropTimepoints() const { + return this->getOption(propTimepointsOptionName).getHasOptionBeenSet(); + } + + std::vector<double> DftIOSettings::getPropTimepoints() const { + double starttime = this->getOption(propTimepointsOptionName).getArgumentByName("starttime").getValueAsDouble(); + double endtime = this->getOption(propTimepointsOptionName).getArgumentByName("endtime").getValueAsDouble(); + double inc = this->getOption(propTimepointsOptionName).getArgumentByName("inc").getValueAsDouble(); + std::vector<double> timepoints; + for (double time = starttime; time <= endtime; time += inc) { + timepoints.push_back(time); + } + return timepoints; + } + + bool DftIOSettings::isComputeMinimalValue() const { + return this->getOption(minValueOptionName).getHasOptionBeenSet(); + } + + bool DftIOSettings::isComputeMaximalValue() const { + return this->getOption(maxValueOptionName).getHasOptionBeenSet(); + } + + bool DftIOSettings::isTransformToGspn() const { + return this->getOption(transformToGspnOptionName).getHasOptionBeenSet(); + } + + bool DftIOSettings::isExportToJson() const { + return this->getOption(exportToJsonOptionName).getHasOptionBeenSet(); + } + + std::string DftIOSettings::getExportJsonFilename() const { + return this->getOption(exportToJsonOptionName).getArgumentByName("filename").getValueAsString(); + } + + void DftIOSettings::finalize() { + } + + bool DftIOSettings::check() const { + // Ensure that at most one of min or max is set + STORM_LOG_THROW(!isComputeMinimalValue() || !isComputeMaximalValue(), storm::exceptions::InvalidSettingsException, "Min and max can not both be set."); + return true; + } + + } // namespace modules + } // namespace settings +} // namespace storm diff --git a/src/storm-dft/settings/modules/DFTSettings.h b/src/storm-dft/settings/modules/DftIOSettings.h similarity index 63% rename from src/storm-dft/settings/modules/DFTSettings.h rename to src/storm-dft/settings/modules/DftIOSettings.h index c04ef1a23..8a8fbcdd1 100644 --- a/src/storm-dft/settings/modules/DFTSettings.h +++ b/src/storm-dft/settings/modules/DftIOSettings.h @@ -1,24 +1,21 @@ #pragma once -#include "storm-config.h" #include "storm/settings/modules/ModuleSettings.h" -#include "storm-dft/builder/DftExplorationHeuristic.h" - namespace storm { namespace settings { namespace modules { /*! - * This class represents the settings for DFT model checking. + * This class represents the settings for IO operations concerning DFTs. */ - class DFTSettings : public ModuleSettings { + class DftIOSettings : public ModuleSettings { public: /*! - * Creates a new set of DFT settings. + * Creates a new set of IO settings for DFTs. */ - DFTSettings(); + DftIOSettings(); /*! * Retrieves whether the dft file option was set. @@ -48,48 +45,6 @@ namespace storm { */ std::string getDftJsonFilename() const; - /*! - * Retrieves whether the option to use symmetry reduction is set. - * - * @return True iff the option was set. - */ - bool useSymmetryReduction() const; - - /*! - * Retrieves whether the option to use modularisation is set. - * - * @return True iff the option was set. - */ - bool useModularisation() const; - - /*! - * Retrieves whether the option to disable Dont Care propagation is set. - * - * @return True iff the option was set. - */ - bool isDisableDC() const; - - /*! - * Retrieves whether the option to compute an approximation is set. - * - * @return True iff the option was set. - */ - bool isApproximationErrorSet() const; - - /*! - * Retrieves the relative error allowed for approximating the model checking result. - * - * @return The allowed errorbound. - */ - double getApproximationError() const; - - /*! - * Retrieves the heuristic used for approximation. - * - * @return The heuristic to use. - */ - storm::builder::ApproximationHeuristic getApproximationHeuristic() const; - /*! * Retrieves whether the property expected time should be used. * @@ -146,13 +101,6 @@ namespace storm { */ bool isComputeMaximalValue() const; - /*! - * Retrieves whether the non-determinism should be avoided by always taking the first possible dependency. - * - * @return True iff the option was set. - */ - bool isTakeFirstDependency() const; - /*! * Retrieves whether the DFT should be transformed into a GSPN. * @@ -174,15 +122,6 @@ namespace storm { */ std::string getExportJsonFilename() const; -#ifdef STORM_HAVE_Z3 - /*! - * Retrieves whether the DFT should be checked via SMT. - * - * @return True iff the option was set. - */ - bool solveWithSMT() const; -#endif - bool check() const override; void finalize() override; @@ -195,13 +134,6 @@ namespace storm { static const std::string dftFileOptionShortName; static const std::string dftJsonFileOptionName; static const std::string dftJsonFileOptionShortName; - static const std::string symmetryReductionOptionName; - static const std::string symmetryReductionOptionShortName; - static const std::string modularisationOptionName; - static const std::string disableDCOptionName; - static const std::string approximationErrorOptionName; - static const std::string approximationErrorOptionShortName; - static const std::string approximationHeuristicOptionName; static const std::string propExpectedTimeOptionName; static const std::string propExpectedTimeOptionShortName; static const std::string propProbabilityOptionName; @@ -209,10 +141,6 @@ namespace storm { static const std::string propTimepointsOptionName; static const std::string minValueOptionName; static const std::string maxValueOptionName; - static const std::string firstDependencyOptionName; -#ifdef STORM_HAVE_Z3 - static const std::string solveWithSmtOptionName; -#endif static const std::string transformToGspnOptionName; static const std::string exportToJsonOptionName; diff --git a/src/storm-dft/settings/modules/FaultTreeSettings.cpp b/src/storm-dft/settings/modules/FaultTreeSettings.cpp new file mode 100644 index 000000000..7c9ef14af --- /dev/null +++ b/src/storm-dft/settings/modules/FaultTreeSettings.cpp @@ -0,0 +1,93 @@ +#include "FaultTreeSettings.h" + +#include "storm/settings/SettingsManager.h" +#include "storm/settings/SettingMemento.h" +#include "storm/settings/Option.h" +#include "storm/settings/OptionBuilder.h" +#include "storm/settings/ArgumentBuilder.h" +#include "storm/settings/Argument.h" +#include "storm/exceptions/IllegalArgumentValueException.h" + +namespace storm { + namespace settings { + namespace modules { + + const std::string FaultTreeSettings::moduleName = "dft"; + const std::string FaultTreeSettings::symmetryReductionOptionName = "symmetryreduction"; + const std::string FaultTreeSettings::symmetryReductionOptionShortName = "symred"; + const std::string FaultTreeSettings::modularisationOptionName = "modularisation"; + const std::string FaultTreeSettings::disableDCOptionName = "disabledc"; + const std::string FaultTreeSettings::approximationErrorOptionName = "approximation"; + const std::string FaultTreeSettings::approximationErrorOptionShortName = "approx"; + const std::string FaultTreeSettings::approximationHeuristicOptionName = "approximationheuristic"; + const std::string FaultTreeSettings::firstDependencyOptionName = "firstdep"; +#ifdef STORM_HAVE_Z3 + const std::string FaultTreeSettings::solveWithSmtOptionName = "smt"; +#endif + + FaultTreeSettings::FaultTreeSettings() : ModuleSettings(moduleName) { + this->addOption(storm::settings::OptionBuilder(moduleName, symmetryReductionOptionName, false, "Exploit symmetric structure of model.").setShortName(symmetryReductionOptionShortName).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, modularisationOptionName, false, "Use modularisation (not applicable for expected time).").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, disableDCOptionName, false, "Disable Dont Care propagation.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, firstDependencyOptionName, false, "Avoid non-determinism by always taking the first possible dependency.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, approximationErrorOptionName, false, "Approximation error allowed.").setShortName(approximationErrorOptionShortName).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("error", "The relative approximation error to use.").addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterEqualValidator(0.0)).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, approximationHeuristicOptionName, false, "Set the heuristic used for approximation.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("heuristic", "Sets which heuristic is used for approximation. Must be in {depth, probability}. Default is").setDefaultValueString("depth").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator({"depth", "rateratio"})).build()).build()); +#ifdef STORM_HAVE_Z3 + this->addOption(storm::settings::OptionBuilder(moduleName, solveWithSmtOptionName, true, "Solve the DFT with SMT.").build()); +#endif + } + + bool FaultTreeSettings::useSymmetryReduction() const { + return this->getOption(symmetryReductionOptionName).getHasOptionBeenSet(); + } + + bool FaultTreeSettings::useModularisation() const { + return this->getOption(modularisationOptionName).getHasOptionBeenSet(); + } + + bool FaultTreeSettings::isDisableDC() const { + return this->getOption(disableDCOptionName).getHasOptionBeenSet(); + } + + bool FaultTreeSettings::isApproximationErrorSet() const { + return this->getOption(approximationErrorOptionName).getHasOptionBeenSet(); + } + + double FaultTreeSettings::getApproximationError() const { + return this->getOption(approximationErrorOptionName).getArgumentByName("error").getValueAsDouble(); + } + + storm::builder::ApproximationHeuristic FaultTreeSettings::getApproximationHeuristic() const { + if (!isApproximationErrorSet() || getApproximationError() == 0.0) { + // No approximation is done + return storm::builder::ApproximationHeuristic::NONE; + } + std::string heuristicAsString = this->getOption(approximationHeuristicOptionName).getArgumentByName("heuristic").getValueAsString(); + if (heuristicAsString == "depth") { + return storm::builder::ApproximationHeuristic::DEPTH; + } else if (heuristicAsString == "probability") { + return storm::builder::ApproximationHeuristic::PROBABILITY; + } + STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Illegal value '" << heuristicAsString << "' set as heuristic for approximation."); + } + + bool FaultTreeSettings::isTakeFirstDependency() const { + return this->getOption(firstDependencyOptionName).getHasOptionBeenSet(); + } + +#ifdef STORM_HAVE_Z3 + bool FaultTreeSettings::solveWithSMT() const { + return this->getOption(solveWithSmtOptionName).getHasOptionBeenSet(); + } +#endif + + void FaultTreeSettings::finalize() { + } + + bool FaultTreeSettings::check() const { + return true; + } + + } // namespace modules + } // namespace settings +} // namespace storm diff --git a/src/storm-dft/settings/modules/FaultTreeSettings.h b/src/storm-dft/settings/modules/FaultTreeSettings.h new file mode 100644 index 000000000..171d5c38e --- /dev/null +++ b/src/storm-dft/settings/modules/FaultTreeSettings.h @@ -0,0 +1,104 @@ +#pragma once + +#include "storm/settings/modules/ModuleSettings.h" +#include "storm-dft/builder/DftExplorationHeuristic.h" +#include "storm-config.h" + +namespace storm { + namespace settings { + namespace modules { + + /*! + * This class represents the settings for DFT model checking. + */ + class FaultTreeSettings : public ModuleSettings { + public: + + /*! + * Creates a new set of DFT settings. + */ + FaultTreeSettings(); + + /*! + * Retrieves whether the option to use symmetry reduction is set. + * + * @return True iff the option was set. + */ + bool useSymmetryReduction() const; + + /*! + * Retrieves whether the option to use modularisation is set. + * + * @return True iff the option was set. + */ + bool useModularisation() const; + + /*! + * Retrieves whether the option to disable Dont Care propagation is set. + * + * @return True iff the option was set. + */ + bool isDisableDC() const; + + /*! + * Retrieves whether the option to compute an approximation is set. + * + * @return True iff the option was set. + */ + bool isApproximationErrorSet() const; + + /*! + * Retrieves the relative error allowed for approximating the model checking result. + * + * @return The allowed errorbound. + */ + double getApproximationError() const; + + /*! + * Retrieves the heuristic used for approximation. + * + * @return The heuristic to use. + */ + storm::builder::ApproximationHeuristic getApproximationHeuristic() const; + + /*! + * Retrieves whether the non-determinism should be avoided by always taking the first possible dependency. + * + * @return True iff the option was set. + */ + bool isTakeFirstDependency() const; + +#ifdef STORM_HAVE_Z3 + /*! + * Retrieves whether the DFT should be checked via SMT. + * + * @return True iff the option was set. + */ + bool solveWithSMT() const; +#endif + + bool check() const override; + void finalize() override; + + // The name of the module. + static const std::string moduleName; + + private: + // Define the string names of the options as constants. + static const std::string symmetryReductionOptionName; + static const std::string symmetryReductionOptionShortName; + static const std::string modularisationOptionName; + static const std::string disableDCOptionName; + static const std::string approximationErrorOptionName; + static const std::string approximationErrorOptionShortName; + static const std::string approximationHeuristicOptionName; + static const std::string firstDependencyOptionName; +#ifdef STORM_HAVE_Z3 + static const std::string solveWithSmtOptionName; +#endif + + }; + + } // namespace modules + } // namespace settings +} // namespace storm diff --git a/src/storm-pars-cli/CMakeLists.txt b/src/storm-pars-cli/CMakeLists.txt index bda5ad6c2..9e2f6f315 100644 --- a/src/storm-pars-cli/CMakeLists.txt +++ b/src/storm-pars-cli/CMakeLists.txt @@ -1,6 +1,6 @@ # Create storm-pars. add_executable(storm-pars-cli ${PROJECT_SOURCE_DIR}/src/storm-pars-cli/storm-pars.cpp) -target_link_libraries(storm-pars-cli storm-pars) # Adding headers for xcode +target_link_libraries(storm-pars-cli storm-pars storm-cli-utilities) # Adding headers for xcode set_target_properties(storm-pars-cli PROPERTIES OUTPUT_NAME "storm-pars") add_dependencies(binaries storm-pars-cli) diff --git a/src/storm-pars-cli/storm-pars.cpp b/src/storm-pars-cli/storm-pars.cpp index 5fa744c59..907688ef4 100644 --- a/src/storm-pars-cli/storm-pars.cpp +++ b/src/storm-pars-cli/storm-pars.cpp @@ -7,6 +7,7 @@ #include "storm/settings/SettingsManager.h" #include "storm/api/storm.h" #include "storm-cli-utilities/cli.h" +#include "storm-cli-utilities/model-handling.h" #include "storm/models/ModelBase.h" #include "storm/storage/SymbolicModelDescription.h" #include "storm/utility/file.h" @@ -23,8 +24,6 @@ #include "storm/exceptions/InvalidSettingsException.h" #include "storm/exceptions/NotSupportedException.h" -#include "storm-cli-utilities/cli.cpp" - namespace storm { namespace pars { diff --git a/src/storm-pars/modelchecker/region/RegionModelChecker.cpp b/src/storm-pars/modelchecker/region/RegionModelChecker.cpp index 23ba29489..ab0142d0b 100644 --- a/src/storm-pars/modelchecker/region/RegionModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/RegionModelChecker.cpp @@ -17,6 +17,7 @@ #include "storm/settings/SettingsManager.h" #include "storm/settings/modules/CoreSettings.h" +#include "storm/exceptions/NotImplementedException.h" namespace storm { @@ -43,6 +44,12 @@ namespace storm { return std::make_unique<storm::modelchecker::RegionCheckResult<ParametricType>>(std::move(result)); } + template <typename ParametricType> + ParametricType RegionModelChecker<ParametricType>::getBoundAtInitState(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForParameters) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "The selected region model checker does not support this functionality."); + return storm::utility::zero<ParametricType>(); + } + template <typename ParametricType> std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ParametricType>> RegionModelChecker<ParametricType>::performRegionRefinement(storm::storage::ParameterRegion<ParametricType> const& region, boost::optional<ParametricType> const& coverageThreshold, boost::optional<uint64_t> depthThreshold, RegionResultHypothesis const& hypothesis) { STORM_LOG_INFO("Applying refinement on region: " << region.toString(true) << " ."); diff --git a/src/storm-pars/modelchecker/region/RegionModelChecker.h b/src/storm-pars/modelchecker/region/RegionModelChecker.h index c1a558800..595d1e8f0 100644 --- a/src/storm-pars/modelchecker/region/RegionModelChecker.h +++ b/src/storm-pars/modelchecker/region/RegionModelChecker.h @@ -42,6 +42,8 @@ namespace storm { */ std::unique_ptr<storm::modelchecker::RegionCheckResult<ParametricType>> analyzeRegions(std::vector<storm::storage::ParameterRegion<ParametricType>> const& regions, std::vector<RegionResultHypothesis> const& hypotheses, bool sampleVerticesOfRegion = false) ; + virtual ParametricType getBoundAtInitState(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForParameters); + /*! * Iteratively refines the region until the region analysis yields a conclusive result (AllSat or AllViolated). * @param region the considered region diff --git a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp index 119e3791d..8ac2bdea8 100644 --- a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp @@ -6,6 +6,8 @@ #include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" +#include "storm/modelchecker/prctl/helper/DsMpiUpperRewardBoundsComputer.h" +#include "storm/modelchecker/prctl/helper/BaierUpperRewardBoundsComputer.h" #include "storm/models/sparse/Dtmc.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/solver/StandardMinMaxLinearEquationSolver.h" @@ -17,6 +19,8 @@ #include "storm/exceptions/InvalidPropertyException.h" #include "storm/exceptions/NotSupportedException.h" #include "storm/exceptions/UnexpectedException.h" +#include "storm/exceptions/UncheckedRequirementException.h" + namespace storm { namespace modelchecker { @@ -27,7 +31,7 @@ namespace storm { } template <typename SparseModelType, typename ConstantType> - SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::SparseDtmcParameterLiftingModelChecker(std::unique_ptr<storm::solver::MinMaxLinearEquationSolverFactory<ConstantType>>&& solverFactory) : solverFactory(std::move(solverFactory)) { + SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::SparseDtmcParameterLiftingModelChecker(std::unique_ptr<storm::solver::MinMaxLinearEquationSolverFactory<ConstantType>>&& solverFactory) : solverFactory(std::move(solverFactory)), solvingRequiresUpperRewardBounds(false) { // Intentionally left empty } @@ -109,6 +113,8 @@ namespace storm { // We know some bounds for the results so set them lowerResultBound = storm::utility::zero<ConstantType>(); upperResultBound = storm::utility::one<ConstantType>(); + // No requirements for bounded formulas + solverFactory->setRequirementsChecked(true); } template <typename SparseModelType, typename ConstantType> @@ -139,6 +145,12 @@ namespace storm { // We know some bounds for the results so set them lowerResultBound = storm::utility::zero<ConstantType>(); upperResultBound = storm::utility::one<ConstantType>(); + + auto req = solverFactory->getRequirements(storm::solver::EquationSystemType::UntilProbabilities); + req.clearBounds(); + req.clearNoEndComponents(); // We never have end components within the maybestates (assuming graph-preserving instantiations). + STORM_LOG_THROW(req.empty(), storm::exceptions::UncheckedRequirementException, "Unchecked solver requirement."); + solverFactory->setRequirementsChecked(true); } template <typename SparseModelType, typename ConstantType> @@ -172,6 +184,17 @@ namespace storm { // We only know a lower bound for the result lowerResultBound = storm::utility::zero<ConstantType>(); + + auto req = solverFactory->getRequirements(storm::solver::EquationSystemType::ReachabilityRewards); + req.clearNoEndComponents(); // We never have end components within the maybestates (assuming graph-preserving instantiations). + req.clearLowerBounds(); + if (req.requiresUpperBounds()) { + solvingRequiresUpperRewardBounds = true; + req.clearUpperBounds(); + } + STORM_LOG_THROW(req.empty(), storm::exceptions::UncheckedRequirementException, "Unchecked solver requirement."); + solverFactory->setRequirementsChecked(true); + } @@ -200,6 +223,9 @@ namespace storm { // We only know a lower bound for the result lowerResultBound = storm::utility::zero<ConstantType>(); + + // No requirements for bounded reward formula + solverFactory->setRequirementsChecked(true); } template <typename SparseModelType, typename ConstantType> @@ -228,7 +254,23 @@ namespace storm { } auto solver = solverFactory->create(parameterLifter->getMatrix()); if (lowerResultBound) solver->setLowerBound(lowerResultBound.get()); - if (upperResultBound) solver->setUpperBound(upperResultBound.get()); + if (upperResultBound) { + solver->setUpperBound(upperResultBound.get()); + } else if (solvingRequiresUpperRewardBounds) { + // For the min-case, we use DS-MPI, for the max-case variant 2 of the Baier et al. paper (CAV'17). + std::vector<ConstantType> oneStepProbs; + oneStepProbs.reserve(parameterLifter->getMatrix().getRowCount()); + for (uint64_t row = 0; row < parameterLifter->getMatrix().getRowCount(); ++row) { + oneStepProbs.push_back(storm::utility::one<ConstantType>() - parameterLifter->getMatrix().getRowSum(row)); + } + if (dirForParameters == storm::OptimizationDirection::Minimize) { + storm::modelchecker::helper::DsMpiMdpUpperRewardBoundsComputer<ConstantType> dsmpi(parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs); + solver->setUpperBounds(dsmpi.computeUpperBounds()); + } else { + storm::modelchecker::helper::BaierUpperRewardBoundsComputer<ConstantType> baier(parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs); + solver->setUpperBound(baier.computeUpperBound()); + } + } if (!stepBound) solver->setTrackScheduler(true); if (storm::solver::minimize(dirForParameters) && minSchedChoices && !stepBound) solver->setInitialScheduler(std::move(minSchedChoices.get())); if (storm::solver::maximize(dirForParameters) && maxSchedChoices && !stepBound) solver->setInitialScheduler(std::move(maxSchedChoices.get())); @@ -247,7 +289,7 @@ namespace storm { } // Invoke the solver - if(stepBound) { + if (stepBound) { assert(*stepBound > 0); x = std::vector<ConstantType>(maybeStates.getNumberOfSetBits(), storm::utility::zero<ConstantType>()); solver->repeatedMultiply(dirForParameters, x, ¶meterLifter->getVector(), *stepBound); diff --git a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h index d6f51032b..b4a8644b6 100644 --- a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h +++ b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h @@ -12,7 +12,6 @@ #include "storm/solver/MinMaxLinearEquationSolver.h" #include "storm/logic/FragmentSpecification.h" - namespace storm { namespace modelchecker { @@ -49,12 +48,13 @@ namespace storm { storm::storage::BitVector maybeStates; std::vector<ConstantType> resultsForNonMaybeStates; boost::optional<uint_fast64_t> stepBound; - + std::unique_ptr<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>> instantiationChecker; - + std::unique_ptr<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>> parameterLifter; std::unique_ptr<storm::solver::MinMaxLinearEquationSolverFactory<ConstantType>> solverFactory; - + bool solvingRequiresUpperRewardBounds; + // Results from the most recent solver call. boost::optional<std::vector<uint_fast64_t>> minSchedChoices, maxSchedChoices; std::vector<ConstantType> x; diff --git a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp index d0b2af22b..1accd68cc 100644 --- a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp @@ -4,6 +4,7 @@ #include "storm/logic/FragmentSpecification.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "storm/utility/vector.h" #include "storm/models/sparse/Dtmc.h" #include "storm/models/sparse/Mdp.h" #include "storm/models/sparse/StandardRewardModel.h" @@ -130,6 +131,18 @@ namespace storm { } } + template <typename SparseModelType, typename ConstantType> + std::unique_ptr<QuantitativeCheckResult<ConstantType>> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getBound(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) { + STORM_LOG_WARN_COND(this->currentCheckTask->getFormula().hasQuantitativeResult(), "Computing quantitative bounds for a qualitative formula..."); + return std::make_unique<ExplicitQuantitativeCheckResult<ConstantType>>(std::move(computeQuantitativeValues(region, dirForParameters)->template asExplicitQuantitativeCheckResult<ConstantType>())); + } + + template <typename SparseModelType, typename ConstantType> + typename SparseModelType::ValueType SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getBoundAtInitState(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) { + STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Getting a bound at the initial state requires a model with a single initial state."); + return storm::utility::convertNumber<typename SparseModelType::ValueType>(getBound(region, dirForParameters)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()]); + } + template <typename SparseModelType, typename ConstantType> SparseModelType const& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getConsideredParametricModel() const { return *parametricModel; diff --git a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h index e0cabd5f0..cb993ad4f 100644 --- a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h +++ b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h @@ -45,6 +45,9 @@ namespace storm { */ std::unique_ptr<CheckResult> check(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters); + std::unique_ptr<QuantitativeCheckResult<ConstantType>> getBound(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters); + virtual typename SparseModelType::ValueType getBoundAtInitState(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) override; + SparseModelType const& getConsideredParametricModel() const; CheckTask<storm::logic::Formula, ConstantType> const& getCurrentCheckTask() const; diff --git a/src/storm/analysis/GraphConditions.cpp b/src/storm/analysis/GraphConditions.cpp index be8073bf7..663bf2b52 100644 --- a/src/storm/analysis/GraphConditions.cpp +++ b/src/storm/analysis/GraphConditions.cpp @@ -35,6 +35,8 @@ namespace storm { void ConstraintCollector<ValueType>::wellformedRequiresNonNegativeEntries(std::vector<ValueType> const& vec) { for(auto const& entry : vec) { if (!storm::utility::isConstant(entry)) { + auto const& transitionVars = entry.gatherVariables(); + variableSet.insert(transitionVars.begin(), transitionVars.end()); if (entry.denominator().isConstant()) { if (entry.denominatorAsNumber() > 0) { wellformedConstraintSet.emplace(entry.nominator().polynomialWithCoefficient(), storm::CompareRelation::GEQ); diff --git a/src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp b/src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp index 54779b96a..245e9d54d 100644 --- a/src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp +++ b/src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp @@ -3,6 +3,7 @@ #include <iostream> #include <cstdio> #include <chrono> +#include <errno.h> #include "storm/solver/SmtSolver.h" @@ -163,7 +164,7 @@ namespace storm { STORM_LOG_TRACE("Executing command: " << command); std::unique_ptr<FILE> pipe(popen(command.c_str(), "r")); - STORM_LOG_THROW(pipe, storm::exceptions::InvalidStateException, "Call to popen failed."); + STORM_LOG_THROW(pipe, storm::exceptions::InvalidStateException, "Call to popen failed: " << strerror(errno)); while (!feof(pipe.get())) { if (fgets(buffer, 128, pipe.get()) != nullptr) diff --git a/src/storm/modelchecker/prctl/helper/DsMpiUpperRewardBoundsComputer.h b/src/storm/modelchecker/prctl/helper/DsMpiUpperRewardBoundsComputer.h index f54c810fb..7f2bb4278 100644 --- a/src/storm/modelchecker/prctl/helper/DsMpiUpperRewardBoundsComputer.h +++ b/src/storm/modelchecker/prctl/helper/DsMpiUpperRewardBoundsComputer.h @@ -1,6 +1,7 @@ #pragma once #include <vector> +#include <cstdint> namespace storm { namespace storage { diff --git a/src/storm/models/sparse/MarkovAutomaton.cpp b/src/storm/models/sparse/MarkovAutomaton.cpp index 485aa8a64..ca79ccfac 100644 --- a/src/storm/models/sparse/MarkovAutomaton.cpp +++ b/src/storm/models/sparse/MarkovAutomaton.cpp @@ -50,7 +50,9 @@ namespace storm { if (components.exitRates) { exitRates = std::move(components.exitRates.get()); - } else { + } + + if (components.rateTransitions) { this->turnRatesToProbabilities(); } closed = this->checkIsClosed(); @@ -150,7 +152,7 @@ namespace storm { uint_fast64_t row = this->getTransitionMatrix().getRowGroupIndices()[state]; if (this->markovianStates.get(state)) { if (assertRates) { - STORM_LOG_THROW(this->exitRates[state] == this->getTransitionMatrix().getRowSum(row), storm::exceptions::InvalidArgumentException, "The specified exit rate is inconsistent with the rate matrix. Difference is " << (this->exitRates[state] - this->getTransitionMatrix().getRowSum(row)) << "."); + STORM_LOG_THROW(this->exitRates[state] == this->getTransitionMatrix().getRowSum(row), storm::exceptions::InvalidArgumentException, "The specified exit rate is inconsistent with the rate matrix. Difference is " << (this->exitRates[state] - this->getTransitionMatrix().getRowSum(row)) << "."); } else { this->exitRates.push_back(this->getTransitionMatrix().getRowSum(row)); } @@ -159,7 +161,11 @@ namespace storm { } ++row; } else { - this->exitRates.push_back(storm::utility::zero<ValueType>()); + if (assertRates) { + STORM_LOG_THROW(storm::utility::isZero<ValueType>(this->exitRates[state]), storm::exceptions::InvalidArgumentException, "The specified exit rate for (non-Markovian) choice should be 0."); + } else { + this->exitRates.push_back(storm::utility::zero<ValueType>()); + } } for (; row < this->getTransitionMatrix().getRowGroupIndices()[state+1]; ++row) { STORM_LOG_THROW(storm::utility::isOne(this->getTransitionMatrix().getRowSum(row)), storm::exceptions::InvalidArgumentException, "Entries of transition matrix do not sum up to one for (non-Markovian) choice " << row << " of state " << state << " (sum is " << this->getTransitionMatrix().getRowSum(row) << ")."); diff --git a/src/storm/models/sparse/MarkovAutomaton.h b/src/storm/models/sparse/MarkovAutomaton.h index 249766b62..89c73f83c 100644 --- a/src/storm/models/sparse/MarkovAutomaton.h +++ b/src/storm/models/sparse/MarkovAutomaton.h @@ -22,7 +22,7 @@ namespace storm { * For hybrid states (i.e., states with Markovian and probabilistic transitions), it is assumed that the first * choice corresponds to the markovian transitions. * - * @param transitionMatrix The matrix representing the transitions in the model in terms of rates (markocian choices) and probabilities (probabilistic choices). + * @param transitionMatrix The matrix representing the transitions in the model in terms of rates (Markovian choices) and probabilities (probabilistic choices). * @param stateLabeling The labeling of the states. * @param markovianStates A bit vector indicating the Markovian states of the automaton (i.e., states with at least one markovian transition). * @param rewardModels A mapping of reward model names to reward models. @@ -38,7 +38,7 @@ namespace storm { * For hybrid states (i.e., states with Markovian and probabilistic transitions), it is assumed that the first * choice corresponds to the markovian transitions. * - * @param transitionMatrix The matrix representing the transitions in the model in terms of rates (markocian choices) and probabilities (probabilistic choices). + * @param transitionMatrix The matrix representing the transitions in the model in terms of rates (Markovian choices) and probabilities (probabilistic choices). * @param stateLabeling The labeling of the states. * @param markovianStates A bit vector indicating the Markovian states of the automaton (i.e., states with at least one markovian transition). * @param rewardModels A mapping of reward model names to reward models. diff --git a/src/storm/parser/DirectEncodingParser.cpp b/src/storm/parser/DirectEncodingParser.cpp index c18d1965c..45768f908 100644 --- a/src/storm/parser/DirectEncodingParser.cpp +++ b/src/storm/parser/DirectEncodingParser.cpp @@ -3,6 +3,9 @@ #include <iostream> #include <string> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/predicate.hpp> + #include "storm/models/sparse/MarkovAutomaton.h" #include "storm/models/sparse/Ctmc.h" @@ -18,6 +21,7 @@ #include "storm/utility/macros.h" #include "storm/utility/file.h" + namespace storm { namespace parser { @@ -58,46 +62,62 @@ namespace storm { // Initialize ValueParser<ValueType> valueParser; + bool sawType = false; + bool sawParameters = false; + size_t nrStates = 0; + storm::models::ModelType type; + + std::vector<std::string> rewardModelNames; + + std::shared_ptr<storm::storage::sparse::ModelComponents<ValueType, RewardModelType>> modelComponents; // Parse header - std::getline(file, line); - STORM_LOG_THROW(line == "// Exported by storm", storm::exceptions::WrongFormatException, "Expected header information."); - std::getline(file, line); - STORM_LOG_THROW(boost::starts_with(line, "// Original model type: "), storm::exceptions::WrongFormatException, "Expected header information."); - // Parse model type - std::getline(file, line); - STORM_LOG_THROW(boost::starts_with(line, "@type: "), storm::exceptions::WrongFormatException, "Expected model type."); - storm::models::ModelType type = storm::models::getModelType(line.substr(7)); - STORM_LOG_TRACE("Model type: " << type); - STORM_LOG_THROW(type != storm::models::ModelType::MarkovAutomaton, storm::exceptions::NotSupportedException, "Markov Automata in DRN format are not supported (unclear indication of Markovian Choices in DRN format)"); - STORM_LOG_THROW(type != storm::models::ModelType::S2pg, storm::exceptions::NotSupportedException, "Stochastic Two Player Games in DRN format are not supported."); - - // Parse parameters - std::getline(file, line); - STORM_LOG_THROW(line == "@parameters", storm::exceptions::WrongFormatException, "Expected parameter declaration."); - std::getline(file, line); - if (line != "") { - std::vector<std::string> parameters; - boost::split(parameters, line, boost::is_any_of(" ")); - for (std::string parameter : parameters) { - STORM_LOG_TRACE("New parameter: " << parameter); - valueParser.addParameter(parameter); + while(std::getline(file, line)) { + if(line.empty() || boost::starts_with(line, "//")) { + continue; } - } - // Parse no. states - std::getline(file, line); - STORM_LOG_THROW(line == "@nr_states", storm::exceptions::WrongFormatException, "Expected number of states."); - std::getline(file, line); - size_t nrStates = boost::lexical_cast<size_t>(line); - STORM_LOG_TRACE("Model type: " << type); + if (boost::starts_with(line, "@type: ")) { + STORM_LOG_THROW(!sawType, storm::exceptions::WrongFormatException, "Type declared twice"); + type = storm::models::getModelType(line.substr(7)); + STORM_LOG_TRACE("Model type: " << type); + STORM_LOG_THROW(type != storm::models::ModelType::MarkovAutomaton, storm::exceptions::NotSupportedException, "Markov Automata in DRN format are not supported (unclear indication of Markovian Choices in DRN format)"); + STORM_LOG_THROW(type != storm::models::ModelType::S2pg, storm::exceptions::NotSupportedException, "Stochastic Two Player Games in DRN format are not supported."); + sawType = true; + } + if(line == "@parameters") { + std::getline(file, line); + if (line != "") { + std::vector<std::string> parameters; + boost::split(parameters, line, boost::is_any_of(" ")); + for (std::string parameter : parameters) { + STORM_LOG_TRACE("New parameter: " << parameter); + valueParser.addParameter(parameter); + } + } + sawParameters = true; + } + if(line == "@reward_models") { + STORM_LOG_THROW(rewardModelNames.size() == 0, storm::exceptions::WrongFormatException, "Reward model names declared twice"); + std::getline(file, line); + boost::split(rewardModelNames, line, boost::is_any_of("\t ")); + } + if(line == "@nr_states") { + STORM_LOG_THROW(nrStates == 0, storm::exceptions::WrongFormatException, "Number states declared twice"); + std::getline(file, line); + nrStates = boost::lexical_cast<size_t>(line); - std::getline(file, line); - STORM_LOG_THROW(line == "@model", storm::exceptions::WrongFormatException, "Expected model declaration."); + } + if(line == "@model") { + STORM_LOG_THROW(sawType, storm::exceptions::WrongFormatException, "Type has to be declared before model."); + STORM_LOG_THROW(sawParameters, storm::exceptions::WrongFormatException, "Parameters have to be declared before model."); + STORM_LOG_THROW(nrStates != 0, storm::exceptions::WrongFormatException, "Nr States has to be declared before model."); - // Construct model components - std::shared_ptr<storm::storage::sparse::ModelComponents<ValueType, RewardModelType>> modelComponents = parseStates(file, type, nrStates, valueParser); - + // Construct model components + modelComponents = parseStates(file, type, nrStates, valueParser, rewardModelNames); + break; + } + } // Done parsing storm::utility::closeFile(file); @@ -106,13 +126,14 @@ namespace storm { } template<typename ValueType, typename RewardModelType> - std::shared_ptr<storm::storage::sparse::ModelComponents<ValueType, RewardModelType>> DirectEncodingParser<ValueType, RewardModelType>::parseStates(std::istream& file, storm::models::ModelType type, size_t stateSize, ValueParser<ValueType> const& valueParser) { + std::shared_ptr<storm::storage::sparse::ModelComponents<ValueType, RewardModelType>> DirectEncodingParser<ValueType, RewardModelType>::parseStates(std::istream& file, storm::models::ModelType type, size_t stateSize, ValueParser<ValueType> const& valueParser, std::vector<std::string> const& rewardModelNames) { // Initialize auto modelComponents = std::make_shared<storm::storage::sparse::ModelComponents<ValueType, RewardModelType>>(); bool nonDeterministic = (type == storm::models::ModelType::Mdp || type == storm::models::ModelType::MarkovAutomaton); storm::storage::SparseMatrixBuilder<ValueType> builder = storm::storage::SparseMatrixBuilder<ValueType>(0, 0, 0, false, nonDeterministic, 0); modelComponents->stateLabeling = storm::models::sparse::StateLabeling(stateSize); - + std::vector<std::vector<ValueType>> stateRewards; + // We parse rates for continuous time models. if (type == storm::models::ModelType::Ctmc) { modelComponents->rateTransitions = true; @@ -146,10 +167,18 @@ namespace storm { // Rewards found size_t posEndReward = line.find(']'); STORM_LOG_THROW(posEndReward != std::string::npos, storm::exceptions::WrongFormatException, "] missing."); - std::string rewards = line.substr(1, posEndReward-1); - STORM_LOG_TRACE("State rewards: " << rewards); - // TODO import rewards - STORM_LOG_WARN("Rewards were not imported"); + std::string rewardsStr = line.substr(1, posEndReward-1); + STORM_LOG_TRACE("State rewards: " << rewardsStr); + std::vector<std::string> rewards; + boost::split(rewards, rewardsStr, boost::is_any_of(",")); + if (stateRewards.size() < rewards.size()) { + stateRewards.resize(rewards.size(), std::vector<ValueType>(stateSize, storm::utility::zero<ValueType>())); + } + auto stateRewardsIt = stateRewards.begin(); + for (auto const& rew : rewards) { + (*stateRewardsIt)[state] = valueParser.parseValue(rew); + ++stateRewardsIt; + } line = line.substr(posEndReward+1); } // Check for labels @@ -188,6 +217,7 @@ namespace storm { STORM_LOG_THROW(posEndReward != std::string::npos, storm::exceptions::WrongFormatException, "] missing."); std::string rewards = line.substr(1, posEndReward-1); STORM_LOG_TRACE("Transition rewards: " << rewards); + STORM_LOG_WARN("Transition rewards [" << rewards << "] not parsed."); // TODO save rewards line = line.substr(posEndReward+1); } @@ -207,6 +237,16 @@ namespace storm { STORM_LOG_TRACE("Finished parsing"); modelComponents->transitionMatrix = builder.build(row + 1, stateSize, nonDeterministic ? stateSize : 0); + + for (uint64_t i = 0; i < stateRewards.size(); ++i) { + std::string rewardModelName; + if (rewardModelNames.size() <= i) { + rewardModelName = "rew" + std::to_string(i); + } else { + rewardModelName = rewardModelNames[i]; + } + modelComponents->rewardModels.emplace(rewardModelName, storm::models::sparse::StandardRewardModel<ValueType>(std::move(stateRewards[i]))); + } STORM_LOG_TRACE("Built matrix"); return modelComponents; } diff --git a/src/storm/parser/DirectEncodingParser.h b/src/storm/parser/DirectEncodingParser.h index 43a5a6f8b..db1bac12e 100644 --- a/src/storm/parser/DirectEncodingParser.h +++ b/src/storm/parser/DirectEncodingParser.h @@ -75,7 +75,7 @@ namespace storm { * * @return Transition matrix. */ - static std::shared_ptr<storm::storage::sparse::ModelComponents<ValueType, RewardModelType>> parseStates(std::istream& file, storm::models::ModelType type, size_t stateSize, ValueParser<ValueType> const& valueParser); + static std::shared_ptr<storm::storage::sparse::ModelComponents<ValueType, RewardModelType>> parseStates(std::istream& file, storm::models::ModelType type, size_t stateSize, ValueParser<ValueType> const& valueParser, std::vector<std::string> const& rewardModelNames); }; } // namespace parser diff --git a/src/storm/parser/ExpressionParser.cpp b/src/storm/parser/ExpressionParser.cpp index 3292a03f9..f78e47d13 100644 --- a/src/storm/parser/ExpressionParser.cpp +++ b/src/storm/parser/ExpressionParser.cpp @@ -197,7 +197,7 @@ namespace storm { try { // Start parsing. bool succeeded = qi::phrase_parse(iter, last, *this, boost::spirit::ascii::space | qi::lit("//") >> *(qi::char_ - (qi::eol | qi::eoi)) >> (qi::eol | qi::eoi), result); - STORM_LOG_THROW(succeeded, storm::exceptions::WrongFormatException, "Could not parse expression."); + STORM_LOG_THROW(succeeded, storm::exceptions::WrongFormatException, "Could not parse expression '" << expressionString << "'."); STORM_LOG_DEBUG("Parsed expression successfully."); } catch (qi::expectation_failure<PositionIteratorType> const& e) { STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, e.what_); diff --git a/src/storm/settings/modules/EigenEquationSolverSettings.cpp b/src/storm/settings/modules/EigenEquationSolverSettings.cpp index 3047a13a4..e43bda28b 100644 --- a/src/storm/settings/modules/EigenEquationSolverSettings.cpp +++ b/src/storm/settings/modules/EigenEquationSolverSettings.cpp @@ -113,6 +113,7 @@ namespace storm { case EigenEquationSolverSettings::LinearEquationMethod::DGMRES: out << "dgmres"; break; case EigenEquationSolverSettings::LinearEquationMethod::SparseLU: out << "sparselu"; break; } + return out; } } // namespace modules diff --git a/src/storm/solver/AbstractEquationSolver.h b/src/storm/solver/AbstractEquationSolver.h index 4e27517fa..22e9d0f56 100644 --- a/src/storm/solver/AbstractEquationSolver.h +++ b/src/storm/solver/AbstractEquationSolver.h @@ -2,6 +2,7 @@ #define STORM_SOLVER_ABSTRACTEQUATIONSOLVER_H_ #include <memory> +#include <chrono> #include <boost/optional.hpp> diff --git a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp index 696425cbc..f5061580a 100644 --- a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp @@ -277,6 +277,12 @@ namespace storm { } } + // In case we perform value iteration and need to retrieve a scheduler, end components are forbidden + if (this->getSettings().getSolutionMethod() == IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::ValueIteration && this->isTrackSchedulerSet()) { + requirements.requireNoEndComponents(); + } + + // Guide requirements by whether or not we force soundness. if (this->getSettings().getForceSoundness()) { if (this->getSettings().getSolutionMethod() == IterativeMinMaxLinearEquationSolverSettings<ValueType>::SolutionMethod::ValueIteration) { // Require both bounds now. @@ -430,7 +436,7 @@ namespace storm { // If requested, we store the scheduler for retrieval. if (this->isTrackSchedulerSet()) { this->schedulerChoices = std::vector<uint_fast64_t>(this->A->getRowGroupCount()); - this->linEqSolverA->multiplyAndReduce(dir, this->A->getRowGroupIndices(), x, &b, *currentX, &this->schedulerChoices.get()); + this->linEqSolverA->multiplyAndReduce(dir, this->A->getRowGroupIndices(), x, &b, *auxiliaryRowGroupVector.get(), &this->schedulerChoices.get()); } if (!this->isCachingEnabled()) { diff --git a/src/storm/solver/LpMinMaxLinearEquationSolver.cpp b/src/storm/solver/LpMinMaxLinearEquationSolver.cpp index 2f360f513..024bcf260 100644 --- a/src/storm/solver/LpMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/LpMinMaxLinearEquationSolver.cpp @@ -108,6 +108,21 @@ namespace storm { StandardMinMaxLinearEquationSolver<ValueType>::clearCache(); } + + template<typename ValueType> + MinMaxLinearEquationSolverRequirements LpMinMaxLinearEquationSolver<ValueType>::getRequirements(EquationSystemType const& equationSystemType, boost::optional<storm::solver::OptimizationDirection> const& direction) const { + + MinMaxLinearEquationSolverRequirements requirements; + + // In case we need to retrieve a scheduler, end components are forbidden + if (this->isTrackSchedulerSet()) { + requirements.requireNoEndComponents(); + } + + return requirements; + } + + template<typename ValueType> LpMinMaxLinearEquationSolverFactory<ValueType>::LpMinMaxLinearEquationSolverFactory(bool trackScheduler) : StandardMinMaxLinearEquationSolverFactory<ValueType>(MinMaxMethodSelection::LinearProgramming, trackScheduler), lpSolverFactory(std::make_unique<storm::utility::solver::LpSolverFactory<ValueType>>()) { // Intentionally left empty diff --git a/src/storm/solver/LpMinMaxLinearEquationSolver.h b/src/storm/solver/LpMinMaxLinearEquationSolver.h index a31ac710b..3e5af458f 100644 --- a/src/storm/solver/LpMinMaxLinearEquationSolver.h +++ b/src/storm/solver/LpMinMaxLinearEquationSolver.h @@ -18,6 +18,8 @@ namespace storm { virtual void clearCache() const override; + virtual MinMaxLinearEquationSolverRequirements getRequirements(EquationSystemType const& equationSystemType, boost::optional<storm::solver::OptimizationDirection> const& direction = boost::none) const override; + private: std::unique_ptr<storm::utility::solver::LpSolverFactory<ValueType>> lpSolverFactory; }; diff --git a/src/storm/storage/SparseMatrix.cpp b/src/storm/storage/SparseMatrix.cpp index c8f53a342..bf1b15d14 100644 --- a/src/storm/storage/SparseMatrix.cpp +++ b/src/storm/storage/SparseMatrix.cpp @@ -1497,8 +1497,9 @@ namespace storm { typename std::vector<ValueType>::iterator resultIterator = x.end() - 1; typename std::vector<ValueType>::iterator resultIteratorEnd = x.begin() - 1; - index_type currentRow = 0; + index_type currentRow = getRowCount(); for (; resultIterator != resultIteratorEnd; --rowIterator, --resultIterator, --bIt) { + --currentRow; ValueType tmpValue = storm::utility::zero<ValueType>(); ValueType diagonalElement = storm::utility::zero<ValueType>(); @@ -1509,9 +1510,8 @@ namespace storm { diagonalElement += it->getValue(); } } - + assert(!storm::utility::isZero(diagonalElement)); *resultIterator = ((storm::utility::one<ValueType>() - omega) * *resultIterator) + (omega / diagonalElement) * (*bIt - tmpValue); - ++currentRow; } } diff --git a/src/storm/utility/DirectEncodingExporter.cpp b/src/storm/utility/DirectEncodingExporter.cpp index 6bdd629a7..e0913a0f2 100644 --- a/src/storm/utility/DirectEncodingExporter.cpp +++ b/src/storm/utility/DirectEncodingExporter.cpp @@ -46,6 +46,11 @@ namespace storm { } } os << std::endl; + os << "@reward_models" << std::endl; + for (auto const& rewardModel : sparseModel->getRewardModels()) { + os << rewardModel.first << " "; + } + os << std::endl; os << "@nr_states" << std::endl << sparseModel->getNumberOfStates() << std::endl; os << "@model" << std::endl; diff --git a/travis/build-helper.sh b/travis/build-helper.sh index 8d73c87e7..04c2dc52e 100755 --- a/travis/build-helper.sh +++ b/travis/build-helper.sh @@ -50,7 +50,13 @@ run() { # Test all travis_fold start test_all cd build - ctest test --output-on-failure + # Hack to avoid memout problem with jit and sylvan + # 1. Run other tests without builder tests + ctest test --output-on-failure -E run-test-builder + # 2. Run builder tests without sylvan tests + ./bin/test-builder --gtest_filter=-"DdJaniModelBuilderTest_Sylvan.*" + # 3. Just run sylvan tests + ./bin/test-builder --gtest_filter="DdJaniModelBuilderTest_Sylvan.*" travis_fold end test_all ;; @@ -66,57 +72,17 @@ run() { export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH" case $COMPILER in -gcc-4.8) - export CC=gcc-4.8 - export CXX=g++-4.8 - ;; - -gcc-4.9) - export CC=gcc-4.9 - export CXX=g++-4.9 - ;; - -gcc-5) - export CC=gcc-5 - export CXX=g++-5 - ;; - gcc-6) export CC=gcc-6 export CXX=g++-6 ;; -gcc-default) +gcc) export CC=gcc export CXX=g++ ;; -clang-3.5) - export CC=clang-3.5 - export CXX=clang++-3.5 - ;; - -clang-3.6) - export CC=clang-3.6 - export CXX=clang++-3.6 - ;; - -clang-3.7) - export CC=clang-3.7 - export CXX=clang++-3.7 - ;; - -clang-3.8) - export CC=clang-3.8 - export CXX=clang++-3.8 - ;; - -clang-3.9) - export CC=clang-3.9 - export CXX=clang++-3.9 - ;; - -clang-4.0) +clang-4) case "$OS" in linux) export CC=clang-4.0 @@ -130,7 +96,7 @@ clang-4.0) esac ;; -clang-default) +clang) export CC=clang export CXX=clang++ ;; diff --git a/travis/dockerfiles/Dockerfile.debian-9 b/travis/dockerfiles/Dockerfile.debian-9 deleted file mode 100644 index 3735b38fd..000000000 --- a/travis/dockerfiles/Dockerfile.debian-9 +++ /dev/null @@ -1,16 +0,0 @@ -FROM debian:9 -MAINTAINER Matthias Volk <matthias.volk@cs.rwth-aachen.de> - -RUN apt-get update -qq && apt-get install -y --no-install-recommends \ - build-essential \ - ruby \ - git \ - cmake \ - libboost-all-dev \ - libcln-dev \ - libeigen3-dev \ - libgmp-dev \ - libginac-dev \ - automake \ - libglpk-dev \ - libz3-dev diff --git a/travis/dockerfiles/Dockerfile.storm b/travis/dockerfiles/Dockerfile.storm deleted file mode 100644 index 272b97825..000000000 --- a/travis/dockerfiles/Dockerfile.storm +++ /dev/null @@ -1,8 +0,0 @@ -FROM mvolk/storm-basesystem:ubuntu-16.10 -MAINTAINER Matthias Volk <matthias.volk@cs.rwth-aachen.de> - -COPY build_carl.sh /opt -RUN cd opt && ./build_carl.sh - -COPY build_storm.sh /opt -RUN cd opt && ./build_storm.sh diff --git a/travis/dockerfiles/Dockerfile.ubuntu-16.10 b/travis/dockerfiles/Dockerfile.ubuntu-16.10 deleted file mode 100644 index a612faccb..000000000 --- a/travis/dockerfiles/Dockerfile.ubuntu-16.10 +++ /dev/null @@ -1,17 +0,0 @@ -FROM ubuntu:16.10 -MAINTAINER Matthias Volk <matthias.volk@cs.rwth-aachen.de> - -RUN apt-get update -qq && apt-get install -y --no-install-recommends \ - build-essential \ - ruby \ - git \ - cmake \ - libboost-all-dev \ - libcln-dev \ - libeigen3-dev \ - libgmp-dev \ - libginac-dev \ - automake \ - libglpk-dev \ - libhwloc-dev \ - libz3-dev diff --git a/travis/dockerfiles/build_carl.sh b/travis/dockerfiles/build_carl.sh deleted file mode 100755 index fc3d43d22..000000000 --- a/travis/dockerfiles/build_carl.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -echo "Building Carl..." -git clone https://github.com/smtrat/carl.git -cd carl -mkdir build -cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -DUSE_CLN_NUMBERS=ON -DUSE_GINAC=ON -make lib_carl -j2 -echo "Building Carl finished" diff --git a/travis/dockerfiles/build_docker.sh b/travis/dockerfiles/build_docker.sh deleted file mode 100755 index 280710566..000000000 --- a/travis/dockerfiles/build_docker.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -# Build Ubuntu 16.10 "Yakkety Yak" -docker build -t mvolk/storm-basesystem:ubuntu-16.10 -f Dockerfile.ubuntu-16.10 . -docker push mvolk/storm-basesystem:ubuntu-16.10 - -# Build Debian 9 "Stretch" -docker build -t mvolk/storm-basesystem:debian-9 -f Dockerfile.debian-9 . -docker push mvolk/storm-basesystem:debian-9 diff --git a/travis/dockerfiles/build_storm.sh b/travis/dockerfiles/build_storm.sh deleted file mode 100755 index 4c11b708d..000000000 --- a/travis/dockerfiles/build_storm.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -echo "Building Storm..." -git clone https://github.com/moves-rwth/storm.git -cd storm -mkdir build -cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -make storm storm-dft storm-pars -j1 -echo "Building Storm finished" diff --git a/travis/generate_travis.py b/travis/generate_travis.py index 14e058a85..508568a34 100644 --- a/travis/generate_travis.py +++ b/travis/generate_travis.py @@ -2,14 +2,14 @@ # Configuration for Linux configs_linux = [ # OS, compiler - ("ubuntu-16.10", "gcc", "-6"), - #("debian-9", "gcc", "-6"), + ("ubuntu-16.10", "gcc", ""), + #("debian-9", "gcc", ""), ] # Configurations for Mac configs_mac = [ # OS, compiler - ("osx", "clang", "-4.0"), + ("osx", "clang", ""), ] # Build types @@ -23,8 +23,7 @@ stages = [ ("Build (1st run)", "Build1"), ("Build (2nd run)", "Build2"), ("Build (3rd run)", "Build3"), - ("Build (4th run)", "Build4"), - ("Build (5th run)", "BuildLast"), + ("Build (4th run)", "BuildLast"), ("Test all", "TestAll"), ] @@ -41,6 +40,7 @@ if __name__ == "__main__": s += " only:\n" s += " - master\n" s += " - stable\n" + s += "sudo: required\n" s += "dist: trusty\n" s += "language: cpp\n" s += "\n" @@ -54,7 +54,6 @@ if __name__ == "__main__": s += "# Enable docker support\n" s += "services:\n" s += "- docker\n" - s += "sudo: required\n" s += "\n" s += "notifications:\n" diff --git a/travis/install_linux.sh b/travis/install_linux.sh index c7ef04886..62a7d75f6 100755 --- a/travis/install_linux.sh +++ b/travis/install_linux.sh @@ -8,4 +8,4 @@ then exit 0 fi -sudo apt-get install -qq -y docker +#sudo apt-get install -qq -y docker diff --git a/travis/install_osx.sh b/travis/install_osx.sh index 8d703a82a..89e155730 100755 --- a/travis/install_osx.sh +++ b/travis/install_osx.sh @@ -18,8 +18,7 @@ install_brew_package() { brew outdated "$1" || brew upgrade "$@" else # Package not installed yet, install. - # If there are conflicts, try overwriting the files (these are in /usr/local anyway so it should be ok). - brew install "$@" || brew link --overwrite gcc49 + brew install "$@" || brew link --overwrite "$@" fi } @@ -40,19 +39,14 @@ install_brew_package md5sha1sum # For `timeout' install_brew_package coreutils -which cmake &>/dev/null || install_brew_package cmake +install_brew_package cmake # Install compiler case "${COMPILER}" in -gcc-4.8) install_brew_package gcc@4.8 ;; -gcc-4.9) install_brew_package gcc@4.9 ;; -gcc-5) install_brew_package gcc@5 ;; -gcc-6) install_brew_package gcc@6 ;; -clang-default) ;; -clang-3.7) install_brew_package llvm@3.7 --with-clang --with-libcxx;; -clang-3.8) install_brew_package llvm@3.8 --with-clang --with-libcxx;; -clang-3.9) install_brew_package llvm@3.9 --with-clang --with-libcxx;; -clang-4.0) install_brew_package llvm --with-clang --with-libcxx;; +gcc) install_brew_package gcc ;; +gcc-6) install_brew_package gcc@6 ;; +clang) ;; +clang-4) install_brew_package llvm@4 --with-clang --with-libcxx;; *) echo "Compiler not supported: ${COMPILER}. See travis/install_osx.sh"; exit 1 ;; esac travis_fold end brew_install_util @@ -66,8 +60,7 @@ install_brew_package ginac install_brew_package doxygen install_brew_package boost --c++11 install_brew_package z3 # optional -brew tap homebrew/science -install_brew_package homebrew/science/glpk -install_brew_package homebrew/science/hwloc +install_brew_package glpk +install_brew_package hwloc install_brew_package eigen travis_fold end brew_install_dependencies diff --git a/util/astyle/options.astyle b/util/astyle/options.astyle deleted file mode 100644 index 5f02d356d..000000000 --- a/util/astyle/options.astyle +++ /dev/null @@ -1,34 +0,0 @@ -# Select overall style. ---style=google ---mode=c ---recursive - -# Attach brackets to namespaces and classes. ---attach-namespaces ---attach-classes - -# Of course, namespaces should also be indented. ---indent-namespaces - -# C++ comments starting in column 1 are also supposed to be indented properly. ---indent-col1-comments - -# Add space padding around operators. ---pad-oper - -# Put the pointer/reference operators next to the variable. ---align-pointer=type - -# Add brackets to conditional one-line statements. ---add-brackets - -# Convert tabs to spaces in non-indentation part of the line as well. ---convert-tabs - -# Remove spaces in definition of C++ templates. ---close-templates - -# Don't let lines get too long. ---max-code-length=140 - ---verbose diff --git a/util/checklist/storage.ods b/util/checklist/storage.ods deleted file mode 100644 index d05761496..000000000 Binary files a/util/checklist/storage.ods and /dev/null differ diff --git a/util/osx-package/package.sh b/util/osx-package/package.sh deleted file mode 100644 index 0db5b2b5d..000000000 --- a/util/osx-package/package.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -DYLIBBUNDLER_DIR=/Users/chris/work/macdylibbundler/ - -mkdir -p $1 -mkdir -p $1/bin -mkdir -p $1/lib -cp $2 $1/bin -$DYLIBBUNDLER_DIR/dylibbundler -cd -od -b -p @executable_path/../lib -x $1/bin/storm -d $1/lib -python packager.py --bin storm --dir $1 diff --git a/util/osx-package/packager.py b/util/osx-package/packager.py deleted file mode 100644 index 60b26a8b8..000000000 --- a/util/osx-package/packager.py +++ /dev/null @@ -1,103 +0,0 @@ -import argparse -import subprocess -import os -from shutil import copyfile - -def get_dependencies(file): - # Call otool -L file to obtain the dependencies. - proc = subprocess.Popen(["otool", "-L", args.binary], stdout=subprocess.PIPE) - result = {} - for line_bytes in proc.stdout: - line = line_bytes.decode("utf-8").strip() - lib = line.split()[0] - if (lib.startswith("@")): - lib = lib.split("/", 1)[1] - (base, file) = os.path.split(lib) - print(base + " // " + file) - - return result - -def create_package(args): - create_package_dirs(args.dir) - copy_binary_to_package_dir(args.bin, args.binary_basename, args.dir) - run_dylibbundler(args.bundler_binary, args.dir, args.binary_basename) - pass - -def parse_arguments(): - parser = argparse.ArgumentParser(description='Package the storm binary on Mac OS.') - parser.add_argument('--bin', dest='bin', help='the binary to package', default='storm') - parser.add_argument('--dir', dest='dir', help='the root directory of the package (will be created if it does not exist)', default='.') - parser.add_argument('--dylibbundler', dest='bundler_binary', help='the binary of the dylibbundler', default='dylibbundler') - args = parser.parse_args() - args.binary_dir = os.path.split(args.bin)[0] - args.binary_basename = os.path.split(args.bin)[1] - return args - -def create_package_dirs(root_dir): - if not os.path.exists(root_dir): - os.makedirs(root_dir) - if not os.path.exists(root_dir + "/bin"): - os.makedirs(root_dir + "/bin") - if not os.path.exists(root_dir + "/lib"): - os.makedirs(root_dir + "/bin") - pass - -def copy_binary_to_package_dir(binary, binary_basename, root_dir): - copyfile(binary, root_dir + "/bin/storm") - pass - -def run_dylibbundler(bundler_binary, root_dir, binary_basename): - command = [bundler_binary, "-cd", "-od", "-b", "-p", "@executable_path/../lib", "-x", root_dir + "/bin/" + binary_basename, "-d", root_dir + "/lib"] - print("executing " + str(command)) - #proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - pass - -def fix_paths(root_dir, binary_basename): - fix_paths_file(root_dir + "/bin/" + binary_basename) - for file in os.listdir(root_dir + "/lib"): - fix_paths_file(root_dir + "/lib/" + file) - pass - pass - -def fix_paths_file(file): - print("fixing paths for " + file) - fixable_deps = get_fixable_deps(file) - for (path, lib) in fixable_deps: - change_fixable_dep(file, path, lib) - -native_libs = ["libc++.1.dylib", "libSystem.B.dylib"] - -def get_fixable_deps(file): - # Call otool -L file to obtain the dependencies. - proc = subprocess.Popen(["otool", "-L", file], stdin=subprocess.PIPE, stdout=subprocess.PIPE) - result = [] - for line_bytes in proc.stdout: - line = line_bytes.decode("utf-8").strip() - lib = line.split()[0] - if lib.startswith("@rpath/"): - result.append(("@rpath", lib.split("/", 1)[1])) - elif lib.startswith("/"): - path_file = os.path.split(lib) - if path_file[1] not in native_libs: - result.append((path_file[0], path_file[1])) - return result - -def change_fixable_dep(file, path, lib): - # Call install_name_tool to change the fixable dependencies - command = ["install_name_tool", "-change", path + "/" + lib, "@executable_path/../lib/" + lib, file] - print("executing " + str(command)) - proc = subprocess.Popen(command, stdout=subprocess.PIPE) - #print ("after call to install_name_tool") - #proc = subprocess.Popen(["otool", "-L", file], stdout=subprocess.PIPE) - #for line_bytes in proc.stdout: - # line = line_bytes.decode("utf-8").strip() - # print(line) - pass - -if __name__ == "__main__": - args = parse_arguments() - - #create_package(args) - fix_paths(args.dir, args.binary_basename) - -