diff --git a/.travis.yml b/.travis.yml index d6e942eec..edeac6e25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,9 @@ sudo: required dist: trusty language: cpp +git: + depth: false + # Enable caching cache: timeout: 1000 @@ -25,7 +28,7 @@ notifications: on_failure: always on_success: change recipients: - secure: "Q9CW/PtyWkZwExDrfFFb9n1STGYsRfI6awv1bZHcGwfrDvhpXoMCuF8CwviIfilm7fFJJEoKizTtdRpY0HrOnY/8KY111xrtcFYosxdndv7xbESodIxQOUoIEFCO0mCNHwWYisoi3dAA7H3Yn661EsxluwHjzX5vY0xSbw0n229hlsFz092HwGLSU33zHl7eXpQk+BOFmBTRGlOq9obtDZ17zbHz1oXFZntHK/JDUIYP0b0uq8NvE2jM6CIVdcuSwmIkOhZJoO2L3Py3rBbPci+L2PSK4Smv2UjCPF8KusnOaFIyDB3LcNM9Jqq5ssJMrK/KaO6BiuYrOZXYWZ7KEg3Y/naC8HjOH1dzty+P7oW46sb9F03pTsufqD4R7wcK+9wROTztO6aJPDG/IPH7EWgrBTxqlOkVRwi2eYuQouZpZUW6EMClKbMHMIxCH2S8aOk/r1w2cNwmPEcunnP0nl413x/ByHr4fTPFykPj8pQxIsllYjpdWBRQfDOauKWGzk6LcrFW0qpWP+/aJ2vYu/IoZQMG5lMHbp6Y1Lg09pYm7Q983v3b7D+JvXhOXMyGq91HyPahA2wwKoG1GA4uoZ2I95/IFYNiKkelDd3WTBoFLNF9YFoEJNdCywm1fO2WY4WkyEFBuQcgDA+YpFMJJMxjTbivYk9jvHk2gji//2w=" + - secure: "VWnsiQkt1xjgRo1hfNiNQqvLSr0fshFmLV7jJlUixhCr094mgD0U2bNKdUfebm28Byg9UyDYPbOFDC0sx7KydKiL1q7FKKXkyZH0k04wUu8XiNw+fYkDpmPnQs7G2n8oJ/GFJnr1Wp/1KI3qX5LX3xot4cJfx1I5iFC2O+p+ng6v/oSX+pewlMv4i7KL16ftHHHMo80N694v3g4B2NByn4GU2/bjVQcqlBp/TiVaUa5Nqu9DxZi/n9CJqGEaRHOblWyMO3EyTZsn45BNSWeQ3DtnMwZ73rlIr9CaEgCeuArc6RGghUAVqRI5ao+N5apekIaILwTgL6AJn+Lw/+NRPa8xclgd0rKqUQJMJCDZKjKz2lmIs3bxfELOizxJ3FJQ5R95FAxeAZ6rb/j40YqVVTw2IMBDnEE0J5ZmpUYNUtPti/Adf6GD9Fb2y8sLo0XDJzkI8OxYhfgjSy5KYmRj8O5MXcP2MAE8LQauNO3MaFnL9VMVOTZePJrPozQUgM021uyahf960+QNI06Uqlmg+PwWkSdllQlxHHplOgW7zClFhtSUpnJxcsUBzgg4kVg80gXUwAQkaDi7A9Wh2bs+TvMlmHzBwg+2SaAfWDgjeJIeOaipDkF1uSGzC+EHAiiKYMLd4Aahoi8SuelJUucoyJyLAq00WdUFQIh/izVhM4Y=" # # Configurations @@ -37,11 +40,11 @@ jobs: # Stage: Build Carl ### - # ubuntu-17.10 - DefaultDebugTravis + # ubuntu-18.04 - DefaultDebugTravis - stage: Build Carl os: linux compiler: gcc - env: CONFIG=DefaultDebugTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultDebugTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - travis/install_linux.sh script: @@ -50,11 +53,11 @@ jobs: - docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD"; - docker commit carl movesrwth/carl:travis-debug; - docker push movesrwth/carl:travis-debug; - # ubuntu-17.10 - DefaultReleaseTravis + # ubuntu-18.04 - DefaultReleaseTravis - stage: Build Carl os: linux compiler: gcc - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - travis/install_linux.sh script: @@ -96,11 +99,11 @@ jobs: - docker cp storm:/opt/storm/. . after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - # ubuntu-17.10 - DefaultDebugTravis + # ubuntu-18.04 - DefaultDebugTravis - stage: Build (1st run) os: linux compiler: gcc - env: CONFIG=DefaultDebugTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultDebugTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - rm -rf build - travis/install_linux.sh @@ -110,11 +113,11 @@ jobs: - docker cp storm:/opt/storm/. . after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - # ubuntu-17.10 - DefaultReleaseTravis + # ubuntu-18.04 - DefaultReleaseTravis - stage: Build (1st run) os: linux compiler: gcc - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - rm -rf build - travis/install_linux.sh @@ -183,11 +186,11 @@ jobs: - docker cp storm:/opt/storm/. . after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - # ubuntu-17.10 - DefaultDebugTravis + # ubuntu-18.04 - DefaultDebugTravis - stage: Build (2nd run) os: linux compiler: gcc - env: CONFIG=DefaultDebugTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultDebugTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - travis/install_linux.sh script: @@ -196,11 +199,11 @@ jobs: - docker cp storm:/opt/storm/. . after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - # ubuntu-17.10 - DefaultReleaseTravis + # ubuntu-18.04 - DefaultReleaseTravis - stage: Build (2nd run) os: linux compiler: gcc - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - travis/install_linux.sh script: @@ -266,11 +269,11 @@ jobs: - docker cp storm:/opt/storm/. . after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - # ubuntu-17.10 - DefaultDebugTravis + # ubuntu-18.04 - DefaultDebugTravis - stage: Build (3rd run) os: linux compiler: gcc - env: CONFIG=DefaultDebugTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultDebugTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - travis/install_linux.sh script: @@ -279,11 +282,11 @@ jobs: - docker cp storm:/opt/storm/. . after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - # ubuntu-17.10 - DefaultReleaseTravis + # ubuntu-18.04 - DefaultReleaseTravis - stage: Build (3rd run) os: linux compiler: gcc - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - travis/install_linux.sh script: @@ -349,11 +352,11 @@ jobs: - docker cp storm:/opt/storm/. . after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - # ubuntu-17.10 - DefaultDebugTravis + # ubuntu-18.04 - DefaultDebugTravis - stage: Build (4th run) os: linux compiler: gcc - env: CONFIG=DefaultDebugTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultDebugTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - travis/install_linux.sh script: @@ -362,11 +365,11 @@ jobs: - docker cp storm:/opt/storm/. . after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - # ubuntu-17.10 - DefaultReleaseTravis + # ubuntu-18.04 - DefaultReleaseTravis - stage: Build (4th run) os: linux compiler: gcc - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - travis/install_linux.sh script: @@ -432,11 +435,11 @@ jobs: - docker cp storm:/opt/storm/. . after_failure: - find build -iname '*err*.log' -type f -print -exec cat {} \; - # ubuntu-17.10 - DefaultDebugTravis + # ubuntu-18.04 - DefaultDebugTravis - stage: Test all os: linux compiler: gcc - env: CONFIG=DefaultDebugTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultDebugTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - travis/install_linux.sh script: @@ -449,11 +452,11 @@ jobs: - docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD"; - docker commit storm movesrwth/storm:travis-debug; - docker push movesrwth/storm:travis-debug; - # ubuntu-17.10 - DefaultReleaseTravis + # ubuntu-18.04 - DefaultReleaseTravis - stage: Test all os: linux compiler: gcc - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc install: - travis/install_linux.sh script: @@ -495,17 +498,17 @@ jobs: allow_failures: - stage: Build (1st run) os: linux - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc - stage: Build (2nd run) os: linux - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc - stage: Build (3rd run) os: linux - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc - stage: Build (4th run) os: linux - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc - stage: Test all os: linux - env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-17.10 COMPILER=gcc + env: CONFIG=DefaultReleaseTravis LINUX=ubuntu-18.04 COMPILER=gcc diff --git a/CHANGELOG.md b/CHANGELOG.md index 54be35d3f..7dd207331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,32 @@ The releases of major and minor versions contain an overview of changes since th Version 1.2.x ------------- -### Version 1.2.2 (to be released) +### Version 1.2.4 (2018/08) +- New binary `storm-conv` that handles conversions between model files (currently: prism to jani) +- Added support for expected time properties for discrete time models +- Several bug fixes related to jani +- `storm-gspn`: Improved .pnpro parser +- `storm-gspn`: Added support for single/infinite/k-server semantics for GSPNs given in the .pnpro format +- `storm-gspn`: Added option to set a global capacity for all places +- `storm-gspn`: Added option to include a set of standard properties when converting GSPNs to jani + +### Version 1.2.3 (2018/07) +- Fix in version parsing + +### Version 1.2.2 (2018/07) +- Sound value iteration (SVI) for DTMCs and MDPs +- Topological solver for linear equation systems and MinMax equation systems (enabled by default) +- Added support for expected total rewards in the sparse engine +- By default, iteration-based solvers are no longer aborted after a given number of steps. +- Improved export for jani models +- A fix in parsing jani properties +- Several extensions to high-level counterexamples +- `storm-parsers` extracted to reduce linking time +- `storm-counterexamples` extracted to reduce linking time - `storm-dft`: improvements in Galileo parser - `storm-dft`: test cases for DFT analysis -- Sound value iteration (SVI) for DTMCs and MDPs -- Topological solver for linear equation systems and MinMax equation systems. - +- Improved Storm installation +- Several bug fixes ### Version 1.2.1 (2018/02) - Multi-dimensional reward bounded reachability properties for DTMCs. diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fe522046..0999e0ce3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,15 @@ set(BIN_INSTALL_DIR lib/ CACHE PATH "Installation directory for executables") set(DEF_INSTALL_CMAKE_DIR "lib/CMake/storm") set(CMAKE_INSTALL_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") -message("CMAKE_INSTALL_DIR: ${CMAKE_INSTALL_DIR}") +# Add CMake install prefix +foreach(p LIB BIN INCLUDE CMAKE) + set(var ${p}_INSTALL_DIR) + if(NOT IS_ABSOLUTE "${${var}}") + set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}") + endif() +endforeach() + +message(STATUS "Storm - CMake install dir: ${CMAKE_INSTALL_DIR}") # If the STORM_DEVELOPER option was turned on, by default we target a debug version, otherwise a release version. set(CMAKE_BUILD_TYPE "DEBUG") @@ -135,15 +143,15 @@ elseif (WIN32) set(STATIC_EXT ".lib") set(LIB_PREFIX "") endif() -message(STATUS "Assuming extension for shared libraries: ${DYNAMIC_EXT}") -message(STATUS "Assuming extension for static libraries: ${STATIC_EXT}") +message(STATUS "Storm - Assuming extension for shared libraries: ${DYNAMIC_EXT}") +message(STATUS "Storm - Assuming extension for static libraries: ${STATIC_EXT}") if(BUILD_SHARED_LIBS) set(LIB_EXT ${DYNAMIC_EXT}) - message(STATUS "Build dynamic libraries.") + message(STATUS "Storm - Build dynamic libraries.") else() set(LIB_EXT ${STATIC_EXT}) - message(STATUS "Build static libraries.") + message(STATUS "Storm - Build static libraries.") endif() ############################################################# @@ -169,7 +177,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") set(STORM_COMPILER_APPLECLANG ON) set(CLANG ON) set(STORM_COMPILER_ID "AppleClang") - set(CMAKE_MACOSX_RPATH ON) + set(CMAKE_MACOSX_RPATH ON) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(GCC ON) # using GCC @@ -213,16 +221,16 @@ if (STORM_COMPILER_CLANG OR STORM_COMPILER_APPLECLANG) if(FORCE_COLOR) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") endif() - + if (LINUX) set(CLANG_STDLIB libstdc++) else() set(CLANG_STDLIB libc++) endif() - + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -stdlib=${CLANG_STDLIB} -ftemplate-depth=1024") - set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffast-math -fno-finite-math-only") - + set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + if(LINUX) set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -rdynamic") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic") @@ -232,20 +240,20 @@ if (STORM_COMPILER_CLANG OR STORM_COMPILER_APPLECLANG) endif() elseif (STORM_COMPILER_GCC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fprefetch-loop-arrays -ffast-math -fno-finite-math-only") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fprefetch-loop-arrays") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -rdynamic") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic") endif () if (STORM_USE_LTO) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto") - + # Fix for problems that occurred when using LTO on gcc. This should be removed when it # is not needed anymore as it makes the the already long link-step potentially longer. if (STORM_COMPILER_GCC) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto-partition=none") endif() - + message(STATUS "Storm - Enabling link-time optimizations.") else() message(STATUS "Storm - Disabling link-time optimizations.") @@ -402,16 +410,22 @@ set(STORM_VERSION_MAJOR "${CMAKE_MATCH_1}") set(STORM_VERSION_MINOR "${CMAKE_MATCH_2}") set(STORM_VERSION_PATCH "${CMAKE_MATCH_3}") set(STORM_GIT_VERSION_REST "${CMAKE_MATCH_4}") -# parse rest of the form (-label)-commitsahead-hash-appendix +# parse rest of the form (-label)-commitsahead-hash(-appendix) string(REGEX MATCH "^(\\-([a-z][a-z0-9\\.]+))?\\-([0-9]+)\\-([a-z0-9]+)(\\-.*)?$" STORM_VERSION_REST_MATCH "${STORM_GIT_VERSION_REST}") set(STORM_VERSION_LABEL "${CMAKE_MATCH_2}") # might be empty set(STORM_VERSION_COMMITS_AHEAD "${CMAKE_MATCH_3}") -set(STORM_VERSION_TAG_HASH "${CMAKE_MATCH_4}") +set(STORM_VERSION_TAG_HASH "${CMAKE_MATCH_4}") # is not used set(STORM_VERSION_APPENDIX "${CMAKE_MATCH_5}") # might be empty - -set(STORM_VERSION_DIRTY boost::none) -if (NOT "${STORM_GIT_VERSION_STRING}" STREQUAL "") +# check whether the git version lookup failed +if (STORM_GIT_VERSION_STRING MATCHES "NOTFOUND") + set(STORM_VERSION_SOURCE "VersionSource::Static") + set(STORM_VERSION_COMMITS_AHEAD 0) + set(STORM_VERSION_DIRTY boost::none) + include(version.cmake) + message(WARNING "Storm - Git version information not available, statically assuming version ${STORM_VERSION_MAJOR}.${STORM_VERSION_MINOR}.${STORM_VERSION_PATCH}.") +else() + set(STORM_VERSION_SOURCE "VersionSource::Git") if ("${STORM_VERSION_APPENDIX}" MATCHES "^.*dirty.*$") set(STORM_VERSION_DIRTY "true") else() @@ -419,15 +433,6 @@ if (NOT "${STORM_GIT_VERSION_STRING}" STREQUAL "") endif() endif() -# now check whether the git version lookup failed -set(STORM_VERSION_SOURCE "VersionSource::Git") -if (STORM_GIT_VERSION_STRING MATCHES "NOTFOUND") - set(STORM_VERSION_SOURCE "VersionSource::Static") - set(STORM_VERSION_COMMITS_AHEAD "boost::none") - include(version.cmake) - message(WARNING "Storm - git version information not available, statically assuming version ${STORM_VERSION_MAJOR}.${STORM_VERSION_MINOR}.${STORM_VERSION_PATCH}.") -endif() - # check whether there is a label ('alpha', 'pre', etc.) if ("${STORM_VERSION_LABEL}" STREQUAL "") set(STORM_VERSION_LABEL_STRING "") @@ -436,18 +441,19 @@ else() endif() # check for development version with commits ahead of latest tag -set(STORM_VERSION_DEV "false") -set(STORM_VERSION_DEV_STRING "") if(STORM_VERSION_COMMITS_AHEAD) + set(STORM_VERSION_DEV "true") + set(STORM_VERSION_DEV_STRING " (dev)") if ("${STORM_VERSION_LABEL}" STREQUAL "") # increase patch number to indicate newer version MATH(EXPR STORM_VERSION_DEV_PATCH "${STORM_VERSION_PATCH}+1") else() set(STORM_VERSION_DEV_PATCH "${STORM_VERSION_PATCH}") endif() - set(STORM_VERSION_DEV "true") - set(STORM_VERSION_DEV_STRING " (dev)") else() + set(STORM_VERSION_COMMITS_AHEAD 0) + set(STORM_VERSION_DEV "false") + set(STORM_VERSION_DEV_STRING "") set(STORM_VERSION_DEV_PATCH ${STORM_VERSION_PATCH}) endif() @@ -455,7 +461,7 @@ endif() set(STORM_VERSION "${STORM_VERSION_MAJOR}.${STORM_VERSION_MINOR}.${STORM_VERSION_DEV_PATCH}") set(STORM_VERSION_STRING "${STORM_VERSION}${STORM_VERSION_LABEL_STRING}${STORM_VERSION_DEV_STRING}") -message(STATUS "Storm - version is ${STORM_VERSION_STRING} (version ${STORM_VERSION_MAJOR}.${STORM_VERSION_MINOR}.${STORM_VERSION_PATCH}${STORM_VERSION_LABEL_STRING} + ${STORM_VERSION_COMMITS_AHEAD} commits), building from git: ${STORM_VERSION_GIT_HASH} (dirty: ${STORM_VERSION_DIRTY}).") +message(STATUS "Storm - Version is ${STORM_VERSION_STRING} (version ${STORM_VERSION_MAJOR}.${STORM_VERSION_MINOR}.${STORM_VERSION_PATCH}${STORM_VERSION_LABEL_STRING} + ${STORM_VERSION_COMMITS_AHEAD} commits), building from git: ${STORM_VERSION_GIT_HASH} (dirty: ${STORM_VERSION_DIRTY}).") # Configure a header file to pass some of the CMake settings to the source code @@ -490,4 +496,8 @@ add_subdirectory(src) include(export) +install(FILES ${CMAKE_BINARY_DIR}/stormConfig.install.cmake DESTINATION ${CMAKE_INSTALL_DIR} RENAME stormConfig.cmake) +install(FILES ${CMAKE_BINARY_DIR}/stormConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_DIR}) +install(EXPORT storm_Targets FILE stormTargets.cmake DESTINATION ${CMAKE_INSTALL_DIR}) + include(StormCPackConfig.cmake) diff --git a/doc/checklist_new_release.md b/doc/checklist_new_release.md index 05be534aa..a55349ef5 100644 --- a/doc/checklist_new_release.md +++ b/doc/checklist_new_release.md @@ -1,5 +1,5 @@ The following steps should be performed before releasing a new storm version. -Note that in most case a simultaneous release of [carl](https://github.com/smtrat/carl), [storm](https://github.com/moves-rwth/storm), [pycarl](https://github.com/moves-rwth/pycarl/) and [stormpy](https://github.com/moves-rwth/stormpy/) is preferred. +Note that in most cases a simultaneous release of [carl](https://github.com/smtrat/carl), [storm](https://github.com/moves-rwth/storm), [pycarl](https://github.com/moves-rwth/pycarl/) and [stormpy](https://github.com/moves-rwth/stormpy/) is preferred. 1. Update `CHANGELOG.md` To get all the commits from an author since the last tag execute: diff --git a/resources/3rdparty/CMakeLists.txt b/resources/3rdparty/CMakeLists.txt index 5957f3b51..18a68a23f 100644 --- a/resources/3rdparty/CMakeLists.txt +++ b/resources/3rdparty/CMakeLists.txt @@ -168,6 +168,15 @@ else() message (WARNING "Storm - Z3 not found. Building of Prism/JANI models will not be supported.") endif(Z3_FOUND) +# split Z3 version into its components +string(REPLACE "." ";" Z3_VERSION_LIST ${Z3_VERSION}) +list(GET Z3_VERSION_LIST 0 STORM_Z3_VERSION_MAJOR) +list(GET Z3_VERSION_LIST 1 STORM_Z3_VERSION_MINOR) +list(GET Z3_VERSION_LIST 2 STORM_Z3_VERSION_PATCH) +if (NOT "${Z3_VERSION}" VERSION_LESS "4.7.1") + set(STORM_Z3_API_USES_STANDARD_INTEGERS ON) +endif() + ############################################################# ## ## glpk @@ -239,16 +248,16 @@ if(carl_FOUND AND NOT STORM_FORCE_SHIPPED_CARL) else() set(STORM_SHIPPED_CARL ON) # The first external project will be built at *configure stage* - message("START CARL CONFIG PROCESS") - file(MAKE_DIRECTORY ${STORM_3RDPARTY_BINARY_DIR}/carl_download) + message(STATUS "Carl - Start of config process") + file(MAKE_DIRECTORY ${STORM_3RDPARTY_BINARY_DIR}/carl_download) execute_process( COMMAND ${CMAKE_COMMAND} ${STORM_3RDPARTY_SOURCE_DIR}/carl "-DSTORM_3RDPARTY_BINARY_DIR=${STORM_3RDPARTY_BINARY_DIR}" "-DBoost_LIBRARY_DIRS=${Boost_LIBRARY_DIRS}" "-DBoost_INCLUDE_DIRS=${Boost_INCLUDE_DIRS}" WORKING_DIRECTORY ${STORM_3RDPARTY_BINARY_DIR}/carl_download OUTPUT_VARIABLE carlconfig_out RESULT_VARIABLE carlconfig_result) - + if(NOT carlconfig_result) - message("${carlconfig_out}") + message(STATUS "${carlconfig_out}") endif() execute_process( COMMAND ${CMAKE_COMMAND} --build . --target carl-config @@ -257,10 +266,10 @@ else() RESULT_VARIABLE carlconfig_result ) if(NOT carlconfig_result) - message("${carlconfig_out}") + message(STATUS "${carlconfig_out}") endif() - message("END CARL CONFIG PROCESS") - + message(STATUS "Carl - End of config process") + message(STATUS "Storm - Using shipped version of carl.") ExternalProject_Add( carl diff --git a/resources/3rdparty/carl/CMakeLists.txt b/resources/3rdparty/carl/CMakeLists.txt index 4b819682d..e74d53de6 100644 --- a/resources/3rdparty/carl/CMakeLists.txt +++ b/resources/3rdparty/carl/CMakeLists.txt @@ -4,11 +4,11 @@ include(ExternalProject) option(STORM_3RDPARTY_BINARY_DIR "3rd party bin dir") -message(STORM_3RDPARTY_BINARY_DIR: ${STORM_3RDPARTY_BINARY_DIR}) +message(STATUS "Carl - Storm 3rdparty binary dir: ${STORM_3RDPARTY_BINARY_DIR}") ExternalProject_Add(carl-config GIT_REPOSITORY https://github.com/smtrat/carl - GIT_TAG 17.12 + GIT_TAG 18.06 PREFIX here SOURCE_DIR source_dir BINARY_DIR ${STORM_3RDPARTY_BINARY_DIR}/carl @@ -22,4 +22,4 @@ ExternalProject_Add(carl-config add_custom_target(build-carl) add_dependencies(build-carl carl-config) -message("done") +message(STATUS "Carl - Finished configuration.") diff --git a/resources/cmake/macros/export.cmake b/resources/cmake/macros/export.cmake index eb2e8fa84..606bde62f 100644 --- a/resources/cmake/macros/export.cmake +++ b/resources/cmake/macros/export.cmake @@ -8,7 +8,7 @@ message(STATUS "Registered with cmake") export(PACKAGE storm) set(DEP_TARGETS "") -foreach(dt ${STORM_DEP_TARGETS}) +foreach(dt ${STORM_DEP_TARGETS}) export_target(DEP_TARGETS ${dt}) endforeach() @@ -19,10 +19,26 @@ endforeach() include(CMakePackageConfigHelpers) +write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/stormConfigVersion.cmake + VERSION 0.1.0 + COMPATIBILITY SameMajorVersion ) + +# For the build tree set(CONF_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/include/") configure_package_config_file( resources/cmake/stormConfig.cmake.in ${PROJECT_BINARY_DIR}/stormConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_DIR} - PATH_VARS INCLUDE_INSTALL_DIR #SYSCONFIG_INSTALL_DIR + PATH_VARS INCLUDE_INSTALL_DIR +) + + # For the install tree +file(RELATIVE_PATH REL_INCLUDE_DIR "${CMAKE_INSTALL_DIR}" "${INCLUDE_INSTALL_DIR}") +set(CONF_INCLUDE_DIRS "\${storm_CMAKE_DIR}/${REL_INCLUDE_DIR}/storm") + +configure_package_config_file( + resources/cmake/stormConfig.cmake.in + ${PROJECT_BINARY_DIR}/stormConfig.install.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_DIR} + PATH_VARS INCLUDE_INSTALL_DIR ) diff --git a/resources/cmake/stormConfigVersion.cmake.in b/resources/cmake/stormConfigVersion.cmake.in new file mode 100644 index 000000000..d248393ad --- /dev/null +++ b/resources/cmake/stormConfigVersion.cmake.in @@ -0,0 +1,11 @@ +set(PACKAGE_VERSION "@storm_VERSION@") + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/resources/examples/testfiles/ctmc/simple2.sm b/resources/examples/testfiles/ctmc/simple2.sm new file mode 100644 index 000000000..5fcc0dcab --- /dev/null +++ b/resources/examples/testfiles/ctmc/simple2.sm @@ -0,0 +1,28 @@ + +ctmc + + +module main + + s : [0..4]; // current state: + + + <> s=0 -> 4 : (s'=1) + 4 : (s'=2); + <> s=1 -> 0.3 : (s'=2) + 0.7 : (s'=1); + <> s=2 -> 0.5 : (s'=2) + 0.5 : (s'=3); + <> s=3 -> 1 : (s'=4); + <> s=4 -> 1 : (s'=3); + +endmodule + +rewards "rew1" + s=0 : 7; + [] s=2 : 1; +endrewards + + +rewards "rew2" + s=0 : 7; + [] s=2 : 1; + [] s=4 : 100; +endrewards diff --git a/resources/examples/testfiles/dft/and.json b/resources/examples/testfiles/dft/and.json new file mode 100644 index 000000000..e6c0c0f2e --- /dev/null +++ b/resources/examples/testfiles/dft/and.json @@ -0,0 +1,71 @@ +{ + "toplevel": "2", + "parameters": {}, + "nodes": [ + { + "data": { + "id": "0", + "name": "A", + "type": "be", + "rate": "1", + "dorm": "1", + "label": "A (1)" + }, + "position": { + "x": 440, + "y": 260 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "classes": "be" + }, + { + "data": { + "id": "1", + "name": "B", + "type": "be", + "rate": "1", + "dorm": "1", + "label": "B (1)" + }, + "position": { + "x": 548, + "y": 265 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "classes": "be" + }, + { + "data": { + "id": "2", + "name": "Z", + "type": "and", + "children": [ + "0", + "1" + ], + "label": "Z" + }, + "position": { + "x": 505, + "y": 119 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "classes": "and" + } + ] +} diff --git a/resources/examples/testfiles/ma/simple2.ma b/resources/examples/testfiles/ma/simple2.ma new file mode 100644 index 000000000..46fa2207b --- /dev/null +++ b/resources/examples/testfiles/ma/simple2.ma @@ -0,0 +1,42 @@ + +ma + + +module main + + s : [0..5]; // current state: + + + <> s=0 -> 4 : (s'=1) + 4 : (s'=2); + [alpha] s=1 -> 1 : (s'=0); + [beta] s=1 -> 0.3 : (s'=5) + 0.7 : (s'=1); + <> s=5 -> 1 : (s'=2); + [gamma] s=2 -> 1 : (s'=1); + [delta] s=2 -> 0.5 : (s'=2) + 0.5 : (s'=3); + <> s=3 -> 1 : (s'=4); + [lambda] s=4 -> 1 : (s'=3); + +endmodule + +rewards "rew0" + [delta] s=2 : 1; +endrewards + +rewards "rew1" + s=0 : 7; + [delta] s=2 : 1; +endrewards + + +rewards "rew2" + s=0 : 7; + [delta] s=2 : 1; + [lambda] s=4 : 100; +endrewards + +rewards "rew3" + s=0 : 7; + [delta] s=2 : 1; + [gamma] s=2 : 100; + [lambda] s=4 : 27; +endrewards diff --git a/resources/examples/testfiles/mdp/prism-mec-example1.nm b/resources/examples/testfiles/mdp/prism-mec-example1.nm new file mode 100644 index 000000000..bb8ec7d17 --- /dev/null +++ b/resources/examples/testfiles/mdp/prism-mec-example1.nm @@ -0,0 +1,12 @@ +mdp + +module test + + x : [0..2]; + + [] x=0 -> true; + [] x=0 -> 0.5 : (x'=1) + 0.5: (x'=2); + [] x=1 -> (x'=0); + [] x=2 -> true; + +endmodule diff --git a/resources/examples/testfiles/mdp/prism-mec-example2.nm b/resources/examples/testfiles/mdp/prism-mec-example2.nm new file mode 100644 index 000000000..7ef54d4b2 --- /dev/null +++ b/resources/examples/testfiles/mdp/prism-mec-example2.nm @@ -0,0 +1,13 @@ +mdp + +module test + + x : [0..2]; + + [] x=0 -> true; + [] x=0 -> 0.5 : (x'=1) + 0.5: (x'=1); + [] x=0 -> (x'=2); + [] x=1 -> (x'=0); + [] x=2 -> true; + +endmodule diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index acf819119..6c61656ab 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) add_custom_target(binaries) add_subdirectory(storm) +add_subdirectory(storm-counterexamples) +add_subdirectory(storm-parsers) add_subdirectory(storm-cli-utilities) add_subdirectory(storm-pgcl) add_subdirectory(storm-pgcl-cli) @@ -14,7 +16,8 @@ add_subdirectory(storm-dft) add_subdirectory(storm-dft-cli) add_subdirectory(storm-pars) add_subdirectory(storm-pars-cli) - +add_subdirectory(storm-conv) +add_subdirectory(storm-conv-cli) add_subdirectory(test) diff --git a/src/storm-cli-utilities/CMakeLists.txt b/src/storm-cli-utilities/CMakeLists.txt index 64c2848c7..de4c0fde1 100644 --- a/src/storm-cli-utilities/CMakeLists.txt +++ b/src/storm-cli-utilities/CMakeLists.txt @@ -17,7 +17,7 @@ set_target_properties(storm-cli-utilities PROPERTIES DEFINE_SYMBOL "") list(APPEND STORM_TARGETS storm-cli-utilities) set(STORM_TARGETS ${STORM_TARGETS} PARENT_SCOPE) -target_link_libraries(storm-cli-utilities PUBLIC storm) +target_link_libraries(storm-cli-utilities PUBLIC storm storm-counterexamples storm-parsers) # Install storm headers to include directory. foreach(HEADER ${STORM_CLI_UTIL_HEADERS}) @@ -36,5 +36,5 @@ add_custom_target(copy_storm_cli_util_headers DEPENDS ${STORM_CLI_UTIL_OUTPUT_HE add_dependencies(storm-cli-utilities copy_storm_pars_headers) # installation -install(TARGETS storm-cli-utilities RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) +install(TARGETS storm-cli-utilities EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-cli-utilities/cli.cpp b/src/storm-cli-utilities/cli.cpp index dfc3065b8..e79971cbc 100644 --- a/src/storm-cli-utilities/cli.cpp +++ b/src/storm-cli-utilities/cli.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "storm-cli-utilities/model-handling.h" @@ -48,6 +49,8 @@ namespace storm { storm::cli::printHeader("Storm", argc, argv); storm::settings::initializeAll("Storm", "storm"); + storm::settings::addModule(); + storm::utility::Stopwatch totalTimer(true); if (!storm::cli::parseOptions(argc, argv)) { return -1; @@ -63,7 +66,27 @@ namespace storm { storm::utility::cleanUp(); return 0; } - + + std::string shellQuoteSingleIfNecessary(const std::string& arg) { + // quote empty argument + if (arg.empty()) { + return "''"; + } + + if (arg.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_./=") != std::string::npos) { + // contains potentially unsafe character, needs quoting + if (arg.find('\'') != std::string::npos) { + // contains ', we have to replace all ' with '\'' + std::string escaped(arg); + boost::replace_all(escaped, "'", "'\\''"); + return "'" + escaped + "'"; + } else { + return "'" + arg + "'"; + } + } + + return arg; + } void printHeader(std::string const& name, const int argc, const char** argv) { STORM_PRINT(name << " " << storm::utility::StormVersion::shortVersionString() << std::endl << std::endl); @@ -71,7 +94,7 @@ namespace storm { // "Compute" the command line argument string with which storm was invoked. std::stringstream commandStream; for (int i = 1; i < argc; ++i) { - commandStream << argv[i] << " "; + commandStream << " " << shellQuoteSingleIfNecessary(argv[i]); } std::string command = commandStream.str(); @@ -79,7 +102,7 @@ namespace storm { if (!command.empty()) { std::time_t result = std::time(nullptr); STORM_PRINT("Date: " << std::ctime(&result)); - STORM_PRINT("Command line arguments: " << commandStream.str() << std::endl); + STORM_PRINT("Command line arguments:" << commandStream.str() << std::endl); STORM_PRINT("Current working directory: " << storm::utility::cli::getCurrentWorkingDirectory() << std::endl << std::endl); } } diff --git a/src/storm-cli-utilities/cli.h b/src/storm-cli-utilities/cli.h index e8d8601e9..66c51bad6 100644 --- a/src/storm-cli-utilities/cli.h +++ b/src/storm-cli-utilities/cli.h @@ -11,6 +11,13 @@ namespace storm { */ int64_t process(const int argc, const char** argv); + /*! + * For a command-line argument, returns a quoted version + * with single quotes if it contains unsafe characters. + * Otherwise, just returns the unquoted argument. + */ + std::string shellQuoteSingleIfNecessary(const std::string& arg); + void printHeader(std::string const& name, const int argc, const char** argv); void printVersion(std::string const& name); diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h index d03cfe630..f60a3ffb5 100644 --- a/src/storm-cli-utilities/model-handling.h +++ b/src/storm-cli-utilities/model-handling.h @@ -2,6 +2,9 @@ #include "storm/api/storm.h" +#include "storm-counterexamples/api/counterexamples.h" +#include "storm-parsers/api/storm-parsers.h" + #include "storm/utility/resources.h" #include "storm/utility/file.h" #include "storm/utility/storm-version.h" @@ -14,6 +17,7 @@ #include "storm/storage/SymbolicModelDescription.h" +#include "storm/storage/jani/Property.h" #include "storm/models/ModelBase.h" @@ -34,7 +38,6 @@ #include "storm/settings/modules/CoreSettings.h" #include "storm/settings/modules/AbstractionSettings.h" #include "storm/settings/modules/ResourceSettings.h" -#include "storm/settings/modules/JaniExportSettings.h" #include "storm/utility/Stopwatch.h" @@ -63,10 +66,16 @@ namespace storm { auto const& janiPropertyInput = janiInput.second; if (ioSettings.isJaniPropertiesSet()) { - for (auto const& propName : ioSettings.getJaniProperties()) { - auto propertyIt = janiPropertyInput.find(propName); - STORM_LOG_THROW(propertyIt != janiPropertyInput.end(), storm::exceptions::InvalidArgumentException, "No JANI property with name '" << propName << "' is known."); - input.properties.emplace_back(propertyIt->second); + if (ioSettings.areJaniPropertiesSelected()) { + for (auto const& propName : ioSettings.getSelectedJaniProperties()) { + auto propertyIt = janiPropertyInput.find(propName); + STORM_LOG_THROW(propertyIt != janiPropertyInput.end(), storm::exceptions::InvalidArgumentException, "No JANI property with name '" << propName << "' is known."); + input.properties.emplace_back(propertyIt->second); + } + } else { + for (auto const& property : janiPropertyInput) { + input.properties.emplace_back(property.second); + } } } } @@ -126,7 +135,7 @@ namespace storm { if (transformToJani) { storm::prism::Program const& model = output.model.get().asPrismProgram(); - auto modelAndRenaming = model.toJaniWithLabelRenaming(true); + auto modelAndRenaming = model.toJaniWithLabelRenaming(true, "", false); output.model = modelAndRenaming.first; if (!modelAndRenaming.second.empty()) { @@ -150,10 +159,6 @@ namespace storm { if (ioSettings.isExportJaniDotSet()) { storm::api::exportJaniModelAsDot(model.asJaniModel(), ioSettings.getExportJaniDotFilename()); } - - if (model.isJaniModel() && storm::settings::getModule().isJaniFileSet()) { - storm::api::exportJaniModel(model.asJaniModel(), input.properties, storm::settings::getModule().getJaniFilename()); - } } } @@ -183,11 +188,15 @@ namespace storm { template std::shared_ptr buildModelSparse(SymbolicInput const& input, storm::settings::modules::BuildSettings const& buildSettings) { - auto counterexampleGeneratorSettings = storm::settings::getModule(); storm::builder::BuilderOptions options(createFormulasToRespect(input.properties)); options.setBuildChoiceLabels(buildSettings.isBuildChoiceLabelsSet()); options.setBuildStateValuations(buildSettings.isBuildStateValuationsSet()); - options.setBuildChoiceOrigins(counterexampleGeneratorSettings.isMinimalCommandSetGenerationSet()); + if (storm::settings::manager().hasModule(storm::settings::modules::CounterexampleGeneratorSettings::moduleName)) { + auto counterexampleGeneratorSettings = storm::settings::getModule(); + options.setBuildChoiceOrigins(counterexampleGeneratorSettings.isMinimalCommandSetGenerationSet()); + } else { + options.setBuildChoiceOrigins(false); + } options.setBuildAllLabels(buildSettings.isBuildFullModelSet()); options.setBuildAllRewardModels(buildSettings.isBuildFullModelSet()); options.setAddOutOfBoundsState(buildSettings.isBuildOutOfBoundsStateSet()); @@ -356,18 +365,12 @@ namespace storm { result->second = true; std::shared_ptr> symbolicModel = result->first->template as>(); - if (symbolicModel->isOfType(storm::models::ModelType::Dtmc)) { - storm::transformer::SymbolicDtmcToSparseDtmcTransformer transformer; - result->first = transformer.translate(*symbolicModel->template as>()); - } else if (symbolicModel->isOfType(storm::models::ModelType::Ctmc)) { - storm::transformer::SymbolicCtmcToSparseCtmcTransformer transformer; - result->first = transformer.translate(*symbolicModel->template as>()); - } else if (symbolicModel->isOfType(storm::models::ModelType::Mdp)) { - storm::transformer::SymbolicMdpToSparseMdpTransformer transformer; - result->first = transformer.translate(*symbolicModel->template as>()); - } else { - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The translation to a sparse model is not supported for the given model type."); + std::vector> formulas; + for (auto const& property : input.properties) { + formulas.emplace_back(property.getRawFormula()); } + result->first = storm::api::transformSymbolicToSparseModel(symbolicModel, formulas); + STORM_LOG_THROW(result, storm::exceptions::NotSupportedException, "The translation to a sparse model is not supported for the given model type."); } return *result; @@ -419,7 +422,10 @@ namespace storm { STORM_LOG_THROW(model->isSparseModel(), storm::exceptions::NotSupportedException, "Counterexample generation is currently only supported for sparse models."); auto sparseModel = model->as>(); - + for (auto& rewModel : sparseModel->getRewardModels()) { + rewModel.second.reduceToStateBasedRewards(sparseModel->getTransitionMatrix(), true); + } + STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc) || sparseModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "Counterexample is currently only supported for discrete-time models."); auto counterexampleSettings = storm::settings::getModule(); @@ -505,7 +511,7 @@ namespace storm { } void printModelCheckingProperty(storm::jani::Property const& property) { - STORM_PRINT(std::endl << "Model checking property " << *property.getRawFormula() << " ..." << std::endl); + STORM_PRINT(std::endl << "Model checking property \"" << property.getName() << "\": " << *property.getRawFormula() << " ..." << std::endl); } template diff --git a/src/storm-conv-cli/CMakeLists.txt b/src/storm-conv-cli/CMakeLists.txt new file mode 100644 index 000000000..72a311b11 --- /dev/null +++ b/src/storm-conv-cli/CMakeLists.txt @@ -0,0 +1,9 @@ +# Create storm-conv. +add_executable(storm-conv-cli ${PROJECT_SOURCE_DIR}/src/storm-conv-cli/storm-conv.cpp) +target_link_libraries(storm-conv-cli storm-conv storm-cli-utilities) # Adding headers for xcode +set_target_properties(storm-conv-cli PROPERTIES OUTPUT_NAME "storm-conv") + +add_dependencies(binaries storm-conv-cli) + +# installation +install(TARGETS storm-conv-cli EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-conv-cli/storm-conv.cpp b/src/storm-conv-cli/storm-conv.cpp new file mode 100644 index 000000000..a499fccf2 --- /dev/null +++ b/src/storm-conv-cli/storm-conv.cpp @@ -0,0 +1,207 @@ + +#include "storm-conv/api/storm-conv.h" + +#include "storm/settings/SettingsManager.h" +#include "storm-conv/settings/ConvSettings.h" +#include "storm-conv/settings/modules/ConversionGeneralSettings.h" +#include "storm-conv/settings/modules/ConversionInputSettings.h" +#include "storm-conv/settings/modules/ConversionOutputSettings.h" + +#include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" +#include "storm/utility/initialize.h" +#include "storm/utility/macros.h" + +#include "storm/storage/SymbolicModelDescription.h" +#include "storm/storage/jani/Model.h" +#include "storm/storage/jani/Property.h" + + +#include "storm-cli-utilities/cli.h" +#include "storm/exceptions/OptionParserException.h" + +namespace storm { + namespace conv { + + void setUrgentOptions() { + + // Set the correct log level + if (storm::settings::getModule().isStdOutOutputEnabled()) { + storm::utility::setLogLevel(l3pp::LogLevel::OFF); + } else { + auto const& general = storm::settings::getModule(); + if (general.isVerboseSet()) { + storm::utility::setLogLevel(l3pp::LogLevel::INFO); + } + if (general.isDebugOutputSet()) { + storm::utility::setLogLevel(l3pp::LogLevel::DEBUG); + } + if (general.isTraceOutputSet()) { + storm::utility::setLogLevel(l3pp::LogLevel::TRACE); + } + } + } + + void processPrismInputJaniOutput(storm::prism::Program const& prismProg, std::vector const& properties) { + auto const& output = storm::settings::getModule(); + auto const& input = storm::settings::getModule(); + auto const& jani = storm::settings::getModule(); + + storm::converter::PrismToJaniConverterOptions options; + options.allVariablesGlobal = jani.isGlobalVarsSet(); + options.suffix = ""; + options.janiOptions = storm::converter::JaniConversionOptions(jani); + + std::string outputFilename = ""; + if (output.isJaniOutputFilenameSet()) { + outputFilename = output.getJaniOutputFilename(); + } else if (input.isPrismInputSet() && !output.isStdOutOutputEnabled()) { + outputFilename = input.getPrismInputFilename(); + // Remove extension if present + auto dotPos = outputFilename.rfind('.'); + if (dotPos != std::string::npos) { + outputFilename.erase(dotPos); + } + std::string suffix = ""; + if (input.isConstantsSet()) { + suffix = input.getConstantDefinitionString(); + std::replace(suffix.begin(), suffix.end(), ',', '_'); + std::replace(suffix.begin(), suffix.end(), '=', '-'); + } + suffix = suffix + ".jani"; + outputFilename += suffix; + } + auto startOfFilename = outputFilename.rfind("/"); + if (startOfFilename == std::string::npos) { + startOfFilename = 0; + } else { + ++startOfFilename; + } + auto endOfFilename = outputFilename.rfind("."); + if (endOfFilename == std::string::npos) { + endOfFilename = outputFilename.size(); + } + options.janiOptions.modelName = outputFilename.substr(startOfFilename, endOfFilename - startOfFilename); + + auto janiModelProperties = storm::api::convertPrismToJani(prismProg, properties, options); + + if (outputFilename != "") { + storm::api::exportJaniToFile(janiModelProperties.first, janiModelProperties.second, outputFilename, jani.isCompactJsonSet()); + } + + if (output.isStdOutOutputEnabled()) { + storm::api::printJaniToStream(janiModelProperties.first, janiModelProperties.second, std::cout, jani.isCompactJsonSet()); + } + } + + void processPrismInput() { + auto const& input = storm::settings::getModule(); + + // Parse the prism program + storm::storage::SymbolicModelDescription prismProg = storm::api::parseProgram(input.getPrismInputFilename(), input.isPrismCompatibilityEnabled()); + + // Parse properties (if available) + std::vector properties; + if (input.isPropertyInputSet()) { + boost::optional> propertyFilter = storm::api::parsePropertyFilter(input.getPropertyInputFilter()); + properties = storm::api::parsePropertiesForSymbolicModelDescription(input.getPropertyInput(), prismProg, propertyFilter); + } + + // Substitute constant definitions in program and properties. + std::string constantDefinitionString = input.getConstantDefinitionString(); + auto constantDefinitions = prismProg.parseConstantDefinitions(constantDefinitionString); + prismProg = prismProg.preprocess(constantDefinitions); + if (!properties.empty()) { + properties = storm::api::substituteConstantsInProperties(properties, constantDefinitions); + } + + // Branch on the type of output + auto const& output = storm::settings::getModule(); + if (output.isJaniOutputSet()) { + processPrismInputJaniOutput(prismProg.asPrismProgram(), properties); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "There is either no outputformat specified or the provided combination of input and output format is not compatible."); + } + } + + void processOptions() { + // Start by setting some urgent options (log levels, etc.) + setUrgentOptions(); + + // Branch on the type of input + auto const& input = storm::settings::getModule(); + if (input.isPrismInputSet()) { + processPrismInput(); + } + } + } +} + +bool parseOptions(const int argc, const char* argv[]) { + try { + storm::settings::mutableManager().setFromCommandLine(argc, argv); + } catch (storm::exceptions::OptionParserException& e) { + storm::settings::manager().printHelp(); + throw e; + return false; + } + + auto const& general = storm::settings::getModule(); + + // Set options from config file (if given) + if (general.isConfigSet()) { + storm::settings::mutableManager().setFromConfigurationFile(general.getConfigFilename()); + } + + bool result = true; + if (general.isHelpSet()) { + storm::settings::manager().printHelp(general.getHelpModuleName()); + result = false; + } + + if (general.isVersionSet()) { + storm::cli::printVersion("storm-conv"); + result = false;; + } + + return result; +} + +/*! + * Main entry point of the executable storm-conv. + */ +int main(const int argc, const char** argv) { + + try { + storm::utility::setUp(); + + // Print header info only if output to sdtout is disabled + bool outputToStdOut = false; + for (int i = 1; i < argc; ++i) { + if (std::string(argv[i]) == "--" + storm::settings::modules::ConversionOutputSettings::stdoutOptionName) { + outputToStdOut = true; + } + } + if (outputToStdOut) { + storm::utility::setLogLevel(l3pp::LogLevel::OFF); + } else { + storm::cli::printHeader("Storm-conv", argc, argv); + } + + storm::settings::initializeConvSettings("Storm-conv", "storm-conv"); + if (!parseOptions(argc, argv)) { + return -1; + } + + storm::conv::processOptions(); + + storm::utility::cleanUp(); + return 0; + } catch (storm::exceptions::BaseException const& exception) { + STORM_LOG_ERROR("An exception caused Storm-conv to terminate. The message of the exception is: " << exception.what()); + return 1; + } catch (std::exception const& exception) { + STORM_LOG_ERROR("An unexpected exception occurred and caused Storm-conv to terminate. The message of this exception is: " << exception.what()); + return 2; + } +} diff --git a/src/storm-conv/CMakeLists.txt b/src/storm-conv/CMakeLists.txt new file mode 100644 index 000000000..4ead2828e --- /dev/null +++ b/src/storm-conv/CMakeLists.txt @@ -0,0 +1,40 @@ +file(GLOB_RECURSE ALL_FILES ${PROJECT_SOURCE_DIR}/src/storm-conv/*.h ${PROJECT_SOURCE_DIR}/src/storm-conv/*.cpp) + +register_source_groups_from_filestructure("${ALL_FILES}" storm-conv) + + + +file(GLOB_RECURSE STORM_CONV_SOURCES ${PROJECT_SOURCE_DIR}/src/storm-conv/*/*.cpp) +file(GLOB_RECURSE STORM_CONV_HEADERS ${PROJECT_SOURCE_DIR}/src/storm-conv/*/*.h) + + +# Create storm-conv. +add_library(storm-conv SHARED ${STORM_CONV_SOURCES} ${STORM_CONV_HEADERS}) + +# Remove define symbol for shared libstorm. +set_target_properties(storm-conv PROPERTIES DEFINE_SYMBOL "") +#add_dependencies(storm resources) +list(APPEND STORM_TARGETS storm-conv) +set(STORM_TARGETS ${STORM_TARGETS} PARENT_SCOPE) + +target_link_libraries(storm-conv PUBLIC storm ${STORM_CONV_LINK_LIBRARIES}) + +# Install storm headers to include directory. +foreach(HEADER ${STORM_CONV_HEADERS}) + string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/src/?" "" RELATIVE_HEADER_PATH ${HEADER}) + string(REGEX MATCH "(.*)[/\\]" RELATIVE_DIRECTORY ${RELATIVE_HEADER_PATH}) + string(REGEX REPLACE "${RELATIVE_DIRECTORY}/?" "" HEADER_FILENAME ${RELATIVE_HEADER_PATH}) + add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${HEADER} ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME} + DEPENDS ${HEADER} + ) + list(APPEND STORM_CONV_OUTPUT_HEADERS "${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME}") +endforeach() +add_custom_target(copy_storm_conv_headers DEPENDS ${STORM_CONV_OUTPUT_HEADERS} ${STORM_CONV_HEADERS}) +add_dependencies(storm-conv copy_storm_conv_headers) + +# installation +install(TARGETS storm-conv EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) + diff --git a/src/storm-conv/api/storm-conv.cpp b/src/storm-conv/api/storm-conv.cpp new file mode 100644 index 000000000..aa37dd191 --- /dev/null +++ b/src/storm-conv/api/storm-conv.cpp @@ -0,0 +1,71 @@ +#include "storm-conv/api/storm-conv.h" + +#include "storm/storage/prism/Program.h" +#include "storm/storage/jani/Property.h" +#include "storm/storage/jani/JaniLocationExpander.h" +#include "storm/storage/jani/JSONExporter.h" + +#include "storm/settings/SettingsManager.h" +#include "storm/settings/modules/CoreSettings.h" + +namespace storm { + namespace api { + + void postprocessJani(storm::jani::Model& janiModel, storm::converter::JaniConversionOptions options) { + + if (!options.locationVariables.empty()) { + for (auto const& pair : options.locationVariables) { + storm::jani::JaniLocationExpander expander(janiModel); + expander.transform(pair.first, pair.second); + janiModel = expander.getResult(); + } + } + + if (options.exportFlattened) { + std::shared_ptr smtSolverFactory; + if (storm::settings::hasModule()) { + smtSolverFactory = std::make_shared(); + } else { + smtSolverFactory = std::make_shared(); + } + janiModel = janiModel.flattenComposition(smtSolverFactory); + } + + if (options.standardCompliant) { + janiModel.makeStandardJaniCompliant(); + } + + if (options.modelName) { + janiModel.setName(options.modelName.get()); + } + } + + std::pair> convertPrismToJani(storm::prism::Program const& program, std::vector const& properties, storm::converter::PrismToJaniConverterOptions options) { + std::pair> res; + + // Perform conversion + auto modelAndRenaming = program.toJaniWithLabelRenaming(options.allVariablesGlobal, options.suffix, options.janiOptions.standardCompliant); + res.first = std::move(modelAndRenaming.first); + + // Amend properties to potentially changed labels + for (auto const& property : properties) { + res.second.emplace_back(property.substituteLabels(modelAndRenaming.second)); + } + + // Postprocess Jani model based on the options + postprocessJani(res.first, options.janiOptions); + + return res; + } + + void exportJaniToFile(storm::jani::Model const& model, std::vector const& properties, std::string const& filename, bool compact) { + storm::jani::JsonExporter::toFile(model, properties, filename, true, compact); + } + + void printJaniToStream(storm::jani::Model const& model, std::vector const& properties, std::ostream& ostream, bool compact) { + storm::jani::JsonExporter::toStream(model, properties, ostream, true, compact); + } + + + } +} \ No newline at end of file diff --git a/src/storm-conv/api/storm-conv.h b/src/storm-conv/api/storm-conv.h new file mode 100644 index 000000000..42cbf804d --- /dev/null +++ b/src/storm-conv/api/storm-conv.h @@ -0,0 +1,28 @@ +#pragma once + +#include "storm-conv/converter/options/PrismToJaniConverterOptions.h" +#include "storm-conv/converter/options/JaniConversionOptions.h" + +namespace storm { + + namespace prism { + class Program; + } + namespace jani { + class Model; + class Property; + } + + namespace api { + + void postprocessJani(storm::jani::Model& janiModel, storm::converter::JaniConversionOptions options); + + std::pair> convertPrismToJani(storm::prism::Program const& program, std::vector const& properties = std::vector(), storm::converter::PrismToJaniConverterOptions options = storm::converter::PrismToJaniConverterOptions()); + + void exportJaniToFile(storm::jani::Model const& model, std::vector const& properties, std::string const& filename, bool compact = false); + + void printJaniToStream(storm::jani::Model const& model, std::vector const& properties, std::ostream& ostream, bool compact = false); + + + } +} \ No newline at end of file diff --git a/src/storm-conv/converter/options/JaniConversionOptions.cpp b/src/storm-conv/converter/options/JaniConversionOptions.cpp new file mode 100644 index 000000000..7b72c4797 --- /dev/null +++ b/src/storm-conv/converter/options/JaniConversionOptions.cpp @@ -0,0 +1,15 @@ +#include "storm-conv/converter/options/PrismToJaniConverterOptions.h" + +namespace storm { + namespace converter { + + JaniConversionOptions::JaniConversionOptions() : standardCompliant(false), exportFlattened(false) { + // Intentionally left empty + }; + + JaniConversionOptions::JaniConversionOptions(storm::settings::modules::JaniExportSettings const& settings) : locationVariables(settings.getLocationVariables()), standardCompliant(settings.isExportAsStandardJaniSet()), exportFlattened(settings.isExportFlattenedSet()) { + // Intentionally left empty + }; + } +} + diff --git a/src/storm-conv/converter/options/JaniConversionOptions.h b/src/storm-conv/converter/options/JaniConversionOptions.h new file mode 100644 index 000000000..000d9cc3b --- /dev/null +++ b/src/storm-conv/converter/options/JaniConversionOptions.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +#include "storm-conv/settings/modules/JaniExportSettings.h" + +namespace storm { + namespace converter { + + struct JaniConversionOptions { + + JaniConversionOptions(); + JaniConversionOptions(storm::settings::modules::JaniExportSettings const& settings); + + /// (Automaton,Variable)-pairs that will be transformed to location variables of the respective automaton. + std::vector> locationVariables; + + /// If set, the model will be made standard compliant (e.g. no state rewards for discrete time models) + bool standardCompliant; + + /// If set, the model is transformed into a single automaton + bool exportFlattened; + + /// If given, the model will get this name + boost::optional modelName; + + }; + } +} + diff --git a/src/storm-conv/converter/options/PrismToJaniConverterOptions.cpp b/src/storm-conv/converter/options/PrismToJaniConverterOptions.cpp new file mode 100644 index 000000000..a64f9edbc --- /dev/null +++ b/src/storm-conv/converter/options/PrismToJaniConverterOptions.cpp @@ -0,0 +1,12 @@ +#include "storm-conv/converter/options/PrismToJaniConverterOptions.h" + + +namespace storm { + namespace converter { + + PrismToJaniConverterOptions::PrismToJaniConverterOptions() : allVariablesGlobal(false), suffix("") { + // Intentionally left empty + }; + } +} + diff --git a/src/storm-conv/converter/options/PrismToJaniConverterOptions.h b/src/storm-conv/converter/options/PrismToJaniConverterOptions.h new file mode 100644 index 000000000..0d3d02c85 --- /dev/null +++ b/src/storm-conv/converter/options/PrismToJaniConverterOptions.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "storm-conv/converter/options/JaniConversionOptions.h" + + +namespace storm { + namespace converter { + + + struct PrismToJaniConverterOptions { + + PrismToJaniConverterOptions(); + + bool allVariablesGlobal; + std::string suffix; + JaniConversionOptions janiOptions; + }; + } +} + diff --git a/src/storm-conv/settings/ConvSettings.cpp b/src/storm-conv/settings/ConvSettings.cpp new file mode 100644 index 000000000..cbb872390 --- /dev/null +++ b/src/storm-conv/settings/ConvSettings.cpp @@ -0,0 +1,24 @@ +#include "storm-conv/settings/ConvSettings.h" + +#include "storm-conv/settings/modules/ConversionGeneralSettings.h" +#include "storm-conv/settings/modules/ConversionInputSettings.h" +#include "storm-conv/settings/modules/ConversionOutputSettings.h" +#include "storm-conv/settings/modules/JaniExportSettings.h" + +#include "storm/settings/SettingsManager.h" + + +namespace storm { + namespace settings { + void initializeConvSettings(std::string const& name, std::string const& executableName) { + storm::settings::mutableManager().setName(name, executableName); + + // Register relevant settings modules. + storm::settings::addModule(); + storm::settings::addModule(); + storm::settings::addModule(); + storm::settings::addModule(); + } + + } +} diff --git a/src/storm-conv/settings/ConvSettings.h b/src/storm-conv/settings/ConvSettings.h new file mode 100644 index 000000000..21cbcab31 --- /dev/null +++ b/src/storm-conv/settings/ConvSettings.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace storm { + namespace settings { + + void initializeConvSettings(std::string const& name, std::string const& executableName); + + } +} \ No newline at end of file diff --git a/src/storm-conv/settings/modules/ConversionGeneralSettings.cpp b/src/storm-conv/settings/modules/ConversionGeneralSettings.cpp new file mode 100644 index 000000000..4f1c65e74 --- /dev/null +++ b/src/storm-conv/settings/modules/ConversionGeneralSettings.cpp @@ -0,0 +1,77 @@ +#include "storm-conv/settings/modules/ConversionGeneralSettings.h" + +#include "storm/settings/SettingsManager.h" +#include "storm/settings/Option.h" +#include "storm/settings/OptionBuilder.h" +#include "storm/settings/ArgumentBuilder.h" +#include "storm/settings/Argument.h" + +namespace storm { + namespace settings { + namespace modules { + + const std::string ConversionGeneralSettings::moduleName = "general"; + const std::string ConversionGeneralSettings::helpOptionName = "help"; + const std::string ConversionGeneralSettings::helpOptionShortName = "h"; + const std::string ConversionGeneralSettings::versionOptionName = "version"; + const std::string ConversionGeneralSettings::verboseOptionName = "verbose"; + const std::string ConversionGeneralSettings::verboseOptionShortName = "v"; + const std::string ConversionGeneralSettings::debugOptionName = "debug"; + const std::string ConversionGeneralSettings::traceOptionName = "trace"; + const std::string ConversionGeneralSettings::configOptionName = "config"; + const std::string ConversionGeneralSettings::configOptionShortName = "c"; + + ConversionGeneralSettings::ConversionGeneralSettings() : ModuleSettings(moduleName) { + this->addOption(storm::settings::OptionBuilder(moduleName, helpOptionName, false, "Shows all available options, arguments and descriptions.").setShortName(helpOptionShortName) + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("hint", "A regular expression to show help for all matching entities or 'all' for the complete help.").setDefaultValueString("all").build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, versionOptionName, false, "Prints the version information.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, verboseOptionName, false, "Enables more verbose output.").setShortName(verboseOptionShortName).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, debugOptionName, false, "Enables verbose and debug output.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, traceOptionName, false, "Enables verbose and debug and trace output.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, configOptionName, false, "If given, this file will be read and parsed for additional configuration settings.").setShortName(configOptionShortName) + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the file from which to read the configuration.").addValidatorString(ArgumentValidatorFactory::createExistingFileValidator()).build()).build()); + } + + bool ConversionGeneralSettings::isHelpSet() const { + return this->getOption(helpOptionName).getHasOptionBeenSet(); + } + + bool ConversionGeneralSettings::isVersionSet() const { + return this->getOption(versionOptionName).getHasOptionBeenSet(); + } + + std::string ConversionGeneralSettings::getHelpModuleName() const { + return this->getOption(helpOptionName).getArgumentByName("hint").getValueAsString(); + } + + bool ConversionGeneralSettings::isVerboseSet() const { + return this->getOption(verboseOptionName).getHasOptionBeenSet(); + } + + bool ConversionGeneralSettings::isDebugOutputSet() const { + return this->getOption(debugOptionName).getHasOptionBeenSet(); + } + + bool ConversionGeneralSettings::isTraceOutputSet() const { + return this->getOption(traceOptionName).getHasOptionBeenSet(); + } + + bool ConversionGeneralSettings::isConfigSet() const { + return this->getOption(configOptionName).getHasOptionBeenSet(); + } + + std::string ConversionGeneralSettings::getConfigFilename() const { + return this->getOption(configOptionName).getArgumentByName("filename").getValueAsString(); + } + + void ConversionGeneralSettings::finalize() { + // Intentionally left empty. + } + + bool ConversionGeneralSettings::check() const { + return true; + } + + } // namespace modules + } // namespace settings +} // namespace storm diff --git a/src/storm-conv/settings/modules/ConversionGeneralSettings.h b/src/storm-conv/settings/modules/ConversionGeneralSettings.h new file mode 100644 index 000000000..dc7e79f27 --- /dev/null +++ b/src/storm-conv/settings/modules/ConversionGeneralSettings.h @@ -0,0 +1,88 @@ +#pragma once +#include "storm/settings/modules/ModuleSettings.h" + +namespace storm { + namespace settings { + namespace modules { + + class ConversionGeneralSettings : public ModuleSettings { + public: + + ConversionGeneralSettings(); + + /*! + * Retrieves whether the help option was set. + * + * @return True if the help option was set. + */ + bool isHelpSet() const; + + /*! + * Retrieves whether the version option was set. + * + * @return True if the version option was set. + */ + bool isVersionSet() const; + + /*! + * Retrieves the name of the module for which to show the help or "all" to indicate that the full help + * needs to be shown. + * + * @return The name of the module for which to show the help or "all". + */ + std::string getHelpModuleName() const; + + /*! + * Retrieves whether the verbose option was set. + * + * @return True if the verbose option was set. + */ + bool isVerboseSet() const; + + /*! + * Retrieves whether the debug output option was set. + * + */ + bool isDebugOutputSet() const; + + /*! + * Retrieves whether the trace output option was set. + * + */ + bool isTraceOutputSet() const; + + /*! + * Retrieves whether the config option was set. + * + * @return True if the config option was set. + */ + bool isConfigSet() const; + + /*! + * Retrieves the name of the file that is to be scanned for settings. + * + * @return The name of the file that is to be scanned for settings. + */ + std::string getConfigFilename() const; + + 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 helpOptionName; + static const std::string helpOptionShortName; + static const std::string versionOptionName; + static const std::string verboseOptionName; + static const std::string verboseOptionShortName; + static const std::string debugOptionName; + static const std::string traceOptionName; + static const std::string configOptionName; + static const std::string configOptionShortName; + }; + } + } +} \ No newline at end of file diff --git a/src/storm-conv/settings/modules/ConversionInputSettings.cpp b/src/storm-conv/settings/modules/ConversionInputSettings.cpp new file mode 100644 index 000000000..cc91d597c --- /dev/null +++ b/src/storm-conv/settings/modules/ConversionInputSettings.cpp @@ -0,0 +1,80 @@ +#include "storm-conv/settings/modules/ConversionInputSettings.h" + +#include "storm/settings/SettingsManager.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 ConversionInputSettings::moduleName = "input"; + const std::string ConversionInputSettings::propertyOptionName = "prop"; + const std::string ConversionInputSettings::propertyOptionShortName = "prop"; + const std::string ConversionInputSettings::constantsOptionName = "constants"; + const std::string ConversionInputSettings::constantsOptionShortName = "const"; + const std::string ConversionInputSettings::prismInputOptionName = "prism"; + const std::string ConversionInputSettings::prismCompatibilityOptionName = "prismcompat"; + const std::string ConversionInputSettings::prismCompatibilityOptionShortName = "pc"; + + + ConversionInputSettings::ConversionInputSettings() : ModuleSettings(moduleName) { + this->addOption(storm::settings::OptionBuilder(moduleName, propertyOptionName, false, "Specifies the properties to be checked on the model.").setShortName(propertyOptionShortName) + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("property or filename", "The formula or the file containing the formulas.").build()) + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filter", "The names of the properties to check.").setDefaultValueString("all").build()) + .build()); + this->addOption(storm::settings::OptionBuilder(moduleName, constantsOptionName, false, "Specifies the constant replacements to use in symbolic models. Note that this requires the model to be given as an symbolic model (i.e., via --" + prismInputOptionName + ").").setShortName(constantsOptionShortName) + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("values", "A comma separated list of constants and their value, e.g. a=1,b=2,c=3.").setDefaultValueString("").build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, prismInputOptionName, false, "Parses the model given in the PRISM format.") + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the file from which to read the PRISM input.").addValidatorString(ArgumentValidatorFactory::createExistingFileValidator()).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, prismCompatibilityOptionName, false, "Enables PRISM compatibility. This may be necessary to process some PRISM models.").setShortName(prismCompatibilityOptionShortName).build()); + } + + bool ConversionInputSettings::isPrismInputSet() const { + return this->getOption(prismInputOptionName).getHasOptionBeenSet(); + } + + std::string ConversionInputSettings::getPrismInputFilename() const { + return this->getOption(prismInputOptionName).getArgumentByName("filename").getValueAsString(); + } + + bool ConversionInputSettings::isPrismCompatibilityEnabled() const { + return this->getOption(prismCompatibilityOptionName).getHasOptionBeenSet(); + } + + bool ConversionInputSettings::isConstantsSet() const { + return this->getOption(constantsOptionName).getHasOptionBeenSet(); + } + + std::string ConversionInputSettings::getConstantDefinitionString() const { + return this->getOption(constantsOptionName).getArgumentByName("values").getValueAsString(); + } + + bool ConversionInputSettings::isPropertyInputSet() const { + return this->getOption(propertyOptionName).getHasOptionBeenSet(); + } + + std::string ConversionInputSettings::getPropertyInput() const { + return this->getOption(propertyOptionName).getArgumentByName("property or filename").getValueAsString(); + } + + std::string ConversionInputSettings::getPropertyInputFilter() const { + return this->getOption(propertyOptionName).getArgumentByName("filter").getValueAsString(); + } + + void ConversionInputSettings::finalize() { + // Intentionally left empty. + } + + bool ConversionInputSettings::check() const { + return true; + } + + + } // namespace modules + } // namespace settings +} // namespace storm diff --git a/src/storm-conv/settings/modules/ConversionInputSettings.h b/src/storm-conv/settings/modules/ConversionInputSettings.h new file mode 100644 index 000000000..41cf6549e --- /dev/null +++ b/src/storm-conv/settings/modules/ConversionInputSettings.h @@ -0,0 +1,86 @@ +#pragma once +#include "storm/settings/modules/ModuleSettings.h" + +namespace storm { + namespace settings { + namespace modules { + + class ConversionInputSettings : public ModuleSettings { + public: + + ConversionInputSettings(); + + /*! + * Retrieves whether the property option was set. + * + * @return True if the property option was set. + */ + bool isPropertyInputSet() const; + + /*! + * Retrieves the property specified with the property option. + * + * @return The property specified with the property option. + */ + std::string getPropertyInput() const; + + /*! + * Retrieves the property filter. + * + * @return The property filter. + */ + std::string getPropertyInputFilter() const; + + /*! + * Retrieves whether constant definition option was set. + * + * @return True if the constant definition option was set. + */ + bool isConstantsSet() const; + + /*! + * Retrieves the string that defines the constants of a symbolic model (given via the symbolic option). + * + * @return The string that defines the constants of a symbolic model. + */ + std::string getConstantDefinitionString() const; + + /*! + * Retrieves whether the PRISM language option was set. + */ + bool isPrismInputSet() const; + + /*! + * Retrieves the name of the file that contains the PRISM model specification if the model was given + * using the PRISM input option. + */ + std::string getPrismInputFilename() const; + + /*! + * Retrieves whether the PRISM compatibility mode was enabled. + * + * @return True iff the PRISM compatibility mode was enabled. + */ + bool isPrismCompatibilityEnabled() const; + + 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 propertyOptionName; + static const std::string propertyOptionShortName; + static const std::string constantsOptionName; + static const std::string constantsOptionShortName; + static const std::string prismInputOptionName; + static const std::string prismCompatibilityOptionName; + static const std::string prismCompatibilityOptionShortName; + }; + + + } + } +} \ No newline at end of file diff --git a/src/storm-conv/settings/modules/ConversionOutputSettings.cpp b/src/storm-conv/settings/modules/ConversionOutputSettings.cpp new file mode 100644 index 000000000..61002078d --- /dev/null +++ b/src/storm-conv/settings/modules/ConversionOutputSettings.cpp @@ -0,0 +1,57 @@ +#include "storm-conv/settings/modules/ConversionOutputSettings.h" + +#include "storm/settings/SettingsManager.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/InvalidOperationException.h" + +namespace storm { + namespace settings { + namespace modules { + + const std::string ConversionOutputSettings::moduleName = "output"; + const std::string ConversionOutputSettings::stdoutOptionName = "stdout"; + const std::string ConversionOutputSettings::janiOutputOptionName = "tojani"; + + ConversionOutputSettings::ConversionOutputSettings() : ModuleSettings(moduleName) { + this->addOption(storm::settings::OptionBuilder(moduleName, stdoutOptionName, false, "If set, the output will be printed to stdout.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, janiOutputOptionName, false, "converts the input model to Jani.") + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "the name of the output file (if not empty).").setDefaultValueString("").build()).build()); + } + + bool ConversionOutputSettings::isStdOutOutputEnabled() const { + return this->getOption(stdoutOptionName).getHasOptionBeenSet(); + } + + bool ConversionOutputSettings::isJaniOutputSet() const { + return this->getOption(janiOutputOptionName).getHasOptionBeenSet(); + } + + bool ConversionOutputSettings::isJaniOutputFilenameSet() const { + return isJaniOutputSet() + && !this->getOption(janiOutputOptionName).getArgumentByName("filename").wasSetFromDefaultValue() + && this->getOption(janiOutputOptionName).getArgumentByName("filename").getHasBeenSet() + && this->getOption(janiOutputOptionName).getArgumentByName("filename").getValueAsString() != ""; + } + + std::string ConversionOutputSettings::getJaniOutputFilename() const { + STORM_LOG_THROW(isJaniOutputFilenameSet(), storm::exceptions::InvalidOperationException, "Tried to get the jani output name although none was specified."); + return this->getOption(janiOutputOptionName).getArgumentByName("filename").getValueAsString(); + } + + void ConversionOutputSettings::finalize() { + // Intentionally left empty. + } + + bool ConversionOutputSettings::check() const { + STORM_LOG_THROW(!isJaniOutputFilenameSet() || ArgumentValidatorFactory::createWritableFileValidator()->isValid(getJaniOutputFilename()), storm::exceptions::InvalidSettingsException, "Unable to write at file " + getJaniOutputFilename()); + return true; + } + + } // namespace modules + } // namespace settings +} // namespace storm diff --git a/src/storm-conv/settings/modules/ConversionOutputSettings.h b/src/storm-conv/settings/modules/ConversionOutputSettings.h new file mode 100644 index 000000000..e429cdd93 --- /dev/null +++ b/src/storm-conv/settings/modules/ConversionOutputSettings.h @@ -0,0 +1,49 @@ +#pragma once +#include "storm/settings/modules/ModuleSettings.h" + +namespace storm { + namespace settings { + namespace modules { + + class ConversionOutputSettings : public ModuleSettings { + public: + + ConversionOutputSettings(); + + /*! + * Retrieves whether the output should be printed to stdout + */ + bool isStdOutOutputEnabled() const; + + /*! + * Retrieves whether the output should be in the Jani format + */ + bool isJaniOutputSet() const; + + /*! + * Retrieves whether an output filename for the jani file was specified + */ + bool isJaniOutputFilenameSet() const; + + /*! + * Retrieves the name of the jani output (if specified) + */ + std::string getJaniOutputFilename() const; + + bool check() const override; + void finalize() override; + + // The name of the module. + static const std::string moduleName; + // name of the option that enables output to stdout. It needs to be public because we have to check this option very early + static const std::string stdoutOptionName; + + private: + // Define the string names of the options as constants. + static const std::string janiOutputOptionName; + }; + + + } + } +} \ No newline at end of file diff --git a/src/storm-conv/settings/modules/JaniExportSettings.cpp b/src/storm-conv/settings/modules/JaniExportSettings.cpp new file mode 100644 index 000000000..707e91dac --- /dev/null +++ b/src/storm-conv/settings/modules/JaniExportSettings.cpp @@ -0,0 +1,78 @@ +#include "JaniExportSettings.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 + +namespace storm { + namespace settings { + namespace modules { + const std::string JaniExportSettings::moduleName = "exportJani"; + + const std::string JaniExportSettings::standardCompliantOptionName = "standard-compliant"; + const std::string JaniExportSettings::standardCompliantOptionShortName = "standard"; + const std::string JaniExportSettings::exportFlattenOptionName = "flatten"; + const std::string JaniExportSettings::locationVariablesOptionName = "location-variables"; + const std::string JaniExportSettings::globalVariablesOptionName = "globalvars"; + const std::string JaniExportSettings::compactJsonOptionName = "compactjson"; + + + JaniExportSettings::JaniExportSettings() : ModuleSettings(moduleName) { + this->addOption(storm::settings::OptionBuilder(moduleName, locationVariablesOptionName, true, "Variables to export in the location").addArgument(storm::settings::ArgumentBuilder::createStringArgument("variables", "A comma separated list of automaton and local variable names seperated by a dot, e.g. A.x,B.y.").setDefaultValueString("").build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, standardCompliantOptionName, false, "Export in standard compliant variant.").setShortName(standardCompliantOptionShortName).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, exportFlattenOptionName, false, "Flattens the composition of Automata to obtain an equivalent model that contains exactly one automaton").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, globalVariablesOptionName, false, "If set, variables will preferably be made global, e.g., to guarantee the same variable order as in the input file.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, compactJsonOptionName, false, "If set, the size of the resulting jani file will be reduced at the cost of (human-)readability.").build()); + } + + bool JaniExportSettings::isExportAsStandardJaniSet() const { + return this->getOption(standardCompliantOptionName).getHasOptionBeenSet(); + } + + bool JaniExportSettings::isExportFlattenedSet() const { + return this->getOption(exportFlattenOptionName).getHasOptionBeenSet(); + } + + bool JaniExportSettings::isLocationVariablesSet() const { + return this->getOption(locationVariablesOptionName).getHasOptionBeenSet(); + } + + std::vector> JaniExportSettings::getLocationVariables() const { + std::vector> result; + if (isLocationVariablesSet()) { + std::string argument = this->getOption(locationVariablesOptionName).getArgumentByName("variables").getValueAsString(); + std::vector arguments; + boost::split( arguments, argument, boost::is_any_of(",")); + for (auto const& pair : arguments) { + std::vector keyvaluepair; + boost::split( keyvaluepair, pair, boost::is_any_of(".")); + STORM_LOG_THROW(keyvaluepair.size() == 2, storm::exceptions::IllegalArgumentException, "Expected a name of the form AUTOMATON.VARIABLE (with no further dots) but got " << pair); + result.emplace_back(keyvaluepair.at(0), keyvaluepair.at(1)); + } + } + return result; + } + + bool JaniExportSettings::isGlobalVarsSet() const { + return this->getOption(globalVariablesOptionName).getHasOptionBeenSet(); + } + + bool JaniExportSettings::isCompactJsonSet() const { + return this->getOption(compactJsonOptionName).getHasOptionBeenSet(); + } + + void JaniExportSettings::finalize() { + + } + + bool JaniExportSettings::check() const { + return true; + } + } + } +} diff --git a/src/storm/settings/modules/JaniExportSettings.h b/src/storm-conv/settings/modules/JaniExportSettings.h similarity index 69% rename from src/storm/settings/modules/JaniExportSettings.h rename to src/storm-conv/settings/modules/JaniExportSettings.h index 397eb183a..2b2d6de7a 100644 --- a/src/storm/settings/modules/JaniExportSettings.h +++ b/src/storm-conv/settings/modules/JaniExportSettings.h @@ -25,7 +25,17 @@ namespace storm { std::string getJaniFilename() const; bool isExportAsStandardJaniSet() const; + + bool isExportFlattenedSet() const; + + bool isLocationVariablesSet() const; + + bool isGlobalVarsSet() const; + bool isCompactJsonSet() const; + + std::vector> getLocationVariables() const; + bool check() const override; void finalize() override; @@ -36,6 +46,10 @@ namespace storm { static const std::string janiFileOptionShortName; static const std::string standardCompliantOptionName; static const std::string standardCompliantOptionShortName; + static const std::string exportFlattenOptionName; + static const std::string locationVariablesOptionName; + static const std::string globalVariablesOptionName; + static const std::string compactJsonOptionName; }; } diff --git a/src/storm-counterexamples/CMakeLists.txt b/src/storm-counterexamples/CMakeLists.txt new file mode 100644 index 000000000..2052930c6 --- /dev/null +++ b/src/storm-counterexamples/CMakeLists.txt @@ -0,0 +1,40 @@ +file(GLOB_RECURSE ALL_FILES ${PROJECT_SOURCE_DIR}/src/storm-counterexamples/*.h ${PROJECT_SOURCE_DIR}/src/storm-counterexamples/*.cpp) + +register_source_groups_from_filestructure("${ALL_FILES}" storm-counterexamples) + + + +file(GLOB_RECURSE STORM_CEX_SOURCES ${PROJECT_SOURCE_DIR}/src/storm-counterexamples/*/*.cpp) +file(GLOB_RECURSE STORM_CEX_HEADERS ${PROJECT_SOURCE_DIR}/src/storm-counterexamples/*/*.h) + + +# Create storm-dft. +add_library(storm-counterexamples SHARED ${STORM_CEX_SOURCES} ${STORM_CEX_HEADERS}) + +# Remove define symbol for shared libstorm. +set_target_properties(storm-counterexamples PROPERTIES DEFINE_SYMBOL "") +#add_dependencies(storm resources) +list(APPEND STORM_TARGETS storm-counterexamples) +set(STORM_TARGETS ${STORM_TARGETS} PARENT_SCOPE) + +target_link_libraries(storm-counterexamples PUBLIC storm) + +# Install storm headers to include directory. +foreach(HEADER ${STORM_CEX_HEADERS}) + string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/src/?" "" RELATIVE_HEADER_PATH ${HEADER}) + string(REGEX MATCH "(.*)[/\\]" RELATIVE_DIRECTORY ${RELATIVE_HEADER_PATH}) + string(REGEX REPLACE "${RELATIVE_DIRECTORY}/?" "" HEADER_FILENAME ${RELATIVE_HEADER_PATH}) + add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${HEADER} ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME} + DEPENDS ${HEADER} + ) + list(APPEND STORM_CEX_OUTPUT_HEADERS "${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME}") +endforeach() +add_custom_target(copy_storm_cex_headers DEPENDS ${STORM_CEX_OUTPUT_HEADERS} ${STORM_CEX_HEADERS}) +add_dependencies(storm-counterexamples copy_storm_cex_headers) + +# installation +install(TARGETS storm-counterexamples EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) + diff --git a/src/storm/api/counterexamples.cpp b/src/storm-counterexamples/api/counterexamples.cpp similarity index 95% rename from src/storm/api/counterexamples.cpp rename to src/storm-counterexamples/api/counterexamples.cpp index c57724ad5..5652beba6 100644 --- a/src/storm/api/counterexamples.cpp +++ b/src/storm-counterexamples/api/counterexamples.cpp @@ -1,4 +1,4 @@ -#include "storm/api/counterexamples.h" +#include "storm-counterexamples/api/counterexamples.h" #include "storm/environment/Environment.h" diff --git a/src/storm/api/counterexamples.h b/src/storm-counterexamples/api/counterexamples.h similarity index 80% rename from src/storm/api/counterexamples.h rename to src/storm-counterexamples/api/counterexamples.h index 672ddc4df..6a5984991 100644 --- a/src/storm/api/counterexamples.h +++ b/src/storm-counterexamples/api/counterexamples.h @@ -1,7 +1,7 @@ #pragma once -#include "storm/counterexamples/MILPMinimalLabelSetGenerator.h" -#include "storm/counterexamples/SMTMinimalLabelSetGenerator.h" +#include "storm-counterexamples/counterexamples/MILPMinimalLabelSetGenerator.h" +#include "storm-counterexamples/counterexamples/SMTMinimalLabelSetGenerator.h" namespace storm { namespace api { diff --git a/src/storm/counterexamples/Counterexample.cpp b/src/storm-counterexamples/counterexamples/Counterexample.cpp similarity index 79% rename from src/storm/counterexamples/Counterexample.cpp rename to src/storm-counterexamples/counterexamples/Counterexample.cpp index 4aba71b9c..9975f54ba 100644 --- a/src/storm/counterexamples/Counterexample.cpp +++ b/src/storm-counterexamples/counterexamples/Counterexample.cpp @@ -1,4 +1,4 @@ -#include "storm/counterexamples/Counterexample.h" +#include "storm-counterexamples/counterexamples/Counterexample.h" namespace storm { namespace counterexamples { diff --git a/src/storm/counterexamples/Counterexample.h b/src/storm-counterexamples/counterexamples/Counterexample.h similarity index 100% rename from src/storm/counterexamples/Counterexample.h rename to src/storm-counterexamples/counterexamples/Counterexample.h diff --git a/src/storm/counterexamples/HighLevelCounterexample.cpp b/src/storm-counterexamples/counterexamples/HighLevelCounterexample.cpp similarity index 92% rename from src/storm/counterexamples/HighLevelCounterexample.cpp rename to src/storm-counterexamples/counterexamples/HighLevelCounterexample.cpp index 707ed6d39..11279a220 100644 --- a/src/storm/counterexamples/HighLevelCounterexample.cpp +++ b/src/storm-counterexamples/counterexamples/HighLevelCounterexample.cpp @@ -1,4 +1,4 @@ -#include "storm/counterexamples/HighLevelCounterexample.h" +#include "storm-counterexamples/counterexamples/HighLevelCounterexample.h" namespace storm { namespace counterexamples { diff --git a/src/storm/counterexamples/HighLevelCounterexample.h b/src/storm-counterexamples/counterexamples/HighLevelCounterexample.h similarity index 93% rename from src/storm/counterexamples/HighLevelCounterexample.h rename to src/storm-counterexamples/counterexamples/HighLevelCounterexample.h index 92310a5ba..27fe981c0 100644 --- a/src/storm/counterexamples/HighLevelCounterexample.h +++ b/src/storm-counterexamples/counterexamples/HighLevelCounterexample.h @@ -1,6 +1,6 @@ #pragma once -#include "storm/counterexamples/Counterexample.h" +#include "Counterexample.h" #include "storm/storage/SymbolicModelDescription.h" diff --git a/src/storm/counterexamples/MILPMinimalLabelSetGenerator.h b/src/storm-counterexamples/counterexamples/MILPMinimalLabelSetGenerator.h similarity index 99% rename from src/storm/counterexamples/MILPMinimalLabelSetGenerator.h rename to src/storm-counterexamples/counterexamples/MILPMinimalLabelSetGenerator.h index 861f86f83..52ed50778 100644 --- a/src/storm/counterexamples/MILPMinimalLabelSetGenerator.h +++ b/src/storm-counterexamples/counterexamples/MILPMinimalLabelSetGenerator.h @@ -17,7 +17,7 @@ #include "storm/solver/MinMaxLinearEquationSolver.h" -#include "storm/counterexamples/HighLevelCounterexample.h" +#include "storm-counterexamples/counterexamples/HighLevelCounterexample.h" #include "storm/utility/graph.h" #include "storm/utility/counterexamples.h" @@ -29,7 +29,7 @@ #include "storm/settings/SettingsManager.h" #include "storm/settings/modules/GeneralSettings.h" -#include "storm/settings/modules/CounterexampleGeneratorSettings.h" +#include "storm-counterexamples/settings/modules/CounterexampleGeneratorSettings.h" namespace storm { diff --git a/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h b/src/storm-counterexamples/counterexamples/SMTMinimalLabelSetGenerator.h similarity index 78% rename from src/storm/counterexamples/SMTMinimalLabelSetGenerator.h rename to src/storm-counterexamples/counterexamples/SMTMinimalLabelSetGenerator.h index dfd9942b8..624a13205 100644 --- a/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h +++ b/src/storm-counterexamples/counterexamples/SMTMinimalLabelSetGenerator.h @@ -5,7 +5,7 @@ #include "storm/solver/Z3SmtSolver.h" -#include "storm/counterexamples/HighLevelCounterexample.h" +#include "storm-counterexamples/counterexamples/HighLevelCounterexample.h" #include "storm/storage/prism/Program.h" #include "storm/storage/expressions/Expression.h" @@ -28,7 +28,20 @@ namespace storm { class Environment; namespace counterexamples { - + + /** + * Helper to avoid case disticinot between prism and jani + * Returns the number of edges/commands in a symbolic model description. + */ + size_t nrCommands(storm::storage::SymbolicModelDescription const& descr) { + if (descr.isJaniModel()) { + return descr.asJaniModel().getNumberOfEdges(); + } else { + assert(descr.isPrismProgram()); + return descr.asPrismProgram().getNumberOfCommands(); + } + } + /*! * This class provides functionality to generate a minimal counterexample to a probabilistic reachability * property in terms of used labels. @@ -49,14 +62,16 @@ namespace storm { // The set of labels that matter in terms of minimality. boost::container::flat_set minimalityLabels; - + // A set of labels that is definitely known to be taken in the final solution. boost::container::flat_set knownLabels; + + boost::container::flat_set dontCareLabels; // A list of relevant choices for each relevant state. std::map> relevantChoicesForRelevantStates; }; - + struct VariableInformation { // The manager responsible for the constraints we are building. std::shared_ptr manager; @@ -154,10 +169,12 @@ namespace storm { std::set_difference(relevancyInformation.relevantLabels.begin(), relevancyInformation.relevantLabels.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(remainingLabels, remainingLabels.end())); relevancyInformation.relevantLabels = remainingLabels; } - + + relevancyInformation.dontCareLabels = dontCareLabels; std::set_difference(relevancyInformation.relevantLabels.begin(), relevancyInformation.relevantLabels.end(), dontCareLabels.begin(), dontCareLabels.end(), std::inserter(relevancyInformation.minimalityLabels, relevancyInformation.minimalityLabels.begin())); STORM_LOG_DEBUG("Found " << relevancyInformation.relevantLabels.size() << " relevant and " << relevancyInformation.knownLabels.size() << " known labels."); + STORM_LOG_DEBUG("Found " << relevancyInformation.minimalityLabels.size() << " labels to minize over."); return relevancyInformation; } @@ -263,21 +280,6 @@ namespace storm { return variableInformation; } - /*! - * Asserts the constraints that are initially needed for the Fu-Malik procedure. - * - * @param solver The solver in which to assert the constraints. - * @param variableInformation A structure with information about the variables for the labels. - */ - static void assertFuMalikInitialConstraints(z3::solver& solver, VariableInformation const& variableInformation) { - // Assert that at least one of the labels must be taken. - z3::expr formula = variableInformation.labelVariables.at(0); - for (uint_fast64_t index = 1; index < variableInformation.labelVariables.size(); ++index) { - formula = formula || variableInformation.labelVariables.at(index); - } - solver.add(formula); - } - static storm::expressions::Expression getOtherSynchronizationEnabledFormula(boost::container::flat_set const& labelSet, std::map>> const& synchronizingLabels, boost::container::flat_map, storm::expressions::Expression> const& labelSetToFormula, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation) { // Taking all commands of a combination does not necessarily mean that a following label set needs to be taken. // This is because it could be that the commands are taken to enable other synchronizations. Therefore, we need @@ -324,13 +326,19 @@ namespace storm { * @param context The Z3 context in which to build the expressions. * @param solver The solver to use for the satisfiability evaluation. */ - static void assertCuts(storm::storage::SymbolicModelDescription const& symbolicModel, storm::models::sparse::Model const& model, std::vector> const& labelSets, storm::storage::BitVector const& psiStates, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver) { + static std::chrono::milliseconds assertCuts(storm::storage::SymbolicModelDescription const& symbolicModel, storm::models::sparse::Model const& model, std::vector> const& labelSets, storm::storage::BitVector const& psiStates, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver, bool addBackwardImplications) { // Walk through the model and // * identify labels enabled in initial states // * identify labels that can directly precede a given action // * identify labels that directly reach a target state // * identify labels that can directly follow a given action - + auto assertCutsClock = std::chrono::high_resolution_clock::now(); + + // + if (addBackwardImplications) { + STORM_LOG_THROW(!symbolicModel.isJaniModel() || !symbolicModel.asJaniModel().usesAssignmentLevels(), storm::exceptions::NotSupportedException, "Counterexample generation with backward implications is not supported for indexed assignments"); + } + boost::container::flat_set initialLabels; std::set> initialCombinations; boost::container::flat_set targetLabels; @@ -345,6 +353,7 @@ namespace storm { storm::storage::BitVector const& initialStates = model.getInitialStates(); for (auto currentState : relevancyInformation.relevantStates) { + bool isInitial = initialStates.get(currentState); for (auto currentChoice : relevancyInformation.relevantChoicesForRelevantStates.at(currentState)) { // If the choice is a synchronization choice, we need to record it. @@ -355,7 +364,7 @@ namespace storm { } // If the state is initial, we need to add all the choice labels to the initial label set. - if (initialStates.get(currentState)) { + if (isInitial) { initialLabels.insert(labelSets[currentChoice].begin(), labelSets[currentChoice].end()); initialCombinations.insert(labelSets[currentChoice]); } @@ -389,6 +398,7 @@ namespace storm { for (auto const& successorEntry : transitionMatrix.getRow(predecessorChoice)) { if (successorEntry.getColumn() == currentState) { choiceTargetsCurrentState = true; + break; } } @@ -400,11 +410,10 @@ namespace storm { } } } - + // Store the found implications in a container similar to the preceding label sets. std::map, std::set>> backwardImplications; - - if (!symbolicModel.isJaniModel() || !symbolicModel.asJaniModel().usesAssignmentLevels()) { + if (addBackwardImplications) { // Create a new solver over the same variables as the given symbolic model description to use it for // determining the symbolic cuts. std::unique_ptr localSolver; @@ -458,6 +467,12 @@ namespace storm { // Now check for possible backward cuts. for (auto const& labelSetAndPrecedingLabelSetsPair : precedingLabels) { + bool backwardImplicationAdded = false; +// std::cout << "labelSetAndPrecedingLabelSetsPair.first "; +// for (auto const& e : labelSetAndPrecedingLabelSetsPair.first) { +// std::cout << e << ", "; +// } +// std::cout << std::endl; // Find out the commands for the currently considered label set. storm::expressions::Expression guardConjunction; @@ -510,15 +525,18 @@ namespace storm { // If the solver reports unsat, then we know that the current selection is not enabled in the initial state. if (checkResult == storm::solver::SmtSolver::CheckResult::Unsat) { STORM_LOG_DEBUG("Selection not enabled in initial state."); - + + //std::cout << "not gc: " << !guardConjunction << std::endl; localSolver->add(!guardConjunction); STORM_LOG_DEBUG("Asserted disjunction of negated guards."); // Now check the possible preceding label sets for the essential ones. for (auto const& precedingLabelSet : labelSetAndPrecedingLabelSetsPair.second) { + if (labelSetAndPrecedingLabelSetsPair.first == precedingLabelSet) continue; - + + //std::cout << "push" << std::endl; // Create a restore point so we can easily pop-off all weakest precondition expressions. localSolver->push(); @@ -576,7 +594,9 @@ namespace storm { } } } - + + //std::cout << "pgc: " << preceedingGuardConjunction << std::endl; + // Assert all the guards of the preceding command set. localSolver->add(preceedingGuardConjunction); @@ -587,8 +607,7 @@ namespace storm { // Iterate over all possible combinations of updates of the preceding command set. std::vector formulae; - bool done = false; - while (!done) { + while (true) { std::map currentVariableUpdateCombinationMap; for (auto const& updateIterator : iteratorVector) { for (auto const& variableUpdatePair : *updateIterator) { @@ -616,7 +635,7 @@ namespace storm { // If we had to reset all iterator to the start, we are done. if (k == 0) { - done = true; + break; } } @@ -624,19 +643,25 @@ namespace storm { assertDisjunction(*localSolver, formulae, symbolicModel.isPrismProgram() ? symbolicModel.asPrismProgram().getManager() : symbolicModel.asJaniModel().getManager()); STORM_LOG_DEBUG("Asserted disjunction of all weakest preconditions."); - - if (localSolver->check() == storm::solver::SmtSolver::CheckResult::Sat) { + storm::solver::SmtSolver::CheckResult result = localSolver->check(); + + if (result == storm::solver::SmtSolver::CheckResult::Sat) { backwardImplications[labelSetAndPrecedingLabelSetsPair.first].insert(precedingLabelSet); + backwardImplicationAdded = true; + } else if (result == storm::solver::SmtSolver::CheckResult::Unknown) { + STORM_LOG_ERROR("The SMT solver does not come to a conclusive answer. Does your model contain integer division?"); } - + localSolver->pop(); } // Popping the disjunction of negated guards from the solver stack. localSolver->pop(); + STORM_LOG_ERROR_COND(backwardImplicationAdded, "Error in adding cuts for counterexample generation (backward implication misses a label set)."); } else { STORM_LOG_DEBUG("Selection is enabled in initial state."); } + } } else if (symbolicModel.isJaniModel()) { STORM_LOG_WARN("Model uses assignment levels, did not assert backward implications."); @@ -716,85 +741,88 @@ namespace storm { assertDisjunction(solver, formulae, *variableInformation.manager); } } - - STORM_LOG_DEBUG("Asserting taken labels are followed and preceeded by another label if they are not a target label or an initial label, respectively."); - boost::container::flat_map, storm::expressions::Expression> labelSetToFormula; - for (auto const& labelSet : relevancyInformation.relevantLabelSets) { - storm::expressions::Expression labelSetFormula = variableInformation.manager->boolean(false); - // Compute the set of unknown labels on the left-hand side of the implication. - boost::container::flat_set unknownLhsLabels; - std::set_difference(labelSet.begin(), labelSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownLhsLabels, unknownLhsLabels.end())); - for (auto label : unknownLhsLabels) { - labelSetFormula = labelSetFormula || !variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); - } - - // Only build a constraint if the combination does not lead to a target state and - // no successor set is already known. - storm::expressions::Expression successorExpression; - if (targetCombinations.find(labelSet) == targetCombinations.end() && hasKnownSuccessor.find(labelSet) == hasKnownSuccessor.end()) { - successorExpression = variableInformation.manager->boolean(false); + if(addBackwardImplications) { - auto const& followingLabelSets = followingLabels.at(labelSet); + STORM_LOG_DEBUG("Asserting taken labels are followed and preceeded by another label if they are not a target label or an initial label, respectively."); + boost::container::flat_map, storm::expressions::Expression> labelSetToFormula; + for (auto const &labelSet : relevancyInformation.relevantLabelSets) { + storm::expressions::Expression labelSetFormula = variableInformation.manager->boolean(false); - for (auto const& followingSet : followingLabelSets) { - boost::container::flat_set tmpSet; - - // Check which labels of the current following set are not known. This set must be non-empty, because - // otherwise a successor combination would already be known and control cannot reach this point. - std::set_difference(followingSet.begin(), followingSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(tmpSet, tmpSet.end())); - - // Construct an expression that enables all unknown labels of the current following set. - storm::expressions::Expression conj = variableInformation.manager->boolean(true); - for (auto label : tmpSet) { - conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + // Compute the set of unknown labels on the left-hand side of the implication. + boost::container::flat_set unknownLhsLabels; + std::set_difference(labelSet.begin(), labelSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownLhsLabels, unknownLhsLabels.end())); + for (auto label : unknownLhsLabels) { + labelSetFormula = labelSetFormula || !variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + } + + // Only build a constraint if the combination does not lead to a target state and + // no successor set is already known. + storm::expressions::Expression successorExpression; + if (targetCombinations.find(labelSet) == targetCombinations.end() && hasKnownSuccessor.find(labelSet) == hasKnownSuccessor.end()) { + successorExpression = variableInformation.manager->boolean(false); + + auto const &followingLabelSets = followingLabels.at(labelSet); + + for (auto const &followingSet : followingLabelSets) { + boost::container::flat_set tmpSet; + + // Check which labels of the current following set are not known. This set must be non-empty, because + // otherwise a successor combination would already be known and control cannot reach this point. + std::set_difference(followingSet.begin(), followingSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(tmpSet, tmpSet.end())); + + // Construct an expression that enables all unknown labels of the current following set. + storm::expressions::Expression conj = variableInformation.manager->boolean(true); + for (auto label : tmpSet) { + conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + } + successorExpression = successorExpression || conj; } - successorExpression = successorExpression || conj; + } else { + successorExpression = variableInformation.manager->boolean(true); } - } else { - successorExpression = variableInformation.manager->boolean(true); - } - // Constructed following cuts at this point. - - // Only build a constraint if the combination is no initial combination and no - // predecessor set is already known. - storm::expressions::Expression predecessorExpression; - if (initialCombinations.find(labelSet) == initialCombinations.end() && hasKnownPredecessor.find(labelSet) == hasKnownPredecessor.end()) { - predecessorExpression = variableInformation.manager->boolean(false); - + // Constructed following cuts at this point. + + // Only build a constraint if the combination is no initial combination and no + // predecessor set is already known. + storm::expressions::Expression predecessorExpression; + if (initialCombinations.find(labelSet) == initialCombinations.end() && hasKnownPredecessor.find(labelSet) == hasKnownPredecessor.end()) { + predecessorExpression = variableInformation.manager->boolean(false); + // std::cout << "labelSet" << std::endl; // for (auto const& e : labelSet) { // std::cout << e << ", "; // } // std::cout << std::endl; - auto const& preceedingLabelSets = backwardImplications.at(labelSet); + auto const &preceedingLabelSets = backwardImplications.at(labelSet); - for (auto const& preceedingSet : preceedingLabelSets) { - boost::container::flat_set tmpSet; - - // Check which labels of the current following set are not known. This set must be non-empty, because - // otherwise a predecessor combination would already be known and control cannot reach this point. - std::set_difference(preceedingSet.begin(), preceedingSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(tmpSet, tmpSet.end())); - - // Construct an expression that enables all unknown labels of the current following set. - storm::expressions::Expression conj = variableInformation.manager->boolean(true); - for (auto label : tmpSet) { - conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + for (auto const &preceedingSet : preceedingLabelSets) { + boost::container::flat_set tmpSet; + + // Check which labels of the current following set are not known. This set must be non-empty, because + // otherwise a predecessor combination would already be known and control cannot reach this point. + std::set_difference(preceedingSet.begin(), preceedingSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(tmpSet, tmpSet.end())); + + // Construct an expression that enables all unknown labels of the current following set. + storm::expressions::Expression conj = variableInformation.manager->boolean(true); + for (auto label : tmpSet) { + conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + } + predecessorExpression = predecessorExpression || conj; } - predecessorExpression = predecessorExpression || conj; + } else { + predecessorExpression = variableInformation.manager->boolean(true); } - } else { - predecessorExpression = variableInformation.manager->boolean(true); + + labelSetFormula = labelSetFormula || (successorExpression && predecessorExpression); + + labelSetToFormula[labelSet] = labelSetFormula; } - - labelSetFormula = labelSetFormula || (successorExpression && predecessorExpression); - labelSetToFormula[labelSet] = labelSetFormula; - } - - for (auto const& labelSetFormula : labelSetToFormula) { - solver.add(labelSetFormula.second || getOtherSynchronizationEnabledFormula(labelSetFormula.first, synchronizingLabels, labelSetToFormula, variableInformation, relevancyInformation)); + for (auto const &labelSetFormula : labelSetToFormula) { + solver.add(labelSetFormula.second || getOtherSynchronizationEnabledFormula(labelSetFormula.first, synchronizingLabels, labelSetToFormula, variableInformation, relevancyInformation)); + } } STORM_LOG_DEBUG("Asserting synchronization cuts."); @@ -831,6 +859,9 @@ namespace storm { assertDisjunction(solver, formulae, *variableInformation.manager); } } + + auto endTime = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(endTime - assertCutsClock); } /*! @@ -945,22 +976,7 @@ namespace storm { } solver.add(disjunction); } - - /*! - * Asserts that the conjunction of the given formulae holds. If the content of the conjunction is empty, - * this corresponds to asserting true. - * - * @param context The Z3 context in which to build the expressions. - * @param solver The solver to use for the satisfiability evaluation. - * @param formulaVector A vector of expressions that shall form the conjunction. - */ - static void assertConjunction(z3::context& context, z3::solver& solver, std::vector const& formulaVector) { - z3::expr conjunction = context.bool_val(true); - for (auto expr : formulaVector) { - conjunction = conjunction && expr; - } - solver.add(conjunction); - } + /*! * Creates a full-adder for the two inputs and returns the resulting bit as well as the carry bit. @@ -1030,10 +1046,12 @@ namespace storm { */ static std::vector> createAdderPairs(VariableInformation const& variableInformation, std::vector> const& in) { std::vector> result; - - result.reserve(in.size() / 2 + in.size() % 2); - - for (uint_fast64_t index = 0; index < in.size() / 2; ++index) { + + + uint64_t maxIndex = in.size() / 2; + result.reserve(maxIndex + in.size() % 2); + + for (uint_fast64_t index = 0; index < maxIndex; ++index) { result.push_back(createAdder(variableInformation, in[2 * index], in[2 * index + 1])); } @@ -1137,112 +1155,6 @@ namespace storm { return relaxingVariable; } - /*! - * Asserts that the input vector encodes a decimal smaller or equal to one. - * - * @param context The Z3 context in which to build the expressions. - * @param solver The solver to use for the satisfiability evaluation. - * @param input The binary encoded input number. - */ - static void assertLessOrEqualOne(z3::context& context, z3::solver& solver, std::vector input) { - std::transform(input.begin(), input.end(), input.begin(), [](z3::expr e) -> z3::expr { return !e; }); - assertConjunction(context, solver, input); - } - - /*! - * Asserts that at most one of given literals may be true at any time. - * - * @param context The Z3 context in which to build the expressions. - * @param solver The solver to use for the satisfiability evaluation. - * @param blockingVariables A vector of variables out of which only one may be true. - */ - static void assertAtMostOne(z3::context& context, z3::solver& solver, std::vector const& literals) { - std::vector counter = createCounterCircuit(context, literals); - assertLessOrEqualOne(context, solver, counter); - } - - /*! - * Performs one Fu-Malik-Maxsat step. - * - * @param context The Z3 context in which to build the expressions. - * @param solver The solver to use for the satisfiability evaluation. - * @param variableInformation A structure with information about the variables for the labels. - * @return True iff the constraint system was satisfiable. - */ - static bool fuMalikMaxsatStep(z3::context& context, z3::solver& solver, std::vector& auxiliaryVariables, std::vector& softConstraints, uint_fast64_t& nextFreeVariableIndex) { - z3::expr_vector assumptions(context); - for (auto const& auxiliaryVariable : auxiliaryVariables) { - assumptions.push_back(!auxiliaryVariable); - } - - // Check whether the assumptions are satisfiable. - STORM_LOG_DEBUG("Invoking satisfiability checking."); - z3::check_result result = solver.check(assumptions); - STORM_LOG_DEBUG("Done invoking satisfiability checking."); - - if (result == z3::sat) { - return true; - } else { - STORM_LOG_DEBUG("Computing unsat core."); - z3::expr_vector unsatCore = solver.unsat_core(); - STORM_LOG_DEBUG("Computed unsat core."); - - std::vector blockingVariables; - blockingVariables.reserve(unsatCore.size()); - - // Create stringstream to build expression names. - std::stringstream variableName; - - for (uint_fast64_t softConstraintIndex = 0; softConstraintIndex < softConstraints.size(); ++softConstraintIndex) { - for (uint_fast64_t coreIndex = 0; coreIndex < unsatCore.size(); ++coreIndex) { - bool isContainedInCore = false; - if (softConstraints[softConstraintIndex] == unsatCore[coreIndex]) { - isContainedInCore = true; - } - - if (isContainedInCore) { - variableName.clear(); - variableName.str(""); - variableName << "b" << nextFreeVariableIndex; - blockingVariables.push_back(context.bool_const(variableName.str().c_str())); - - variableName.clear(); - variableName.str(""); - variableName << "a" << nextFreeVariableIndex; - ++nextFreeVariableIndex; - auxiliaryVariables[softConstraintIndex] = context.bool_const(variableName.str().c_str()); - - softConstraints[softConstraintIndex] = softConstraints[softConstraintIndex] || blockingVariables.back(); - - solver.add(softConstraints[softConstraintIndex] || auxiliaryVariables[softConstraintIndex]); - } - } - } - - assertAtMostOne(context, solver, blockingVariables); - } - - return false; - } - - /*! - * Rules out the given command set for the given solver. - * - * @param context The Z3 context in which to build the expressions. - * @param solver The solver to use for the satisfiability evaluation. - * @param commandSet The command set to rule out as a solution. - * @param variableInformation A structure with information about the variables for the labels. - */ - static void ruleOutSolution(z3::context& context, z3::solver& solver, boost::container::flat_set const& commandSet, VariableInformation const& variableInformation) { - z3::expr blockSolutionExpression = context.bool_val(false); - for (auto labelIndexPair : variableInformation.labelToIndexMap) { - if (commandSet.find(labelIndexPair.first) != commandSet.end()) { - blockSolutionExpression = blockSolutionExpression || variableInformation.labelVariables[labelIndexPair.second]; - } - } - - solver.add(blockSolutionExpression); - } /*! * Determines the set of labels that was chosen by the given model. @@ -1300,7 +1212,7 @@ namespace storm { * in order to satisfy the constraint system. * @return The smallest set of labels such that the constraint system of the solver is satisfiable. */ - static boost::container::flat_set findSmallestCommandSet(storm::solver::SmtSolver& solver, VariableInformation& variableInformation, uint_fast64_t& currentBound) { + static boost::optional> findSmallestCommandSet(storm::solver::SmtSolver& solver, VariableInformation& variableInformation, uint_fast64_t& currentBound) { // Check if we can find a solution with the current bound. storm::expressions::Expression assumption = !variableInformation.auxiliaryVariables.back(); @@ -1315,6 +1227,10 @@ namespace storm { solver.add(variableInformation.auxiliaryVariables.back()); variableInformation.auxiliaryVariables.push_back(assertLessOrEqualKRelaxed(solver, variableInformation, ++currentBound)); assumption = !variableInformation.auxiliaryVariables.back(); + if (currentBound > variableInformation.minimalityLabelVariables.size()) { + STORM_LOG_DEBUG("Constraint system fully explored: Bound exceeds maximum of " << variableInformation.minimalityLabelVariables.size()); + return boost::none; + } } // At this point we know that the constraint system was satisfiable, so compute the induced label @@ -1326,12 +1242,17 @@ namespace storm { std::vector formulae; boost::container::flat_set unknownLabels; - std::set_difference(labelSet.begin(), labelSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownLabels, unknownLabels.end())); + std::set_intersection(labelSet.begin(), labelSet.end(), relevancyInformation.minimalityLabels.begin(), relevancyInformation.minimalityLabels.end(), std::inserter(unknownLabels, unknownLabels.end())); + + //std::set_difference(labelSet.begin(), labelSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownLabels, unknownLabels.end())); for (auto const& label : unknownLabels) { formulae.emplace_back(!variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label))); } + boost::container::flat_set remainingLabels; - std::set_difference(relevancyInformation.relevantLabels.begin(), relevancyInformation.relevantLabels.end(), labelSet.begin(), labelSet.end(), std::inserter(remainingLabels, remainingLabels.end())); + //std::set_difference(relevancyInformation.relevantLabels.begin(), relevancyInformation.relevantLabels.end(), labelSet.begin(), labelSet.end(), std::inserter(remainingLabels, remainingLabels.end())); + std::set_difference(relevancyInformation.minimalityLabels.begin(), relevancyInformation.minimalityLabels.end(), labelSet.begin(), labelSet.end(), std::inserter(remainingLabels, remainingLabels.end())); + for (auto const& label : remainingLabels) { formulae.emplace_back(variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label))); } @@ -1339,6 +1260,22 @@ namespace storm { STORM_LOG_DEBUG("Ruling out single solution."); assertDisjunction(solver, formulae, *variableInformation.manager); } + + static void ruleOutBiggerSolutions(storm::solver::SmtSolver& solver, boost::container::flat_set const& labelSet, VariableInformation& variableInformation, RelevancyInformation const& relevancyInformation) { + std::vector formulae; + + boost::container::flat_set unknownLabels; + std::set_intersection(labelSet.begin(), labelSet.end(), relevancyInformation.minimalityLabels.begin(), relevancyInformation.minimalityLabels.end(), std::inserter(unknownLabels, unknownLabels.end())); + + //std::set_difference(labelSet.begin(), labelSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownLabels, unknownLabels.end())); + for (auto const& label : unknownLabels) { + formulae.emplace_back(!variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label))); + } + + + STORM_LOG_DEBUG("Ruling out set of solutions."); + assertDisjunction(solver, formulae, *variableInformation.manager); + } /*! * Analyzes the given sub-model that has a maximal reachability of zero (i.e. no psi states are reachable) and tries to construct assertions that aim to make at least one psi state reachable. @@ -1443,7 +1380,10 @@ namespace storm { std::vector formulae; boost::container::flat_set unknownReachableLabels; std::set_difference(reachableLabels.begin(), reachableLabels.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownReachableLabels, unknownReachableLabels.end())); - for (auto label : unknownReachableLabels) { + boost::container::flat_set unknownReachableMinimalityLabels; + std::set_intersection(unknownReachableLabels.begin(), unknownReachableLabels.end(), relevancyInformation.minimalityLabels.begin(), relevancyInformation.minimalityLabels.end(), std::inserter(unknownReachableMinimalityLabels, unknownReachableMinimalityLabels.end())); + + for (auto label : unknownReachableMinimalityLabels) { formulae.push_back(!variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label))); } for (auto const& cutLabelSet : cutLabels) { @@ -1544,7 +1484,10 @@ namespace storm { std::vector formulae; boost::container::flat_set unknownReachableLabels; std::set_difference(reachableLabels.begin(), reachableLabels.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownReachableLabels, unknownReachableLabels.end())); - for (auto label : unknownReachableLabels) { + boost::container::flat_set unknownReachableMinimalityLabels; + std::set_intersection(unknownReachableLabels.begin(), unknownReachableLabels.end(), relevancyInformation.minimalityLabels.begin(), relevancyInformation.minimalityLabels.end(), std::inserter(unknownReachableMinimalityLabels, unknownReachableMinimalityLabels.end())); + + for (auto label : unknownReachableMinimalityLabels) { formulae.push_back(!variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label))); } for (auto const& cutLabelSet : cutLabels) { @@ -1566,7 +1509,7 @@ namespace storm { * Returns the sub-model obtained from removing all choices that do not originate from the specified filterLabelSet. * Also returns the Labelsets of the sub-model. */ - static std::pair>, std::vector>> restrictModelToLabelSet(storm::models::sparse::Model const& model, boost::container::flat_set const& filterLabelSet) { + static std::pair>, std::vector>> restrictModelToLabelSet(storm::models::sparse::Model const& model, boost::container::flat_set const& filterLabelSet, boost::optional absorbState = boost::none) { bool customRowGrouping = model.isOfType(storm::models::ModelType::Mdp); @@ -1600,39 +1543,61 @@ namespace storm { if (customRowGrouping) { transitionMatrixBuilder.newRowGroup(currentRow); } - transitionMatrixBuilder.addNextValue(currentRow, state, storm::utility::one()); + uint64_t targetState = absorbState == boost::none ? state : absorbState.get(); + transitionMatrixBuilder.addNextValue(currentRow, targetState, storm::utility::one()); // Insert an empty label set for this choice resultLabelSet.emplace_back(); ++currentRow; } + } - + std::shared_ptr> resultModel; if (model.isOfType(storm::models::ModelType::Dtmc)) { - resultModel = std::make_shared>(transitionMatrixBuilder.build(), storm::models::sparse::StateLabeling(model.getStateLabeling())); + resultModel = std::make_shared>(transitionMatrixBuilder.build(), storm::models::sparse::StateLabeling(model.getStateLabeling()), model.getRewardModels()); } else { - resultModel = std::make_shared>(transitionMatrixBuilder.build(), storm::models::sparse::StateLabeling(model.getStateLabeling())); + resultModel = std::make_shared>(transitionMatrixBuilder.build(), storm::models::sparse::StateLabeling(model.getStateLabeling()), model.getRewardModels()); } return std::make_pair(resultModel, std::move(resultLabelSet)); } - static T computeMaximalReachabilityProbability(Environment const& env, storm::models::sparse::Model const& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) { - T result = storm::utility::zero(); - + static std::vector computeMaximalReachabilityProbability(Environment const& env, storm::models::sparse::Model const& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, boost::optional> const& rewardName) { + std::vector results; + std::vector allStatesResult; STORM_LOG_DEBUG("Invoking model checker."); if (model.isOfType(storm::models::ModelType::Dtmc)) { - allStatesResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeUntilProbabilities(env, false, model.getTransitionMatrix(), model.getBackwardTransitions(), phiStates, psiStates, false); + if (rewardName == boost::none) { + results.push_back(storm::utility::zero()); + allStatesResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeUntilProbabilities(env, false, model.getTransitionMatrix(), model.getBackwardTransitions(), phiStates, psiStates, false); + for (auto state : model.getInitialStates()) { + results.back() = std::max(results.back(), allStatesResult[state]); + } + } else { + for (auto const &rewName : rewardName.get()) { + results.push_back(storm::utility::zero()); + allStatesResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeReachabilityRewards(env, false, model.getTransitionMatrix(), model.getBackwardTransitions(), model.getRewardModel(rewName), psiStates, false); + for (auto state : model.getInitialStates()) { + results.back() = std::max(results.back(), allStatesResult[state]); + } + } + } } else { - storm::modelchecker::helper::SparseMdpPrctlHelper modelCheckerHelper; - allStatesResult = std::move(modelCheckerHelper.computeUntilProbabilities(env, false, model.getTransitionMatrix(), model.getBackwardTransitions(), phiStates, psiStates, false, false).values); - } - for (auto state : model.getInitialStates()) { - result = std::max(result, allStatesResult[state]); + if (rewardName == boost::none) { + results.push_back(storm::utility::zero()); + storm::modelchecker::helper::SparseMdpPrctlHelper modelCheckerHelper; + allStatesResult = std::move(modelCheckerHelper.computeUntilProbabilities(env, false, model.getTransitionMatrix(),model.getBackwardTransitions(), phiStates,psiStates, false, false).values); + for (auto state : model.getInitialStates()) { + results.back() = std::max(results.back(), allStatesResult[state]); + } + } else { + STORM_LOG_THROW(rewardName != boost::none, storm::exceptions::NotSupportedException, "Reward property counterexample generation is currently only supported for DTMCs."); + } } - return result; + + return results; } public: @@ -1648,8 +1613,23 @@ namespace storm { bool encodeReachability; bool useDynamicConstraints; bool silent = false; + bool addBackwardImplicationCuts = true; + uint64_t continueAfterFirstCounterexampleUntil = 0; + uint64_t maximumCounterexamples = 1; + uint64_t multipleCounterexampleSizeCap = 100000000; + uint64_t maximumExtraIterations = 100000000; }; - + + struct GeneratorStats { + std::chrono::milliseconds setupTime; + std::chrono::milliseconds solverTime; + std::chrono::milliseconds modelCheckingTime; + std::chrono::milliseconds analysisTime; + std::chrono::milliseconds cutTime; + uint64_t iterations; + }; + + /*! * Computes the minimal command set that is needed in the given model to exceed the given probability threshold for satisfying phi until psi. * @@ -1657,12 +1637,16 @@ namespace storm { * @param model The sparse model in which to find the minimal command set. * @param phiStates A bit vector characterizing all phi states in the model. * @param psiStates A bit vector characterizing all psi states in the model. - * @param probabilityThreshold The threshold that is to be achieved or exceeded. + * @param propertyThreshold The threshold that is to be achieved or exceeded. + * @param rewardName The name of the reward structure to use, or boost::none if probabilities are considerd. * @param strictBound Indicates whether the threshold needs to be achieved (true) or exceeded (false). * @param options A set of options for customization. */ - static boost::container::flat_set getMinimalLabelSet(Environment const& env, storm::storage::SymbolicModelDescription const& symbolicModel, storm::models::sparse::Model const& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, double probabilityThreshold, bool strictBound, boost::container::flat_set const& dontCareLabels = boost::container::flat_set(), Options const& options = Options()) { + static std::vector> getMinimalLabelSet(Environment const& env, GeneratorStats& stats, storm::storage::SymbolicModelDescription const& symbolicModel, storm::models::sparse::Model const& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, std::vector propertyThreshold, boost::optional> const& rewardName, bool strictBound, boost::container::flat_set const& dontCareLabels = boost::container::flat_set(), Options const& options = Options()) { #ifdef STORM_HAVE_Z3 + STORM_LOG_THROW(propertyThreshold.size() > 0, storm::exceptions::InvalidArgumentException, "At least one threshold has to be specified."); + STORM_LOG_THROW(propertyThreshold.size() == 1 || (rewardName && rewardName.get().size() == propertyThreshold.size()), storm::exceptions::InvalidArgumentException, "Multiple thresholds is only supported for multiple reward structures"); + std::vector> result; // Set up all clocks used for time measurement. auto totalClock = std::chrono::high_resolution_clock::now(); auto timeOfLastMessage = std::chrono::high_resolution_clock::now(); @@ -1697,14 +1681,18 @@ namespace storm { labelSets[choice] = choiceOrigins.getEdgeIndexSet(choice); } } + assert(labelSets.size() == model.getNumberOfChoices()); // (1) Check whether its possible to exceed the threshold if checkThresholdFeasible is set. - double maximalReachabilityProbability = 0; + std::vector maximalReachabilityProbability; if (options.checkThresholdFeasible) { - maximalReachabilityProbability = computeMaximalReachabilityProbability(env, model, phiStates, psiStates); - - STORM_LOG_THROW((strictBound && maximalReachabilityProbability >= probabilityThreshold) || (!strictBound && maximalReachabilityProbability > probabilityThreshold), storm::exceptions::InvalidArgumentException, "Given probability threshold " << probabilityThreshold << " can not be " << (strictBound ? "achieved" : "exceeded") << " in model with maximal reachability probability of " << maximalReachabilityProbability << "."); - std::cout << std::endl << "Maximal reachability in model is " << maximalReachabilityProbability << "." << std::endl << std::endl; + maximalReachabilityProbability = computeMaximalReachabilityProbability(env, model, phiStates, psiStates, rewardName); + + for (uint64_t i = 0; i < maximalReachabilityProbability.size(); ++i) { + STORM_LOG_THROW((strictBound && maximalReachabilityProbability[i] >= propertyThreshold[i]) || (!strictBound && maximalReachabilityProbability[i] > propertyThreshold[i]), storm::exceptions::InvalidArgumentException, "Given probability threshold " << propertyThreshold[i] << " can not be " << (strictBound ? "achieved" : "exceeded") << " in model with maximal reachability probability of " << maximalReachabilityProbability[i] << "."); + std::cout << std::endl << "Maximal property value in model is " << maximalReachabilityProbability[i] << "." << std::endl << std::endl; + } + } // (2) Identify all states and commands that are relevant, because only these need to be considered later. @@ -1723,19 +1711,19 @@ namespace storm { // and subsequently relax that. variableInformation.adderVariables = assertAdder(*solver, variableInformation); variableInformation.auxiliaryVariables.push_back(assertLessOrEqualKRelaxed(*solver, variableInformation, 0)); - + + // As we are done with the setup at this point, stop the clock for the setup time. + totalSetupTime = std::chrono::high_resolution_clock::now() - setupTimeClock; + // (6) Add constraints that cut off a lot of suboptimal solutions. STORM_LOG_DEBUG("Asserting cuts."); - assertCuts(symbolicModel, model, labelSets, psiStates, variableInformation, relevancyInformation, *solver); + stats.cutTime = assertCuts(symbolicModel, model, labelSets, psiStates, variableInformation, relevancyInformation, *solver, options.addBackwardImplicationCuts); STORM_LOG_DEBUG("Asserted cuts."); if (options.encodeReachability) { assertReachabilityCuts(model, labelSets, psiStates, variableInformation, relevancyInformation, *solver); STORM_LOG_DEBUG("Asserted reachability cuts."); } - - // As we are done with the setup at this point, stop the clock for the setup time. - totalSetupTime = std::chrono::high_resolution_clock::now() - setupTimeClock; - + // (7) Find the smallest set of commands that satisfies all constraints. If the probability of // satisfying phi until psi exceeds the given threshold, the set of labels is minimal and can be returned. // Otherwise, the current solution has to be ruled out and the next smallest solution is retrieved from @@ -1745,10 +1733,10 @@ namespace storm { // If there are no relevant labels, return directly. if (relevancyInformation.relevantLabels.empty()) { - return commandSet; + return {commandSet}; } else if (relevancyInformation.minimalityLabels.empty()) { commandSet.insert(relevancyInformation.relevantLabels.begin(), relevancyInformation.relevantLabels.end()); - return commandSet; + return {commandSet}; } // Set up some variables for the iterations. @@ -1756,36 +1744,68 @@ namespace storm { uint_fast64_t lastSize = 0; uint_fast64_t iterations = 0; uint_fast64_t currentBound = 0; - maximalReachabilityProbability = 0; + uint64_t firstCounterexampleFound = 0; // The value is not queried before being set. + std::vector maximalPropertyValue; uint_fast64_t zeroProbabilityCount = 0; + size_t smallestCounterexampleSize = model.getNumberOfChoices(); // Definitive upper bound uint64_t progressDelay = storm::settings::getModule().getShowProgressDelay(); do { + ++iterations; + + if (result.size() > 0 && iterations > firstCounterexampleFound + options.maximumExtraIterations) { + break; + } STORM_LOG_DEBUG("Computing minimal command set."); solverClock = std::chrono::high_resolution_clock::now(); - commandSet = findSmallestCommandSet(*solver, variableInformation, currentBound); + boost::optional> smallest = findSmallestCommandSet(*solver, variableInformation, currentBound); totalSolverTime += std::chrono::high_resolution_clock::now() - solverClock; - STORM_LOG_DEBUG("Computed minimal command set of size " << (commandSet.size() + relevancyInformation.knownLabels.size()) << "."); + if(smallest == boost::none) { + STORM_LOG_DEBUG("No further counterexamples."); + break; + } else { + commandSet = smallest.get(); + } + STORM_LOG_DEBUG("Computed minimal command with bound " << currentBound << " and set of size " << commandSet.size() + relevancyInformation.knownLabels.size() << " (" << commandSet.size() << " + " << relevancyInformation.knownLabels.size() << ") "); // Restrict the given model to the current set of labels and compute the reachability probability. modelCheckingClock = std::chrono::high_resolution_clock::now(); commandSet.insert(relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end()); - auto subChoiceOrigins = restrictModelToLabelSet(model, commandSet); + commandSet.insert(relevancyInformation.dontCareLabels.begin(), relevancyInformation.dontCareLabels.end()); + if (commandSet.size() > smallestCounterexampleSize + options.continueAfterFirstCounterexampleUntil || (result.size() > 1 && commandSet.size() > options.multipleCounterexampleSizeCap) ) { + STORM_LOG_DEBUG("No further counterexamples of similar size."); + break; + } + + if (commandSet.size() == nrCommands(symbolicModel)) { + result.push_back(commandSet); + break; + } + + auto subChoiceOrigins = restrictModelToLabelSet(model, commandSet, psiStates.getNextSetIndex(0)); std::shared_ptr> const& subModel = subChoiceOrigins.first; std::vector> const& subLabelSets = subChoiceOrigins.second; // Now determine the maximal reachability probability in the sub-model. - maximalReachabilityProbability = computeMaximalReachabilityProbability(env, *subModel, phiStates, psiStates); + maximalPropertyValue = computeMaximalReachabilityProbability(env, *subModel, phiStates, psiStates, rewardName); totalModelCheckingTime += std::chrono::high_resolution_clock::now() - modelCheckingClock; // Depending on whether the threshold was successfully achieved or not, we proceed by either analyzing the bad solution or stopping the iteration process. analysisClock = std::chrono::high_resolution_clock::now(); - if ((strictBound && maximalReachabilityProbability < probabilityThreshold) || (!strictBound && maximalReachabilityProbability <= probabilityThreshold)) { - if (maximalReachabilityProbability == storm::utility::zero()) { + bool violation = false; + for (uint64_t i = 0; i < maximalPropertyValue.size(); i++) { + violation |= (strictBound && maximalPropertyValue[i] < propertyThreshold[i]) || (!strictBound && maximalPropertyValue[i] <= propertyThreshold[i]); + } + + if (violation) { + if (!rewardName && maximalPropertyValue.front() == storm::utility::zero()) { ++zeroProbabilityCount; } if (options.useDynamicConstraints) { - if (maximalReachabilityProbability == storm::utility::zero()) { + // Determine which of the two analysis techniques to call by performing a reachability analysis. + storm::storage::BitVector reachableStates = storm::utility::graph::getReachableStates(subModel->getTransitionMatrix(), subModel->getInitialStates(), phiStates, psiStates); + + if (reachableStates.isDisjointFrom(psiStates)) { // If there was no target state reachable, analyze the solution and guide the solver into the right direction. analyzeZeroProbabilitySolution(*solver, *subModel, subLabelSets, model, labelSets, phiStates, psiStates, commandSet, variableInformation, relevancyInformation); } else { @@ -1793,25 +1813,41 @@ namespace storm { // the given threshold, we analyze the solution and try to guide the solver into the right direction. analyzeInsufficientProbabilitySolution(*solver, *subModel, subLabelSets, model, labelSets, phiStates, psiStates, commandSet, variableInformation, relevancyInformation); } + + if (relevancyInformation.dontCareLabels.size() > 0) { + ruleOutSingleSolution(*solver, commandSet, variableInformation, relevancyInformation); + } } else { // Do not guide solver, just rule out current solution. ruleOutSingleSolution(*solver, commandSet, variableInformation, relevancyInformation); } } else { - done = true; + STORM_LOG_DEBUG("Found a counterexample."); + if (result.empty()) { + // If this is the first counterexample we find, we store when we found it. + firstCounterexampleFound = iterations; + } + result.push_back(commandSet); + if (options.maximumCounterexamples > result.size()) { + STORM_LOG_DEBUG("Exclude counterexample for future."); + ruleOutBiggerSolutions(*solver, commandSet, variableInformation, relevancyInformation); + } else { + STORM_LOG_DEBUG("Stop searching for further counterexamples."); + done = true; + } + smallestCounterexampleSize = std::min(smallestCounterexampleSize, commandSet.size()); } totalAnalysisTime += (std::chrono::high_resolution_clock::now() - analysisClock); - ++iterations; auto now = std::chrono::high_resolution_clock::now(); auto durationSinceLastMessage = std::chrono::duration_cast(now - timeOfLastMessage).count(); if (static_cast(durationSinceLastMessage) >= progressDelay || lastSize < commandSet.size()) { auto milliseconds = std::chrono::duration_cast(now - totalClock).count(); if (lastSize < commandSet.size()) { - std::cout << "Improved lower bound to " << commandSet.size() << " after " << milliseconds << "ms." << std::endl; + std::cout << "Improved lower bound to " << currentBound << " after " << milliseconds << "ms." << std::endl; lastSize = commandSet.size(); } else { - std::cout << "Lower bound on label set size is " << commandSet.size() << " after " << milliseconds << "ms (checked " << iterations << " models, " << zeroProbabilityCount << " could not reach the target set)." << std::endl; + std::cout << "Lower bound on label set size is " << currentBound << " after " << milliseconds << "ms (checked " << iterations << " models, " << zeroProbabilityCount << " could not reach the target set)." << std::endl; timeOfLastMessage = std::chrono::high_resolution_clock::now(); } } @@ -1819,12 +1855,20 @@ namespace storm { // Compute and emit the time measurements if the corresponding flag was set. totalTime = std::chrono::high_resolution_clock::now() - totalClock; + + stats.analysisTime = std::chrono::duration_cast(totalAnalysisTime); + stats.setupTime = std::chrono::duration_cast(totalSetupTime); + stats.modelCheckingTime = std::chrono::duration_cast(totalModelCheckingTime); + stats.solverTime = std::chrono::duration_cast(totalSolverTime); + stats.iterations = iterations; + if (storm::settings::getModule().isShowStatisticsSet()) { boost::container::flat_set allLabels; for (auto const& e : labelSets) { allLabels.insert(e.begin(), e.end()); } - + + std::cout << "Metrics:" << std::endl; std::cout << " * all labels: " << allLabels.size() << std::endl; std::cout << " * known labels: " << relevancyInformation.knownLabels.size() << std::endl; @@ -1843,13 +1887,13 @@ namespace storm { std::cout << " * number of models that could not reach a target state: " << zeroProbabilityCount << " (" << 100 * static_cast(zeroProbabilityCount)/iterations << "%)" << std::endl << std::endl; } - return commandSet; + return result; #else throw storm::exceptions::NotImplementedException() << "This functionality is unavailable since storm has been compiled without support for Z3."; #endif } - static void extendLabelSetLowerBound(storm::models::sparse::Model const& model, boost::container::flat_set& commandSet, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool silent = false) { + static void extendLabelSetLowerBound(storm::models::sparse::Model const& model, boost::container::flat_set& commandSet, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool silent = false) { auto startTime = std::chrono::high_resolution_clock::now(); // Create sub-model that only contains the choices allowed by the given command set. @@ -1887,11 +1931,17 @@ namespace storm { } if (onlyProb0ESuccessors) { - auto const& labelSet = model.getChoiceOrigins()->asPrismChoiceOrigins().getCommandSet(choice); - hasLabeledChoice |= !labelSet.empty(); + uint64_t labelSetSize = 0; + if (model.getChoiceOrigins()->isPrismChoiceOrigins()) { + labelSetSize = model.getChoiceOrigins()->asPrismChoiceOrigins().getCommandSet(choice).size(); + } else { + assert(model.getChoiceOrigins()->isJaniChoiceOrigins()); + labelSetSize = model.getChoiceOrigins()->asJaniChoiceOrigins().getEdgeIndexSet(choice).size(); + } + hasLabeledChoice |= (labelSetSize != 0); - if (smallestCommandChoice == 0 || labelSet.size() < smallestCommandSetSize) { - smallestCommandSetSize = labelSet.size(); + if (smallestCommandChoice == 0 || labelSetSize < smallestCommandSetSize) { + smallestCommandSetSize = labelSetSize; smallestCommandChoice = choice; } } @@ -1899,9 +1949,15 @@ namespace storm { if (hasLabeledChoice) { // Take all labels of the selected choice. - auto const& labelSet = model.getChoiceOrigins()->asPrismChoiceOrigins().getCommandSet(smallestCommandChoice); - commandSet.insert(labelSet.begin(), labelSet.end()); - + if (model.getChoiceOrigins()->isPrismChoiceOrigins()) { + auto const& labelSet = model.getChoiceOrigins()->asPrismChoiceOrigins().getCommandSet(smallestCommandChoice); + commandSet.insert(labelSet.begin(), labelSet.end()); + } else { + assert(model.getChoiceOrigins()->isJaniChoiceOrigins()); + auto const& labelSet = model.getChoiceOrigins()->asJaniChoiceOrigins().getEdgeIndexSet(smallestCommandChoice); + commandSet.insert(labelSet.begin(), labelSet.end()); + } + // Check for which successor states choices need to be added for (auto const& successorEntry : model.getTransitionMatrix().getRow(smallestCommandChoice)) { if (!storm::utility::isZero(successorEntry.getValue())) { @@ -1921,28 +1977,58 @@ namespace storm { } - static boost::container::flat_set computeCounterexampleLabelSet(Environment const& env, storm::storage::SymbolicModelDescription const& symbolicModel, storm::models::sparse::Model const& model, std::shared_ptr const& formula, boost::container::flat_set const& dontCareLabels = boost::container::flat_set(), Options const& options = Options(true)) { - STORM_LOG_THROW(model.isOfType(storm::models::ModelType::Dtmc) || model.isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "MaxSAT-based counterexample generation is supported only for discrete-time models."); - if (!options.silent) { - std::cout << std::endl << "Generating minimal label counterexample for formula " << *formula << std::endl; + + struct CexInput { + storm::logic::ComparisonType comparisonType; + std::vector threshold; + boost::optional> rewardName = boost::none; + bool lowerBoundedFormula = false; + bool strictBound; + storm::storage::BitVector phiStates; + storm::storage::BitVector psiStates; + + void addRewardThresholdCombination(std::string reward, double thresh) { + STORM_LOG_THROW(rewardName, storm::exceptions::InvalidOperationException, "Can only add more reward names if a reward name is already set"); + rewardName.get().push_back(reward); + threshold.push_back(thresh); } + }; - STORM_LOG_THROW(formula->isProbabilityOperatorFormula(), storm::exceptions::InvalidPropertyException, "Counterexample generation does not support this kind of formula. Expecting a probability operator as the outermost formula element."); - storm::logic::ProbabilityOperatorFormula const& probabilityOperator = formula->asProbabilityOperatorFormula(); - STORM_LOG_THROW(probabilityOperator.hasBound(), storm::exceptions::InvalidPropertyException, "Counterexample generation only supports bounded formulas."); - STORM_LOG_THROW(probabilityOperator.getSubformula().isUntilFormula() || probabilityOperator.getSubformula().isEventuallyFormula(), storm::exceptions::InvalidPropertyException, "Path formula is required to be of the form 'phi U psi' for counterexample generation."); + static CexInput precompute(Environment const& env, storm::storage::SymbolicModelDescription const& symbolicModel, storm::models::sparse::Model const& model, std::shared_ptr const& formula) { + + CexInput result; + STORM_LOG_THROW(formula->isProbabilityOperatorFormula() || formula->isRewardOperatorFormula(), storm::exceptions::InvalidPropertyException, "Counterexample generation does not support this kind of formula. Expecting a probability operator as the outermost formula element."); + if (formula->isProbabilityOperatorFormula()) { + storm::logic::ProbabilityOperatorFormula const& probabilityOperator = formula->asProbabilityOperatorFormula(); + STORM_LOG_THROW(probabilityOperator.hasBound(), storm::exceptions::InvalidPropertyException, "Counterexample generation only supports bounded formulas."); + STORM_LOG_THROW(probabilityOperator.getSubformula().isUntilFormula() || probabilityOperator.getSubformula().isEventuallyFormula(), storm::exceptions::InvalidPropertyException, "Path formula is required to be of the form 'phi U psi' for counterexample generation."); + + result.comparisonType = probabilityOperator.getComparisonType(); + result.threshold.push_back(probabilityOperator.getThresholdAs()); + } else { + assert(formula->isRewardOperatorFormula()); + storm::logic::RewardOperatorFormula const& rewardOperator = formula->asRewardOperatorFormula(); + STORM_LOG_THROW(rewardOperator.hasBound(), storm::exceptions::InvalidPropertyException, "Counterexample generation only supports bounded formulas."); + STORM_LOG_THROW( rewardOperator.getSubformula().isEventuallyFormula(), storm::exceptions::InvalidPropertyException, "Path formula is required to be of the form 'F phi' for counterexample generation."); + + result.comparisonType = rewardOperator.getComparisonType(); + result.threshold.push_back(rewardOperator.getThresholdAs()); + result.rewardName = std::vector(); + result.rewardName.get().push_back(rewardOperator.getRewardModelName()); + + STORM_LOG_THROW(!storm::logic::isLowerBound(result.comparisonType), storm::exceptions::NotSupportedException, "Lower bounds in counterexamples are only supported for probability formulas."); + STORM_LOG_THROW(model.hasRewardModel(result.rewardName.get().front()), storm::exceptions::InvalidPropertyException, "Property refers to reward " << result.rewardName.get().front() << " but model does not contain such a reward model."); + STORM_LOG_THROW(model.getRewardModel(result.rewardName.get().front()).hasOnlyStateRewards(), storm::exceptions::NotSupportedException, "We only support state-based rewards at the moment."); + } + result.strictBound = result.comparisonType == storm::logic::ComparisonType::Less; + storm::logic::Formula const& subformula = formula->asOperatorFormula().getSubformula(); - storm::logic::ComparisonType comparisonType = probabilityOperator.getComparisonType(); - bool strictBound = comparisonType == storm::logic::ComparisonType::Less; - double threshold = probabilityOperator.getThresholdAs(); - storm::storage::BitVector phiStates; - storm::storage::BitVector psiStates; storm::modelchecker::SparsePropositionalModelChecker> modelchecker(model); - if (probabilityOperator.getSubformula().isUntilFormula()) { - STORM_LOG_THROW(!storm::logic::isLowerBound(comparisonType), storm::exceptions::NotSupportedException, "Lower bounds in counterexamples are only supported for eventually formulas."); - storm::logic::UntilFormula const& untilFormula = probabilityOperator.getSubformula().asUntilFormula(); + if (subformula.isUntilFormula()) { + STORM_LOG_THROW(!storm::logic::isLowerBound(result.comparisonType), storm::exceptions::NotSupportedException, "Lower bounds in counterexamples are only supported for eventually formulas."); + storm::logic::UntilFormula const& untilFormula = subformula.asUntilFormula(); std::unique_ptr leftResult = modelchecker.check(env, untilFormula.getLeftSubformula()); std::unique_ptr rightResult = modelchecker.check(env, untilFormula.getRightSubformula()); @@ -1950,21 +2036,21 @@ namespace storm { storm::modelchecker::ExplicitQualitativeCheckResult const& leftQualitativeResult = leftResult->asExplicitQualitativeCheckResult(); storm::modelchecker::ExplicitQualitativeCheckResult const& rightQualitativeResult = rightResult->asExplicitQualitativeCheckResult(); - phiStates = leftQualitativeResult.getTruthValuesVector(); - psiStates = rightQualitativeResult.getTruthValuesVector(); - } else if (probabilityOperator.getSubformula().isEventuallyFormula()) { - storm::logic::EventuallyFormula const& eventuallyFormula = probabilityOperator.getSubformula().asEventuallyFormula(); + result.phiStates = leftQualitativeResult.getTruthValuesVector(); + result.psiStates = rightQualitativeResult.getTruthValuesVector(); + } else if (subformula.isEventuallyFormula()) { + storm::logic::EventuallyFormula const& eventuallyFormula = subformula.asEventuallyFormula(); std::unique_ptr subResult = modelchecker.check(env, eventuallyFormula.getSubformula()); storm::modelchecker::ExplicitQualitativeCheckResult const& subQualitativeResult = subResult->asExplicitQualitativeCheckResult(); - phiStates = storm::storage::BitVector(model.getNumberOfStates(), true); - psiStates = subQualitativeResult.getTruthValuesVector(); + result.phiStates = storm::storage::BitVector(model.getNumberOfStates(), true); + result.psiStates = subQualitativeResult.getTruthValuesVector(); } - bool lowerBoundedFormula = false; - if (storm::logic::isLowerBound(comparisonType)) { + if (storm::logic::isLowerBound(result.comparisonType)) { + STORM_LOG_DEBUG("Computing counterexample for a lowerbound."); // If the formula specifies a lower bound, we need to modify the phi and psi states. // More concretely, we convert P(min)>lambda(F psi) to P(max)<(1-lambda)(G !psi) = P(max)<(1-lambda)(!psi U prob0E(psi)) // where prob0E(psi) is the set of states for which there exists a strategy \sigma_0 that avoids @@ -1973,48 +2059,68 @@ namespace storm { // This means that from all states in prob0E(psi) we need to include labels such that \sigma_0 // is actually included in the resulting model. This prevents us from guaranteeing the minimality of // the returned counterexample, so we warn about that. - if (!options.silent) { - STORM_LOG_WARN("Generating counterexample for lower-bounded property. The resulting command set need not be minimal."); - } + // Modify bound appropriately. - comparisonType = storm::logic::invertPreserveStrictness(comparisonType); - threshold = storm::utility::one() - threshold; + result.comparisonType = storm::logic::invertPreserveStrictness(result.comparisonType); + result.threshold.back() = storm::utility::one() - result.threshold.back(); // Modify the phi and psi states appropriately. - storm::storage::BitVector statesWithProbability0E = storm::utility::graph::performProb0E(model.getTransitionMatrix(), model.getTransitionMatrix().getRowGroupIndices(), model.getBackwardTransitions(), phiStates, psiStates); - phiStates = ~psiStates; - psiStates = std::move(statesWithProbability0E); + storm::storage::BitVector statesWithProbability0E = storm::utility::graph::performProb0E(model.getTransitionMatrix(), model.getTransitionMatrix().getRowGroupIndices(), model.getBackwardTransitions(), result.phiStates, result.psiStates); + result.phiStates = ~result.psiStates; + result.psiStates = std::move(statesWithProbability0E); // Remember our transformation so we can add commands to guarantee that the prob0E(a) states actually // have a strategy that voids a states. - lowerBoundedFormula = true; + result.lowerBoundedFormula = true; } + return result; + + } + + + static std::vector> computeCounterexampleLabelSet(Environment const& env, GeneratorStats& stats, storm::storage::SymbolicModelDescription const& symbolicModel, storm::models::sparse::Model const& model, CexInput const& counterexInput, boost::container::flat_set const& dontCareLabels = boost::container::flat_set(), Options const& options = Options(true)) { + STORM_LOG_THROW(model.isOfType(storm::models::ModelType::Dtmc) || model.isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "MaxSAT-based counterexample generation is supported only for discrete-time models."); // Delegate the actual computation work to the function of equal name. auto startTime = std::chrono::high_resolution_clock::now(); - auto labelSet = getMinimalLabelSet(env, symbolicModel, model, phiStates, psiStates, threshold, strictBound, dontCareLabels, options); + auto labelSets = getMinimalLabelSet(env, stats, symbolicModel, model, counterexInput.phiStates, counterexInput.psiStates, counterexInput.threshold, counterexInput.rewardName, counterexInput.strictBound, dontCareLabels, options); auto endTime = std::chrono::high_resolution_clock::now(); if (!options.silent) { - std::cout << std::endl << "Computed minimal label set of size " << labelSet.size() << " in " << std::chrono::duration_cast(endTime - startTime).count() << "ms." << std::endl; + for (auto const& labelSet : labelSets) { + std::cout << std::endl << "Computed minimal label set of size " << labelSet.size(); + + } + std::cout << std::endl << " in " << std::chrono::duration_cast(endTime - startTime).count() << "ms." << std::endl; + } // Extend the command set properly. - if (lowerBoundedFormula) { - extendLabelSetLowerBound(model, labelSet, phiStates, psiStates, options.silent); + for (auto& labelSet : labelSets) { + if (counterexInput.lowerBoundedFormula) { + STORM_LOG_DEBUG("Extending the counterexample due to lower bound computation."); + extendLabelSetLowerBound(model, labelSet, counterexInput.phiStates, counterexInput.psiStates, options.silent); + } } - return labelSet; + return labelSets; } static std::shared_ptr computeCounterexample(Environment const& env, storm::storage::SymbolicModelDescription const& symbolicModel, storm::models::sparse::Model const& model, std::shared_ptr const& formula) { #ifdef STORM_HAVE_Z3 - auto labelSet = computeCounterexampleLabelSet(env, symbolicModel, model, formula); - + std::cout << std::endl << "Generating minimal label counterexample for formula " << *formula << std::endl; + GeneratorStats stats; + CexInput prec = precompute(env, symbolicModel, model, formula); + if (prec.lowerBoundedFormula) { + STORM_LOG_WARN("Generating counterexample for lower-bounded property. The resulting command set need not be minimal."); + } + auto labelSets = computeCounterexampleLabelSet(env, stats, symbolicModel, model, prec); + + if (symbolicModel.isPrismProgram()) { - return std::make_shared(symbolicModel.asPrismProgram().restrictCommands(labelSet)); + return std::make_shared(symbolicModel.asPrismProgram().restrictCommands(labelSets[0])); } else { STORM_LOG_ASSERT(symbolicModel.isJaniModel(), "Unknown symbolic model description type."); - return std::make_shared(symbolicModel.asJaniModel().restrictEdges(labelSet)); + return std::make_shared(symbolicModel.asJaniModel().restrictEdges(labelSets[0])); } #else throw storm::exceptions::NotImplementedException() << "This functionality is unavailable since storm has been compiled without support for Z3."; diff --git a/src/storm/settings/modules/CounterexampleGeneratorSettings.cpp b/src/storm-counterexamples/settings/modules/CounterexampleGeneratorSettings.cpp similarity index 92% rename from src/storm/settings/modules/CounterexampleGeneratorSettings.cpp rename to src/storm-counterexamples/settings/modules/CounterexampleGeneratorSettings.cpp index 2525f19b0..5b2142ee3 100644 --- a/src/storm/settings/modules/CounterexampleGeneratorSettings.cpp +++ b/src/storm-counterexamples/settings/modules/CounterexampleGeneratorSettings.cpp @@ -1,4 +1,4 @@ -#include "storm/settings/modules/CounterexampleGeneratorSettings.h" +#include "storm-counterexamples/settings/modules/CounterexampleGeneratorSettings.h" #include "storm/settings/SettingsManager.h" #include "storm/exceptions/InvalidSettingsException.h" @@ -53,7 +53,7 @@ namespace storm { bool CounterexampleGeneratorSettings::check() const { // Ensure that the model was given either symbolically or explicitly. - STORM_LOG_THROW(!isMinimalCommandSetGenerationSet() || storm::settings::getModule().isPrismInputSet(), storm::exceptions::InvalidSettingsException, "For the generation of a minimal command set, the model has to be specified in the PRISM format."); + STORM_LOG_THROW(!isMinimalCommandSetGenerationSet() || storm::settings::getModule().isPrismInputSet() || storm::settings::getModule().isJaniInputSet(), storm::exceptions::InvalidSettingsException, "For the generation of a minimal command set, the model has to be specified in the PRISM/JANI format."); if (isMinimalCommandSetGenerationSet()) { STORM_LOG_WARN_COND(isUseMaxSatBasedMinimalCommandSetGenerationSet() || !isEncodeReachabilitySet(), "Encoding reachability is only available for the MaxSat-based minimal command set generation, so selecting it has no effect."); diff --git a/src/storm/settings/modules/CounterexampleGeneratorSettings.h b/src/storm-counterexamples/settings/modules/CounterexampleGeneratorSettings.h similarity index 100% rename from src/storm/settings/modules/CounterexampleGeneratorSettings.h rename to src/storm-counterexamples/settings/modules/CounterexampleGeneratorSettings.h diff --git a/src/storm-dft-cli/CMakeLists.txt b/src/storm-dft-cli/CMakeLists.txt index 4780eef4d..5886ac0a0 100644 --- a/src/storm-dft-cli/CMakeLists.txt +++ b/src/storm-dft-cli/CMakeLists.txt @@ -6,4 +6,4 @@ 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) +install(TARGETS storm-dft-cli EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 1d7f52ffc..d0fa84495 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -6,7 +6,10 @@ #include "storm-dft/settings/modules/DftGspnSettings.h" #include "storm/settings/modules/IOSettings.h" #include "storm/settings/modules/ResourceSettings.h" +#include "storm/settings/modules/GeneralSettings.h" + +#include "storm-parsers/api/storm-parsers.h" #include "storm/utility/initialize.h" #include "storm-cli-utilities/cli.h" @@ -32,10 +35,10 @@ void processOptions() { std::shared_ptr> dft; if (dftIOSettings.isDftJsonFileSet()) { STORM_LOG_DEBUG("Loading DFT from file " << dftIOSettings.getDftJsonFilename()); - dft = storm::api::loadDFTJson(dftIOSettings.getDftJsonFilename()); + dft = storm::api::loadDFTJsonFile(dftIOSettings.getDftJsonFilename()); } else { STORM_LOG_DEBUG("Loading DFT from file " << dftIOSettings.getDftFilename()); - dft = storm::api::loadDFTGalileo(dftIOSettings.getDftFilename()); + dft = storm::api::loadDFTGalileoFile(dftIOSettings.getDftFilename()); } if (dftIOSettings.isDisplayStatsSet()) { @@ -46,7 +49,7 @@ void processOptions() { if (dftIOSettings.isExportToJson()) { // Export to json - storm::api::exportDFTToJson(*dft, dftIOSettings.getExportJsonFilename()); + storm::api::exportDFTToJsonFile(*dft, dftIOSettings.getExportJsonFilename()); return; } diff --git a/src/storm-dft/CMakeLists.txt b/src/storm-dft/CMakeLists.txt index 9ec70aed2..48a046238 100644 --- a/src/storm-dft/CMakeLists.txt +++ b/src/storm-dft/CMakeLists.txt @@ -17,7 +17,7 @@ set_target_properties(storm-dft PROPERTIES DEFINE_SYMBOL "") list(APPEND STORM_TARGETS storm-dft) set(STORM_TARGETS ${STORM_TARGETS} PARENT_SCOPE) -target_link_libraries(storm-dft PUBLIC storm storm-gspn ${STORM_DFT_LINK_LIBRARIES}) +target_link_libraries(storm-dft PUBLIC storm storm-gspn storm-parsers ${STORM_DFT_LINK_LIBRARIES}) # Install storm headers to include directory. foreach(HEADER ${STORM_DFT_HEADERS}) @@ -36,5 +36,5 @@ add_custom_target(copy_storm_dft_headers DEPENDS ${STORM_DFT_OUTPUT_HEADERS} ${S add_dependencies(storm-dft copy_storm_dft_headers) # installation -install(TARGETS storm-dft RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) +install(TARGETS storm-dft EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-dft/api/storm-dft.cpp b/src/storm-dft/api/storm-dft.cpp index 2021c3e51..5740f71bf 100644 --- a/src/storm-dft/api/storm-dft.cpp +++ b/src/storm-dft/api/storm-dft.cpp @@ -7,12 +7,24 @@ namespace storm { namespace api { template<> - void exportDFTToJson(storm::storage::DFT const& dft, std::string const& file) { + void exportDFTToJsonFile(storm::storage::DFT const& dft, std::string const& file) { storm::storage::DftJsonExporter::toFile(dft, file); } template<> - void exportDFTToJson(storm::storage::DFT const& dft, std::string const& file) { + void exportDFTToJsonFile(storm::storage::DFT const& dft, std::string const& file) { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Export to JSON not supported for this data type."); + } + + template<> + std::string exportDFTToJsonString(storm::storage::DFT const& dft) { + std::stringstream stream; + storm::storage::DftJsonExporter::toStream(dft, stream); + return stream.str(); + } + + template<> + std::string exportDFTToJsonString(storm::storage::DFT const& dft) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Export to JSON not supported for this data type."); } diff --git a/src/storm-dft/api/storm-dft.h b/src/storm-dft/api/storm-dft.h index 3746923bd..136e8bfb4 100644 --- a/src/storm-dft/api/storm-dft.h +++ b/src/storm-dft/api/storm-dft.h @@ -23,8 +23,21 @@ namespace storm { * @return DFT. */ template - std::shared_ptr> loadDFTGalileo(std::string const& file) { - return std::make_shared>(storm::parser::DFTGalileoParser::parseDFT(file)); + std::shared_ptr> loadDFTGalileoFile(std::string const& file) { + return std::make_shared>(storm::parser::DFTGalileoParser::parseDFT(file)); + } + + /*! + * Load DFT from JSON string. + * + * @param jsonString String containing DFT description in JSON format. + * + * @return DFT. + */ + template + std::shared_ptr> loadDFTJsonString(std::string const& jsonString) { + storm::parser::DFTJsonParser parser; + return std::make_shared>(parser.parseJsonFromString(jsonString)); } /*! @@ -35,9 +48,9 @@ namespace storm { * @return DFT. */ template - std::shared_ptr> loadDFTJson(std::string const& file) { - storm::parser::DFTJsonParser parser; - return std::make_shared>(parser.parseJson(file)); + std::shared_ptr> loadDFTJsonFile(std::string const& file) { + storm::parser::DFTJsonParser parser; + return std::make_shared>(parser.parseJsonFromFile(file)); } /*! @@ -99,7 +112,15 @@ namespace storm { * @param file File. */ template - void exportDFTToJson(storm::storage::DFT const& dft, std::string const& file); + void exportDFTToJsonFile(storm::storage::DFT const& dft, std::string const& file); + + /*! + * Export DFT to JSON string. + * + * @param dft DFT. + */ + template + std::string exportDFTToJsonString(storm::storage::DFT const& dft); /*! * Export DFT to SMT encoding. diff --git a/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp b/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp index 8d277d60d..24e39a0f5 100644 --- a/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp @@ -1,9 +1,12 @@ #include "DFTModelChecker.h" #include "storm/settings/modules/IOSettings.h" +#include "storm/settings/modules/GeneralSettings.h" #include "storm/builder/ParallelCompositionBuilder.h" #include "storm/utility/bitoperations.h" #include "storm/utility/DirectEncodingExporter.h" +#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" +#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm-dft/builder/ExplicitDFTModelBuilder.h" #include "storm-dft/storage/dft/DFTIsomorphism.h" diff --git a/src/storm-dft/parser/DFTGalileoParser.h b/src/storm-dft/parser/DFTGalileoParser.h index c56540edd..f956d8cb9 100644 --- a/src/storm-dft/parser/DFTGalileoParser.h +++ b/src/storm-dft/parser/DFTGalileoParser.h @@ -3,12 +3,12 @@ #include #include "storm/storage/expressions/ExpressionManager.h" -#include "storm/parser/ExpressionParser.h" +#include "storm-parsers/parser/ExpressionParser.h" #include "storm/storage/expressions/ExpressionEvaluator.h" #include "storm-dft/storage/dft/DFT.h" #include "storm-dft/builder/DFTBuilder.h" -#include "storm/parser/ValueParser.h" +#include "storm-parsers/parser/ValueParser.h" namespace storm { diff --git a/src/storm-dft/parser/DFTJsonParser.cpp b/src/storm-dft/parser/DFTJsonParser.cpp index 0d6f312f2..e44042980 100644 --- a/src/storm-dft/parser/DFTJsonParser.cpp +++ b/src/storm-dft/parser/DFTJsonParser.cpp @@ -15,54 +15,48 @@ namespace storm { namespace parser { template - storm::storage::DFT DFTJsonParser::parseJson(const std::string& filename) { - readFile(filename); - storm::storage::DFT dft = builder.build(); - STORM_LOG_DEBUG("Elements:" << std::endl << dft.getElementsString()); - STORM_LOG_DEBUG("Spare Modules:" << std::endl << dft.getSpareModulesString()); - return dft; + storm::storage::DFT DFTJsonParser::parseJsonFromFile(std::string const& filename) { + STORM_LOG_DEBUG("Parsing from JSON file"); + std::ifstream file; + storm::utility::openFile(filename, file); + json jsonInput; + jsonInput << file; + storm::utility::closeFile(file); + return parseJson(jsonInput); } template - std::string DFTJsonParser::generateUniqueName(std::string const& id, std::string const& name) { - std::string newId = name; - std::replace(newId.begin(), newId.end(), ' ', '_'); - std::replace(newId.begin(), newId.end(), '-', '_'); - return newId + "_" + id; + storm::storage::DFT DFTJsonParser::parseJsonFromString(std::string const& jsonString) { + STORM_LOG_DEBUG("Parsing from JSON string"); + json jsonInput = json::parse(jsonString); + return parseJson(jsonInput); } template - void DFTJsonParser::readFile(const std::string& filename) { - STORM_LOG_DEBUG("Parsing from JSON"); - - std::ifstream file; - storm::utility::openFile(filename, file); - json parsedJson; - parsedJson << file; - storm::utility::closeFile(file); - - json parameters = parsedJson.at("parameters"); -#ifdef STORM_HAVE_CARL - for (auto it = parameters.begin(); it != parameters.end(); ++it) { - STORM_LOG_THROW((std::is_same::value), storm::exceptions::NotSupportedException, "Parameters only allowed when using rational functions."); - std::string parameter = it.key(); - storm::expressions::Variable var = manager->declareRationalVariable(parameter); - identifierMapping.emplace(var.getName(), var); - parser.setIdentifierMapping(identifierMapping); - STORM_LOG_TRACE("Added parameter: " << var.getName()); + storm::storage::DFT DFTJsonParser::parseJson(json const& jsonInput) { + // Try to parse parameters + if (jsonInput.find("parameters") != jsonInput.end()) { + json parameters = jsonInput.at("parameters"); + STORM_LOG_THROW(parameters.empty() || (std::is_same::value), storm::exceptions::NotSupportedException, "Parameters only allowed when using rational functions."); + for (auto it = parameters.begin(); it != parameters.end(); ++it) { + std::string parameter = it.key(); + storm::expressions::Variable var = manager->declareRationalVariable(parameter); + identifierMapping.emplace(var.getName(), var); + parser.setIdentifierMapping(identifierMapping); + STORM_LOG_TRACE("Added parameter: " << var.getName()); + } } -#endif - - json nodes = parsedJson.at("nodes"); + json nodes = jsonInput.at("nodes"); // Start by building mapping from ids to their unique names std::map nameMapping; - for (auto& element: nodes) { + for (auto& element : nodes) { json data = element.at("data"); std::string id = data.at("id"); nameMapping[id] = generateUniqueName(id, data.at("name")); } + // Parse nodes for (auto& element : nodes) { STORM_LOG_TRACE("Parsing: " << element); bool success = true; @@ -82,7 +76,7 @@ namespace storm { } else if (type == "or") { success = builder.addOrElement(name, childNames); } else if (type == "vot") { - std::string votThreshold = data.at("voting"); + std::string votThreshold = parseJsonNumber(data.at("voting")); success = builder.addVotElement(name, boost::lexical_cast(votThreshold), childNames); } else if (type == "pand") { success = builder.addPandElement(name, childNames); @@ -95,11 +89,11 @@ namespace storm { } else if (type== "fdep") { success = builder.addDepElement(name, childNames, storm::utility::one()); } else if (type== "pdep") { - ValueType probability = parseRationalExpression(data.at("prob")); + ValueType probability = parseRationalExpression(parseJsonNumber(data.at("prob"))); success = builder.addDepElement(name, childNames, probability); } else if (type == "be") { - ValueType failureRate = parseRationalExpression(data.at("rate")); - ValueType dormancyFactor = parseRationalExpression(data.at("dorm")); + ValueType failureRate = parseRationalExpression(parseJsonNumber(data.at("rate"))); + ValueType dormancyFactor = parseRationalExpression(parseJsonNumber(data.at("dorm"))); bool transient = false; if (data.count("transient") > 0) { transient = data.at("transient"); @@ -110,23 +104,52 @@ namespace storm { success = false; } - // Do not set layout for dependencies - // This does not work because dependencies might be splitted - // TODO: do splitting later in rewriting step - if (type != "fdep" && type != "pdep") { - // Set layout positions - json position = element.at("position"); - double x = position.at("x"); - double y = position.at("y"); - builder.addLayoutInfo(name, x / 7, y / 7); + // Try to set layout information + if (element.find("position") != element.end()) { + // Do not set layout for dependencies + // This does not work because dependencies might be splitted + // TODO: do splitting later in rewriting step + if (type != "fdep" && type != "pdep") { + // Set layout positions + json position = element.at("position"); + double x = position.at("x"); + double y = position.at("y"); + builder.addLayoutInfo(name, x / 7, y / 7); + } } STORM_LOG_THROW(success, storm::exceptions::FileIoException, "Error while adding element '" << element << "'."); } - std::string toplevelName = nameMapping[parsedJson.at("toplevel")]; + std::string toplevelName = nameMapping[parseJsonNumber(jsonInput.at("toplevel"))]; if(!builder.setTopLevel(toplevelName)) { STORM_LOG_THROW(false, storm::exceptions::FileIoException, "Top level id unknown."); } + + // Build DFT + storm::storage::DFT dft = builder.build(); + STORM_LOG_DEBUG("Elements:" << std::endl << dft.getElementsString()); + STORM_LOG_DEBUG("Spare Modules:" << std::endl << dft.getSpareModulesString()); + return dft; + + } + + template + std::string DFTJsonParser::generateUniqueName(std::string const& id, std::string const& name) { + std::string newId = name; + std::replace(newId.begin(), newId.end(), ' ', '_'); + std::replace(newId.begin(), newId.end(), '-', '_'); + return newId + "_" + id; + } + + template + std::string DFTJsonParser::parseJsonNumber(json number) { + if (number.is_string()) { + return number.get(); + } else { + std::stringstream stream; + stream << number; + return stream.str(); + } } template @@ -140,10 +163,6 @@ namespace storm { return boost::lexical_cast(expr); } - // Explicitly instantiate the class. - template class DFTJsonParser; - -#ifdef STORM_HAVE_CARL template<> storm::RationalFunction DFTJsonParser::parseRationalExpression(std::string const& expr) { STORM_LOG_TRACE("Translating expression: " << expr); @@ -154,8 +173,9 @@ namespace storm { return rationalFunction; } + + // Explicitly instantiate the class. + template class DFTJsonParser; template class DFTJsonParser; -#endif - } } diff --git a/src/storm-dft/parser/DFTJsonParser.h b/src/storm-dft/parser/DFTJsonParser.h index 5517d85e1..5187e3cc3 100644 --- a/src/storm-dft/parser/DFTJsonParser.h +++ b/src/storm-dft/parser/DFTJsonParser.h @@ -3,7 +3,7 @@ #include #include "storm/storage/expressions/ExpressionManager.h" -#include "storm/parser/ExpressionParser.h" +#include "storm-parsers/parser/ExpressionParser.h" #include "storm/storage/expressions/ExpressionEvaluator.h" #include "storm-dft/storage/dft/DFT.h" @@ -33,14 +33,18 @@ namespace storm { DFTJsonParser() : manager(new storm::expressions::ExpressionManager()), parser(*manager), evaluator(*manager) { } - storm::storage::DFT parseJson(std::string const& filename); - + storm::storage::DFT parseJsonFromString(std::string const& jsonString); + + storm::storage::DFT parseJsonFromFile(std::string const& filename); + private: - void readFile(std::string const& filename); + storm::storage::DFT parseJson(json const& jsonInput); std::string generateUniqueName(std::string const& id, std::string const& name); ValueType parseRationalExpression(std::string const& expr); + + std::string parseJsonNumber(json number); }; } } diff --git a/src/storm-dft/settings/DftSettings.cpp b/src/storm-dft/settings/DftSettings.cpp index 0b98c5373..43f0b0f51 100644 --- a/src/storm-dft/settings/DftSettings.cpp +++ b/src/storm-dft/settings/DftSettings.cpp @@ -20,7 +20,7 @@ #include "storm/settings/modules/GameSolverSettings.h" #include "storm/settings/modules/BisimulationSettings.h" #include "storm/settings/modules/ResourceSettings.h" -#include "storm/settings/modules/JaniExportSettings.h" +#include "storm-conv/settings/modules/JaniExportSettings.h" #include "storm-gspn/settings/modules/GSPNSettings.h" #include "storm-gspn/settings/modules/GSPNExportSettings.h" diff --git a/src/storm-dft/storage/dft/DftJsonExporter.cpp b/src/storm-dft/storage/dft/DftJsonExporter.cpp index 1a4cd7019..699490984 100644 --- a/src/storm-dft/storage/dft/DftJsonExporter.cpp +++ b/src/storm-dft/storage/dft/DftJsonExporter.cpp @@ -24,28 +24,23 @@ namespace storm { template modernjson::json DftJsonExporter::translate(storm::storage::DFT const& dft) { - modernjson::json jsonDft; - jsonDft["toplevel"] = dft.getTopLevelIndex(); - - // Parameters - modernjson::json jsonParameters; - jsonDft["parameters"] = jsonParameters; - // Nodes modernjson::json jsonNodes; for (size_t i = 0; i < dft.nrElements(); ++i) { modernjson::json jsonNode = translateNode(dft.getElement(i)); jsonNodes.push_back(jsonNode); } - jsonDft["nodes"] = jsonNodes; + modernjson::json jsonDft; + jsonDft["toplevel"] = std::to_string(dft.getTopLevelIndex()); + jsonDft["nodes"] = jsonNodes; return jsonDft; } template modernjson::json DftJsonExporter::translateNode(DFTElementCPointer const& element) { modernjson::json nodeData; - nodeData["id"] = element->id(); + nodeData["id"] = std::to_string(element->id()); nodeData["name"] = element->name(); std::string type = storm::storage::toString(element->type()); std::transform(type.begin(), type.end(), type.begin(), ::tolower); @@ -54,36 +49,39 @@ namespace storm { if (element->isGate()) { // Set children for gate std::shared_ptr const> gate = std::static_pointer_cast const>(element); - std::vector children; + std::vector children; for (DFTElementPointer const& child : gate->children()) { - children.push_back(child->id()); + children.push_back(std::to_string(child->id())); } nodeData["children"] = children; - // Set gate specific data + // Gate dependent export switch (element->type()) { case storm::storage::DFTElementType::VOT: nodeData["voting"] = std::static_pointer_cast const>(element)->threshold(); break; + case storm::storage::DFTElementType::PDEP: + { + ValueType probability = std::static_pointer_cast const>(element)->probability(); + if (!storm::utility::isOne(probability)) { + std::stringstream stream; + stream << probability; + nodeData["prob"] = stream.str(); + } + break; + } default: break; } } else if (element->isBasicElement()) { // Set BE specific data std::shared_ptr const> be = std::static_pointer_cast const>(element); - nodeData["rate"] = storm::utility::to_string(be->activeFailureRate()); - nodeData["dorm"] = storm::utility::to_string(be->passiveFailureRate() / be->activeFailureRate()); - nodeData["repair"] = 0; - nodeData["transient"] = be->isTransient(); - } else if (element->isDependency()) { - std::shared_ptr const> dependency = std::static_pointer_cast const>(element); - std::vector children = { dependency->triggerEvent()->id() }; - for (DFTElementPointer const& child : dependency->dependentEvents()) { - children.push_back(child->id()); - } - nodeData["children"] = children; - nodeData["prob"] = storm::utility::to_string(dependency->probability()); - } else { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Type name: " << type << " not supported for export."); + std::stringstream stream; + stream << be->activeFailureRate(); + nodeData["rate"] = stream.str(); + stream.str(std::string()); // Clear stringstream + ValueType dormancy = be->passiveFailureRate() / be->activeFailureRate(); + stream << dormancy; + nodeData["dorm"] = stream.str(); } modernjson::json jsonNode; diff --git a/src/storm-dft/storage/dft/DftJsonExporter.h b/src/storm-dft/storage/dft/DftJsonExporter.h index e7650614c..646821bb6 100644 --- a/src/storm-dft/storage/dft/DftJsonExporter.h +++ b/src/storm-dft/storage/dft/DftJsonExporter.h @@ -29,14 +29,12 @@ namespace storm { static void toStream(storm::storage::DFT const& dft, std::ostream& os); - static modernjson::json translate(storm::storage::DFT const& dft); - - private: + static modernjson::json translate(storm::storage::DFT const& dft); + static modernjson::json translateNode(DFTElementCPointer const& element); }; - } } diff --git a/src/storm-gspn-cli/CMakeLists.txt b/src/storm-gspn-cli/CMakeLists.txt index 39d656a4c..7b9ebf7aa 100644 --- a/src/storm-gspn-cli/CMakeLists.txt +++ b/src/storm-gspn-cli/CMakeLists.txt @@ -1,3 +1,4 @@ +# Create storm-gspn. add_executable(storm-gspn-cli ${PROJECT_SOURCE_DIR}/src/storm-gspn-cli/storm-gspn.cpp) target_link_libraries(storm-gspn-cli storm-gspn storm-cli-utilities) # Adding headers for xcode set_target_properties(storm-gspn-cli PROPERTIES OUTPUT_NAME "storm-gspn") @@ -5,4 +6,4 @@ set_target_properties(storm-gspn-cli PROPERTIES OUTPUT_NAME "storm-gspn") add_dependencies(binaries storm-gspn-cli) # installation -install(TARGETS storm-gspn-cli RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) \ No newline at end of file +install(TARGETS storm-gspn-cli EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-gspn-cli/storm-gspn.cpp b/src/storm-gspn-cli/storm-gspn.cpp index 602b353a0..9d6924297 100644 --- a/src/storm-gspn-cli/storm-gspn.cpp +++ b/src/storm-gspn-cli/storm-gspn.cpp @@ -5,16 +5,15 @@ #include "storm-gspn/builder/JaniGSPNBuilder.h" #include "storm-gspn/api/storm-gspn.h" -#include "storm/exceptions/BaseException.h" -#include "storm/exceptions/WrongFormatException.h" - #include "storm/utility/macros.h" #include "storm/utility/initialize.h" #include "api/storm.h" + +#include "storm-parsers/api/storm-parsers.h" #include "storm-cli-utilities/cli.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/storage/expressions/ExpressionManager.h" #include "storm/storage/jani/Model.h" @@ -27,12 +26,13 @@ #include "storm/exceptions/FileIoException.h" +#include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/modules/IOSettings.h" #include "storm-gspn/settings/modules/GSPNSettings.h" #include "storm-gspn/settings/modules/GSPNExportSettings.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/settings/modules/DebugSettings.h" -#include "storm/settings/modules/JaniExportSettings.h" +#include "storm-conv/settings/modules/JaniExportSettings.h" #include "storm/settings/modules/ResourceSettings.h" @@ -54,25 +54,6 @@ void initializeSettings() { } -std::unordered_map parseCapacitiesList(std::string const& filename) { - std::unordered_map map; - - std::ifstream stream; - storm::utility::openFile(filename, stream); - - std::string line; - while ( std::getline(stream, line) ) { - std::vector strs; - boost::split(strs, line, boost::is_any_of("\t ")); - STORM_LOG_THROW(strs.size() == 2, storm::exceptions::WrongFormatException, "Expect key value pairs"); - std::cout << std::stoll(strs[1]) << std::endl; - map[strs[0]] = std::stoll(strs[1]); - } - storm::utility::closeFile(stream); - return map; -} - - int main(const int argc, const char **argv) { try { storm::utility::setUp(); @@ -83,14 +64,22 @@ int main(const int argc, const char **argv) { if (!optionsCorrect) { return -1; } + + auto gspnSettings = storm::settings::getModule(); // parse gspn from file - if (!storm::settings::getModule().isGspnFileSet()) { - return -1; + if (!gspnSettings.isGspnFileSet()) { + // If no gspn file is given, nothing needs to be done. + return 0; + } + + std::string constantDefinitionString = ""; + if (gspnSettings.isConstantsSet()) { + constantDefinitionString = gspnSettings.getConstantDefinitionString(); } auto parser = storm::parser::GspnParser(); - auto gspn = parser.parse(storm::settings::getModule().getGspnFilename()); + auto gspn = parser.parse(gspnSettings.getGspnFilename(), constantDefinitionString); std::string formulaString = ""; if (storm::settings::getModule().isPropertySet()) { @@ -99,28 +88,29 @@ int main(const int argc, const char **argv) { boost::optional> propertyFilter; storm::parser::FormulaParser formulaParser(gspn->getExpressionManager()); std::vector properties = storm::api::parseProperties(formulaParser, formulaString, propertyFilter); - + properties = storm::api::substituteConstantsInProperties(properties, gspn->getConstantsSubstitution()); + if (!gspn->isValid()) { STORM_LOG_ERROR("The gspn is not valid."); } - if(storm::settings::getModule().isCapacitiesFileSet()) { - auto capacities = parseCapacitiesList(storm::settings::getModule().getCapacitiesFilename()); + if(gspnSettings.isCapacitiesFileSet()) { + auto capacities = storm::api::parseCapacitiesList(gspnSettings.getCapacitiesFilename(), *gspn); + gspn->setCapacities(capacities); + } else if (gspnSettings.isCapacitySet()) { + uint64_t capacity = gspnSettings.getCapacity(); + std::unordered_map capacities; + for (auto const& place : gspn->getPlaces()) { + capacities.emplace(place.getName(), capacity); + } gspn->setCapacities(capacities); } - storm::api::handleGSPNExportSettings(*gspn); + storm::api::handleGSPNExportSettings(*gspn, [&](storm::builder::JaniGSPNBuilder const&) { return properties; }); - if(storm::settings::getModule().isJaniFileSet()) { - storm::jani::Model* model = storm::api::buildJani(*gspn); - storm::api::exportJaniModel(*model, properties, storm::settings::getModule().getJaniFilename()); - delete model; - } - delete gspn; return 0; -// // // construct ma // auto builder = storm::builder::ExplicitGspnModelBuilder<>(); // auto ma = builder.translateGspn(gspn, formula); diff --git a/src/storm-gspn/CMakeLists.txt b/src/storm-gspn/CMakeLists.txt index cf80ff6aa..c2b8dd896 100644 --- a/src/storm-gspn/CMakeLists.txt +++ b/src/storm-gspn/CMakeLists.txt @@ -8,11 +8,31 @@ file(GLOB_RECURSE STORM_GSPN_SOURCES ${PROJECT_SOURCE_DIR}/src/storm-gspn/*/*.cp file(GLOB_RECURSE STORM_GSPN_HEADERS ${PROJECT_SOURCE_DIR}/src/storm-gspn/*/*.h) -# Create storm-pgcl. +# Create storm-gspn. add_library(storm-gspn SHARED ${STORM_GSPN_SOURCES} ${STORM_GSPN_HEADERS}) + +# Remove define symbol for shared libstorm. +set_target_properties(storm-gspn PROPERTIES DEFINE_SYMBOL "") +#add_dependencies(storm resources) list(APPEND STORM_TARGETS storm-gspn) set(STORM_TARGETS ${STORM_TARGETS} PARENT_SCOPE) -target_link_libraries(storm-gspn storm ${STORM_GSPN_LINK_LIBRARIES}) +target_link_libraries(storm-gspn PUBLIC storm storm-conv storm-parsers ${STORM_GSPN_LINK_LIBRARIES}) + +# Install storm headers to include directory. +foreach(HEADER ${STORM_GSPN_HEADERS}) + string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/src/?" "" RELATIVE_HEADER_PATH ${HEADER}) + string(REGEX MATCH "(.*)[/\\]" RELATIVE_DIRECTORY ${RELATIVE_HEADER_PATH}) + string(REGEX REPLACE "${RELATIVE_DIRECTORY}/?" "" HEADER_FILENAME ${RELATIVE_HEADER_PATH}) + add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${HEADER} ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME} + DEPENDS ${HEADER} + ) + list(APPEND STORM_GSPN_OUTPUT_HEADERS "${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME}") +endforeach() +add_custom_target(copy_storm_gspn_headers DEPENDS ${STORM_GSPN_OUTPUT_HEADERS} ${STORM_GSPN_HEADERS}) +add_dependencies(storm-gspn copy_storm_gspn_headers) # installation -install(TARGETS storm-gspn RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) +install(TARGETS storm-gspn EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-gspn/api/storm-gspn.cpp b/src/storm-gspn/api/storm-gspn.cpp index 08796386b..63da78250 100644 --- a/src/storm-gspn/api/storm-gspn.cpp +++ b/src/storm-gspn/api/storm-gspn.cpp @@ -3,7 +3,10 @@ #include "storm/settings/SettingsManager.h" #include "storm/utility/file.h" #include "storm-gspn/settings/modules/GSPNExportSettings.h" - +#include "storm-conv/settings/modules/JaniExportSettings.h" +#include "storm-conv/api/storm-conv.h" +#include "storm-parsers/parser/ExpressionParser.h" +#include "storm/exceptions/WrongFormatException.h" namespace storm { namespace api { @@ -13,7 +16,7 @@ namespace storm { return builder.build(); } - void handleGSPNExportSettings(storm::gspn::GSPN const& gspn) { + void handleGSPNExportSettings(storm::gspn::GSPN const& gspn, std::function(storm::builder::JaniGSPNBuilder const&)> const& janiProperyGetter) { storm::settings::modules::GSPNExportSettings const& exportSettings = storm::settings::getModule(); if (exportSettings.isWriteToDotSet()) { std::ofstream fs; @@ -55,7 +58,54 @@ namespace storm { gspn.writeStatsToStream(fs); storm::utility::closeFile(fs); } - } + + if (exportSettings.isWriteToJaniSet()) { + auto const& jani = storm::settings::getModule(); + storm::converter::JaniConversionOptions options(jani); + + storm::builder::JaniGSPNBuilder builder(gspn); + storm::jani::Model* model = builder.build("gspn_automaton", exportSettings.isAddJaniPropertiesSet()); + storm::api::postprocessJani(*model, options); + + auto properties = janiProperyGetter(builder); + if (exportSettings.isAddJaniPropertiesSet()) { + properties.insert(properties.end(), builder.getStandardProperties().begin(), builder.getStandardProperties().end()); + } + storm::api::exportJaniToFile(*model, properties, exportSettings.getWriteToJaniFilename(), jani.isCompactJsonSet()); + delete model; + } + } + + std::unordered_map parseCapacitiesList(std::string const& filename, storm::gspn::GSPN const& gspn) { + storm::parser::ExpressionParser expressionParser(*gspn.getExpressionManager()); + std::unordered_map identifierMapping; + for (auto const& var : gspn.getExpressionManager()->getVariables()) { + identifierMapping.emplace(var.getName(), var.getExpression()); + } + expressionParser.setIdentifierMapping(identifierMapping); + expressionParser.setAcceptDoubleLiterals(false); + + std::unordered_map map; + + std::ifstream stream; + storm::utility::openFile(filename, stream); + + std::string line; + while ( std::getline(stream, line) ) { + std::vector strs; + boost::split(strs, line, boost::is_any_of("\t ")); + STORM_LOG_THROW(strs.size() == 2, storm::exceptions::WrongFormatException, "Expect key value pairs"); + storm::expressions::Expression expr = expressionParser.parseFromString(strs[1]); + if (!gspn.getConstantsSubstitution().empty()) { + expr = expr.substitute(gspn.getConstantsSubstitution()); + } + STORM_LOG_THROW(!expr.containsVariables(), storm::exceptions::WrongFormatException, "The capacity expression '" << strs[1] << "' still contains undefined constants."); + map[strs[0]] = expr.evaluateAsInt(); + } + storm::utility::closeFile(stream); + return map; + } } } + diff --git a/src/storm-gspn/api/storm-gspn.h b/src/storm-gspn/api/storm-gspn.h index 7687c35e4..a54bd6e28 100644 --- a/src/storm-gspn/api/storm-gspn.h +++ b/src/storm-gspn/api/storm-gspn.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "storm/storage/jani/Model.h" #include "storm-gspn/storage/gspn/GSPN.h" #include "storm-gspn/builder/JaniGSPNBuilder.h" @@ -12,6 +14,10 @@ namespace storm { */ storm::jani::Model* buildJani(storm::gspn::GSPN const& gspn); - void handleGSPNExportSettings(storm::gspn::GSPN const& gspn); + void handleGSPNExportSettings(storm::gspn::GSPN const& gspn, + std::function(storm::builder::JaniGSPNBuilder const&)> const& janiProperyGetter = [](storm::builder::JaniGSPNBuilder const&) { return std::vector(); }); + + std::unordered_map parseCapacitiesList(std::string const& filename, storm::gspn::GSPN const& gspn); + } } diff --git a/src/storm-gspn/builder/ExplicitGspnModelBuilder.cpp b/src/storm-gspn/builder/ExplicitGspnModelBuilder.cpp index 1960a623a..23ba78594 100644 --- a/src/storm-gspn/builder/ExplicitGspnModelBuilder.cpp +++ b/src/storm-gspn/builder/ExplicitGspnModelBuilder.cpp @@ -5,7 +5,7 @@ //#include "storm/utility/macros.h" //#include "storm/exceptions/NotImplementedException.h" //#include "storm/storage/expressions/ExpressionManager.h" -//#include "storm/parser/FormulaParser.h" +//#include "storm-parsers/parser/FormulaParser.h" //#include "storm/storage/expressions/ExpressionEvaluator.h" // //namespace storm { diff --git a/src/storm-gspn/builder/JaniGSPNBuilder.cpp b/src/storm-gspn/builder/JaniGSPNBuilder.cpp index cd81bb266..744b76eb4 100644 --- a/src/storm-gspn/builder/JaniGSPNBuilder.cpp +++ b/src/storm-gspn/builder/JaniGSPNBuilder.cpp @@ -1,18 +1,36 @@ #include "JaniGSPNBuilder.h" +#include + +#include "storm/logic/Formulas.h" + +#include "storm/exceptions/InvalidModelException.h" namespace storm { namespace builder { - storm::jani::Model* JaniGSPNBuilder::build(std::string const& automatonName) { - storm::jani::Model* model = new storm::jani::Model(gspn.getName(), storm::jani::ModelType::MA, janiVersion, expressionManager); + storm::jani::Model* JaniGSPNBuilder::build(std::string const& automatonName, bool buildStandardProperties) { + storm::jani::ModelType modelType = storm::jani::ModelType::MA; + if (gspn.getNumberOfTimedTransitions() == 0) { + storm::jani::ModelType modelType = storm::jani::ModelType::MDP; + } else if (gspn.getNumberOfImmediateTransitions() == 0) { + storm::jani::ModelType modelType = storm::jani::ModelType::CTMC; + } + storm::jani::Model* model = new storm::jani::Model(gspn.getName(), modelType, janiVersion, expressionManager); storm::jani::Automaton mainAutomaton(automatonName, expressionManager->declareIntegerVariable("loc")); addVariables(model); uint64_t locId = addLocation(mainAutomaton); addEdges(mainAutomaton, locId); model->addAutomaton(mainAutomaton); model->setStandardSystemComposition(); + if (buildStandardProperties) { + buildProperties(model); + } return model; } + + std::vector const& JaniGSPNBuilder::getStandardProperties() const { + return standardProperties; + } void JaniGSPNBuilder::addVariables(storm::jani::Model* model) { for (auto const& place : gspn.getPlaces()) { @@ -122,7 +140,7 @@ namespace storm { } for (auto const& trans : gspn.getTimedTransitions()) { storm::expressions::Expression guard = expressionManager->boolean(true); - + std::vector assignments; for (auto const& inPlaceEntry : trans.getInputPlaces()) { guard = guard && (vars[inPlaceEntry.first]->getExpressionVariable() >= inPlaceEntry.second); @@ -143,12 +161,143 @@ namespace storm { std::shared_ptr templateEdge = std::make_shared(guard); automaton.registerTemplateEdge(templateEdge); + + storm::expressions::Expression rate = expressionManager->rational(trans.getRate()); + if (trans.hasInfiniteServerSemantics() || (trans.hasKServerSemantics() && !trans.hasSingleServerSemantics())) { + STORM_LOG_THROW(trans.hasKServerSemantics() || !trans.getInputPlaces().empty(), storm::exceptions::InvalidModelException, "Unclear semantics: Found a transition with infinite-server semantics and without input place."); + storm::expressions::Expression enablingDegree; + bool firstArgumentOfMinExpression = true; + if (trans.hasKServerSemantics()) { + enablingDegree = expressionManager->integer(trans.getNumberOfServers()); + firstArgumentOfMinExpression = false; + } + for (auto const& inPlaceEntry : trans.getInputPlaces()) { + storm::expressions::Expression enablingDegreeInPlace = vars[inPlaceEntry.first]->getExpressionVariable() / expressionManager->integer(inPlaceEntry.second); // Integer division! + if (firstArgumentOfMinExpression == true) { + enablingDegree = enablingDegreeInPlace; + } else { + enablingDegree = storm::expressions::minimum(enablingDegree, enablingDegreeInPlace); + } + } + rate = rate * enablingDegree; + } templateEdge->addDestination(assignments); - storm::jani::Edge e(locId, storm::jani::Model::SILENT_ACTION_INDEX, expressionManager->rational(trans.getRate()), templateEdge, {locId}, {expressionManager->integer(1)}); + storm::jani::Edge e(locId, storm::jani::Model::SILENT_ACTION_INDEX, rate, templateEdge, {locId}, {expressionManager->integer(1)}); automaton.addEdge(e); } } + + storm::jani::Variable const& JaniGSPNBuilder::addDeadlockTransientVariable(storm::jani::Model* model, std::string name, bool ignoreCapacities, bool ignoreInhibitorArcs, bool ignoreEmptyPlaces) { + + storm::expressions::Expression transientValue = expressionManager->boolean(true); + + // build the conjunction over all transitions + std::vector transitions; + transitions.reserve(gspn.getNumberOfImmediateTransitions() + gspn.getNumberOfTimedTransitions()); + for (auto const& t : gspn.getImmediateTransitions()) { + transitions.push_back(&t); + } + for (auto const& t : gspn.getTimedTransitions()) { + transitions.push_back(&t); + } + bool firstTransition = true; + for (auto const& transition : transitions) { + + // build the disjunction over all in/out places and inhibitor arcs + storm::expressions::Expression transitionDisabled = expressionManager->boolean(false); + bool firstPlace = true; + if (!ignoreEmptyPlaces) { + for (auto const& placeIdMult : transition->getInputPlaces()) { + storm::expressions::Expression placeBlocksTransition = (vars.at(placeIdMult.first)->getExpressionVariable() < expressionManager->integer(placeIdMult.second)); + if (firstPlace) { + transitionDisabled = placeBlocksTransition; + firstPlace = false; + } else { + transitionDisabled = transitionDisabled || placeBlocksTransition; + } + } + } + if (!ignoreInhibitorArcs) { + for (auto const& placeIdMult : transition->getInhibitionPlaces()) { + storm::expressions::Expression placeBlocksTransition = (vars.at(placeIdMult.first)->getExpressionVariable() >= expressionManager->integer(placeIdMult.second)); + if (firstPlace) { + transitionDisabled = placeBlocksTransition; + firstPlace = false; + } else { + transitionDisabled = transitionDisabled || placeBlocksTransition; + } + } + } + if (!ignoreCapacities) { + for (auto const& placeIdMult : transition->getOutputPlaces()) { + auto const& place = gspn.getPlace(placeIdMult.first); + if (place->hasRestrictedCapacity()) { + storm::expressions::Expression placeBlocksTransition = (vars.at(placeIdMult.first)->getExpressionVariable() + expressionManager->integer(placeIdMult.second) > expressionManager->integer(place->getCapacity())); + if (firstPlace) { + transitionDisabled = placeBlocksTransition; + firstPlace = false; + } else { + transitionDisabled = transitionDisabled || placeBlocksTransition; + } + } + } + } + + if (firstTransition) { + transientValue = transitionDisabled; + firstTransition = false; + } else { + transientValue = transientValue && transitionDisabled; + } + } + + auto exprVar = expressionManager->declareBooleanVariable(name); + auto const& janiVar = model->addVariable(*storm::jani::makeBooleanVariable(name, exprVar, expressionManager->boolean(false), true)); + storm::jani::Assignment assignment(janiVar, transientValue); + model->getAutomata().front().getLocations().front().addTransientAssignment(assignment); + return janiVar; + } + + + std::string getUniqueVarName(storm::expressions::ExpressionManager const& manager, std::string name) { + std::string res = name; + while (manager.hasVariable(res)) { + res.append("_"); + } + return res; + } + + void JaniGSPNBuilder::buildProperties(storm::jani::Model* model) { + standardProperties.clear(); + + auto const& deadlockVar = addDeadlockTransientVariable(model, getUniqueVarName(*expressionManager, "deadl")); + auto deadlock = std::make_shared(deadlockVar.getExpressionVariable().getExpression()); + auto trueFormula = std::make_shared(true); + + auto maxReachDeadlock = std::make_shared( + std::make_shared(deadlock, storm::logic::FormulaContext::Probability), + storm::logic::OperatorInformation(storm::solver::OptimizationDirection::Maximize)); + standardProperties.emplace_back("MaxPrReachDeadlock", maxReachDeadlock, "The maximal probability to eventually reach a deadlock."); + + auto exprTB = expressionManager->declareRationalVariable(getUniqueVarName(*expressionManager, "TIME_BOUND")); + auto janiTB = storm::jani::Constant(exprTB.getName(), exprTB); + model->addConstant(janiTB); + storm::logic::TimeBound tb(false, janiTB.getExpressionVariable().getExpression()); + storm::logic::TimeBoundReference tbr(storm::logic::TimeBoundType::Time); + auto maxReachDeadlockTimeBounded = std::make_shared( + std::make_shared(trueFormula, deadlock, boost::none, tb, tbr), + storm::logic::OperatorInformation(storm::solver::OptimizationDirection::Maximize)); + standardProperties.emplace_back("MaxPrReachDeadlockTB", maxReachDeadlockTimeBounded, "The maximal probability to reach a deadlock within 'TIME_BOUND' steps."); + + auto expTimeDeadlock = std::make_shared( + std::make_shared(deadlock, storm::logic::FormulaContext::Time), + storm::logic::OperatorInformation(storm::solver::OptimizationDirection::Maximize)); + standardProperties.emplace_back("MinExpTimeDeadlock", expTimeDeadlock, "The minimal expected time to reach a deadlock."); + + } + + } } \ No newline at end of file diff --git a/src/storm-gspn/builder/JaniGSPNBuilder.h b/src/storm-gspn/builder/JaniGSPNBuilder.h index 474d04807..205f248e1 100644 --- a/src/storm-gspn/builder/JaniGSPNBuilder.h +++ b/src/storm-gspn/builder/JaniGSPNBuilder.h @@ -19,11 +19,13 @@ namespace storm { } - storm::jani::Model* build(std::string const& automatonName = "gspn_automaton"); + storm::jani::Model* build(std::string const& automatonName = "gspn_automaton", bool buildStandardProperties = false); - storm::jani::Variable const& getPlaceVariable(uint64_t placeId) { + storm::jani::Variable const& getPlaceVariable(uint64_t placeId) const { return *vars.at(placeId); } + + std::vector const& getStandardProperties() const; private: @@ -33,11 +35,16 @@ namespace storm { void addEdges(storm::jani::Automaton& automaton, uint64_t locId); + storm::jani::Variable const& addDeadlockTransientVariable(storm::jani::Model* model, std::string name, bool ignoreCapacities = false, bool ignoreInhibitorArcs = false, bool ignoreEmptyPlaces = false); + void buildProperties(storm::jani::Model* model); + const uint64_t janiVersion = 1; storm::gspn::GSPN const& gspn; std::map vars; std::shared_ptr expressionManager; + std::vector standardProperties; + }; } } diff --git a/src/storm-gspn/parser/GreatSpnEditorProjectParser.cpp b/src/storm-gspn/parser/GreatSpnEditorProjectParser.cpp index ae17ca59b..4c77bc3a2 100644 --- a/src/storm-gspn/parser/GreatSpnEditorProjectParser.cpp +++ b/src/storm-gspn/parser/GreatSpnEditorProjectParser.cpp @@ -9,6 +9,7 @@ #include "storm/exceptions/UnexpectedException.h" #include "storm/exceptions/WrongFormatException.h" +#include "storm/exceptions/NotSupportedException.h" #include "storm/utility/macros.h" namespace { @@ -22,17 +23,27 @@ namespace { namespace storm { namespace parser { - + GreatSpnEditorProjectParser::GreatSpnEditorProjectParser(std::string const& constantDefinitionString) : manager(std::make_shared()) { + if (constantDefinitionString != "") { + std::vector constDefs; + boost::split( constDefs, constantDefinitionString, boost::is_any_of(",")); + for (auto const& pair : constDefs) { + std::vector keyvaluepair; + boost::split( keyvaluepair, pair, boost::is_any_of("=")); + STORM_LOG_THROW(keyvaluepair.size() == 2, storm::exceptions::WrongFormatException, "Expected a constant definition of the form N=42 but got " << pair); + constantDefinitions.emplace(keyvaluepair.at(0), keyvaluepair.at(1)); + } + } + } + storm::gspn::GSPN* GreatSpnEditorProjectParser::parse(xercesc::DOMElement const* elementRoot) { if (storm::adapters::XMLtoString(elementRoot->getTagName()) == "project") { traverseProjectElement(elementRoot); - return builder.buildGspn(); + return builder.buildGspn(manager, constantsSubstitution); } else { // If the top-level node is not a "pnml" or "" node, then throw an exception. STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Failed to identify the root element.\n"); } - - } void GreatSpnEditorProjectParser::traverseProjectElement(xercesc::DOMNode const* const node) { @@ -118,6 +129,25 @@ namespace storm { } // traverse children + // First pass: find constant definitions + constantsSubstitution.clear(); + expressionParser = std::make_shared(*manager); + std::unordered_map identifierMapping; + expressionParser->setIdentifierMapping(identifierMapping); + for (uint_fast64_t i = 0; i < node->getChildNodes()->getLength(); ++i) { + auto child = node->getChildNodes()->item(i); + auto name = storm::adapters::getName(child); + if (name.compare("constant") == 0 || name.compare("template") == 0) { + traverseConstantOrTemplateElement(child); + } + } + // Update the expression parser to make the newly created variables known to it + expressionParser = std::make_shared(*manager); + for (auto const& var : manager->getVariables()) { + identifierMapping.emplace(var.getName(), var.getExpression()); + } + expressionParser->setIdentifierMapping(identifierMapping); + // Second pass: traverse other children for (uint_fast64_t i = 0; i < node->getChildNodes()->getLength(); ++i) { auto child = node->getChildNodes()->item(i); auto name = storm::adapters::getName(child); @@ -126,6 +156,8 @@ namespace storm { traversePlaceElement(child); } else if(name.compare("transition") == 0) { traverseTransitionElement(child); + } else if(name.compare("constant") == 0 || name.compare("template") == 0) { + // Ignore constant def in second pass } else if (isOnlyWhitespace(name)) { // ignore node (contains only whitespace) } else { @@ -136,6 +168,70 @@ namespace storm { } } + void GreatSpnEditorProjectParser::traverseConstantOrTemplateElement(xercesc::DOMNode const* const node) { + std::string identifier; + storm::expressions::Type type; + std::string valueStr = ""; + + // traverse attributes + for (uint_fast64_t i = 0; i < node->getAttributes()->getLength(); ++i) { + auto attr = node->getAttributes()->item(i); + auto name = storm::adapters::getName(attr); + + if (name.compare("name") == 0) { + identifier = storm::adapters::XMLtoString(attr->getNodeValue()); + } else if (name.compare("consttype") == 0 || name.compare("type") == 0) { + if (storm::adapters::XMLtoString(attr->getNodeValue()).compare("REAL") == 0) { + type = manager->getRationalType(); + } else if (storm::adapters::XMLtoString(attr->getNodeValue()).compare("INTEGER") == 0) { + type = manager->getIntegerType(); + } else { + STORM_PRINT_AND_LOG("Unknown consttype: " << storm::adapters::XMLtoString(attr->getNodeValue()) << std::endl); + } + } else if (name.compare("value") == 0) { + valueStr = storm::adapters::XMLtoString(attr->getNodeValue()); + } else if ((name == "x") || (name == "y")) { + // Ignore + } else { + // Found node or attribute which is at the moment not handled by this parser. + // Notify the user and continue the parsing. + STORM_PRINT_AND_LOG("unknown attribute (node=" + storm::adapters::XMLtoString(node->getNodeName()) + "): " + name + "\n"); + } + } + + STORM_LOG_THROW(!manager->hasVariable(identifier), storm::exceptions::NotSupportedException, "Multiple definitions of constant '" << identifier << "' were found."); + if (valueStr == "") { + auto constDef = constantDefinitions.find(identifier); + STORM_LOG_THROW(constDef != constantDefinitions.end(), storm::exceptions::NotSupportedException, "Constant '" << identifier << "' has no value defined."); + valueStr = constDef->second; + } + storm::expressions::Variable var; + if (type.isRationalType()) { + var = manager->declareRationalVariable(identifier); + expressionParser->setAcceptDoubleLiterals(true); + } else if (type.isIntegerType()) { + var = manager->declareIntegerVariable(identifier); + expressionParser->setAcceptDoubleLiterals(false); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Unknown type of constant" << type << "."); + } + constantsSubstitution.emplace(var, expressionParser->parseFromString(valueStr)); + + // traverse children + for (uint_fast64_t i = 0; i < node->getChildNodes()->getLength(); ++i) { + auto child = node->getChildNodes()->item(i); + auto name = storm::adapters::getName(child); + + if (isOnlyWhitespace(name)) { + // ignore node (contains only whitespace) + } else { + // Found node or attribute which is at the moment nod handled by this parser. + // Notify the user and continue the parsing. + STORM_PRINT_AND_LOG("unknown child (node=" + storm::adapters::XMLtoString(node->getNodeName()) + "): " + name + "\n"); + } + } + } + void GreatSpnEditorProjectParser::traverseEdgesElement(xercesc::DOMNode const* const node) { // traverse attributes for (uint_fast64_t i = 0; i < node->getAttributes()->getLength(); ++i) { @@ -187,7 +283,7 @@ namespace storm { if (name.compare("name") == 0) { placeName = storm::adapters::XMLtoString(attr->getNodeValue()); } else if (name.compare("marking") == 0) { - initialTokens = std::stoull(storm::adapters::XMLtoString(attr->getNodeValue())); + initialTokens = parseInt(storm::adapters::XMLtoString(attr->getNodeValue())); } else if (ignorePlaceAttribute(name)) { // ignore node } else { @@ -217,7 +313,7 @@ namespace storm { bool ignoreTransitionAttribute(std::string const& name) { // TODO we should probably not ignore x-servers but check that it is 0.5. - if ((name == "label-x") || (name == "label-y") || (name == "x") || (name == "y") || (name == "nservers-x")) { + if ((name == "label-x") || (name == "label-y") || (name == "x") || (name == "y") || (name == "nservers-x") || (name == "delay-x") || (name == "delay-y") || (name == "rotation")) { return true; } return false; @@ -227,6 +323,9 @@ namespace storm { std::string transitionName; bool immediateTransition; double rate = 1.0; // The default rate in GreatSPN. + double weight = 1.0; // The default weight in GreatSpn + uint64_t priority = 1; // The default priority in GreatSpn + boost::optional numServers; // traverse attributes for (uint_fast64_t i = 0; i < node->getAttributes()->getLength(); ++i) { @@ -238,11 +337,26 @@ namespace storm { } else if (name.compare("type") == 0) { if (storm::adapters::XMLtoString(attr->getNodeValue()).compare("EXP") == 0) { immediateTransition = false; - } else { + } else if (storm::adapters::XMLtoString(attr->getNodeValue()).compare("IMM") == 0) { immediateTransition = true; + } else { + STORM_PRINT_AND_LOG("unknown transition type: " << storm::adapters::XMLtoString(attr->getNodeValue())); } } else if(name.compare("delay") == 0) { - rate = std::stod(storm::adapters::XMLtoString(attr->getNodeValue())); + rate = parseReal(storm::adapters::XMLtoString(attr->getNodeValue())); + } else if(name.compare("weight") == 0) { + weight = parseReal(storm::adapters::XMLtoString(attr->getNodeValue())); + } else if(name.compare("priority") == 0) { + priority = parseInt(storm::adapters::XMLtoString(attr->getNodeValue())); + } else if (name.compare("nservers") == 0) { + std::string nservers = storm::adapters::XMLtoString(attr->getNodeValue()); + if (nservers == "Single") { + numServers = 1; + } else if (nservers == "Infinite") { + // Ignore this case as we assume infinite by default (similar to GreatSpn) + } else { + numServers = parseInt(nservers); + } } else if (ignoreTransitionAttribute(name)) { // ignore node } else { @@ -254,9 +368,9 @@ namespace storm { } if (immediateTransition) { - builder.addImmediateTransition(0, 0, transitionName); + builder.addImmediateTransition(priority, weight, transitionName); } else { - builder.addTimedTransition(0, rate, transitionName); + builder.addTimedTransition(0, rate, numServers, transitionName); } // traverse children @@ -303,10 +417,10 @@ namespace storm { head = storm::adapters::XMLtoString(attr->getNodeValue()); } else if (name.compare("tail") == 0) { tail = storm::adapters::XMLtoString(attr->getNodeValue()); - } else if (name.compare("kind") == 0) { + } else if (name.compare("kind") == 0 || name.compare("type") == 0) { kind = storm::adapters::XMLtoString(attr->getNodeValue()); } else if (name.compare("mult") == 0) { - mult = std::stoull(storm::adapters::XMLtoString(attr->getNodeValue())); + mult = parseInt(storm::adapters::XMLtoString(attr->getNodeValue())); } else if (ignoreArcAttribute(name)) { // ignore node } else { @@ -349,6 +463,21 @@ namespace storm { } } + int64_t GreatSpnEditorProjectParser::parseInt(std::string str) { + expressionParser->setAcceptDoubleLiterals(false); + auto expr = expressionParser->parseFromString(str).substitute(constantsSubstitution); + STORM_LOG_ASSERT(!expr.containsVariables(), "Can not evaluate expression " << str << " as it contains undefined variables."); + return expr.evaluateAsInt(); + } + + double GreatSpnEditorProjectParser::parseReal(std::string str) { + expressionParser->setAcceptDoubleLiterals(true); + auto expr = expressionParser->parseFromString(str).substitute(constantsSubstitution); + STORM_LOG_ASSERT(!expr.containsVariables(), "Can not evaluate expression " << str << " as it contains undefined variables."); + return expr.evaluateAsDouble(); + } + + } } #endif diff --git a/src/storm-gspn/parser/GreatSpnEditorProjectParser.h b/src/storm-gspn/parser/GreatSpnEditorProjectParser.h index 7c0f5e713..7663d2a23 100644 --- a/src/storm-gspn/parser/GreatSpnEditorProjectParser.h +++ b/src/storm-gspn/parser/GreatSpnEditorProjectParser.h @@ -7,6 +7,9 @@ #include #include +#include "storm/storage/expressions/ExpressionManager.h" +#include "storm-parsers/parser/ExpressionParser.h" + #include "storm-gspn/storage/gspn/GSPN.h" #include "storm-gspn/storage/gspn/GspnBuilder.h" @@ -17,6 +20,8 @@ namespace storm { public: + GreatSpnEditorProjectParser(std::string const& constantDefinitionString); + /*! * Parses the given file into the GSPN. * @@ -31,14 +36,20 @@ namespace storm { void traverseNodesElement(xercesc::DOMNode const* const node); void traverseEdgesElement(xercesc::DOMNode const* const node); + void traverseConstantOrTemplateElement(xercesc::DOMNode const* const node); void traversePlaceElement(xercesc::DOMNode const* const node); void traverseTransitionElement(xercesc::DOMNode const* const node); void traverseArcElement(xercesc::DOMNode const* const node); + int64_t parseInt(std::string str); + double parseReal(std::string str); // the constructed gspn storm::gspn::GspnBuilder builder; - + std::shared_ptr manager; + std::shared_ptr expressionParser; + std::unordered_map constantDefinitions; + std::map constantsSubstitution; }; } } diff --git a/src/storm-gspn/parser/GspnParser.cpp b/src/storm-gspn/parser/GspnParser.cpp index b92b6f006..89a2f57e9 100644 --- a/src/storm-gspn/parser/GspnParser.cpp +++ b/src/storm-gspn/parser/GspnParser.cpp @@ -12,7 +12,7 @@ namespace storm { namespace parser { - storm::gspn::GSPN* GspnParser::parse(std::string const& filename) { + storm::gspn::GSPN* GspnParser::parse(std::string const& filename, std::string const& constantDefinitions) { #ifdef STORM_HAVE_XERCES // initialize xercesc try { @@ -62,10 +62,11 @@ namespace storm { xercesc::DOMElement* elementRoot = parser->getDocument()->getDocumentElement(); if (storm::adapters::XMLtoString(elementRoot->getTagName()) == "pnml") { + STORM_LOG_WARN_COND(constantDefinitions == "", "Constant definitions for pnml files are currently not supported."); PnmlParser p; return p.parse(elementRoot); } else if (storm::adapters::XMLtoString(elementRoot->getTagName()) == "project") { - GreatSpnEditorProjectParser p; + GreatSpnEditorProjectParser p(constantDefinitions); return p.parse(elementRoot); } else { // If the top-level node is not a "pnml" or "" node, then throw an exception. diff --git a/src/storm-gspn/parser/GspnParser.h b/src/storm-gspn/parser/GspnParser.h index 862dfc757..512f0ddd6 100644 --- a/src/storm-gspn/parser/GspnParser.h +++ b/src/storm-gspn/parser/GspnParser.h @@ -4,7 +4,7 @@ namespace storm { namespace parser { class GspnParser { public: - static storm::gspn::GSPN* parse(std::string const& filename); + static storm::gspn::GSPN* parse(std::string const& filename, std::string const& constantDefinitions = ""); }; } } diff --git a/src/storm-gspn/settings/modules/GSPNExportSettings.cpp b/src/storm-gspn/settings/modules/GSPNExportSettings.cpp index 5b7653eef..895866cdf 100644 --- a/src/storm-gspn/settings/modules/GSPNExportSettings.cpp +++ b/src/storm-gspn/settings/modules/GSPNExportSettings.cpp @@ -1,5 +1,4 @@ #include "storm-gspn/settings/modules/GSPNExportSettings.h" -#include "storm/settings/modules/JaniExportSettings.h" #include "storm/settings/SettingsManager.h" #include "storm/settings/SettingMemento.h" @@ -20,6 +19,8 @@ namespace storm { const std::string GSPNExportSettings::writeToPnmlOptionName = "to-pnml"; const std::string GSPNExportSettings::writeToPnproOptionName = "to-pnpro"; const std::string GSPNExportSettings::writeToJsonOptionName = "to-json"; + const std::string GSPNExportSettings::writeToJaniOptionName = "to-jani"; + const std::string GSPNExportSettings::addJaniPropertiesOptionName = "addprops"; const std::string GSPNExportSettings::writeStatsOptionName = "to-stats"; const std::string GSPNExportSettings::displayStatsOptionName = "show-stats"; @@ -31,6 +32,8 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, writeToPnmlOptionName, false, "Destination for the pnml output").addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, writeToPnproOptionName, false, "Destination for the pnpro output").addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, writeToJsonOptionName, false, "Destination for the json output").addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, writeToJaniOptionName, false, "Destination for the jani output").addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, addJaniPropertiesOptionName, false, "If set, a set of standard properties is added to the exported jani model.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, writeStatsOptionName, false, "Destination for the stats file").addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, displayStatsOptionName, false, "Print stats to stdout").build()); } @@ -67,6 +70,18 @@ namespace storm { return this->getOption(writeToJsonOptionName).getArgumentByName("filename").getValueAsString(); } + bool GSPNExportSettings::isWriteToJaniSet() const { + return this->getOption(writeToJaniOptionName).getHasOptionBeenSet(); + } + + std::string GSPNExportSettings::getWriteToJaniFilename() const { + return this->getOption(writeToJaniOptionName).getArgumentByName("filename").getValueAsString(); + } + + bool GSPNExportSettings::isAddJaniPropertiesSet() const { + return this->getOption(addJaniPropertiesOptionName).getHasOptionBeenSet(); + } + bool GSPNExportSettings::isDisplayStatsSet() const { return this->getOption(displayStatsOptionName).getHasOptionBeenSet(); } diff --git a/src/storm-gspn/settings/modules/GSPNExportSettings.h b/src/storm-gspn/settings/modules/GSPNExportSettings.h index ce203a841..5ebb3ac5a 100644 --- a/src/storm-gspn/settings/modules/GSPNExportSettings.h +++ b/src/storm-gspn/settings/modules/GSPNExportSettings.h @@ -10,7 +10,7 @@ namespace storm { class GSPNExportSettings : public ModuleSettings { public: /*! - * Creates a new JaniExport setting + * Creates a new GSPNExport setting */ GSPNExportSettings(); @@ -45,6 +45,19 @@ namespace storm { */ std::string getWriteToJsonFilename() const; + + bool isWriteToJaniSet() const; + + /** + * + */ + std::string getWriteToJaniFilename() const; + + /*! + * Returns whether a set of standard properties is to be added when exporting to jani + */ + bool isAddJaniPropertiesSet() const; + bool isDisplayStatsSet() const; bool isWriteStatsToFileSet() const; @@ -62,6 +75,8 @@ namespace storm { static const std::string writeToPnmlOptionName; static const std::string writeToPnproOptionName; static const std::string writeToJsonOptionName; + static const std::string writeToJaniOptionName; + static const std::string addJaniPropertiesOptionName; static const std::string displayStatsOptionName; static const std::string writeStatsOptionName; diff --git a/src/storm-gspn/settings/modules/GSPNSettings.cpp b/src/storm-gspn/settings/modules/GSPNSettings.cpp index 2a0588b3d..b114559a5 100644 --- a/src/storm-gspn/settings/modules/GSPNSettings.cpp +++ b/src/storm-gspn/settings/modules/GSPNSettings.cpp @@ -16,16 +16,19 @@ namespace storm { const std::string GSPNSettings::gspnFileOptionName = "gspnfile"; const std::string GSPNSettings::gspnFileOptionShortName = "gspn"; - const std::string GSPNSettings::gspnToJaniOptionName = "to-jani"; - const std::string GSPNSettings::gspnToJaniOptionShortName = "tj"; const std::string GSPNSettings::capacitiesFileOptionName = "capacitiesfile"; const std::string GSPNSettings::capacitiesFileOptionShortName = "capacities"; + const std::string GSPNSettings::capacityOptionName = "capacity"; + const std::string GSPNSettings::constantsOptionName = "constants"; + const std::string GSPNSettings::constantsOptionShortName = "const"; + GSPNSettings::GSPNSettings() : ModuleSettings(moduleName) { this->addOption(storm::settings::OptionBuilder(moduleName, gspnFileOptionName, false, "Parses the GSPN.").setShortName(gspnFileOptionShortName).addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").addValidatorString(ArgumentValidatorFactory::createExistingFileValidator()).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, gspnToJaniOptionName, false, "Transform to JANI.").setShortName(gspnToJaniOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, capacitiesFileOptionName, false, "Capacaties as invariants for places.").setShortName(capacitiesFileOptionShortName).addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").addValidatorString(ArgumentValidatorFactory::createExistingFileValidator()).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, capacityOptionName, false, "Global capacity as invariants for all places.").addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("value", "capacity").addValidatorUnsignedInteger(ArgumentValidatorFactory::createUnsignedGreaterValidator(0)).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, constantsOptionName, false, "Specifies the constant replacements to use.").setShortName(constantsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createStringArgument("values", "A comma separated list of constants and their value, e.g. a=1,b=2,c=3.").setDefaultValueString("").build()).build()); } bool GSPNSettings::isGspnFileSet() const { @@ -36,10 +39,6 @@ namespace storm { return this->getOption(gspnFileOptionName).getArgumentByName("filename").getValueAsString(); } - bool GSPNSettings::isToJaniSet() const { - return this->getOption(gspnToJaniOptionName).getHasOptionBeenSet(); - } - bool GSPNSettings::isCapacitiesFileSet() const { return this->getOption(capacitiesFileOptionName).getHasOptionBeenSet(); } @@ -48,19 +47,37 @@ namespace storm { return this->getOption(capacitiesFileOptionName).getArgumentByName("filename").getValueAsString(); } + bool GSPNSettings::isCapacitySet() const { + return this->getOption(capacityOptionName).getHasOptionBeenSet(); + } + + uint64_t GSPNSettings::getCapacity() const { + return this->getOption(capacityOptionName).getArgumentByName("value").getValueAsUnsignedInteger(); + } + + bool GSPNSettings::isConstantsSet() const { + return this->getOption(constantsOptionName).getHasOptionBeenSet(); + } + + std::string GSPNSettings::getConstantDefinitionString() const { + return this->getOption(constantsOptionName).getArgumentByName("values").getValueAsString(); + } + void GSPNSettings::finalize() { } bool GSPNSettings::check() const { if(!isGspnFileSet()) { - if(isToJaniSet()) { - return false; - } if(isCapacitiesFileSet()) { return false; } } + + if (isCapacitiesFileSet() && isCapacitySet()) { + STORM_LOG_ERROR("Conflicting settings: Capacity file AND capacity was set."); + return false; + } return true; } } diff --git a/src/storm-gspn/settings/modules/GSPNSettings.h b/src/storm-gspn/settings/modules/GSPNSettings.h index f4ff4ff2c..78586cf9c 100644 --- a/src/storm-gspn/settings/modules/GSPNSettings.h +++ b/src/storm-gspn/settings/modules/GSPNSettings.h @@ -24,11 +24,6 @@ namespace storm { */ std::string getGspnFilename() const; - /** - * Whether the gspn should be transformed to Jani - */ - bool isToJaniSet() const; - /** * Retrievew whether the pgcl file option was set */ @@ -39,6 +34,26 @@ namespace storm { */ std::string getCapacitiesFilename() const; + /** + * Retrievew whether a global capacity was set + */ + bool isCapacitySet() const; + + /** + * Retrieves the global capacity + */ + uint64_t getCapacity() const; + + /*! + * Retrieves whether the constants ption was set. + */ + bool isConstantsSet() const; + + /*! + * Retrieves the string that defines the constants of a gspn + */ + std::string getConstantDefinitionString() const; + bool check() const override; void finalize() override; @@ -48,11 +63,11 @@ namespace storm { private: static const std::string gspnFileOptionName; static const std::string gspnFileOptionShortName; - static const std::string gspnToJaniOptionName; - static const std::string gspnToJaniOptionShortName; static const std::string capacitiesFileOptionName; static const std::string capacitiesFileOptionShortName; - + static const std::string capacityOptionName; + static const std::string constantsOptionName; + static const std::string constantsOptionShortName; }; } } diff --git a/src/storm-gspn/storage/gspn/GSPN.cpp b/src/storm-gspn/storage/gspn/GSPN.cpp index af41a94c2..bac028c27 100644 --- a/src/storm-gspn/storage/gspn/GSPN.cpp +++ b/src/storm-gspn/storage/gspn/GSPN.cpp @@ -26,8 +26,8 @@ namespace storm { return tId; } - GSPN::GSPN(std::string const& name, std::vector const& places, std::vector> const& itransitions, std::vector> const& ttransitions, std::vector const& partitions, std::shared_ptr const& exprManager) - : name(name), places(places), immediateTransitions(itransitions), timedTransitions(ttransitions), partitions(partitions), exprManager(exprManager) + GSPN::GSPN(std::string const& name, std::vector const& places, std::vector> const& itransitions, std::vector> const& ttransitions, std::vector const& partitions, std::shared_ptr const& exprManager, std::map const& constantsSubstitution) + : name(name), places(places), immediateTransitions(itransitions), timedTransitions(ttransitions), partitions(partitions), exprManager(exprManager), constantsSubstitution(constantsSubstitution) { } @@ -134,12 +134,16 @@ namespace storm { return getImmediateTransition(id); } + + std::shared_ptr const& GSPN::getExpressionManager() const { + return exprManager; + } + + std::map const& GSPN::getConstantsSubstitution() const { + return constantsSubstitution; + } - std::shared_ptr const& GSPN::getExpressionManager() const { - return exprManager; - } - - void GSPN::setCapacities(std::unordered_map const& mapping) { + void GSPN::setCapacities(std::unordered_map const& mapping) { for(auto const& entry : mapping) { storm::gspn::Place* place = getPlace(entry.first); STORM_LOG_THROW(place != nullptr, storm::exceptions::InvalidArgumentException, "No place with name " << entry.first); @@ -168,6 +172,7 @@ namespace storm { for (auto& trans : this->getTimedTransitions()) { outStream << "\t" << trans.getName() << " [label=\"" << trans.getName(); outStream << "(" << trans.getRate() << ")\"];" << std::endl; + STORM_LOG_WARN_COND(trans.hasSingleServerSemantics(), "Unable to export non-trivial transition semantics"); // TODO } // print arcs @@ -561,6 +566,7 @@ namespace storm { // add timed transitions for (const auto &trans : timedTransitions) { + STORM_LOG_WARN_COND(trans.hasInfiniteServerSemantics(), "Unable to export non-trivial transition semantics"); // TODO stream << space2 << "" << std::endl; stream << space3 << "" << std::endl; stream << space4 << "" << trans.getRate() << "" << std::endl; diff --git a/src/storm-gspn/storage/gspn/GSPN.h b/src/storm-gspn/storage/gspn/GSPN.h index 7b37ed5cb..343dcd887 100644 --- a/src/storm-gspn/storage/gspn/GSPN.h +++ b/src/storm-gspn/storage/gspn/GSPN.h @@ -32,7 +32,7 @@ namespace storm { GSPN(std::string const& name, std::vector const& places, std::vector> const& itransitions, - std::vector> const& ttransitions, std::vector const& partitions, std::shared_ptr const& exprManager); + std::vector> const& ttransitions, std::vector const& partitions, std::shared_ptr const& exprManager, std::map const& constantsSubstitution = std::map()); /*! * Returns the number of places in this gspn. @@ -145,8 +145,13 @@ namespace storm { */ std::shared_ptr const& getExpressionManager() const; + /*! + * Gets an assignment of occurring constants of the GSPN to their value + */ + std::map const& getConstantsSubstitution() const; + /** - * Set Capacities according to name->capacity map. + * Set Capacities of places according to name->capacity map. */ void setCapacities(std::unordered_map const& mapping); @@ -217,6 +222,8 @@ namespace storm { std::vector partitions; std::shared_ptr exprManager; + + std::map constantsSubstitution; // Layout information mutable std::map placeLayout; diff --git a/src/storm-gspn/storage/gspn/GspnBuilder.cpp b/src/storm-gspn/storage/gspn/GspnBuilder.cpp index e273768ea..c39b5c0f7 100644 --- a/src/storm-gspn/storage/gspn/GspnBuilder.cpp +++ b/src/storm-gspn/storage/gspn/GspnBuilder.cpp @@ -65,13 +65,22 @@ namespace storm { return newId; } - + uint_fast64_t GspnBuilder::addTimedTransition(uint_fast64_t const &priority, double const &rate, std::string const& name) { + return addTimedTransition(priority, rate, 1, name); + } + + uint_fast64_t GspnBuilder::addTimedTransition(uint_fast64_t const &priority, double const &rate, boost::optional numServers, std::string const& name) { auto trans = storm::gspn::TimedTransition(); auto newId = GSPN::timedTransitionIdToTransitionId(timedTransitions.size()); trans.setName(name); trans.setPriority(priority); trans.setRate(rate); + if (numServers) { + trans.setKServerSemantics(numServers.get()); + } else { + trans.setInfiniteServerSemantics(); + } trans.setID(newId); timedTransitions.push_back(trans); @@ -162,8 +171,13 @@ namespace storm { - storm::gspn::GSPN* GspnBuilder::buildGspn() const { - std::shared_ptr exprManager(new storm::expressions::ExpressionManager()); + storm::gspn::GSPN* GspnBuilder::buildGspn(std::shared_ptr const& exprManager, std::map const& constantsSubstitution) const { + std::shared_ptr actualExprManager; + if (exprManager) { + actualExprManager = exprManager; + } else { + actualExprManager = std::make_shared(); + } std::vector orderedPartitions; for(auto const& priorityPartitions : partitions) { @@ -179,10 +193,10 @@ namespace storm { } std::reverse(orderedPartitions.begin(), orderedPartitions.end()); for(auto const& placeEntry : placeNames) { - exprManager->declareIntegerVariable(placeEntry.first, false); + actualExprManager->declareIntegerVariable(placeEntry.first, false); } - GSPN* result = new GSPN(gspnName, places, immediateTransitions, timedTransitions, orderedPartitions, exprManager); + GSPN* result = new GSPN(gspnName, places, immediateTransitions, timedTransitions, orderedPartitions, actualExprManager, constantsSubstitution); result->setTransitionLayoutInfo(transitionLayout); result->setPlaceLayoutInfo(placeLayout); return result; diff --git a/src/storm-gspn/storage/gspn/GspnBuilder.h b/src/storm-gspn/storage/gspn/GspnBuilder.h index a9a99699f..5e4db4a34 100644 --- a/src/storm-gspn/storage/gspn/GspnBuilder.h +++ b/src/storm-gspn/storage/gspn/GspnBuilder.h @@ -39,11 +39,20 @@ namespace storm { /** * Adds an timed transition to the gspn. + * The transition is assumed to have Single-Server-Semantics * @param priority The priority for the transtion. - * @param weight The weight for the transition. + * @param rate The rate for the transition. */ uint_fast64_t addTimedTransition(uint_fast64_t const &priority, RateType const& rate, std::string const& name = ""); + /** + * Adds an timed transition to the gspn. + * @param priority The priority for the transtion. + * @param rate The rate for the transition. + * @param numServers The number of servers this transition has (in case of K-Server semantics) or boost::none (in case of Infinite-Server-Semantics). + */ + uint_fast64_t addTimedTransition(uint_fast64_t const &priority, RateType const& rate, boost::optional numServers, std::string const& name = ""); + void setTransitionLayoutInfo(uint64_t transitionId, LayoutInfo const& layoutInfo); @@ -91,10 +100,10 @@ namespace storm { /** - * + * @param exprManager The expression manager that will be associated with the new gspn. If this is nullptr, a new expressionmanager will be created. * @return The gspn which is constructed by the builder. */ - storm::gspn::GSPN* buildGspn() const; + storm::gspn::GSPN* buildGspn(std::shared_ptr const& exprManager = nullptr, std::map const& constantsSubstitution = std::map()) const; private: bool isImmediateTransitionId(uint64_t) const; bool isTimedTransitionId(uint64_t) const; diff --git a/src/storm-gspn/storage/gspn/GspnJsonExporter.cpp b/src/storm-gspn/storage/gspn/GspnJsonExporter.cpp index 4e451d094..f4e910b81 100644 --- a/src/storm-gspn/storage/gspn/GspnJsonExporter.cpp +++ b/src/storm-gspn/storage/gspn/GspnJsonExporter.cpp @@ -160,7 +160,16 @@ namespace storm { data["name"] = transition.getName(); data["rate"] = transition.getRate(); data["priority"] = transition.getPriority(); - + if (!transition.hasSingleServerSemantics()) { + if (transition.hasInfiniteServerSemantics()) { + data["server-semantics"] = "infinite"; + } else if (transition.hasKServerSemantics()) { + data["server-semantics"] = transition.getNumberOfServers(); + } else { + STORM_LOG_WARN("Unable to export transition semantics."); + } + } + modernjson::json position; position["x"] = x * scaleFactor; position["y"] = y * scaleFactor; diff --git a/src/storm-gspn/storage/gspn/TimedTransition.h b/src/storm-gspn/storage/gspn/TimedTransition.h index d8399319d..37b5476f6 100644 --- a/src/storm-gspn/storage/gspn/TimedTransition.h +++ b/src/storm-gspn/storage/gspn/TimedTransition.h @@ -1,12 +1,18 @@ #pragma once #include "storm-gspn/storage/gspn/Transition.h" +#include "storm/exceptions/InvalidArgumentException.h" namespace storm { namespace gspn { template class TimedTransition : public storm::gspn::Transition { public: + + TimedTransition() { + setSingleServerSemantics(); + } + /*! * Sets the rate of this transition to the given value. * @@ -15,7 +21,43 @@ namespace storm { void setRate(RateType const& rate) { this->rate = rate; } - + + /*! + * Sets the semantics of this transition + */ + void setKServerSemantics(uint64_t k) { + STORM_LOG_THROW(k>0, storm::exceptions::InvalidArgumentException, "Invalid Parameter for server semantics: 0"); + nServers = k; + } + + void setSingleServerSemantics() { + nServers = 1; + } + + void setInfiniteServerSemantics() { + nServers = 0; + } + + /*! + * Retrieves the semantics of this transition + */ + bool hasKServerSemantics() const { + return nServers > 0; + } + + bool hasSingleServerSemantics() const { + return nServers == 1; + } + + bool hasInfiniteServerSemantics() const { + return nServers == 0; + } + + uint64_t getNumberOfServers() const { + STORM_LOG_ASSERT(hasKServerSemantics(), "Tried to get the number of servers of a timed transition although it does not have K-Server-Semantics."); + return nServers; + } + /*! * Retrieves the rate of this transition. * @@ -28,6 +70,9 @@ namespace storm { private: // the rate of the transition RateType rate; + + // the number of servers of this transition. 0 means infinite server semantics. + uint64_t nServers; }; } } \ No newline at end of file diff --git a/src/storm-pars-cli/CMakeLists.txt b/src/storm-pars-cli/CMakeLists.txt index 9e2f6f315..955ee93c8 100644 --- a/src/storm-pars-cli/CMakeLists.txt +++ b/src/storm-pars-cli/CMakeLists.txt @@ -6,4 +6,4 @@ set_target_properties(storm-pars-cli PROPERTIES OUTPUT_NAME "storm-pars") add_dependencies(binaries storm-pars-cli) # installation -install(TARGETS storm-pars-cli RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) +install(TARGETS storm-pars-cli EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-pars-cli/storm-pars.cpp b/src/storm-pars-cli/storm-pars.cpp index 8546f81d1..badcb0212 100644 --- a/src/storm-pars-cli/storm-pars.cpp +++ b/src/storm-pars-cli/storm-pars.cpp @@ -15,6 +15,8 @@ #include "storm/utility/Stopwatch.h" #include "storm/utility/macros.h" +#include "storm-pars/modelchecker/instantiation/SparseCtmcInstantiationModelChecker.h" + #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/settings/modules/IOSettings.h" @@ -29,7 +31,20 @@ namespace storm { typedef typename storm::cli::SymbolicInput SymbolicInput; - + template + struct SampleInformation { + SampleInformation(bool graphPreserving = false, bool exact = false) : graphPreserving(graphPreserving), exact(exact) { + // Intentionally left empty. + } + + bool empty() const { + return cartesianProducts.empty(); + } + + std::vector::type, std::vector::type>>> cartesianProducts; + bool graphPreserving; + bool exact; + }; template std::vector> parseRegions(std::shared_ptr const& model) { @@ -41,6 +56,76 @@ namespace storm { return result; } + template + SampleInformation parseSamples(std::shared_ptr const& model, std::string const& sampleString, bool graphPreserving) { + STORM_LOG_THROW(!model || model->isSparseModel(), storm::exceptions::NotSupportedException, "Sampling is only supported for sparse models."); + + SampleInformation sampleInfo(graphPreserving); + if (sampleString.empty()) { + return sampleInfo; + } + + // Get all parameters from the model. + std::set::type> modelParameters; + auto const& sparseModel = *model->as>(); + modelParameters = storm::models::sparse::getProbabilityParameters(sparseModel); + auto rewParameters = storm::models::sparse::getRewardParameters(sparseModel); + modelParameters.insert(rewParameters.begin(), rewParameters.end()); + + std::vector cartesianProducts; + boost::split(cartesianProducts, sampleString, boost::is_any_of(";")); + for (auto& product : cartesianProducts) { + boost::trim(product); + + // Get the values string for each variable. + std::vector valuesForVariables; + boost::split(valuesForVariables, product, boost::is_any_of(",")); + for (auto& values : valuesForVariables) { + boost::trim(values); + } + + std::set::type> encounteredParameters; + sampleInfo.cartesianProducts.emplace_back(); + auto& newCartesianProduct = sampleInfo.cartesianProducts.back(); + for (auto const& varValues : valuesForVariables) { + auto equalsPosition = varValues.find("="); + STORM_LOG_THROW(equalsPosition != varValues.npos, storm::exceptions::WrongFormatException, "Incorrect format of samples."); + std::string variableName = varValues.substr(0, equalsPosition); + boost::trim(variableName); + std::string values = varValues.substr(equalsPosition + 1); + boost::trim(values); + + bool foundParameter = false; + typename utility::parametric::VariableType::type theParameter; + for (auto const& parameter : modelParameters) { + std::stringstream parameterStream; + parameterStream << parameter; + if (parameterStream.str() == variableName) { + foundParameter = true; + theParameter = parameter; + encounteredParameters.insert(parameter); + } + } + STORM_LOG_THROW(foundParameter, storm::exceptions::WrongFormatException, "Unknown parameter '" << variableName << "'."); + + std::vector splitValues; + boost::split(splitValues, values, boost::is_any_of(":")); + STORM_LOG_THROW(!splitValues.empty(), storm::exceptions::WrongFormatException, "Expecting at least one value per parameter."); + + auto& list = newCartesianProduct[theParameter]; + + for (auto& value : splitValues) { + boost::trim(value); + list.push_back(storm::utility::convertNumber::type>(value)); + } + } + + STORM_LOG_THROW(encounteredParameters == modelParameters, storm::exceptions::WrongFormatException, "Variables for all parameters are required when providing samples."); + } + + return sampleInfo; + } + template std::pair, bool> preprocessSparseModel(std::shared_ptr> const& model, SymbolicInput const& input) { auto generalSettings = storm::settings::getModule(); @@ -106,9 +191,24 @@ namespace storm { } template - void printInitialStatesResult(std::unique_ptr const& result, storm::jani::Property const& property, storm::utility::Stopwatch* watch = nullptr) { + void printInitialStatesResult(std::unique_ptr const& result, storm::jani::Property const& property, storm::utility::Stopwatch* watch = nullptr, storm::utility::parametric::Valuation const* valuation = nullptr) { if (result) { - STORM_PRINT_AND_LOG("Result (initial states): " << std::endl); + STORM_PRINT_AND_LOG("Result (initial states)"); + if (valuation) { + bool first = true; + std::stringstream ss; + for (auto const& entry : *valuation) { + if (!first) { + ss << ", "; + } else { + first = false; + } + ss << entry.first << "=" << entry.second; + } + + STORM_PRINT_AND_LOG(" for instance [" << ss.str() << "]"); + } + STORM_PRINT_AND_LOG(": ") auto const* regionCheckResult = dynamic_cast const*>(result.get()); if (regionCheckResult != nullptr) { @@ -132,7 +232,7 @@ namespace storm { STORM_PRINT_AND_LOG(*result << std::endl); } if (watch) { - STORM_PRINT_AND_LOG("Time for model checking: " << *watch << "." << std::endl); + STORM_PRINT_AND_LOG("Time for model checking: " << *watch << "." << std::endl << std::endl); } } else { STORM_PRINT_AND_LOG(" failed, property is unsupported by selected engine/settings." << std::endl); @@ -151,24 +251,118 @@ namespace storm { } } + template class ModelCheckerType, typename ModelType, typename ValueType, typename SolveValueType = double> + void verifyPropertiesAtSamplePoints(ModelType const& model, SymbolicInput const& input, SampleInformation const& samples) { + + // When samples are provided, we create an instantiation model checker. + ModelCheckerType modelchecker(model); + + for (auto const& property : input.properties) { + storm::cli::printModelCheckingProperty(property); + + modelchecker.specifyFormula(storm::api::createTask(property.getRawFormula(), true)); + modelchecker.setInstantiationsAreGraphPreserving(samples.graphPreserving); + + storm::utility::parametric::Valuation valuation; + + std::vector::type> parameters; + std::vector::type>::const_iterator> iterators; + std::vector::type>::const_iterator> iteratorEnds; + + storm::utility::Stopwatch watch(true); + for (auto const& product : samples.cartesianProducts) { + parameters.clear(); + iterators.clear(); + iteratorEnds.clear(); + + for (auto const& entry : product) { + parameters.push_back(entry.first); + iterators.push_back(entry.second.cbegin()); + iteratorEnds.push_back(entry.second.cend()); + } + + bool done = false; + while (!done) { + // Read off valuation. + for (uint64_t i = 0; i < parameters.size(); ++i) { + valuation[parameters[i]] = *iterators[i]; + } + + storm::utility::Stopwatch valuationWatch(true); + std::unique_ptr result = modelchecker.check(Environment(), valuation); + valuationWatch.stop(); + + if (result) { + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model.getInitialStates())); + } + printInitialStatesResult(result, property, &valuationWatch, &valuation); + + for (uint64_t i = 0; i < parameters.size(); ++i) { + ++iterators[i]; + if (iterators[i] == iteratorEnds[i]) { + // Reset iterator and proceed to move next iterator. + iterators[i] = product.at(parameters[i]).cbegin(); + + // If the last iterator was removed, we are done. + if (i == parameters.size() - 1) { + done = true; + } + } else { + // If an iterator was moved but not reset, we have another valuation to check. + break; + } + } + + } + } + + watch.stop(); + STORM_PRINT_AND_LOG("Overall time for sampling all instances: " << watch << std::endl << std::endl); + } + } + + template + void verifyPropertiesAtSamplePoints(std::shared_ptr> const& model, SymbolicInput const& input, SampleInformation const& samples) { + if (model->isOfType(storm::models::ModelType::Dtmc)) { + verifyPropertiesAtSamplePoints, ValueType, SolveValueType>(*model->template as>(), input, samples); + } else if (model->isOfType(storm::models::ModelType::Ctmc)) { + verifyPropertiesAtSamplePoints, ValueType, SolveValueType>(*model->template as>(), input, samples); + } else if (model->isOfType(storm::models::ModelType::Ctmc)) { + verifyPropertiesAtSamplePoints, ValueType, SolveValueType>(*model->template as>(), input, samples); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Sampling is currently only supported for DTMCs, CTMCs and MDPs."); + } + } + template - void verifyPropertiesWithSparseEngine(std::shared_ptr> const& model, SymbolicInput const& input) { - verifyProperties(input.properties, - [&model] (std::shared_ptr const& formula) { - std::unique_ptr result = storm::api::verifyWithSparseEngine(model, storm::api::createTask(formula, true)); - if (result) { - result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); - } - return result; - }, - [&model] (std::unique_ptr const& result) { - auto parametricSettings = storm::settings::getModule(); - if (parametricSettings.exportResultToFile() && model->isOfType(storm::models::ModelType::Dtmc)) { - auto dtmc = model->template as>(); - boost::optional rationalFunction = result->asExplicitQuantitativeCheckResult()[*model->getInitialStates().begin()]; - storm::api::exportParametricResultToFile(rationalFunction, storm::analysis::ConstraintCollector(*dtmc), parametricSettings.exportResultPath()); - } - }); + void verifyPropertiesWithSparseEngine(std::shared_ptr> const& model, SymbolicInput const& input, SampleInformation const& samples) { + + if (samples.empty()) { + verifyProperties(input.properties, + [&model] (std::shared_ptr const& formula) { + std::unique_ptr result = storm::api::verifyWithSparseEngine(model, storm::api::createTask(formula, true)); + if (result) { + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); + } + return result; + }, + [&model] (std::unique_ptr const& result) { + auto parametricSettings = storm::settings::getModule(); + if (parametricSettings.exportResultToFile() && model->isOfType(storm::models::ModelType::Dtmc)) { + auto dtmc = model->template as>(); + boost::optional rationalFunction = result->asExplicitQuantitativeCheckResult()[*model->getInitialStates().begin()]; + storm::api::exportParametricResultToFile(rationalFunction, storm::analysis::ConstraintCollector(*dtmc), parametricSettings.exportResultPath()); + } + }); + } else { + STORM_LOG_TRACE("Sampling the model at given points."); + + if (samples.exact) { + verifyPropertiesAtSamplePoints(model, input, samples); + } else { + verifyPropertiesAtSamplePoints(model, input, samples); + } + } } template @@ -226,18 +420,18 @@ namespace storm { } template - void verifyWithSparseEngine(std::shared_ptr> const& model, SymbolicInput const& input, std::vector> const& regions) { + void verifyWithSparseEngine(std::shared_ptr> const& model, SymbolicInput const& input, std::vector> const& regions, SampleInformation const& samples) { if (regions.empty()) { - storm::pars::verifyPropertiesWithSparseEngine(model, input); + storm::pars::verifyPropertiesWithSparseEngine(model, input, samples); } else { storm::pars::verifyRegionsWithSparseEngine(model, input, regions); } } template - void verifyParametricModel(std::shared_ptr const& model, SymbolicInput const& input, std::vector> const& regions) { + void verifyParametricModel(std::shared_ptr const& model, SymbolicInput const& input, std::vector> const& regions, SampleInformation const& samples) { STORM_LOG_ASSERT(model->isSparseModel(), "Unexpected model type."); - storm::pars::verifyWithSparseEngine(model->as>(), input, regions); + storm::pars::verifyWithSparseEngine(model->as>(), input, regions, samples); } template @@ -271,9 +465,13 @@ namespace storm { } std::vector> regions = parseRegions(model); - - - + std::string samplesAsString = parSettings.getSamples(); + SampleInformation samples; + if (!samplesAsString.empty()) { + samples = parseSamples(model, samplesAsString, parSettings.isSamplesAreGraphPreservingSet()); + samples.exact = parSettings.isSampleExactSet(); + } + if (model) { storm::cli::exportModel(model, input); } @@ -285,7 +483,7 @@ namespace storm { } if (model) { - verifyParametricModel(model, input, regions); + verifyParametricModel(model, input, regions, samples); } } diff --git a/src/storm-pars/CMakeLists.txt b/src/storm-pars/CMakeLists.txt index 4e2ec1e0f..b91292be4 100644 --- a/src/storm-pars/CMakeLists.txt +++ b/src/storm-pars/CMakeLists.txt @@ -36,5 +36,5 @@ add_custom_target(copy_storm_pars_headers DEPENDS ${STORM_PARS_OUTPUT_HEADERS} $ add_dependencies(storm-pars copy_storm_pars_headers) # installation -install(TARGETS storm-pars RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) +install(TARGETS storm-pars EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-pars/modelchecker/instantiation/SparseCtmcInstantiationModelChecker.cpp b/src/storm-pars/modelchecker/instantiation/SparseCtmcInstantiationModelChecker.cpp new file mode 100644 index 000000000..4af65eb19 --- /dev/null +++ b/src/storm-pars/modelchecker/instantiation/SparseCtmcInstantiationModelChecker.cpp @@ -0,0 +1,27 @@ +#include "storm-pars/modelchecker/instantiation/SparseCtmcInstantiationModelChecker.h" + +#include "storm/modelchecker/csl/SparseCtmcCslModelChecker.h" + +#include "storm/exceptions/InvalidStateException.h" + +namespace storm { + namespace modelchecker { + + template + SparseCtmcInstantiationModelChecker::SparseCtmcInstantiationModelChecker(SparseModelType const& parametricModel) : SparseInstantiationModelChecker(parametricModel), modelInstantiator(parametricModel) { + //Intentionally left empty + } + + template + std::unique_ptr SparseCtmcInstantiationModelChecker::check(Environment const& env, storm::utility::parametric::Valuation const& valuation) { + STORM_LOG_THROW(this->currentCheckTask, storm::exceptions::InvalidStateException, "Checking has been invoked but no property has been specified before."); + auto const& instantiatedModel = modelInstantiator.instantiate(valuation); + storm::modelchecker::SparseCtmcCslModelChecker> modelChecker(instantiatedModel); + + return modelChecker.check(env, *this->currentCheckTask); + } + + template class SparseCtmcInstantiationModelChecker, double>; + template class SparseCtmcInstantiationModelChecker, storm::RationalNumber>; + } +} diff --git a/src/storm-pars/modelchecker/instantiation/SparseCtmcInstantiationModelChecker.h b/src/storm-pars/modelchecker/instantiation/SparseCtmcInstantiationModelChecker.h new file mode 100644 index 000000000..4e4b7643f --- /dev/null +++ b/src/storm-pars/modelchecker/instantiation/SparseCtmcInstantiationModelChecker.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "storm-pars/modelchecker/instantiation/SparseInstantiationModelChecker.h" +#include "storm-pars/utility/ModelInstantiator.h" +#include "storm/models/sparse/Dtmc.h" +#include "storm/models/sparse/StandardRewardModel.h" +#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h" + +namespace storm { + namespace modelchecker { + + /*! + * Class to efficiently check a formula on a parametric model with different parameter instantiations. + */ + template + class SparseCtmcInstantiationModelChecker : public SparseInstantiationModelChecker { + public: + SparseCtmcInstantiationModelChecker(SparseModelType const& parametricModel); + + virtual std::unique_ptr check(Environment const& env, storm::utility::parametric::Valuation const& valuation) override; + + storm::utility::ModelInstantiator> modelInstantiator; + }; + } +} diff --git a/src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp b/src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp index d13f3bf44..5de3d8fd3 100644 --- a/src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp +++ b/src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp @@ -20,11 +20,11 @@ namespace storm { std::unique_ptr SparseDtmcInstantiationModelChecker::check(Environment const& env, storm::utility::parametric::Valuation const& valuation) { STORM_LOG_THROW(this->currentCheckTask, storm::exceptions::InvalidStateException, "Checking has been invoked but no property has been specified before."); auto const& instantiatedModel = modelInstantiator.instantiate(valuation); - STORM_LOG_ASSERT(instantiatedModel.getTransitionMatrix().isProbabilistic(), "Instantiated matrix is not probabilistic!"); + STORM_LOG_THROW(instantiatedModel.getTransitionMatrix().isProbabilistic(), storm::exceptions::InvalidArgumentException, "Instantiation point is invalid as the transition matrix becomes non-stochastic."); storm::modelchecker::SparseDtmcPrctlModelChecker> modelChecker(instantiatedModel); // Check if there are some optimizations implemented for the specified property - if(this->currentCheckTask->getFormula().isInFragment(storm::logic::reachability())) { + if (this->currentCheckTask->getFormula().isInFragment(storm::logic::reachability())) { return checkReachabilityProbabilityFormula(env, modelChecker); } else if (this->currentCheckTask->getFormula().isInFragment(storm::logic::propositional().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setOperatorAtTopLevelRequired(true).setNestedOperatorsAllowed(false))) { return checkReachabilityRewardFormula(env, modelChecker); @@ -144,4 +144,4 @@ namespace storm { template class SparseDtmcInstantiationModelChecker, storm::RationalNumber>; } -} \ No newline at end of file +} diff --git a/src/storm-pars/modelchecker/instantiation/SparseInstantiationModelChecker.cpp b/src/storm-pars/modelchecker/instantiation/SparseInstantiationModelChecker.cpp index d81d5b9a6..7d5143a50 100644 --- a/src/storm-pars/modelchecker/instantiation/SparseInstantiationModelChecker.cpp +++ b/src/storm-pars/modelchecker/instantiation/SparseInstantiationModelChecker.cpp @@ -2,6 +2,7 @@ #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/models/sparse/Dtmc.h" +#include "storm/models/sparse/Ctmc.h" #include "storm/models/sparse/Mdp.h" #include "storm/models/sparse/StandardRewardModel.h" @@ -12,10 +13,9 @@ namespace storm { template SparseInstantiationModelChecker::SparseInstantiationModelChecker(SparseModelType const& parametricModel) : parametricModel(parametricModel), instantiationsAreGraphPreserving(false) { - //Intentionally left empty + // Intentionally left empty } - template void SparseInstantiationModelChecker::specifyFormula(storm::modelchecker::CheckTask const& checkTask) { currentFormula = checkTask.getFormula().asSharedPointer(); @@ -33,9 +33,11 @@ namespace storm { } template class SparseInstantiationModelChecker, double>; + template class SparseInstantiationModelChecker, double>; template class SparseInstantiationModelChecker, double>; template class SparseInstantiationModelChecker, storm::RationalNumber>; + template class SparseInstantiationModelChecker, storm::RationalNumber>; template class SparseInstantiationModelChecker, storm::RationalNumber>; } diff --git a/src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp b/src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp index a2a6c0094..3dc4866d3 100644 --- a/src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp +++ b/src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp @@ -23,7 +23,7 @@ namespace storm { std::unique_ptr SparseMdpInstantiationModelChecker::check(Environment const& env, storm::utility::parametric::Valuation const& valuation) { STORM_LOG_THROW(this->currentCheckTask, storm::exceptions::InvalidStateException, "Checking has been invoked but no property has been specified before."); auto const& instantiatedModel = modelInstantiator.instantiate(valuation); - STORM_LOG_ASSERT(instantiatedModel.getTransitionMatrix().isProbabilistic(), "Instantiated matrix is not probabilistic!"); + STORM_LOG_THROW(instantiatedModel.getTransitionMatrix().isProbabilistic(), storm::exceptions::InvalidArgumentException, "Instantiation point is invalid as the transition matrix becomes non-stochastic."); storm::modelchecker::SparseMdpPrctlModelChecker> modelChecker(instantiatedModel); // Check if there are some optimizations implemented for the specified property diff --git a/src/storm-pars/settings/ParsSettings.cpp b/src/storm-pars/settings/ParsSettings.cpp index a0e9d8073..d819f8e24 100644 --- a/src/storm-pars/settings/ParsSettings.cpp +++ b/src/storm-pars/settings/ParsSettings.cpp @@ -1,4 +1,3 @@ -#include #include "storm-pars/settings/ParsSettings.h" #include "storm-pars/settings/modules/ParametricSettings.h" @@ -21,7 +20,6 @@ #include "storm/settings/modules/GameSolverSettings.h" #include "storm/settings/modules/BisimulationSettings.h" #include "storm/settings/modules/ResourceSettings.h" -#include "storm/settings/modules/JaniExportSettings.h" #include "storm/settings/modules/JitBuilderSettings.h" #include "storm/settings/modules/MultiplierSettings.h" @@ -38,7 +36,6 @@ namespace storm { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); - storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); @@ -51,7 +48,6 @@ namespace storm { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); - storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); } diff --git a/src/storm-pars/settings/modules/ParametricSettings.cpp b/src/storm-pars/settings/modules/ParametricSettings.cpp index 28975aa5c..7ecbd6166 100644 --- a/src/storm-pars/settings/modules/ParametricSettings.cpp +++ b/src/storm-pars/settings/modules/ParametricSettings.cpp @@ -18,13 +18,20 @@ namespace storm { const std::string ParametricSettings::transformContinuousOptionName = "transformcontinuous"; const std::string ParametricSettings::transformContinuousShortOptionName = "tc"; const std::string ParametricSettings::onlyWellformednessConstraintsOptionName = "onlyconstraints"; - + const std::string ParametricSettings::samplesOptionName = "samples"; + const std::string ParametricSettings::samplesGraphPreservingOptionName = "samples-graph-preserving"; + const std::string ParametricSettings::sampleExactOptionName = "sample-exact"; + ParametricSettings::ParametricSettings() : ModuleSettings(moduleName) { this->addOption(storm::settings::OptionBuilder(moduleName, exportResultOptionName, false, "A path to a file where the parametric result should be saved.") .addArgument(storm::settings::ArgumentBuilder::createStringArgument("path", "the location.").addValidatorString(ArgumentValidatorFactory::createWritableFileValidator()).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, derivativesOptionName, false, "Sets whether to generate the derivatives of the resulting rational function.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, transformContinuousOptionName, false, "Sets whether to transform a continuous time input model to a discrete time model.").setShortName(transformContinuousShortOptionName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, onlyWellformednessConstraintsOptionName, false, "Sets whether you only want to obtain the wellformedness constraints").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, samplesOptionName, false, "The points at which to sample the model.") + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("samples", "The samples are semicolon-separated entries of the form 'Var1=Val1:Val2:...:Valk,Var2=... that span the sample spaces.").setDefaultValueString("").build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, samplesGraphPreservingOptionName, false, "Sets whether it can be assumed that the samples are graph-preserving.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, sampleExactOptionName, false, "Sets whether to sample using exact arithmetic.").build()); } bool ParametricSettings::exportResultToFile() const { @@ -46,7 +53,19 @@ namespace storm { bool ParametricSettings::onlyObtainConstraints() const { return this->getOption(onlyWellformednessConstraintsOptionName).getHasOptionBeenSet(); } - + + std::string ParametricSettings::getSamples() const { + return this->getOption(samplesOptionName).getArgumentByName("samples").getValueAsString(); + } + + bool ParametricSettings::isSamplesAreGraphPreservingSet() const { + return this->getOption(samplesGraphPreservingOptionName).getHasOptionBeenSet(); + } + + bool ParametricSettings::isSampleExactSet() const { + return this->getOption(sampleExactOptionName).getHasOptionBeenSet(); + } + } // namespace modules } // namespace settings } // namespace storm diff --git a/src/storm-pars/settings/modules/ParametricSettings.h b/src/storm-pars/settings/modules/ParametricSettings.h index e610f033d..1f10a4b35 100644 --- a/src/storm-pars/settings/modules/ParametricSettings.h +++ b/src/storm-pars/settings/modules/ParametricSettings.h @@ -47,6 +47,23 @@ namespace storm { */ bool onlyObtainConstraints() const; + /*! + * Retrieves the samples as a comma-separated list of samples for each (relevant) variable, where the + * samples are colon-separated values. For example, 'N=1:2:3,K=2:4' means that N takes the values 1 to + * 3 and K either 2 or 4. + */ + std::string getSamples() const; + + /*! + * Retrieves whether the samples are graph preserving. + */ + bool isSamplesAreGraphPreservingSet() const; + + /*! + * Retrieves whether samples are to be computed exactly. + */ + bool isSampleExactSet() const; + const static std::string moduleName; private: @@ -55,6 +72,9 @@ namespace storm { const static std::string transformContinuousOptionName; const static std::string transformContinuousShortOptionName; const static std::string onlyWellformednessConstraintsOptionName; + const static std::string samplesOptionName; + const static std::string samplesGraphPreservingOptionName; + const static std::string sampleExactOptionName; }; } // namespace modules diff --git a/src/storm-parsers/CMakeLists.txt b/src/storm-parsers/CMakeLists.txt new file mode 100644 index 000000000..24d9532b5 --- /dev/null +++ b/src/storm-parsers/CMakeLists.txt @@ -0,0 +1,42 @@ +file(GLOB_RECURSE ALL_FILES ${PROJECT_SOURCE_DIR}/src/storm-parsers/*.h ${PROJECT_SOURCE_DIR}/src/storm-parsers/*.cpp) + +register_source_groups_from_filestructure("${ALL_FILES}" storm-parsers) + + + +file(GLOB_RECURSE STORM_PARSER_SOURCES ${PROJECT_SOURCE_DIR}/src/storm-parsers/*/*.cpp) +file(GLOB_RECURSE STORM_PARSER_HEADERS ${PROJECT_SOURCE_DIR}/src/storm-parsers/*/*.h) + + +# Disable Debug compiler flags for PrismParser to lessen memory consumption during compilation +SET_SOURCE_FILES_PROPERTIES(${PROJECT_SOURCE_DIR}/src/storm-parsers/parser/PrismParser.cpp PROPERTIES COMPILE_FLAGS -g0) +# Create storm-parsers. +add_library(storm-parsers SHARED ${STORM_PARSER_SOURCES} ${STORM_PARSER_HEADERS}) + +# Remove define symbol for shared libstorm. +set_target_properties(storm-parsers PROPERTIES DEFINE_SYMBOL "") +#add_dependencies(storm resources) +list(APPEND STORM_TARGETS storm-parsers) +set(STORM_TARGETS ${STORM_TARGETS} PARENT_SCOPE) + +target_link_libraries(storm-parsers PUBLIC storm) + +# Install storm headers to include directory. +foreach(HEADER ${STORM_PARSER_HEADERS}) + string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/src/?" "" RELATIVE_HEADER_PATH ${HEADER}) + string(REGEX MATCH "(.*)[/\\]" RELATIVE_DIRECTORY ${RELATIVE_HEADER_PATH}) + string(REGEX REPLACE "${RELATIVE_DIRECTORY}/?" "" HEADER_FILENAME ${RELATIVE_HEADER_PATH}) + add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${HEADER} ${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME} + DEPENDS ${HEADER} + ) + list(APPEND STORM_PARSER_OUTPUT_HEADERS "${CMAKE_BINARY_DIR}/include/${RELATIVE_DIRECTORY}${HEADER_FILENAME}") +endforeach() +add_custom_target(copy_storm_parser_headers DEPENDS ${STORM_PARSER_OUTPUT_HEADERS} ${STORM_PARSER_HEADERS}) +add_dependencies(storm-parsers copy_storm_parser_headers) + +# installation +install(TARGETS storm-parsers EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) + diff --git a/src/storm/api/model_descriptions.cpp b/src/storm-parsers/api/model_descriptions.cpp similarity index 88% rename from src/storm/api/model_descriptions.cpp rename to src/storm-parsers/api/model_descriptions.cpp index 76ce24116..a440dffef 100644 --- a/src/storm/api/model_descriptions.cpp +++ b/src/storm-parsers/api/model_descriptions.cpp @@ -1,7 +1,7 @@ -#include "storm/api/model_descriptions.h" +#include "model_descriptions.h" -#include "storm/parser/PrismParser.h" -#include "storm/parser/JaniParser.h" +#include "storm-parsers/parser/PrismParser.h" +#include "storm-parsers/parser/JaniParser.h" #include "storm/storage/jani/Model.h" #include "storm/storage/jani/Property.h" diff --git a/src/storm/api/model_descriptions.h b/src/storm-parsers/api/model_descriptions.h similarity index 100% rename from src/storm/api/model_descriptions.h rename to src/storm-parsers/api/model_descriptions.h diff --git a/src/storm-parsers/api/properties.cpp b/src/storm-parsers/api/properties.cpp new file mode 100644 index 000000000..425fee410 --- /dev/null +++ b/src/storm-parsers/api/properties.cpp @@ -0,0 +1,71 @@ +#include "storm-parsers/api/properties.h" + +#include "storm-parsers/parser/FormulaParser.h" +#include "storm/api/properties.h" + + +#include "storm/storage/SymbolicModelDescription.h" +#include "storm/storage/prism/Program.h" +#include "storm/storage/jani/Model.h" +#include "storm/storage/jani/Property.h" + +#include "storm/logic/Formula.h" + +#include "storm/utility/cli.h" + + +namespace storm { + namespace api { + + boost::optional > parsePropertyFilter(std::string const &propertyFilter) { + if (propertyFilter == "all") { + return boost::none; + } + std::vector propertyNames = storm::utility::cli::parseCommaSeparatedStrings(propertyFilter); + std::set propertyNameSet(propertyNames.begin(), propertyNames.end()); + return propertyNameSet; + } + + std::vector parseProperties(storm::parser::FormulaParser &formulaParser, std::string const &inputString, boost::optional > const &propertyFilter) { + // If the given property is a file, we parse it as a file, otherwise we assume it's a property. + std::vector properties; + if (std::ifstream(inputString).good()) { + STORM_LOG_INFO("Loading properties from file: " << inputString << std::endl); + properties = formulaParser.parseFromFile(inputString); + } else { + properties = formulaParser.parseFromString(inputString); + } + + return filterProperties(properties, propertyFilter); + } + + std::vector parseProperties(std::string const &inputString, boost::optional > const &propertyFilter) { + auto exprManager = std::make_shared(); + storm::parser::FormulaParser formulaParser(exprManager); + return parseProperties(formulaParser, inputString, propertyFilter); + } + + std::vector parsePropertiesForJaniModel(std::string const &inputString, storm::jani::Model const &model, boost::optional > const &propertyFilter) { + storm::parser::FormulaParser formulaParser(model.getManager().getSharedPointer()); + auto formulas = parseProperties(formulaParser, inputString, propertyFilter); + return substituteConstantsInProperties(formulas, model.getConstantsSubstitution()); + } + + std::vector parsePropertiesForPrismProgram(std::string const &inputString, storm::prism::Program const &program, boost::optional > const &propertyFilter) { + storm::parser::FormulaParser formulaParser(program); + auto formulas = parseProperties(formulaParser, inputString, propertyFilter); + return substituteConstantsInProperties(formulas, program.getConstantsSubstitution()); + } + + std::vector parsePropertiesForSymbolicModelDescription(std::string const &inputString, storm::storage::SymbolicModelDescription const &modelDescription, boost::optional > const &propertyFilter) { + std::vector result; + if (modelDescription.isPrismProgram()) { + result = storm::api::parsePropertiesForPrismProgram(inputString, modelDescription.asPrismProgram(), propertyFilter); + } else { + STORM_LOG_ASSERT(modelDescription.isJaniModel(), "Unknown model description type."); + result = storm::api::parsePropertiesForJaniModel(inputString, modelDescription.asJaniModel(), propertyFilter); + } + return result; + } + } +} \ No newline at end of file diff --git a/src/storm-parsers/api/properties.h b/src/storm-parsers/api/properties.h new file mode 100644 index 000000000..81d37d965 --- /dev/null +++ b/src/storm-parsers/api/properties.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace storm { + namespace parser { + class FormulaParser; + } + namespace jani { + class Property; + class Model; + } + namespace expressions { + class Variable; + class Expression; + } + namespace prism { + class Program; + } + namespace storage { + class SymbolicModelDescription; + } + namespace logic { + class Formula; + } + + namespace api { + boost::optional> parsePropertyFilter(std::string const& propertyFilter); + + // Parsing properties. + std::vector parseProperties(storm::parser::FormulaParser& formulaParser, std::string const& inputString, boost::optional> const& propertyFilter = boost::none); + std::vector parseProperties(std::string const& inputString, boost::optional> const& propertyFilter = boost::none); + std::vector parsePropertiesForPrismProgram(std::string const& inputString, storm::prism::Program const& program, boost::optional> const& propertyFilter = boost::none); + std::vector parsePropertiesForJaniModel(std::string const& inputString, storm::jani::Model const& model, boost::optional> const& propertyFilter = boost::none); + std::vector parsePropertiesForSymbolicModelDescription(std::string const& inputString, storm::storage::SymbolicModelDescription const& modelDescription, boost::optional> const& propertyFilter = boost::none); + + } +} diff --git a/src/storm-parsers/api/storm-parsers.h b/src/storm-parsers/api/storm-parsers.h new file mode 100644 index 000000000..b88188976 --- /dev/null +++ b/src/storm-parsers/api/storm-parsers.h @@ -0,0 +1,4 @@ +#pragma once + +#include "storm-parsers/api/model_descriptions.h" +#include "storm-parsers/api/properties.h" \ No newline at end of file diff --git a/src/storm/parser/AtomicPropositionLabelingParser.cpp b/src/storm-parsers/parser/AtomicPropositionLabelingParser.cpp similarity index 98% rename from src/storm/parser/AtomicPropositionLabelingParser.cpp rename to src/storm-parsers/parser/AtomicPropositionLabelingParser.cpp index 4d55b4fe8..a8afc93d7 100644 --- a/src/storm/parser/AtomicPropositionLabelingParser.cpp +++ b/src/storm-parsers/parser/AtomicPropositionLabelingParser.cpp @@ -5,14 +5,14 @@ * Author: Gereon Kremer */ -#include "storm/parser/AtomicPropositionLabelingParser.h" +#include "storm-parsers/parser/AtomicPropositionLabelingParser.h" #include #include #include #include "storm/utility/cstring.h" -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/exceptions/FileIoException.h" diff --git a/src/storm/parser/AtomicPropositionLabelingParser.h b/src/storm-parsers/parser/AtomicPropositionLabelingParser.h similarity index 100% rename from src/storm/parser/AtomicPropositionLabelingParser.h rename to src/storm-parsers/parser/AtomicPropositionLabelingParser.h diff --git a/src/storm/parser/AutoParser.cpp b/src/storm-parsers/parser/AutoParser.cpp similarity index 95% rename from src/storm/parser/AutoParser.cpp rename to src/storm-parsers/parser/AutoParser.cpp index 896c4215e..9e96af5e6 100644 --- a/src/storm/parser/AutoParser.cpp +++ b/src/storm-parsers/parser/AutoParser.cpp @@ -1,12 +1,12 @@ -#include "storm/parser/AutoParser.h" +#include "storm-parsers/parser/AutoParser.h" #include "storm/models/sparse/StandardRewardModel.h" -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" -#include "storm/parser/DeterministicModelParser.h" -#include "storm/parser/NondeterministicModelParser.h" -#include "storm/parser/MarkovAutomatonParser.h" +#include "storm-parsers/parser/DeterministicModelParser.h" +#include "storm-parsers/parser/NondeterministicModelParser.h" +#include "storm-parsers/parser/MarkovAutomatonParser.h" #include "storm/utility/macros.h" #include "storm/exceptions/WrongFormatException.h" diff --git a/src/storm/parser/AutoParser.h b/src/storm-parsers/parser/AutoParser.h similarity index 100% rename from src/storm/parser/AutoParser.h rename to src/storm-parsers/parser/AutoParser.h diff --git a/src/storm/parser/DeterministicModelParser.cpp b/src/storm-parsers/parser/DeterministicModelParser.cpp similarity index 95% rename from src/storm/parser/DeterministicModelParser.cpp rename to src/storm-parsers/parser/DeterministicModelParser.cpp index cfe5a16df..6b81c0630 100644 --- a/src/storm/parser/DeterministicModelParser.cpp +++ b/src/storm-parsers/parser/DeterministicModelParser.cpp @@ -1,13 +1,13 @@ -#include "storm/parser/DeterministicModelParser.h" +#include "storm-parsers/parser/DeterministicModelParser.h" #include #include #include "storm/models/sparse/StandardRewardModel.h" -#include "storm/parser/DeterministicSparseTransitionParser.h" -#include "storm/parser/SparseItemLabelingParser.h" -#include "storm/parser/SparseStateRewardParser.h" +#include "storm-parsers/parser/DeterministicSparseTransitionParser.h" +#include "storm-parsers/parser/SparseItemLabelingParser.h" +#include "storm-parsers/parser/SparseStateRewardParser.h" #include "storm/adapters/RationalFunctionAdapter.h" diff --git a/src/storm/parser/DeterministicModelParser.h b/src/storm-parsers/parser/DeterministicModelParser.h similarity index 100% rename from src/storm/parser/DeterministicModelParser.h rename to src/storm-parsers/parser/DeterministicModelParser.h diff --git a/src/storm/parser/DeterministicSparseTransitionParser.cpp b/src/storm-parsers/parser/DeterministicSparseTransitionParser.cpp similarity index 99% rename from src/storm/parser/DeterministicSparseTransitionParser.cpp rename to src/storm-parsers/parser/DeterministicSparseTransitionParser.cpp index 540371f74..229cc3c2e 100644 --- a/src/storm/parser/DeterministicSparseTransitionParser.cpp +++ b/src/storm-parsers/parser/DeterministicSparseTransitionParser.cpp @@ -1,4 +1,4 @@ -#include "storm/parser/DeterministicSparseTransitionParser.h" +#include "storm-parsers/parser/DeterministicSparseTransitionParser.h" #include #include @@ -9,7 +9,7 @@ #include "storm/utility/constants.h" #include "storm/utility/cstring.h" -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" #include "storm/exceptions/FileIoException.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/exceptions/InvalidArgumentException.h" diff --git a/src/storm/parser/DeterministicSparseTransitionParser.h b/src/storm-parsers/parser/DeterministicSparseTransitionParser.h similarity index 100% rename from src/storm/parser/DeterministicSparseTransitionParser.h rename to src/storm-parsers/parser/DeterministicSparseTransitionParser.h diff --git a/src/storm/parser/DirectEncodingParser.cpp b/src/storm-parsers/parser/DirectEncodingParser.cpp similarity index 99% rename from src/storm/parser/DirectEncodingParser.cpp rename to src/storm-parsers/parser/DirectEncodingParser.cpp index 93d8a60f2..da4cd5228 100644 --- a/src/storm/parser/DirectEncodingParser.cpp +++ b/src/storm-parsers/parser/DirectEncodingParser.cpp @@ -1,4 +1,4 @@ -#include "storm/parser/DirectEncodingParser.h" +#include "storm-parsers/parser/DirectEncodingParser.h" #include #include diff --git a/src/storm/parser/DirectEncodingParser.h b/src/storm-parsers/parser/DirectEncodingParser.h similarity index 97% rename from src/storm/parser/DirectEncodingParser.h rename to src/storm-parsers/parser/DirectEncodingParser.h index 456445e1c..f025b8dfc 100644 --- a/src/storm/parser/DirectEncodingParser.h +++ b/src/storm-parsers/parser/DirectEncodingParser.h @@ -1,7 +1,7 @@ #ifndef STORM_PARSER_DIRECTENCODINGPARSER_H_ #define STORM_PARSER_DIRECTENCODINGPARSER_H_ -#include "storm/parser/ValueParser.h" +#include "storm-parsers/parser/ValueParser.h" #include "storm/models/sparse/Model.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/storage/sparse/ModelComponents.h" diff --git a/src/storm/parser/ExpressionCreator.cpp b/src/storm-parsers/parser/ExpressionCreator.cpp similarity index 100% rename from src/storm/parser/ExpressionCreator.cpp rename to src/storm-parsers/parser/ExpressionCreator.cpp diff --git a/src/storm/parser/ExpressionCreator.h b/src/storm-parsers/parser/ExpressionCreator.h similarity index 98% rename from src/storm/parser/ExpressionCreator.h rename to src/storm-parsers/parser/ExpressionCreator.h index 20bcc5854..814dc964a 100644 --- a/src/storm/parser/ExpressionCreator.h +++ b/src/storm-parsers/parser/ExpressionCreator.h @@ -1,7 +1,7 @@ #pragma once #include // Very ugly, but currently we would like to have the symbol table here. -#include "storm/parser/SpiritParserDefinitions.h" +#include "storm-parsers/parser/SpiritParserDefinitions.h" #include #include "storm/adapters/RationalNumberAdapter.h" diff --git a/src/storm/parser/ExpressionParser.cpp b/src/storm-parsers/parser/ExpressionParser.cpp similarity index 99% rename from src/storm/parser/ExpressionParser.cpp rename to src/storm-parsers/parser/ExpressionParser.cpp index 64e185b54..d1d6dcaf0 100644 --- a/src/storm/parser/ExpressionParser.cpp +++ b/src/storm-parsers/parser/ExpressionParser.cpp @@ -1,10 +1,10 @@ -#include "storm/parser/ExpressionParser.h" +#include "storm-parsers/parser/ExpressionParser.h" #include "storm/exceptions/InvalidArgumentException.h" #include "storm/exceptions/InvalidTypeException.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/utility/constants.h" -#include "storm/parser/ExpressionCreator.h" +#include "storm-parsers/parser/ExpressionCreator.h" #include "storm/storage/expressions/Expression.h" diff --git a/src/storm/parser/ExpressionParser.h b/src/storm-parsers/parser/ExpressionParser.h similarity index 99% rename from src/storm/parser/ExpressionParser.h rename to src/storm-parsers/parser/ExpressionParser.h index cb415c53e..4fa075adc 100644 --- a/src/storm/parser/ExpressionParser.h +++ b/src/storm-parsers/parser/ExpressionParser.h @@ -2,8 +2,8 @@ #include -#include "storm/parser/SpiritParserDefinitions.h" -#include "storm/parser/SpiritErrorHandler.h" +#include "storm-parsers/parser/SpiritParserDefinitions.h" +#include "storm-parsers/parser/SpiritErrorHandler.h" #include "storm/storage/expressions/OperatorType.h" #include "storm/adapters/RationalNumberAdapter.h" diff --git a/src/storm/parser/FormulaParser.cpp b/src/storm-parsers/parser/FormulaParser.cpp similarity index 98% rename from src/storm/parser/FormulaParser.cpp rename to src/storm-parsers/parser/FormulaParser.cpp index d8b9c2df2..0f44d469b 100644 --- a/src/storm/parser/FormulaParser.cpp +++ b/src/storm-parsers/parser/FormulaParser.cpp @@ -1,8 +1,8 @@ -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include -#include "storm/parser/SpiritErrorHandler.h" +#include "storm-parsers/parser/SpiritErrorHandler.h" #include "storm/storage/prism/Program.h" diff --git a/src/storm/parser/FormulaParser.h b/src/storm-parsers/parser/FormulaParser.h similarity index 96% rename from src/storm/parser/FormulaParser.h rename to src/storm-parsers/parser/FormulaParser.h index d9c06fa92..49c78a720 100644 --- a/src/storm/parser/FormulaParser.h +++ b/src/storm-parsers/parser/FormulaParser.h @@ -3,8 +3,8 @@ #include -#include "storm/parser/SpiritParserDefinitions.h" -#include "storm/parser/ExpressionParser.h" +#include "storm-parsers/parser/SpiritParserDefinitions.h" +#include "storm-parsers/parser/ExpressionParser.h" #include "storm/storage/jani/Property.h" #include "storm/storage/expressions/Expression.h" #include "storm/utility/macros.h" diff --git a/src/storm/parser/FormulaParserGrammar.cpp b/src/storm-parsers/parser/FormulaParserGrammar.cpp similarity index 100% rename from src/storm/parser/FormulaParserGrammar.cpp rename to src/storm-parsers/parser/FormulaParserGrammar.cpp diff --git a/src/storm/parser/FormulaParserGrammar.h b/src/storm-parsers/parser/FormulaParserGrammar.h similarity index 99% rename from src/storm/parser/FormulaParserGrammar.h rename to src/storm-parsers/parser/FormulaParserGrammar.h index dd9a3a06f..c9d5ae020 100644 --- a/src/storm/parser/FormulaParserGrammar.h +++ b/src/storm-parsers/parser/FormulaParserGrammar.h @@ -3,11 +3,11 @@ #include #include -#include "storm/parser/SpiritErrorHandler.h" +#include "storm-parsers/parser/SpiritErrorHandler.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/storage/jani/Property.h" #include "storm/logic/Formulas.h" -#include "storm/parser/ExpressionParser.h" +#include "storm-parsers/parser/ExpressionParser.h" #include "storm/modelchecker/results/FilterType.h" diff --git a/src/storm/parser/ImcaMarkovAutomatonParser.cpp b/src/storm-parsers/parser/ImcaMarkovAutomatonParser.cpp similarity index 99% rename from src/storm/parser/ImcaMarkovAutomatonParser.cpp rename to src/storm-parsers/parser/ImcaMarkovAutomatonParser.cpp index f55d09219..0cbf917f1 100644 --- a/src/storm/parser/ImcaMarkovAutomatonParser.cpp +++ b/src/storm-parsers/parser/ImcaMarkovAutomatonParser.cpp @@ -1,4 +1,4 @@ -#include "storm/parser/ImcaMarkovAutomatonParser.h" +#include "storm-parsers/parser/ImcaMarkovAutomatonParser.h" #include "storm/settings/SettingsManager.h" #include "storm/settings/modules/BuildSettings.h" diff --git a/src/storm/parser/ImcaMarkovAutomatonParser.h b/src/storm-parsers/parser/ImcaMarkovAutomatonParser.h similarity index 98% rename from src/storm/parser/ImcaMarkovAutomatonParser.h rename to src/storm-parsers/parser/ImcaMarkovAutomatonParser.h index f2820242e..b5c7beded 100644 --- a/src/storm/parser/ImcaMarkovAutomatonParser.h +++ b/src/storm-parsers/parser/ImcaMarkovAutomatonParser.h @@ -7,7 +7,7 @@ #include "storm/models/sparse/MarkovAutomaton.h" #include "storm/generator/StateBehavior.h" -#include "storm/parser/SpiritErrorHandler.h" +#include "storm-parsers/parser/SpiritErrorHandler.h" namespace storm { namespace parser { diff --git a/src/storm/parser/JaniParser.cpp b/src/storm-parsers/parser/JaniParser.cpp similarity index 92% rename from src/storm/parser/JaniParser.cpp rename to src/storm-parsers/parser/JaniParser.cpp index 725ab0a9d..f6d6251dd 100644 --- a/src/storm/parser/JaniParser.cpp +++ b/src/storm-parsers/parser/JaniParser.cpp @@ -163,12 +163,10 @@ namespace storm { return { parseFormula(propertyStructure.at("left"), formulaContext, globalVars, constants, "Operand of operator " + opstring), parseFormula(propertyStructure.at("right"), formulaContext, globalVars, constants, "Operand of operator " + opstring) }; } - storm::jani::PropertyInterval JaniParser::parsePropertyInterval(json const& piStructure) { + storm::jani::PropertyInterval JaniParser::parsePropertyInterval(json const& piStructure, std::unordered_map> const& constants) { storm::jani::PropertyInterval pi; if (piStructure.count("lower") > 0) { - pi.lowerBound = parseExpression(piStructure.at("lower"), "Lower bound for property interval", {}, {}); - // TODO substitute constants. - STORM_LOG_THROW(!pi.lowerBound.containsVariables(), storm::exceptions::NotSupportedException, "Only constant expressions are supported as lower bounds"); + pi.lowerBound = parseExpression(piStructure.at("lower"), "Lower bound for property interval", {}, constants); } if (piStructure.count("lower-exclusive") > 0) { STORM_LOG_THROW(pi.lowerBound.isInitialized(), storm::exceptions::InvalidJaniException, "Lower-exclusive can only be set if a lower bound is present"); @@ -176,9 +174,7 @@ namespace storm { } if (piStructure.count("upper") > 0) { - pi.upperBound = parseExpression(piStructure.at("upper"), "Upper bound for property interval", {}, {}); - // TODO substitute constants. - STORM_LOG_THROW(!pi.upperBound.containsVariables(), storm::exceptions::NotSupportedException, "Only constant expressions are supported as upper bounds"); + pi.upperBound = parseExpression(piStructure.at("upper"), "Upper bound for property interval", {}, constants); } if (piStructure.count("upper-exclusive") > 0) { @@ -228,12 +224,7 @@ namespace storm { } else { time = true; } - std::shared_ptr reach; - if (propertyStructure.count("reach") > 0) { - reach = std::make_shared(parseFormula(propertyStructure.at("reach"), time ? storm::logic::FormulaContext::Time : storm::logic::FormulaContext::Reward, globalVars, constants, "Reach-expression of operator " + opString)); - } else { - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Total reward is currently not supported"); - } + storm::logic::OperatorInformation opInfo; opInfo.optimalityType = opString == "Emin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize; opInfo.bound = bound; @@ -252,15 +243,13 @@ namespace storm { } } } - STORM_LOG_THROW(!(accTime && accSteps), storm::exceptions::NotSupportedException, "Storm does not allow to accumulate over both time and steps"); - + // TODO: handle accumulation parameters! if (propertyStructure.count("step-instant") > 0) { - storm::expressions::Expression stepInstantExpr = parseExpression(propertyStructure.at("step-instant"), "Step instant in " + context, globalVars, constants); - STORM_LOG_THROW(!stepInstantExpr.containsVariables(), storm::exceptions::NotSupportedException, "Storm only allows constant step-instants"); + STORM_LOG_THROW(propertyStructure.count("time-instant") == 0, storm::exceptions::NotSupportedException, "Storm does not support to have a step-instant and a time-instant in " + context); + STORM_LOG_THROW(propertyStructure.count("reward-instants") == 0, storm::exceptions::NotSupportedException, "Storm does not support to have a step-instant and a reward-instant in " + context); - int64_t stepInstant = stepInstantExpr.evaluateAsInt(); - STORM_LOG_THROW(stepInstant >= 0, storm::exceptions::InvalidJaniException, "Only non-negative step-instants are allowed"); + storm::expressions::Expression stepInstantExpr = parseExpression(propertyStructure.at("step-instant"), "Step instant in " + context, globalVars, constants); if(!accTime && !accSteps) { if (rewExpr.isVariable()) { std::string rewardName = rewExpr.getVariables().begin()->getName(); @@ -277,11 +266,10 @@ namespace storm { } } } else if (propertyStructure.count("time-instant") > 0) { + STORM_LOG_THROW(propertyStructure.count("reward-instants") == 0, storm::exceptions::NotSupportedException, "Storm does not support to have a time-instant and a reward-instant in " + context); + storm::expressions::Expression timeInstantExpr = parseExpression(propertyStructure.at("time-instant"), "time instant in " + context, globalVars, constants); - STORM_LOG_THROW(!timeInstantExpr.containsVariables(), storm::exceptions::NotSupportedException, "Storm only allows constant time-instants"); - double timeInstant = timeInstantExpr.evaluateAsDouble(); - STORM_LOG_THROW(timeInstant >= 0, storm::exceptions::InvalidJaniException, "Only non-negative time-instants are allowed"); if(!accTime && !accSteps) { if (rewExpr.isVariable()) { std::string rewardName = rewExpr.getVariables().begin()->getName(); @@ -298,38 +286,69 @@ namespace storm { } } } else if (propertyStructure.count("reward-instants") > 0) { - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Instant Reward for reward constraints not supported currently."); - } - - //STORM_LOG_THROW(!accTime && !accSteps, storm::exceptions::NotSupportedException, "Storm only allows accumulation if a step- or time-bound is given."); - - if (rewExpr.isVariable()) { - std::string rewardName = rewExpr.getVariables().begin()->getName(); - return std::make_shared(reach, rewardName, opInfo); - } else if (!rewExpr.containsVariables()) { - if(rewExpr.hasIntegerType()) { - if (rewExpr.evaluateAsInt() == 1) { - - return std::make_shared(reach, opInfo); - } else { - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Expected steps/time only works with constant one."); + std::vector bounds; + std::vector boundReferences; + for (auto const& rewInst : propertyStructure.at("reward-instants")) { + storm::expressions::Expression rewInstExpression = parseExpression(rewInst.at("exp"), "Reward expression in " + context, globalVars, constants); + STORM_LOG_THROW(!rewInstExpression.isVariable(), storm::exceptions::NotSupportedException, "Reward bounded cumulative reward formulas should only argue over reward expressions."); + boundReferences.emplace_back(rewInstExpression.getVariables().begin()->getName()); + bool rewInstAccSteps(false), rewInstAccTime(false); + for (auto const& accEntry : rewInst.at("accumulate")) { + if (accEntry == "steps") { + rewInstAccSteps = true; + } else if (accEntry == "time") { + rewInstAccTime = true; + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "One may only accumulate either 'steps' or 'time', got " << accEntry.dump() << " in " << context); + } } - } else if (rewExpr.hasRationalType()){ - if (rewExpr.evaluateAsDouble() == 1.0) { - - return std::make_shared(reach, opInfo); + STORM_LOG_THROW(rewInstAccSteps || rewInstAccTime, storm::exceptions::NotSupportedException, "Storm only allows to accumulate either over time or over steps in " + context); + // TODO: handle accumulation parameters + storm::expressions::Expression rewInstantExpr = parseExpression(rewInst.at("instant"), "reward instant in " + context, globalVars, constants); + bounds.emplace_back(false, rewInstantExpr); + } + if (rewExpr.isVariable()) { + std::string rewardName = rewExpr.getVariables().begin()->getName(); + return std::make_shared(std::make_shared(bounds, boundReferences), rewardName, opInfo); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Only simple reward expressions are currently supported"); + } + } else { + std::shared_ptr subformula; + if (propertyStructure.count("reach") > 0) { + auto context = time ? storm::logic::FormulaContext::Time : storm::logic::FormulaContext::Reward; + subformula = std::make_shared(parseFormula(propertyStructure.at("reach"), context, globalVars, constants, "Reach-expression of operator " + opString), context); + } else { + subformula = std::make_shared(); + } + if (rewExpr.isVariable()) { + assert(!time); + std::string rewardName = rewExpr.getVariables().begin()->getName(); + return std::make_shared(subformula, rewardName, opInfo); + } else if (!rewExpr.containsVariables()) { + assert(time); + assert(subformula->isTotalRewardFormula() || subformula->isTimePathFormula()); + if(rewExpr.hasIntegerType()) { + if (rewExpr.evaluateAsInt() == 1) { + return std::make_shared(subformula, opInfo); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Expected steps/time only works with constant one."); + } + } else if (rewExpr.hasRationalType()){ + if (rewExpr.evaluateAsDouble() == 1.0) { + + return std::make_shared(subformula, opInfo); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Expected steps/time only works with constant one."); + } } else { - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Expected steps/time only works with constant one."); + STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Only numerical reward expressions are allowed"); } + } else { - STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Only numerical reward expressions are allowed"); + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "No complex reward expressions are supported at the moment"); } - - } else { - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "No complex reward expressions are supported at the moment"); } - - } else if (opString == "Smin" || opString == "Smax") { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Smin and Smax are currently not supported"); } else if (opString == "U" || opString == "F") { @@ -343,49 +362,47 @@ namespace storm { args.push_back(args[0]); args[0] = storm::logic::BooleanLiteralFormula::getTrueFormula(); } + + std::vector> lowerBounds, upperBounds; + std::vector tbReferences; if (propertyStructure.count("step-bounds") > 0) { - storm::jani::PropertyInterval pi = parsePropertyInterval(propertyStructure.at("step-bounds")); - STORM_LOG_THROW(pi.hasUpperBound(), storm::exceptions::NotSupportedException, "Storm only supports step-bounded until with an upper bound"); - if(pi.hasLowerBound()) { - STORM_LOG_THROW(pi.lowerBound.evaluateAsInt() == 0, storm::exceptions::NotSupportedException, "Storm only supports step-bounded until without a (non-trivial) lower-bound"); + storm::jani::PropertyInterval pi = parsePropertyInterval(propertyStructure.at("step-bounds"), constants); + boost::optional lowerBound, upperBound; + if (pi.hasLowerBound()) { + lowerBounds.push_back(storm::logic::TimeBound(pi.lowerBoundStrict, pi.lowerBound)); + } else { + lowerBounds.push_back(boost::none); } - int64_t upperBound = pi.upperBound.evaluateAsInt(); - if(pi.upperBoundStrict) { - upperBound--; + if (pi.hasUpperBound()) { + upperBounds.push_back(storm::logic::TimeBound(pi.upperBoundStrict, pi.upperBound)); + } else { + upperBounds.push_back(boost::none); } - STORM_LOG_THROW(upperBound >= 0, storm::exceptions::InvalidJaniException, "Step-bounds cannot be negative"); - return std::make_shared(args[0], args[1], storm::logic::TimeBound(pi.lowerBoundStrict, pi.lowerBound), storm::logic::TimeBound(pi.upperBoundStrict, pi.upperBound), storm::logic::TimeBoundReference(storm::logic::TimeBoundType::Steps)); - } else if (propertyStructure.count("time-bounds") > 0) { - storm::jani::PropertyInterval pi = parsePropertyInterval(propertyStructure.at("time-bounds")); - STORM_LOG_THROW(pi.hasUpperBound(), storm::exceptions::NotSupportedException, "Storm only supports time-bounded until with an upper bound."); - double lowerBound = 0.0; - if(pi.hasLowerBound()) { - lowerBound = pi.lowerBound.evaluateAsDouble(); + tbReferences.emplace_back(storm::logic::TimeBoundType::Steps); + } + if (propertyStructure.count("time-bounds") > 0) { + storm::jani::PropertyInterval pi = parsePropertyInterval(propertyStructure.at("time-bounds"), constants); + boost::optional lowerBound, upperBound; + if (pi.hasLowerBound()) { + lowerBounds.push_back(storm::logic::TimeBound(pi.lowerBoundStrict, pi.lowerBound)); + } else { + lowerBounds.push_back(boost::none); } - double upperBound = pi.upperBound.evaluateAsDouble(); - STORM_LOG_THROW(lowerBound >= 0, storm::exceptions::InvalidJaniException, "(Lower) time-bounds cannot be negative"); - STORM_LOG_THROW(upperBound >= 0, storm::exceptions::InvalidJaniException, "(Upper) time-bounds cannot be negative"); - return std::make_shared(args[0], args[1], storm::logic::TimeBound(pi.lowerBoundStrict, pi.lowerBound), storm::logic::TimeBound(pi.upperBoundStrict, pi.upperBound), storm::logic::TimeBoundReference(storm::logic::TimeBoundType::Time)); - - } else if (propertyStructure.count("reward-bounds") > 0 ) { - std::vector> lowerBounds; - std::vector> upperBounds; - std::vector tbReferences; + if (pi.hasUpperBound()) { + upperBounds.push_back(storm::logic::TimeBound(pi.upperBoundStrict, pi.upperBound)); + } else { + upperBounds.push_back(boost::none); + } + tbReferences.emplace_back(storm::logic::TimeBoundType::Time); + } + if (propertyStructure.count("reward-bounds") > 0 ) { for (auto const& rbStructure : propertyStructure.at("reward-bounds")) { - storm::jani::PropertyInterval pi = parsePropertyInterval(rbStructure.at("bounds")); - STORM_LOG_THROW(pi.hasUpperBound(), storm::exceptions::NotSupportedException, "Storm only supports time-bounded until with an upper bound."); + storm::jani::PropertyInterval pi = parsePropertyInterval(rbStructure.at("bounds"), constants); STORM_LOG_THROW(rbStructure.count("exp") == 1, storm::exceptions::InvalidJaniException, "Expecting reward-expression for operator " << opString << " in " << context); storm::expressions::Expression rewExpr = parseExpression(rbStructure.at("exp"), "Reward expression in " + context, globalVars, constants); STORM_LOG_THROW(rewExpr.isVariable(), storm::exceptions::NotSupportedException, "Storm currently does not support complex reward expressions."); std::string rewardName = rewExpr.getVariables().begin()->getName(); STORM_LOG_WARN("Reward-type (steps, time) is deduced from model type."); - double lowerBound = 0.0; - if(pi.hasLowerBound()) { - lowerBound = pi.lowerBound.evaluateAsDouble(); - } - double upperBound = pi.upperBound.evaluateAsDouble(); - STORM_LOG_THROW(lowerBound >= 0, storm::exceptions::InvalidJaniException, "(Lower) time-bounds cannot be negative"); - STORM_LOG_THROW(upperBound >= 0, storm::exceptions::InvalidJaniException, "(Upper) time-bounds cannot be negative"); if (pi.hasLowerBound()) { lowerBounds.push_back(storm::logic::TimeBound(pi.lowerBoundStrict, pi.lowerBound)); } else { @@ -398,11 +415,10 @@ namespace storm { } tbReferences.push_back(storm::logic::TimeBoundReference(rewardName)); } - auto res = std::make_shared(args[0], args[1], lowerBounds, upperBounds, tbReferences); - return res; - } - if (args[0]->isTrueFormula()) { + if (!tbReferences.empty()) { + return std::make_shared(args[0], args[1], lowerBounds, upperBounds, tbReferences); + } else if (args[0]->isTrueFormula()) { return std::make_shared(args[1], formulaContext); } else { return std::make_shared(args[0], args[1]); @@ -480,6 +496,7 @@ namespace storm { STORM_LOG_THROW(propertyStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "Property must have a name"); // TODO check unique name std::string name = getString(propertyStructure.at("name"), "property-name"); + STORM_LOG_TRACE("Parsing property named: " << name); std::string comment = ""; if (propertyStructure.count("comment") > 0) { comment = getString(propertyStructure.at("comment"), "comment for property named '" + name + "'."); @@ -800,7 +817,7 @@ namespace storm { if(opstring == "ite") { STORM_LOG_THROW(expressionStructure.count("if") == 1, storm::exceptions::InvalidJaniException, "If operator required"); STORM_LOG_THROW(expressionStructure.count("else") == 1, storm::exceptions::InvalidJaniException, "Else operator required"); - STORM_LOG_THROW(expressionStructure.count("then") == 1, storm::exceptions::InvalidJaniException, "If operator required"); + STORM_LOG_THROW(expressionStructure.count("then") == 1, storm::exceptions::InvalidJaniException, "Then operator required"); arguments.push_back(parseExpression(expressionStructure.at("if"), "if-formula in " + scopeDescription, globalVars, constants, localVars, returnNoneInitializedOnUnknownOperator)); arguments.push_back(parseExpression(expressionStructure.at("then"), "then-formula in " + scopeDescription, globalVars, constants, localVars, returnNoneInitializedOnUnknownOperator)); arguments.push_back(parseExpression(expressionStructure.at("else"), "else-formula in " + scopeDescription, globalVars, constants, localVars, returnNoneInitializedOnUnknownOperator)); @@ -1005,7 +1022,7 @@ namespace storm { } STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "No supported operator declaration found for complex expressions as " << expressionStructure.dump() << " in " << scopeDescription << "."); } - assert(false); + STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "No supported expression found at " << expressionStructure.dump() << " in " << scopeDescription << "."); // Silly warning suppression. return storm::expressions::Expression(); diff --git a/src/storm/parser/JaniParser.h b/src/storm-parsers/parser/JaniParser.h similarity index 98% rename from src/storm/parser/JaniParser.h rename to src/storm-parsers/parser/JaniParser.h index 9a6bdace6..1ac4a514f 100644 --- a/src/storm/parser/JaniParser.h +++ b/src/storm-parsers/parser/JaniParser.h @@ -65,7 +65,7 @@ namespace storm { std::vector> parseUnaryFormulaArgument(json const& propertyStructure, storm::logic::FormulaContext formulaContext, std::string const& opstring, std::unordered_map> const& globalVars, std::unordered_map> const& constants, std::string const& context); std::vector> parseBinaryFormulaArguments(json const& propertyStructure, storm::logic::FormulaContext formulaContext, std::string const& opstring, std::unordered_map> const& globalVars, std::unordered_map> const& constants, std::string const& context); - storm::jani::PropertyInterval parsePropertyInterval(json const& piStructure); + storm::jani::PropertyInterval parsePropertyInterval(json const& piStructure, std::unordered_map> const& constants); std::shared_ptr parseComposition(json const& compositionStructure); diff --git a/src/storm/parser/KeyValueParser.cpp b/src/storm-parsers/parser/KeyValueParser.cpp similarity index 100% rename from src/storm/parser/KeyValueParser.cpp rename to src/storm-parsers/parser/KeyValueParser.cpp diff --git a/src/storm/parser/KeyValueParser.h b/src/storm-parsers/parser/KeyValueParser.h similarity index 100% rename from src/storm/parser/KeyValueParser.h rename to src/storm-parsers/parser/KeyValueParser.h diff --git a/src/storm/parser/MappedFile.cpp b/src/storm-parsers/parser/MappedFile.cpp similarity index 98% rename from src/storm/parser/MappedFile.cpp rename to src/storm-parsers/parser/MappedFile.cpp index 5b9f02714..d28e8e927 100644 --- a/src/storm/parser/MappedFile.cpp +++ b/src/storm-parsers/parser/MappedFile.cpp @@ -5,7 +5,7 @@ * Author: Manuel Sascha Weiand */ -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" #include #include diff --git a/src/storm/parser/MappedFile.h b/src/storm-parsers/parser/MappedFile.h similarity index 100% rename from src/storm/parser/MappedFile.h rename to src/storm-parsers/parser/MappedFile.h diff --git a/src/storm/parser/MarkovAutomatonParser.cpp b/src/storm-parsers/parser/MarkovAutomatonParser.cpp similarity index 100% rename from src/storm/parser/MarkovAutomatonParser.cpp rename to src/storm-parsers/parser/MarkovAutomatonParser.cpp diff --git a/src/storm/parser/MarkovAutomatonParser.h b/src/storm-parsers/parser/MarkovAutomatonParser.h similarity index 96% rename from src/storm/parser/MarkovAutomatonParser.h rename to src/storm-parsers/parser/MarkovAutomatonParser.h index 763ece141..1f2087674 100644 --- a/src/storm/parser/MarkovAutomatonParser.h +++ b/src/storm-parsers/parser/MarkovAutomatonParser.h @@ -2,7 +2,7 @@ #define STORM_PARSER_MARKOVAUTOMATONPARSER_H_ #include "storm/models/sparse/MarkovAutomaton.h" -#include "storm/parser/MarkovAutomatonSparseTransitionParser.h" +#include "storm-parsers/parser/MarkovAutomatonSparseTransitionParser.h" namespace storm { namespace parser { diff --git a/src/storm/parser/MarkovAutomatonSparseTransitionParser.cpp b/src/storm-parsers/parser/MarkovAutomatonSparseTransitionParser.cpp similarity index 99% rename from src/storm/parser/MarkovAutomatonSparseTransitionParser.cpp rename to src/storm-parsers/parser/MarkovAutomatonSparseTransitionParser.cpp index 25b62a2e2..9acd151b0 100644 --- a/src/storm/parser/MarkovAutomatonSparseTransitionParser.cpp +++ b/src/storm-parsers/parser/MarkovAutomatonSparseTransitionParser.cpp @@ -4,7 +4,7 @@ #include "storm/settings/modules/CoreSettings.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/exceptions/FileIoException.h" -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" #include "storm/utility/cstring.h" #include "storm/utility/constants.h" #include "storm/utility/macros.h" diff --git a/src/storm/parser/MarkovAutomatonSparseTransitionParser.h b/src/storm-parsers/parser/MarkovAutomatonSparseTransitionParser.h similarity index 100% rename from src/storm/parser/MarkovAutomatonSparseTransitionParser.h rename to src/storm-parsers/parser/MarkovAutomatonSparseTransitionParser.h diff --git a/src/storm/parser/NondeterministicModelParser.cpp b/src/storm-parsers/parser/NondeterministicModelParser.cpp similarity index 93% rename from src/storm/parser/NondeterministicModelParser.cpp rename to src/storm-parsers/parser/NondeterministicModelParser.cpp index c107803eb..b26626fb0 100644 --- a/src/storm/parser/NondeterministicModelParser.cpp +++ b/src/storm-parsers/parser/NondeterministicModelParser.cpp @@ -1,13 +1,13 @@ -#include "storm/parser/NondeterministicModelParser.h" +#include "storm-parsers/parser/NondeterministicModelParser.h" #include #include #include "storm/models/sparse/StandardRewardModel.h" -#include "storm/parser/NondeterministicSparseTransitionParser.h" -#include "storm/parser/SparseItemLabelingParser.h" -#include "storm/parser/SparseStateRewardParser.h" +#include "storm-parsers/parser/NondeterministicSparseTransitionParser.h" +#include "storm-parsers/parser/SparseItemLabelingParser.h" +#include "storm-parsers/parser/SparseStateRewardParser.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/utility/macros.h" diff --git a/src/storm/parser/NondeterministicModelParser.h b/src/storm-parsers/parser/NondeterministicModelParser.h similarity index 100% rename from src/storm/parser/NondeterministicModelParser.h rename to src/storm-parsers/parser/NondeterministicModelParser.h diff --git a/src/storm/parser/NondeterministicSparseTransitionParser.cpp b/src/storm-parsers/parser/NondeterministicSparseTransitionParser.cpp similarity index 99% rename from src/storm/parser/NondeterministicSparseTransitionParser.cpp rename to src/storm-parsers/parser/NondeterministicSparseTransitionParser.cpp index cb941aec5..56b00070c 100644 --- a/src/storm/parser/NondeterministicSparseTransitionParser.cpp +++ b/src/storm-parsers/parser/NondeterministicSparseTransitionParser.cpp @@ -1,8 +1,8 @@ -#include "storm/parser/NondeterministicSparseTransitionParser.h" +#include "storm-parsers/parser/NondeterministicSparseTransitionParser.h" #include -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" #include "storm/settings/SettingsManager.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/exceptions/FileIoException.h" diff --git a/src/storm/parser/NondeterministicSparseTransitionParser.h b/src/storm-parsers/parser/NondeterministicSparseTransitionParser.h similarity index 100% rename from src/storm/parser/NondeterministicSparseTransitionParser.h rename to src/storm-parsers/parser/NondeterministicSparseTransitionParser.h diff --git a/src/storm/parser/PrismParser.cpp b/src/storm-parsers/parser/PrismParser.cpp similarity index 99% rename from src/storm/parser/PrismParser.cpp rename to src/storm-parsers/parser/PrismParser.cpp index ca59981cb..5d76736f7 100644 --- a/src/storm/parser/PrismParser.cpp +++ b/src/storm-parsers/parser/PrismParser.cpp @@ -1,4 +1,4 @@ -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/storage/prism/Compositions.h" @@ -11,7 +11,7 @@ #include "storm/storage/expressions/ExpressionManager.h" -#include "storm/parser/ExpressionParser.h" +#include "storm-parsers/parser/ExpressionParser.h" namespace storm { namespace parser { diff --git a/src/storm/parser/PrismParser.h b/src/storm-parsers/parser/PrismParser.h similarity index 99% rename from src/storm/parser/PrismParser.h rename to src/storm-parsers/parser/PrismParser.h index fe7f0bc83..38a112e9d 100644 --- a/src/storm/parser/PrismParser.h +++ b/src/storm-parsers/parser/PrismParser.h @@ -6,8 +6,8 @@ #include #include -#include "storm/parser/SpiritParserDefinitions.h" -#include "storm/parser/SpiritErrorHandler.h" +#include "storm-parsers/parser/SpiritParserDefinitions.h" +#include "storm-parsers/parser/SpiritErrorHandler.h" #include "storm/storage/prism/Program.h" #include "storm/storage/expressions/Expression.h" diff --git a/src/storm/parser/ReadValues.h b/src/storm-parsers/parser/ReadValues.h similarity index 100% rename from src/storm/parser/ReadValues.h rename to src/storm-parsers/parser/ReadValues.h diff --git a/src/storm/parser/SparseChoiceLabelingParser.cpp b/src/storm-parsers/parser/SparseChoiceLabelingParser.cpp similarity index 97% rename from src/storm/parser/SparseChoiceLabelingParser.cpp rename to src/storm-parsers/parser/SparseChoiceLabelingParser.cpp index b51e8b97c..452045319 100644 --- a/src/storm/parser/SparseChoiceLabelingParser.cpp +++ b/src/storm-parsers/parser/SparseChoiceLabelingParser.cpp @@ -1,10 +1,10 @@ -#include "storm/parser/SparseChoiceLabelingParser.h" +#include "storm-parsers/parser/SparseChoiceLabelingParser.h" #include "storm/utility/macros.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/exceptions/OutOfRangeException.h" #include "storm/exceptions/FileIoException.h" -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" #include "storm/utility/cstring.h" namespace storm { diff --git a/src/storm/parser/SparseChoiceLabelingParser.h b/src/storm-parsers/parser/SparseChoiceLabelingParser.h similarity index 100% rename from src/storm/parser/SparseChoiceLabelingParser.h rename to src/storm-parsers/parser/SparseChoiceLabelingParser.h diff --git a/src/storm/parser/SparseItemLabelingParser.cpp b/src/storm-parsers/parser/SparseItemLabelingParser.cpp similarity index 99% rename from src/storm/parser/SparseItemLabelingParser.cpp rename to src/storm-parsers/parser/SparseItemLabelingParser.cpp index 2e98e4673..9e5e7b154 100644 --- a/src/storm/parser/SparseItemLabelingParser.cpp +++ b/src/storm-parsers/parser/SparseItemLabelingParser.cpp @@ -1,11 +1,11 @@ -#include "storm/parser/SparseItemLabelingParser.h" +#include "storm-parsers/parser/SparseItemLabelingParser.h" #include #include #include #include "storm/utility/cstring.h" -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" #include "storm/utility/macros.h" #include "storm/exceptions/WrongFormatException.h" diff --git a/src/storm/parser/SparseItemLabelingParser.h b/src/storm-parsers/parser/SparseItemLabelingParser.h similarity index 98% rename from src/storm/parser/SparseItemLabelingParser.h rename to src/storm-parsers/parser/SparseItemLabelingParser.h index 4b901ab82..97c30cc25 100644 --- a/src/storm/parser/SparseItemLabelingParser.h +++ b/src/storm-parsers/parser/SparseItemLabelingParser.h @@ -4,7 +4,7 @@ #include #include -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" #include "storm/models/sparse/StateLabeling.h" #include "storm/models/sparse/ChoiceLabeling.h" diff --git a/src/storm/parser/SparseStateRewardParser.cpp b/src/storm-parsers/parser/SparseStateRewardParser.cpp similarity index 96% rename from src/storm/parser/SparseStateRewardParser.cpp rename to src/storm-parsers/parser/SparseStateRewardParser.cpp index f42b4cbe8..acb67e9fb 100644 --- a/src/storm/parser/SparseStateRewardParser.cpp +++ b/src/storm-parsers/parser/SparseStateRewardParser.cpp @@ -1,11 +1,11 @@ #include -#include "storm/parser/SparseStateRewardParser.h" +#include "storm-parsers/parser/SparseStateRewardParser.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/exceptions/OutOfRangeException.h" #include "storm/exceptions/FileIoException.h" #include "storm/utility/cstring.h" -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/utility/macros.h" diff --git a/src/storm/parser/SparseStateRewardParser.h b/src/storm-parsers/parser/SparseStateRewardParser.h similarity index 100% rename from src/storm/parser/SparseStateRewardParser.h rename to src/storm-parsers/parser/SparseStateRewardParser.h diff --git a/src/storm/parser/SpiritErrorHandler.h b/src/storm-parsers/parser/SpiritErrorHandler.h similarity index 95% rename from src/storm/parser/SpiritErrorHandler.h rename to src/storm-parsers/parser/SpiritErrorHandler.h index 34280e422..ab64008bc 100644 --- a/src/storm/parser/SpiritErrorHandler.h +++ b/src/storm-parsers/parser/SpiritErrorHandler.h @@ -1,6 +1,6 @@ #pragma once -#include "storm/parser/SpiritParserDefinitions.h" +#include "storm-parsers/parser/SpiritParserDefinitions.h" #include "storm/utility/macros.h" #include "storm/exceptions/WrongFormatException.h" diff --git a/src/storm/parser/SpiritParserDefinitions.h b/src/storm-parsers/parser/SpiritParserDefinitions.h similarity index 100% rename from src/storm/parser/SpiritParserDefinitions.h rename to src/storm-parsers/parser/SpiritParserDefinitions.h diff --git a/src/storm/parser/ValueParser.cpp b/src/storm-parsers/parser/ValueParser.cpp similarity index 96% rename from src/storm/parser/ValueParser.cpp rename to src/storm-parsers/parser/ValueParser.cpp index 17495b0d9..61cd5c0ac 100644 --- a/src/storm/parser/ValueParser.cpp +++ b/src/storm-parsers/parser/ValueParser.cpp @@ -1,4 +1,4 @@ -#include "storm/parser/ValueParser.h" +#include "storm-parsers/parser/ValueParser.h" #include "storm/exceptions/NotSupportedException.h" diff --git a/src/storm/parser/ValueParser.h b/src/storm-parsers/parser/ValueParser.h similarity index 97% rename from src/storm/parser/ValueParser.h rename to src/storm-parsers/parser/ValueParser.h index db948b75e..626857a3e 100644 --- a/src/storm/parser/ValueParser.h +++ b/src/storm-parsers/parser/ValueParser.h @@ -2,7 +2,7 @@ #define STORM_PARSER_VALUEPARSER_H_ #include "storm/storage/expressions/ExpressionManager.h" -#include "storm/parser/ExpressionParser.h" +#include "storm-parsers/parser/ExpressionParser.h" #include "storm/storage/expressions/ExpressionEvaluator.h" #include "storm/exceptions/WrongFormatException.h" diff --git a/src/storm-pgcl-cli/CMakeLists.txt b/src/storm-pgcl-cli/CMakeLists.txt index 4f2799d4d..506772fd9 100644 --- a/src/storm-pgcl-cli/CMakeLists.txt +++ b/src/storm-pgcl-cli/CMakeLists.txt @@ -5,4 +5,4 @@ set_target_properties(storm-pgcl-cli PROPERTIES OUTPUT_NAME "storm-pgcl") add_dependencies(binaries storm-pgcl-cli) # installation -install(TARGETS storm-pgcl-cli RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) \ No newline at end of file +install(TARGETS storm-pgcl-cli EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-pgcl-cli/storm-pgcl.cpp b/src/storm-pgcl-cli/storm-pgcl.cpp index bfbb6cad4..fe4585023 100644 --- a/src/storm-pgcl-cli/storm-pgcl.cpp +++ b/src/storm-pgcl-cli/storm-pgcl.cpp @@ -18,7 +18,8 @@ #include "storm-pgcl/settings/modules/PGCLSettings.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/settings/modules/DebugSettings.h" -#include "storm/settings/modules/JaniExportSettings.h" +#include "storm-conv/settings/modules/JaniExportSettings.h" +#include "storm-conv/api/storm-conv.h" #include "storm/utility/file.h" @@ -38,11 +39,13 @@ void initializeSettings() { } void handleJani(storm::jani::Model& model) { - if (!storm::settings::getModule().isJaniFileSet()) { - // For now, we have to have a jani file - storm::jani::JsonExporter::toStream(model, {}, std::cout); + auto const& jani = storm::settings::getModule(); + storm::converter::JaniConversionOptions options(jani); + storm::api::postprocessJani(model, options); + if (storm::settings::getModule().isToJaniSet()) { + storm::api::exportJaniToFile(model, {}, storm::settings::getModule().getWriteToJaniFilename(), jani.isCompactJsonSet()); } else { - storm::jani::JsonExporter::toFile(model, {}, storm::settings::getModule().getJaniFilename()); + storm::api::printJaniToStream(model, {}, std::cout); } } diff --git a/src/storm-pgcl/CMakeLists.txt b/src/storm-pgcl/CMakeLists.txt index e20a99483..1adf442c6 100644 --- a/src/storm-pgcl/CMakeLists.txt +++ b/src/storm-pgcl/CMakeLists.txt @@ -10,7 +10,7 @@ file(GLOB_RECURSE STORM_PGCL_HEADERS ${PROJECT_SOURCE_DIR}/src/storm-pgcl/*/*.h) # Create storm-pgcl. add_library(storm-pgcl SHARED ${STORM_PGCL_SOURCES} ${STORM_PGCL_HEADERS}) -target_link_libraries(storm-pgcl storm) +target_link_libraries(storm-pgcl storm storm-parsers storm-conv) # installation -install(TARGETS storm-pgcl RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) \ No newline at end of file +install(TARGETS storm-pgcl EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) diff --git a/src/storm-pgcl/parser/PgclParser.h b/src/storm-pgcl/parser/PgclParser.h index dfd75a810..0e31094b9 100755 --- a/src/storm-pgcl/parser/PgclParser.h +++ b/src/storm-pgcl/parser/PgclParser.h @@ -5,9 +5,9 @@ #include #include // Includes files for building and parsing the PGCL program -#include "storm/parser/SpiritParserDefinitions.h" -#include "storm/parser/SpiritErrorHandler.h" -#include "storm/parser/ExpressionParser.h" +#include "storm-parsers/parser/SpiritParserDefinitions.h" +#include "storm-parsers/parser/SpiritErrorHandler.h" +#include "storm-parsers/parser/ExpressionParser.h" #include "storm/storage/expressions/ExpressionManager.h" #include "storm/storage/expressions/Expression.h" #include "storm-pgcl/storage/pgcl/PgclProgram.h" diff --git a/src/storm-pgcl/settings/modules/PGCLSettings.cpp b/src/storm-pgcl/settings/modules/PGCLSettings.cpp index a70b87f6f..4225dcd39 100644 --- a/src/storm-pgcl/settings/modules/PGCLSettings.cpp +++ b/src/storm-pgcl/settings/modules/PGCLSettings.cpp @@ -26,7 +26,7 @@ namespace storm { PGCLSettings::PGCLSettings() : ModuleSettings(moduleName) { this->addOption(storm::settings::OptionBuilder(moduleName, pgclFileOptionName, false, "Parses the pgcl program.").setShortName(pgclFileOptionShortName).addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").addValidatorString(ArgumentValidatorFactory::createExistingFileValidator()).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, pgclToJaniOptionName, false, "Transform to JANI.").setShortName(pgclToJaniOptionShortName).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, pgclToJaniOptionName, false, "Transform to JANI.").setShortName(pgclToJaniOptionShortName).addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").addValidatorString(ArgumentValidatorFactory::createWritableFileValidator()).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, programGraphToDotOptionName, false, "Destination for the program graph dot output.").setShortName(programGraphToDotShortOptionName).addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, programVariableRestrictionsOptionName, false, "Restrictions of program variables").setShortName(programVariableRestrictionShortOptionName).addArgument(storm::settings::ArgumentBuilder::createStringArgument("description", "description of the variable restrictions").build()).build()); } @@ -43,6 +43,11 @@ namespace storm { return this->getOption(pgclToJaniOptionName).getHasOptionBeenSet(); } + std::string const& PGCLSettings::getWriteToJaniFilename() const { + return this->getOption(pgclToJaniOptionName).getArgumentByName("filename").getValueAsString(); + } + + bool PGCLSettings::isProgramGraphToDotSet() const { return this->getOption(programGraphToDotOptionName).getHasOptionBeenSet(); } diff --git a/src/storm-pgcl/settings/modules/PGCLSettings.h b/src/storm-pgcl/settings/modules/PGCLSettings.h index 29e689366..5870ccbe5 100644 --- a/src/storm-pgcl/settings/modules/PGCLSettings.h +++ b/src/storm-pgcl/settings/modules/PGCLSettings.h @@ -29,6 +29,11 @@ namespace storm { */ bool isToJaniSet() const; + /** + * returns the file name where jani output should be stored. + */ + std::string const& getWriteToJaniFilename() const; + /** * Whether the program graph should be drawn (dot output) */ diff --git a/src/storm/CMakeLists.txt b/src/storm/CMakeLists.txt index 0cf8d3183..dcd1bb9f9 100644 --- a/src/storm/CMakeLists.txt +++ b/src/storm/CMakeLists.txt @@ -28,8 +28,6 @@ if (ADDITIONAL_LINK_DIRS) link_directories(${ADDITIONAL_LINK_DIRS}) endif(ADDITIONAL_LINK_DIRS) -# Disable Debug compiler flags for PrismParser to lessen memory consumption during compilation -SET_SOURCE_FILES_PROPERTIES(${PROJECT_SOURCE_DIR}/src/storm/parser/PrismParser.cpp PROPERTIES COMPILE_FLAGS -g0) ############################################################################### ## @@ -72,7 +70,7 @@ add_dependencies(storm copy_resources_headers) add_dependencies(binaries storm-main) # installation -install(TARGETS storm RUNTIME DESTINATION bin LIBRARY DESTINATION lib) -install(TARGETS storm-main RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) +install(TARGETS storm EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib) +install(TARGETS storm-main EXPORT storm_Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib OPTIONAL) install(DIRECTORY ${CMAKE_BINARY_DIR}/include/ DESTINATION include/storm FILES_MATCHING PATTERN "*.h") diff --git a/src/storm/adapters/Z3ExpressionAdapter.cpp b/src/storm/adapters/Z3ExpressionAdapter.cpp index c7e27756d..8a7f55bf5 100644 --- a/src/storm/adapters/Z3ExpressionAdapter.cpp +++ b/src/storm/adapters/Z3ExpressionAdapter.cpp @@ -1,5 +1,7 @@ #include "storm/adapters/Z3ExpressionAdapter.h" +#include + #include "storm/storage/expressions/Expressions.h" #include "storm/storage/expressions/ExpressionManager.h" #include "storm/utility/macros.h" @@ -12,6 +14,15 @@ namespace storm { namespace adapters { #ifdef STORM_HAVE_Z3 + +#ifdef STORM_Z3_API_USES_STANDARD_INTEGERS + typedef int64_t Z3_SIGNED_INTEGER; + typedef uint64_t Z3_UNSIGNED_INTEGER; +#else + typedef long long Z3_SIGNED_INTEGER; + typedef unsigned long long Z3_UNSIGNED_INTEGER; +#endif + Z3ExpressionAdapter::Z3ExpressionAdapter(storm::expressions::ExpressionManager& manager, z3::context& context) : manager(manager), context(context), additionalAssertions(), variableToExpressionMapping() { // Intentionally left empty. } @@ -133,17 +144,17 @@ namespace storm { case Z3_OP_ANUM: // Arithmetic numeral. if (expr.is_int() && expr.is_const()) { - long long value; + Z3_SIGNED_INTEGER value; if (Z3_get_numeral_int64(expr.ctx(), expr, &value)) { return manager.integer(value); } else { STORM_LOG_THROW(false, storm::exceptions::ExpressionEvaluationException, "Failed to convert Z3 expression. Expression is constant integer and value does not fit into 64-bit integer."); } } else if (expr.is_real() && expr.is_const()) { - long long num; - long long den; + Z3_SIGNED_INTEGER num; + Z3_SIGNED_INTEGER den; if (Z3_get_numeral_rational_int64(expr.ctx(), expr, &num, &den)) { - return manager.rational(storm::utility::convertNumber((int_fast64_t) num) / storm::utility::convertNumber((int_fast64_t) den)); + return manager.rational(storm::utility::convertNumber(static_cast(num)) / storm::utility::convertNumber(static_cast(den))); } else { return manager.rational(storm::utility::convertNumber(std::string(Z3_get_numeral_string(expr.ctx(), expr)))); } @@ -304,7 +315,7 @@ namespace storm { return cacheIt->second; } - z3::expr result = context.int_val(static_cast(expression.getValue())); + z3::expr result = context.int_val(static_cast(expression.getValue())); expressionCache.emplace(&expression, result); return result; diff --git a/src/storm/api/builder.h b/src/storm/api/builder.h index 715e6e1ac..8aa2e2e17 100644 --- a/src/storm/api/builder.h +++ b/src/storm/api/builder.h @@ -1,8 +1,8 @@ #pragma once -#include "storm/parser/AutoParser.h" -#include "storm/parser/DirectEncodingParser.h" -#include "storm/parser/ImcaMarkovAutomatonParser.h" +#include "storm-parsers/parser/AutoParser.h" +#include "storm-parsers/parser/DirectEncodingParser.h" +#include "storm-parsers/parser/ImcaMarkovAutomatonParser.h" #include "storm/storage/SymbolicModelDescription.h" diff --git a/src/storm/api/export.cpp b/src/storm/api/export.cpp index 193ec0668..80d45ed2c 100644 --- a/src/storm/api/export.cpp +++ b/src/storm/api/export.cpp @@ -1,20 +1,9 @@ #include "storm/api/export.h" +#include "storm/storage/jani/JaniLocationExpander.h" namespace storm { namespace api { - void exportJaniModel(storm::jani::Model const& model, std::vector const& properties, std::string const& filename) { - auto janiSettings = storm::settings::getModule(); - - if (janiSettings.isExportAsStandardJaniSet()) { - storm::jani::Model normalisedModel = model; - normalisedModel.makeStandardJaniCompliant(); - storm::jani::JsonExporter::toFile(normalisedModel, properties, filename); - } else { - storm::jani::JsonExporter::toFile(model, properties, filename); - } - } - void exportJaniModelAsDot(storm::jani::Model const& model, std::string const& filename) { std::ofstream out; storm::utility::openFile(filename, out); diff --git a/src/storm/api/export.h b/src/storm/api/export.h index 3ba81525d..39a3ba1b3 100644 --- a/src/storm/api/export.h +++ b/src/storm/api/export.h @@ -1,20 +1,19 @@ #pragma once -#include "storm/storage/jani/JSONExporter.h" - - #include "storm/settings/SettingsManager.h" -#include "storm/settings/modules/JaniExportSettings.h" #include "storm/utility/DirectEncodingExporter.h" #include "storm/utility/file.h" #include "storm/utility/macros.h" namespace storm { + + namespace jani { + class Model; + } + namespace api { - void exportJaniModel(storm::jani::Model const& model, std::vector const& properties, std::string const& filename); - void exportJaniModelAsDot(storm::jani::Model const& model, std::string const& filename); template diff --git a/src/storm/api/properties.cpp b/src/storm/api/properties.cpp index f6a2d00e3..e5010d8b9 100644 --- a/src/storm/api/properties.cpp +++ b/src/storm/api/properties.cpp @@ -1,6 +1,5 @@ #include "storm/api/properties.h" -#include "storm/parser/FormulaParser.h" #include "storm/storage/SymbolicModelDescription.h" #include "storm/storage/prism/Program.h" @@ -13,57 +12,7 @@ namespace storm { namespace api { - - boost::optional> parsePropertyFilter(std::string const& propertyFilter) { - if (propertyFilter == "all") { - return boost::none; - } - std::vector propertyNames = storm::utility::cli::parseCommaSeparatedStrings(propertyFilter); - std::set propertyNameSet(propertyNames.begin(), propertyNames.end()); - return propertyNameSet; - } - std::vector parseProperties(storm::parser::FormulaParser& formulaParser, std::string const& inputString, boost::optional> const& propertyFilter) { - // If the given property looks like a file (containing a dot and there exists a file with that name), - // we try to parse it as a file, otherwise we assume it's a property. - std::vector properties; - if (inputString.find(".") != std::string::npos && std::ifstream(inputString).good()) { - properties = formulaParser.parseFromFile(inputString); - } else { - properties = formulaParser.parseFromString(inputString); - } - - return filterProperties(properties, propertyFilter); - } - - std::vector parseProperties(std::string const& inputString, boost::optional> const& propertyFilter) { - auto exprManager = std::make_shared(); - storm::parser::FormulaParser formulaParser(exprManager); - return parseProperties(formulaParser, inputString, propertyFilter); - } - - std::vector parsePropertiesForJaniModel(std::string const& inputString, storm::jani::Model const& model, boost::optional> const& propertyFilter) { - storm::parser::FormulaParser formulaParser(model.getManager().getSharedPointer()); - auto formulas = parseProperties(formulaParser, inputString, propertyFilter); - return substituteConstantsInProperties(formulas, model.getConstantsSubstitution()); - } - - std::vector parsePropertiesForPrismProgram(std::string const& inputString, storm::prism::Program const& program, boost::optional> const& propertyFilter) { - storm::parser::FormulaParser formulaParser(program); - auto formulas = parseProperties(formulaParser, inputString, propertyFilter); - return substituteConstantsInProperties(formulas, program.getConstantsSubstitution()); - } - - std::vector parsePropertiesForSymbolicModelDescription(std::string const& inputString, storm::storage::SymbolicModelDescription const& modelDescription, boost::optional> const& propertyFilter) { - std::vector result; - if (modelDescription.isPrismProgram()) { - result = storm::api::parsePropertiesForPrismProgram(inputString, modelDescription.asPrismProgram(), propertyFilter); - } else { - STORM_LOG_ASSERT(modelDescription.isJaniModel(), "Unknown model description type."); - result = storm::api::parsePropertiesForJaniModel(inputString, modelDescription.asJaniModel(), propertyFilter); - } - return result; - } std::vector substituteConstantsInProperties(std::vector const& properties, std::map const& substitution) { std::vector preprocessedProperties; @@ -78,6 +27,11 @@ namespace storm { std::set const& propertyNameSet = propertyFilter.get(); std::vector result; std::set reducedPropertyNames; + + if (propertyNameSet.empty()) { + STORM_LOG_WARN("Filtering all properties."); + } + for (auto const& property : properties) { if (propertyNameSet.find(property.getName()) != propertyNameSet.end()) { result.push_back(property); diff --git a/src/storm/api/properties.h b/src/storm/api/properties.h index c52073105..c0c878f48 100644 --- a/src/storm/api/properties.h +++ b/src/storm/api/properties.h @@ -8,9 +8,7 @@ #include namespace storm { - namespace parser { - class FormulaParser; - } + namespace jani { class Property; class Model; @@ -30,14 +28,6 @@ namespace storm { } namespace api { - boost::optional> parsePropertyFilter(std::string const& propertyFilter); - - // Parsing properties. - std::vector parseProperties(storm::parser::FormulaParser& formulaParser, std::string const& inputString, boost::optional> const& propertyFilter = boost::none); - std::vector parseProperties(std::string const& inputString, boost::optional> const& propertyFilter = boost::none); - std::vector parsePropertiesForPrismProgram(std::string const& inputString, storm::prism::Program const& program, boost::optional> const& propertyFilter = boost::none); - std::vector parsePropertiesForJaniModel(std::string const& inputString, storm::jani::Model const& model, boost::optional> const& propertyFilter = boost::none); - std::vector parsePropertiesForSymbolicModelDescription(std::string const& inputString, storm::storage::SymbolicModelDescription const& modelDescription, boost::optional> const& propertyFilter = boost::none); // Process properties. std::vector substituteConstantsInProperties(std::vector const& properties, std::map const& substitution); diff --git a/src/storm/api/storm.h b/src/storm/api/storm.h index ffc4c75a6..0048bc315 100644 --- a/src/storm/api/storm.h +++ b/src/storm/api/storm.h @@ -1,10 +1,8 @@ #pragma once -#include "storm/api/model_descriptions.h" #include "storm/api/properties.h" #include "storm/api/builder.h" #include "storm/api/bisimulation.h" #include "storm/api/transformation.h" #include "storm/api/verification.h" -#include "storm/api/counterexamples.h" #include "storm/api/export.h" diff --git a/src/storm/api/transformation.h b/src/storm/api/transformation.h index 204e283db..d26f32ea1 100644 --- a/src/storm/api/transformation.h +++ b/src/storm/api/transformation.h @@ -62,14 +62,14 @@ namespace storm { * Transforms the given symbolic model to a sparse model. */ template - std::shared_ptr> transformSymbolicToSparseModel(std::shared_ptr> const& symbolicModel) { + std::shared_ptr> transformSymbolicToSparseModel(std::shared_ptr> const& symbolicModel, std::vector> const& formulas = std::vector>()) { switch (symbolicModel->getType()) { case storm::models::ModelType::Dtmc: - return storm::transformer::SymbolicDtmcToSparseDtmcTransformer().translate(*symbolicModel->template as>()); + return storm::transformer::SymbolicDtmcToSparseDtmcTransformer().translate(*symbolicModel->template as>(), formulas); case storm::models::ModelType::Mdp: - return storm::transformer::SymbolicMdpToSparseMdpTransformer::translate(*symbolicModel->template as>()); + return storm::transformer::SymbolicMdpToSparseMdpTransformer::translate(*symbolicModel->template as>(), formulas); case storm::models::ModelType::Ctmc: - return storm::transformer::SymbolicCtmcToSparseCtmcTransformer::translate(*symbolicModel->template as>()); + return storm::transformer::SymbolicCtmcToSparseCtmcTransformer::translate(*symbolicModel->template as>(), formulas); default: STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Transformation of symbolic " << symbolicModel->getType() << " to sparse model is not supported."); } diff --git a/src/storm/builder/BuilderOptions.cpp b/src/storm/builder/BuilderOptions.cpp index 33152ca36..d4d729d21 100644 --- a/src/storm/builder/BuilderOptions.cpp +++ b/src/storm/builder/BuilderOptions.cpp @@ -36,7 +36,7 @@ namespace storm { return boost::get(labelOrExpression); } - BuilderOptions::BuilderOptions(bool buildAllRewardModels, bool buildAllLabels) : buildAllRewardModels(buildAllRewardModels), buildAllLabels(buildAllLabels), buildChoiceLabels(false), buildStateValuations(false), buildChoiceOrigins(false), explorationChecks(false), addOutOfBoundsState(false), showProgress(false), showProgressDelay(0) { + BuilderOptions::BuilderOptions(bool buildAllRewardModels, bool buildAllLabels) : buildAllRewardModels(buildAllRewardModels), buildAllLabels(buildAllLabels), buildChoiceLabels(false), buildStateValuations(false), buildChoiceOrigins(false), explorationChecks(false), addOverlappingGuardsLabel(false), addOutOfBoundsState(false), showProgress(false), showProgressDelay(0) { // Intentionally left empty. } @@ -164,6 +164,10 @@ namespace storm { return addOutOfBoundsState; } + bool BuilderOptions::isAddOverlappingGuardLabelSet() const { + return addOverlappingGuardsLabel; + } + BuilderOptions& BuilderOptions::setBuildAllRewardModels(bool newValue) { buildAllRewardModels = newValue; return *this; @@ -238,5 +242,10 @@ namespace storm { return *this; } + BuilderOptions& BuilderOptions::setAddOverlappingGuardsLabel(bool newValue) { + addOverlappingGuardsLabel = newValue; + return *this; + } + } } diff --git a/src/storm/builder/BuilderOptions.h b/src/storm/builder/BuilderOptions.h index c6106172b..904d45de6 100644 --- a/src/storm/builder/BuilderOptions.h +++ b/src/storm/builder/BuilderOptions.h @@ -108,6 +108,7 @@ namespace storm { bool isExplorationChecksSet() const; bool isShowProgressSet() const; bool isAddOutOfBoundsStateSet() const; + bool isAddOverlappingGuardLabelSet() const; uint64_t getShowProgressDelay() const; /** @@ -164,6 +165,12 @@ namespace storm { */ BuilderOptions& setAddOutOfBoundsState(bool newValue = true); + /** + * Should a state be labelled for overlapping guards + * @param newValue the new value (default true) + */ + BuilderOptions& setAddOverlappingGuardsLabel(bool newValue = true); + private: /// A flag that indicates whether all reward models are to be built. In this case, the reward model names are @@ -194,10 +201,13 @@ namespace storm { // A flag that indicates whether or not to generate the information from which parts of the model specification // each choice originates. bool buildChoiceOrigins; - + /// A flag that stores whether exploration checks are to be performed. bool explorationChecks; + /// A flag for states with overlapping guards + bool addOverlappingGuardsLabel; + /// A flag indicating that the an additional state for out of bounds should be created. bool addOutOfBoundsState; diff --git a/src/storm/builder/DdJaniModelBuilder.cpp b/src/storm/builder/DdJaniModelBuilder.cpp index 091c3fe6d..4260c7c7f 100644 --- a/src/storm/builder/DdJaniModelBuilder.cpp +++ b/src/storm/builder/DdJaniModelBuilder.cpp @@ -653,6 +653,40 @@ namespace storm { ActionDd multiplyTransitions(storm::dd::Add const& factor) const { return ActionDd(guard, transitions * factor, transientEdgeAssignments, localNondeterminismVariables, variableToWritingFragment, illegalFragment); } + + ActionDd add(ActionDd const& other) const { + storm::dd::Bdd newGuard = this->guard || other.guard; + storm::dd::Add newTransitions = this->transitions + other.transitions; + + // Join the transient edge assignments. + std::map> newTransientEdgeAssignments(this->transientEdgeAssignments); + for (auto const& entry : other.transientEdgeAssignments) { + auto it = newTransientEdgeAssignments.find(entry.first); + if (it == newTransientEdgeAssignments.end()) { + newTransientEdgeAssignments[entry.first] = entry.second; + } else { + it->second += entry.second; + } + } + + std::pair newLocalNondeterminismVariables = std::make_pair(std::min(this->localNondeterminismVariables.first, other.localNondeterminismVariables.first), std::max(this->localNondeterminismVariables.second, other.localNondeterminismVariables.second)); + + // Join variable-to-writing-fragment maps. + std::map> newVariableToWritingFragment(this->variableToWritingFragment); + for (auto const& entry : other.variableToWritingFragment) { + auto it = newVariableToWritingFragment.find(entry.first); + if (it == newVariableToWritingFragment.end()) { + newVariableToWritingFragment[entry.first] = entry.second; + } else { + it->second |= entry.second; + } + } + + // Join illegal fragments. + storm::dd::Bdd newIllegalFragment = this->illegalFragment || other.illegalFragment; + + return ActionDd(newGuard, newTransitions, newTransientEdgeAssignments, newLocalNondeterminismVariables, newVariableToWritingFragment, newIllegalFragment); + } bool isInputEnabled() const { return inputEnabled; @@ -975,7 +1009,16 @@ namespace storm { // Finally, combine (potentially) multiple action DDs. for (auto const& actionDds : actions) { - ActionDd combinedAction = actionDds.second.size() > 1 ? combineUnsynchronizedActions(actionDds.second) : actionDds.second.front(); + ActionDd combinedAction; + if (actionDds.first == silentMarkovianActionIdentification) { + // For the Markovian transitions, we can simply add the actions. + combinedAction = actionDds.second.front(); + for (uint64_t i = 1; i < actionDds.second.size(); ++i) { + combinedAction = combinedAction.add(actionDds.second[i]); + } + } else { + combinedAction = actionDds.second.size() > 1 ? combineUnsynchronizedActions(actionDds.second) : actionDds.second.front(); + } result.actions[actionDds.first] = combinedAction; result.extendLocalNondeterminismVariables(combinedAction.getLocalNondeterminismVariables()); } @@ -1333,7 +1376,7 @@ namespace storm { } else if (modelType == storm::jani::ModelType::MDP || modelType == storm::jani::ModelType::LTS) { return combineEdgesToActionNondeterministic(edgeDds, localNondeterminismVariableOffset); } else if (modelType == storm::jani::ModelType::MA) { - if (instantiation.isMarkovian()){ + if (instantiation.isMarkovian()) { return combineEdgesToActionDeterministic(edgeDds); } else { return combineEdgesToActionNondeterministic(edgeDds, localNondeterminismVariableOffset); diff --git a/src/storm/builder/DdPrismModelBuilder.cp b/src/storm/builder/DdPrismModelBuilder.cp deleted file mode 100644 index 26308f45b..000000000 --- a/src/storm/builder/DdPrismModelBuilder.cp +++ /dev/null @@ -1,1429 +0,0 @@ -#include "storm/builder/DdPrismModelBuilder.h" - -#include - -#include "storm/models/symbolic/Dtmc.h" -#include "storm/models/symbolic/Ctmc.h" -#include "storm/models/symbolic/Mdp.h" -#include "storm/models/symbolic/StandardRewardModel.h" - -#include "storm/settings/SettingsManager.h" - -#include "storm/exceptions/InvalidStateException.h" -#include "storm/exceptions/NotSupportedException.h" -#include "storm/exceptions/InvalidArgumentException.h" - -#include "storm/utility/prism.h" -#include "storm/utility/math.h" -#include "storm/utility/dd.h" - -#include "storm/storage/dd/DdManager.h" -#include "storm/storage/prism/Program.h" -#include "storm/storage/prism/Compositions.h" -#include "storm/storage/dd/Add.h" -#include "storm/storage/dd/cudd/CuddAddIterator.h" -#include "storm/storage/dd/Bdd.h" - -#include "storm/settings/modules/CoreSettings.h" - -namespace storm { - namespace builder { - - template - class DdPrismModelBuilder::GenerationInformation { - public: - GenerationInformation(storm::prism::Program const& program) : program(program), manager(std::make_shared>()), rowMetaVariables(), variableToRowMetaVariableMap(std::make_shared>()), rowExpressionAdapter(std::make_shared>(manager, variableToRowMetaVariableMap)), columnMetaVariables(), variableToColumnMetaVariableMap((std::make_shared>())), columnExpressionAdapter(std::make_shared>(manager, variableToColumnMetaVariableMap)), rowColumnMetaVariablePairs(), nondeterminismMetaVariables(), variableToIdentityMap(), allGlobalVariables(), moduleToIdentityMap() { - // Initializes variables and identity DDs. - createMetaVariablesAndIdentities(); - } - - // The program that is currently translated. - storm::prism::Program const& program; - - // The manager used to build the decision diagrams. - std::shared_ptr> manager; - - // The meta variables for the row encoding. - std::set rowMetaVariables; - std::shared_ptr> variableToRowMetaVariableMap; - std::shared_ptr> rowExpressionAdapter; - - // The meta variables for the column encoding. - std::set columnMetaVariables; - std::shared_ptr> variableToColumnMetaVariableMap; - std::shared_ptr> columnExpressionAdapter; - - // All pairs of row/column meta variables. - std::vector> rowColumnMetaVariablePairs; - - // The meta variables used to encode the nondeterminism. - std::vector nondeterminismMetaVariables; - - // The meta variables used to encode the synchronization. - std::vector synchronizationMetaVariables; - - // A set of all variables used for encoding the nondeterminism (i.e. nondetermism + synchronization - // variables). This is handy to abstract from this variable set. - std::set allNondeterminismVariables; - - // As set of all variables used for encoding the synchronization. - std::set allSynchronizationMetaVariables; - - // DDs representing the identity for each variable. - std::map> variableToIdentityMap; - - // A set of all meta variables that correspond to global variables. - std::set allGlobalVariables; - - // DDs representing the identity for each module. - std::map> moduleToIdentityMap; - - // DDs representing the valid ranges of the variables of each module. - std::map> moduleToRangeMap; - - private: - /*! - * Creates the required meta variables and variable/module identities. - */ - void createMetaVariablesAndIdentities() { - // Add synchronization variables. - for (auto const& actionIndex : program.getSynchronizingActionIndices()) { - std::pair variablePair = manager->addMetaVariable(program.getActionName(actionIndex)); - synchronizationMetaVariables.push_back(variablePair.first); - allSynchronizationMetaVariables.insert(variablePair.first); - allNondeterminismVariables.insert(variablePair.first); - } - - // Add nondeterminism variables (number of modules + number of commands). - uint_fast64_t numberOfNondeterminismVariables = program.getModules().size(); - for (auto const& module : program.getModules()) { - numberOfNondeterminismVariables += module.getNumberOfCommands(); - } - for (uint_fast64_t i = 0; i < numberOfNondeterminismVariables; ++i) { - std::pair variablePair = manager->addMetaVariable("nondet" + std::to_string(i)); - nondeterminismMetaVariables.push_back(variablePair.first); - allNondeterminismVariables.insert(variablePair.first); - } - - // Create meta variables for global program variables. - for (storm::prism::IntegerVariable const& integerVariable : program.getGlobalIntegerVariables()) { - int_fast64_t low = integerVariable.getLowerBoundExpression().evaluateAsInt(); - int_fast64_t high = integerVariable.getUpperBoundExpression().evaluateAsInt(); - std::pair variablePair = manager->addMetaVariable(integerVariable.getName(), low, high); - - STORM_LOG_TRACE("Created meta variables for global integer variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); - - rowMetaVariables.insert(variablePair.first); - variableToRowMetaVariableMap->emplace(integerVariable.getExpressionVariable(), variablePair.first); - - columnMetaVariables.insert(variablePair.second); - variableToColumnMetaVariableMap->emplace(integerVariable.getExpressionVariable(), variablePair.second); - - storm::dd::Add variableIdentity = manager->template getIdentity(variablePair.first).equals(manager->template getIdentity(variablePair.second)).template toAdd() * manager->getRange(variablePair.first).template toAdd() * manager->getRange(variablePair.second).template toAdd(); - variableToIdentityMap.emplace(integerVariable.getExpressionVariable(), variableIdentity); - rowColumnMetaVariablePairs.push_back(variablePair); - - allGlobalVariables.insert(integerVariable.getExpressionVariable()); - } - for (storm::prism::BooleanVariable const& booleanVariable : program.getGlobalBooleanVariables()) { - std::pair variablePair = manager->addMetaVariable(booleanVariable.getName()); - - STORM_LOG_TRACE("Created meta variables for global boolean variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); - - rowMetaVariables.insert(variablePair.first); - variableToRowMetaVariableMap->emplace(booleanVariable.getExpressionVariable(), variablePair.first); - - columnMetaVariables.insert(variablePair.second); - variableToColumnMetaVariableMap->emplace(booleanVariable.getExpressionVariable(), variablePair.second); - - storm::dd::Add variableIdentity = manager->template getIdentity(variablePair.first).equals(manager->template getIdentity(variablePair.second)).template toAdd(); - variableToIdentityMap.emplace(booleanVariable.getExpressionVariable(), variableIdentity); - - rowColumnMetaVariablePairs.push_back(variablePair); - allGlobalVariables.insert(booleanVariable.getExpressionVariable()); - } - - // Create meta variables for each of the modules' variables. - for (storm::prism::Module const& module : program.getModules()) { - storm::dd::Bdd moduleIdentity = manager->getBddOne(); - storm::dd::Bdd moduleRange = manager->getBddOne(); - - for (storm::prism::IntegerVariable const& integerVariable : module.getIntegerVariables()) { - int_fast64_t low = integerVariable.getLowerBoundExpression().evaluateAsInt(); - int_fast64_t high = integerVariable.getUpperBoundExpression().evaluateAsInt(); - std::pair variablePair = manager->addMetaVariable(integerVariable.getName(), low, high); - STORM_LOG_TRACE("Created meta variables for integer variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); - - rowMetaVariables.insert(variablePair.first); - variableToRowMetaVariableMap->emplace(integerVariable.getExpressionVariable(), variablePair.first); - - columnMetaVariables.insert(variablePair.second); - variableToColumnMetaVariableMap->emplace(integerVariable.getExpressionVariable(), variablePair.second); - - storm::dd::Bdd variableIdentity = manager->template getIdentity(variablePair.first).equals(manager->template getIdentity(variablePair.second)) && manager->getRange(variablePair.first) && manager->getRange(variablePair.second); - variableToIdentityMap.emplace(integerVariable.getExpressionVariable(), variableIdentity.template toAdd()); - moduleIdentity &= variableIdentity; - moduleRange &= manager->getRange(variablePair.first); - - rowColumnMetaVariablePairs.push_back(variablePair); - } - for (storm::prism::BooleanVariable const& booleanVariable : module.getBooleanVariables()) { - std::pair variablePair = manager->addMetaVariable(booleanVariable.getName()); - STORM_LOG_TRACE("Created meta variables for boolean variable: " << variablePair.first.getName() << "[" << variablePair.first.getIndex() << "] and " << variablePair.second.getName() << "[" << variablePair.second.getIndex() << "]"); - - rowMetaVariables.insert(variablePair.first); - variableToRowMetaVariableMap->emplace(booleanVariable.getExpressionVariable(), variablePair.first); - - columnMetaVariables.insert(variablePair.second); - variableToColumnMetaVariableMap->emplace(booleanVariable.getExpressionVariable(), variablePair.second); - - storm::dd::Bdd variableIdentity = manager->template getIdentity(variablePair.first).equals(manager->template getIdentity(variablePair.second)) && manager->getRange(variablePair.first) && manager->getRange(variablePair.second); - variableToIdentityMap.emplace(booleanVariable.getExpressionVariable(), variableIdentity.template toAdd()); - moduleIdentity &= variableIdentity; - moduleRange &= manager->getRange(variablePair.first); - - rowColumnMetaVariablePairs.push_back(variablePair); - } - moduleToIdentityMap[module.getName()] = moduleIdentity.template toAdd(); - moduleToRangeMap[module.getName()] = moduleRange.template toAdd(); - } - } - }; - - template - class ModuleComposer : public storm::prism::CompositionVisitor { - public: - ModuleComposer(typename DdPrismModelBuilder::GenerationInformation& generationInfo) : generationInfo(generationInfo) { - // Intentionally left empty. - } - - typename DdPrismModelBuilder::ModuleDecisionDiagram compose(storm::prism::Composition const& composition) { - return boost::any_cast::ModuleDecisionDiagram>(composition.accept(*this, newSynchronizingActionToOffsetMap())); - } - - std::map newSynchronizingActionToOffsetMap() const { - std::map result; - for (auto const& actionIndex : generationInfo.program.getSynchronizingActionIndices()) { - result[actionIndex] = 0; - } - return result; - } - - std::map updateSynchronizingActionToOffsetMap(typename DdPrismModelBuilder::ModuleDecisionDiagram const& sub, std::map const& oldMapping) const { - std::map result = oldMapping; - for (auto const& action : sub.synchronizingActionToDecisionDiagramMap) { - result[action.first] = action.second.numberOfUsedNondeterminismVariables; - } - return result; - } - - virtual boost::any visit(storm::prism::ModuleComposition const& composition, boost::any const& data) override { - STORM_LOG_TRACE("Translating module '" << composition.getModuleName() << "'."); - std::map const& synchronizingActionToOffsetMap = boost::any_cast const&>(data); - - typename DdPrismModelBuilder::ModuleDecisionDiagram result = DdPrismModelBuilder::createModuleDecisionDiagram(generationInfo, generationInfo.program.getModule(composition.getModuleName()), synchronizingActionToOffsetMap); - - return result; - } - - virtual boost::any visit(storm::prism::RenamingComposition const& composition, boost::any const& data) override { - // Create the mapping from action indices to action indices. - std::map renaming; - for (auto const& namePair : composition.getActionRenaming()) { - STORM_LOG_THROW(generationInfo.program.hasAction(namePair.first), storm::exceptions::InvalidArgumentException, "Composition refers to unknown action '" << namePair.first << "'."); - STORM_LOG_THROW(generationInfo.program.hasAction(namePair.second), storm::exceptions::InvalidArgumentException, "Composition refers to unknown action '" << namePair.second << "'."); - renaming.emplace(generationInfo.program.getActionIndex(namePair.first), generationInfo.program.getActionIndex(namePair.second)); - } - - // Prepare the new offset mapping. - std::map const& synchronizingActionToOffsetMap = boost::any_cast const&>(data); - std::map newSynchronizingActionToOffsetMap = synchronizingActionToOffsetMap; - for (auto const& indexPair : renaming) { - auto it = synchronizingActionToOffsetMap.find(indexPair.second); - STORM_LOG_THROW(it != synchronizingActionToOffsetMap.end(), storm::exceptions::InvalidArgumentException, "Invalid action index " << indexPair.second << "."); - newSynchronizingActionToOffsetMap[indexPair.first] = it->second; - } - - // Then, we translate the subcomposition. - typename DdPrismModelBuilder::ModuleDecisionDiagram sub = boost::any_cast::ModuleDecisionDiagram>(composition.getSubcomposition().accept(*this, newSynchronizingActionToOffsetMap)); - - // Perform the renaming and return result. - return rename(sub, renaming); - } - - virtual boost::any visit(storm::prism::HidingComposition const& composition, boost::any const& data) override { - // Create the mapping from action indices to action indices. - std::set actionIndicesToHide; - for (auto const& action : composition.getActionsToHide()) { - STORM_LOG_THROW(generationInfo.program.hasAction(action), storm::exceptions::InvalidArgumentException, "Composition refers to unknown action '" << action << "'."); - actionIndicesToHide.insert(generationInfo.program.getActionIndex(action)); - } - - // Prepare the new offset mapping. - std::map const& synchronizingActionToOffsetMap = boost::any_cast const&>(data); - std::map newSynchronizingActionToOffsetMap = synchronizingActionToOffsetMap; - for (auto const& index : actionIndicesToHide) { - newSynchronizingActionToOffsetMap[index] = 0; - } - - // Then, we translate the subcomposition. - typename DdPrismModelBuilder::ModuleDecisionDiagram sub = boost::any_cast::ModuleDecisionDiagram>(composition.getSubcomposition().accept(*this, newSynchronizingActionToOffsetMap)); - - // Perform the hiding and return result. - hide(sub, actionIndicesToHide); - return sub; - } - - virtual boost::any visit(storm::prism::SynchronizingParallelComposition const& composition, boost::any const& data) override { - // First, we translate the subcompositions. - typename DdPrismModelBuilder::ModuleDecisionDiagram left = boost::any_cast::ModuleDecisionDiagram>(composition.getLeftSubcomposition().accept(*this, data)); - - // Prepare the new offset mapping. - std::map const& synchronizingActionToOffsetMap = boost::any_cast const&>(data); - std::map newSynchronizingActionToOffsetMap = synchronizingActionToOffsetMap; - for (auto const& action : left.synchronizingActionToDecisionDiagramMap) { - newSynchronizingActionToOffsetMap[action.first] = action.second.numberOfUsedNondeterminismVariables; - } - - typename DdPrismModelBuilder::ModuleDecisionDiagram right = boost::any_cast::ModuleDecisionDiagram>(composition.getRightSubcomposition().accept(*this, newSynchronizingActionToOffsetMap)); - - // Then, determine the action indices on which we need to synchronize. - std::set leftSynchronizationActionIndices = left.getSynchronizingActionIndices(); - std::set rightSynchronizationActionIndices = right.getSynchronizingActionIndices(); - std::set synchronizationActionIndices; - std::set_intersection(leftSynchronizationActionIndices.begin(), leftSynchronizationActionIndices.end(), rightSynchronizationActionIndices.begin(), rightSynchronizationActionIndices.end(), std::inserter(synchronizationActionIndices, synchronizationActionIndices.begin())); - - // Finally, we compose the subcompositions to create the result. - composeInParallel(left, right, synchronizationActionIndices); - return left; - } - - virtual boost::any visit(storm::prism::InterleavingParallelComposition const& composition, boost::any const& data) override { - // First, we translate the subcompositions. - typename DdPrismModelBuilder::ModuleDecisionDiagram left = boost::any_cast::ModuleDecisionDiagram>(composition.getLeftSubcomposition().accept(*this, data)); - - typename DdPrismModelBuilder::ModuleDecisionDiagram right = boost::any_cast::ModuleDecisionDiagram>(composition.getRightSubcomposition().accept(*this, data)); - - // Finally, we compose the subcompositions to create the result. - composeInParallel(left, right, std::set()); - return left; - } - - virtual boost::any visit(storm::prism::RestrictedParallelComposition const& composition, boost::any const& data) override { - // Construct the synchronizing action indices from the synchronizing action names. - std::set synchronizingActionIndices; - for (auto const& action : composition.getSynchronizingActions()) { - synchronizingActionIndices.insert(generationInfo.program.getActionIndex(action)); - } - - // Then, we translate the subcompositions. - typename DdPrismModelBuilder::ModuleDecisionDiagram left = boost::any_cast::ModuleDecisionDiagram>(composition.getLeftSubcomposition().accept(*this, data)); - - // Prepare the new offset mapping. - std::map const& synchronizingActionToOffsetMap = boost::any_cast const&>(data); - std::map newSynchronizingActionToOffsetMap = synchronizingActionToOffsetMap; - for (auto const& actionIndex : synchronizingActionIndices) { - auto it = left.synchronizingActionToDecisionDiagramMap.find(actionIndex); - if (it != left.synchronizingActionToDecisionDiagramMap.end()) { - newSynchronizingActionToOffsetMap[actionIndex] = it->second.numberOfUsedNondeterminismVariables; - } - } - - typename DdPrismModelBuilder::ModuleDecisionDiagram right = boost::any_cast::ModuleDecisionDiagram>(composition.getRightSubcomposition().accept(*this, newSynchronizingActionToOffsetMap)); - - std::set leftSynchronizationActionIndices = left.getSynchronizingActionIndices(); - bool isContainedInLeft = std::includes(leftSynchronizationActionIndices.begin(), leftSynchronizationActionIndices.end(), synchronizingActionIndices.begin(), synchronizingActionIndices.end()); - STORM_LOG_WARN_COND(isContainedInLeft, "Left subcomposition of composition '" << composition << "' does not include all actions over which to synchronize."); - - std::set rightSynchronizationActionIndices = right.getSynchronizingActionIndices(); - bool isContainedInRight = std::includes(rightSynchronizationActionIndices.begin(), rightSynchronizationActionIndices.end(), synchronizingActionIndices.begin(), synchronizingActionIndices.end()); - STORM_LOG_WARN_COND(isContainedInRight, "Right subcomposition of composition '" << composition << "' does not include all actions over which to synchronize."); - - // Finally, we compose the subcompositions to create the result. - composeInParallel(left, right, synchronizingActionIndices); - return left; - } - - private: - /*! - * Hides the actions of the given module according to the given set. As a result, the module is modified in - * place. - */ - void hide(typename DdPrismModelBuilder::ModuleDecisionDiagram& sub, std::set const& actionIndicesToHide) const { - STORM_LOG_TRACE("Hiding actions."); - - for (auto const& actionIndex : actionIndicesToHide) { - auto it = sub.synchronizingActionToDecisionDiagramMap.find(actionIndex); - if (it != sub.synchronizingActionToDecisionDiagramMap.end()) { - sub.independentAction = DdPrismModelBuilder::combineUnsynchronizedActions(generationInfo, sub.independentAction, it->second); - sub.numberOfUsedNondeterminismVariables = std::max(sub.numberOfUsedNondeterminismVariables, sub.independentAction.numberOfUsedNondeterminismVariables); - sub.synchronizingActionToDecisionDiagramMap.erase(it); - } - } - } - - /*! - * Renames the actions of the given module according to the given renaming. - */ - typename DdPrismModelBuilder::ModuleDecisionDiagram rename(typename DdPrismModelBuilder::ModuleDecisionDiagram& sub, std::map const& renaming) const { - STORM_LOG_TRACE("Renaming actions."); - std::map::ActionDecisionDiagram> actionIndexToDdMap; - - // Go through all action DDs with a synchronizing label and rename them if they appear in the renaming. - for (auto& action : sub.synchronizingActionToDecisionDiagramMap) { - auto renamingIt = renaming.find(action.first); - if (renamingIt != renaming.end()) { - // If the action is to be renamed and an action with the target index already exists, we need - // to combine the action DDs. - auto itNewActions = actionIndexToDdMap.find(renamingIt->second); - if (itNewActions != actionIndexToDdMap.end()) { - actionIndexToDdMap[renamingIt->second] = DdPrismModelBuilder::combineUnsynchronizedActions(generationInfo, action.second, itNewActions->second); - - } else { - // In this case, we can simply copy the action over. - actionIndexToDdMap[renamingIt->second] = action.second; - } - } else { - // If the action is not to be renamed, we need to copy it over. However, if some other action - // was renamed to the very same action name before, we need to combine the transitions. - auto itNewActions = actionIndexToDdMap.find(action.first); - if (itNewActions != actionIndexToDdMap.end()) { - actionIndexToDdMap[action.first] = DdPrismModelBuilder::combineUnsynchronizedActions(generationInfo, action.second, itNewActions->second); - } else { - // In this case, we can simply copy the action over. - actionIndexToDdMap[action.first] = action.second; - } - } - } - - return typename DdPrismModelBuilder::ModuleDecisionDiagram(sub.independentAction, actionIndexToDdMap, sub.identity, sub.numberOfUsedNondeterminismVariables); - } - - /*! - * Composes the given modules while synchronizing over the provided action indices. As a result, the first - * module is modified in place and will contain the composition after a call to this method. - */ - void composeInParallel(typename DdPrismModelBuilder::ModuleDecisionDiagram& left, typename DdPrismModelBuilder::ModuleDecisionDiagram& right, std::set const& synchronizationActionIndices) const { - STORM_LOG_TRACE("Composing two modules."); - - // Combine the tau action. - uint_fast64_t numberOfUsedNondeterminismVariables = right.independentAction.numberOfUsedNondeterminismVariables; - left.independentAction = DdPrismModelBuilder::combineUnsynchronizedActions(generationInfo, left.independentAction, right.independentAction, left.identity, right.identity); - numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, left.independentAction.numberOfUsedNondeterminismVariables); - - // Create an empty action for the case where one of the modules does not have a certain action. - typename DdPrismModelBuilder::ActionDecisionDiagram emptyAction(*generationInfo.manager); - - // Treat all non-tau actions of the left module. - for (auto& action : left.synchronizingActionToDecisionDiagramMap) { - // If we need to synchronize over this action index, we try to do so now. - if (synchronizationActionIndices.find(action.first) != synchronizationActionIndices.end()) { - // If we are to synchronize over an action that does not exist in the second module, the result - // is that the synchronization is the empty action. - if (!right.hasSynchronizingAction(action.first)) { - action.second = emptyAction; - } else { - // Otherwise, the actions of the modules are synchronized. - action.second = DdPrismModelBuilder::combineSynchronizingActions(action.second, right.synchronizingActionToDecisionDiagramMap[action.first]); - } - } else { - // If we don't synchronize over this action, we need to construct the interleaving. - - // If both modules contain the action, we need to mutually multiply the other identity. - if (right.hasSynchronizingAction(action.first)) { - action.second = DdPrismModelBuilder::combineUnsynchronizedActions(generationInfo, action.second, right.synchronizingActionToDecisionDiagramMap[action.first], left.identity, right.identity); - } else { - // If only the first module has this action, we need to use a dummy action decision diagram - // for the second module. - action.second = DdPrismModelBuilder::combineUnsynchronizedActions(generationInfo, action.second, emptyAction, left.identity, right.identity); - } - } - numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, action.second.numberOfUsedNondeterminismVariables); - } - - // Treat all non-tau actions of the right module. - for (auto const& actionIndex : right.getSynchronizingActionIndices()) { - // Here, we only need to treat actions that the first module does not have, because we have handled - // this case earlier. - if (!left.hasSynchronizingAction(actionIndex)) { - if (synchronizationActionIndices.find(actionIndex) != synchronizationActionIndices.end()) { - // If we are to synchronize over this action that does not exist in the first module, the - // result is that the synchronization is the empty action. - left.synchronizingActionToDecisionDiagramMap[actionIndex] = emptyAction; - } else { - // If only the second module has this action, we need to use a dummy action decision diagram - // for the first module. - left.synchronizingActionToDecisionDiagramMap[actionIndex] = DdPrismModelBuilder::combineUnsynchronizedActions(generationInfo, emptyAction, right.synchronizingActionToDecisionDiagramMap[actionIndex], left.identity, right.identity); - } - } - numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, left.synchronizingActionToDecisionDiagramMap[actionIndex].numberOfUsedNondeterminismVariables); - } - - // Combine identity matrices. - left.identity = left.identity * right.identity; - - // Keep track of the number of nondeterminism variables used. - left.numberOfUsedNondeterminismVariables = std::max(left.numberOfUsedNondeterminismVariables, numberOfUsedNondeterminismVariables); - } - - typename DdPrismModelBuilder::GenerationInformation& generationInfo; - }; - - template - DdPrismModelBuilder::Options::Options() : buildAllRewardModels(false), rewardModelsToBuild(), buildAllLabels(false), labelsToBuild(), terminalStates(), negatedTerminalStates() { - // Intentionally left empty. - } - - template - DdPrismModelBuilder::Options::Options(storm::logic::Formula const& formula) : buildAllRewardModels(false), rewardModelsToBuild(), buildAllLabels(false), labelsToBuild(std::set()), terminalStates(), negatedTerminalStates() { - this->preserveFormula(formula); - this->setTerminalStatesFromFormula(formula); - } - - template - DdPrismModelBuilder::Options::Options(std::vector> const& formulas) : buildAllRewardModels(false), rewardModelsToBuild(), buildAllLabels(false), labelsToBuild(), terminalStates(), negatedTerminalStates() { - if (formulas.empty()) { - this->buildAllRewardModels = true; - this->buildAllLabels = true; - } else { - for (auto const& formula : formulas) { - this->preserveFormula(*formula); - } - if (formulas.size() == 1) { - this->setTerminalStatesFromFormula(*formulas.front()); - } - } - } - - template - void DdPrismModelBuilder::Options::preserveFormula(storm::logic::Formula const& formula) { - // If we already had terminal states, we need to erase them. - if (terminalStates) { - terminalStates.reset(); - } - if (negatedTerminalStates) { - negatedTerminalStates.reset(); - } - - // If we are not required to build all reward models, we determine the reward models we need to build. - if (!buildAllRewardModels) { - std::set referencedRewardModels = formula.getReferencedRewardModels(); - rewardModelsToBuild.insert(referencedRewardModels.begin(), referencedRewardModels.end()); - } - - // Extract all the labels used in the formula. - std::vector> atomicLabelFormulas = formula.getAtomicLabelFormulas(); - for (auto const& formula : atomicLabelFormulas) { - if (!labelsToBuild) { - labelsToBuild = std::set(); - } - labelsToBuild.get().insert(formula.get()->getLabel()); - } - } - - template - void DdPrismModelBuilder::Options::setTerminalStatesFromFormula(storm::logic::Formula const& formula) { - if (formula.isAtomicExpressionFormula()) { - terminalStates = formula.asAtomicExpressionFormula().getExpression(); - } else if (formula.isAtomicLabelFormula()) { - terminalStates = formula.asAtomicLabelFormula().getLabel(); - } else if (formula.isEventuallyFormula()) { - storm::logic::Formula const& sub = formula.asEventuallyFormula().getSubformula(); - if (sub.isAtomicExpressionFormula() || sub.isAtomicLabelFormula()) { - this->setTerminalStatesFromFormula(sub); - } - } else if (formula.isUntilFormula()) { - storm::logic::Formula const& right = formula.asUntilFormula().getRightSubformula(); - if (right.isAtomicExpressionFormula() || right.isAtomicLabelFormula()) { - this->setTerminalStatesFromFormula(right); - } - storm::logic::Formula const& left = formula.asUntilFormula().getLeftSubformula(); - if (left.isAtomicExpressionFormula()) { - negatedTerminalStates = left.asAtomicExpressionFormula().getExpression(); - } else if (left.isAtomicLabelFormula()) { - negatedTerminalStates = left.asAtomicLabelFormula().getLabel(); - } - } else if (formula.isProbabilityOperatorFormula()) { - storm::logic::Formula const& sub = formula.asProbabilityOperatorFormula().getSubformula(); - if (sub.isEventuallyFormula() || sub.isUntilFormula()) { - this->setTerminalStatesFromFormula(sub); - } - } - } - - template - struct DdPrismModelBuilder::SystemResult { - SystemResult(storm::dd::Add const& allTransitionsDd, DdPrismModelBuilder::ModuleDecisionDiagram const& globalModule, storm::dd::Add const& stateActionDd) : allTransitionsDd(allTransitionsDd), globalModule(globalModule), stateActionDd(stateActionDd) { - // Intentionally left empty. - } - - storm::dd::Add allTransitionsDd; - typename DdPrismModelBuilder::ModuleDecisionDiagram globalModule; - storm::dd::Add stateActionDd; - }; - - template - typename DdPrismModelBuilder::UpdateDecisionDiagram DdPrismModelBuilder::createUpdateDecisionDiagram(GenerationInformation& generationInfo, storm::prism::Module const& module, storm::dd::Add const& guard, storm::prism::Update const& update) { - storm::dd::Add updateDd = generationInfo.manager->template getAddOne(); - - STORM_LOG_TRACE("Translating update " << update); - - // Iterate over all assignments (boolean and integer) and build the DD for it. - std::vector assignments = update.getAssignments(); - std::set assignedVariables; - for (auto const& assignment : assignments) { - // Record the variable as being written. - STORM_LOG_TRACE("Assigning to variable " << generationInfo.variableToRowMetaVariableMap->at(assignment.getVariable()).getName()); - assignedVariables.insert(assignment.getVariable()); - - // Translate the written variable. - auto const& primedMetaVariable = generationInfo.variableToColumnMetaVariableMap->at(assignment.getVariable()); - storm::dd::Add writtenVariable = generationInfo.manager->template getIdentity(primedMetaVariable); - - // Translate the expression that is being assigned. - storm::dd::Add updateExpression = generationInfo.rowExpressionAdapter->translateExpression(assignment.getExpression()); - - // Combine the update expression with the guard. - storm::dd::Add result = updateExpression * guard; - - // Combine the variable and the assigned expression. - storm::dd::Add tmp = result; - result = result.equals(writtenVariable).template toAdd(); - result *= guard; - - // Restrict the transitions to the range of the written variable. - result = result * generationInfo.manager->getRange(primedMetaVariable).template toAdd(); - - updateDd *= result; - } - - // Compute the set of assigned global variables. - std::set assignedGlobalVariables; - std::set_intersection(assignedVariables.begin(), assignedVariables.end(), generationInfo.allGlobalVariables.begin(), generationInfo.allGlobalVariables.end(), std::inserter(assignedGlobalVariables, assignedGlobalVariables.begin())); - - // All unassigned boolean variables need to keep their value. - for (storm::prism::BooleanVariable const& booleanVariable : module.getBooleanVariables()) { - if (assignedVariables.find(booleanVariable.getExpressionVariable()) == assignedVariables.end()) { - STORM_LOG_TRACE("Multiplying identity of variable " << booleanVariable.getName()); - updateDd *= generationInfo.variableToIdentityMap.at(booleanVariable.getExpressionVariable()); - } - } - - // All unassigned integer variables need to keep their value. - for (storm::prism::IntegerVariable const& integerVariable : module.getIntegerVariables()) { - if (assignedVariables.find(integerVariable.getExpressionVariable()) == assignedVariables.end()) { - STORM_LOG_TRACE("Multiplying identity of variable " << integerVariable.getName()); - updateDd *= generationInfo.variableToIdentityMap.at(integerVariable.getExpressionVariable()); - } - } - - return UpdateDecisionDiagram(updateDd, assignedGlobalVariables); - } - - template - typename DdPrismModelBuilder::ActionDecisionDiagram DdPrismModelBuilder::createCommandDecisionDiagram(GenerationInformation& generationInfo, storm::prism::Module const& module, storm::prism::Command const& command) { - STORM_LOG_TRACE("Translating guard " << command.getGuardExpression()); - storm::dd::Add guard = generationInfo.rowExpressionAdapter->translateExpression(command.getGuardExpression()) * generationInfo.moduleToRangeMap[module.getName()]; - STORM_LOG_WARN_COND(!guard.isZero(), "The guard '" << command.getGuardExpression() << "' is unsatisfiable."); - - if (!guard.isZero()) { - // Create the DDs representing the individual updates. - std::vector updateResults; - for (storm::prism::Update const& update : command.getUpdates()) { - updateResults.push_back(createUpdateDecisionDiagram(generationInfo, module, guard, update)); - - STORM_LOG_WARN_COND(!updateResults.back().updateDd.isZero(), "Update '" << update << "' does not have any effect."); - } - - // Start by gathering all variables that were written in at least one update. - std::set globalVariablesInSomeUpdate; - - // If the command is labeled, we have to analyze which portion of the global variables was written by - // any of the updates and make all update results equal w.r.t. this set. If the command is not labeled, - // we can already multiply the identities of all global variables. - if (command.isLabeled()) { - std::for_each(updateResults.begin(), updateResults.end(), [&globalVariablesInSomeUpdate] (UpdateDecisionDiagram const& update) { globalVariablesInSomeUpdate.insert(update.assignedGlobalVariables.begin(), update.assignedGlobalVariables.end()); } ); - } else { - globalVariablesInSomeUpdate = generationInfo.allGlobalVariables; - } - - // Then, multiply the missing identities. - for (auto& updateResult : updateResults) { - std::set missingIdentities; - std::set_difference(globalVariablesInSomeUpdate.begin(), globalVariablesInSomeUpdate.end(), updateResult.assignedGlobalVariables.begin(), updateResult.assignedGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin())); - - for (auto const& variable : missingIdentities) { - STORM_LOG_TRACE("Multiplying identity for variable " << variable.getName() << "[" << variable.getIndex() << "] to update."); - updateResult.updateDd *= generationInfo.variableToIdentityMap.at(variable); - } - } - - // Now combine the update DDs to the command DD. - storm::dd::Add commandDd = generationInfo.manager->template getAddZero(); - auto updateResultsIt = updateResults.begin(); - for (auto updateIt = command.getUpdates().begin(), updateIte = command.getUpdates().end(); updateIt != updateIte; ++updateIt, ++updateResultsIt) { - storm::dd::Add probabilityDd = generationInfo.rowExpressionAdapter->translateExpression(updateIt->getLikelihoodExpression()); - commandDd += updateResultsIt->updateDd * probabilityDd; - } - - return ActionDecisionDiagram(guard, guard * commandDd, globalVariablesInSomeUpdate); - } else { - return ActionDecisionDiagram(*generationInfo.manager); - } - } - - template - typename DdPrismModelBuilder::ActionDecisionDiagram DdPrismModelBuilder::createActionDecisionDiagram(GenerationInformation& generationInfo, storm::prism::Module const& module, uint_fast64_t synchronizationActionIndex, uint_fast64_t nondeterminismVariableOffset) { - std::vector commandDds; - for (storm::prism::Command const& command : module.getCommands()) { - - // Determine whether the command is relevant for the selected action. - bool relevant = (synchronizationActionIndex == 0 && !command.isLabeled()) || (synchronizationActionIndex && command.isLabeled() && command.getActionIndex() == synchronizationActionIndex); - - if (!relevant) { - continue; - } - - STORM_LOG_TRACE("Translating command " << command); - - // At this point, the command is known to be relevant for the action. - commandDds.push_back(createCommandDecisionDiagram(generationInfo, module, command)); - } - - ActionDecisionDiagram result(*generationInfo.manager); - if (!commandDds.empty()) { - switch (generationInfo.program.getModelType()){ - case storm::prism::Program::ModelType::DTMC: - case storm::prism::Program::ModelType::CTMC: - result = combineCommandsToActionMarkovChain(generationInfo, commandDds); - break; - case storm::prism::Program::ModelType::MDP: - result = combineCommandsToActionMDP(generationInfo, commandDds, nondeterminismVariableOffset); - break; - default: - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Cannot translate model of this type."); - } - } - - return result; - } - - template - std::set DdPrismModelBuilder::equalizeAssignedGlobalVariables(GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2) { - // Start by gathering all variables that were written in at least one action DD. - std::set globalVariablesInActionDd; - std::set_union(action1.assignedGlobalVariables.begin(), action1.assignedGlobalVariables.end(), action2.assignedGlobalVariables.begin(), action2.assignedGlobalVariables.end(), std::inserter(globalVariablesInActionDd, globalVariablesInActionDd.begin())); - - std::set missingIdentitiesInAction1; - std::set_difference(globalVariablesInActionDd.begin(), globalVariablesInActionDd.end(), action1.assignedGlobalVariables.begin(), action1.assignedGlobalVariables.end(), std::inserter(missingIdentitiesInAction1, missingIdentitiesInAction1.begin())); - for (auto const& variable : missingIdentitiesInAction1) { - action1.transitionsDd *= generationInfo.variableToIdentityMap.at(variable); - } - - std::set missingIdentitiesInAction2; - std::set_difference(globalVariablesInActionDd.begin(), globalVariablesInActionDd.end(), action1.assignedGlobalVariables.begin(), action1.assignedGlobalVariables.end(), std::inserter(missingIdentitiesInAction2, missingIdentitiesInAction2.begin())); - for (auto const& variable : missingIdentitiesInAction2) { - action2.transitionsDd *= generationInfo.variableToIdentityMap.at(variable); - } - - return globalVariablesInActionDd; - } - - template - std::set DdPrismModelBuilder::equalizeAssignedGlobalVariables(GenerationInformation const& generationInfo, std::vector& actionDds) { - // Start by gathering all variables that were written in at least one action DD. - std::set globalVariablesInActionDd; - for (auto const& commandDd : actionDds) { - globalVariablesInActionDd.insert(commandDd.assignedGlobalVariables.begin(), commandDd.assignedGlobalVariables.end()); - } - - STORM_LOG_TRACE("Equalizing assigned global variables."); - - // Then multiply the transitions of each action with the missing identities. - for (auto& actionDd : actionDds) { - STORM_LOG_TRACE("Equalizing next action."); - std::set missingIdentities; - std::set_difference(globalVariablesInActionDd.begin(), globalVariablesInActionDd.end(), actionDd.assignedGlobalVariables.begin(), actionDd.assignedGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin())); - for (auto const& variable : missingIdentities) { - STORM_LOG_TRACE("Multiplying identity of variable " << variable.getName() << "."); - actionDd.transitionsDd *= generationInfo.variableToIdentityMap.at(variable); - } - } - return globalVariablesInActionDd; - } - - template - typename DdPrismModelBuilder::ActionDecisionDiagram DdPrismModelBuilder::combineCommandsToActionMarkovChain(GenerationInformation& generationInfo, std::vector& commandDds) { - storm::dd::Add allGuards = generationInfo.manager->template getAddZero(); - storm::dd::Add allCommands = generationInfo.manager->template getAddZero(); - storm::dd::Add temporary; - - // Make all command DDs assign to the same global variables. - std::set assignedGlobalVariables = equalizeAssignedGlobalVariables(generationInfo, commandDds); - - // Then combine the commands to the full action DD and multiply missing identities along the way. - for (auto& commandDd : commandDds) { - // Check for overlapping guards. - temporary = commandDd.guardDd * allGuards; - - // Issue a warning if there are overlapping guards in a non-CTMC model. - STORM_LOG_WARN_COND(temporary.isZero() || generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC, "Guard of a command overlaps with previous guards."); - - allGuards += commandDd.guardDd; - allCommands += commandDd.transitionsDd; - } - - return ActionDecisionDiagram(allGuards, allCommands, assignedGlobalVariables); - } - - template - storm::dd::Add DdPrismModelBuilder::encodeChoice(GenerationInformation& generationInfo, uint_fast64_t nondeterminismVariableOffset, uint_fast64_t numberOfBinaryVariables, int_fast64_t value) { - storm::dd::Add result = generationInfo.manager->template getAddZero(); - - STORM_LOG_TRACE("Encoding " << value << " with " << numberOfBinaryVariables << " binary variable(s) starting from offset " << nondeterminismVariableOffset << "."); - - std::map metaVariableNameToValueMap; - for (uint_fast64_t i = nondeterminismVariableOffset; i < nondeterminismVariableOffset + numberOfBinaryVariables; ++i) { - if (value & (1ull << (numberOfBinaryVariables - i - 1))) { - metaVariableNameToValueMap.emplace(generationInfo.nondeterminismMetaVariables[i], 1); - } else { - metaVariableNameToValueMap.emplace(generationInfo.nondeterminismMetaVariables[i], 0); - } - } - - result.setValue(metaVariableNameToValueMap, ValueType(1)); - return result; - } - - template - typename DdPrismModelBuilder::ActionDecisionDiagram DdPrismModelBuilder::combineCommandsToActionMDP(GenerationInformation& generationInfo, std::vector& commandDds, uint_fast64_t nondeterminismVariableOffset) { - storm::dd::Bdd allGuards = generationInfo.manager->getBddZero(); - storm::dd::Add allCommands = generationInfo.manager->template getAddZero(); - - // Make all command DDs assign to the same global variables. - std::set assignedGlobalVariables = equalizeAssignedGlobalVariables(generationInfo, commandDds); - - // Sum all guards, so we can read off the maximal number of nondeterministic choices in any given state. - storm::dd::Add sumOfGuards = generationInfo.manager->template getAddZero(); - for (auto const& commandDd : commandDds) { - sumOfGuards += commandDd.guardDd; - allGuards |= commandDd.guardDd.toBdd(); - } - uint_fast64_t maxChoices = static_cast(sumOfGuards.getMax()); - - STORM_LOG_TRACE("Found " << maxChoices << " local choices."); - - // Depending on the maximal number of nondeterminstic choices, we need to use some variables to encode the nondeterminism. - if (maxChoices == 0) { - return ActionDecisionDiagram(*generationInfo.manager); - } else if (maxChoices == 1) { - // Sum up all commands. - for (auto const& commandDd : commandDds) { - allCommands += commandDd.transitionsDd; - } - return ActionDecisionDiagram(sumOfGuards, allCommands, assignedGlobalVariables); - } else { - // Calculate number of required variables to encode the nondeterminism. - uint_fast64_t numberOfBinaryVariables = static_cast(std::ceil(storm::utility::math::log2(maxChoices))); - - storm::dd::Bdd equalsNumberOfChoicesDd; - std::vector> choiceDds(maxChoices, generationInfo.manager->template getAddZero()); - std::vector> remainingDds(maxChoices, generationInfo.manager->getBddZero()); - - for (uint_fast64_t currentChoices = 1; currentChoices <= maxChoices; ++currentChoices) { - // Determine the set of states with exactly currentChoices choices. - equalsNumberOfChoicesDd = sumOfGuards.equals(generationInfo.manager->getConstant(ValueType(currentChoices))); - - // If there is no such state, continue with the next possible number of choices. - if (equalsNumberOfChoicesDd.isZero()) { - continue; - } - - // Reset the previously used intermediate storage. - for (uint_fast64_t j = 0; j < currentChoices; ++j) { - choiceDds[j] = generationInfo.manager->template getAddZero(); - remainingDds[j] = equalsNumberOfChoicesDd; - } - - for (std::size_t j = 0; j < commandDds.size(); ++j) { - // Check if command guard overlaps with equalsNumberOfChoicesDd. That is, there are states with exactly currentChoices - // choices such that one outgoing choice is given by the j-th command. - storm::dd::Bdd guardChoicesIntersection = commandDds[j].guardDd.toBdd() && equalsNumberOfChoicesDd; - - // If there is no such state, continue with the next command. - if (guardChoicesIntersection.isZero()) { - continue; - } - - // Split the nondeterministic choices. - for (uint_fast64_t k = 0; k < currentChoices; ++k) { - // Calculate the overlapping part of command guard and the remaining DD. - storm::dd::Bdd remainingGuardChoicesIntersection = guardChoicesIntersection && remainingDds[k]; - - // Check if we can add some overlapping parts to the current index. - if (!remainingGuardChoicesIntersection.isZero()) { - // Remove overlapping parts from the remaining DD. - remainingDds[k] = remainingDds[k] && !remainingGuardChoicesIntersection; - - // Combine the overlapping part of the guard with command updates and add it to the resulting DD. - choiceDds[k] += remainingGuardChoicesIntersection.template toAdd() * commandDds[j].transitionsDd; - } - - // Remove overlapping parts from the command guard DD - guardChoicesIntersection = guardChoicesIntersection && !remainingGuardChoicesIntersection; - - // If the guard DD has become equivalent to false, we can stop here. - if (guardChoicesIntersection.isZero()) { - break; - } - } - } - - // Add the meta variables that encode the nondeterminisim to the different choices. - for (uint_fast64_t j = 0; j < currentChoices; ++j) { - allCommands += encodeChoice(generationInfo, nondeterminismVariableOffset, numberOfBinaryVariables, j) * choiceDds[j]; - } - - // Delete currentChoices out of overlapping DD - sumOfGuards = sumOfGuards * (!equalsNumberOfChoicesDd).template toAdd(); - } - - return ActionDecisionDiagram(allGuards.template toAdd(), allCommands, assignedGlobalVariables, nondeterminismVariableOffset + numberOfBinaryVariables); - } - } - - template - typename DdPrismModelBuilder::ActionDecisionDiagram DdPrismModelBuilder::combineSynchronizingActions(ActionDecisionDiagram const& action1, ActionDecisionDiagram const& action2) { - std::set assignedGlobalVariables; - std::set_union(action1.assignedGlobalVariables.begin(), action1.assignedGlobalVariables.end(), action2.assignedGlobalVariables.begin(), action2.assignedGlobalVariables.end(), std::inserter(assignedGlobalVariables, assignedGlobalVariables.begin())); - return ActionDecisionDiagram(action1.guardDd * action2.guardDd, action1.transitionsDd * action2.transitionsDd, assignedGlobalVariables, std::max(action1.numberOfUsedNondeterminismVariables, action2.numberOfUsedNondeterminismVariables)); - } - - template - typename DdPrismModelBuilder::ActionDecisionDiagram DdPrismModelBuilder::combineUnsynchronizedActions(GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2, storm::dd::Add const& identityDd1, storm::dd::Add const& identityDd2) { - - // First extend the action DDs by the other identities. - STORM_LOG_TRACE("Multiplying identities to combine unsynchronized actions."); - action1.transitionsDd = action1.transitionsDd * identityDd2; - action2.transitionsDd = action2.transitionsDd * identityDd1; - - // Then combine the extended action DDs. - return combineUnsynchronizedActions(generationInfo, action1, action2); - } - - template - typename DdPrismModelBuilder::ActionDecisionDiagram DdPrismModelBuilder::combineUnsynchronizedActions(GenerationInformation const& generationInfo, ActionDecisionDiagram& action1, ActionDecisionDiagram& action2) { - STORM_LOG_TRACE("Combining unsynchronized actions."); - - // Make both action DDs write to the same global variables. - std::set assignedGlobalVariables = equalizeAssignedGlobalVariables(generationInfo, action1, action2); - - if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC || generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC) { - return ActionDecisionDiagram(action1.guardDd + action2.guardDd, action1.transitionsDd + action2.transitionsDd, assignedGlobalVariables, 0); - } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) { - if (action1.transitionsDd.isZero()) { - return ActionDecisionDiagram(action2.guardDd, action2.transitionsDd, assignedGlobalVariables, action2.numberOfUsedNondeterminismVariables); - } else if (action2.transitionsDd.isZero()) { - return ActionDecisionDiagram(action1.guardDd, action1.transitionsDd, assignedGlobalVariables, action1.numberOfUsedNondeterminismVariables); - } - - // Bring both choices to the same number of variables that encode the nondeterminism. - uint_fast64_t numberOfUsedNondeterminismVariables = std::max(action1.numberOfUsedNondeterminismVariables, action2.numberOfUsedNondeterminismVariables); - if (action1.numberOfUsedNondeterminismVariables > action2.numberOfUsedNondeterminismVariables) { - storm::dd::Add nondeterminismEncoding = generationInfo.manager->template getAddOne(); - - for (uint_fast64_t i = action2.numberOfUsedNondeterminismVariables; i < action1.numberOfUsedNondeterminismVariables; ++i) { - nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd(); - } - action2.transitionsDd *= nondeterminismEncoding; - } else if (action2.numberOfUsedNondeterminismVariables > action1.numberOfUsedNondeterminismVariables) { - storm::dd::Add nondeterminismEncoding = generationInfo.manager->template getAddOne(); - - for (uint_fast64_t i = action1.numberOfUsedNondeterminismVariables; i < action2.numberOfUsedNondeterminismVariables; ++i) { - nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd(); - } - action1.transitionsDd *= nondeterminismEncoding; - } - - // Add a new variable that resolves the nondeterminism between the two choices. - storm::dd::Add combinedTransitions = generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[numberOfUsedNondeterminismVariables], 1).ite(action2.transitionsDd, action1.transitionsDd); - - return ActionDecisionDiagram((action1.guardDd.toBdd() || action2.guardDd.toBdd()).template toAdd(), combinedTransitions, assignedGlobalVariables, numberOfUsedNondeterminismVariables + 1); - } else { - STORM_LOG_THROW(false, storm::exceptions::InvalidStateException, "Illegal model type."); - } - } - - template - typename DdPrismModelBuilder::ModuleDecisionDiagram DdPrismModelBuilder::createModuleDecisionDiagram(GenerationInformation& generationInfo, storm::prism::Module const& module, std::map const& synchronizingActionToOffsetMap) { - // Start by creating the action DD for the independent action. - ActionDecisionDiagram independentActionDd = createActionDecisionDiagram(generationInfo, module, 0, 0); - uint_fast64_t numberOfUsedNondeterminismVariables = independentActionDd.numberOfUsedNondeterminismVariables; - - // Create module DD for all synchronizing actions of the module. - std::map actionIndexToDdMap; - for (auto const& actionIndex : module.getSynchronizingActionIndices()) { - STORM_LOG_TRACE("Creating DD for action '" << actionIndex << "'."); - ActionDecisionDiagram tmp = createActionDecisionDiagram(generationInfo, module, actionIndex, synchronizingActionToOffsetMap.at(actionIndex)); - numberOfUsedNondeterminismVariables = std::max(numberOfUsedNondeterminismVariables, tmp.numberOfUsedNondeterminismVariables); - actionIndexToDdMap.emplace(actionIndex, tmp); - } - - return ModuleDecisionDiagram(independentActionDd, actionIndexToDdMap, generationInfo.moduleToIdentityMap.at(module.getName()), numberOfUsedNondeterminismVariables); - } - - template - storm::dd::Add DdPrismModelBuilder::getSynchronizationDecisionDiagram(GenerationInformation& generationInfo, uint_fast64_t actionIndex) { - storm::dd::Add synchronization = generationInfo.manager->template getAddOne(); - if (actionIndex != 0) { - for (uint_fast64_t i = 0; i < generationInfo.synchronizationMetaVariables.size(); ++i) { - if ((actionIndex - 1) == i) { - synchronization *= generationInfo.manager->getEncoding(generationInfo.synchronizationMetaVariables[i], 1).template toAdd(); - } else { - synchronization *= generationInfo.manager->getEncoding(generationInfo.synchronizationMetaVariables[i], 0).template toAdd(); - } - } - } else { - for (uint_fast64_t i = 0; i < generationInfo.synchronizationMetaVariables.size(); ++i) { - synchronization *= generationInfo.manager->getEncoding(generationInfo.synchronizationMetaVariables[i], 0).template toAdd(); - } - } - return synchronization; - } - - template - storm::dd::Add DdPrismModelBuilder::createSystemFromModule(GenerationInformation& generationInfo, ModuleDecisionDiagram const& module) { - // If the model is an MDP, we need to encode the nondeterminism using additional variables. - if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) { - storm::dd::Add result = generationInfo.manager->template getAddZero(); - - // First, determine the highest number of nondeterminism variables that is used in any action and make - // all actions use the same amout of nondeterminism variables. - uint_fast64_t numberOfUsedNondeterminismVariables = module.numberOfUsedNondeterminismVariables; - - // Compute missing global variable identities in independent action. - std::set missingIdentities; - std::set_difference(generationInfo.allGlobalVariables.begin(), generationInfo.allGlobalVariables.end(), module.independentAction.assignedGlobalVariables.begin(), module.independentAction.assignedGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin())); - storm::dd::Add identityEncoding = generationInfo.manager->template getAddOne(); - for (auto const& variable : missingIdentities) { - STORM_LOG_TRACE("Multiplying identity of global variable " << variable.getName() << " to independent action."); - identityEncoding *= generationInfo.variableToIdentityMap.at(variable); - } - - // Add variables to independent action DD. - storm::dd::Add nondeterminismEncoding = generationInfo.manager->template getAddOne(); - for (uint_fast64_t i = module.independentAction.numberOfUsedNondeterminismVariables; i < numberOfUsedNondeterminismVariables; ++i) { - nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd(); - } - result = identityEncoding * module.independentAction.transitionsDd * nondeterminismEncoding; - - // Add variables to synchronized action DDs. - std::map> synchronizingActionToDdMap; - for (auto const& synchronizingAction : module.synchronizingActionToDecisionDiagramMap) { - // Compute missing global variable identities in synchronizing actions. - missingIdentities = std::set(); - std::set_difference(generationInfo.allGlobalVariables.begin(), generationInfo.allGlobalVariables.end(), synchronizingAction.second.assignedGlobalVariables.begin(), synchronizingAction.second.assignedGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin())); - identityEncoding = generationInfo.manager->template getAddOne(); - for (auto const& variable : missingIdentities) { - STORM_LOG_TRACE("Multiplying identity of global variable " << variable.getName() << " to synchronizing action '" << synchronizingAction.first << "'."); - identityEncoding *= generationInfo.variableToIdentityMap.at(variable); - } - - nondeterminismEncoding = generationInfo.manager->template getAddOne(); - for (uint_fast64_t i = synchronizingAction.second.numberOfUsedNondeterminismVariables; i < numberOfUsedNondeterminismVariables; ++i) { - nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0).template toAdd(); - } - synchronizingActionToDdMap.emplace(synchronizingAction.first, identityEncoding * synchronizingAction.second.transitionsDd * nondeterminismEncoding); - } - - // Add variables for synchronization. - result *= getSynchronizationDecisionDiagram(generationInfo); - - for (auto& synchronizingAction : synchronizingActionToDdMap) { - synchronizingAction.second *= getSynchronizationDecisionDiagram(generationInfo, synchronizingAction.first); - } - - // Now, we can simply add all synchronizing actions to the result. - for (auto const& synchronizingAction : synchronizingActionToDdMap) { - result += synchronizingAction.second; - } - - return result; - } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC || generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC) { - // Simply add all actions, but make sure to include the missing global variable identities. - - // Compute missing global variable identities in independent action. - std::set missingIdentities; - std::set_difference(generationInfo.allGlobalVariables.begin(), generationInfo.allGlobalVariables.end(), module.independentAction.assignedGlobalVariables.begin(), module.independentAction.assignedGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin())); - storm::dd::Add identityEncoding = generationInfo.manager->template getAddOne(); - for (auto const& variable : missingIdentities) { - STORM_LOG_TRACE("Multiplying identity of global variable " << variable.getName() << " to independent action."); - identityEncoding *= generationInfo.variableToIdentityMap.at(variable); - } - - storm::dd::Add result = identityEncoding * module.independentAction.transitionsDd; - - for (auto const& synchronizingAction : module.synchronizingActionToDecisionDiagramMap) { - // Compute missing global variable identities in synchronizing actions. - missingIdentities = std::set(); - std::set_difference(generationInfo.allGlobalVariables.begin(), generationInfo.allGlobalVariables.end(), synchronizingAction.second.assignedGlobalVariables.begin(), synchronizingAction.second.assignedGlobalVariables.end(), std::inserter(missingIdentities, missingIdentities.begin())); - identityEncoding = generationInfo.manager->template getAddOne(); - for (auto const& variable : missingIdentities) { - STORM_LOG_TRACE("Multiplying identity of global variable " << variable.getName() << " to synchronizing action '" << synchronizingAction.first << "'."); - identityEncoding *= generationInfo.variableToIdentityMap.at(variable); - } - - result += identityEncoding * synchronizingAction.second.transitionsDd; - } - return result; - } else { - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Illegal model type."); - } - } - - template - typename DdPrismModelBuilder::SystemResult DdPrismModelBuilder::createSystemDecisionDiagram(GenerationInformation& generationInfo) { - ModuleComposer composer(generationInfo); - ModuleDecisionDiagram system = composer.compose(generationInfo.program.specifiesSystemComposition() ? generationInfo.program.getSystemCompositionConstruct().getSystemComposition() : *generationInfo.program.getDefaultSystemComposition()); - - storm::dd::Add result = createSystemFromModule(generationInfo, system); - - // Create an auxiliary DD that is used later during the construction of reward models. - STORM_LOG_TRACE("Counting: " << result.getNonZeroCount() << " // " << result.getNodeCount()); - storm::dd::Add stateActionDd = result.sumAbstract(generationInfo.columnMetaVariables); - - // For DTMCs, we normalize each row to 1 (to account for non-determinism). - if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC) { - result = result / stateActionDd; - } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) { - // For MDPs, we need to throw away the nondeterminism variables from the generation information that - // were never used. - for (uint_fast64_t index = system.numberOfUsedNondeterminismVariables; index < generationInfo.nondeterminismMetaVariables.size(); ++index) { - generationInfo.allNondeterminismVariables.erase(generationInfo.nondeterminismMetaVariables[index]); - } - generationInfo.nondeterminismMetaVariables.resize(system.numberOfUsedNondeterminismVariables); - } - - return SystemResult(result, system, stateActionDd); - } - - template - storm::models::symbolic::StandardRewardModel DdPrismModelBuilder::createRewardModelDecisionDiagrams(GenerationInformation& generationInfo, storm::prism::RewardModel const& rewardModel, ModuleDecisionDiagram const& globalModule, storm::dd::Add const& reachableStatesAdd, storm::dd::Add const& stateActionDd) { - - // Start by creating the state reward vector. - boost::optional> stateRewards; - if (rewardModel.hasStateRewards()) { - stateRewards = generationInfo.manager->template getAddZero(); - - for (auto const& stateReward : rewardModel.getStateRewards()) { - storm::dd::Add states = generationInfo.rowExpressionAdapter->translateExpression(stateReward.getStatePredicateExpression()); - storm::dd::Add rewards = generationInfo.rowExpressionAdapter->translateExpression(stateReward.getRewardValueExpression()); - - // Restrict the rewards to those states that satisfy the condition. - rewards = reachableStatesAdd * states * rewards; - - // Perform some sanity checks. - STORM_LOG_WARN_COND(rewards.getMin() >= 0, "The reward model assigns negative rewards to some states."); - STORM_LOG_WARN_COND(!rewards.isZero(), "The reward model does not assign any non-zero rewards."); - - // Add the rewards to the global state reward vector. - stateRewards.get() += rewards; - } - } - - // Next, build the state-action reward vector. - boost::optional> stateActionRewards; - if (rewardModel.hasStateActionRewards()) { - stateActionRewards = generationInfo.manager->template getAddZero(); - - for (auto const& stateActionReward : rewardModel.getStateActionRewards()) { - storm::dd::Add states = generationInfo.rowExpressionAdapter->translateExpression(stateActionReward.getStatePredicateExpression()); - storm::dd::Add rewards = generationInfo.rowExpressionAdapter->translateExpression(stateActionReward.getRewardValueExpression()); - storm::dd::Add synchronization = generationInfo.manager->template getAddOne(); - - if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) { - synchronization = getSynchronizationDecisionDiagram(generationInfo, stateActionReward.getActionIndex()); - } - ActionDecisionDiagram const& actionDd = stateActionReward.isLabeled() ? globalModule.synchronizingActionToDecisionDiagramMap.at(stateActionReward.getActionIndex()) : globalModule.independentAction; - states *= actionDd.guardDd * reachableStatesAdd; - storm::dd::Add stateActionRewardDd = synchronization * states * rewards; - - // If we are building the state-action rewards for an MDP, we need to make sure that the encoding - // of the nondeterminism is present in the reward vector, so we ne need to multiply it with the - // legal state-actions. - if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) { - stateActionRewardDd *= stateActionDd; - } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC) { - // For CTMCs, we need to multiply the entries with the exit rate of the corresponding action. - stateActionRewardDd *= actionDd.transitionsDd.sumAbstract(generationInfo.columnMetaVariables); - } - - // Perform some sanity checks. - STORM_LOG_WARN_COND(stateActionRewardDd.getMin() >= 0, "The reward model assigns negative rewards to some states."); - STORM_LOG_WARN_COND(!stateActionRewardDd.isZero(), "The reward model does not assign any non-zero rewards."); - - // Add the rewards to the global transition reward matrix. - stateActionRewards.get() += stateActionRewardDd; - } - - // Scale state-action rewards for DTMCs and CTMCs. - if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC || generationInfo.program.getModelType() == storm::prism::Program::ModelType::CTMC) { - stateActionRewards.get() /= stateActionDd; - } - } - - // Then build the transition reward matrix. - boost::optional> transitionRewards; - if (rewardModel.hasTransitionRewards()) { - transitionRewards = generationInfo.manager->template getAddZero(); - - for (auto const& transitionReward : rewardModel.getTransitionRewards()) { - storm::dd::Add sourceStates = generationInfo.rowExpressionAdapter->translateExpression(transitionReward.getSourceStatePredicateExpression()); - storm::dd::Add targetStates = generationInfo.rowExpressionAdapter->translateExpression(transitionReward.getTargetStatePredicateExpression()); - storm::dd::Add rewards = generationInfo.rowExpressionAdapter->translateExpression(transitionReward.getRewardValueExpression()); - - storm::dd::Add synchronization = generationInfo.manager->template getAddOne(); - - storm::dd::Add transitions; - if (transitionReward.isLabeled()) { - if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) { - synchronization = getSynchronizationDecisionDiagram(generationInfo, transitionReward.getActionIndex()); - } - transitions = globalModule.synchronizingActionToDecisionDiagramMap.at(transitionReward.getActionIndex()).transitionsDd; - } else { - if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) { - synchronization = getSynchronizationDecisionDiagram(generationInfo); - } - transitions = globalModule.independentAction.transitionsDd; - } - - storm::dd::Add transitionRewardDd = synchronization * sourceStates * targetStates * rewards; - if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC) { - // For DTMCs we need to keep the weighting for the scaling that follows. - transitionRewardDd = transitions * transitionRewardDd; - } else { - // For all other model types, we do not scale the rewards. - transitionRewardDd = transitions.notZero().template toAdd() * transitionRewardDd; - } - - // Perform some sanity checks. - STORM_LOG_WARN_COND(transitionRewardDd.getMin() >= 0, "The reward model assigns negative rewards to some states."); - STORM_LOG_WARN_COND(!transitionRewardDd.isZero(), "The reward model does not assign any non-zero rewards."); - - // Add the rewards to the global transition reward matrix. - transitionRewards.get() += transitionRewardDd; - } - - // Scale transition rewards for DTMCs. - if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC) { - transitionRewards.get() /= stateActionDd; - } - } - - return storm::models::symbolic::StandardRewardModel(stateRewards, stateActionRewards, transitionRewards); - } - - template - std::shared_ptr> DdPrismModelBuilder::build(storm::prism::Program const& program, Options const& options) { - if (program.hasUndefinedConstants()) { - std::vector> undefinedConstants = program.getUndefinedConstants(); - std::stringstream stream; - bool printComma = false; - for (auto const& constant : undefinedConstants) { - if (printComma) { - stream << ", "; - } else { - printComma = true; - } - stream << constant.get().getName() << " (" << constant.get().getType() << ")"; - } - stream << "."; - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str()); - } - - STORM_LOG_TRACE("Building representation of program:" << std::endl << program << std::endl); - - // Start by initializing the structure used for storing all information needed during the model generation. - // In particular, this creates the meta variables used to encode the model. - GenerationInformation generationInfo(program); - - SystemResult system = createSystemDecisionDiagram(generationInfo); - storm::dd::Add transitionMatrix = system.allTransitionsDd; - - ModuleDecisionDiagram const& globalModule = system.globalModule; - storm::dd::Add stateActionDd = system.stateActionDd; - - // If we were asked to treat some states as terminal states, we cut away their transitions now. - storm::dd::Bdd terminalStatesBdd = generationInfo.manager->getBddZero(); - if (options.terminalStates || options.negatedTerminalStates) { - std::map constantsSubstitution = program.getConstantsSubstitution(); - - if (options.terminalStates) { - storm::expressions::Expression terminalExpression; - if (options.terminalStates.get().type() == typeid(storm::expressions::Expression)) { - terminalExpression = boost::get(options.terminalStates.get()); - } else { - std::string const& labelName = boost::get(options.terminalStates.get()); - if (program.hasLabel(labelName)) { - terminalExpression = program.getLabelExpression(labelName); - } else { - STORM_LOG_THROW(labelName == "init" || labelName == "deadlock", storm::exceptions::InvalidArgumentException, "Terminal states refer to illegal label '" << labelName << "'."); - } - } - - if (terminalExpression.isInitialized()) { - // If the expression refers to constants of the model, we need to substitute them. - terminalExpression = terminalExpression.substitute(constantsSubstitution); - - STORM_LOG_TRACE("Making the states satisfying " << terminalExpression << " terminal."); - terminalStatesBdd = generationInfo.rowExpressionAdapter->translateExpression(terminalExpression).toBdd(); - } - } - if (options.negatedTerminalStates) { - storm::expressions::Expression negatedTerminalExpression; - if (options.negatedTerminalStates.get().type() == typeid(storm::expressions::Expression)) { - negatedTerminalExpression = boost::get(options.negatedTerminalStates.get()); - } else { - std::string const& labelName = boost::get(options.negatedTerminalStates.get()); - if (program.hasLabel(labelName)) { - negatedTerminalExpression = program.getLabelExpression(labelName); - } else { - STORM_LOG_THROW(labelName == "init" || labelName == "deadlock", storm::exceptions::InvalidArgumentException, "Terminal states refer to illegal label '" << labelName << "'."); - } - } - - if (negatedTerminalExpression.isInitialized()) { - // If the expression refers to constants of the model, we need to substitute them. - negatedTerminalExpression = negatedTerminalExpression.substitute(constantsSubstitution); - - STORM_LOG_TRACE("Making the states *not* satisfying " << negatedTerminalExpression << " terminal."); - terminalStatesBdd |= !generationInfo.rowExpressionAdapter->translateExpression(negatedTerminalExpression).toBdd(); - } - } - - transitionMatrix *= (!terminalStatesBdd).template toAdd(); - } - - std::cout << "trans matrix has size " << transitionMatrix.getNodeCount() << std::endl; - - // Cut the transitions and rewards to the reachable fragment of the state space. - storm::dd::Bdd initialStates = createInitialStatesDecisionDiagram(generationInfo); - - storm::dd::Bdd transitionMatrixBdd = transitionMatrix.notZero(); - if (program.getModelType() == storm::prism::Program::ModelType::MDP) { - transitionMatrixBdd = transitionMatrixBdd.existsAbstract(generationInfo.allNondeterminismVariables); - } - - storm::dd::Bdd reachableStates = storm::utility::dd::computeReachableStates(initialStates, transitionMatrixBdd, generationInfo.rowMetaVariables, generationInfo.columnMetaVariables); - storm::dd::Add reachableStatesAdd = reachableStates.template toAdd(); - transitionMatrix *= reachableStatesAdd; - stateActionDd *= reachableStatesAdd; - - // Detect deadlocks and 1) fix them if requested 2) throw an error otherwise. - storm::dd::Bdd statesWithTransition = transitionMatrixBdd.existsAbstract(generationInfo.columnMetaVariables); - storm::dd::Bdd deadlockStates = reachableStates && !statesWithTransition; - - // If there are deadlocks, either fix them or raise an error. - if (!deadlockStates.isZero()) { - // If we need to fix deadlocks, we do so now. - if (!storm::settings::getModule().isDontFixDeadlocksSet()) { - STORM_LOG_INFO("Fixing deadlocks in " << deadlockStates.getNonZeroCount() << " states. The first three of these states are: "); - - storm::dd::Add deadlockStatesAdd = deadlockStates.template toAdd(); - uint_fast64_t count = 0; - for (auto it = deadlockStatesAdd.begin(), ite = deadlockStatesAdd.end(); it != ite && count < 3; ++it, ++count) { - STORM_LOG_INFO((*it).first.toPrettyString(generationInfo.rowMetaVariables) << std::endl); - } - - if (program.getModelType() == storm::prism::Program::ModelType::DTMC || program.getModelType() == storm::prism::Program::ModelType::CTMC) { - storm::dd::Add identity = globalModule.identity; - - // Make sure that global variables do not change along the introduced self-loops. - for (auto const& var : generationInfo.allGlobalVariables) { - identity *= generationInfo.variableToIdentityMap.at(var); - } - - // For DTMCs, we can simply add the identity of the global module for all deadlock states. - transitionMatrix += deadlockStatesAdd * identity; - } else if (program.getModelType() == storm::prism::Program::ModelType::MDP) { - // For MDPs, however, we need to select an action associated with the self-loop, if we do not - // want to attach a lot of self-loops to the deadlock states. - storm::dd::Add action = generationInfo.manager->template getAddOne(); - for (auto const& metaVariable : generationInfo.allNondeterminismVariables) { - action *= generationInfo.manager->template getIdentity(metaVariable); - } - // Make sure that global variables do not change along the introduced self-loops. - for (auto const& var : generationInfo.allGlobalVariables) { - action *= generationInfo.variableToIdentityMap.at(var); - } - transitionMatrix += deadlockStatesAdd * globalModule.identity * action; - } - } else { - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The model contains " << deadlockStates.getNonZeroCount() << " deadlock states. Please unset the option to not fix deadlocks, if you want to fix them automatically."); - } - } - - // Reduce the deadlock states by the states that we did simply not explore. - deadlockStates = deadlockStates && !terminalStatesBdd; - - // Now build the reward models. - std::vector> selectedRewardModels; - - // First, we make sure that all selected reward models actually exist. - for (auto const& rewardModelName : options.rewardModelsToBuild) { - STORM_LOG_THROW(rewardModelName.empty() || program.hasRewardModel(rewardModelName), storm::exceptions::InvalidArgumentException, "Model does not possess a reward model with the name '" << rewardModelName << "'."); - } - - for (auto const& rewardModel : program.getRewardModels()) { - if (options.buildAllRewardModels || options.rewardModelsToBuild.find(rewardModel.getName()) != options.rewardModelsToBuild.end()) { - std::cout << "build all? " << buildAllRewardModels << std::endl; - selectedRewardModels.push_back(rewardModel); - } - } - // If no reward model was selected until now and a referenced reward model appears to be unique, we build - // the only existing reward model (given that no explicit name was given for the referenced reward model). - if (selectedRewardModels.empty() && program.getNumberOfRewardModels() == 1 && options.rewardModelsToBuild.size() == 1 && *options.rewardModelsToBuild.begin() == "") { - selectedRewardModels.push_back(program.getRewardModel(0)); - } - - std::unordered_map> rewardModels; - for (auto const& rewardModel : selectedRewardModels) { - rewardModels.emplace(rewardModel.get().getName(), createRewardModelDecisionDiagrams(generationInfo, rewardModel.get(), globalModule, reachableStatesAdd, stateActionDd)); - } - - // Build the labels that can be accessed as a shortcut. - std::map labelToExpressionMapping; - for (auto const& label : program.getLabels()) { - labelToExpressionMapping.emplace(label.getName(), label.getStatePredicateExpression()); - } - - if (program.getModelType() == storm::prism::Program::ModelType::DTMC) { - return std::shared_ptr>(new storm::models::symbolic::Dtmc(generationInfo.manager, reachableStates, initialStates, deadlockStates, transitionMatrix, generationInfo.rowMetaVariables, generationInfo.rowExpressionAdapter, generationInfo.columnMetaVariables, generationInfo.columnExpressionAdapter, generationInfo.rowColumnMetaVariablePairs, labelToExpressionMapping, rewardModels)); - } else if (program.getModelType() == storm::prism::Program::ModelType::CTMC) { - return std::shared_ptr>(new storm::models::symbolic::Ctmc(generationInfo.manager, reachableStates, initialStates, deadlockStates, transitionMatrix, generationInfo.rowMetaVariables, generationInfo.rowExpressionAdapter, generationInfo.columnMetaVariables, generationInfo.columnExpressionAdapter, generationInfo.rowColumnMetaVariablePairs, labelToExpressionMapping, rewardModels)); - } else if (program.getModelType() == storm::prism::Program::ModelType::MDP) { - return std::shared_ptr>(new storm::models::symbolic::Mdp(generationInfo.manager, reachableStates, initialStates, deadlockStates, transitionMatrix, generationInfo.rowMetaVariables, generationInfo.rowExpressionAdapter, generationInfo.columnMetaVariables, generationInfo.columnExpressionAdapter, generationInfo.rowColumnMetaVariablePairs, generationInfo.allNondeterminismVariables, labelToExpressionMapping, rewardModels)); - } else { - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Invalid model type."); - } - } - - template - storm::dd::Bdd DdPrismModelBuilder::createInitialStatesDecisionDiagram(GenerationInformation& generationInfo) { - storm::dd::Bdd initialStates = generationInfo.rowExpressionAdapter->translateExpression(generationInfo.program.getInitialStatesExpression()).toBdd(); - - for (auto const& metaVariable : generationInfo.rowMetaVariables) { - initialStates &= generationInfo.manager->getRange(metaVariable); - } - - return initialStates; - } - - // Explicitly instantiate the symbolic model builder. - template class DdPrismModelBuilder; - template class DdPrismModelBuilder; - - } // namespace adapters -} // namespace storm - - diff --git a/src/storm/builder/ExplicitModelBuilder.cpp b/src/storm/builder/ExplicitModelBuilder.cpp index 986e04ae7..70e548ae4 100644 --- a/src/storm/builder/ExplicitModelBuilder.cpp +++ b/src/storm/builder/ExplicitModelBuilder.cpp @@ -275,6 +275,7 @@ namespace storm { // (a) the transition matrix // (b) the initial states // (c) the hash map storing the mapping states -> ids + // (d) fix remapping for state-generation labels // Fix (a). transitionMatrixBuilder.replaceColumns(remapping, 0); @@ -287,6 +288,8 @@ namespace storm { // Fix (c). this->stateStorage.stateToId.remap([&remapping] (StateType const& state) { return remapping[state]; } ); + + this->generator->remapStateIds([&remapping] (StateType const& state) { return remapping[state]; }); } } diff --git a/src/storm/environment/Environment.cpp b/src/storm/environment/Environment.cpp index 0836de3f7..6d5d3ca2b 100644 --- a/src/storm/environment/Environment.cpp +++ b/src/storm/environment/Environment.cpp @@ -1,6 +1,8 @@ #include "storm/environment/Environment.h" #include "storm/environment/SubEnvironment.h" #include "storm/environment/solver/SolverEnvironment.h" +#include "storm/environment/modelchecker/ModelCheckerEnvironment.h" + namespace storm { @@ -12,11 +14,28 @@ namespace storm { // Intentionally left empty. } + Environment::Environment(Environment const& other) : internalEnv(other.internalEnv) { + // Intentionally left empty. + } + + Environment& Environment::operator=(Environment const& other) { + internalEnv = other.internalEnv; + return *this; + } + SolverEnvironment& Environment::solver() { - return solverEnvironment.get(); + return internalEnv.get().solverEnvironment.get(); } SolverEnvironment const& Environment::solver() const { - return solverEnvironment.get(); + return internalEnv.get().solverEnvironment.get(); + } + + ModelCheckerEnvironment& Environment::modelchecker() { + return internalEnv.get().modelcheckerEnvironment.get(); + } + + ModelCheckerEnvironment const& Environment::modelchecker() const { + return internalEnv.get().modelcheckerEnvironment.get(); } } \ No newline at end of file diff --git a/src/storm/environment/Environment.h b/src/storm/environment/Environment.h index 988ca34c6..30ac31803 100644 --- a/src/storm/environment/Environment.h +++ b/src/storm/environment/Environment.h @@ -6,19 +6,30 @@ namespace storm { // Forward declare sub-environments class SolverEnvironment; + class ModelCheckerEnvironment; + + // Avoid implementing ugly copy constructors for environment by using an internal environment. + struct InternalEnvironment { + SubEnvironment solverEnvironment; + SubEnvironment modelcheckerEnvironment; + }; class Environment { public: Environment(); virtual ~Environment(); + Environment(Environment const& other); + Environment& operator=(Environment const& other); SolverEnvironment& solver(); SolverEnvironment const& solver() const; + ModelCheckerEnvironment& modelchecker(); + ModelCheckerEnvironment const& modelchecker() const; private: - SubEnvironment solverEnvironment; + SubEnvironment internalEnv; }; } diff --git a/src/storm/environment/SubEnvironment.cpp b/src/storm/environment/SubEnvironment.cpp index f11b2b1bf..5226fdca4 100644 --- a/src/storm/environment/SubEnvironment.cpp +++ b/src/storm/environment/SubEnvironment.cpp @@ -1,4 +1,9 @@ -#include +#include + +#include "storm/environment/Environment.h" +#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h" +#include "storm/environment/modelchecker/ModelCheckerEnvironment.h" + #include "storm/environment/solver/SolverEnvironment.h" #include "storm/environment/solver/EigenSolverEnvironment.h" #include "storm/environment/solver/GmmxxSolverEnvironment.h" @@ -11,31 +16,49 @@ namespace storm { template - SubEnvironment::SubEnvironment() : subEnv(std::make_unique()) { + SubEnvironment::SubEnvironment() : subEnv(nullptr) { // Intentionally left empty } template - SubEnvironment::SubEnvironment(SubEnvironment const& other) : subEnv(new EnvironmentType(*other.subEnv)) { + SubEnvironment::SubEnvironment(SubEnvironment const& other) : subEnv(other.subEnv ? new EnvironmentType(*other.subEnv) : nullptr) { // Intentionally left empty } template SubEnvironment& SubEnvironment::operator=(SubEnvironment const& other) { - subEnv = std::make_unique(*other.subEnv); + if (other.subEnv) { + subEnv = std::make_unique(*other.subEnv); + } else { + subEnv.reset(); + } return *this; } template EnvironmentType const& SubEnvironment::get() const { + assertInitialized(); return *subEnv; } template EnvironmentType& SubEnvironment::get() { + assertInitialized(); return *subEnv; } + template + void SubEnvironment::assertInitialized() const { + if (!subEnv) { + subEnv = std::make_unique(); + } + } + + template class SubEnvironment; + + template class SubEnvironment; + template class SubEnvironment; + template class SubEnvironment; template class SubEnvironment; template class SubEnvironment; diff --git a/src/storm/environment/SubEnvironment.h b/src/storm/environment/SubEnvironment.h index 2b6ecbf87..b1858936d 100644 --- a/src/storm/environment/SubEnvironment.h +++ b/src/storm/environment/SubEnvironment.h @@ -16,8 +16,8 @@ namespace storm { EnvironmentType& get(); private: - std::unique_ptr subEnv; + void assertInitialized() const; + mutable std::unique_ptr subEnv; }; } - diff --git a/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp b/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp new file mode 100644 index 000000000..f0d917a28 --- /dev/null +++ b/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp @@ -0,0 +1,31 @@ +#include "storm/environment/modelchecker/ModelCheckerEnvironment.h" + +#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h" + +#include "storm/settings/SettingsManager.h" +#include "storm/utility/macros.h" + +#include "storm/exceptions/InvalidEnvironmentException.h" +#include "storm/exceptions/UnexpectedException.h" + + +namespace storm { + + ModelCheckerEnvironment::ModelCheckerEnvironment() { + // Intentionally left empty + } + + ModelCheckerEnvironment::~ModelCheckerEnvironment() { + // Intentionally left empty + } + + MultiObjectiveModelCheckerEnvironment& ModelCheckerEnvironment::multi() { + return multiObjectiveModelCheckerEnvironment.get(); + } + + MultiObjectiveModelCheckerEnvironment const& ModelCheckerEnvironment::multi() const { + return multiObjectiveModelCheckerEnvironment.get(); + } +} + + diff --git a/src/storm/environment/modelchecker/ModelCheckerEnvironment.h b/src/storm/environment/modelchecker/ModelCheckerEnvironment.h new file mode 100644 index 000000000..2ec1eebd8 --- /dev/null +++ b/src/storm/environment/modelchecker/ModelCheckerEnvironment.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "storm/environment/Environment.h" +#include "storm/environment/SubEnvironment.h" + +namespace storm { + + // Forward declare subenvironments + class MultiObjectiveModelCheckerEnvironment; + + class ModelCheckerEnvironment { + public: + + ModelCheckerEnvironment(); + ~ModelCheckerEnvironment(); + + MultiObjectiveModelCheckerEnvironment& multi(); + MultiObjectiveModelCheckerEnvironment const& multi() const; + + + private: + SubEnvironment multiObjectiveModelCheckerEnvironment; + }; +} + diff --git a/src/storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.cpp b/src/storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.cpp new file mode 100644 index 000000000..92b51fde8 --- /dev/null +++ b/src/storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.cpp @@ -0,0 +1,100 @@ +#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h" + +#include "storm/settings/SettingsManager.h" +#include "storm/settings/modules/MultiObjectiveSettings.h" +#include "storm/utility/constants.h" +#include "storm/utility/macros.h" + +namespace storm { + + MultiObjectiveModelCheckerEnvironment::MultiObjectiveModelCheckerEnvironment() { + auto const& multiobjectiveSettings = storm::settings::getModule(); + method = multiobjectiveSettings.getMultiObjectiveMethod(); + if (multiobjectiveSettings.isExportPlotSet()) { + plotPathUnderApprox = multiobjectiveSettings.getExportPlotDirectory() + "underapproximation.csv"; + plotPathOverApprox = multiobjectiveSettings.getExportPlotDirectory() + "overapproximation.csv"; + plotPathParetoPoints = multiobjectiveSettings.getExportPlotDirectory() + "paretopoints.csv"; + } + + precision = storm::utility::convertNumber(multiobjectiveSettings.getPrecision()); + if (multiobjectiveSettings.isMaxStepsSet()) { + maxSteps = multiobjectiveSettings.getMaxSteps(); + } + } + + MultiObjectiveModelCheckerEnvironment::~MultiObjectiveModelCheckerEnvironment() { + // Intentionally left empty + } + + storm::modelchecker::multiobjective::MultiObjectiveMethod const& MultiObjectiveModelCheckerEnvironment::getMethod() const { + return this->method; + } + + void MultiObjectiveModelCheckerEnvironment::setMethod(storm::modelchecker::multiobjective::MultiObjectiveMethod value) { + this->method = value; + } + + bool MultiObjectiveModelCheckerEnvironment::isExportPlotSet() const { + return this->plotPathUnderApprox.is_initialized() || this->plotPathOverApprox.is_initialized() || this->plotPathParetoPoints.is_initialized(); + } + + boost::optional MultiObjectiveModelCheckerEnvironment::getPlotPathUnderApproximation() const { + return plotPathUnderApprox; + } + + void MultiObjectiveModelCheckerEnvironment::setPlotPathUnderApproximation(std::string const& path) { + plotPathUnderApprox = path; + } + + void MultiObjectiveModelCheckerEnvironment::unsetPlotPathUnderApproximation() { + plotPathUnderApprox = boost::none; + } + + boost::optional MultiObjectiveModelCheckerEnvironment::getPlotPathOverApproximation() const { + return plotPathOverApprox; + } + + void MultiObjectiveModelCheckerEnvironment::setPlotPathOverApproximation(std::string const& path) { + plotPathOverApprox = path; + } + + void MultiObjectiveModelCheckerEnvironment::unsetPlotPathOverApproximation() { + plotPathOverApprox = boost::none; + } + + boost::optional MultiObjectiveModelCheckerEnvironment::getPlotPathParetoPoints() const { + return plotPathParetoPoints; + } + + void MultiObjectiveModelCheckerEnvironment::setPlotPathParetoPoints(std::string const& path) { + plotPathParetoPoints = path; + } + + void MultiObjectiveModelCheckerEnvironment::unsetPlotPathParetoPoints() { + plotPathParetoPoints = boost::none; + } + + storm::RationalNumber const& MultiObjectiveModelCheckerEnvironment::getPrecision() const { + return precision; + } + + void MultiObjectiveModelCheckerEnvironment::setPrecision(storm::RationalNumber const& value) { + precision = value; + } + + bool MultiObjectiveModelCheckerEnvironment::isMaxStepsSet() const { + return maxSteps.is_initialized(); + } + + uint64_t const& MultiObjectiveModelCheckerEnvironment::getMaxSteps() const { + return maxSteps.get(); + } + + void MultiObjectiveModelCheckerEnvironment::setMaxSteps(uint64_t const& value) { + maxSteps = value; + } + + void MultiObjectiveModelCheckerEnvironment::unsetMaxSteps() { + maxSteps = boost::none; + } +} \ No newline at end of file diff --git a/src/storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h b/src/storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h new file mode 100644 index 000000000..f1d4f0203 --- /dev/null +++ b/src/storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include "storm/environment/modelchecker/ModelCheckerEnvironment.h" +#include "storm/modelchecker/multiobjective/MultiObjectiveModelCheckingMethod.h" +#include "storm/adapters/RationalNumberAdapter.h" + +namespace storm { + + class MultiObjectiveModelCheckerEnvironment { + public: + + MultiObjectiveModelCheckerEnvironment(); + ~MultiObjectiveModelCheckerEnvironment(); + + storm::modelchecker::multiobjective::MultiObjectiveMethod const& getMethod() const; + void setMethod(storm::modelchecker::multiobjective::MultiObjectiveMethod value); + + bool isExportPlotSet() const; + boost::optional getPlotPathUnderApproximation() const; + void setPlotPathUnderApproximation(std::string const& path); + void unsetPlotPathUnderApproximation(); + boost::optional getPlotPathOverApproximation() const; + void setPlotPathOverApproximation(std::string const& path); + void unsetPlotPathOverApproximation(); + boost::optional getPlotPathParetoPoints() const; + void setPlotPathParetoPoints(std::string const& path); + void unsetPlotPathParetoPoints(); + + storm::RationalNumber const& getPrecision() const; + void setPrecision(storm::RationalNumber const& value); + + uint64_t const& getMaxSteps() const; + bool isMaxStepsSet() const; + void setMaxSteps(uint64_t const& value); + void unsetMaxSteps(); + + + private: + storm::modelchecker::multiobjective::MultiObjectiveMethod method; + boost::optional plotPathUnderApprox, plotPathOverApprox, plotPathParetoPoints; + storm::RationalNumber precision; + boost::optional maxSteps; + + }; +} + diff --git a/src/storm/environment/solver/EigenSolverEnvironment.cpp b/src/storm/environment/solver/EigenSolverEnvironment.cpp index a36bd0062..4b1c561e0 100644 --- a/src/storm/environment/solver/EigenSolverEnvironment.cpp +++ b/src/storm/environment/solver/EigenSolverEnvironment.cpp @@ -14,7 +14,11 @@ namespace storm { methodSetFromDefault = eigenSettings.isLinearEquationSystemMethodSetFromDefault(); preconditioner = eigenSettings.getPreconditioningMethod(); restartThreshold = eigenSettings.getRestartIterationCount(); - maxIterationCount = eigenSettings.getMaximalIterationCount(); + if (eigenSettings.isMaximalIterationCountSet()) { + maxIterationCount = eigenSettings.getMaximalIterationCount(); + } else { + maxIterationCount = std::numeric_limits::max(); + } precision = storm::utility::convertNumber(eigenSettings.getPrecision()); } @@ -44,11 +48,11 @@ namespace storm { } uint64_t const& EigenSolverEnvironment::getRestartThreshold() const { - return maxIterationCount; + return restartThreshold; } void EigenSolverEnvironment::setRestartThreshold(uint64_t value) { - maxIterationCount = value; + restartThreshold = value; } uint64_t const& EigenSolverEnvironment::getMaximalNumberOfIterations() const { diff --git a/src/storm/environment/solver/GameSolverEnvironment.cpp b/src/storm/environment/solver/GameSolverEnvironment.cpp index aea27278d..9793312d3 100644 --- a/src/storm/environment/solver/GameSolverEnvironment.cpp +++ b/src/storm/environment/solver/GameSolverEnvironment.cpp @@ -12,7 +12,11 @@ namespace storm { gameMethod = gameSettings.getGameSolvingMethod(); methodSetFromDefault = gameSettings.isGameSolvingMethodSetFromDefaultValue(); - maxIterationCount = gameSettings.getMaximalIterationCount(); + if (gameSettings.isMaximalIterationCountSet()) { + maxIterationCount = gameSettings.getMaximalIterationCount(); + } else { + maxIterationCount = std::numeric_limits::max(); + } precision = storm::utility::convertNumber(gameSettings.getPrecision()); considerRelativeTerminationCriterion = gameSettings.getConvergenceCriterion() == storm::settings::modules::GameSolverSettings::ConvergenceCriterion::Relative; STORM_LOG_ASSERT(considerRelativeTerminationCriterion || gameSettings.getConvergenceCriterion() == storm::settings::modules::GameSolverSettings::ConvergenceCriterion::Absolute, "Unknown convergence criterion"); diff --git a/src/storm/environment/solver/GmmxxSolverEnvironment.cpp b/src/storm/environment/solver/GmmxxSolverEnvironment.cpp index 8b8992db4..d78f3e039 100644 --- a/src/storm/environment/solver/GmmxxSolverEnvironment.cpp +++ b/src/storm/environment/solver/GmmxxSolverEnvironment.cpp @@ -13,7 +13,11 @@ namespace storm { method = gmmxxSettings.getLinearEquationSystemMethod(); preconditioner = gmmxxSettings.getPreconditioningMethod(); restartThreshold = gmmxxSettings.getRestartIterationCount(); - maxIterationCount = gmmxxSettings.getMaximalIterationCount(); + if (gmmxxSettings.isMaximalIterationCountSet()) { + maxIterationCount = gmmxxSettings.getMaximalIterationCount(); + } else { + maxIterationCount = std::numeric_limits::max(); + } precision = storm::utility::convertNumber(gmmxxSettings.getPrecision()); } diff --git a/src/storm/environment/solver/MinMaxSolverEnvironment.cpp b/src/storm/environment/solver/MinMaxSolverEnvironment.cpp index 536bf5913..63cf5c698 100644 --- a/src/storm/environment/solver/MinMaxSolverEnvironment.cpp +++ b/src/storm/environment/solver/MinMaxSolverEnvironment.cpp @@ -12,7 +12,11 @@ namespace storm { minMaxMethod = minMaxSettings.getMinMaxEquationSolvingMethod(); methodSetFromDefault = minMaxSettings.isMinMaxEquationSolvingMethodSetFromDefaultValue(); - maxIterationCount = minMaxSettings.getMaximalIterationCount(); + if (minMaxSettings.isMaximalIterationCountSet()) { + maxIterationCount = minMaxSettings.getMaximalIterationCount(); + } else { + maxIterationCount = std::numeric_limits::max(); + } precision = storm::utility::convertNumber(minMaxSettings.getPrecision()); considerRelativeTerminationCriterion = minMaxSettings.getConvergenceCriterion() == storm::settings::modules::MinMaxEquationSolverSettings::ConvergenceCriterion::Relative; STORM_LOG_ASSERT(considerRelativeTerminationCriterion || minMaxSettings.getConvergenceCriterion() == storm::settings::modules::MinMaxEquationSolverSettings::ConvergenceCriterion::Absolute, "Unknown convergence criterion"); diff --git a/src/storm/environment/solver/NativeSolverEnvironment.cpp b/src/storm/environment/solver/NativeSolverEnvironment.cpp index 149a2feb6..9d152f92f 100644 --- a/src/storm/environment/solver/NativeSolverEnvironment.cpp +++ b/src/storm/environment/solver/NativeSolverEnvironment.cpp @@ -12,7 +12,11 @@ namespace storm { method = nativeSettings.getLinearEquationSystemMethod(); methodSetFromDefault = nativeSettings.isLinearEquationSystemTechniqueSetFromDefaultValue(); - maxIterationCount = nativeSettings.getMaximalIterationCount(); + if (nativeSettings.isMaximalIterationCountSet()) { + maxIterationCount = nativeSettings.getMaximalIterationCount(); + } else { + maxIterationCount = std::numeric_limits::max(); + } precision = storm::utility::convertNumber(nativeSettings.getPrecision()); considerRelativeTerminationCriterion = nativeSettings.getConvergenceCriterion() == storm::settings::modules::NativeEquationSolverSettings::ConvergenceCriterion::Relative; STORM_LOG_ASSERT(considerRelativeTerminationCriterion || nativeSettings.getConvergenceCriterion() == storm::settings::modules::NativeEquationSolverSettings::ConvergenceCriterion::Absolute, "Unknown convergence criterion"); diff --git a/src/storm/environment/solver/TopologicalSolverEnvironment.cpp b/src/storm/environment/solver/TopologicalSolverEnvironment.cpp index 678c6cb6c..aa580d67c 100644 --- a/src/storm/environment/solver/TopologicalSolverEnvironment.cpp +++ b/src/storm/environment/solver/TopologicalSolverEnvironment.cpp @@ -14,7 +14,7 @@ namespace storm { underlyingEquationSolverTypeSetFromDefault = topologicalSettings.isUnderlyingEquationSolverTypeSetFromDefaultValue(); underlyingMinMaxMethod = topologicalSettings.getUnderlyingMinMaxMethod(); - underlyingEquationSolverTypeSetFromDefault = topologicalSettings.isUnderlyingMinMaxMethodSetFromDefaultValue(); + underlyingMinMaxMethodSetFromDefault = topologicalSettings.isUnderlyingMinMaxMethodSetFromDefaultValue(); } TopologicalSolverEnvironment::~TopologicalSolverEnvironment() { diff --git a/src/storm/generator/CompressedState.cpp b/src/storm/generator/CompressedState.cpp index a785f1c79..175a753fa 100644 --- a/src/storm/generator/CompressedState.cpp +++ b/src/storm/generator/CompressedState.cpp @@ -46,6 +46,13 @@ namespace storm { template void unpackStateIntoEvaluator(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator& evaluator); storm::expressions::SimpleValuation unpackStateIntoValuation(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionManager const& manager); + CompressedState createOutOfBoundsState(VariableInformation const& varInfo, bool roundTo64Bit) { + CompressedState result(varInfo.getTotalBitOffset(roundTo64Bit)); + assert(varInfo.hasOutOfBoundsBit()); + result.set(varInfo.getOutOfBoundsBit()); + return result; + } + #ifdef STORM_HAVE_CARL template void unpackStateIntoEvaluator(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator& evaluator); template void unpackStateIntoEvaluator(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionEvaluator& evaluator); diff --git a/src/storm/generator/CompressedState.h b/src/storm/generator/CompressedState.h index 1a785dbaa..6965022f2 100644 --- a/src/storm/generator/CompressedState.h +++ b/src/storm/generator/CompressedState.h @@ -36,6 +36,8 @@ namespace storm { * @return A valuation that corresponds to the compressed state. */ storm::expressions::SimpleValuation unpackStateIntoValuation(CompressedState const& state, VariableInformation const& variableInformation, storm::expressions::ExpressionManager const& manager); + + CompressedState createOutOfBoundsState(VariableInformation const& varInfo, bool roundTo64Bit = true); } } diff --git a/src/storm/generator/JaniNextStateGenerator.cpp b/src/storm/generator/JaniNextStateGenerator.cpp index a1a683b5b..cf7a7b43e 100644 --- a/src/storm/generator/JaniNextStateGenerator.cpp +++ b/src/storm/generator/JaniNextStateGenerator.cpp @@ -320,6 +320,10 @@ namespace storm { // If the model is a deterministic model, we need to fuse the choices into one. if (this->isDeterministicModel() && totalNumberOfChoices > 1) { Choice globalChoice; + + if (this->options.isAddOverlappingGuardLabelSet()) { + this->overlappingGuardStates->push_back(stateToIdCallback(*this->state)); + } // For CTMCs, we need to keep track of the total exit rate to scale the action rewards later. For DTMCs // this is equal to the number of choices, which is why we initialize it like this here. @@ -461,12 +465,12 @@ namespace storm { nextDistribution.add(newTargetState, probability); } } - - // Create the state-action reward for the newly created choice. - auto valueIt = stateActionRewards.begin(); - performTransientAssignments(edge.getAssignments().getTransientAssignments(), [&valueIt] (ValueType const& value) { *valueIt += value; ++valueIt; } ); } + // Create the state-action reward for the newly created choice. + auto valueIt = stateActionRewards.begin(); + performTransientAssignments(edge.getAssignments().getTransientAssignments(), [&valueIt] (ValueType const& value) { *valueIt += value; ++valueIt; } ); + nextDistribution.compress(); // If there is one more command to come, shift the target states one time step back. diff --git a/src/storm/generator/NextStateGenerator.cpp b/src/storm/generator/NextStateGenerator.cpp index bea64b0bf..3b3186b68 100644 --- a/src/storm/generator/NextStateGenerator.cpp +++ b/src/storm/generator/NextStateGenerator.cpp @@ -1,4 +1,5 @@ #include +#include #include "storm/generator/NextStateGenerator.h" #include "storm/adapters/RationalFunctionAdapter.h" @@ -18,12 +19,22 @@ namespace storm { template NextStateGenerator::NextStateGenerator(storm::expressions::ExpressionManager const& expressionManager, VariableInformation const& variableInformation, NextStateGeneratorOptions const& options) : options(options), expressionManager(expressionManager.getSharedPointer()), variableInformation(variableInformation), evaluator(nullptr), state(nullptr) { - // Intentionally left empty. + if(variableInformation.hasOutOfBoundsBit()) { + outOfBoundsState = createOutOfBoundsState(variableInformation); + } + if (options.isAddOverlappingGuardLabelSet()) { + overlappingGuardStates = std::vector(); + } } template NextStateGenerator::NextStateGenerator(storm::expressions::ExpressionManager const& expressionManager, NextStateGeneratorOptions const& options) : options(options), expressionManager(expressionManager.getSharedPointer()), variableInformation(), evaluator(nullptr), state(nullptr) { - // Intentionally left empty. + if(variableInformation.hasOutOfBoundsBit()) { + outOfBoundsState = createOutOfBoundsState(variableInformation); + } + if (options.isAddOverlappingGuardLabelSet()) { + overlappingGuardStates = std::vector(); + } } template @@ -101,7 +112,15 @@ namespace storm { } } - if (stateStorage.stateToId.contains(outOfBoundsState)) { + if (this->options.isAddOverlappingGuardLabelSet()) { + STORM_LOG_THROW(!result.containsLabel("overlap_guards"), storm::exceptions::WrongFormatException, "Label 'overlap_guards' is reserved when adding overlapping guard labels"); + result.addLabel("overlap_guards"); + for (auto index : overlappingGuardStates.get()) { + result.addLabelToState("overlap_guards", index); + } + } + + if (this->options.isAddOutOfBoundsStateSet() && stateStorage.stateToId.contains(outOfBoundsState)) { STORM_LOG_THROW(!result.containsLabel("out_of_bounds"),storm::exceptions::WrongFormatException, "Label 'out_of_bounds' is reserved when adding out of bounds states."); result.addLabel("out_of_bounds"); result.addLabelToState("out_of_bounds", stateStorage.stateToId.getValue(outOfBoundsState)); @@ -163,6 +182,13 @@ namespace storm { return nullptr; } + template + void NextStateGenerator::remapStateIds(std::function const& remapping) { + if (overlappingGuardStates != boost::none) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Remapping of Ids during model building is not supported for overlapping guard statements."); + } + } + template class NextStateGenerator; #ifdef STORM_HAVE_CARL diff --git a/src/storm/generator/NextStateGenerator.h b/src/storm/generator/NextStateGenerator.h index ce1dd1f7b..7229ca802 100644 --- a/src/storm/generator/NextStateGenerator.h +++ b/src/storm/generator/NextStateGenerator.h @@ -66,6 +66,13 @@ namespace storm { NextStateGeneratorOptions const& getOptions() const; virtual std::shared_ptr generateChoiceOrigins(std::vector& dataForChoiceOrigins) const; + + /*! + * Performs a remapping of all values stored by applying the given remapping. + * + * @param remapping The remapping to apply. + */ + void remapStateIds(std::function const& remapping); protected: /*! @@ -98,6 +105,9 @@ namespace storm { /// A state that encodes the outOfBoundsState CompressedState outOfBoundsState; + + /// A map that stores the indices of states with overlapping guards. + boost::optional> overlappingGuardStates; }; } } diff --git a/src/storm/generator/PrismNextStateGenerator.cpp b/src/storm/generator/PrismNextStateGenerator.cpp index 1de65ce30..61fe1e80f 100644 --- a/src/storm/generator/PrismNextStateGenerator.cpp +++ b/src/storm/generator/PrismNextStateGenerator.cpp @@ -229,6 +229,10 @@ namespace storm { // If the model is a deterministic model, we need to fuse the choices into one. if (this->isDeterministicModel() && totalNumberOfChoices > 1) { Choice globalChoice; + + if (this->options.isAddOverlappingGuardLabelSet()) { + this->overlappingGuardStates->push_back(stateToIdCallback(*this->state)); + } // For CTMCs, we need to keep track of the total exit rate to scale the action rewards later. For DTMCs // this is equal to the number of choices, which is why we initialize it like this here. diff --git a/src/storm/generator/VariableInformation.cpp b/src/storm/generator/VariableInformation.cpp index 7f7ce0708..e4befba41 100644 --- a/src/storm/generator/VariableInformation.cpp +++ b/src/storm/generator/VariableInformation.cpp @@ -31,11 +31,14 @@ namespace storm { VariableInformation::VariableInformation(storm::prism::Program const& program, bool outOfBoundsState) : totalBitOffset(0) { if(outOfBoundsState) { - deadlockBit = 0; + outOfBoundsBit = 0; ++totalBitOffset; } else { - deadlockBit = boost::none; + outOfBoundsBit = boost::none; } + + + for (auto const& booleanVariable : program.getGlobalBooleanVariables()) { booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), totalBitOffset, true); ++totalBitOffset; @@ -75,11 +78,13 @@ namespace storm { STORM_LOG_THROW(!automaton.getVariables().containsNonTransientRealVariables(), storm::exceptions::InvalidArgumentException, "Cannot build model from JANI model that contains non-transient real variables in automaton '" << automaton.getName() << "'."); } if(outOfBoundsState) { - deadlockBit = 0; + outOfBoundsBit = 0; ++totalBitOffset; } else { - deadlockBit = boost::none; + outOfBoundsBit = boost::none; } + + for (auto const& variable : model.getGlobalVariables().getBooleanVariables()) { if (!variable.isTransient()) { @@ -133,6 +138,16 @@ namespace storm { } return result; } + + bool VariableInformation::hasOutOfBoundsBit() const { + return outOfBoundsBit != boost::none; + } + + uint64_t VariableInformation::getOutOfBoundsBit() const { + assert(hasOutOfBoundsBit()); + return outOfBoundsBit.get(); + } + void VariableInformation::sortVariables() { // Sort the variables so we can make some assumptions when iterating over them (in the next-state generators). diff --git a/src/storm/generator/VariableInformation.h b/src/storm/generator/VariableInformation.h index 2e51f7db7..fac200d1a 100644 --- a/src/storm/generator/VariableInformation.h +++ b/src/storm/generator/VariableInformation.h @@ -93,9 +93,12 @@ namespace storm { /// The integer variables. std::vector integerVariables; - + bool hasOutOfBoundsBit() const; + + uint64_t getOutOfBoundsBit() const; + private: - boost::optional deadlockBit; + boost::optional outOfBoundsBit; /*! * Sorts the variables to establish a known ordering. diff --git a/src/storm/logic/EventuallyFormula.cpp b/src/storm/logic/EventuallyFormula.cpp index 5b7a2cdac..2f1243f8a 100644 --- a/src/storm/logic/EventuallyFormula.cpp +++ b/src/storm/logic/EventuallyFormula.cpp @@ -31,7 +31,7 @@ namespace storm { } bool EventuallyFormula::isProbabilityPathFormula() const { - return this->isEventuallyFormula(); + return this->isReachabilityProbabilityFormula(); } bool EventuallyFormula::isRewardPathFormula() const { diff --git a/src/storm/logic/LabelSubstitutionVisitor.cpp b/src/storm/logic/LabelSubstitutionVisitor.cpp index 1e17649a3..08726901c 100644 --- a/src/storm/logic/LabelSubstitutionVisitor.cpp +++ b/src/storm/logic/LabelSubstitutionVisitor.cpp @@ -24,14 +24,14 @@ namespace storm { if (it != labelToExpressionMapping->end()) { return std::static_pointer_cast(std::make_shared(it->second)); } else { - return f.asSharedPointer(); + return std::static_pointer_cast(std::make_shared(f.getLabel())); } } else { auto it = labelToLabelMapping->find(f.getLabel()); if (it != labelToLabelMapping->end()) { return std::static_pointer_cast(std::make_shared(it->second)); } else { - return f.asSharedPointer(); + return std::static_pointer_cast(std::make_shared(f.getLabel())); } } } diff --git a/src/storm/logic/TimeOperatorFormula.cpp b/src/storm/logic/TimeOperatorFormula.cpp index 7ae4b7fea..f32568cdc 100644 --- a/src/storm/logic/TimeOperatorFormula.cpp +++ b/src/storm/logic/TimeOperatorFormula.cpp @@ -1,5 +1,5 @@ #include "storm/logic/TimeOperatorFormula.h" - +#include "storm/logic/EventuallyFormula.h" #include "storm/logic/FormulaVisitor.h" #include "storm/utility/macros.h" @@ -8,6 +8,7 @@ namespace storm { namespace logic { TimeOperatorFormula::TimeOperatorFormula(std::shared_ptr const& subformula, OperatorInformation const& operatorInformation, RewardMeasureType rewardMeasureType) : OperatorFormula(subformula, operatorInformation), rewardMeasureType(rewardMeasureType) { + assert(subformula->isTimePathFormula()); // Intentionally left empty. } diff --git a/src/storm/modelchecker/AbstractModelChecker.cpp b/src/storm/modelchecker/AbstractModelChecker.cpp index 2712b6d4a..42bb2a753 100644 --- a/src/storm/modelchecker/AbstractModelChecker.cpp +++ b/src/storm/modelchecker/AbstractModelChecker.cpp @@ -24,9 +24,16 @@ #include "storm/storage/dd/Add.h" #include "storm/storage/dd/Bdd.h" +#include + namespace storm { namespace modelchecker { + template + std::string AbstractModelChecker::getClassName() const { + return std::string(boost::core::demangled_name(BOOST_CORE_TYPEID(*this))); + } + template std::unique_ptr AbstractModelChecker::check(CheckTask const& checkTask) { Environment env; @@ -36,7 +43,7 @@ namespace storm { template std::unique_ptr AbstractModelChecker::check(Environment const& env, CheckTask const& checkTask) { storm::logic::Formula const& formula = checkTask.getFormula(); - STORM_LOG_THROW(this->canHandle(checkTask), storm::exceptions::InvalidArgumentException, "The model checker is not able to check the formula '" << formula << "'."); + STORM_LOG_THROW(this->canHandle(checkTask), storm::exceptions::InvalidArgumentException, "The model checker (" << getClassName() << ") is not able to check the formula '" << formula << "'."); if (formula.isStateFormula()) { return this->checkStateFormula(env, checkTask.substituteFormula(formula.asStateFormula())); } else if (formula.isMultiObjectiveFormula()){ @@ -66,12 +73,12 @@ namespace storm { template std::unique_ptr AbstractModelChecker::computeBoundedUntilProbabilities(Environment const& env, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template std::unique_ptr AbstractModelChecker::computeConditionalProbabilities(Environment const& env, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template @@ -83,17 +90,17 @@ namespace storm { template std::unique_ptr AbstractModelChecker::computeGloballyProbabilities(Environment const& env, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template std::unique_ptr AbstractModelChecker::computeNextProbabilities(Environment const& env, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template std::unique_ptr AbstractModelChecker::computeUntilProbabilities(Environment const& env, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template @@ -105,6 +112,8 @@ namespace storm { return this->computeInstantaneousRewards(env, rewardMeasureType, checkTask.substituteFormula(rewardFormula.asInstantaneousRewardFormula())); } else if (rewardFormula.isReachabilityRewardFormula()) { return this->computeReachabilityRewards(env, rewardMeasureType, checkTask.substituteFormula(rewardFormula.asReachabilityRewardFormula())); + } else if (rewardFormula.isTotalRewardFormula()) { + return this->computeTotalRewards(env, rewardMeasureType, checkTask.substituteFormula(rewardFormula.asTotalRewardFormula())); } else if (rewardFormula.isLongRunAverageRewardFormula()) { return this->computeLongRunAverageRewards(env, rewardMeasureType, checkTask.substituteFormula(rewardFormula.asLongRunAverageRewardFormula())); } else if (rewardFormula.isConditionalRewardFormula()) { @@ -115,32 +124,37 @@ namespace storm { template std::unique_ptr AbstractModelChecker::computeConditionalRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template std::unique_ptr AbstractModelChecker::computeCumulativeRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template std::unique_ptr AbstractModelChecker::computeInstantaneousRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template std::unique_ptr AbstractModelChecker::computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); + } + + template + std::unique_ptr AbstractModelChecker::computeTotalRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template std::unique_ptr AbstractModelChecker::computeLongRunAverageRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template std::unique_ptr AbstractModelChecker::computeLongRunAverageProbabilities(Environment const& env, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template @@ -149,12 +163,12 @@ namespace storm { if (timeFormula.isReachabilityTimeFormula()) { return this->computeReachabilityTimes(env, rewardMeasureType, checkTask.substituteFormula(timeFormula.asReachabilityTimeFormula())); } - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template std::unique_ptr AbstractModelChecker::computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template @@ -194,7 +208,7 @@ namespace storm { template std::unique_ptr AbstractModelChecker::checkAtomicLabelFormula(Environment const& env, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template @@ -220,7 +234,7 @@ namespace storm { template std::unique_ptr AbstractModelChecker::checkBooleanLiteralFormula(Environment const& env, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } template @@ -294,7 +308,7 @@ namespace storm { template std::unique_ptr AbstractModelChecker::checkMultiObjectiveFormula(Environment const& env, CheckTask const& checkTask) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << checkTask.getFormula() << "."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker (" << getClassName() << ") does not support the formula: " << checkTask.getFormula() << "."); } /////////////////////////////////////////////// diff --git a/src/storm/modelchecker/AbstractModelChecker.h b/src/storm/modelchecker/AbstractModelChecker.h index 0d391f1d9..44373de03 100644 --- a/src/storm/modelchecker/AbstractModelChecker.h +++ b/src/storm/modelchecker/AbstractModelChecker.h @@ -1,6 +1,7 @@ #ifndef STORM_MODELCHECKER_ABSTRACTMODELCHECKER_H_ #define STORM_MODELCHECKER_ABSTRACTMODELCHECKER_H_ +#include #include #include "storm/modelchecker/CheckTask.h" @@ -24,7 +25,12 @@ namespace storm { } typedef typename ModelType::ValueType ValueType; - + + /*! + * Returns the name of the model checker class (e.g., for display in error messages). + */ + virtual std::string getClassName() const; + /*! * Determines whether the model checker can handle the given verification task. If this method returns * false, the task must not be checked using this model checker. @@ -63,6 +69,7 @@ namespace storm { virtual std::unique_ptr computeCumulativeRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask); virtual std::unique_ptr computeInstantaneousRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask); virtual std::unique_ptr computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask); + virtual std::unique_ptr computeTotalRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask); virtual std::unique_ptr computeLongRunAverageRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask); // The methods to compute the long-run average probabilities and timing measures. diff --git a/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp b/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp index 52d49815e..3cedc1fab 100644 --- a/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp +++ b/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp @@ -37,14 +37,14 @@ namespace storm { template::SupportsExponential, int>::type> bool SparseCtmcCslModelChecker::canHandleImplementation(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - return formula.isInFragment(storm::logic::csrl().setGloballyFormulasAllowed(false).setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeAllowed(true)); + return formula.isInFragment(storm::logic::csrl().setGloballyFormulasAllowed(false).setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeAllowed(true).setTotalRewardFormulasAllowed(true)); } template template::SupportsExponential, int>::type> bool SparseCtmcCslModelChecker::canHandleImplementation(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - return formula.isInFragment(storm::logic::prctl().setGloballyFormulasAllowed(false).setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeAllowed(true)); + return formula.isInFragment(storm::logic::prctl().setGloballyFormulasAllowed(false).setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeAllowed(true).setTotalRewardFormulasAllowed(true)); } template @@ -117,6 +117,12 @@ namespace storm { return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); } + template + std::unique_ptr SparseCtmcCslModelChecker::computeTotalRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + std::vector numericResult = storm::modelchecker::helper::SparseCtmcCslHelper::computeTotalRewards(env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), this->getModel().getExitRateVector(), checkTask.isRewardModelSet() ? this->getModel().getRewardModel(checkTask.getRewardModel()) : this->getModel().getRewardModel(""), checkTask.isQualitativeSet()); + return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); + } + template std::unique_ptr SparseCtmcCslModelChecker::computeLongRunAverageProbabilities(Environment const& env, CheckTask const& checkTask) { storm::logic::StateFormula const& stateFormula = checkTask.getFormula(); diff --git a/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.h b/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.h index 9bf94fe93..5bf4735d0 100644 --- a/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.h +++ b/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.h @@ -33,6 +33,7 @@ namespace storm { virtual std::unique_ptr computeCumulativeRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeInstantaneousRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; + virtual std::unique_ptr computeTotalRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; private: template::SupportsExponential, int>::type = 0> diff --git a/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp b/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp index 74c976fd6..929ea8721 100644 --- a/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp +++ b/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp @@ -29,7 +29,7 @@ namespace storm { template bool SparseMarkovAutomatonCslModelChecker::canHandle(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - if(formula.isInFragment(storm::logic::csl().setGloballyFormulasAllowed(false).setNextFormulasAllowed(false).setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setTimeAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setLongRunAverageRewardFormulasAllowed(true))) { + if(formula.isInFragment(storm::logic::csl().setGloballyFormulasAllowed(false).setNextFormulasAllowed(false).setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setTotalRewardFormulasAllowed(true).setTimeAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setLongRunAverageRewardFormulasAllowed(true))) { return true; } else { // Check whether we consider a multi-objective formula @@ -89,7 +89,16 @@ namespace storm { return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(result))); } - + + template + std::unique_ptr SparseMarkovAutomatonCslModelChecker::computeTotalRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); + STORM_LOG_THROW(this->getModel().isClosed(), storm::exceptions::InvalidPropertyException, "Unable to compute reachability rewards in non-closed Markov automaton."); + std::vector result = storm::modelchecker::helper::SparseMarkovAutomatonCslHelper::computeTotalRewards(env, checkTask.getOptimizationDirection(), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), this->getModel().getExitRates(), this->getModel().getMarkovianStates(), checkTask.isRewardModelSet() ? this->getModel().getRewardModel(checkTask.getRewardModel()) : this->getModel().getRewardModel("")); + + return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(result))); + } + template std::unique_ptr SparseMarkovAutomatonCslModelChecker::computeLongRunAverageProbabilities(Environment const& env, CheckTask const& checkTask) { storm::logic::StateFormula const& stateFormula = checkTask.getFormula(); diff --git a/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h b/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h index 60cf49d37..3bd557dd4 100644 --- a/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h +++ b/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h @@ -24,6 +24,7 @@ namespace storm { virtual std::unique_ptr computeBoundedUntilProbabilities(Environment const& env, CheckTask const& checkTask) override; virtual std::unique_ptr computeUntilProbabilities(Environment const& env, CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; + virtual std::unique_ptr computeTotalRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeLongRunAverageProbabilities(Environment const& env, CheckTask const& checkTask) override; virtual std::unique_ptr computeLongRunAverageRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; diff --git a/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.cpp b/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.cpp index 7e50785d0..634eaf806 100644 --- a/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.cpp +++ b/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.cpp @@ -329,6 +329,38 @@ namespace storm { return storm::modelchecker::helper::SparseDtmcPrctlHelper::computeReachabilityRewards(env, std::move(goal), probabilityMatrix, backwardTransitions, totalRewardVector, targetStates, qualitative); } + template + std::vector SparseCtmcCslHelper::computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& rateMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, RewardModelType const& rewardModel, bool qualitative) { + STORM_LOG_THROW(!rewardModel.empty(), storm::exceptions::InvalidPropertyException, "Missing reward model for formula. Skipping formula."); + + storm::storage::SparseMatrix probabilityMatrix = computeProbabilityMatrix(rateMatrix, exitRateVector); + + std::vector totalRewardVector; + if (rewardModel.hasStateRewards()) { + totalRewardVector = rewardModel.getStateRewardVector(); + typename std::vector::const_iterator it2 = exitRateVector.begin(); + for (typename std::vector::iterator it1 = totalRewardVector.begin(), ite1 = totalRewardVector.end(); it1 != ite1; ++it1, ++it2) { + *it1 /= *it2; + } + if (rewardModel.hasStateActionRewards()) { + storm::utility::vector::addVectors(totalRewardVector, rewardModel.getStateActionRewardVector(), totalRewardVector); + } + if (rewardModel.hasTransitionRewards()) { + storm::utility::vector::addVectors(totalRewardVector, probabilityMatrix.getPointwiseProductRowSumVector(rewardModel.getTransitionRewardMatrix()), totalRewardVector); + } + } else if (rewardModel.hasTransitionRewards()) { + totalRewardVector = probabilityMatrix.getPointwiseProductRowSumVector(rewardModel.getTransitionRewardMatrix()); + if (rewardModel.hasStateActionRewards()) { + storm::utility::vector::addVectors(totalRewardVector, rewardModel.getStateActionRewardVector(), totalRewardVector); + } + } else { + totalRewardVector = rewardModel.getStateActionRewardVector(); + } + + RewardModelType dtmcRewardModel(std::move(totalRewardVector)); + return storm::modelchecker::helper::SparseDtmcPrctlHelper::computeTotalRewards(env, std::move(goal), probabilityMatrix, backwardTransitions, dtmcRewardModel, qualitative); + } + template std::vector SparseCtmcCslHelper::computeLongRunAverageProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& probabilityMatrix, storm::storage::BitVector const& psiStates, std::vector const* exitRateVector) { @@ -446,35 +478,56 @@ namespace storm { } } + // Build a different system depending on the problem format of the equation solver. + // Check solver requirements. + storm::solver::GeneralLinearEquationSolverFactory linearEquationSolverFactory; + auto requirements = linearEquationSolverFactory.getRequirements(env); + requirements.clearLowerBounds(); + requirements.clearUpperBounds(); + STORM_LOG_THROW(!requirements.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException, "Solver requirements " + requirements.getEnabledRequirementsAsString() + " not checked."); + + bool fixedPointSystem = false; + if (linearEquationSolverFactory.getEquationProblemFormat(env) == storm::solver::LinearEquationSolverProblemFormat::FixedPointSystem) { + fixedPointSystem = true; + } + // Now build the final equation system matrix, the initial guess and the right-hand side in one go. std::vector bsccEquationSystemRightSide(bsccEquationSystem.getColumnCount(), zero); storm::storage::SparseMatrixBuilder builder; for (uint_fast64_t row = 0; row < bsccEquationSystem.getRowCount(); ++row) { - + // If the current row is the first one belonging to a BSCC, we substitute it by the constraint that the // values for states of this BSCC must sum to one. However, in order to have a non-zero value on the // diagonal, we add the constraint of the BSCC that produces a 1 on the diagonal. if (firstStatesInBsccs.get(row)) { uint_fast64_t requiredBscc = stateToBsccIndexMap[row]; storm::storage::StronglyConnectedComponent const& bscc = bsccDecomposition[requiredBscc]; - - for (auto const& state : bscc) { - builder.addNextValue(row, indexInStatesInBsccs[state], one); + + if (fixedPointSystem) { + for (auto const& state : bscc) { + if (row == indexInStatesInBsccs[state]) { + builder.addNextValue(row, indexInStatesInBsccs[state], zero); + } else { + builder.addNextValue(row, indexInStatesInBsccs[state], -one); + } + } + } else { + for (auto const& state : bscc) { + builder.addNextValue(row, indexInStatesInBsccs[state], one); + } } bsccEquationSystemRightSide[row] = one; - } else { - // Otherwise, we copy the row, and subtract 1 from the diagonal. + // Otherwise, we copy the row, and subtract 1 from the diagonal (only for the equation solver format). for (auto& entry : bsccEquationSystem.getRow(row)) { - if (entry.getColumn() == row) { - builder.addNextValue(row, entry.getColumn(), entry.getValue() - one); - } else { + if (fixedPointSystem || entry.getColumn() != row) { builder.addNextValue(row, entry.getColumn(), entry.getValue()); + } else { + builder.addNextValue(row, entry.getColumn(), entry.getValue() - one); } } } - } // Create the initial guess for the LRAs. We take a uniform distribution over all states in a BSCC. @@ -488,15 +541,10 @@ namespace storm { } bsccEquationSystem = builder.build(); - { - // Check solver requirements - storm::solver::GeneralLinearEquationSolverFactory linearEquationSolverFactory; - auto requirements = linearEquationSolverFactory.getRequirements(env); - STORM_LOG_THROW(!requirements.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException, "Solver requirements " + requirements.getEnabledRequirementsAsString() + " not checked."); - // Check whether we have the right input format for the solver. - STORM_LOG_THROW(linearEquationSolverFactory.getEquationProblemFormat(env) == storm::solver::LinearEquationSolverProblemFormat::EquationSystem, storm::exceptions::FormatUnsupportedBySolverException, "The selected solver does not support the required format."); std::unique_ptr> solver = linearEquationSolverFactory.create(env, std::move(bsccEquationSystem)); + solver->setLowerBound(storm::utility::zero()); + solver->setUpperBound(storm::utility::one()); solver->solveEquations(env, bsccEquationSystemSolution, bsccEquationSystemRightSide); } @@ -722,7 +770,7 @@ namespace storm { for (uint_fast64_t row = 0; row < generatorMatrix.getRowCount(); ++row) { for (auto& entry : generatorMatrix.getRow(row)) { if (entry.getColumn() == row) { - entry.setValue(-exitRates[row]); + entry.setValue(-exitRates[row] + entry.getValue()); } } } @@ -743,6 +791,8 @@ namespace storm { template std::vector SparseCtmcCslHelper::computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& rateMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative); + template std::vector SparseCtmcCslHelper::computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& rateMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::models::sparse::StandardRewardModel const& rewardModel, bool qualitative); + template std::vector SparseCtmcCslHelper::computeLongRunAverageProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& probabilityMatrix, storm::storage::BitVector const& psiStates, std::vector const* exitRateVector); template std::vector SparseCtmcCslHelper::computeLongRunAverageRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& probabilityMatrix, storm::models::sparse::StandardRewardModel const& rewardModel, std::vector const* exitRateVector); template std::vector SparseCtmcCslHelper::computeLongRunAverageRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& probabilityMatrix, std::vector const& stateRewardVector, std::vector const* exitRateVector); @@ -772,6 +822,9 @@ namespace storm { template std::vector SparseCtmcCslHelper::computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& rateMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative); template std::vector SparseCtmcCslHelper::computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& rateMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative); + template std::vector SparseCtmcCslHelper::computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& rateMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::models::sparse::StandardRewardModel const& rewardModel, bool qualitative); + template std::vector SparseCtmcCslHelper::computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& rateMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::models::sparse::StandardRewardModel const& rewardModel, bool qualitative); + template std::vector SparseCtmcCslHelper::computeLongRunAverageProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& probabilityMatrix, storm::storage::BitVector const& psiStates, std::vector const* exitRateVector); template std::vector SparseCtmcCslHelper::computeLongRunAverageProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& probabilityMatrix, storm::storage::BitVector const& psiStates, std::vector const* exitRateVector); diff --git a/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.h b/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.h index 28125c7c9..b6e7b508c 100644 --- a/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.h +++ b/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.h @@ -44,6 +44,9 @@ namespace storm { template static std::vector computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& rateMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, RewardModelType const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative); + + template + static std::vector computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& rateMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, RewardModelType const& rewardModel, bool qualitative); template static std::vector computeLongRunAverageProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& probabilityMatrix, storm::storage::BitVector const& psiStates, std::vector const* exitRateVector); diff --git a/src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.cpp b/src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.cpp index 8eb688d55..6e6a39acf 100644 --- a/src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.cpp +++ b/src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.cpp @@ -583,6 +583,20 @@ namespace storm { return std::move(storm::modelchecker::helper::SparseMdpPrctlHelper::computeUntilProbabilities(env, dir, transitionMatrix, backwardTransitions, phiStates, psiStates, qualitative, false).values); } + template + std::vector SparseMarkovAutomatonCslHelper::computeTotalRewards(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, RewardModelType const& rewardModel) { + + // Get a reward model where the state rewards are scaled accordingly + std::vector stateRewardWeights(transitionMatrix.getRowGroupCount(), storm::utility::zero()); + for (auto const markovianState : markovianStates) { + stateRewardWeights[markovianState] = storm::utility::one() / exitRateVector[markovianState]; + } + std::vector totalRewardVector = rewardModel.getTotalActionRewardVector(transitionMatrix, stateRewardWeights); + RewardModelType scaledRewardModel(boost::none, std::move(totalRewardVector)); + + return SparseMdpPrctlHelper::computeTotalRewards(env, dir, transitionMatrix, backwardTransitions, scaledRewardModel, false, false).values; + } + template std::vector SparseMarkovAutomatonCslHelper::computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, RewardModelType const& rewardModel, storm::storage::BitVector const& psiStates) { @@ -1020,6 +1034,8 @@ namespace storm { template std::vector SparseMarkovAutomatonCslHelper::computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::BitVector const& psiStates); + template std::vector SparseMarkovAutomatonCslHelper::computeTotalRewards(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::models::sparse::StandardRewardModel const& rewardModel); + template std::vector SparseMarkovAutomatonCslHelper::computeLongRunAverageProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& psiStates); template std::vector SparseMarkovAutomatonCslHelper::computeLongRunAverageRewards(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::models::sparse::StandardRewardModel const& rewardModel); @@ -1038,6 +1054,8 @@ namespace storm { template std::vector SparseMarkovAutomatonCslHelper::computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::BitVector const& psiStates); + template std::vector SparseMarkovAutomatonCslHelper::computeTotalRewards(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::models::sparse::StandardRewardModel const& rewardModel); + template std::vector SparseMarkovAutomatonCslHelper::computeLongRunAverageProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& psiStates); template std::vector SparseMarkovAutomatonCslHelper::computeLongRunAverageRewards(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::models::sparse::StandardRewardModel const& rewardModel); diff --git a/src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.h b/src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.h index fbfeaa7dd..6b6a94a50 100644 --- a/src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.h +++ b/src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.h @@ -26,6 +26,9 @@ namespace storm { template static std::vector computeUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool qualitative); + template + static std::vector computeTotalRewards(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, RewardModelType const& rewardModel); + template static std::vector computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& exitRateVector, storm::storage::BitVector const& markovianStates, RewardModelType const& rewardModel, storm::storage::BitVector const& psiStates); diff --git a/src/storm/modelchecker/multiobjective/multiObjectiveModelChecking.cpp b/src/storm/modelchecker/multiobjective/multiObjectiveModelChecking.cpp index 10276ffa6..bb18e9385 100644 --- a/src/storm/modelchecker/multiobjective/multiObjectiveModelChecking.cpp +++ b/src/storm/modelchecker/multiobjective/multiObjectiveModelChecking.cpp @@ -1,7 +1,7 @@ #include "storm/modelchecker/multiobjective/multiObjectiveModelChecking.h" #include "storm/utility/macros.h" - +#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h" #include "storm/models/sparse/Mdp.h" #include "storm/models/sparse/MarkovAutomaton.h" #include "storm/models/sparse/StandardRewardModel.h" @@ -74,8 +74,8 @@ namespace storm { result = query->check(env); - if(storm::settings::getModule().isExportPlotSet()) { - query->exportPlotOfCurrentApproximation(storm::settings::getModule().getExportPlotDirectory()); + if (env.modelchecker().multi().isExportPlotSet()) { + query->exportPlotOfCurrentApproximation(env); } break; } diff --git a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.cpp b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.cpp index 409bd9ba9..b8ac5fe2d 100644 --- a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.cpp +++ b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.cpp @@ -7,9 +7,8 @@ #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/utility/constants.h" #include "storm/utility/vector.h" -#include "storm/settings/SettingsManager.h" -#include "storm/settings/modules/GeneralSettings.h" -#include "storm/settings/modules/MultiObjectiveSettings.h" +#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h" + #include "storm/exceptions/InvalidOperationException.h" @@ -57,7 +56,7 @@ namespace storm { template bool SparsePcaaAchievabilityQuery::checkAchievability(Environment const& env) { // repeatedly refine the over/ under approximation until the threshold point is either in the under approx. or not in the over approx. - while(!this->maxStepsPerformed()){ + while(!this->maxStepsPerformed(env)){ WeightVector separatingVector = this->findSeparatingVector(thresholds); this->updateWeightedPrecision(separatingVector); this->performRefinementStep(env, std::move(separatingVector)); diff --git a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.cpp b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.cpp index fe7a7b367..93c04e527 100644 --- a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.cpp +++ b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaParetoQuery.cpp @@ -7,10 +7,7 @@ #include "storm/modelchecker/results/ExplicitParetoCurveCheckResult.h" #include "storm/utility/constants.h" #include "storm/utility/vector.h" -#include "storm/settings/SettingsManager.h" -#include "storm/settings/modules/MultiObjectiveSettings.h" -#include "storm/settings/modules/GeneralSettings.h" - +#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h" namespace storm { namespace modelchecker { @@ -19,20 +16,19 @@ namespace storm { template SparsePcaaParetoQuery::SparsePcaaParetoQuery(SparseMultiObjectivePreprocessorResult& preprocessorResult) : SparsePcaaQuery(preprocessorResult) { STORM_LOG_ASSERT(preprocessorResult.queryType==SparseMultiObjectivePreprocessorResult::QueryType::Pareto, "Invalid query Type"); + } + + template + std::unique_ptr SparsePcaaParetoQuery::check(Environment const& env) { // Set the precision of the weight vector checker - typename SparseModelType::ValueType weightedPrecision = storm::utility::convertNumber(storm::settings::getModule().getPrecision()); + typename SparseModelType::ValueType weightedPrecision = storm::utility::convertNumber(env.modelchecker().multi().getPrecision()); weightedPrecision /= storm::utility::sqrt(storm::utility::convertNumber(this->objectives.size())); // multiobjPrecision / sqrt(numObjectives) is the largest possible value for which termination is guaranteed. // Lets be a little bit more precise to reduce the number of required iterations. weightedPrecision *= storm::utility::convertNumber(0.9); this->weightVectorChecker->setWeightedPrecision(weightedPrecision); - - } - - template - std::unique_ptr SparsePcaaParetoQuery::check(Environment const& env) { - + // refine the approximation exploreSetOfAchievablePoints(env); @@ -55,13 +51,13 @@ namespace storm { void SparsePcaaParetoQuery::exploreSetOfAchievablePoints(Environment const& env) { //First consider the objectives individually - for(uint_fast64_t objIndex = 0; objIndexobjectives.size() && !this->maxStepsPerformed(); ++objIndex) { + for(uint_fast64_t objIndex = 0; objIndexobjectives.size() && !this->maxStepsPerformed(env); ++objIndex) { WeightVector direction(this->objectives.size(), storm::utility::zero()); direction[objIndex] = storm::utility::one(); this->performRefinementStep(env, std::move(direction)); } - while(!this->maxStepsPerformed()) { + while(!this->maxStepsPerformed(env)) { // Get the halfspace of the underApproximation with maximal distance to a vertex of the overApproximation std::vector> underApproxHalfspaces = this->underApproximation->getHalfspaces(); std::vector overApproxVertices = this->overApproximation->getVertices(); @@ -76,7 +72,7 @@ namespace storm { } } } - if(farestDistance < storm::utility::convertNumber(storm::settings::getModule().getPrecision())) { + if(farestDistance < storm::utility::convertNumber(env.modelchecker().multi().getPrecision())) { // Goal precision reached! return; } diff --git a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.cpp b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.cpp index 538752ab2..4df353cdd 100644 --- a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.cpp +++ b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.cpp @@ -8,9 +8,7 @@ #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "storm/utility/constants.h" #include "storm/utility/vector.h" -#include "storm/settings/SettingsManager.h" -#include "storm/settings/modules/MultiObjectiveSettings.h" -#include "storm/settings/modules/GeneralSettings.h" +#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h" #include "storm/exceptions/InvalidOperationException.h" @@ -99,7 +97,7 @@ namespace storm { // We don't care for the optimizing objective at this point this->diracWeightVectorsToBeChecked.set(indexOfOptimizingObjective, false); - while(!this->maxStepsPerformed()){ + while(!this->maxStepsPerformed(env)){ WeightVector separatingVector = this->findSeparatingVector(thresholds); this->updateWeightedPrecisionInAchievabilityPhase(separatingVector); this->performRefinementStep(env, std::move(separatingVector)); @@ -150,10 +148,10 @@ namespace storm { // the supremum over all strategies. Hence, one could combine a scheduler inducing the optimum value (but possibly violating strict // thresholds) and (with very low probability) a scheduler that satisfies all (possibly strict) thresholds. GeometryValueType result = storm::utility::zero(); - while(!this->maxStepsPerformed()) { + while(!this->maxStepsPerformed(env)) { if (this->refinementSteps.empty()) { // We did not make any refinement steps during the checkAchievability phase (e.g., because there is only one objective). - this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber(storm::settings::getModule().getPrecision())); + this->weightVectorChecker->setWeightedPrecision(storm::utility::convertNumber(env.modelchecker().multi().getPrecision())); WeightVector separatingVector = directionOfOptimizingObjective; this->performRefinementStep(env, std::move(separatingVector)); } @@ -165,7 +163,7 @@ namespace storm { optimizationRes = this->overApproximation->intersection(thresholdsAsPolytope)->optimize(directionOfOptimizingObjective); if (optimizationRes.second) { GeometryValueType precisionOfResult = optimizationRes.first[indexOfOptimizingObjective] - result; - if (precisionOfResult < storm::utility::convertNumber(storm::settings::getModule().getPrecision())) { + if (precisionOfResult < storm::utility::convertNumber(env.modelchecker().multi().getPrecision())) { // Goal precision reached! return result; } else { @@ -176,7 +174,7 @@ namespace storm { thresholds[indexOfOptimizingObjective] = result + storm::utility::one(); } WeightVector separatingVector = this->findSeparatingVector(thresholds); - this->updateWeightedPrecisionInImprovingPhase(separatingVector); + this->updateWeightedPrecisionInImprovingPhase(env, separatingVector); this->performRefinementStep(env, std::move(separatingVector)); } STORM_LOG_ERROR("Could not reach the desired precision: Exceeded maximum number of refinement steps"); @@ -185,11 +183,11 @@ namespace storm { template - void SparsePcaaQuantitativeQuery::updateWeightedPrecisionInImprovingPhase(WeightVector const& weights) { + void SparsePcaaQuantitativeQuery::updateWeightedPrecisionInImprovingPhase(Environment const& env, WeightVector const& weights) { STORM_LOG_THROW(!storm::utility::isZero(weights[this->indexOfOptimizingObjective]), exceptions::UnexpectedException, "The chosen weight-vector gives zero weight for the objective that is to be optimized."); // If weighs[indexOfOptimizingObjective] is low, the computation of the weightVectorChecker needs to be more precise. // Our heuristic ensures that if p is the new vertex of the under-approximation, then max{ eps | p' = p + (0..0 eps 0..0) is in the over-approximation } <= multiobjective_precision/0.9 - GeometryValueType weightedPrecision = weights[this->indexOfOptimizingObjective] * storm::utility::convertNumber(storm::settings::getModule().getPrecision()); + GeometryValueType weightedPrecision = weights[this->indexOfOptimizingObjective] * storm::utility::convertNumber(env.modelchecker().multi().getPrecision()); // Normalize by division with the Euclidean Norm of the weight-vector weightedPrecision /= storm::utility::sqrt(storm::utility::vector::dotProduct(weights, weights)); weightedPrecision *= storm::utility::convertNumber(0.9); diff --git a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.h b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.h index 164a87f21..234e6f291 100644 --- a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.h +++ b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.h @@ -45,7 +45,7 @@ namespace storm { * Updates the precision of the weightVectorChecker w.r.t. the provided weights */ void updateWeightedPrecisionInAchievabilityPhase(WeightVector const& weights); - void updateWeightedPrecisionInImprovingPhase(WeightVector const& weights); + void updateWeightedPrecisionInImprovingPhase(Environment const& env, WeightVector const& weights); /* * Given that the thresholds are achievable, this function further refines the approximations and returns the optimized value diff --git a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp index 2164665e0..ef7e50d80 100644 --- a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp +++ b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp @@ -5,8 +5,7 @@ #include "storm/models/sparse/MarkovAutomaton.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/modelchecker/multiobjective/Objective.h" -#include "storm/settings/SettingsManager.h" -#include "storm/settings/modules/MultiObjectiveSettings.h" +#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h" #include "storm/storage/geometry/Hyperrectangle.h" #include "storm/utility/constants.h" #include "storm/utility/vector.h" @@ -126,9 +125,9 @@ namespace storm { } template - bool SparsePcaaQuery::maxStepsPerformed() const { - return storm::settings::getModule().isMaxStepsSet() && - this->refinementSteps.size() >= storm::settings::getModule().getMaxSteps(); + bool SparsePcaaQuery::maxStepsPerformed(Environment const& env) const { + return env.modelchecker().multi().isMaxStepsSet() && + this->refinementSteps.size() >= env.modelchecker().multi().getMaxSteps(); } @@ -191,7 +190,7 @@ namespace storm { } template - void SparsePcaaQuery::exportPlotOfCurrentApproximation(std::string const& destinationDir) const { + void SparsePcaaQuery::exportPlotOfCurrentApproximation(Environment const& env) const { STORM_LOG_ERROR_COND(objectives.size()==2, "Exporting plot requested but this is only implemented for the two-dimensional case."); @@ -223,35 +222,33 @@ namespace storm { std::vector columnHeaders = {"x", "y"}; std::vector> pointsForPlotting; - underApproxVertices = transformedUnderApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder(); - pointsForPlotting.reserve(underApproxVertices.size()); - for(auto const& v : underApproxVertices) { - pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); - } - storm::utility::exportDataToCSVFile(destinationDir + "underapproximation.csv", pointsForPlotting, columnHeaders); - - pointsForPlotting.clear(); - overApproxVertices = transformedOverApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder(); - pointsForPlotting.reserve(overApproxVertices.size()); - for(auto const& v : overApproxVertices) { - pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); + if (env.modelchecker().multi().getPlotPathUnderApproximation()) { + underApproxVertices = transformedUnderApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder(); + pointsForPlotting.reserve(underApproxVertices.size()); + for(auto const& v : underApproxVertices) { + pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); + } + storm::utility::exportDataToCSVFile(env.modelchecker().multi().getPlotPathUnderApproximation().get(), pointsForPlotting, columnHeaders); } - storm::utility::exportDataToCSVFile(destinationDir + "overapproximation.csv", pointsForPlotting, columnHeaders); - pointsForPlotting.clear(); - pointsForPlotting.reserve(paretoPoints.size()); - for(auto const& v : paretoPoints) { - pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); + if (env.modelchecker().multi().getPlotPathOverApproximation()) { + pointsForPlotting.clear(); + overApproxVertices = transformedOverApprox->intersection(boundariesAsPolytope)->getVerticesInClockwiseOrder(); + pointsForPlotting.reserve(overApproxVertices.size()); + for(auto const& v : overApproxVertices) { + pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); + } + storm::utility::exportDataToCSVFile(env.modelchecker().multi().getPlotPathOverApproximation().get(), pointsForPlotting, columnHeaders); } - storm::utility::exportDataToCSVFile(destinationDir + "paretopoints.csv", pointsForPlotting, columnHeaders); - pointsForPlotting.clear(); - auto boundVertices = boundariesAsPolytope->getVerticesInClockwiseOrder(); - pointsForPlotting.reserve(4); - for(auto const& v : boundVertices) { - pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); + if (env.modelchecker().multi().getPlotPathParetoPoints()) { + pointsForPlotting.clear(); + pointsForPlotting.reserve(paretoPoints.size()); + for(auto const& v : paretoPoints) { + pointsForPlotting.push_back(storm::utility::vector::convertNumericVector(v)); + } + storm::utility::exportDataToCSVFile(env.modelchecker().multi().getPlotPathParetoPoints().get(), pointsForPlotting, columnHeaders); } - storm::utility::exportDataToCSVFile(destinationDir + "boundaries.csv", pointsForPlotting, columnHeaders); } #ifdef STORM_HAVE_CARL diff --git a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h index d92f0ccee..630503a08 100644 --- a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h +++ b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.h @@ -38,7 +38,7 @@ namespace storm { * Note that the approximations will be intersected with a (sufficiently large) hyperrectangle in order to ensure that the polytopes are bounded * This only works for 2 dimensional queries. */ - void exportPlotOfCurrentApproximation(std::string const& destinationDir) const; + void exportPlotOfCurrentApproximation(Environment const& env) const; protected: @@ -87,7 +87,7 @@ namespace storm { /* * Returns true iff the maximum number of refinement steps (as possibly specified in the settings) has been reached */ - bool maxStepsPerformed() const; + bool maxStepsPerformed(Environment const& env) const; /* * Transforms the given point (or polytope) to values w.r.t. the original model/formula (e.g. negates values for minimizing objectives). diff --git a/src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.cpp index 94b7ca4e1..0654a9788 100644 --- a/src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.cpp @@ -33,7 +33,7 @@ namespace storm { template bool HybridDtmcPrctlModelChecker::canHandle(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true)); + return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true)); } template @@ -95,6 +95,15 @@ namespace storm { SymbolicQualitativeCheckResult const& subResult = subResultPointer->asSymbolicQualitativeCheckResult(); return storm::modelchecker::helper::HybridDtmcPrctlHelper::computeReachabilityRewards(env, this->getModel(), this->getModel().getTransitionMatrix(), checkTask.isRewardModelSet() ? this->getModel().getRewardModel(checkTask.getRewardModel()) : this->getModel().getRewardModel(""), subResult.getTruthValuesVector(), checkTask.isQualitativeSet()); } + + template + std::unique_ptr HybridDtmcPrctlModelChecker::computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula(); + std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); + SymbolicQualitativeCheckResult const& subResult = subResultPointer->asSymbolicQualitativeCheckResult(); + + return storm::modelchecker::helper::HybridDtmcPrctlHelper::computeReachabilityTimes(env, this->getModel(), this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet()); + } template diff --git a/src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.h b/src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.h index d76ae69ac..854fae195 100644 --- a/src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.h +++ b/src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.h @@ -31,6 +31,7 @@ namespace storm { virtual std::unique_ptr computeCumulativeRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeInstantaneousRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; + virtual std::unique_ptr computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; }; diff --git a/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp index 6ae7ea4bc..9af47c767 100644 --- a/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp @@ -39,7 +39,7 @@ namespace storm { template bool HybridMdpPrctlModelChecker::canHandle(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - if(formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false))) { + if (formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true))) { return true; } else { // Check whether we consider a multi-objective formula @@ -117,6 +117,15 @@ namespace storm { SymbolicQualitativeCheckResult const& subResult = subResultPointer->asSymbolicQualitativeCheckResult(); return storm::modelchecker::helper::HybridMdpPrctlHelper::computeReachabilityRewards(env, checkTask.getOptimizationDirection(), this->getModel(), this->getModel().getTransitionMatrix(), checkTask.isRewardModelSet() ? this->getModel().getRewardModel(checkTask.getRewardModel()) : this->getModel().getRewardModel(""), subResult.getTruthValuesVector(), checkTask.isQualitativeSet()); } + + template + std::unique_ptr HybridMdpPrctlModelChecker::computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula(); + STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); + std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); + SymbolicQualitativeCheckResult const& subResult = subResultPointer->asSymbolicQualitativeCheckResult(); + return storm::modelchecker::helper::HybridMdpPrctlHelper::computeReachabilityTimes(env, checkTask.getOptimizationDirection(), this->getModel(), this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet()); + } template std::unique_ptr HybridMdpPrctlModelChecker::checkMultiObjectiveFormula(Environment const& env, CheckTask const& checkTask) { diff --git a/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.h b/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.h index 246a991d5..68eb12baa 100644 --- a/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.h +++ b/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.h @@ -35,6 +35,7 @@ namespace storm { virtual std::unique_ptr computeCumulativeRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeInstantaneousRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; + virtual std::unique_ptr computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr checkMultiObjectiveFormula(Environment const& env, CheckTask const& checkTask) override; }; diff --git a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp index 4a746bf07..f561d3e98 100644 --- a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp @@ -31,7 +31,7 @@ namespace storm { template bool SparseDtmcPrctlModelChecker::canHandle(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setConditionalProbabilityFormulasAllowed(true).setConditionalRewardFormulasAllowed(true).setOnlyEventuallyFormuluasInConditionalFormulasAllowed(true).setRewardBoundedUntilFormulasAllowed(true).setRewardBoundedCumulativeRewardFormulasAllowed(true).setMultiDimensionalBoundedUntilFormulasAllowed(true).setMultiDimensionalCumulativeRewardFormulasAllowed(true)); + return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setConditionalProbabilityFormulasAllowed(true).setConditionalRewardFormulasAllowed(true).setTotalRewardFormulasAllowed(true).setOnlyEventuallyFormuluasInConditionalFormulasAllowed(true).setRewardBoundedUntilFormulasAllowed(true).setRewardBoundedCumulativeRewardFormulasAllowed(true).setMultiDimensionalBoundedUntilFormulasAllowed(true).setMultiDimensionalCumulativeRewardFormulasAllowed(true).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true)); } template @@ -123,6 +123,21 @@ namespace storm { std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeReachabilityRewards(env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), checkTask.isRewardModelSet() ? this->getModel().getRewardModel(checkTask.getRewardModel()) : this->getModel().getRewardModel(""), subResult.getTruthValuesVector(), checkTask.isQualitativeSet(), checkTask.getHint()); return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); } + + template + std::unique_ptr SparseDtmcPrctlModelChecker::computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula(); + std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeReachabilityTimes(env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet(), checkTask.getHint()); + return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); + } + + template + std::unique_ptr SparseDtmcPrctlModelChecker::computeTotalRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeTotalRewards(env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), checkTask.isRewardModelSet() ? this->getModel().getRewardModel(checkTask.getRewardModel()) : this->getModel().getRewardModel(""), checkTask.isQualitativeSet(), checkTask.getHint()); + return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); + } template std::unique_ptr SparseDtmcPrctlModelChecker::computeLongRunAverageProbabilities(Environment const& env, CheckTask const& checkTask) { diff --git a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h index ebc7d39a3..1aafc0efb 100644 --- a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h +++ b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h @@ -30,9 +30,10 @@ namespace storm { virtual std::unique_ptr computeCumulativeRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeInstantaneousRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; + virtual std::unique_ptr computeTotalRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeConditionalRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeLongRunAverageRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; - + virtual std::unique_ptr computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; }; } // namespace modelchecker diff --git a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index 074a679b6..b78ad3856 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp @@ -38,7 +38,7 @@ namespace storm { template bool SparseMdpPrctlModelChecker::canHandle(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - if (formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setConditionalProbabilityFormulasAllowed(true).setOnlyEventuallyFormuluasInConditionalFormulasAllowed(true).setRewardBoundedUntilFormulasAllowed(true).setRewardBoundedCumulativeRewardFormulasAllowed(true).setMultiDimensionalBoundedUntilFormulasAllowed(true).setMultiDimensionalCumulativeRewardFormulasAllowed(true))) { + if (formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setConditionalProbabilityFormulasAllowed(true).setOnlyEventuallyFormuluasInConditionalFormulasAllowed(true).setTotalRewardFormulasAllowed(true).setRewardBoundedUntilFormulasAllowed(true).setRewardBoundedCumulativeRewardFormulasAllowed(true).setMultiDimensionalBoundedUntilFormulasAllowed(true).setMultiDimensionalCumulativeRewardFormulasAllowed(true).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true))) { return true; } else { // Check whether we consider a multi-objective formula @@ -172,6 +172,31 @@ namespace storm { } return result; } + + template + std::unique_ptr SparseMdpPrctlModelChecker::computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula(); + STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); + std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + auto ret = storm::modelchecker::helper::SparseMdpPrctlHelper::computeReachabilityTimes(env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet(), checkTask.isProduceSchedulersSet(), checkTask.getHint()); + std::unique_ptr result(new ExplicitQuantitativeCheckResult(std::move(ret.values))); + if (checkTask.isProduceSchedulersSet() && ret.scheduler) { + result->asExplicitQuantitativeCheckResult().setScheduler(std::move(ret.scheduler)); + } + return result; + } + + template + std::unique_ptr SparseMdpPrctlModelChecker::computeTotalRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); + auto ret = storm::modelchecker::helper::SparseMdpPrctlHelper::computeTotalRewards(env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), checkTask.isRewardModelSet() ? this->getModel().getRewardModel(checkTask.getRewardModel()) : this->getModel().getRewardModel(""), checkTask.isQualitativeSet(), checkTask.isProduceSchedulersSet(), checkTask.getHint()); + std::unique_ptr result(new ExplicitQuantitativeCheckResult(std::move(ret.values))); + if (checkTask.isProduceSchedulersSet() && ret.scheduler) { + result->asExplicitQuantitativeCheckResult().setScheduler(std::move(ret.scheduler)); + } + return result; + } template std::unique_ptr SparseMdpPrctlModelChecker::computeLongRunAverageProbabilities(Environment const& env, CheckTask const& checkTask) { diff --git a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h index 5ea9a010a..19c2cc6a1 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h @@ -27,7 +27,9 @@ namespace storm { virtual std::unique_ptr computeConditionalProbabilities(Environment const& env, CheckTask const& checkTask) override; virtual std::unique_ptr computeCumulativeRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeInstantaneousRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; + virtual std::unique_ptr computeTotalRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; + virtual std::unique_ptr computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeLongRunAverageProbabilities(Environment const& env, CheckTask const& checkTask) override; virtual std::unique_ptr computeLongRunAverageRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr checkMultiObjectiveFormula(Environment const& env, CheckTask const& checkTask) override; diff --git a/src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.cpp index e8af30350..a6a099833 100644 --- a/src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.cpp @@ -30,7 +30,7 @@ namespace storm { template bool SymbolicDtmcPrctlModelChecker::canHandle(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false)); + return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true)); } template @@ -100,6 +100,16 @@ namespace storm { return std::make_unique>(this->getModel().getReachableStates(), numericResult); } + + template + std::unique_ptr SymbolicDtmcPrctlModelChecker::computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula(); + std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); + SymbolicQualitativeCheckResult const& subResult = subResultPointer->asSymbolicQualitativeCheckResult(); + storm::dd::Add numericResult = storm::modelchecker::helper::SymbolicDtmcPrctlHelper::computeReachabilityTimes(env, this->getModel(), this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet()); + return std::make_unique>(this->getModel().getReachableStates(), numericResult); + } + template class SymbolicDtmcPrctlModelChecker>; template class SymbolicDtmcPrctlModelChecker>; diff --git a/src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.h b/src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.h index 311669bf7..1186c0271 100644 --- a/src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.h +++ b/src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.h @@ -26,7 +26,7 @@ namespace storm { virtual std::unique_ptr computeCumulativeRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeInstantaneousRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; - + virtual std::unique_ptr computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; }; } // namespace modelchecker diff --git a/src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.cpp index 3c813d001..713ce3295 100644 --- a/src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.cpp @@ -30,7 +30,7 @@ namespace storm { template bool SymbolicMdpPrctlModelChecker::canHandle(CheckTask const& checkTask) const { storm::logic::Formula const& formula = checkTask.getFormula(); - return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false)); + return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true)); } template @@ -99,6 +99,15 @@ namespace storm { SymbolicQualitativeCheckResult const& subResult = subResultPointer->asSymbolicQualitativeCheckResult(); return storm::modelchecker::helper::SymbolicMdpPrctlHelper::computeReachabilityRewards(env, checkTask.getOptimizationDirection(), this->getModel(), this->getModel().getTransitionMatrix(), checkTask.isRewardModelSet() ? this->getModel().getRewardModel(checkTask.getRewardModel()) : this->getModel().getRewardModel(""), subResult.getTruthValuesVector()); } + + template + std::unique_ptr SymbolicMdpPrctlModelChecker::computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType, CheckTask const& checkTask) { + storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula(); + STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); + std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); + SymbolicQualitativeCheckResult const& subResult = subResultPointer->asSymbolicQualitativeCheckResult(); + return storm::modelchecker::helper::SymbolicMdpPrctlHelper::computeReachabilityTimes(env, checkTask.getOptimizationDirection(), this->getModel(), this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector()); + } template class SymbolicMdpPrctlModelChecker>; template class SymbolicMdpPrctlModelChecker>; diff --git a/src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.h b/src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.h index b116af0fc..bf259e8b7 100644 --- a/src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.h +++ b/src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.h @@ -27,7 +27,7 @@ namespace storm { virtual std::unique_ptr computeCumulativeRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeInstantaneousRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; virtual std::unique_ptr computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; - + virtual std::unique_ptr computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask const& checkTask) override; }; } // namespace modelchecker diff --git a/src/storm/modelchecker/prctl/helper/HybridDtmcPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/HybridDtmcPrctlHelper.cpp index 659a17cab..ce5bf4467 100644 --- a/src/storm/modelchecker/prctl/helper/HybridDtmcPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/HybridDtmcPrctlHelper.cpp @@ -338,6 +338,12 @@ namespace storm { } } + template + std::unique_ptr HybridDtmcPrctlHelper::computeReachabilityTimes(Environment const& env, storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& targetStates, bool qualitative) { + RewardModelType rewardModel(model.getManager().getConstant(storm::utility::one()), boost::none, boost::none); + return computeReachabilityRewards(env, model, transitionMatrix, rewardModel, targetStates, qualitative); + } + template std::unique_ptr HybridDtmcPrctlHelper::computeLongRunAverageProbabilities(Environment const& env, storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& targetStates) { // Create ODD for the translation. diff --git a/src/storm/modelchecker/prctl/helper/HybridDtmcPrctlHelper.h b/src/storm/modelchecker/prctl/helper/HybridDtmcPrctlHelper.h index 67896c8bc..9e20c5e2c 100644 --- a/src/storm/modelchecker/prctl/helper/HybridDtmcPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/HybridDtmcPrctlHelper.h @@ -37,6 +37,8 @@ namespace storm { static std::unique_ptr computeReachabilityRewards(Environment const& env, storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, RewardModelType const& rewardModel, storm::dd::Bdd const& targetStates, bool qualitative); + static std::unique_ptr computeReachabilityTimes(Environment const& env, storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& targetStates, bool qualitative); + static std::unique_ptr computeLongRunAverageProbabilities(Environment const& env, storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& targetStates); static std::unique_ptr computeLongRunAverageRewards(Environment const& env, storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, RewardModelType const& rewardModel); diff --git a/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp index cca377b9d..7cb054574 100644 --- a/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.cpp @@ -676,6 +676,13 @@ namespace storm { } } + template + std::unique_ptr HybridMdpPrctlHelper::computeReachabilityTimes(Environment const& env, OptimizationDirection dir, storm::models::symbolic::NondeterministicModel const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& targetStates, bool qualitative) { + RewardModelType rewardModel(model.getManager().getConstant(storm::utility::one()), boost::none, boost::none); + return computeReachabilityRewards(env, dir, model, transitionMatrix, rewardModel, targetStates, qualitative); + } + + template class HybridMdpPrctlHelper; template class HybridMdpPrctlHelper; diff --git a/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.h b/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.h index 78ee1f7d3..ce7072624 100644 --- a/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.h @@ -37,6 +37,8 @@ namespace storm { static std::unique_ptr computeInstantaneousRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::NondeterministicModel const& model, storm::dd::Add const& transitionMatrix, RewardModelType const& rewardModel, uint_fast64_t stepBound); static std::unique_ptr computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::NondeterministicModel const& model, storm::dd::Add const& transitionMatrix, RewardModelType const& rewardModel, storm::dd::Bdd const& targetStates, bool qualitative); + + static std::unique_ptr computeReachabilityTimes(Environment const& env, OptimizationDirection dir, storm::models::symbolic::NondeterministicModel const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& targetStates, bool qualitative); }; } diff --git a/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.cpp index 03d3fb1a0..0e5cf1849 100644 --- a/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.cpp @@ -383,6 +383,16 @@ namespace storm { return result; } + template + std::vector SparseDtmcPrctlHelper::computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, RewardModelType const& rewardModel, bool qualitative, ModelCheckerHint const& hint) { + // Identify the states from which only states with zero reward are reachable. + // We can then compute reachability rewards assuming these states as target set. + storm::storage::BitVector statesWithoutReward = rewardModel.getStatesWithZeroReward(transitionMatrix); + storm::storage::BitVector rew0States = storm::utility::graph::performProbGreater0(backwardTransitions, statesWithoutReward, ~statesWithoutReward); + rew0States.complement(); + return computeReachabilityRewards(env, std::move(goal), transitionMatrix, backwardTransitions, rewardModel, rew0States, qualitative, hint); + } + template std::vector SparseDtmcPrctlHelper::computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, RewardModelType const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative, ModelCheckerHint const& hint) { @@ -413,6 +423,21 @@ namespace storm { hint); } + template + std::vector SparseDtmcPrctlHelper::computeReachabilityTimes(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& targetStates, bool qualitative, ModelCheckerHint const& hint) { + + return computeReachabilityRewards(env, std::move(goal), transitionMatrix, backwardTransitions, + [&] (uint_fast64_t numberOfRows, storm::storage::SparseMatrix const&, storm::storage::BitVector const&) { + return std::vector(numberOfRows, storm::utility::one()); + }, + targetStates, qualitative, + [&] () { + return storm::storage::BitVector(transitionMatrix.getRowGroupCount(), false); + }, + hint); + } + + // This function computes an upper bound on the reachability rewards (see Baier et al, CAV'17). template std::vector computeUpperRewardBounds(storm::storage::SparseMatrix const& transitionMatrix, std::vector const& rewards, std::vector const& oneStepTargetProbabilities) { diff --git a/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.h b/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.h index a4e2882bc..16702ffed 100644 --- a/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.h @@ -42,9 +42,13 @@ namespace storm { static std::vector computeInstantaneousRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, RewardModelType const& rewardModel, uint_fast64_t stepCount); + static std::vector computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, RewardModelType const& rewardModel, bool qualitative, ModelCheckerHint const& hint = ModelCheckerHint()); + static std::vector computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, RewardModelType const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative, ModelCheckerHint const& hint = ModelCheckerHint()); static std::vector computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, std::vector const& totalStateRewardVector, storm::storage::BitVector const& targetStates, bool qualitative, ModelCheckerHint const& hint = ModelCheckerHint()); + + static std::vector computeReachabilityTimes(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& targetStates, bool qualitative, ModelCheckerHint const& hint = ModelCheckerHint()); static std::vector computeLongRunAverageProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& psiStates); diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp index f5cbbb2e8..58c94a95b 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp @@ -35,6 +35,8 @@ #include "storm/utility/ProgressMeasurement.h" #include "storm/utility/export.h" +#include "storm/transformer/EndComponentEliminator.h" + #include "storm/environment/solver/MinMaxSolverEnvironment.h" #include "storm/exceptions/InvalidStateException.h" @@ -839,6 +841,79 @@ namespace storm { return result; } + template + template + MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper::computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, RewardModelType const& rewardModel, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint) { + + // Reduce to reachability rewards + if (goal.minimize()) { + STORM_LOG_ERROR_COND(!produceScheduler, "Can not produce scheduler for this property (functionality not implemented"); + // Identify the states from which no reward can be collected under some scheduler + storm::storage::BitVector choicesWithoutReward = rewardModel.getChoicesWithZeroReward(transitionMatrix); + storm::storage::BitVector statesWithZeroRewardChoice(transitionMatrix.getRowGroupCount(), false); + for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { + if (choicesWithoutReward.getNextSetIndex(transitionMatrix.getRowGroupIndices()[state])< transitionMatrix.getRowGroupIndices()[state + 1]) { + statesWithZeroRewardChoice.set(state); + } + } + storm::storage::BitVector rew0EStates = storm::utility::graph::performProbGreater0A(transitionMatrix, transitionMatrix.getRowGroupIndices(), backwardTransitions, statesWithZeroRewardChoice, ~statesWithZeroRewardChoice, false, 0, choicesWithoutReward); + rew0EStates.complement(); + return computeReachabilityRewards(env, std::move(goal), transitionMatrix, backwardTransitions, rewardModel, rew0EStates, qualitative, false, hint); + } else { + // Identify the states from which only states with zero reward are reachable. + storm::storage::BitVector statesWithoutReward = rewardModel.getStatesWithZeroReward(transitionMatrix); + storm::storage::BitVector rew0AStates = storm::utility::graph::performProbGreater0E(backwardTransitions, statesWithoutReward, ~statesWithoutReward); + rew0AStates.complement(); + + // There might be end components that consists only of states/choices with zero rewards. The reachability reward semantics would assign such + // end components reward infinity. To avoid this, we potentially need to eliminate such end components + storm::storage::BitVector trueStates(transitionMatrix.getRowGroupCount(), true); + if (storm::utility::graph::performProb1A(transitionMatrix, transitionMatrix.getRowGroupIndices(), backwardTransitions, trueStates, rew0AStates).full()) { + return computeReachabilityRewards(env, std::move(goal), transitionMatrix, backwardTransitions, rewardModel, rew0AStates, qualitative, produceScheduler, hint); + } else { + // The transformation of schedulers for the ec-eliminated system back to the original one is not implemented. + STORM_LOG_ERROR_COND(!produceScheduler, "Can not produce scheduler for this property (functionality not implemented"); + storm::storage::BitVector choicesWithoutReward = rewardModel.getChoicesWithZeroReward(transitionMatrix); + auto ecElimResult = storm::transformer::EndComponentEliminator::transform(transitionMatrix, storm::storage::BitVector(transitionMatrix.getRowGroupCount(), true), choicesWithoutReward, rew0AStates, true); + storm::storage::BitVector newRew0AStates(ecElimResult.matrix.getRowGroupCount(), false); + for (auto const& oldRew0AState : rew0AStates) { + newRew0AStates.set(ecElimResult.oldToNewStateMapping[oldRew0AState]); + } + + return computeReachabilityRewardsHelper(env, std::move(goal), ecElimResult.matrix, ecElimResult.matrix.transpose(true), + [&] (uint_fast64_t rowCount, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& maybeStates) { + std::vector result; + std::vector oldChoiceRewards = rewardModel.getTotalRewardVector(transitionMatrix); + result.reserve(rowCount); + for (uint64_t newState : maybeStates) { + for (uint64_t newChoice = transitionMatrix.getRowGroupIndices()[newState]; newChoice < transitionMatrix.getRowGroupIndices()[newState + 1]; ++newChoice) { + uint64_t oldChoice = ecElimResult.newToOldRowMapping[newChoice]; + result.push_back(oldChoiceRewards[oldChoice]); + } + } + STORM_LOG_ASSERT(result.size() == rowCount, "Unexpected size of reward vector."); + return result; + }, newRew0AStates, qualitative, false, + [&] () { + storm::storage::BitVector newStatesWithoutReward(ecElimResult.matrix.getRowGroupCount(), false); + for (auto const& oldStateWithoutRew : statesWithoutReward) { + newStatesWithoutReward.set(ecElimResult.oldToNewStateMapping[oldStateWithoutRew]); + } + return newStatesWithoutReward; + }, + [&] () { + storm::storage::BitVector newChoicesWithoutReward(ecElimResult.matrix.getRowGroupCount(), false); + for (uint64_t newChoice = 0; newChoice < ecElimResult.matrix.getRowCount(); ++newChoice) { + if (choicesWithoutReward.get(ecElimResult.newToOldRowMapping[newChoice])) { + newChoicesWithoutReward.set(newChoice); + } + } + return newChoicesWithoutReward; + }); + } + } + } + template template MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper::computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, RewardModelType const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint) { @@ -858,6 +933,22 @@ namespace storm { hint); } + template + MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper::computeReachabilityTimes(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& targetStates, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint) { + return computeReachabilityRewardsHelper(env, std::move(goal), transitionMatrix, backwardTransitions, + [] (uint_fast64_t rowCount, storm::storage::SparseMatrix const&, storm::storage::BitVector const&) { + return std::vector(rowCount, storm::utility::one()); + }, + targetStates, qualitative, produceScheduler, + [&] () { + return storm::storage::BitVector(transitionMatrix.getRowGroupCount(), false); + }, + [&] () { + return storm::storage::BitVector(transitionMatrix.getRowCount(), false); + }, + hint); + } + #ifdef STORM_HAVE_CARL template std::vector SparseMdpPrctlHelper::computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::models::sparse::StandardRewardModel const& intervalRewardModel, bool lowerBoundOfIntervals, storm::storage::BitVector const& targetStates, bool qualitative) { @@ -1284,7 +1375,7 @@ namespace storm { // Now insert all (cumulative) probability values that target an MEC. for (uint_fast64_t mecIndex = 0; mecIndex < auxiliaryStateToProbabilityMap.size(); ++mecIndex) { - if (auxiliaryStateToProbabilityMap[mecIndex] != 0) { + if (!storm::utility::isZero(auxiliaryStateToProbabilityMap[mecIndex])) { sspMatrixBuilder.addNextValue(currentChoice, firstAuxiliaryStateIndex + mecIndex, auxiliaryStateToProbabilityMap[mecIndex]); } } @@ -1319,7 +1410,7 @@ namespace storm { // Now insert all (cumulative) probability values that target an MEC. for (uint_fast64_t targetMecIndex = 0; targetMecIndex < auxiliaryStateToProbabilityMap.size(); ++targetMecIndex) { - if (auxiliaryStateToProbabilityMap[targetMecIndex] != 0) { + if (!storm::utility::isZero(auxiliaryStateToProbabilityMap[targetMecIndex])) { sspMatrixBuilder.addNextValue(currentChoice, firstAuxiliaryStateIndex + targetMecIndex, auxiliaryStateToProbabilityMap[targetMecIndex]); } } @@ -1686,6 +1777,7 @@ namespace storm { template std::vector SparseMdpPrctlHelper::computeInstantaneousRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::models::sparse::StandardRewardModel const& rewardModel, uint_fast64_t stepCount); template std::vector SparseMdpPrctlHelper::computeCumulativeRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::models::sparse::StandardRewardModel const& rewardModel, uint_fast64_t stepBound); template MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper::computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint); + template MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper::computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::models::sparse::StandardRewardModel const& rewardModel, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint); template std::vector SparseMdpPrctlHelper::computeLongRunAverageRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::models::sparse::StandardRewardModel const& rewardModel); template double SparseMdpPrctlHelper::computeLraForMaximalEndComponent(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::MaximalEndComponent const& mec); template double SparseMdpPrctlHelper::computeLraForMaximalEndComponentVI(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::MaximalEndComponent const& mec); @@ -1696,11 +1788,11 @@ namespace storm { template std::vector SparseMdpPrctlHelper::computeInstantaneousRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::models::sparse::StandardRewardModel const& rewardModel, uint_fast64_t stepCount); template std::vector SparseMdpPrctlHelper::computeCumulativeRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::models::sparse::StandardRewardModel const& rewardModel, uint_fast64_t stepBound); template MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper::computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint); + template MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper::computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::models::sparse::StandardRewardModel const& rewardModel, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint); template std::vector SparseMdpPrctlHelper::computeLongRunAverageRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::models::sparse::StandardRewardModel const& rewardModel); template storm::RationalNumber SparseMdpPrctlHelper::computeLraForMaximalEndComponent(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::MaximalEndComponent const& mec); template storm::RationalNumber SparseMdpPrctlHelper::computeLraForMaximalEndComponentVI(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::MaximalEndComponent const& mec); template storm::RationalNumber SparseMdpPrctlHelper::computeLraForMaximalEndComponentLP(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::MaximalEndComponent const& mec); - #endif } } diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h index 920bb4d47..0be4ad060 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h @@ -55,9 +55,14 @@ namespace storm { template static std::vector computeCumulativeRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, RewardModelType const& rewardModel, uint_fast64_t stepBound); + template + static MDPSparseModelCheckingHelperReturnType computeTotalRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, RewardModelType const& rewardModel, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint = ModelCheckerHint()); + template static MDPSparseModelCheckingHelperReturnType computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, RewardModelType const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint = ModelCheckerHint()); + static MDPSparseModelCheckingHelperReturnType computeReachabilityTimes(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& targetStates, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint = ModelCheckerHint()); + #ifdef STORM_HAVE_CARL static std::vector computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::models::sparse::StandardRewardModel const& intervalRewardModel, bool lowerBoundOfIntervals, storm::storage::BitVector const& targetStates, bool qualitative); #endif diff --git a/src/storm/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.cpp index 2b64db267..8f2468a70 100644 --- a/src/storm/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.cpp @@ -202,6 +202,12 @@ namespace storm { return infinityStates.ite(model.getManager().getConstant(storm::utility::infinity()), result); } + template + storm::dd::Add SymbolicDtmcPrctlHelper::computeReachabilityTimes(Environment const& env, storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& targetStates, bool qualitative, boost::optional> const& startValues) { + RewardModelType rewardModel(model.getManager().getConstant(storm::utility::one()), boost::none, boost::none); + return computeReachabilityRewards(env, model, transitionMatrix, rewardModel, targetStates, qualitative, startValues); + } + template class SymbolicDtmcPrctlHelper; template class SymbolicDtmcPrctlHelper; diff --git a/src/storm/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.h b/src/storm/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.h index 995936786..ecf6a6fe4 100644 --- a/src/storm/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/SymbolicDtmcPrctlHelper.h @@ -37,6 +37,8 @@ namespace storm { static storm::dd::Add computeReachabilityRewards(Environment const& env, storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, RewardModelType const& rewardModel, storm::dd::Bdd const& targetStates, bool qualitative, boost::optional> const& startValues = boost::none); static storm::dd::Add computeReachabilityRewards(Environment const& env, storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, RewardModelType const& rewardModel, storm::dd::Bdd const& maybeStates, storm::dd::Bdd const& targetStates, storm::dd::Bdd const& infinityStates, boost::optional> const& startValues = boost::none); + + static storm::dd::Add computeReachabilityTimes(Environment const& env, storm::models::symbolic::Model const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& targetStates, bool qualitative, boost::optional> const& startValues = boost::none); }; } diff --git a/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp index d5c9fea6e..5080d90a8 100644 --- a/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.cpp @@ -300,6 +300,13 @@ namespace storm { } } + template + std::unique_ptr SymbolicMdpPrctlHelper::computeReachabilityTimes(Environment const& env, OptimizationDirection dir, storm::models::symbolic::NondeterministicModel const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& targetStates, boost::optional> const& startValues) { + RewardModelType rewardModel(model.getManager().getConstant(storm::utility::one()), boost::none, boost::none); + return computeReachabilityRewards(env, dir, model, transitionMatrix, rewardModel, targetStates, startValues); + } + + template class SymbolicMdpPrctlHelper; template class SymbolicMdpPrctlHelper; diff --git a/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.h b/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.h index e21342008..51f5f7dd1 100644 --- a/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/SymbolicMdpPrctlHelper.h @@ -41,6 +41,8 @@ namespace storm { static std::unique_ptr computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::NondeterministicModel const& model, storm::dd::Add const& transitionMatrix, RewardModelType const& rewardModel, storm::dd::Bdd const& targetStates, boost::optional> const& startValues = boost::none); static std::unique_ptr computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::NondeterministicModel const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& transitionMatrixBdd, RewardModelType const& rewardModel, storm::dd::Bdd const& maybeStates, storm::dd::Bdd const& targetStates, storm::dd::Bdd const& infinityStates, boost::optional> const& startValues = boost::none); + + static std::unique_ptr computeReachabilityTimes(Environment const& env, OptimizationDirection dir, storm::models::symbolic::NondeterministicModel const& model, storm::dd::Add const& transitionMatrix, storm::dd::Bdd const& targetStates, boost::optional> const& startValues = boost::none); }; } diff --git a/src/storm/models/sparse/MarkovAutomaton.cpp b/src/storm/models/sparse/MarkovAutomaton.cpp index 587a28a7b..e7c296b07 100644 --- a/src/storm/models/sparse/MarkovAutomaton.cpp +++ b/src/storm/models/sparse/MarkovAutomaton.cpp @@ -171,7 +171,7 @@ namespace storm { template bool MarkovAutomaton::isConvertibleToCtmc() const { - return markovianStates.full(); + return isClosed() && markovianStates.full(); } template @@ -203,6 +203,21 @@ namespace storm { template std::shared_ptr> MarkovAutomaton::convertToCtmc() const { + if (isClosed() && markovianStates.full()) { + storm::storage::sparse::ModelComponents components(this->getTransitionMatrix(), this->getStateLabeling(), this->getRewardModels(), false); + components.transitionMatrix.makeRowGroupingTrivial(); + components.exitRates = this->getExitRates(); + if (this->hasChoiceLabeling()) { + components.choiceLabeling = this->getChoiceLabeling(); + } + if (this->hasStateValuations()) { + components.stateValuations = this->getStateValuations(); + } + if (this->hasChoiceOrigins()) { + components.choiceOrigins = this->getChoiceOrigins(); + } + return std::make_shared>(std::move(components)); + } STORM_LOG_TRACE("MA matrix:" << std::endl << this->getTransitionMatrix()); STORM_LOG_TRACE("Markovian states: " << getMarkovianStates()); @@ -249,12 +264,10 @@ namespace storm { //TODO update reward models and choice labels according to kept states STORM_LOG_WARN_COND(this->getRewardModels().empty(), "Conversion of MA to CTMC does not preserve rewards."); - std::unordered_map rewardModels = this->getRewardModels(); STORM_LOG_WARN_COND(!this->hasChoiceLabeling(), "Conversion of MA to CTMC does not preserve choice labels."); STORM_LOG_WARN_COND(!this->hasStateValuations(), "Conversion of MA to CTMC does not preserve choice labels."); STORM_LOG_WARN_COND(!this->hasChoiceOrigins(), "Conversion of MA to CTMC does not preserve choice labels."); - - return std::make_shared>(std::move(rateMatrix), std::move(stateLabeling), std::move(rewardModels)); + return std::make_shared>(std::move(rateMatrix), std::move(stateLabeling)); } diff --git a/src/storm/models/sparse/Model.cpp b/src/storm/models/sparse/Model.cpp index 4bece4dcf..9ee91b5dd 100644 --- a/src/storm/models/sparse/Model.cpp +++ b/src/storm/models/sparse/Model.cpp @@ -423,11 +423,7 @@ namespace storm { template bool Model::supportsParameters() const { -#ifdef STORM_HAVE_CARL return std::is_same::value; -#else - return false; -#endif } template diff --git a/src/storm/models/sparse/NondeterministicModel.cpp b/src/storm/models/sparse/NondeterministicModel.cpp index 263f67c16..08331c6f5 100644 --- a/src/storm/models/sparse/NondeterministicModel.cpp +++ b/src/storm/models/sparse/NondeterministicModel.cpp @@ -45,7 +45,7 @@ namespace storm { } template - std::shared_ptr> NondeterministicModel::applyScheduler(storm::storage::Scheduler const& scheduler, bool dropUnreachableStates) { + std::shared_ptr> NondeterministicModel::applyScheduler(storm::storage::Scheduler const& scheduler, bool dropUnreachableStates) const { storm::storage::SparseModelMemoryProduct memoryProduct(*this, scheduler); if (!dropUnreachableStates) { memoryProduct.setBuildFullProduct(); diff --git a/src/storm/models/sparse/NondeterministicModel.h b/src/storm/models/sparse/NondeterministicModel.h index ac8603e7f..5280ba962 100644 --- a/src/storm/models/sparse/NondeterministicModel.h +++ b/src/storm/models/sparse/NondeterministicModel.h @@ -57,7 +57,7 @@ namespace storm { * @param scheduler the considered scheduler. * @param dropUnreachableStates if set, the resulting model only considers the states that are reachable from an initial state */ - std::shared_ptr> applyScheduler(storm::storage::Scheduler const& scheduler, bool dropUnreachableStates = true); + std::shared_ptr> applyScheduler(storm::storage::Scheduler const& scheduler, bool dropUnreachableStates = true) const; virtual void printModelInformationToStream(std::ostream& out) const override; diff --git a/src/storm/models/symbolic/Model.cpp b/src/storm/models/symbolic/Model.cpp index ee90118cb..f7a9f367f 100644 --- a/src/storm/models/symbolic/Model.cpp +++ b/src/storm/models/symbolic/Model.cpp @@ -15,6 +15,7 @@ #include "storm/models/symbolic/StandardRewardModel.h" +#include "storm/utility/constants.h" #include "storm/utility/macros.h" #include "storm/utility/dd.h" @@ -362,6 +363,26 @@ namespace storm { bool Model::isSymbolicModel() const { return true; } + + template + bool Model::supportsParameters() const { + return std::is_same::value; + } + + template + bool Model::hasParameters() const { + if (!this->supportsParameters()) { + return false; + } + // Check for parameters + for (auto it = this->getTransitionMatrix().begin(false); it != this->getTransitionMatrix().end(); ++it) { + if (!storm::utility::isConstant((*it).second)) { + return true; + } + } + // Only constant values present + return false; + } template void Model::addParameters(std::set const& parameters) { diff --git a/src/storm/models/symbolic/Model.h b/src/storm/models/symbolic/Model.h index 1dca3dc6d..7b3500bd1 100644 --- a/src/storm/models/symbolic/Model.h +++ b/src/storm/models/symbolic/Model.h @@ -321,6 +321,16 @@ namespace storm { virtual bool isSymbolicModel() const override; + virtual bool supportsParameters() const override; + + /*! + * Checks whether the model has parameters. + * Performance warning: the worst-case complexity is linear in the number of transitions. + * + * @return True iff the model has parameters. + */ + virtual bool hasParameters() const override; + std::vector getLabels() const; void addParameters(std::set const& parameters); diff --git a/src/storm/settings/SettingsManager.cpp b/src/storm/settings/SettingsManager.cpp index fc2b80b35..ebc0bc0da 100644 --- a/src/storm/settings/SettingsManager.cpp +++ b/src/storm/settings/SettingsManager.cpp @@ -17,7 +17,6 @@ #include "storm/settings/modules/IOSettings.h" #include "storm/settings/modules/ModelCheckerSettings.h" #include "storm/settings/modules/DebugSettings.h" -#include "storm/settings/modules/CounterexampleGeneratorSettings.h" #include "storm/settings/modules/CuddSettings.h" #include "storm/settings/modules/BuildSettings.h" #include "storm/settings/modules/SylvanSettings.h" @@ -35,7 +34,6 @@ #include "storm/settings/modules/ExplorationSettings.h" #include "storm/settings/modules/ResourceSettings.h" #include "storm/settings/modules/AbstractionSettings.h" -#include "storm/settings/modules/JaniExportSettings.h" #include "storm/settings/modules/JitBuilderSettings.h" #include "storm/settings/modules/MultiObjectiveSettings.h" #include "storm/settings/modules/MultiplierSettings.h" @@ -139,7 +137,7 @@ namespace storm { } // Include the options from a possibly specified configuration file, but don't overwrite existing settings. - if (storm::settings::getModule().isConfigSet()) { + if (storm::settings::hasModule() && storm::settings::getModule().isConfigSet()) { this->setFromConfigurationFile(storm::settings::getModule().getConfigFilename()); } @@ -531,7 +529,6 @@ namespace storm { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); - storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); @@ -548,7 +545,6 @@ namespace storm { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); - storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); diff --git a/src/storm/settings/SettingsManager.h b/src/storm/settings/SettingsManager.h index 1ccb10bfa..1e02e4471 100644 --- a/src/storm/settings/SettingsManager.h +++ b/src/storm/settings/SettingsManager.h @@ -270,6 +270,20 @@ namespace storm { return dynamic_cast(manager().getModule(SettingsType::moduleName)); } + + /*! + * Returns true if the given module is registered. + * + */ + template + bool hasModule() { + static_assert(std::is_base_of::value, "Template argument must be derived from ModuleSettings"); + if (manager().hasModule(SettingsType::moduleName)) { + return dynamic_cast(&(manager().getModule(SettingsType::moduleName))) != nullptr; + } + return false; + } + /*! * Retrieves the core settings in a mutable form. This is only meant to be used for debug purposes or very * rare cases where it is necessary. diff --git a/src/storm/settings/modules/CoreSettings.cpp b/src/storm/settings/modules/CoreSettings.cpp index 8419f209f..343d3c3d1 100644 --- a/src/storm/settings/modules/CoreSettings.cpp +++ b/src/storm/settings/modules/CoreSettings.cpp @@ -45,7 +45,7 @@ namespace storm { std::vector linearEquationSolver = {"gmm++", "native", "eigen", "elimination", "topological"}; this->addOption(storm::settings::OptionBuilder(moduleName, eqSolverOptionName, false, "Sets which solver is preferred for solving systems of linear equations.") - .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the solver to prefer.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(linearEquationSolver)).setDefaultValueString("gmm++").build()).build()); + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the solver to prefer.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(linearEquationSolver)).setDefaultValueString("topological").build()).build()); std::vector ddLibraries = {"cudd", "sylvan"}; this->addOption(storm::settings::OptionBuilder(moduleName, ddLibraryOptionName, false, "Sets which library is preferred for decision-diagram operations.") diff --git a/src/storm/settings/modules/EigenEquationSolverSettings.cpp b/src/storm/settings/modules/EigenEquationSolverSettings.cpp index 78eff7cef..f339a852e 100644 --- a/src/storm/settings/modules/EigenEquationSolverSettings.cpp +++ b/src/storm/settings/modules/EigenEquationSolverSettings.cpp @@ -34,7 +34,7 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, restartOptionName, true, "The number of iteration until restarted methods are actually restarted.").addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The number of iterations.").setDefaultValueUnsignedInteger(50).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").setDefaultValueUnsignedInteger(20000).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, precisionOptionName, false, "The precision used for detecting convergence of iterative methods.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to achieve.").setDefaultValueDouble(1e-06).addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); } diff --git a/src/storm/settings/modules/GameSolverSettings.cpp b/src/storm/settings/modules/GameSolverSettings.cpp index 58cbd3529..84feaba4f 100644 --- a/src/storm/settings/modules/GameSolverSettings.cpp +++ b/src/storm/settings/modules/GameSolverSettings.cpp @@ -23,7 +23,7 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, solvingMethodOptionName, false, "Sets which game solving technique is preferred.") .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of a game solving technique.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(gameSolvingTechniques)).setDefaultValueString("vi").build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").setDefaultValueUnsignedInteger(20000).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, precisionOptionName, false, "The precision used for detecting convergence of iterative methods.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to achieve.").setDefaultValueDouble(1e-06).addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); diff --git a/src/storm/settings/modules/GmmxxEquationSolverSettings.cpp b/src/storm/settings/modules/GmmxxEquationSolverSettings.cpp index 76da580a6..46b7f58fb 100644 --- a/src/storm/settings/modules/GmmxxEquationSolverSettings.cpp +++ b/src/storm/settings/modules/GmmxxEquationSolverSettings.cpp @@ -34,7 +34,7 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, restartOptionName, true, "The number of iteration until restarted methods are actually restarted.").addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The number of iterations.").setDefaultValueUnsignedInteger(50).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").setDefaultValueUnsignedInteger(20000).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, precisionOptionName, false, "The precision used for detecting convergence of iterative methods.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to achieve.").setDefaultValueDouble(1e-06).addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); } diff --git a/src/storm/settings/modules/IOSettings.cpp b/src/storm/settings/modules/IOSettings.cpp index 658b59cbe..2dabdbdb9 100644 --- a/src/storm/settings/modules/IOSettings.cpp +++ b/src/storm/settings/modules/IOSettings.cpp @@ -207,7 +207,11 @@ namespace storm { return this->getOption(janiPropertyOptionName).getHasOptionBeenSet(); } - std::vector IOSettings::getJaniProperties() const { + bool IOSettings::areJaniPropertiesSelected() const { + return this->getOption(janiPropertyOptionName).getHasOptionBeenSet() && (this->getOption(janiPropertyOptionName).getArgumentByName("values").getValueAsString() != ""); + } + + std::vector IOSettings::getSelectedJaniProperties() const { return storm::parser::parseCommaSeperatedValues(this->getOption(janiPropertyOptionName).getArgumentByName("values").getValueAsString()); } diff --git a/src/storm/settings/modules/IOSettings.h b/src/storm/settings/modules/IOSettings.h index cc41176bf..09ed02acb 100644 --- a/src/storm/settings/modules/IOSettings.h +++ b/src/storm/settings/modules/IOSettings.h @@ -233,10 +233,16 @@ namespace storm { */ bool isJaniPropertiesSet() const; + /*! + * Retrieves whether one or more jani-properties have been selected + * @return + */ + bool areJaniPropertiesSelected() const; + /*! * @return The names of the jani properties to check */ - std::vector getJaniProperties() const; + std::vector getSelectedJaniProperties() const; /*! * Retrieves whether the property option was set. diff --git a/src/storm/settings/modules/JaniExportSettings.cpp b/src/storm/settings/modules/JaniExportSettings.cpp deleted file mode 100644 index a56cbc668..000000000 --- a/src/storm/settings/modules/JaniExportSettings.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "JaniExportSettings.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" - -namespace storm { - namespace settings { - namespace modules { - const std::string JaniExportSettings::moduleName = "exportJani"; - - const std::string JaniExportSettings::janiFileOptionName = "jani-output"; - const std::string JaniExportSettings::janiFileOptionShortName = "output"; - const std::string JaniExportSettings::standardCompliantOptionName = "standard-compliant"; - const std::string JaniExportSettings::standardCompliantOptionShortName = "standard"; - - - JaniExportSettings::JaniExportSettings() : ModuleSettings(moduleName) { - this->addOption(storm::settings::OptionBuilder(moduleName, janiFileOptionName, false, "Destination for the jani model.").setShortName(janiFileOptionShortName).addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "path to file").build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, standardCompliantOptionName, false, "Export in standard compliant variant.").setShortName(standardCompliantOptionShortName).build()); - } - - bool JaniExportSettings::isJaniFileSet() const { - return this->getOption(janiFileOptionName).getHasOptionBeenSet(); - } - - std::string JaniExportSettings::getJaniFilename() const { - return this->getOption(janiFileOptionName).getArgumentByName("filename").getValueAsString(); - } - - bool JaniExportSettings::isExportAsStandardJaniSet() const { - return this->getOption(standardCompliantOptionName).getHasOptionBeenSet(); - } - - void JaniExportSettings::finalize() { - - } - - bool JaniExportSettings::check() const { - return true; - } - } - } -} diff --git a/src/storm/settings/modules/MinMaxEquationSolverSettings.cpp b/src/storm/settings/modules/MinMaxEquationSolverSettings.cpp index 186f0caf9..95accf94b 100644 --- a/src/storm/settings/modules/MinMaxEquationSolverSettings.cpp +++ b/src/storm/settings/modules/MinMaxEquationSolverSettings.cpp @@ -25,9 +25,9 @@ namespace storm { MinMaxEquationSolverSettings::MinMaxEquationSolverSettings() : ModuleSettings(moduleName) { std::vector minMaxSolvingTechniques = {"vi", "value-iteration", "pi", "policy-iteration", "lp", "linear-programming", "rs", "ratsearch", "ii", "interval-iteration", "svi", "sound-value-iteration", "topological"}; this->addOption(storm::settings::OptionBuilder(moduleName, solvingMethodOptionName, false, "Sets which min/max linear equation solving technique is preferred.") - .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of a min/max linear equation solving technique.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(minMaxSolvingTechniques)).setDefaultValueString("vi").build()).build()); + .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of a min/max linear equation solving technique.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(minMaxSolvingTechniques)).setDefaultValueString("topological").build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").setDefaultValueUnsignedInteger(20000).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, precisionOptionName, false, "The precision used for detecting convergence of iterative methods.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to achieve.").setDefaultValueDouble(1e-06).addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); diff --git a/src/storm/settings/modules/NativeEquationSolverSettings.cpp b/src/storm/settings/modules/NativeEquationSolverSettings.cpp index 3f0282f0a..f96bd8714 100644 --- a/src/storm/settings/modules/NativeEquationSolverSettings.cpp +++ b/src/storm/settings/modules/NativeEquationSolverSettings.cpp @@ -29,7 +29,7 @@ namespace storm { std::vector methods = { "jacobi", "gaussseidel", "sor", "walkerchae", "power", "sound-value-iteration", "svi", "interval-iteration", "ii", "ratsearch" }; this->addOption(storm::settings::OptionBuilder(moduleName, techniqueOptionName, true, "The method to be used for solving linear equation systems with the native engine.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the method to use.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(methods)).setDefaultValueString("jacobi").build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").setDefaultValueUnsignedInteger(20000).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, precisionOptionName, false, "The precision used for detecting convergence of iterative methods.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to achieve.").setDefaultValueDouble(1e-06).addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); diff --git a/src/storm/solver/EigenLinearEquationSolver.cpp b/src/storm/solver/EigenLinearEquationSolver.cpp index afbc88a3b..55046a495 100644 --- a/src/storm/solver/EigenLinearEquationSolver.cpp +++ b/src/storm/solver/EigenLinearEquationSolver.cpp @@ -116,7 +116,10 @@ namespace storm { } else { bool converged = false; uint64_t numberOfIterations = 0; - uint64_t maxIter = env.solver().eigen().getMaximalNumberOfIterations(); + StormEigen::Index maxIter = std::numeric_limits::max(); + if (env.solver().eigen().getMaximalNumberOfIterations() < static_cast(maxIter)) { + maxIter = env.solver().eigen().getMaximalNumberOfIterations(); + } uint64_t restartThreshold = env.solver().eigen().getRestartThreshold(); ValueType precision = storm::utility::convertNumber(env.solver().eigen().getPrecision()); EigenLinearEquationSolverPreconditioner preconditioner = env.solver().eigen().getPreconditioner(); diff --git a/src/storm/solver/GmmxxLinearEquationSolver.cpp b/src/storm/solver/GmmxxLinearEquationSolver.cpp index 83d5b6ef6..9ce51fa72 100644 --- a/src/storm/solver/GmmxxLinearEquationSolver.cpp +++ b/src/storm/solver/GmmxxLinearEquationSolver.cpp @@ -65,7 +65,11 @@ namespace storm { } // Prepare an iteration object that determines the accuracy and the maximum number of iterations. - gmm::iteration iter(storm::utility::convertNumber(env.solver().gmmxx().getPrecision()), 0, env.solver().gmmxx().getMaximalNumberOfIterations()); + gmm::size_type maxIter = std::numeric_limits::max(); + if (env.solver().gmmxx().getMaximalNumberOfIterations() < static_cast(maxIter)) { + maxIter = env.solver().gmmxx().getMaximalNumberOfIterations(); + } + gmm::iteration iter(storm::utility::convertNumber(env.solver().gmmxx().getPrecision()), 0, maxIter); // Invoke gmm with the corresponding settings if (method == GmmxxLinearEquationSolverMethod::Bicgstab) { @@ -103,10 +107,10 @@ namespace storm { // Check if the solver converged and issue a warning otherwise. if (iter.converged()) { - STORM_LOG_INFO("Iterative solver converged after " << iter.get_iteration() << " iterations."); + STORM_LOG_INFO("Iterative solver converged after " << iter.get_iteration() << " iteration(s)."); return true; } else { - STORM_LOG_WARN("Iterative solver did not converge."); + STORM_LOG_WARN("Iterative solver did not converge within " << iter.get_iteration() << " iteration(s)."); return false; } } diff --git a/src/storm/solver/StandardGameSolver.cpp b/src/storm/solver/StandardGameSolver.cpp index 6140a5c15..9656af643 100644 --- a/src/storm/solver/StandardGameSolver.cpp +++ b/src/storm/solver/StandardGameSolver.cpp @@ -123,7 +123,9 @@ namespace storm { } else { // Update the solver. getInducedMatrixVector(x, b, player1Choices, player2Choices, submatrix, subB); - submatrix.convertToEquationSystem(); + if (this->linearEquationSolverFactory->getEquationProblemFormat(environmentOfSolver) == LinearEquationSolverProblemFormat::EquationSystem) { + submatrix.convertToEquationSystem(); + } submatrixSolver->setMatrix(std::move(submatrix)); } diff --git a/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp b/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp index b599face7..c54df3acf 100644 --- a/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/SymbolicMinMaxLinearEquationSolver.cpp @@ -40,7 +40,10 @@ namespace storm { STORM_LOG_WARN("The selected solution method does not guarantee exact results."); } } - STORM_LOG_THROW(method == MinMaxMethod::ValueIteration || method == MinMaxMethod::PolicyIteration || method == MinMaxMethod::RationalSearch, storm::exceptions::InvalidEnvironmentException, "This solver does not support the selected method."); + if (method != MinMaxMethod::ValueIteration && method != MinMaxMethod::PolicyIteration && method != MinMaxMethod::RationalSearch) { + STORM_LOG_WARN("Selected method is not supported for this solver, switching to value iteration."); + method = MinMaxMethod::ValueIteration; + } return method; } diff --git a/src/storm/solver/TopologicalLinearEquationSolver.cpp b/src/storm/solver/TopologicalLinearEquationSolver.cpp index 7568f3c65..877ba76dd 100644 --- a/src/storm/solver/TopologicalLinearEquationSolver.cpp +++ b/src/storm/solver/TopologicalLinearEquationSolver.cpp @@ -68,7 +68,7 @@ namespace storm { storm::Environment sccSolverEnvironment = getEnvironmentForUnderlyingSolver(env, needAdaptPrecision); - STORM_LOG_INFO("Found " << this->sortedSccDecomposition->size() << "SCCs. Average size is " << static_cast(this->getMatrixRowCount()) / static_cast(this->sortedSccDecomposition->size()) << "."); + STORM_LOG_INFO("Found " << this->sortedSccDecomposition->size() << " SCC(s). Average size is " << static_cast(this->getMatrixRowCount()) / static_cast(this->sortedSccDecomposition->size()) << "."); if (this->longestSccChainSize) { STORM_LOG_INFO("Longest SCC chain size is " << this->longestSccChainSize.get() << "."); } @@ -169,8 +169,8 @@ namespace storm { if (asEquationSystem) { sccA.convertToEquationSystem(); } - //std::cout << "Solving SCC " << scc << std::endl; - //std::cout << "Matrix is " << sccA << std::endl; +// std::cout << "Solving SCC " << scc << std::endl; +// std::cout << "Matrix is " << sccA << std::endl; this->sccSolver->setMatrix(std::move(sccA)); // x Vector diff --git a/src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp b/src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp index 92692b966..d6c1b69e1 100644 --- a/src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp @@ -190,6 +190,10 @@ namespace storm { if (req.lowerBounds() && this->hasLowerBound()) { req.clearLowerBounds(); } + + // If all requirements of the underlying solver have been passed as requirements to the calling site, we can + // assume that the system has no end components if the underlying solver requires this. + req.clearNoEndComponents(); STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException, "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked."); this->sccSolver->setRequirementsChecked(true); diff --git a/src/storm/solver/helper/SoundValueIterationHelper.cpp b/src/storm/solver/helper/SoundValueIterationHelper.cpp index 6e47bfe3e..674ea5160 100644 --- a/src/storm/solver/helper/SoundValueIterationHelper.cpp +++ b/src/storm/solver/helper/SoundValueIterationHelper.cpp @@ -301,14 +301,14 @@ namespace storm { storm::utility::vector::applyPointwise(x, y, x, [&meanBound] (ValueType const& xi, ValueType const& yi) -> ValueType { return xi + yi * meanBound; }); - STORM_LOG_INFO("Sound Value Iteration terminated with lower value bound " + STORM_LOG_INFO("Sound Value Iteration terminated with lower bound (over all states) " << (hasLowerBound ? lowerBound : storm::utility::zero()) << (hasLowerBound ? "" : "(none)") - << " and upper value bound " - << (hasUpperBound ? upperBound : storm::utility::zero()) << (hasUpperBound ? "" : "(none)") + << " and upper bound (over all states) " + << (hasUpperBound ? upperBound : storm::utility::infinity()) << (hasUpperBound ? "" : "(none)") << ". Decision value is " - << (hasDecisionValue ? decisionValue : storm::utility::zero()) << (hasDecisionValue ? "" : "(none)") + << (hasDecisionValue ? decisionValue : -storm::utility::infinity()) << (hasDecisionValue ? "" : "(none)") << "."); - } + } template bool SoundValueIterationHelper::checkCustomTerminationCondition(storm::solver::TerminationCondition const& condition) { diff --git a/src/storm/solver/stateelimination/DynamicStatePriorityQueue.h b/src/storm/solver/stateelimination/DynamicStatePriorityQueue.h index 0ac38769d..4065ee265 100644 --- a/src/storm/solver/stateelimination/DynamicStatePriorityQueue.h +++ b/src/storm/solver/stateelimination/DynamicStatePriorityQueue.h @@ -17,7 +17,7 @@ namespace storm { namespace stateelimination { struct PriorityComparator { - bool operator()(std::pair const& first, std::pair const& second) { + bool operator()(std::pair const& first, std::pair const& second) const { return (first.second < second.second) || (first.second == second.second && first.first < second.first) ; } }; diff --git a/src/storm/storage/MaximalEndComponentDecomposition.cpp b/src/storm/storage/MaximalEndComponentDecomposition.cpp index cd8b637e3..7cbf36fd7 100644 --- a/src/storm/storage/MaximalEndComponentDecomposition.cpp +++ b/src/storm/storage/MaximalEndComponentDecomposition.cpp @@ -80,7 +80,20 @@ namespace storm { endComponentStateSets.emplace_back(states.begin(), states.end(), true); } storm::storage::BitVector statesToCheck(numberOfStates); - + storm::storage::BitVector includedChoices; + if (choices) { + includedChoices = *choices; + } else if (states) { + includedChoices = storm::storage::BitVector(transitionMatrix.getRowCount()); + for (auto state : *states) { + for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) { + includedChoices.set(choice, true); + } + } + } else { + includedChoices = storm::storage::BitVector(transitionMatrix.getRowCount(), true); + } + for (std::list::const_iterator mecIterator = endComponentStateSets.begin(); mecIterator != endComponentStateSets.end();) { StateBlock const& mec = *mecIterator; @@ -88,7 +101,7 @@ namespace storm { bool mecChanged = false; // Get an SCC decomposition of the current MEC candidate. - StronglyConnectedComponentDecomposition sccs(transitionMatrix, mec, true); + StronglyConnectedComponentDecomposition sccs(transitionMatrix, mec, includedChoices, true); // We need to do another iteration in case we have either more than once SCC or the SCC is smaller than // the MEC canditate itself. @@ -105,10 +118,16 @@ namespace storm { bool keepStateInMEC = false; for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) { + // If the choice is not part of our subsystem, skip it. if (choices && !choices->get(choice)) { continue; } + + // If the choice is not included any more, skip it. + if (!includedChoices.get(choice)) { + continue; + } bool choiceContainedInMEC = true; for (auto const& entry : transitionMatrix.getRow(choice)) { @@ -117,6 +136,7 @@ namespace storm { } if (!scc.containsState(entry.getColumn())) { + includedChoices.set(choice, false); choiceContainedInMEC = false; break; } @@ -125,7 +145,6 @@ namespace storm { // If there is at least one choice whose successor states are fully contained in the MEC, we can leave the state in the MEC. if (choiceContainedInMEC) { keepStateInMEC = true; - break; } } @@ -185,15 +204,7 @@ namespace storm { continue; } - bool choiceContained = true; - for (auto const& entry : transitionMatrix.getRow(choice)) { - if (!mecStateSet.containsState(entry.getColumn())) { - choiceContained = false; - break; - } - } - - if (choiceContained) { + if (includedChoices.get(choice)) { containedChoices.insert(choice); } } diff --git a/src/storm/storage/StronglyConnectedComponentDecomposition.cpp b/src/storm/storage/StronglyConnectedComponentDecomposition.cpp index f6567985c..6ab3e31f6 100644 --- a/src/storm/storage/StronglyConnectedComponentDecomposition.cpp +++ b/src/storm/storage/StronglyConnectedComponentDecomposition.cpp @@ -23,30 +23,40 @@ namespace storm { template StronglyConnectedComponentDecomposition::StronglyConnectedComponentDecomposition(storm::models::sparse::Model const& model, StateBlock const& block, bool dropNaiveSccs, bool onlyBottomSccs) { storm::storage::BitVector subsystem(model.getNumberOfStates(), block.begin(), block.end()); - performSccDecomposition(model.getTransitionMatrix(), subsystem, dropNaiveSccs, onlyBottomSccs); + performSccDecomposition(model.getTransitionMatrix(), &subsystem, nullptr, dropNaiveSccs, onlyBottomSccs); } template template StronglyConnectedComponentDecomposition::StronglyConnectedComponentDecomposition(storm::models::sparse::Model const& model, storm::storage::BitVector const& subsystem, bool dropNaiveSccs, bool onlyBottomSccs) { - performSccDecomposition(model.getTransitionMatrix(), subsystem, dropNaiveSccs, onlyBottomSccs); + performSccDecomposition(model.getTransitionMatrix(), &subsystem, nullptr, dropNaiveSccs, onlyBottomSccs); } template StronglyConnectedComponentDecomposition::StronglyConnectedComponentDecomposition(storm::storage::SparseMatrix const& transitionMatrix, StateBlock const& block, bool dropNaiveSccs, bool onlyBottomSccs) { storm::storage::BitVector subsystem(transitionMatrix.getRowGroupCount(), block.begin(), block.end()); - performSccDecomposition(transitionMatrix, subsystem, dropNaiveSccs, onlyBottomSccs); + performSccDecomposition(transitionMatrix, &subsystem, nullptr, dropNaiveSccs, onlyBottomSccs); } + template + StronglyConnectedComponentDecomposition::StronglyConnectedComponentDecomposition(storm::storage::SparseMatrix const& transitionMatrix, StateBlock const& block, storm::storage::BitVector const& choices, bool dropNaiveSccs, bool onlyBottomSccs) { + storm::storage::BitVector subsystem(transitionMatrix.getRowGroupCount(), block.begin(), block.end()); + performSccDecomposition(transitionMatrix, &subsystem, &choices, dropNaiveSccs, onlyBottomSccs); + } template StronglyConnectedComponentDecomposition::StronglyConnectedComponentDecomposition(storm::storage::SparseMatrix const& transitionMatrix, bool dropNaiveSccs, bool onlyBottomSccs) { - performSccDecomposition(transitionMatrix, storm::storage::BitVector(transitionMatrix.getRowGroupCount(), true), dropNaiveSccs, onlyBottomSccs); + performSccDecomposition(transitionMatrix, nullptr, nullptr, dropNaiveSccs, onlyBottomSccs); } template StronglyConnectedComponentDecomposition::StronglyConnectedComponentDecomposition(storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& subsystem, bool dropNaiveSccs, bool onlyBottomSccs) { - performSccDecomposition(transitionMatrix, subsystem, dropNaiveSccs, onlyBottomSccs); + performSccDecomposition(transitionMatrix, &subsystem, nullptr, dropNaiveSccs, onlyBottomSccs); + } + + template + StronglyConnectedComponentDecomposition::StronglyConnectedComponentDecomposition(storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& subsystem, storm::storage::BitVector const& choices, bool dropNaiveSccs, bool onlyBottomSccs) { + performSccDecomposition(transitionMatrix, &subsystem, &choices, dropNaiveSccs, onlyBottomSccs); } template @@ -72,7 +82,10 @@ namespace storm { } template - void StronglyConnectedComponentDecomposition::performSccDecomposition(storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& subsystem, bool dropNaiveSccs, bool onlyBottomSccs) { + void StronglyConnectedComponentDecomposition::performSccDecomposition(storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const* subsystem, storm::storage::BitVector const* choices, bool dropNaiveSccs, bool onlyBottomSccs) { + + STORM_LOG_ASSERT(!choices || subsystem, "Expecting subsystem if choices are given."); + uint_fast64_t numberOfStates = transitionMatrix.getRowGroupCount(); // Set up the environment of the algorithm. @@ -94,16 +107,30 @@ namespace storm { // Start the search for SCCs from every state in the block. uint_fast64_t currentIndex = 0; - for (auto state : subsystem) { - if (!hasPreorderNumber.get(state)) { - performSccDecompositionGCM(transitionMatrix, state, statesWithSelfLoop, subsystem, currentIndex, hasPreorderNumber, preorderNumbers, s, p, stateHasScc, stateToSccMapping, sccCount); + if (subsystem) { + for (auto state : *subsystem) { + if (!hasPreorderNumber.get(state)) { + performSccDecompositionGCM(transitionMatrix, state, statesWithSelfLoop, subsystem, choices, currentIndex, hasPreorderNumber, preorderNumbers, s, p, stateHasScc, stateToSccMapping, sccCount); + } + } + } else { + for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { + if (!hasPreorderNumber.get(state)) { + performSccDecompositionGCM(transitionMatrix, state, statesWithSelfLoop, subsystem, choices, currentIndex, hasPreorderNumber, preorderNumbers, s, p, stateHasScc, stateToSccMapping, sccCount); + } } } // After we obtained the state-to-SCC mapping, we build the actual blocks. this->blocks.resize(sccCount); - for (auto state : subsystem) { - this->blocks[stateToSccMapping[state]].insert(state); + if (subsystem) { + for (auto state : *subsystem) { + this->blocks[stateToSccMapping[state]].insert(state); + } + } else { + for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { + this->blocks[stateToSccMapping[state]].insert(state); + } } // Now flag all trivial SCCs as such. @@ -132,13 +159,34 @@ namespace storm { // If requested, we need to drop all non-bottom SCCs. if (onlyBottomSccs) { - for (uint_fast64_t state = 0; state < numberOfStates; ++state) { - // If the block of the state is already known to be dropped, we don't need to check the transitions. - if (!blocksToDrop.get(stateToSccMapping[state])) { - for (typename storm::storage::SparseMatrix::const_iterator successorIt = transitionMatrix.getRowGroup(state).begin(), successorIte = transitionMatrix.getRowGroup(state).end(); successorIt != successorIte; ++successorIt) { - if (subsystem.get(successorIt->getColumn()) && stateToSccMapping[state] != stateToSccMapping[successorIt->getColumn()]) { - blocksToDrop.set(stateToSccMapping[state]); - break; + if (subsystem) { + for (uint64_t state : *subsystem) { + // If the block of the state is already known to be dropped, we don't need to check the transitions. + if (!blocksToDrop.get(stateToSccMapping[state])) { + for (uint64_t row = transitionMatrix.getRowGroupIndices()[state], endRow = transitionMatrix.getRowGroupIndices()[state + 1]; row != endRow; ++row) { + if (choices && !choices->get(row)) { + continue; + } + for (auto const& entry : transitionMatrix.getRow(row)) { + if (subsystem->get(entry.getColumn()) && stateToSccMapping[state] != stateToSccMapping[entry.getColumn()]) { + blocksToDrop.set(stateToSccMapping[state]); + break; + } + } + } + } + } + } else { + for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { + // If the block of the state is already known to be dropped, we don't need to check the transitions. + if (!blocksToDrop.get(stateToSccMapping[state])) { + for (uint64_t row = transitionMatrix.getRowGroupIndices()[state], endRow = transitionMatrix.getRowGroupIndices()[state + 1]; row != endRow; ++row) { + for (auto const& entry : transitionMatrix.getRow(row)) { + if (stateToSccMapping[state] != stateToSccMapping[entry.getColumn()]) { + blocksToDrop.set(stateToSccMapping[state]); + break; + } + } } } } @@ -163,15 +211,11 @@ namespace storm { template template void StronglyConnectedComponentDecomposition::performSccDecomposition(storm::models::sparse::Model const& model, bool dropNaiveSccs, bool onlyBottomSccs) { - // Prepare a block that contains all states for a call to the other overload of this function. - storm::storage::BitVector fullSystem(model.getNumberOfStates(), true); - - // Call the overloaded function. - performSccDecomposition(model.getTransitionMatrix(), fullSystem, dropNaiveSccs, onlyBottomSccs); + performSccDecomposition(model.getTransitionMatrix(), nullptr, nullptr, dropNaiveSccs, onlyBottomSccs); } template - void StronglyConnectedComponentDecomposition::performSccDecompositionGCM(storm::storage::SparseMatrix const& transitionMatrix, uint_fast64_t startState, storm::storage::BitVector& statesWithSelfLoop, storm::storage::BitVector const& subsystem, uint_fast64_t& currentIndex, storm::storage::BitVector& hasPreorderNumber, std::vector& preorderNumbers, std::vector& s, std::vector& p, storm::storage::BitVector& stateHasScc, std::vector& stateToSccMapping, uint_fast64_t& sccCount) { + void StronglyConnectedComponentDecomposition::performSccDecompositionGCM(storm::storage::SparseMatrix const& transitionMatrix, uint_fast64_t startState, storm::storage::BitVector& statesWithSelfLoop, storm::storage::BitVector const* subsystem, storm::storage::BitVector const* choices, uint_fast64_t& currentIndex, storm::storage::BitVector& hasPreorderNumber, std::vector& preorderNumbers, std::vector& s, std::vector& p, storm::storage::BitVector& stateHasScc, std::vector& stateToSccMapping, uint_fast64_t& sccCount) { // Prepare the stack used for turning the recursive procedure into an iterative one. std::vector recursionStateStack; @@ -190,20 +234,26 @@ namespace storm { s.push_back(currentState); p.push_back(currentState); - for (auto const& successor : transitionMatrix.getRowGroup(currentState)) { - if (subsystem.get(successor.getColumn()) && successor.getValue() != storm::utility::zero()) { - if (currentState == successor.getColumn()) { - statesWithSelfLoop.set(currentState); - } - - if (!hasPreorderNumber.get(successor.getColumn())) { - // In this case, we must recursively visit the successor. We therefore push the state - // onto the recursion stack. - recursionStateStack.push_back(successor.getColumn()); - } else { - if (!stateHasScc.get(successor.getColumn())) { - while (preorderNumbers[p.back()] > preorderNumbers[successor.getColumn()]) { - p.pop_back(); + for (uint64_t row = transitionMatrix.getRowGroupIndices()[currentState], rowEnd = transitionMatrix.getRowGroupIndices()[currentState + 1]; row != rowEnd; ++row) { + if (choices && !choices->get(row)) { + continue; + } + + for (auto const& successor : transitionMatrix.getRow(row)) { + if ((!subsystem || subsystem->get(successor.getColumn())) && successor.getValue() != storm::utility::zero()) { + if (currentState == successor.getColumn()) { + statesWithSelfLoop.set(currentState); + } + + if (!hasPreorderNumber.get(successor.getColumn())) { + // In this case, we must recursively visit the successor. We therefore push the state + // onto the recursion stack. + recursionStateStack.push_back(successor.getColumn()); + } else { + if (!stateHasScc.get(successor.getColumn())) { + while (preorderNumbers[p.back()] > preorderNumbers[successor.getColumn()]) { + p.pop_back(); + } } } } diff --git a/src/storm/storage/StronglyConnectedComponentDecomposition.h b/src/storm/storage/StronglyConnectedComponentDecomposition.h index 04cebff92..7d244b914 100644 --- a/src/storm/storage/StronglyConnectedComponentDecomposition.h +++ b/src/storm/storage/StronglyConnectedComponentDecomposition.h @@ -78,7 +78,21 @@ namespace storm { * leaving the SCC), are kept. */ StronglyConnectedComponentDecomposition(storm::storage::SparseMatrix const& transitionMatrix, StateBlock const& block, bool dropNaiveSccs = false, bool onlyBottomSccs = false); - + + /* + * Creates an SCC decomposition of the given subsystem in the given system (whose transition relation is + * given by a sparse matrix). + * + * @param transitionMatrix The transition matrix of the system to decompose. + * @param block The block to decompose into SCCs. + * @param choices A bit vector indicating which choices of the states are contained in the subsystem. + * @param dropNaiveSccs A flag that indicates whether trivial SCCs (i.e. SCCs consisting of just one state + * without a self-loop) are to be kept in the decomposition. + * @param onlyBottomSccs If set to true, only bottom SCCs, i.e. SCCs in which all states have no way of + * leaving the SCC), are kept. + */ + StronglyConnectedComponentDecomposition(storm::storage::SparseMatrix const& transitionMatrix, StateBlock const& block, storm::storage::BitVector const& choices, bool dropNaiveSccs = false, bool onlyBottomSccs = false); + /* * Creates an SCC decomposition of the given system (whose transition relation is given by a sparse matrix). * @@ -103,6 +117,20 @@ namespace storm { */ StronglyConnectedComponentDecomposition(storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& subsystem, bool dropNaiveSccs = false, bool onlyBottomSccs = false); + /* + * Creates an SCC decomposition of the given subsystem in the given system (whose transition relation is + * given by a sparse matrix). + * + * @param transitionMatrix The transition matrix of the system to decompose. + * @param subsystem A bit vector indicating which subsystem to consider for the decomposition into SCCs. + * @param choices A bit vector indicating which choices of the states are contained in the subsystem. + * @param dropNaiveSccs A flag that indicates whether trivial SCCs (i.e. SCCs consisting of just one state + * without a self-loop) are to be kept in the decomposition. + * @param onlyBottomSccs If set to true, only bottom SCCs, i.e. SCCs in which all states have no way of + * leaving the SCC), are kept. + */ + StronglyConnectedComponentDecomposition(storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& subsystem, storm::storage::BitVector const& choices, bool dropNaiveSccs = false, bool onlyBottomSccs = false); + /*! * Creates an SCC decomposition by copying the given SCC decomposition. * @@ -158,13 +186,14 @@ namespace storm { * the vector of blocks of the decomposition. * * @param transitionMatrix The transition matrix of the system to decompose. - * @param subsystem A bit vector indicating which subsystem to consider for the decomposition into SCCs. + * @param subsystem An optional bit vector indicating which subsystem to consider. + * @param choices An optional bit vector indicating which choices belong to the subsystem. * @param dropNaiveSccs A flag that indicates whether trivial SCCs (i.e. SCCs consisting of just one state * without a self-loop) are to be kept in the decomposition. * @param onlyBottomSccs If set to true, only bottom SCCs, i.e. SCCs in which all states have no way of * leaving the SCC), are kept. */ - void performSccDecomposition(storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& subsystem, bool dropNaiveSccs, bool onlyBottomSccs); + void performSccDecomposition(storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const* subsystem, storm::storage::BitVector const* choices, bool dropNaiveSccs, bool onlyBottomSccs); /*! * Uses the algorithm by Gabow/Cheriyan/Mehlhorn ("Path-based strongly connected component algorithm") to @@ -175,7 +204,8 @@ namespace storm { * @param startState The starting state for the search of Tarjan's algorithm. * @param statesWithSelfLoop A bit vector that is to be filled with all states that have a self-loop. This * is later needed for identification of the naive SCCs. - * @param subsystem The subsystem to search. + * @param subsystem An optional bit vector indicating which subsystem to consider. + * @param choices An optional bit vector indicating which choices belong to the subsystem. * @param currentIndex The next free index that can be assigned to states. * @param hasPreorderNumber A bit that is used to keep track of the states that already have a preorder number. * @param preorderNumbers A vector storing the preorder number for each state. @@ -187,7 +217,7 @@ namespace storm { * @param sccCount The number of SCCs that have been computed. As a side effect of this function, this count * is increased. */ - void performSccDecompositionGCM(storm::storage::SparseMatrix const& transitionMatrix, uint_fast64_t startState, storm::storage::BitVector& statesWithSelfLoop, storm::storage::BitVector const& subsystem, uint_fast64_t& currentIndex, storm::storage::BitVector& hasPreorderNumber, std::vector& preorderNumbers, std::vector& s, std::vector& p, storm::storage::BitVector& stateHasScc, std::vector& stateToSccMapping, uint_fast64_t& sccCount); + void performSccDecompositionGCM(storm::storage::SparseMatrix const& transitionMatrix, uint_fast64_t startState, storm::storage::BitVector& statesWithSelfLoop, storm::storage::BitVector const* subsystem, storm::storage::BitVector const* choices, uint_fast64_t& currentIndex, storm::storage::BitVector& hasPreorderNumber, std::vector& preorderNumbers, std::vector& s, std::vector& p, storm::storage::BitVector& stateHasScc, std::vector& stateToSccMapping, uint_fast64_t& sccCount); }; } } diff --git a/src/storm/storage/SymbolicModelDescription.cpp b/src/storm/storage/SymbolicModelDescription.cpp index b08166125..79a12f8b1 100644 --- a/src/storm/storage/SymbolicModelDescription.cpp +++ b/src/storm/storage/SymbolicModelDescription.cpp @@ -119,23 +119,23 @@ namespace storm { return result; } - SymbolicModelDescription SymbolicModelDescription::toJani(bool makeVariablesGlobal) const { + SymbolicModelDescription SymbolicModelDescription::toJani(bool makeVariablesGlobal, bool standardCompliant) const { if (this->isJaniModel()) { return *this; } if (this->isPrismProgram()) { - return SymbolicModelDescription(this->asPrismProgram().toJani(makeVariablesGlobal)); + return SymbolicModelDescription(this->asPrismProgram().toJani(makeVariablesGlobal, "", standardCompliant)); } else { STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Cannot transform model description to the JANI format."); } } - std::pair> SymbolicModelDescription::toJaniWithLabelRenaming(bool makeVariablesGlobal) const { + std::pair> SymbolicModelDescription::toJaniWithLabelRenaming(bool makeVariablesGlobal, bool standardCompliant) const { if (this->isJaniModel()) { return std::make_pair(*this, std::map()); } if (this->isPrismProgram()) { - auto modelAndRenaming = this->asPrismProgram().toJaniWithLabelRenaming(makeVariablesGlobal); + auto modelAndRenaming = this->asPrismProgram().toJaniWithLabelRenaming(makeVariablesGlobal, "", standardCompliant); return std::make_pair(SymbolicModelDescription(modelAndRenaming.first), modelAndRenaming.second); } else { STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Cannot transform model description to the JANI format."); diff --git a/src/storm/storage/SymbolicModelDescription.h b/src/storm/storage/SymbolicModelDescription.h index 137f1a7d4..3ab94ba5d 100644 --- a/src/storm/storage/SymbolicModelDescription.h +++ b/src/storm/storage/SymbolicModelDescription.h @@ -38,8 +38,8 @@ namespace storm { std::vector getParameterNames() const; - SymbolicModelDescription toJani(bool makeVariablesGlobal = true) const; - std::pair> toJaniWithLabelRenaming(bool makeVariablesGlobal = true) const; + SymbolicModelDescription toJani(bool makeVariablesGlobal = true, bool standardCompliant = false) const; + std::pair> toJaniWithLabelRenaming(bool makeVariablesGlobal = true, bool standardCompliant = false) const; SymbolicModelDescription preprocess(std::string const& constantDefinitionString = "") const; SymbolicModelDescription preprocess(std::map const& constantDefinitions) const; diff --git a/src/storm/storage/expressions/BaseExpression.cpp b/src/storm/storage/expressions/BaseExpression.cpp index 4e07fd089..b6f652e3f 100644 --- a/src/storm/storage/expressions/BaseExpression.cpp +++ b/src/storm/storage/expressions/BaseExpression.cpp @@ -6,6 +6,7 @@ #include "storm/storage/expressions/Expressions.h" #include "storm/storage/expressions/ToRationalNumberVisitor.h" +#include "storm/storage/expressions/ReduceNestingVisitor.h" namespace storm { namespace expressions { @@ -63,7 +64,7 @@ namespace storm { } std::shared_ptr BaseExpression::getOperand(uint_fast64_t operandIndex) const { - STORM_LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 0."); + STORM_LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression '" << *this << "' of arity 0."); } std::string const& BaseExpression::getIdentifier() const { @@ -74,6 +75,11 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access operator of non-function application expression."); } + std::shared_ptr BaseExpression::reduceNesting() const { + ReduceNestingVisitor v; + return v.reduceNesting(this->toExpression()).getBaseExpressionPointer(); + } + bool BaseExpression::containsVariables() const { return false; } diff --git a/src/storm/storage/expressions/BaseExpression.h b/src/storm/storage/expressions/BaseExpression.h index 93091e536..666c4d40b 100644 --- a/src/storm/storage/expressions/BaseExpression.h +++ b/src/storm/storage/expressions/BaseExpression.h @@ -185,6 +185,13 @@ namespace storm { */ virtual std::shared_ptr simplify() const = 0; + /*! + * Tries to flatten the syntax tree of the expression, e.g., 1 + (2 + (3 + 4)) becomes (1 + 2) + (3 + 4) + * + * @return A semantically equivalent expression with reduced nesting + */ + std::shared_ptr reduceNesting() const; + /*! * Accepts the given visitor by calling its visit method. * diff --git a/src/storm/storage/expressions/Expression.cpp b/src/storm/storage/expressions/Expression.cpp index 2ec1604a7..1d31f4c6f 100644 --- a/src/storm/storage/expressions/Expression.cpp +++ b/src/storm/storage/expressions/Expression.cpp @@ -73,6 +73,10 @@ namespace storm { return Expression(this->getBaseExpression().simplify()); } + Expression Expression::reduceNesting() const { + return Expression(this->getBaseExpression().reduceNesting()); + } + OperatorType Expression::getOperator() const { return this->getBaseExpression().getOperator(); } diff --git a/src/storm/storage/expressions/Expression.h b/src/storm/storage/expressions/Expression.h index 99d409e2e..5778f7214 100644 --- a/src/storm/storage/expressions/Expression.h +++ b/src/storm/storage/expressions/Expression.h @@ -152,6 +152,13 @@ namespace storm { */ Expression simplify() const; + /*! + * Tries to flatten the syntax tree of the expression, e.g., 1 + (2 + (3 + 4)) becomes (1 + 2) + (3 + 4) + * + * @return A semantically equivalent expression with reduced nesting + */ + Expression reduceNesting() const; + /*! * Retrieves the operator of a function application. This is only legal to call if the expression is * function application. diff --git a/src/storm/storage/expressions/ReduceNestingVisitor.cpp b/src/storm/storage/expressions/ReduceNestingVisitor.cpp new file mode 100644 index 000000000..5f77ca10c --- /dev/null +++ b/src/storm/storage/expressions/ReduceNestingVisitor.cpp @@ -0,0 +1,180 @@ +#include + +#include "storm/storage/expressions/ReduceNestingVisitor.h" +#include "storm/storage/expressions/Expressions.h" + +namespace storm { + namespace expressions { + + ReduceNestingVisitor::ReduceNestingVisitor() { + // Intentionally left empty. + } + + Expression ReduceNestingVisitor::reduceNesting(Expression const& expression) { + return Expression(boost::any_cast>(expression.getBaseExpression().accept(*this, boost::none))); + } + + boost::any ReduceNestingVisitor::visit(IfThenElseExpression const& expression, boost::any const& data) { + std::shared_ptr conditionExpression = boost::any_cast>(expression.getCondition()->accept(*this, data)); + std::shared_ptr thenExpression = boost::any_cast>(expression.getThenExpression()->accept(*this, data)); + std::shared_ptr elseExpression = boost::any_cast>(expression.getElseExpression()->accept(*this, data)); + + // If the arguments did not change, we simply push the expression itself. + if (conditionExpression.get() == expression.getCondition().get() && thenExpression.get() == expression.getThenExpression().get() && elseExpression.get() == expression.getElseExpression().get()) { + return expression.getSharedPointer(); + } else { + return std::const_pointer_cast(std::shared_ptr(new IfThenElseExpression(expression.getManager(), expression.getType(), conditionExpression, thenExpression, elseExpression))); + } + } + + template + std::vector> getAllOperands(BinaryFunc const& binaryExpression) { + auto opType = binaryExpression.getOperatorType(); + std::vector> stack = {binaryExpression.getSharedPointer()}; + std::vector> res; + while (!stack.empty()) { + auto f = std::move(stack.back()); + stack.pop_back(); + + for (uint64_t opIndex = 0; opIndex < 2; ++opIndex) { + BinaryFunc const* subexp = dynamic_cast(f->getOperand(opIndex).get()); + if (subexp != nullptr && subexp->getOperatorType() == opType) { + stack.push_back(f->getOperand(opIndex)); + } else { + res.push_back(f->getOperand(opIndex)); + } + } + } + return res; + } + + boost::any ReduceNestingVisitor::visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) { + + // Check if the operator is commutative and associative + if (expression.getOperatorType() == BinaryBooleanFunctionExpression::OperatorType::Or || expression.getOperatorType() == BinaryBooleanFunctionExpression::OperatorType::And || expression.getOperatorType() == BinaryBooleanFunctionExpression::OperatorType::Iff) { + + std::vector> operands = getAllOperands(expression); + + // Balance the syntax tree if there are enough operands + if (operands.size() >= 4) { + + for (auto& operand : operands) { + operand = boost::any_cast>(operand->accept(*this, data)); + } + + auto opIt = operands.begin(); + while (operands.size() > 1) { + if (opIt == operands.end() || opIt == operands.end() - 1) { + opIt = operands.begin(); + } + *opIt = std::const_pointer_cast(std::shared_ptr(new BinaryBooleanFunctionExpression(expression.getManager(), expression.getType(), *opIt, operands.back(), expression.getOperatorType()))); + operands.pop_back(); + ++opIt; + } + return operands.front(); + } + } + + std::shared_ptr firstExpression = boost::any_cast>(expression.getFirstOperand()->accept(*this, data)); + std::shared_ptr secondExpression = boost::any_cast>(expression.getSecondOperand()->accept(*this, data)); + + // If the arguments did not change, we simply push the expression itself. + if (firstExpression.get() == expression.getFirstOperand().get() && secondExpression.get() == expression.getSecondOperand().get()) { + return expression.getSharedPointer(); + } else { + return std::const_pointer_cast(std::shared_ptr(new BinaryBooleanFunctionExpression(expression.getManager(), expression.getType(), firstExpression, secondExpression, expression.getOperatorType()))); + } + } + + boost::any ReduceNestingVisitor::visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) { + // Check if the operator is commutative and associative + if (expression.getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Plus || expression.getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Times || expression.getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Max || expression.getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Min) { + + std::vector> operands = getAllOperands(expression); + + // Balance the syntax tree if there are enough operands + if (operands.size() >= 4) { + + for (auto& operand : operands) { + operand = boost::any_cast>(operand->accept(*this, data)); + } + + auto opIt = operands.begin(); + while (operands.size() > 1) { + if (opIt == operands.end() || opIt == operands.end() - 1) { + opIt = operands.begin(); + } + *opIt = std::const_pointer_cast(std::shared_ptr(new BinaryNumericalFunctionExpression(expression.getManager(), expression.getType(), *opIt, operands.back(), expression.getOperatorType()))); + operands.pop_back(); + ++opIt; + } + return operands.front(); + } + } + + + + std::shared_ptr firstExpression = boost::any_cast>(expression.getFirstOperand()->accept(*this, data)); + std::shared_ptr secondExpression = boost::any_cast>(expression.getSecondOperand()->accept(*this, data)); + + // If the arguments did not change, we simply push the expression itself. + if (firstExpression.get() == expression.getFirstOperand().get() && secondExpression.get() == expression.getSecondOperand().get()) { + return expression.getSharedPointer(); + } else { + return std::const_pointer_cast(std::shared_ptr(new BinaryNumericalFunctionExpression(expression.getManager(), expression.getType(), firstExpression, secondExpression, expression.getOperatorType()))); + } + } + + boost::any ReduceNestingVisitor::visit(BinaryRelationExpression const& expression, boost::any const& data) { + + std::shared_ptr firstExpression = boost::any_cast>(expression.getFirstOperand()->accept(*this, data)); + std::shared_ptr secondExpression = boost::any_cast>(expression.getSecondOperand()->accept(*this, data)); + + // If the arguments did not change, we simply push the expression itself. + if (firstExpression.get() == expression.getFirstOperand().get() && secondExpression.get() == expression.getSecondOperand().get()) { + return expression.getSharedPointer(); + } else { + return std::const_pointer_cast(std::shared_ptr(new BinaryRelationExpression(expression.getManager(), expression.getType(), firstExpression, secondExpression, expression.getRelationType()))); + } + } + + boost::any ReduceNestingVisitor::visit(VariableExpression const& expression, boost::any const&) { + return expression.getSharedPointer(); + } + + boost::any ReduceNestingVisitor::visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) { + std::shared_ptr operandExpression = boost::any_cast>(expression.getOperand()->accept(*this, data)); + + // If the argument did not change, we simply push the expression itself. + if (operandExpression.get() == expression.getOperand().get()) { + return expression.getSharedPointer(); + } else { + return std::const_pointer_cast(std::shared_ptr(new UnaryBooleanFunctionExpression(expression.getManager(), expression.getType(), operandExpression, expression.getOperatorType()))); + } + } + + boost::any ReduceNestingVisitor::visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) { + std::shared_ptr operandExpression = boost::any_cast>(expression.getOperand()->accept(*this, data)); + + // If the argument did not change, we simply push the expression itself. + if (operandExpression.get() == expression.getOperand().get()) { + return expression.getSharedPointer(); + } else { + return std::const_pointer_cast(std::shared_ptr(new UnaryNumericalFunctionExpression(expression.getManager(), expression.getType(), operandExpression, expression.getOperatorType()))); + } + } + + boost::any ReduceNestingVisitor::visit(BooleanLiteralExpression const& expression, boost::any const&) { + return expression.getSharedPointer(); + } + + boost::any ReduceNestingVisitor::visit(IntegerLiteralExpression const& expression, boost::any const&) { + return expression.getSharedPointer(); + } + + boost::any ReduceNestingVisitor::visit(RationalLiteralExpression const& expression, boost::any const&) { + return expression.getSharedPointer(); + } + + } +} diff --git a/src/storm/storage/expressions/ReduceNestingVisitor.h b/src/storm/storage/expressions/ReduceNestingVisitor.h new file mode 100644 index 000000000..f7c9c1566 --- /dev/null +++ b/src/storm/storage/expressions/ReduceNestingVisitor.h @@ -0,0 +1,36 @@ +#pragma once + +#include "storm/storage/expressions/Expression.h" +#include "storm/storage/expressions/ExpressionVisitor.h" + +namespace storm { + namespace expressions { + class ReduceNestingVisitor : public ExpressionVisitor { + public: + /*! + * Creates a new reduce nesting visitor. + */ + ReduceNestingVisitor(); + + /*! + * Reduces the nesting in the given expression + * + * @return A semantically equivalent expression with reduced nesting + */ + Expression reduceNesting(Expression const& expression); + + virtual boost::any visit(IfThenElseExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BinaryRelationExpression const& expression, boost::any const& data) override; + virtual boost::any visit(VariableExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryBooleanFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(UnaryNumericalFunctionExpression const& expression, boost::any const& data) override; + virtual boost::any visit(BooleanLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(IntegerLiteralExpression const& expression, boost::any const& data) override; + virtual boost::any visit(RationalLiteralExpression const& expression, boost::any const& data) override; + + private: + }; + } +} diff --git a/src/storm/storage/jani/Assignment.cpp b/src/storm/storage/jani/Assignment.cpp index 25fa036af..747679385 100644 --- a/src/storm/storage/jani/Assignment.cpp +++ b/src/storm/storage/jani/Assignment.cpp @@ -37,7 +37,7 @@ namespace storm { } void Assignment::substitute(std::map const& substitution) { - this->setAssignedExpression(this->getAssignedExpression().substitute(substitution)); + this->setAssignedExpression(this->getAssignedExpression().substitute(substitution).simplify()); } int64_t Assignment::getLevel() const { diff --git a/src/storm/storage/jani/Assignment.h b/src/storm/storage/jani/Assignment.h index a4e2dbd23..3ab3b5581 100644 --- a/src/storm/storage/jani/Assignment.h +++ b/src/storm/storage/jani/Assignment.h @@ -14,7 +14,8 @@ namespace storm { * Creates an assignment of the given expression to the given variable. */ Assignment(storm::jani::Variable const& variable, storm::expressions::Expression const& expression, uint64_t index = 0); - + + Assignment(Assignment const&) = default; bool operator==(Assignment const& other) const; /*! diff --git a/src/storm/storage/jani/Automaton.cpp b/src/storm/storage/jani/Automaton.cpp index f4c65c759..cc75d080e 100644 --- a/src/storm/storage/jani/Automaton.cpp +++ b/src/storm/storage/jani/Automaton.cpp @@ -8,6 +8,7 @@ #include "storm/exceptions/WrongFormatException.h" #include "storm/exceptions/InvalidArgumentException.h" #include "storm/exceptions/InvalidTypeException.h" +#include "storm/exceptions/NotSupportedException.h" namespace storm { namespace jani { @@ -53,6 +54,10 @@ namespace storm { return variables.addVariable(variable); } + bool Automaton::hasVariable(std::string const& name) const { + return variables.hasVariable(name); + } + VariableSet& Automaton::getVariables() { return variables; } @@ -419,6 +424,38 @@ namespace storm { edges.pushAssignmentsToDestinations(); } + void Automaton::pushTransientRealLocationAssignmentsToEdges() { + std::set> encounteredTemplateEdges; + + for (uint64_t locationIndex = 0; locationIndex < locations.size(); ++locationIndex) { + auto& location = locations[locationIndex]; + auto edges = this->getEdgesFromLocation(locationIndex); + + storm::jani::Location newLocation(location.getName()); + bool createNewLocation = true; + for (auto& edge : edges) { + STORM_LOG_THROW(encounteredTemplateEdges.find(edge.getTemplateEdge()) == encounteredTemplateEdges.end(), storm::exceptions::NotSupportedException, "Pushing location assignments to edges is only supported for automata with unique template edges."); + + auto& templateEdge = edge.getTemplateEdge(); + encounteredTemplateEdges.insert(templateEdge); + + for (auto const& assignment : location.getAssignments().getTransientAssignments()) { + if (assignment.getVariable().isTransient() && assignment.getVariable().isRealVariable()) { + templateEdge->addTransientAssignment(assignment, true); + } else if (createNewLocation) { + newLocation.addTransientAssignment(assignment); + } + } + + if (createNewLocation) { + createNewLocation = false; + } + } + + location = std::move(newLocation); + } + } + bool Automaton::hasTransientEdgeDestinationAssignments() const { for (auto const& edge : this->getEdges()) { if (edge.hasTransientEdgeDestinationAssignments()) { @@ -440,7 +477,6 @@ namespace storm { return true; } - bool Automaton::usesAssignmentLevels() const { return edges.usesAssignmentLevels(); } diff --git a/src/storm/storage/jani/Automaton.h b/src/storm/storage/jani/Automaton.h index c549ecca8..eb1362741 100644 --- a/src/storm/storage/jani/Automaton.h +++ b/src/storm/storage/jani/Automaton.h @@ -79,6 +79,8 @@ namespace storm { * Retrieves the variables of this automaton. */ VariableSet const& getVariables() const; + + bool hasVariable(std::string const& name) const; /*! * Retrieves all expression variables used by this automaton. @@ -294,6 +296,12 @@ namespace storm { */ void pushEdgeAssignmentsToDestinations(); + /*! + * Pushes the assignments to real-valued transient variables to the edges. + * Note: This is currently only supported if the template edges are uniquely coupled with one source location. + */ + void pushTransientRealLocationAssignmentsToEdges(); + /*! * Retrieves whether there is any transient edge destination assignment in the automaton. */ diff --git a/src/storm/storage/jani/JSONExporter.cpp b/src/storm/storage/jani/JSONExporter.cpp index 0c0c212a0..c957cd29b 100644 --- a/src/storm/storage/jani/JSONExporter.cpp +++ b/src/storm/storage/jani/JSONExporter.cpp @@ -35,6 +35,7 @@ namespace storm { modernjson::json buildExpression(storm::expressions::Expression const& exp, std::vector const& constants, VariableSet const& globalVariables = VariableSet(), VariableSet const& localVariables = VariableSet()) { + STORM_LOG_TRACE("Exporting " << exp); return ExpressionToJson::translate(exp, constants, globalVariables, localVariables); } @@ -178,32 +179,54 @@ namespace storm { return opDecl; } boost::any FormulaToJaniJson::visit(storm::logic::BoundedUntilFormula const& f, boost::any const& data) const { + STORM_LOG_THROW(!f.hasMultiDimensionalSubformulas(), storm::exceptions::NotSupportedException, "Jani export of multi-dimensional bounded until formulas is not supported."); modernjson::json opDecl; opDecl["op"] = "U"; opDecl["left"] = boost::any_cast(f.getLeftSubformula().accept(*this, data)); opDecl["right"] = boost::any_cast(f.getRightSubformula().accept(*this, data)); - boost::optional lower, upper; - boost::optional lowerExclusive, upperExclusive; - if (f.hasLowerBound()) { - lower = f.getLowerBound(); - lowerExclusive = f.isLowerBoundStrict(); + bool hasStepBounds(false), hasTimeBounds(false); + std::vector rewardBounds; + + for (uint64_t i = 0; i < f.getDimension(); ++i) { + boost::optional lower, upper; + boost::optional lowerExclusive, upperExclusive; + if (f.hasLowerBound(i)) { + lower = f.getLowerBound(i); + lowerExclusive = f.isLowerBoundStrict(i); + } + if (f.hasUpperBound(i)) { + upper = f.getUpperBound(i); + upperExclusive = f.isUpperBoundStrict(i); + } + modernjson::json propertyInterval = constructPropertyInterval(lower, lowerExclusive, upper, upperExclusive); + + auto tbr = f.getTimeBoundReference(i); + if (tbr.isStepBound()) { + STORM_LOG_THROW(!hasStepBounds, storm::exceptions::NotSupportedException, "Jani export of until formulas with multiple step bounds is not supported."); + hasStepBounds = true; + opDecl["step-bounds"] = propertyInterval; + } else if(tbr.isRewardBound()) { + modernjson::json rewbound; + rewbound["exp"] = tbr.getRewardName(); + std::vector accvec = {"steps"}; + if (!model.isDiscreteTimeModel()) { + accvec.push_back("time"); + } + rewbound["accumulate"] = modernjson::json(accvec); + rewbound["bounds"] = propertyInterval; + rewardBounds.push_back(std::move(rewbound)); + } else { + STORM_LOG_THROW(!hasTimeBounds, storm::exceptions::NotSupportedException, "Jani export of until formulas with multiple step bounds is not supported."); + hasTimeBounds = true; + opDecl["time-bounds"] = propertyInterval; + } } - if (f.hasUpperBound()) { - upper = f.getUpperBound(); - upperExclusive = f.isUpperBoundStrict(); - } - modernjson::json propertyInterval = constructPropertyInterval(lower, lowerExclusive, upper, upperExclusive); - - auto tbr = f.getTimeBoundReference(); - if(tbr.isStepBound()) { - opDecl["step-bounds"] = propertyInterval; - } else if(tbr.isRewardBound()) { - opDecl["time-bounds"] = propertyInterval; - } else { - opDecl["reward-bounds"] = propertyInterval; + if (!rewardBounds.empty()) { + opDecl["reward-bounds"] = modernjson::json(rewardBounds); } return opDecl; + } boost::any FormulaToJaniJson::visit(storm::logic::ConditionalFormula const&, boost::any const&) const { @@ -224,33 +247,44 @@ namespace storm { boost::any FormulaToJaniJson::visit(storm::logic::TimeOperatorFormula const& f, boost::any const& data) const { modernjson::json opDecl; - std::vector tvec; - tvec.push_back("time"); + std::vector accvec; + if (model.isDiscreteTimeModel()) { + accvec.push_back("steps"); + } else { + accvec.push_back("time"); + } if(f.hasBound()) { auto bound = f.getBound(); opDecl["op"] = comparisonTypeToJani(bound.comparisonType); if(f.hasOptimalityType()) { opDecl["left"]["op"] = f.getOptimalityType() == storm::solver::OptimizationDirection::Minimize ? "Emin" : "Emax"; - opDecl["left"]["reach"] = boost::any_cast(f.getSubformula().accept(*this, data)); + if (f.getSubformula().isEventuallyFormula()) { + opDecl["left"]["reach"] = boost::any_cast(f.getSubformula().asEventuallyFormula().getSubformula().accept(*this, data)); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Unsupported subformula for time operator formula " << f); + } } else { opDecl["left"]["op"] = (bound.comparisonType == storm::logic::ComparisonType::Less || bound.comparisonType == storm::logic::ComparisonType::LessEqual) ? "Emax" : "Emin"; - opDecl["left"]["reach"] = boost::any_cast(f.getSubformula().accept(*this, data)); + opDecl["left"]["reach"] = boost::any_cast(f.getSubformula().asEventuallyFormula().getSubformula().accept(*this, data)); } opDecl["left"]["exp"] = modernjson::json(1); - opDecl["left"]["accumulate"] = modernjson::json(tvec); + opDecl["left"]["accumulate"] = modernjson::json(accvec); opDecl["right"] = buildExpression(bound.threshold, model.getConstants(), model.getGlobalVariables()); } else { if(f.hasOptimalityType()) { opDecl["op"] = f.getOptimalityType() == storm::solver::OptimizationDirection::Minimize ? "Emin" : "Emax"; - opDecl["reach"] = boost::any_cast(f.getSubformula().accept(*this, data)); - + if (f.getSubformula().isEventuallyFormula()) { + opDecl["reach"] = boost::any_cast(f.getSubformula().asEventuallyFormula().getSubformula().accept(*this, data)); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Unsupported subformula for time operator formula " << f); + } } else { // TODO add checks opDecl["op"] = "Emin"; - opDecl["reach"] = boost::any_cast(f.getSubformula().accept(*this, data)); + opDecl["reach"] = boost::any_cast(f.getSubformula().asEventuallyFormula().getSubformula().accept(*this, data)); } opDecl["exp"] = modernjson::json(1); - opDecl["accumulate"] = modernjson::json(tvec); + opDecl["accumulate"] = modernjson::json(accvec); } return opDecl; } @@ -264,7 +298,7 @@ namespace storm { } boost::any FormulaToJaniJson::visit(storm::logic::LongRunAverageOperatorFormula const& f, boost::any const& data) const { - modernjson::json opDecl; + modernjson::json opDecl; if(f.hasBound()) { auto bound = f.getBound(); opDecl["op"] = comparisonTypeToJani(bound.comparisonType); @@ -288,7 +322,6 @@ namespace storm { } } return opDecl; - } boost::any FormulaToJaniJson::visit(storm::logic::LongRunAverageRewardFormula const&, boost::any const&) const { @@ -368,39 +401,73 @@ namespace storm { boost::any FormulaToJaniJson::visit(storm::logic::RewardOperatorFormula const& f, boost::any const& data) const { modernjson::json opDecl; - std::vector accvec; - if(model.isDiscreteTimeModel()) { - accvec.push_back("steps"); + std::vector accvec = {"steps"}; + std::string instantName; + if (model.isDiscreteTimeModel()) { + instantName = "step-instant"; } else { accvec.push_back("time"); + instantName = "time-instant"; } + + std::string rewardModelName; + if (f.hasRewardModelName()) { + rewardModelName = f.getRewardModelName(); + } else { + if (model.getGlobalVariables().getNumberOfRealTransientVariables() == 1) { + for (auto const& variable : model.getGlobalVariables().getRealVariables()) { + if (variable.isTransient()) { + rewardModelName = variable.getName(); + STORM_LOG_WARN("Reward model name was not given, assuming the only global real transient variable '" << rewardModelName << "' to measure the reward."); + break; + } + } + } + } + STORM_LOG_THROW(!rewardModelName.empty(), storm::exceptions::NotSupportedException, "Reward name has to be specified for Jani-conversion"); + if(f.hasBound()) { auto bound = f.getBound(); opDecl["op"] = comparisonTypeToJani(bound.comparisonType); if(f.hasOptimalityType()) { opDecl["left"]["op"] = f.getOptimalityType() == storm::solver::OptimizationDirection::Minimize ? "Emin" : "Emax"; - opDecl["left"]["reach"] = boost::any_cast(f.getSubformula().asEventuallyFormula().getSubformula().accept(*this, data)); } else { opDecl["left"]["op"] = (bound.comparisonType == storm::logic::ComparisonType::Less || bound.comparisonType == storm::logic::ComparisonType::LessEqual) ? "Emax" : "Emin"; + } + if (f.getSubformula().isEventuallyFormula()) { opDecl["left"]["reach"] = boost::any_cast(f.getSubformula().asEventuallyFormula().getSubformula().accept(*this, data)); + } else if (f.getSubformula().isCumulativeRewardFormula()) { + opDecl["left"][instantName] = buildExpression(f.getSubformula().asCumulativeRewardFormula().getBound(), model.getConstants(), model.getGlobalVariables()); + } else if (f.getSubformula().isInstantaneousRewardFormula()) { + opDecl["left"][instantName] = buildExpression(f.getSubformula().asInstantaneousRewardFormula().getBound(), model.getConstants(), model.getGlobalVariables()); } STORM_LOG_THROW(f.hasRewardModelName(), storm::exceptions::NotSupportedException, "Reward name has to be specified for Jani-conversion"); - opDecl["left"]["exp"] = f.getRewardModelName(); - opDecl["left"]["accumulate"] = modernjson::json(accvec); + opDecl["left"]["exp"] = rewardModelName; + if (f.getSubformula().isReachabilityRewardFormula() || f.getSubformula().isCumulativeRewardFormula()) { + opDecl["left"]["accumulate"] = modernjson::json(accvec); + } opDecl["right"] = buildExpression(bound.threshold, model.getConstants(), model.getGlobalVariables()); } else { - if(f.hasOptimalityType()) { + if (f.hasOptimalityType()) { opDecl["op"] = f.getOptimalityType() == storm::solver::OptimizationDirection::Minimize ? "Emin" : "Emax"; - opDecl["reach"] = boost::any_cast(f.getSubformula().asEventuallyFormula().getSubformula().accept(*this, data)); } else { // TODO add checks opDecl["op"] = "Emin"; + } + + if (f.getSubformula().isEventuallyFormula()) { opDecl["reach"] = boost::any_cast(f.getSubformula().asEventuallyFormula().getSubformula().accept(*this, data)); + } else if (f.getSubformula().isCumulativeRewardFormula()) { + opDecl[instantName] = buildExpression(f.getSubformula().asCumulativeRewardFormula().getBound(), model.getConstants(), model.getGlobalVariables()); + } else if (f.getSubformula().isInstantaneousRewardFormula()) { + opDecl[instantName] = buildExpression(f.getSubformula().asInstantaneousRewardFormula().getBound(), model.getConstants(), model.getGlobalVariables()); + } + + opDecl["exp"] = rewardModelName; + if (f.getSubformula().isReachabilityRewardFormula() || f.getSubformula().isCumulativeRewardFormula()) { + opDecl["accumulate"] = modernjson::json(accvec); } - STORM_LOG_THROW(f.hasRewardModelName(), storm::exceptions::NotSupportedException, "Reward name has to be specified for Jani-conversion"); - opDecl["exp"] = f.getRewardModelName(); - opDecl["accumulate"] = modernjson::json(accvec); } return opDecl; } @@ -408,7 +475,6 @@ namespace storm { boost::any FormulaToJaniJson::visit(storm::logic::TotalRewardFormula const&, boost::any const&) const { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Jani currently does not support a total reward formula"); } - boost::any FormulaToJaniJson::visit(storm::logic::UnaryBooleanStateFormula const& f, boost::any const& data) const { modernjson::json opDecl; @@ -483,8 +549,13 @@ namespace storm { } modernjson::json ExpressionToJson::translate(storm::expressions::Expression const& expr, std::vector const& constants, VariableSet const& globalVariables, VariableSet const& localVariables) { + + // Simplify the expression first and reduce the nesting + auto simplifiedExpr = expr.simplify().reduceNesting(); + + ExpressionToJson visitor(constants, globalVariables, localVariables); - return boost::any_cast(expr.accept(visitor, boost::none)); + return boost::any_cast(simplifiedExpr.accept(visitor, boost::none)); } @@ -529,7 +600,7 @@ namespace storm { } } } - STORM_LOG_ASSERT(false, "Expression variable '" << expression.getVariableName() << "' not known in Jani data structures."); + STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Expression variable '" << expression.getVariableName() << "' not known in Jani data structures."); return modernjson::json(); // should not reach this point. } boost::any ExpressionToJson::visit(storm::expressions::UnaryBooleanFunctionExpression const& expression, boost::any const& data) { @@ -554,21 +625,27 @@ namespace storm { return modernjson::json(expression.getValueAsDouble()); } - void JsonExporter::toFile(storm::jani::Model const& janiModel, std::vector const& formulas, std::string const& filepath, bool checkValid) { + void JsonExporter::toFile(storm::jani::Model const& janiModel, std::vector const& formulas, std::string const& filepath, bool checkValid, bool compact) { std::ofstream stream; storm::utility::openFile(filepath, stream); - toStream(janiModel, formulas, stream, checkValid); + toStream(janiModel, formulas, stream, checkValid, compact); storm::utility::closeFile(stream); } - void JsonExporter::toStream(storm::jani::Model const& janiModel, std::vector const& formulas, std::ostream& os, bool checkValid) { + void JsonExporter::toStream(storm::jani::Model const& janiModel, std::vector const& formulas, std::ostream& os, bool checkValid, bool compact) { if(checkValid) { janiModel.checkValid(); } JsonExporter exporter; - exporter.convertModel(janiModel); + exporter.convertModel(janiModel, !compact); exporter.convertProperties(formulas, janiModel); - os << exporter.finalize().dump(4) << std::endl; + if (compact) { + // Dump without line breaks/indents + os << exporter.finalize().dump() << std::endl; + } else { + // Dump with line breaks and indention with 4 spaces + os << exporter.finalize().dump(4) << std::endl; + } } modernjson::json buildActionArray(std::vector const& actions) { @@ -647,7 +724,7 @@ namespace storm { } - modernjson::json buildAssignmentArray(storm::jani::OrderedAssignments const& orderedAssignments, std::vector const& constants, VariableSet const& globalVariables, VariableSet const& localVariables) { + modernjson::json buildAssignmentArray(storm::jani::OrderedAssignments const& orderedAssignments, std::vector const& constants, VariableSet const& globalVariables, VariableSet const& localVariables, bool commentExpressions) { std::vector assignmentDeclarations; bool addIndex = orderedAssignments.hasMultipleLevels(); for(auto const& assignment : orderedAssignments) { @@ -658,18 +735,21 @@ namespace storm { assignmentEntry["index"] = assignment.getLevel(); } assignmentDeclarations.push_back(assignmentEntry); + if (commentExpressions) { + assignmentEntry["comment"] = assignment.getVariable().getName() + " <- " + assignment.getAssignedExpression().toString(); + } } return modernjson::json(assignmentDeclarations); } - modernjson::json buildLocationsArray(std::vector const& locations, std::vector const& constants, VariableSet const& globalVariables, VariableSet const& localVariables) { + modernjson::json buildLocationsArray(std::vector const& locations, std::vector const& constants, VariableSet const& globalVariables, VariableSet const& localVariables, bool commentExpressions) { std::vector locationDeclarations; for(auto const& location : locations) { modernjson::json locEntry; locEntry["name"] = location.getName(); // TODO support invariants? if (!location.getAssignments().empty()) { - locEntry["transient-values"] = buildAssignmentArray(location.getAssignments(), constants, globalVariables, localVariables); + locEntry["transient-values"] = buildAssignmentArray(location.getAssignments(), constants, globalVariables, localVariables, commentExpressions); } locationDeclarations.push_back(locEntry); } @@ -684,7 +764,7 @@ namespace storm { return modernjson::json(names); } - modernjson::json buildDestinations(std::vector const& destinations, std::map const& locationNames, std::vector const& constants, VariableSet const& globalVariables, VariableSet const& localVariables) { + modernjson::json buildDestinations(std::vector const& destinations, std::map const& locationNames, std::vector const& constants, VariableSet const& globalVariables, VariableSet const& localVariables, bool commentExpressions) { assert(destinations.size() > 0); std::vector destDeclarations; for(auto const& destination : destinations) { @@ -698,16 +778,19 @@ namespace storm { } if (!prob1) { destEntry["probability"]["exp"] = buildExpression(destination.getProbability(), constants, globalVariables, localVariables); + if (commentExpressions) { + destEntry["probability"]["comment"] = destination.getProbability().toString(); + } } if (!destination.getOrderedAssignments().empty()) { - destEntry["assignments"] = buildAssignmentArray(destination.getOrderedAssignments(), constants, globalVariables, localVariables); + destEntry["assignments"] = buildAssignmentArray(destination.getOrderedAssignments(), constants, globalVariables, localVariables, commentExpressions); } destDeclarations.push_back(destEntry); } return modernjson::json(destDeclarations); } - modernjson::json buildEdges(std::vector const& edges , std::map const& actionNames, std::map const& locationNames, std::vector const& constants, VariableSet const& globalVariables, VariableSet const& localVariables) { + modernjson::json buildEdges(std::vector const& edges , std::map const& actionNames, std::map const& locationNames, std::vector const& constants, VariableSet const& globalVariables, VariableSet const& localVariables, bool commentExpressions) { std::vector edgeDeclarations; for(auto const& edge : edges) { if (edge.getGuard().isFalse()) { @@ -721,13 +804,19 @@ namespace storm { } if(edge.hasRate()) { edgeEntry["rate"]["exp"] = buildExpression(edge.getRate(), constants, globalVariables, localVariables); + if (commentExpressions) { + edgeEntry["rate"]["comment"] = edge.getRate().toString(); + } } if (!edge.getGuard().isTrue()) { edgeEntry["guard"]["exp"] = buildExpression(edge.getGuard(), constants, globalVariables, localVariables); + if (commentExpressions) { + edgeEntry["guard"]["comment"] = edge.getGuard().toString(); + } } - edgeEntry["destinations"] = buildDestinations(edge.getDestinations(), locationNames, constants, globalVariables, localVariables); + edgeEntry["destinations"] = buildDestinations(edge.getDestinations(), locationNames, constants, globalVariables, localVariables, commentExpressions); if (!edge.getAssignments().empty()) { - edgeEntry["assignments"] = buildAssignmentArray(edge.getAssignments(), constants, globalVariables, localVariables); + edgeEntry["assignments"] = buildAssignmentArray(edge.getAssignments(), constants, globalVariables, localVariables, commentExpressions); } edgeDeclarations.push_back(edgeEntry); @@ -735,7 +824,7 @@ namespace storm { return modernjson::json(edgeDeclarations); } - modernjson::json buildAutomataArray(std::vector const& automata, std::map const& actionNames, std::vector const& constants, VariableSet const& globalVariables) { + modernjson::json buildAutomataArray(std::vector const& automata, std::map const& actionNames, std::vector const& constants, VariableSet const& globalVariables, bool commentExpressions) { std::vector automataDeclarations; for(auto const& automaton : automata) { modernjson::json autoEntry; @@ -744,16 +833,16 @@ namespace storm { if(automaton.hasRestrictedInitialStates()) { autoEntry["restrict-initial"]["exp"] = buildExpression(automaton.getInitialStatesRestriction(), constants, globalVariables, automaton.getVariables()); } - autoEntry["locations"] = buildLocationsArray(automaton.getLocations(), constants, globalVariables, automaton.getVariables()); + autoEntry["locations"] = buildLocationsArray(automaton.getLocations(), constants, globalVariables, automaton.getVariables(), commentExpressions); autoEntry["initial-locations"] = buildInitialLocations(automaton); - autoEntry["edges"] = buildEdges(automaton.getEdges(), actionNames, automaton.buildIdToLocationNameMap(), constants, globalVariables, automaton.getVariables()); + autoEntry["edges"] = buildEdges(automaton.getEdges(), actionNames, automaton.buildIdToLocationNameMap(), constants, globalVariables, automaton.getVariables(), commentExpressions); automataDeclarations.push_back(autoEntry); } return modernjson::json(automataDeclarations); } - void JsonExporter::convertModel(storm::jani::Model const& janiModel) { + void JsonExporter::convertModel(storm::jani::Model const& janiModel, bool commentExpressions) { jsonStruct["jani-version"] = janiModel.getJaniVersion(); jsonStruct["name"] = janiModel.getName(); jsonStruct["type"] = to_string(janiModel.getModelType()); @@ -761,7 +850,7 @@ namespace storm { jsonStruct["constants"] = buildConstantsArray(janiModel.getConstants()); jsonStruct["variables"] = buildVariablesArray(janiModel.getGlobalVariables(), janiModel.getConstants(), janiModel.getGlobalVariables()); jsonStruct["restrict-initial"]["exp"] = buildExpression(janiModel.getInitialStatesRestriction(), janiModel.getConstants(), janiModel.getGlobalVariables()); - jsonStruct["automata"] = buildAutomataArray(janiModel.getAutomata(), janiModel.getActionIndexToNameMap(), janiModel.getConstants(), janiModel.getGlobalVariables()); + jsonStruct["automata"] = buildAutomataArray(janiModel.getAutomata(), janiModel.getActionIndexToNameMap(), janiModel.getConstants(), janiModel.getGlobalVariables(), commentExpressions); jsonStruct["system"] = CompositionJsonExporter::translate(janiModel.getSystemComposition()); std::vector standardFeatureVector = {"derived-operators"}; jsonStruct["features"] = standardFeatureVector; diff --git a/src/storm/storage/jani/JSONExporter.h b/src/storm/storage/jani/JSONExporter.h index 17fa97ad6..f88bb7330 100644 --- a/src/storm/storage/jani/JSONExporter.h +++ b/src/storm/storage/jani/JSONExporter.h @@ -3,7 +3,7 @@ #include "storm/storage/expressions/ExpressionVisitor.h" #include "storm/logic/FormulaVisitor.h" -#include "Model.h" +#include "storm/storage/jani/Model.h" #include "storm/storage/jani/Property.h" #include "storm/adapters/RationalNumberAdapter.h" // JSON parser @@ -75,12 +75,12 @@ namespace storm { JsonExporter() = default; public: - static void toFile(storm::jani::Model const& janiModel, std::vector const& formulas, std::string const& filepath, bool checkValid = true); - static void toStream(storm::jani::Model const& janiModel, std::vector const& formulas, std::ostream& ostream, bool checkValid = false); + static void toFile(storm::jani::Model const& janiModel, std::vector const& formulas, std::string const& filepath, bool checkValid = true, bool compact = false); + static void toStream(storm::jani::Model const& janiModel, std::vector const& formulas, std::ostream& ostream, bool checkValid = false, bool compact = false); private: - void convertModel(storm::jani::Model const& model); + void convertModel(storm::jani::Model const& model, bool commentExpressions = true); void convertProperties(std::vector const& formulas, storm::jani::Model const& model); void appendVariableDeclaration(storm::jani::Variable const& variable); diff --git a/src/storm/storage/jani/JaniLocationExpander.cpp b/src/storm/storage/jani/JaniLocationExpander.cpp new file mode 100644 index 000000000..d955a7754 --- /dev/null +++ b/src/storm/storage/jani/JaniLocationExpander.cpp @@ -0,0 +1,121 @@ +#include "storm/storage/jani/JaniLocationExpander.h" + +#include "storm/storage/expressions/ExpressionManager.h" +#include "storm/exceptions/NotSupportedException.h" +#include "storm/exceptions/IllegalArgumentException.h" + + +namespace storm { + namespace jani { + JaniLocationExpander::JaniLocationExpander(Model const& origModel) : original(origModel) { + + } + + void JaniLocationExpander::transform(std::string const& automatonName, std::string const& variableName) { + STORM_LOG_THROW(original.hasAutomaton(automatonName), storm::exceptions::IllegalArgumentException, "Model has no automaton with name " << automatonName << ". "); + STORM_LOG_THROW(original.getAutomaton(automatonName).hasVariable(variableName), storm::exceptions::IllegalArgumentException, "Automaton " << automatonName << " has no variable with name " << variableName << ". "); + newModel = original; + newModel.replaceAutomaton(newModel.getAutomatonIndex(automatonName), transformAutomaton(original.getAutomaton(automatonName), variableName)); + } + + + Model const& JaniLocationExpander::getResult() const { + return newModel; + } + + Automaton JaniLocationExpander::transformAutomaton(Automaton const& automaton, std::string const& variableName) { + + Automaton newAutomaton(automaton.getName(), automaton.getLocationExpressionVariable()); + int64_t initialVariableValue; + int64_t variableLowerBound; + int64_t variableUpperBound; + storm::expressions::Variable eliminatedExpressionVariable; + const storm::jani::Variable* variable; + + for (auto const& var : automaton.getVariables()) { + if (var.getName() == variableName) { + // This variable will be eliminated in the new automaton. + STORM_LOG_THROW(var.hasInitExpression(), storm::exceptions::IllegalArgumentException, "Variable to be eliminated has to have an initexpression."); + STORM_LOG_THROW(var.isBoundedIntegerVariable(), storm::exceptions::IllegalArgumentException, "Variable to be eliminated has to be an bounded integer variable."); + STORM_LOG_THROW(!var.isTransient(), storm::exceptions::IllegalArgumentException, "Cannot eliminate transient variable"); + + variableUpperBound = var.asBoundedIntegerVariable().getUpperBound().evaluateAsInt(); + variableLowerBound = var.asBoundedIntegerVariable().getLowerBound().evaluateAsInt(); + initialVariableValue = var.getInitExpression().evaluateAsInt(); + variable = &var; + eliminatedExpressionVariable = var.getExpressionVariable(); + + } else { + // Other variables are just copied. + newAutomaton.addVariable(var); + } + } + + STORM_LOG_THROW(!automaton.getInitialStatesRestriction().containsVariable({eliminatedExpressionVariable}), storm::exceptions::NotSupportedException, "Elimination of variable that occurs in the initial state restriction is not allowed"); + newAutomaton.setInitialStatesRestriction(automaton.getInitialStatesRestriction()); + + + std::map substitutionMap; + std::map> locationNames; + std::map> locationVariableValueMap; + for (auto const& loc : automaton.getLocations()) { + locationNames[loc.getName()] = std::vector(); + uint64_t origIndex = automaton.getLocationIndex(loc.getName()); + + for (int64_t i = variableLowerBound; i <= variableUpperBound; i++) { + std::string newLocationName = loc.getName() + "_" + variableName + "_" + std::to_string(i); + substitutionMap[eliminatedExpressionVariable] = original.getExpressionManager().integer(i); + std::cout << "eliminate " << eliminatedExpressionVariable.getName() << " with " << i << std::endl; + OrderedAssignments newAssignments = loc.getAssignments().clone(); + newAssignments.substitute(substitutionMap); + std::cout << newAssignments << std::endl; + uint64_t newLocationIndex = newAutomaton.addLocation(Location(newLocationName, newAssignments)); + + locationVariableValueMap[origIndex][i] = newLocationIndex; + locationNames[loc.getName()].push_back(newLocationName); + } + } + + + + for (auto const& edge : automaton.getEdges()) { + for (auto const& newValueAndLocation : locationVariableValueMap[edge.getSourceLocationIndex()]) { + substitutionMap[eliminatedExpressionVariable] = original.getExpressionManager().integer(newValueAndLocation.first); + + uint64_t newSourceIndex = newValueAndLocation.second; + storm::expressions::Expression newGuard = edge.getGuard().substitute(substitutionMap).simplify(); + if (!newGuard.containsVariables() && !newGuard.evaluateAsBool()) { + continue; + } + std::shared_ptr templateEdge = std::make_shared(newGuard); + + STORM_LOG_THROW(edge.getAssignments().empty(), storm::exceptions::NotImplementedException, "Support for edge-assignments is not implemented"); + + std::vector> destinationLocationsAndProbabilities; + for (auto const& destination : edge.getDestinations()) { + OrderedAssignments oa(destination.getOrderedAssignments().clone()); + oa.substitute(substitutionMap); + int64_t value; + for (auto const& assignment : oa) { + if (assignment.getVariable() == *variable) { + oa.remove(assignment); + value = assignment.getAssignedExpression().evaluateAsInt(); + break; + } + } + TemplateEdgeDestination ted(oa); + templateEdge->addDestination(ted); + destinationLocationsAndProbabilities.emplace_back(locationVariableValueMap[destination.getLocationIndex()][value], destination.getProbability().substitute((substitutionMap))); + } + newAutomaton.addEdge(storm::jani::Edge(newSourceIndex, edge.getActionIndex(), edge.hasRate() ? boost::optional(edge.getRate().substitute(substitutionMap)) : boost::none, templateEdge, destinationLocationsAndProbabilities)); + + } + } + return newAutomaton; + + + } + + + } +} \ No newline at end of file diff --git a/src/storm/storage/jani/JaniLocationExpander.h b/src/storm/storage/jani/JaniLocationExpander.h new file mode 100644 index 000000000..6b4ec0709 --- /dev/null +++ b/src/storm/storage/jani/JaniLocationExpander.h @@ -0,0 +1,26 @@ +#pragma once + +#include "storm/storage/jani/Model.h" +#include "storm/storage/jani/Automaton.h" + + +namespace storm { + namespace jani { + class JaniLocationExpander { + public: + JaniLocationExpander(Model const& original); + void transform(std::string const& automatonName, std::string const& variableName); + Model const& getResult() const; + + private: + Model const& original; + Model newModel; + + Automaton transformAutomaton(Automaton const& automaton, std::string const& variableName); + + + + }; + } + +} diff --git a/src/storm/storage/jani/Location.cpp b/src/storm/storage/jani/Location.cpp index b957d5e9a..5ab431734 100644 --- a/src/storm/storage/jani/Location.cpp +++ b/src/storm/storage/jani/Location.cpp @@ -10,6 +10,10 @@ namespace storm { Location::Location(std::string const& name, std::vector const& transientAssignments) : name(name), assignments(transientAssignments) { // Intentionally left empty. } + + Location::Location(std::string const& name, OrderedAssignments const& assignments) : name(name), assignments(assignments) { + // Intentionally left empty. + } std::string const& Location::getName() const { return name; diff --git a/src/storm/storage/jani/Location.h b/src/storm/storage/jani/Location.h index b2bda7b23..75e53d575 100644 --- a/src/storm/storage/jani/Location.h +++ b/src/storm/storage/jani/Location.h @@ -18,7 +18,8 @@ namespace storm { * Creates a new location. */ Location(std::string const& name, std::vector const& transientAssignments = {}); - + + Location(std::string const& name, OrderedAssignments const& assignments); /*! * Retrieves the name of the location. */ diff --git a/src/storm/storage/jani/Model.cpp b/src/storm/storage/jani/Model.cpp index 47533126c..c74d049a2 100644 --- a/src/storm/storage/jani/Model.cpp +++ b/src/storm/storage/jani/Model.cpp @@ -116,6 +116,10 @@ namespace storm { return name; } + void Model::setName(std::string const& newName) { + name = newName; + } + struct ConditionalMetaEdge { ConditionalMetaEdge() : actionIndex(0) { // Intentionally left empty. @@ -442,6 +446,10 @@ namespace storm { variableRemapping.emplace(&variable, flattenedModel.addVariable(*renamedVariable)); } + for (auto const& constant : getConstants()) { + flattenedModel.addConstant(constant); + } + std::vector> composedAutomata; for (auto const& element : parallelComposition.getSubcompositions()) { STORM_LOG_THROW(element->isAutomatonComposition(), storm::exceptions::WrongFormatException, "Cannot flatten recursive (not standard-compliant) composition."); @@ -608,7 +616,15 @@ namespace storm { std::vector& Model::getConstants() { return constants; } - + + std::size_t Model::getNumberOfEdges() const { + size_t res = 0; + for (auto const& aut : getAutomata()) { + res += aut.getNumberOfEdges(); + } + return res; + } + Variable const& Model::addVariable(Variable const& variable) { if (variable.isBooleanVariable()) { return addVariable(variable.asBooleanVariable()); @@ -711,7 +727,15 @@ namespace storm { std::vector const& Model::getAutomata() const { return automata; } - + + bool Model::hasAutomaton(std::string const& name) const { + return automatonToIndex.find(name) != automatonToIndex.end(); + } + + void Model::replaceAutomaton(uint64_t index, Automaton const& automaton) { + automata[index] = automaton; + } + Automaton& Model::getAutomaton(std::string const& name) { auto it = automatonToIndex.find(name); STORM_LOG_THROW(it != automatonToIndex.end(), storm::exceptions::InvalidOperationException, "Unable to retrieve unknown automaton '" << name << "'."); @@ -742,11 +766,6 @@ namespace storm { } std::shared_ptr Model::getStandardSystemComposition() const { - // If there's just one automaton, we must not use the parallel composition operator. - if (this->getNumberOfAutomata() == 1) { - return std::make_shared(this->getAutomata().front().getName()); - } - // Determine the action indices used by each of the automata and create the standard subcompositions. std::set allActionIndices; std::vector> automatonActionIndices; @@ -1107,6 +1126,11 @@ namespace storm { void Model::makeStandardJaniCompliant() { for (auto& automaton : automata) { + // For discrete-time models, we push the assignments to real-valued transient variables (rewards) to the + // edges. + if (this->isDiscreteTimeModel()) { + automaton.pushTransientRealLocationAssignmentsToEdges(); + } automaton.pushEdgeAssignmentsToDestinations(); } } diff --git a/src/storm/storage/jani/Model.h b/src/storm/storage/jani/Model.h index 2dc69d147..644f90f20 100644 --- a/src/storm/storage/jani/Model.h +++ b/src/storm/storage/jani/Model.h @@ -77,10 +77,15 @@ namespace storm { ModelType const& getModelType() const; /*! - * Retrievest the name of the model. + * Retrieves the name of the model. */ std::string const& getName() const; + /*! + * Sets the name of the model. + */ + void setName(std::string const& newName); + /*! * Flatten the composition to obtain an equivalent model that contains exactly one automaton that has the * standard composition. @@ -245,6 +250,19 @@ namespace storm { */ std::vector const& getAutomata() const; + /** + * Replaces the automaton at index with a new automaton. + * @param index + * @param newAutomaton + */ + void replaceAutomaton(uint64_t index, Automaton const& newAutomaton); + + /*! + * Rerieves whether there exists an automaton with the given name. + * @param name + * @return + */ + bool hasAutomaton(std::string const& name) const; /*! * Retrieves the automaton with the given name. */ @@ -274,6 +292,11 @@ namespace storm { * Retrieves the number of automata in this model. */ std::size_t getNumberOfAutomata() const; + + /*! + * Retrieves the total number of edges in this model. + */ + std::size_t getNumberOfEdges() const; /*! * Sets the system composition expression of the JANI model. diff --git a/src/storm/storage/jani/OrderedAssignments.cpp b/src/storm/storage/jani/OrderedAssignments.cpp index 99eb415a8..9c622fe2b 100644 --- a/src/storm/storage/jani/OrderedAssignments.cpp +++ b/src/storm/storage/jani/OrderedAssignments.cpp @@ -16,29 +16,39 @@ namespace storm { OrderedAssignments::OrderedAssignments(Assignment const& assignment) { add(assignment); } + + OrderedAssignments OrderedAssignments::clone() const { + OrderedAssignments result; + for (auto const& assignment : allAssignments) { + result.add(Assignment(*assignment)); + } + return result; + } - bool OrderedAssignments::add(Assignment const& assignment) { + bool OrderedAssignments::add(Assignment const& assignment, bool addToExisting) { // If the element is contained in this set of assignment, nothing needs to be added. - if (this->contains(assignment)) { + if (!addToExisting && this->contains(assignment)) { return false; } // Otherwise, we find the spot to insert it. auto it = lowerBound(assignment, allAssignments); - - if (it != allAssignments.end()) { - STORM_LOG_THROW(assignment.getExpressionVariable() != (*it)->getExpressionVariable(), storm::exceptions::InvalidArgumentException, "Cannot add assignment ('" << assignment.getAssignedExpression() << "') as an assignment ('" << (*it)->getAssignedExpression() << "') to variable '" << (*it)->getVariable().getName() << "' already exists."); - } - // Finally, insert the new element in the correct vectors. - auto elementToInsert = std::make_shared(assignment); - allAssignments.emplace(it, elementToInsert); - if (assignment.isTransient()) { - auto transientIt = lowerBound(assignment, transientAssignments); - transientAssignments.emplace(transientIt, elementToInsert); + // Check if an assignment to this variable is already present + if (it != allAssignments.end() && assignment.getExpressionVariable() == (*it)->getExpressionVariable()) { + STORM_LOG_THROW(addToExisting && assignment.getExpressionVariable().hasNumericalType(), storm::exceptions::InvalidArgumentException, "Cannot add assignment ('" << assignment.getAssignedExpression() << "') as an assignment ('" << (*it)->getAssignedExpression() << "') to variable '" << (*it)->getVariable().getName() << "' already exists."); + (*it)->setAssignedExpression((*it)->getAssignedExpression() + assignment.getAssignedExpression()); } else { - auto nonTransientIt = lowerBound(assignment, nonTransientAssignments); - nonTransientAssignments.emplace(nonTransientIt, elementToInsert); + // Finally, insert the new element in the correct vectors. + auto elementToInsert = std::make_shared(assignment); + allAssignments.emplace(it, elementToInsert); + if (assignment.isTransient()) { + auto transientIt = lowerBound(assignment, transientAssignments); + transientAssignments.emplace(transientIt, elementToInsert); + } else { + auto nonTransientIt = lowerBound(assignment, nonTransientAssignments); + nonTransientAssignments.emplace(nonTransientIt, elementToInsert); + } } return true; diff --git a/src/storm/storage/jani/OrderedAssignments.h b/src/storm/storage/jani/OrderedAssignments.h index 8e307e6e0..45e769920 100644 --- a/src/storm/storage/jani/OrderedAssignments.h +++ b/src/storm/storage/jani/OrderedAssignments.h @@ -26,9 +26,11 @@ namespace storm { /*! * Adds the given assignment to the set of assignments. * + * @addToExisting If true the value of the assigned expression is added to a (potentially) previous assignment + * to the variable. If false and there is already an assignment, an exception is thrown. * @return True iff the assignment was added. */ - bool add(Assignment const& assignment); + bool add(Assignment const& assignment, bool addToExisting = false); /*! * Removes the given assignment from this set of assignments. @@ -140,7 +142,9 @@ namespace storm { bool areLinear() const; friend std::ostream& operator<<(std::ostream& stream, OrderedAssignments const& assignments); - + + OrderedAssignments clone() const; + private: uint64_t isReadBeforeAssignment(Variable const& var, uint64_t assignmentNumber, uint64_t start = 0) const; uint64_t isWrittenBeforeAssignment(Variable const& var, uint64_t assignmentNumber, uint64_t start = 0) const; diff --git a/src/storm/storage/jani/TemplateEdge.cpp b/src/storm/storage/jani/TemplateEdge.cpp index 987e4f6de..6e5af6695 100644 --- a/src/storm/storage/jani/TemplateEdge.cpp +++ b/src/storm/storage/jani/TemplateEdge.cpp @@ -20,8 +20,8 @@ namespace storm { destinations.emplace_back(destination); } - bool TemplateEdge::addTransientAssignment(Assignment const& assignment) { - return assignments.add(assignment); + bool TemplateEdge::addTransientAssignment(Assignment const& assignment, bool addToExisting) { + return assignments.add(assignment, addToExisting); } void TemplateEdge::finalize(Model const& containingModel) { @@ -81,6 +81,8 @@ namespace storm { if (!destinations.empty()) { auto const& destination = *destinations.begin(); + std::vector> assignmentsToLift; + for (auto const& assignment : destination.getOrderedAssignments().getTransientAssignments()) { // Check if we can lift the assignment to the edge. bool canBeLifted = true; @@ -91,12 +93,18 @@ namespace storm { } } - // If so, remove the assignment from all destinations. if (canBeLifted) { - this->addTransientAssignment(assignment); - for (auto& destination : destinations) { - destination.removeAssignment(assignment); - } + // Do not remove the assignment now, as we currently iterate over them. + // Also we need to make a copy of the assignment since we are about to delete it + assignmentsToLift.push_back(std::make_shared(assignment)); + } + } + + // now actually lift the assignments + for (auto const& assignment : assignmentsToLift) { + this->addTransientAssignment(*assignment); + for (auto& destination : destinations) { + destination.removeAssignment(*assignment); } } } @@ -106,7 +114,7 @@ namespace storm { STORM_LOG_ASSERT(!destinations.empty(), "Need non-empty destinations for this transformation."); for (auto const& assignment : this->getAssignments()) { for (auto& destination : destinations) { - destination.addAssignment(assignment); + destination.addAssignment(assignment, true); } } this->assignments.clear(); diff --git a/src/storm/storage/jani/TemplateEdge.h b/src/storm/storage/jani/TemplateEdge.h index cccc1bb7f..dac58144b 100644 --- a/src/storm/storage/jani/TemplateEdge.h +++ b/src/storm/storage/jani/TemplateEdge.h @@ -40,9 +40,11 @@ namespace storm { * Adds a transient assignment to this edge. * * @param assignment The transient assignment to add. + * @param addToExisting Determines if adding the assigned expression to an already existing assignment is + * allowed (if the assigned variable is quantitative). * @return True if the assignment was added. */ - bool addTransientAssignment(Assignment const& assignment); + bool addTransientAssignment(Assignment const& assignment, bool addToExisting = false); /*! * Retrieves a set of (global) variables that are written by at least one of the edge's destinations. diff --git a/src/storm/storage/jani/TemplateEdgeDestination.cpp b/src/storm/storage/jani/TemplateEdgeDestination.cpp index 2d99a1ad1..b30bbda92 100644 --- a/src/storm/storage/jani/TemplateEdgeDestination.cpp +++ b/src/storm/storage/jani/TemplateEdgeDestination.cpp @@ -31,8 +31,8 @@ namespace storm { return assignments.remove(assignment); } - void TemplateEdgeDestination::addAssignment(Assignment const& assignment) { - assignments.add(assignment); + void TemplateEdgeDestination::addAssignment(Assignment const& assignment, bool addToExisting) { + assignments.add(assignment, addToExisting); } bool TemplateEdgeDestination::hasAssignment(Assignment const& assignment) const { diff --git a/src/storm/storage/jani/TemplateEdgeDestination.h b/src/storm/storage/jani/TemplateEdgeDestination.h index bb94125f9..e6ad889b8 100644 --- a/src/storm/storage/jani/TemplateEdgeDestination.h +++ b/src/storm/storage/jani/TemplateEdgeDestination.h @@ -27,7 +27,7 @@ namespace storm { // Convenience methods to access the assignments. bool hasAssignment(Assignment const& assignment) const; bool removeAssignment(Assignment const& assignment); - void addAssignment(Assignment const& assignment); + void addAssignment(Assignment const& assignment, bool addToExisting = false); /*! * Retrieves whether this destination has transient assignments. diff --git a/src/storm/storage/prism/Program.cpp b/src/storm/storage/prism/Program.cpp index 228df8541..45a4f5a1c 100644 --- a/src/storm/storage/prism/Program.cpp +++ b/src/storm/storage/prism/Program.cpp @@ -192,6 +192,14 @@ namespace storm { bool Program::isDeterministicModel() const { return modelType == ModelType::DTMC || modelType == ModelType::CTMC; } + + size_t Program::getNumberOfCommands() const { + size_t res = 0; + for (auto const& module : this->getModules()) { + res += module.getNumberOfCommands(); + } + return res; + } bool Program::hasUndefinedConstants() const { for (auto const& constant : this->getConstants()) { @@ -1711,16 +1719,16 @@ namespace storm { return Command(newCommandIndex, false, actionIndex, actionName, newGuard, newUpdates, this->getFilename(), 0); } - storm::jani::Model Program::toJani(bool allVariablesGlobal, std::string suffix) const { + storm::jani::Model Program::toJani(bool allVariablesGlobal, std::string suffix, bool standardCompliant) const { ToJaniConverter converter; - storm::jani::Model resultingModel = converter.convert(*this, allVariablesGlobal, suffix); + storm::jani::Model resultingModel = converter.convert(*this, allVariablesGlobal, suffix, standardCompliant); STORM_LOG_WARN_COND(!converter.labelsWereRenamed(), "Labels were renamed in PRISM-to-JANI conversion, but the mapping is not stored."); return resultingModel; } - std::pair> Program::toJaniWithLabelRenaming(bool allVariablesGlobal, std::string suffix) const { + std::pair> Program::toJaniWithLabelRenaming(bool allVariablesGlobal, std::string suffix, bool standardCompliant) const { ToJaniConverter converter; - storm::jani::Model resultingModel = converter.convert(*this, allVariablesGlobal, suffix); + storm::jani::Model resultingModel = converter.convert(*this, allVariablesGlobal, suffix, standardCompliant); return std::make_pair(resultingModel, converter.getLabelRenaming()); } diff --git a/src/storm/storage/prism/Program.h b/src/storm/storage/prism/Program.h index 4d6d303fc..40c3e4a47 100644 --- a/src/storm/storage/prism/Program.h +++ b/src/storm/storage/prism/Program.h @@ -157,7 +157,12 @@ namespace storm { * @return */ std::vector usedConstants() const; - + + /*! + * The total number of commands in the prism file. + */ + size_t getNumberOfCommands() const; + /*! * Retrieves whether a global Boolean variable with the given name exists * @@ -603,13 +608,13 @@ namespace storm { /*! * Converts the PRISM model into an equivalent JANI model. */ - storm::jani::Model toJani(bool allVariablesGlobal = false, std::string suffix = "") const; + storm::jani::Model toJani(bool allVariablesGlobal = true, std::string suffix = "", bool standardCompliant = false) const; /*! * Converts the PRISM model into an equivalent JANI model and retrieves possible label renamings that had * to be performed in the process. */ - std::pair> toJaniWithLabelRenaming(bool allVariablesGlobal = false, std::string suffix = "") const; + std::pair> toJaniWithLabelRenaming(bool allVariablesGlobal = true, std::string suffix = "", bool standardCompliant = false) const; private: /*! diff --git a/src/storm/storage/prism/ToJaniConverter.cpp b/src/storm/storage/prism/ToJaniConverter.cpp index 9712a52a7..25ecca79a 100644 --- a/src/storm/storage/prism/ToJaniConverter.cpp +++ b/src/storm/storage/prism/ToJaniConverter.cpp @@ -7,14 +7,18 @@ #include "storm/storage/jani/Model.h" #include "storm/storage/jani/TemplateEdge.h" +#include "storm/settings/SettingsManager.h" + #include "storm/utility/macros.h" #include "storm/exceptions/NotImplementedException.h" namespace storm { namespace prism { - storm::jani::Model ToJaniConverter::convert(storm::prism::Program const& program, bool allVariablesGlobal, std::string suffix) { + storm::jani::Model ToJaniConverter::convert(storm::prism::Program const& program, bool allVariablesGlobal, std::string suffix, bool standardCompliant) { std::shared_ptr manager = program.getManager().getSharedPointer(); + + bool produceStateRewards = !standardCompliant || program.getModelType() == storm::prism::Program::ModelType::CTMC || program.getModelType() == storm::prism::Program::ModelType::MA; // Start by creating an empty JANI model. storm::jani::ModelType modelType; @@ -95,6 +99,14 @@ namespace storm { } } + // Create a mapping from variables to a flag indicating whether it should be made global + std::map variablesToMakeGlobal; + for (auto const& varMods : variablesToAccessingModuleIndices) { + assert(!varMods.second.empty()); + // If there is exactly one module reading and writing the variable, we can make the variable local to this module. + variablesToMakeGlobal[varMods.first] = allVariablesGlobal || (varMods.second.size() > 1); + } + // Go through the labels and construct assignments to transient variables that are added to the locations. std::vector transientLocationAssignments; for (auto const& label : program.getLabels()) { @@ -107,6 +119,24 @@ namespace storm { auto newExpressionVariable = manager->declareBooleanVariable(finalLabelName); storm::jani::BooleanVariable const& newTransientVariable = janiModel.addVariable(storm::jani::BooleanVariable(newExpressionVariable.getName(), newExpressionVariable, manager->boolean(false), true)); transientLocationAssignments.emplace_back(newTransientVariable, label.getStatePredicateExpression()); + + // Variables that are accessed in the label predicate expression should be made global. + std::set variables = label.getStatePredicateExpression().getVariables(); + for (auto const& variable : variables) { + variablesToMakeGlobal[variable] = true; + } + } + + // Create an initial state restriction if there was an initial construct in the program. + if (program.hasInitialConstruct()) { + janiModel.setInitialStatesRestriction(program.getInitialConstruct().getInitialStatesExpression()); + // Variables in the initial state expression should be made global + std::set variables = program.getInitialConstruct().getInitialStatesExpression().getVariables(); + for (auto const& variable : variables) { + variablesToMakeGlobal[variable] = true; + } + } else { + janiModel.setInitialStatesRestriction(manager->boolean(true)); } // Go through the reward models and construct assignments to the transient variables that are to be added to @@ -127,6 +157,11 @@ namespace storm { } } transientLocationAssignments.emplace_back(newTransientVariable, transientLocationExpression); + // Variables that are accessed in a reward term should be made global. + std::set variables = transientLocationExpression.getVariables(); + for (auto const& variable : variables) { + variablesToMakeGlobal[variable] = true; + } } std::map actionIndexToExpression; @@ -148,11 +183,30 @@ namespace storm { std::vector assignments = {storm::jani::Assignment(newTransientVariable, entry.second)}; transientEdgeAssignments.emplace(entry.first, assignments); } + // Variables that are accessed in a reward term should be made global. + std::set variables = entry.second.getVariables(); + for (auto const& variable : variables) { + variablesToMakeGlobal[variable] = true; + } } STORM_LOG_THROW(!rewardModel.hasTransitionRewards(), storm::exceptions::NotImplementedException, "Transition reward translation currently not implemented."); } STORM_LOG_THROW(transientEdgeAssignments.empty() || transientLocationAssignments.empty() || !program.specifiesSystemComposition(), storm::exceptions::NotImplementedException, "Cannot translate reward models from PRISM to JANI that specify a custom system composition."); + // If we are not allowed to produce state rewards, we need to create a mapping from action indices to transient + // location assignments. This is done so that all assignments are added only *once* for synchronizing actions. + std::map> transientRewardLocationAssignmentsPerAction; + if (!produceStateRewards) { + for (auto const& action : program.getActions()) { + auto& list = transientRewardLocationAssignmentsPerAction[janiModel.getActionIndex(action)]; + for (auto const& assignment : transientLocationAssignments) { + if (assignment.isTransient() && assignment.getVariable().isRealVariable()) { + list.emplace_back(assignment); + } + } + } + } + // Now create the separate JANI automata from the modules of the PRISM program. While doing so, we use the // previously built mapping to make variables global that are read by more than one module. std::set firstModules; @@ -164,28 +218,24 @@ namespace storm { storm::jani::Automaton automaton(module.getName(), manager->declareIntegerVariable("_loc_prism2jani_" + module.getName() + "_" + suffix)); for (auto const& variable : module.getIntegerVariables()) { storm::jani::BoundedIntegerVariable newIntegerVariable = *storm::jani::makeBoundedIntegerVariable(variable.getName(), variable.getExpressionVariable(), variable.hasInitialValue() ? boost::make_optional(variable.getInitialValueExpression()) : boost::none, false, variable.getLowerBoundExpression(), variable.getUpperBoundExpression()); - std::set const& accessingModuleIndices = variablesToAccessingModuleIndices[variable.getExpressionVariable()]; - // If there is exactly one module reading and writing the variable, we can make the variable local to this module. - if (!allVariablesGlobal && accessingModuleIndices.size() == 1) { - storm::jani::BoundedIntegerVariable const& createdVariable = automaton.addVariable(newIntegerVariable); - variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); - } else if (!accessingModuleIndices.empty()) { - // Otherwise, we need to make it global. - storm::jani::BoundedIntegerVariable const& createdVariable = janiModel.addVariable(newIntegerVariable); + auto findRes = variablesToMakeGlobal.find(variable.getExpressionVariable()); + if (findRes != variablesToMakeGlobal.end()) { + bool makeVarGlobal = findRes->second; + storm::jani::BoundedIntegerVariable const& createdVariable = makeVarGlobal ? janiModel.addVariable(newIntegerVariable) : automaton.addVariable(newIntegerVariable); variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } else { + STORM_LOG_INFO("Variable " << variable.getName() << " is declared but never used."); } } for (auto const& variable : module.getBooleanVariables()) { storm::jani::BooleanVariable newBooleanVariable = *storm::jani::makeBooleanVariable(variable.getName(), variable.getExpressionVariable(), variable.hasInitialValue() ? boost::make_optional(variable.getInitialValueExpression()) : boost::none, false); - std::set const& accessingModuleIndices = variablesToAccessingModuleIndices[variable.getExpressionVariable()]; - // If there is exactly one module reading and writing the variable, we can make the variable local to this module. - if (!allVariablesGlobal && accessingModuleIndices.size() == 1) { - storm::jani::BooleanVariable const& createdVariable = automaton.addVariable(newBooleanVariable); - variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); - } else if (!accessingModuleIndices.empty()) { - // Otherwise, we need to make it global. - storm::jani::BooleanVariable const& createdVariable = janiModel.addVariable(newBooleanVariable); + auto findRes = variablesToMakeGlobal.find(variable.getExpressionVariable()); + if (findRes != variablesToMakeGlobal.end()) { + bool makeVarGlobal = findRes->second; + storm::jani::BooleanVariable const& createdVariable = makeVarGlobal ? janiModel.addVariable(newBooleanVariable) : automaton.addVariable(newBooleanVariable); variableToVariableMap.emplace(variable.getExpressionVariable(), createdVariable); + } else { + STORM_LOG_INFO("Variable " << variable.getName() << " is declared but never used."); } } automaton.setInitialStatesRestriction(manager->boolean(true)); @@ -195,10 +245,13 @@ namespace storm { automaton.addInitialLocation(onlyLocationIndex); // If we are translating the first module that has the action, we need to add the transient assignments to the location. + // However, in standard compliant JANI, there are no state rewards if (firstModule) { storm::jani::Location& onlyLocation = automaton.getLocation(onlyLocationIndex); for (auto const& assignment : transientLocationAssignments) { - onlyLocation.addTransientAssignment(assignment); + if (assignment.getVariable().isBooleanVariable() || produceStateRewards) { + onlyLocation.addTransientAssignment(assignment); + } } } @@ -243,6 +296,12 @@ namespace storm { templateEdge->addTransientAssignment(assignment); } } + if (!produceStateRewards) { + transientEdgeAssignmentsToAdd = transientRewardLocationAssignmentsPerAction.find(janiModel.getActionIndex(command.getActionName())); + for (auto const& assignment : transientEdgeAssignmentsToAdd->second) { + templateEdge->addTransientAssignment(assignment, true); + } + } // Create the edge object. storm::jani::Edge newEdge; @@ -261,6 +320,11 @@ namespace storm { // NOTE: This only works for the standard composition and not for any custom compositions. This case // must be checked for earlier. for (auto actionIndex : actionIndicesOfModule) { + // Do not delete rewards dealt out on non-synchronizing edges. + if (actionIndex == janiModel.getActionIndex("")) { + continue; + } + auto it = transientEdgeAssignments.find(actionIndex); if (it != transientEdgeAssignments.end()) { transientEdgeAssignments.erase(it); @@ -271,13 +335,6 @@ namespace storm { firstModule = false; } - // Create an initial state restriction if there was an initial construct in the program. - if (program.hasInitialConstruct()) { - janiModel.setInitialStatesRestriction(program.getInitialConstruct().getInitialStatesExpression()); - } else { - janiModel.setInitialStatesRestriction(manager->boolean(true)); - } - // Set the standard system composition. This is possible, because we reject non-standard compositions anyway. if (program.specifiesSystemComposition()) { CompositionToJaniVisitor visitor; diff --git a/src/storm/storage/prism/ToJaniConverter.h b/src/storm/storage/prism/ToJaniConverter.h index e68c9612e..2ba1c870f 100644 --- a/src/storm/storage/prism/ToJaniConverter.h +++ b/src/storm/storage/prism/ToJaniConverter.h @@ -14,7 +14,7 @@ namespace storm { class ToJaniConverter { public: - storm::jani::Model convert(storm::prism::Program const& program, bool allVariablesGlobal = false, std::string suffix = ""); + storm::jani::Model convert(storm::prism::Program const& program, bool allVariablesGlobal = true, std::string suffix = "", bool standardCompliant = false); bool labelsWereRenamed() const; std::map const& getLabelRenaming() const; diff --git a/src/storm/transformer/EndComponentEliminator.h b/src/storm/transformer/EndComponentEliminator.h index 635cba7b6..378edc86a 100644 --- a/src/storm/transformer/EndComponentEliminator.h +++ b/src/storm/transformer/EndComponentEliminator.h @@ -18,7 +18,7 @@ namespace storm { // The resulting matrix storm::storage::SparseMatrix matrix; // Index mapping that gives for each row of the resulting matrix the corresponding row in the original matrix. - // For the empty rows added to EC states, an arbitrary row of the original matrix that stays inside the EC is given. + // For the sink rows added to EC states, an arbitrary row of the original matrix that stays inside the EC is given. std::vector newToOldRowMapping; // Gives for each state (=rowGroup) of the original matrix the corresponding state in the resulting matrix. // States of a removed ECs are mapped to the state that substitutes the EC. @@ -38,9 +38,10 @@ namespace storm { * * For each such EC (that is not contained in another EC), we add a new state and redirect all incoming and outgoing * transitions of the EC to (and from) this state. - * If addEmptyRowStates is true for at least one state of an eliminated EC, an empty row is added to the new state (representing the choice to stay at the EC forever). + * If addSinkRowStates is true for at least one state of an eliminated EC, a row is added to the new state (representing the choice to stay at the EC forever). + * If addSelfLoopAtSinkStates is true, such rows get a selfloop (with value 1). Otherwise, the row remains empty. */ - static EndComponentEliminatorReturnType transform(storm::storage::SparseMatrix const& originalMatrix, storm::storage::BitVector const& subsystemStates, storm::storage::BitVector const& possibleECRows, storm::storage::BitVector const& addEmptyRowStates) { + static EndComponentEliminatorReturnType transform(storm::storage::SparseMatrix const& originalMatrix, storm::storage::BitVector const& subsystemStates, storm::storage::BitVector const& possibleECRows, storm::storage::BitVector const& addSinkRowStates, bool addSelfLoopAtSinkStates = false) { STORM_LOG_DEBUG("Invoked EndComponentEliminator on matrix with " << originalMatrix.getRowGroupCount() << " row groups."); storm::storage::MaximalEndComponentDecomposition ecs = computeECs(originalMatrix, possibleECRows, subsystemStates); @@ -57,7 +58,7 @@ namespace storm { EndComponentEliminatorReturnType result; std::vector newRowGroupIndices; result.oldToNewStateMapping = std::vector(originalMatrix.getRowGroupCount(), std::numeric_limits::max()); - storm::storage::BitVector emptyRows(originalMatrix.getRowCount(), false); // will be resized as soon as the rowCount of the resulting matrix is known + storm::storage::BitVector sinkRows(originalMatrix.getRowCount(), false); // will be resized as soon as the rowCount of the resulting matrix is known for(auto keptState : keptStates) { result.oldToNewStateMapping[keptState] = newRowGroupIndices.size(); // i.e., the current number of processed states @@ -68,7 +69,7 @@ namespace storm { } for (auto const& ec : ecs) { newRowGroupIndices.push_back(result.newToOldRowMapping.size()); - bool ecGetsEmptyRow = false; + bool ecGetsSinkRow = false; for (auto const& stateActionsPair : ec) { result.oldToNewStateMapping[stateActionsPair.first] = newRowGroupIndices.size()-1; for(uint_fast64_t row = originalMatrix.getRowGroupIndices()[stateActionsPair.first]; row < originalMatrix.getRowGroupIndices()[stateActionsPair.first +1]; ++row) { @@ -76,18 +77,18 @@ namespace storm { result.newToOldRowMapping.push_back(row); } } - ecGetsEmptyRow |= addEmptyRowStates.get(stateActionsPair.first); + ecGetsSinkRow |= addSinkRowStates.get(stateActionsPair.first); } - if(ecGetsEmptyRow) { + if(ecGetsSinkRow) { STORM_LOG_ASSERT(result.newToOldRowMapping.size() < originalMatrix.getRowCount(), "Didn't expect to see more rows in the reduced matrix than in the original one."); - emptyRows.set(result.newToOldRowMapping.size(), true); + sinkRows.set(result.newToOldRowMapping.size(), true); result.newToOldRowMapping.push_back(*ec.begin()->second.begin()); } } newRowGroupIndices.push_back(result.newToOldRowMapping.size()); - emptyRows.resize(result.newToOldRowMapping.size()); + sinkRows.resize(result.newToOldRowMapping.size()); - result.matrix = buildTransformedMatrix(originalMatrix, newRowGroupIndices, result.newToOldRowMapping, result.oldToNewStateMapping, emptyRows); + result.matrix = buildTransformedMatrix(originalMatrix, newRowGroupIndices, result.newToOldRowMapping, result.oldToNewStateMapping, sinkRows, addSelfLoopAtSinkStates); STORM_LOG_DEBUG("EndComponentEliminator is done. Resulting matrix has " << result.matrix.getRowGroupCount() << " row groups."); return result; } @@ -136,15 +137,20 @@ namespace storm { std::vector const& newRowGroupIndices, std::vector const& newToOldRowMapping, std::vector const& oldToNewStateMapping, - storm::storage::BitVector const& emptyRows) { + storm::storage::BitVector const& sinkRows, + bool addSelfLoopAtSinkStates) { uint_fast64_t numRowGroups = newRowGroupIndices.size()-1; uint_fast64_t newRow = 0; storm::storage::SparseMatrixBuilder builder(newToOldRowMapping.size(), numRowGroups, originalMatrix.getEntryCount(), false, true, numRowGroups); - for(uint_fast64_t newRowGroup = 0; newRowGroup < numRowGroups; ++newRowGroup) { + for (uint_fast64_t newRowGroup = 0; newRowGroup < numRowGroups; ++newRowGroup) { builder.newRowGroup(newRow); - for(; newRow < newRowGroupIndices[newRowGroup+1]; ++newRow) { - if(!emptyRows.get(newRow)) { + for (; newRow < newRowGroupIndices[newRowGroup + 1]; ++newRow) { + if (sinkRows.get(newRow)) { + if (addSelfLoopAtSinkStates) { + builder.addNextValue(newRow, newRowGroup, storm::utility::one()); + } + } else { // Make sure that the entries for this row are inserted in the right order. // Also, transitions to the same EC need to be merged and transitions to states that are erased need to be ignored std::map sortedEntries; diff --git a/src/storm/transformer/SymbolicToSparseTransformer.cpp b/src/storm/transformer/SymbolicToSparseTransformer.cpp index 8ebac17de..66d3dd1b3 100644 --- a/src/storm/transformer/SymbolicToSparseTransformer.cpp +++ b/src/storm/transformer/SymbolicToSparseTransformer.cpp @@ -7,12 +7,35 @@ #include "storm/models/sparse/StandardRewardModel.h" #include "storm/utility/macros.h" #include "storm/exceptions/NotImplementedException.h" +#include "storm/logic/AtomicExpressionFormula.h" +#include "storm/logic/AtomicLabelFormula.h" namespace storm { namespace transformer { + struct LabelInformation { + LabelInformation(std::vector> const& formulas) { + for (auto const& formula : formulas) { + std::vector> atomicLabelFormulas = formula->getAtomicLabelFormulas(); + for (auto const& labelFormula : atomicLabelFormulas) { + atomicLabels.insert(labelFormula->getLabel()); + } + + std::vector> atomicExpressionFormulas = formula->getAtomicExpressionFormulas(); + for (auto const& expressionFormula : atomicExpressionFormulas) { + std::stringstream ss; + ss << expressionFormula->getExpression(); + expressionLabels[ss.str()] = expressionFormula->getExpression(); + } + } + } + + std::set atomicLabels; + std::map expressionLabels; + }; + template - std::shared_ptr> SymbolicDtmcToSparseDtmcTransformer::translate(storm::models::symbolic::Dtmc const& symbolicDtmc) { + std::shared_ptr> SymbolicDtmcToSparseDtmcTransformer::translate(storm::models::symbolic::Dtmc const& symbolicDtmc, std::vector> const& formulas) { this->odd = symbolicDtmc.getReachableStates().createOdd(); storm::storage::SparseMatrix transitionMatrix = symbolicDtmc.getTransitionMatrix().toMatrix(this->odd, this->odd); @@ -36,8 +59,18 @@ namespace storm { labelling.addLabel("init", symbolicDtmc.getInitialStates().toVector(this->odd)); labelling.addLabel("deadlock", symbolicDtmc.getDeadlockStates().toVector(this->odd)); - for(auto const& label : symbolicDtmc.getLabels()) { - labelling.addLabel(label, symbolicDtmc.getStates(label).toVector(this->odd)); + if (formulas.empty()) { + for (auto const& label : symbolicDtmc.getLabels()) { + labelling.addLabel(label, symbolicDtmc.getStates(label).toVector(this->odd)); + } + } else { + LabelInformation labelInfo(formulas); + for (auto const& label : labelInfo.atomicLabels) { + labelling.addLabel(label, symbolicDtmc.getStates(label).toVector(this->odd)); + } + for (auto const& expressionLabel : labelInfo.expressionLabels) { + labelling.addLabel(expressionLabel.first, symbolicDtmc.getStates(expressionLabel.second).toVector(this->odd)); + } } return std::make_shared>(transitionMatrix, labelling, rewardModels); } @@ -48,7 +81,7 @@ namespace storm { } template - std::shared_ptr> SymbolicMdpToSparseMdpTransformer::translate(storm::models::symbolic::Mdp const& symbolicMdp) { + std::shared_ptr> SymbolicMdpToSparseMdpTransformer::translate(storm::models::symbolic::Mdp const& symbolicMdp, std::vector> const& formulas) { storm::dd::Odd odd = symbolicMdp.getReachableStates().createOdd(); storm::storage::SparseMatrix transitionMatrix = symbolicMdp.getTransitionMatrix().toMatrix(symbolicMdp.getNondeterminismVariables(), odd, odd); std::unordered_map> rewardModels; @@ -69,15 +102,25 @@ namespace storm { labelling.addLabel("init", symbolicMdp.getInitialStates().toVector(odd)); labelling.addLabel("deadlock", symbolicMdp.getDeadlockStates().toVector(odd)); - for(auto const& label : symbolicMdp.getLabels()) { - labelling.addLabel(label, symbolicMdp.getStates(label).toVector(odd)); + if (formulas.empty()) { + for (auto const& label : symbolicMdp.getLabels()) { + labelling.addLabel(label, symbolicMdp.getStates(label).toVector(odd)); + } + } else { + LabelInformation labelInfo(formulas); + for (auto const& label : labelInfo.atomicLabels) { + labelling.addLabel(label, symbolicMdp.getStates(label).toVector(odd)); + } + for (auto const& expressionLabel : labelInfo.expressionLabels) { + labelling.addLabel(expressionLabel.first, symbolicMdp.getStates(expressionLabel.second).toVector(odd)); + } } + return std::make_shared>(transitionMatrix, labelling, rewardModels); } template - std::shared_ptr> SymbolicCtmcToSparseCtmcTransformer::translate( - storm::models::symbolic::Ctmc const& symbolicCtmc) { + std::shared_ptr> SymbolicCtmcToSparseCtmcTransformer::translate(storm::models::symbolic::Ctmc const& symbolicCtmc, std::vector> const& formulas) { storm::dd::Odd odd = symbolicCtmc.getReachableStates().createOdd(); storm::storage::SparseMatrix transitionMatrix = symbolicCtmc.getTransitionMatrix().toMatrix(odd, odd); std::unordered_map> rewardModels; @@ -100,9 +143,20 @@ namespace storm { labelling.addLabel("init", symbolicCtmc.getInitialStates().toVector(odd)); labelling.addLabel("deadlock", symbolicCtmc.getDeadlockStates().toVector(odd)); - for(auto const& label : symbolicCtmc.getLabels()) { - labelling.addLabel(label, symbolicCtmc.getStates(label).toVector(odd)); + if (formulas.empty()) { + for (auto const& label : symbolicCtmc.getLabels()) { + labelling.addLabel(label, symbolicCtmc.getStates(label).toVector(odd)); + } + } else { + LabelInformation labelInfo(formulas); + for (auto const& label : labelInfo.atomicLabels) { + labelling.addLabel(label, symbolicCtmc.getStates(label).toVector(odd)); + } + for (auto const& expressionLabel : labelInfo.expressionLabels) { + labelling.addLabel(expressionLabel.first, symbolicCtmc.getStates(expressionLabel.second).toVector(odd)); + } } + return std::make_shared>(transitionMatrix, labelling, rewardModels); } diff --git a/src/storm/transformer/SymbolicToSparseTransformer.h b/src/storm/transformer/SymbolicToSparseTransformer.h index 5f4bed6c4..e2c191b49 100644 --- a/src/storm/transformer/SymbolicToSparseTransformer.h +++ b/src/storm/transformer/SymbolicToSparseTransformer.h @@ -1,5 +1,6 @@ #pragma once +#include "storm/logic/Formula.h" #include "storm/models/sparse/Dtmc.h" #include "storm/models/symbolic/Dtmc.h" #include "storm/models/sparse/Mdp.h" @@ -15,7 +16,7 @@ namespace storm { template class SymbolicDtmcToSparseDtmcTransformer { public: - std::shared_ptr> translate(storm::models::symbolic::Dtmc const& symbolicDtmc); + std::shared_ptr> translate(storm::models::symbolic::Dtmc const& symbolicDtmc, std::vector> const& formulas = std::vector>()); storm::dd::Odd const& getOdd() const; private: @@ -25,13 +26,13 @@ namespace storm { template class SymbolicMdpToSparseMdpTransformer { public: - static std::shared_ptr> translate(storm::models::symbolic::Mdp const& symbolicMdp); + static std::shared_ptr> translate(storm::models::symbolic::Mdp const& symbolicMdp, std::vector> const& formulas = std::vector>()); }; template class SymbolicCtmcToSparseCtmcTransformer { public: - static std::shared_ptr> translate(storm::models::symbolic::Ctmc const& symbolicCtmc); + static std::shared_ptr> translate(storm::models::symbolic::Ctmc const& symbolicCtmc, std::vector> const& formulas = std::vector>()); }; } } diff --git a/src/storm/utility/ExpressionHelper.cpp b/src/storm/utility/ExpressionHelper.cpp index 219d6e39c..fd99855f8 100644 --- a/src/storm/utility/ExpressionHelper.cpp +++ b/src/storm/utility/ExpressionHelper.cpp @@ -12,17 +12,16 @@ namespace storm { if (summands.empty()) { return manager->rational(storm::utility::zero()); } - // As the sum can potentially have many summands, we want to make sure that the formula tree is (roughly balanced) - auto it = summands.begin(); - while (summands.size() > 1) { - if (it == summands.end() || it == summands.end() - 1) { - it = summands.begin(); + storm::expressions::Expression res = summands.front(); + bool first = true; + for (auto& s : summands) { + if (first) { + first = false; + } else { + res = res + s; } - *it = *it + summands.back(); - summands.pop_back(); - ++it; } - return summands.front(); + return res.simplify().reduceNesting(); } } diff --git a/src/storm/utility/storm-version.h b/src/storm/utility/storm-version.h index b6b1d3d2e..e87cbd489 100644 --- a/src/storm/utility/storm-version.h +++ b/src/storm/utility/storm-version.h @@ -30,12 +30,12 @@ namespace storm { /// The source of the versioning information. const static VersionSource versionSource; - + /// The short hash of the git commit this build is based on const static std::string gitRevisionHash; /// How many commits passed since the tag was last set. - const static boost::optional commitsAhead; + const static unsigned commitsAhead; /// 0 iff there no files were modified in the checkout, 1 otherwise. If none, no information about dirtyness is given. const static boost::optional dirty; @@ -70,8 +70,8 @@ namespace storm { if (versionSource == VersionSource::Static) { sstream << " (derived statically)"; } - if (commitsAhead) { - sstream << " (+ " << commitsAhead.get() << " commits)"; + if (commitsAhead > 0) { + sstream << " (+ " << commitsAhead << " commits)"; } if (!gitRevisionHash.empty()) { sstream << " build from revision " << gitRevisionHash; diff --git a/src/test/storm-dft/CMakeLists.txt b/src/test/storm-dft/CMakeLists.txt index d461c2f9d..a0e96ee36 100644 --- a/src/test/storm-dft/CMakeLists.txt +++ b/src/test/storm-dft/CMakeLists.txt @@ -13,7 +13,7 @@ foreach (testsuite api) file(GLOB_RECURSE TEST_${testsuite}_FILES ${STORM_TESTS_BASE_PATH}/${testsuite}/*.h ${STORM_TESTS_BASE_PATH}/${testsuite}/*.cpp) add_executable (test-dft-${testsuite} ${TEST_${testsuite}_FILES} ${STORM_TESTS_BASE_PATH}/storm-test.cpp) - target_link_libraries(test-dft-${testsuite} storm-dft) + target_link_libraries(test-dft-${testsuite} storm-dft storm-parsers) target_link_libraries(test-dft-${testsuite} ${STORM_TEST_LINK_LIBRARIES}) add_dependencies(test-dft-${testsuite} test-resources) diff --git a/src/test/storm-dft/api/DftModelCheckerTest.cpp b/src/test/storm-dft/api/DftModelCheckerTest.cpp index 5db58d60d..71c440abd 100644 --- a/src/test/storm-dft/api/DftModelCheckerTest.cpp +++ b/src/test/storm-dft/api/DftModelCheckerTest.cpp @@ -2,6 +2,7 @@ #include "storm-config.h" #include "storm-dft/api/storm-dft.h" +#include "storm-parsers/api/storm-parsers.h" namespace { @@ -62,7 +63,7 @@ namespace { } double analyzeMTTF(std::string const& file) { - std::shared_ptr> dft = storm::api::loadDFTGalileo(file); + std::shared_ptr> dft = storm::api::loadDFTGalileoFile(file); std::string property = "Tmin=? [F \"failed\"]"; std::vector> properties = storm::api::extractFormulasFromProperties(storm::api::parseProperties(property)); typename storm::modelchecker::DFTModelChecker::dft_results results = storm::api::analyzeDFT(*dft, properties, config.useSR, config.useMod, config.useDC, false); diff --git a/src/test/storm-dft/api/DftParserTest.cpp b/src/test/storm-dft/api/DftParserTest.cpp index 48a621ac1..93df569d1 100644 --- a/src/test/storm-dft/api/DftParserTest.cpp +++ b/src/test/storm-dft/api/DftParserTest.cpp @@ -5,9 +5,17 @@ namespace { - TEST(DftParserTest, LoadFromGalileo) { + TEST(DftParserTest, LoadFromGalileoFile) { std::string file = STORM_TEST_RESOURCES_DIR "/dft/and.dft"; - std::shared_ptr> dft = storm::api::loadDFTGalileo(file); + std::shared_ptr> dft = storm::api::loadDFTGalileoFile(file); + EXPECT_EQ(3ul, dft->nrElements()); + EXPECT_EQ(2ul, dft->nrBasicElements()); } + TEST(DftParserTest, LoadFromJsonFile) { + std::string file = STORM_TEST_RESOURCES_DIR "/dft/and.json"; + std::shared_ptr> dft = storm::api::loadDFTJsonFile(file); + EXPECT_EQ(3ul, dft->nrElements()); + EXPECT_EQ(2ul, dft->nrBasicElements()); + } } diff --git a/src/test/storm-pars/CMakeLists.txt b/src/test/storm-pars/CMakeLists.txt index 0f2793553..28c5a391d 100644 --- a/src/test/storm-pars/CMakeLists.txt +++ b/src/test/storm-pars/CMakeLists.txt @@ -13,7 +13,7 @@ foreach (testsuite modelchecker utility) file(GLOB_RECURSE TEST_${testsuite}_FILES ${STORM_TESTS_BASE_PATH}/${testsuite}/*.h ${STORM_TESTS_BASE_PATH}/${testsuite}/*.cpp) add_executable (test-pars-${testsuite} ${TEST_${testsuite}_FILES} ${STORM_TESTS_BASE_PATH}/storm-test.cpp) - target_link_libraries(test-pars-${testsuite} storm-pars) + target_link_libraries(test-pars-${testsuite} storm-pars storm-parsers) target_link_libraries(test-pars-${testsuite} ${STORM_TEST_LINK_LIBRARIES}) add_dependencies(test-pars-${testsuite} test-resources) diff --git a/src/test/storm-pars/modelchecker/ParametricDtmcPrctlModelCheckerTest.cpp b/src/test/storm-pars/modelchecker/ParametricDtmcPrctlModelCheckerTest.cpp index cf30285c5..91e08d2cf 100644 --- a/src/test/storm-pars/modelchecker/ParametricDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm-pars/modelchecker/ParametricDtmcPrctlModelCheckerTest.cpp @@ -2,13 +2,13 @@ #include "storm-config.h" #include "test/storm_gtest.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" -#include "storm/parser/AutoParser.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/AutoParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/storage/expressions/ExpressionManager.h" #include "storm/api/builder.h" diff --git a/src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp b/src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp index dd6d59cf8..f77b61544 100644 --- a/src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp +++ b/src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp @@ -8,7 +8,11 @@ #include "storm-pars/api/storm-pars.h" #include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" + #include "storm/environment/solver/MinMaxSolverEnvironment.h" +#include "storm/storage/jani/Property.h" + namespace { class DoubleViEnvironment { diff --git a/src/test/storm-pars/modelchecker/SparseMdpParameterLiftingTest.cpp b/src/test/storm-pars/modelchecker/SparseMdpParameterLiftingTest.cpp index 3d3c97caa..30b11f15d 100644 --- a/src/test/storm-pars/modelchecker/SparseMdpParameterLiftingTest.cpp +++ b/src/test/storm-pars/modelchecker/SparseMdpParameterLiftingTest.cpp @@ -8,7 +8,11 @@ #include "storm-pars/api/storm-pars.h" #include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" + #include "storm/environment/solver/MinMaxSolverEnvironment.h" +#include "storm/storage/jani/Property.h" + namespace { class DoubleViEnvironment { diff --git a/src/test/storm-pars/modelchecker/SymbolicParametricDtmcPrctlModelCheckerTest.cpp b/src/test/storm-pars/modelchecker/SymbolicParametricDtmcPrctlModelCheckerTest.cpp index de5e6f849..f23a23e4a 100644 --- a/src/test/storm-pars/modelchecker/SymbolicParametricDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm-pars/modelchecker/SymbolicParametricDtmcPrctlModelCheckerTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/utility/solver.h" #include "storm/storage/SymbolicModelDescription.h" @@ -9,7 +9,7 @@ #include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h" #include "storm/modelchecker/results/SymbolicQuantitativeCheckResult.h" #include "storm/solver/SymbolicEliminationLinearEquationSolver.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/builder/DdPrismModelBuilder.h" #include "storm/models/symbolic/StandardRewardModel.h" #include "storm/models/symbolic/Dtmc.h" diff --git a/src/test/storm-pars/utility/ModelInstantiatorTest.cpp b/src/test/storm-pars/utility/ModelInstantiatorTest.cpp index e66a912d5..f9fbd1bf0 100644 --- a/src/test/storm-pars/utility/ModelInstantiatorTest.cpp +++ b/src/test/storm-pars/utility/ModelInstantiatorTest.cpp @@ -13,9 +13,13 @@ #include "storm-pars/utility/ModelInstantiator.h" #include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" #include "storm/models/sparse/Model.h" #include "storm/models/sparse/Dtmc.h" #include "storm/models/sparse/Mdp.h" +#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" +#include "storm/storage/jani/Property.h" + TEST(ModelInstantiatorTest, BrpProb) { carl::VariablePool::getInstance().clear(); diff --git a/src/test/storm/CMakeLists.txt b/src/test/storm/CMakeLists.txt index 0ae2fc509..c745943b4 100644 --- a/src/test/storm/CMakeLists.txt +++ b/src/test/storm/CMakeLists.txt @@ -13,7 +13,7 @@ foreach (testsuite abstraction adapter builder logic modelchecker parser permiss file(GLOB_RECURSE TEST_${testsuite}_FILES ${STORM_TESTS_BASE_PATH}/${testsuite}/*.h ${STORM_TESTS_BASE_PATH}/${testsuite}/*.cpp) add_executable (test-${testsuite} ${TEST_${testsuite}_FILES} ${STORM_TESTS_BASE_PATH}/storm-test.cpp) - target_link_libraries(test-${testsuite} storm) + target_link_libraries(test-${testsuite} storm storm-parsers) target_link_libraries(test-${testsuite} ${STORM_TEST_LINK_LIBRARIES}) add_dependencies(test-${testsuite} test-resources) diff --git a/src/test/storm/abstraction/PrismMenuGameTest.cpp b/src/test/storm/abstraction/PrismMenuGameTest.cpp index a200c1d68..96c484d99 100644 --- a/src/test/storm/abstraction/PrismMenuGameTest.cpp +++ b/src/test/storm/abstraction/PrismMenuGameTest.cpp @@ -3,7 +3,7 @@ #ifdef STORM_HAVE_MSAT -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/abstraction/MenuGameRefiner.h" #include "storm/abstraction/prism/PrismMenuGameAbstractor.h" diff --git a/src/test/storm/builder/DdJaniModelBuilderTest.cpp b/src/test/storm/builder/DdJaniModelBuilderTest.cpp index fa657a96f..1515b83e3 100644 --- a/src/test/storm/builder/DdJaniModelBuilderTest.cpp +++ b/src/test/storm/builder/DdJaniModelBuilderTest.cpp @@ -10,7 +10,7 @@ #include "storm/storage/jani/Compositions.h" #include "storm/models/symbolic/StandardRewardModel.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/builder/DdJaniModelBuilder.h" #include "storm/settings/SettingMemento.h" diff --git a/src/test/storm/builder/DdPrismModelBuilderTest.cpp b/src/test/storm/builder/DdPrismModelBuilderTest.cpp index 1ff4a79fe..a56aa1e12 100644 --- a/src/test/storm/builder/DdPrismModelBuilderTest.cpp +++ b/src/test/storm/builder/DdPrismModelBuilderTest.cpp @@ -8,7 +8,7 @@ #include "storm/models/symbolic/Ctmc.h" #include "storm/models/symbolic/Mdp.h" #include "storm/models/symbolic/StandardRewardModel.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/builder/DdPrismModelBuilder.h" TEST(DdPrismModelBuilderTest_Sylvan, Dtmc) { diff --git a/src/test/storm/builder/ExplicitJaniModelBuilderTest.cpp b/src/test/storm/builder/ExplicitJaniModelBuilderTest.cpp index 4f21c3a64..95b690e96 100644 --- a/src/test/storm/builder/ExplicitJaniModelBuilderTest.cpp +++ b/src/test/storm/builder/ExplicitJaniModelBuilderTest.cpp @@ -3,7 +3,7 @@ #include "storm/models/sparse/StandardRewardModel.h" #include "storm/models/sparse/MarkovAutomaton.h" #include "storm/settings/SettingMemento.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/builder/ExplicitModelBuilder.h" #include "storm/generator/JaniNextStateGenerator.h" #include "storm/storage/jani/Model.h" diff --git a/src/test/storm/builder/ExplicitJitJaniModelBuilderTest.cpp b/src/test/storm/builder/ExplicitJitJaniModelBuilderTest.cpp index 334735b02..c5c0eec05 100644 --- a/src/test/storm/builder/ExplicitJitJaniModelBuilderTest.cpp +++ b/src/test/storm/builder/ExplicitJitJaniModelBuilderTest.cpp @@ -3,7 +3,7 @@ #include "storm/models/sparse/StandardRewardModel.h" #include "storm/models/sparse/MarkovAutomaton.h" #include "storm/settings/SettingMemento.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/builder/jit/ExplicitJitJaniModelBuilder.h" #include "storm/storage/jani/Model.h" diff --git a/src/test/storm/builder/ExplicitPrismModelBuilderTest.cpp b/src/test/storm/builder/ExplicitPrismModelBuilderTest.cpp index 8e5929618..65e1dff1d 100644 --- a/src/test/storm/builder/ExplicitPrismModelBuilderTest.cpp +++ b/src/test/storm/builder/ExplicitPrismModelBuilderTest.cpp @@ -2,7 +2,7 @@ #include "storm-config.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/models/sparse/MarkovAutomaton.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/builder/ExplicitModelBuilder.h" diff --git a/src/test/storm/logic/FragmentCheckerTest.cpp b/src/test/storm/logic/FragmentCheckerTest.cpp index c846f7f68..5adf50028 100644 --- a/src/test/storm/logic/FragmentCheckerTest.cpp +++ b/src/test/storm/logic/FragmentCheckerTest.cpp @@ -1,6 +1,6 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/FragmentChecker.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/storage/expressions/ExpressionManager.h" diff --git a/src/test/storm/modelchecker/ConditionalDtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/ConditionalDtmcPrctlModelCheckerTest.cpp index c0972c474..fd124f456 100644 --- a/src/test/storm/modelchecker/ConditionalDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/ConditionalDtmcPrctlModelCheckerTest.cpp @@ -2,12 +2,12 @@ #include "test/storm_gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/api/builder.h" #include "storm/storage/expressions/ExpressionManager.h" diff --git a/src/test/storm/modelchecker/CtmcCslModelCheckerTest.cpp b/src/test/storm/modelchecker/CtmcCslModelCheckerTest.cpp index f2a790b9b..bf6f8654f 100644 --- a/src/test/storm/modelchecker/CtmcCslModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/CtmcCslModelCheckerTest.cpp @@ -3,9 +3,10 @@ #include "storm-config.h" #include "storm/api/builder.h" -#include "storm/api/model_descriptions.h" +#include "storm-parsers/api/model_descriptions.h" #include "storm/api/properties.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/api/properties.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/solver/EigenLinearEquationSolver.h" #include "storm/models/sparse/StandardRewardModel.h" @@ -18,7 +19,7 @@ #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h" #include "storm/modelchecker/results/QualitativeCheckResult.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/storage/expressions/ExpressionManager.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/environment/solver/NativeSolverEnvironment.h" @@ -139,6 +140,7 @@ namespace { ValueType precision() const { return TestType::isExact ? parseNumber("0") : parseNumber("1e-6");} bool isSparseModel() const { return std::is_same::value; } bool isSymbolicModel() const { return std::is_same::value; } + storm::settings::modules::CoreSettings::Engine getEngine() const { return TestType::engine; } template typename std::enable_if::value, std::pair, std::vector>>>::type @@ -361,7 +363,29 @@ namespace { result = checker->check(this->env(), tasks[6]); EXPECT_NEAR(this->parseNumber("0.9100373532"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + } + + TYPED_TEST(CtmcCslModelCheckerTest, simple2) { + std::string formulasString = "R{\"rew1\"}=? [ C ]"; + formulasString += "; R{\"rew2\"}=? [ C ]"; + auto modelFormulas = this->buildModelFormulas(STORM_TEST_RESOURCES_DIR "/ctmc/simple2.sm", formulasString); + auto model = std::move(modelFormulas.first); + auto tasks = this->getTasks(modelFormulas.second); + EXPECT_EQ(5ul, model->getNumberOfStates()); + EXPECT_EQ(8ul, model->getNumberOfTransitions()); + ASSERT_EQ(model->getType(), storm::models::ModelType::Ctmc); + auto checker = this->createModelChecker(model); + std::unique_ptr result; + + // Total reward formulas are currently not supported for non-sparse models. + if (this->isSparseModel()) { + result = checker->check(this->env(), tasks[0]); + EXPECT_NEAR(this->parseNumber("23/8"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + + result = checker->check(this->env(), tasks[1]); + EXPECT_TRUE(storm::utility::isInfinity(this->getQuantitativeResultAtInitialState(model, result))); + } } } \ No newline at end of file diff --git a/src/test/storm/modelchecker/DtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/DtmcPrctlModelCheckerTest.cpp index ad6654ff4..f7f3b72a1 100644 --- a/src/test/storm/modelchecker/DtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/DtmcPrctlModelCheckerTest.cpp @@ -3,9 +3,10 @@ #include "storm-config.h" #include "storm/api/builder.h" -#include "storm/api/model_descriptions.h" +#include "storm-parsers/api/model_descriptions.h" #include "storm/api/properties.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/api/properties.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/solver/EigenLinearEquationSolver.h" #include "storm/models/sparse/StandardRewardModel.h" @@ -19,7 +20,7 @@ #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h" #include "storm/modelchecker/results/QualitativeCheckResult.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/storage/expressions/ExpressionManager.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/environment/solver/NativeSolverEnvironment.h" diff --git a/src/test/storm/modelchecker/ExplicitDtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/ExplicitDtmcPrctlModelCheckerTest.cpp index 06b90270b..025bc8c45 100644 --- a/src/test/storm/modelchecker/ExplicitDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/ExplicitDtmcPrctlModelCheckerTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h" @@ -9,8 +9,8 @@ #include "storm/settings/SettingsManager.h" #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/SettingMemento.h" -#include "storm/parser/AutoParser.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/AutoParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/builder/ExplicitModelBuilder.h" #include "storm/storage/expressions/ExpressionManager.h" diff --git a/src/test/storm/modelchecker/ExplicitMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/ExplicitMdpPrctlModelCheckerTest.cpp index 91db98302..b295c397b 100644 --- a/src/test/storm/modelchecker/ExplicitMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/ExplicitMdpPrctlModelCheckerTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/solver/StandardMinMaxLinearEquationSolver.h" #include "storm/models/sparse/StandardRewardModel.h" @@ -10,8 +10,8 @@ #include "storm/environment/solver/MinMaxSolverEnvironment.h" -#include "storm/parser/AutoParser.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/AutoParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/builder/ExplicitModelBuilder.h" TEST(ExplicitMdpPrctlModelCheckerTest, Dice) { diff --git a/src/test/storm/modelchecker/GameBasedDtmcModelCheckerTest.cpp b/src/test/storm/modelchecker/GameBasedDtmcModelCheckerTest.cpp index 2a4f64c4d..3511aabec 100644 --- a/src/test/storm/modelchecker/GameBasedDtmcModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/GameBasedDtmcModelCheckerTest.cpp @@ -1,13 +1,13 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/utility/solver.h" #include "storm/modelchecker/abstraction/GameBasedMdpModelChecker.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/builder/DdPrismModelBuilder.h" #include "storm/models/symbolic/StandardRewardModel.h" #include "storm/models/symbolic/Dtmc.h" diff --git a/src/test/storm/modelchecker/GameBasedMdpModelCheckerTest.cpp b/src/test/storm/modelchecker/GameBasedMdpModelCheckerTest.cpp index 960cc56bc..225bf81b4 100644 --- a/src/test/storm/modelchecker/GameBasedMdpModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/GameBasedMdpModelCheckerTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/models/symbolic/StandardRewardModel.h" #include "storm/models/sparse/Model.h" @@ -13,6 +13,8 @@ #include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" + #if defined STORM_HAVE_MSAT TEST(GameBasedMdpModelCheckerTest, Dice_Cudd) { #else diff --git a/src/test/storm/modelchecker/LraDtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/LraDtmcPrctlModelCheckerTest.cpp index 59c589cb1..c964628b9 100644 --- a/src/test/storm/modelchecker/LraDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/LraDtmcPrctlModelCheckerTest.cpp @@ -2,7 +2,7 @@ #include "storm-config.h" #include "test/storm_gtest.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/settings/SettingMemento.h" #include "storm/logic/Formulas.h" #include "storm/solver/NativeLinearEquationSolver.h" @@ -17,7 +17,7 @@ #include "storm/environment/solver/EigenSolverEnvironment.h" #include "storm/environment/solver/GmmxxSolverEnvironment.h" -#include "storm/parser/AutoParser.h" +#include "storm-parsers/parser/AutoParser.h" #include "storm/builder/ExplicitModelBuilder.h" namespace { diff --git a/src/test/storm/modelchecker/LraMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/LraMdpPrctlModelCheckerTest.cpp index ed3abb16b..b05ebfb76 100644 --- a/src/test/storm/modelchecker/LraMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/LraMdpPrctlModelCheckerTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/solver/StandardMinMaxLinearEquationSolver.h" #include "storm/models/sparse/StandardRewardModel.h" @@ -12,7 +12,7 @@ #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/modules/NativeEquationSolverSettings.h" -#include "storm/parser/AutoParser.h" +#include "storm-parsers/parser/AutoParser.h" TEST(LraMdpPrctlModelCheckerTest, LRA_SingleMec) { storm::storage::SparseMatrixBuilder matrixBuilder; diff --git a/src/test/storm/modelchecker/MarkovAutomatonCslModelCheckerTest.cpp b/src/test/storm/modelchecker/MarkovAutomatonCslModelCheckerTest.cpp new file mode 100644 index 000000000..c979c3777 --- /dev/null +++ b/src/test/storm/modelchecker/MarkovAutomatonCslModelCheckerTest.cpp @@ -0,0 +1,270 @@ +#include "gtest/gtest.h" +#include "storm-config.h" + +#include "test/storm_gtest.h" + +#include "storm/api/builder.h" +#include "storm-parsers/api/model_descriptions.h" +#include "storm/api/properties.h" +#include "storm-parsers/api/properties.h" + +#include "storm/models/sparse/MarkovAutomaton.h" +#include "storm/models/symbolic/MarkovAutomaton.h" +#include "storm/models/sparse/StandardRewardModel.h" +#include "storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h" +#include "storm/modelchecker/results/QuantitativeCheckResult.h" +#include "storm/modelchecker/results/QualitativeCheckResult.h" +#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "storm/environment/solver/MinMaxSolverEnvironment.h" +#include "storm/environment/solver/TopologicalSolverEnvironment.h" +#include "storm/settings/modules/CoreSettings.h" +#include "storm/logic/Formulas.h" +#include "storm/storage/jani/Property.h" +#include "storm/exceptions/UncheckedRequirementException.h" + +namespace { + class SparseDoubleValueIterationEnvironment { + public: + static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models + static const storm::settings::modules::CoreSettings::Engine engine = storm::settings::modules::CoreSettings::Engine::Sparse; + static const bool isExact = false; + typedef double ValueType; + typedef storm::models::sparse::MarkovAutomaton ModelType; + static storm::Environment createEnvironment() { + storm::Environment env; + env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); + env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); + return env; + } + }; + class SparseDoubleIntervalIterationEnvironment { + public: + static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models + static const storm::settings::modules::CoreSettings::Engine engine = storm::settings::modules::CoreSettings::Engine::Sparse; + static const bool isExact = false; + typedef double ValueType; + typedef storm::models::sparse::MarkovAutomaton ModelType; + static storm::Environment createEnvironment() { + storm::Environment env; + env.solver().setForceSoundness(true); + env.solver().minMax().setMethod(storm::solver::MinMaxMethod::IntervalIteration); + env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-6)); + env.solver().minMax().setRelativeTerminationCriterion(false); + return env; + } + }; + class SparseRationalPolicyIterationEnvironment { + public: + static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models + static const storm::settings::modules::CoreSettings::Engine engine = storm::settings::modules::CoreSettings::Engine::Sparse; + static const bool isExact = true; + typedef storm::RationalNumber ValueType; + typedef storm::models::sparse::MarkovAutomaton ModelType; + static storm::Environment createEnvironment() { + storm::Environment env; + env.solver().minMax().setMethod(storm::solver::MinMaxMethod::PolicyIteration); + return env; + } + }; + class SparseRationalRationalSearchEnvironment { + public: + static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models + static const storm::settings::modules::CoreSettings::Engine engine = storm::settings::modules::CoreSettings::Engine::Sparse; + static const bool isExact = true; + typedef storm::RationalNumber ValueType; + typedef storm::models::sparse::MarkovAutomaton ModelType; + static storm::Environment createEnvironment() { + storm::Environment env; + env.solver().minMax().setMethod(storm::solver::MinMaxMethod::RationalSearch); + return env; + } + }; + + template + class MarkovAutomatonCslModelCheckerTest : public ::testing::Test { + public: + typedef typename TestType::ValueType ValueType; + typedef typename storm::models::sparse::MarkovAutomaton SparseModelType; + typedef typename storm::models::symbolic::MarkovAutomaton SymbolicModelType; + + MarkovAutomatonCslModelCheckerTest() : _environment(TestType::createEnvironment()) {} + storm::Environment const& env() const { return _environment; } + ValueType parseNumber(std::string const& input) const { return storm::utility::convertNumber(input);} + ValueType precision() const { return TestType::isExact ? parseNumber("0") : parseNumber("1e-6");} + bool isSparseModel() const { return std::is_same::value; } + bool isSymbolicModel() const { return std::is_same::value; } + + template + typename std::enable_if::value, std::pair, std::vector>>>::type + buildModelFormulas(std::string const& pathToPrismFile, std::string const& formulasAsString, std::string const& constantDefinitionString = "") const { + std::pair, std::vector>> result; + storm::prism::Program program = storm::api::parseProgram(pathToPrismFile); + program = storm::utility::prism::preprocess(program, constantDefinitionString); + result.second = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulasAsString, program)); + result.first = storm::api::buildSparseModel(program, result.second)->template as(); + return result; + } + + template + typename std::enable_if::value, std::pair, std::vector>>>::type + buildModelFormulas(std::string const& pathToPrismFile, std::string const& formulasAsString, std::string const& constantDefinitionString = "") const { + std::pair, std::vector>> result; + storm::prism::Program program = storm::api::parseProgram(pathToPrismFile); + program = storm::utility::prism::preprocess(program, constantDefinitionString); + result.second = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulasAsString, program)); + result.first = storm::api::buildSymbolicModel(program, result.second)->template as(); + return result; + } + + std::vector> getTasks(std::vector> const& formulas) const { + std::vector> result; + for (auto const& f : formulas) { + result.emplace_back(*f); + } + return result; + } + + template + typename std::enable_if::value, std::shared_ptr>>::type + createModelChecker(std::shared_ptr const& model) const { + if (TestType::engine == storm::settings::modules::CoreSettings::Engine::Sparse) { + return std::make_shared>(*model); + } + } + + template + typename std::enable_if::value, std::shared_ptr>>::type + createModelChecker(std::shared_ptr const& model) const { +// if (TestType::engine == storm::settings::modules::CoreSettings::Engine::Hybrid) { +// return std::make_shared>(*model); +// } else if (TestType::engine == storm::settings::modules::CoreSettings::Engine::Dd) { +// return std::make_shared>(*model); +// } + return nullptr; + } + + bool getQualitativeResultAtInitialState(std::shared_ptr> const& model, std::unique_ptr& result) { + auto filter = getInitialStateFilter(model); + result->filter(*filter); + return result->asQualitativeCheckResult().forallTrue(); + } + + ValueType getQuantitativeResultAtInitialState(std::shared_ptr> const& model, std::unique_ptr& result) { + auto filter = getInitialStateFilter(model); + result->filter(*filter); + return result->asQuantitativeCheckResult().getMin(); + } + + private: + storm::Environment _environment; + + std::unique_ptr getInitialStateFilter(std::shared_ptr> const& model) const { + if (isSparseModel()) { + return std::make_unique(model->template as()->getInitialStates()); + } else { + // return std::make_unique>(model->template as()->getReachableStates(), model->template as()->getInitialStates()); + return nullptr; + } + } + }; + + typedef ::testing::Types< + SparseDoubleValueIterationEnvironment, + SparseDoubleIntervalIterationEnvironment, + SparseRationalPolicyIterationEnvironment, + SparseRationalRationalSearchEnvironment + > TestingTypes; + + TYPED_TEST_CASE(MarkovAutomatonCslModelCheckerTest, TestingTypes); + + + TYPED_TEST(MarkovAutomatonCslModelCheckerTest, server) { + std::string formulasString = "Tmax=? [F \"error\"]"; + formulasString += "; Pmax=? [F \"processB\"]"; + formulasString += "; Pmax=? [F<1 \"error\"]"; + + auto modelFormulas = this->buildModelFormulas(STORM_TEST_RESOURCES_DIR "/ma/server.ma", formulasString); + auto model = std::move(modelFormulas.first); + auto tasks = this->getTasks(modelFormulas.second); + EXPECT_EQ(6ul, model->getNumberOfStates()); + EXPECT_EQ(10ul, model->getNumberOfTransitions()); + ASSERT_EQ(model->getType(), storm::models::ModelType::MarkovAutomaton); + auto checker = this->createModelChecker(model); + std::unique_ptr result; + + result = checker->check(this->env(), tasks[0]); + EXPECT_NEAR(this->parseNumber("11/6"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + + result = checker->check(this->env(), tasks[1]); + EXPECT_NEAR(this->parseNumber("2/3"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + + if (!storm::utility::isZero(this->precision())) { + result = checker->check(this->env(), tasks[2]); + EXPECT_NEAR(this->parseNumber("0.455504"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + } + + } + + TYPED_TEST(MarkovAutomatonCslModelCheckerTest, simple) { + std::string formulasString = "Pmin=? [F<1 s>2]"; + formulasString += "; Pmax=? [F<1.3 s=3]"; + + auto modelFormulas = this->buildModelFormulas(STORM_TEST_RESOURCES_DIR "/ma/simple.ma", formulasString); + auto model = std::move(modelFormulas.first); + auto tasks = this->getTasks(modelFormulas.second); + EXPECT_EQ(5ul, model->getNumberOfStates()); + EXPECT_EQ(8ul, model->getNumberOfTransitions()); + ASSERT_EQ(model->getType(), storm::models::ModelType::MarkovAutomaton); + auto checker = this->createModelChecker(model); + std::unique_ptr result; + + if (!storm::utility::isZero(this->precision())) { + result = checker->check(this->env(), tasks[0]); + EXPECT_NEAR(this->parseNumber("0.6321205588"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + + result = checker->check(this->env(), tasks[1]); + EXPECT_NEAR(this->parseNumber("0.727468207"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + } + } + + TYPED_TEST(MarkovAutomatonCslModelCheckerTest, simple2) { + std::string formulasString = "R{\"rew0\"}max=? [C]"; + formulasString += "; R{\"rew0\"}min=? [C]"; + formulasString += "; R{\"rew1\"}max=? [C]"; + formulasString += "; R{\"rew1\"}min=? [C]"; + formulasString += "; R{\"rew2\"}max=? [C]"; + formulasString += "; R{\"rew2\"}min=? [C]"; + formulasString += "; R{\"rew3\"}min=? [C]"; + + auto modelFormulas = this->buildModelFormulas(STORM_TEST_RESOURCES_DIR "/ma/simple2.ma", formulasString); + auto model = std::move(modelFormulas.first); + auto tasks = this->getTasks(modelFormulas.second); + EXPECT_EQ(6ul, model->getNumberOfStates()); + EXPECT_EQ(11ul, model->getNumberOfTransitions()); + ASSERT_EQ(model->getType(), storm::models::ModelType::MarkovAutomaton); + auto checker = this->createModelChecker(model); + std::unique_ptr result; + + result = checker->check(this->env(), tasks[0]); + EXPECT_NEAR(this->parseNumber("2"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + + result = checker->check(this->env(), tasks[1]); + EXPECT_NEAR(this->parseNumber("0"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + + result = checker->check(this->env(), tasks[2]); + EXPECT_TRUE(storm::utility::isInfinity(this->getQuantitativeResultAtInitialState(model, result))); + + result = checker->check(this->env(), tasks[3]); + EXPECT_NEAR(this->parseNumber("7/8"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + + result = checker->check(this->env(), tasks[4]); + EXPECT_TRUE(storm::utility::isInfinity(this->getQuantitativeResultAtInitialState(model, result))); + + result = checker->check(this->env(), tasks[5]); + EXPECT_NEAR(this->parseNumber("7/8"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); + + result = checker->check(this->env(), tasks[6]); + EXPECT_TRUE(storm::utility::isInfinity(this->getQuantitativeResultAtInitialState(model, result))); + + } +} \ No newline at end of file diff --git a/src/test/storm/modelchecker/MdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/MdpPrctlModelCheckerTest.cpp index e0741fa30..feb90d438 100644 --- a/src/test/storm/modelchecker/MdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/MdpPrctlModelCheckerTest.cpp @@ -4,8 +4,9 @@ #include "test/storm_gtest.h" #include "storm/api/builder.h" -#include "storm/api/model_descriptions.h" +#include "storm-parsers/api/model_descriptions.h" #include "storm/api/properties.h" +#include "storm-parsers/api/properties.h" #include "storm/models/sparse/Mdp.h" #include "storm/models/symbolic/Mdp.h" diff --git a/src/test/storm/modelchecker/SchedulerGenerationMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/SchedulerGenerationMdpPrctlModelCheckerTest.cpp index 751a28ed2..6f82d6a47 100644 --- a/src/test/storm/modelchecker/SchedulerGenerationMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/SchedulerGenerationMdpPrctlModelCheckerTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/solver/StandardMinMaxLinearEquationSolver.h" #include "storm/models/sparse/StandardRewardModel.h" @@ -12,8 +12,8 @@ #include "storm/environment/solver/MinMaxSolverEnvironment.h" -#include "storm/parser/AutoParser.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/AutoParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/builder/ExplicitModelBuilder.h" namespace { diff --git a/src/test/storm/modelchecker/SparseDtmcEliminationModelCheckerTest.cpp b/src/test/storm/modelchecker/SparseDtmcEliminationModelCheckerTest.cpp index 00c8b88e5..ed182bf2c 100644 --- a/src/test/storm/modelchecker/SparseDtmcEliminationModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/SparseDtmcEliminationModelCheckerTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.h" @@ -10,7 +10,7 @@ #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/SettingMemento.h" -#include "storm/parser/AutoParser.h" +#include "storm-parsers/parser/AutoParser.h" TEST(SparseDtmcEliminationModelCheckerTest, Die) { std::shared_ptr> abstractModel = storm::parser::AutoParser<>::parseModel(STORM_TEST_RESOURCES_DIR "/tra/die.tra", STORM_TEST_RESOURCES_DIR "/lab/die.lab", "", STORM_TEST_RESOURCES_DIR "/rew/die.coin_flips.trans.rew"); diff --git a/src/test/storm/modelchecker/SparseDtmcMultiDimensionalRewardUnfoldingTest.cpp b/src/test/storm/modelchecker/SparseDtmcMultiDimensionalRewardUnfoldingTest.cpp index 36ca41ff0..7819ec7e0 100644 --- a/src/test/storm/modelchecker/SparseDtmcMultiDimensionalRewardUnfoldingTest.cpp +++ b/src/test/storm/modelchecker/SparseDtmcMultiDimensionalRewardUnfoldingTest.cpp @@ -3,10 +3,12 @@ #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "storm/models/sparse/Dtmc.h" +#include "storm/storage/jani/Property.h" #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/SettingsManager.h" #include "storm/utility/constants.h" #include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" #include "storm/environment/Environment.h" TEST(SparseDtmcMultiDimensionalRewardUnfoldingTest, cost_bounded_die) { diff --git a/src/test/storm/modelchecker/SparseExplorationModelCheckerTest.cpp b/src/test/storm/modelchecker/SparseExplorationModelCheckerTest.cpp index a1645e2a0..dc7d7a55a 100644 --- a/src/test/storm/modelchecker/SparseExplorationModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/SparseExplorationModelCheckerTest.cpp @@ -4,8 +4,8 @@ #include "storm/logic/Formulas.h" #include "storm/modelchecker/exploration/SparseExplorationModelChecker.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" -#include "storm/parser/PrismParser.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/PrismParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/settings/SettingsManager.h" #include "storm/settings/modules/ExplorationSettings.h" diff --git a/src/test/storm/modelchecker/SparseMaCbMultiObjectiveModelCheckerTest.cpp b/src/test/storm/modelchecker/SparseMaCbMultiObjectiveModelCheckerTest.cpp index e5843b4e1..090a38ef5 100644 --- a/src/test/storm/modelchecker/SparseMaCbMultiObjectiveModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/SparseMaCbMultiObjectiveModelCheckerTest.cpp @@ -5,10 +5,12 @@ #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/models/sparse/MarkovAutomaton.h" +#include "storm/storage/jani/Property.h" #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/modules/MultiObjectiveSettings.h" #include "storm/settings/SettingsManager.h" #include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" #include "storm/environment/Environment.h" diff --git a/src/test/storm/modelchecker/SparseMaPcaaMultiObjectiveModelCheckerTest.cpp b/src/test/storm/modelchecker/SparseMaPcaaMultiObjectiveModelCheckerTest.cpp index d2c116358..be709707c 100644 --- a/src/test/storm/modelchecker/SparseMaPcaaMultiObjectiveModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/SparseMaPcaaMultiObjectiveModelCheckerTest.cpp @@ -10,10 +10,12 @@ #include "storm/models/sparse/MarkovAutomaton.h" #include "storm/storage/geometry/Polytope.h" #include "storm/storage/geometry/Hyperrectangle.h" +#include "storm/storage/jani/Property.h" #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/modules/MultiObjectiveSettings.h" #include "storm/settings/SettingsManager.h" #include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" #include "storm/environment/Environment.h" diff --git a/src/test/storm/modelchecker/SparseMdpCbMultiObjectiveModelCheckerTest.cpp b/src/test/storm/modelchecker/SparseMdpCbMultiObjectiveModelCheckerTest.cpp index 7b2479ec3..51193af47 100644 --- a/src/test/storm/modelchecker/SparseMdpCbMultiObjectiveModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/SparseMdpCbMultiObjectiveModelCheckerTest.cpp @@ -4,9 +4,11 @@ #include "storm/modelchecker/multiobjective/multiObjectiveModelChecking.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/models/sparse/Mdp.h" +#include "storm/storage/jani/Property.h" #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/SettingsManager.h" #include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" #include "storm/environment/Environment.h" diff --git a/src/test/storm/modelchecker/SparseMdpMultiDimensionalRewardUnfoldingTest.cpp b/src/test/storm/modelchecker/SparseMdpMultiDimensionalRewardUnfoldingTest.cpp index 3f73da77e..05faeb81c 100644 --- a/src/test/storm/modelchecker/SparseMdpMultiDimensionalRewardUnfoldingTest.cpp +++ b/src/test/storm/modelchecker/SparseMdpMultiDimensionalRewardUnfoldingTest.cpp @@ -8,8 +8,10 @@ #include "storm/models/sparse/Mdp.h" #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/SettingsManager.h" +#include "storm/storage/jani/Property.h" #include "storm/utility/constants.h" #include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" #include "storm/environment/Environment.h" #include "storm/environment/solver/MinMaxSolverEnvironment.h" diff --git a/src/test/storm/modelchecker/SparseMdpPcaaMultiObjectiveModelCheckerTest.cpp b/src/test/storm/modelchecker/SparseMdpPcaaMultiObjectiveModelCheckerTest.cpp index 947cbde55..01c3edbc9 100644 --- a/src/test/storm/modelchecker/SparseMdpPcaaMultiObjectiveModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/SparseMdpPcaaMultiObjectiveModelCheckerTest.cpp @@ -9,7 +9,9 @@ #include "storm/models/sparse/Mdp.h" #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/SettingsManager.h" +#include "storm/storage/jani/Property.h" #include "storm/api/storm.h" +#include "storm-parsers/api/storm-parsers.h" #include "storm/environment/Environment.h" TEST(SparseMdpPcaaMultiObjectiveModelCheckerTest, consensus) { diff --git a/src/test/storm/parser/AutoParserTest.cpp b/src/test/storm/parser/AutoParserTest.cpp index 0ddd14894..96aa9cc60 100644 --- a/src/test/storm/parser/AutoParserTest.cpp +++ b/src/test/storm/parser/AutoParserTest.cpp @@ -2,7 +2,7 @@ #include "storm-config.h" #include "storm/models/sparse/StandardRewardModel.h" -#include "storm/parser/AutoParser.h" +#include "storm-parsers/parser/AutoParser.h" #include "storm/exceptions/FileIoException.h" #include "storm/exceptions/WrongFormatException.h" diff --git a/src/test/storm/parser/DeterministicModelParserTest.cpp b/src/test/storm/parser/DeterministicModelParserTest.cpp index b2f7ac9c2..bd5b0bf69 100644 --- a/src/test/storm/parser/DeterministicModelParserTest.cpp +++ b/src/test/storm/parser/DeterministicModelParserTest.cpp @@ -2,7 +2,7 @@ #include "storm-config.h" #include "storm/models/sparse/StandardRewardModel.h" -#include "storm/parser/DeterministicModelParser.h" +#include "storm-parsers/parser/DeterministicModelParser.h" #include "storm/models/sparse/Dtmc.h" #include "storm/models/sparse/Ctmc.h" #include "storm/exceptions/FileIoException.h" diff --git a/src/test/storm/parser/DeterministicSparseTransitionParserTest.cpp b/src/test/storm/parser/DeterministicSparseTransitionParserTest.cpp index bd8e9e2f2..737d8ff04 100644 --- a/src/test/storm/parser/DeterministicSparseTransitionParserTest.cpp +++ b/src/test/storm/parser/DeterministicSparseTransitionParserTest.cpp @@ -8,7 +8,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/DeterministicSparseTransitionParser.h" +#include "storm-parsers/parser/DeterministicSparseTransitionParser.h" #include "storm/storage/SparseMatrix.h" #include "storm/settings/SettingsManager.h" #include "storm/settings/modules/CoreSettings.h" diff --git a/src/test/storm/parser/DirectEncodingParserTest.cpp b/src/test/storm/parser/DirectEncodingParserTest.cpp index 93bd34e04..c5716195b 100644 --- a/src/test/storm/parser/DirectEncodingParserTest.cpp +++ b/src/test/storm/parser/DirectEncodingParserTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/DirectEncodingParser.h" +#include "storm-parsers/parser/DirectEncodingParser.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/models/sparse/Mdp.h" #include "storm/models/sparse/MarkovAutomaton.h" diff --git a/src/test/storm/parser/FormulaParserTest.cpp b/src/test/storm/parser/FormulaParserTest.cpp index 6269a082c..fd1206947 100644 --- a/src/test/storm/parser/FormulaParserTest.cpp +++ b/src/test/storm/parser/FormulaParserTest.cpp @@ -1,6 +1,6 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/FragmentSpecification.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/storage/expressions/ExpressionManager.h" diff --git a/src/test/storm/parser/MappedFileTest.cpp b/src/test/storm/parser/MappedFileTest.cpp index f33ee3aa2..362cbc899 100644 --- a/src/test/storm/parser/MappedFileTest.cpp +++ b/src/test/storm/parser/MappedFileTest.cpp @@ -9,7 +9,7 @@ #include "storm-config.h" #include -#include "storm/parser/MappedFile.h" +#include "storm-parsers/parser/MappedFile.h" #include "storm/utility/cstring.h" #include "storm/utility/file.h" #include "storm/exceptions/FileIoException.h" diff --git a/src/test/storm/parser/MarkovAutomatonParserTest.cpp b/src/test/storm/parser/MarkovAutomatonParserTest.cpp index c31787087..3eb2dc3f7 100644 --- a/src/test/storm/parser/MarkovAutomatonParserTest.cpp +++ b/src/test/storm/parser/MarkovAutomatonParserTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/MarkovAutomatonParser.h" +#include "storm-parsers/parser/MarkovAutomatonParser.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/exceptions/FileIoException.h" #include "storm/exceptions/OutOfRangeException.h" diff --git a/src/test/storm/parser/MarkovAutomatonSparseTransitionParserTest.cpp b/src/test/storm/parser/MarkovAutomatonSparseTransitionParserTest.cpp index 461b28a95..f29a5689d 100644 --- a/src/test/storm/parser/MarkovAutomatonSparseTransitionParserTest.cpp +++ b/src/test/storm/parser/MarkovAutomatonSparseTransitionParserTest.cpp @@ -12,9 +12,9 @@ #include -#include "storm/parser/MarkovAutomatonSparseTransitionParser.h" +#include "storm-parsers/parser/MarkovAutomatonSparseTransitionParser.h" #include "storm/utility/cstring.h" -#include "storm/parser/MarkovAutomatonParser.h" +#include "storm-parsers/parser/MarkovAutomatonParser.h" #include "storm/settings/SettingMemento.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/exceptions/FileIoException.h" diff --git a/src/test/storm/parser/NondeterministicModelParserTest.cpp b/src/test/storm/parser/NondeterministicModelParserTest.cpp index b700c56f4..8ed57b2b7 100644 --- a/src/test/storm/parser/NondeterministicModelParserTest.cpp +++ b/src/test/storm/parser/NondeterministicModelParserTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/NondeterministicModelParser.h" +#include "storm-parsers/parser/NondeterministicModelParser.h" #include "storm/models/sparse/Mdp.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/exceptions/FileIoException.h" diff --git a/src/test/storm/parser/NondeterministicSparseTransitionParserTest.cpp b/src/test/storm/parser/NondeterministicSparseTransitionParserTest.cpp index 77344834a..9c740144f 100644 --- a/src/test/storm/parser/NondeterministicSparseTransitionParserTest.cpp +++ b/src/test/storm/parser/NondeterministicSparseTransitionParserTest.cpp @@ -8,7 +8,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/NondeterministicSparseTransitionParser.h" +#include "storm-parsers/parser/NondeterministicSparseTransitionParser.h" #include "storm/storage/SparseMatrix.h" #include "storm/settings/SettingMemento.h" diff --git a/src/test/storm/parser/PrismParserTest.cpp b/src/test/storm/parser/PrismParserTest.cpp index c86d90f63..4fe5735f5 100644 --- a/src/test/storm/parser/PrismParserTest.cpp +++ b/src/test/storm/parser/PrismParserTest.cpp @@ -1,6 +1,6 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" TEST(PrismParser, StandardModelTest) { storm::prism::Program result; diff --git a/src/test/storm/parser/SparseItemLabelingParserTest.cpp b/src/test/storm/parser/SparseItemLabelingParserTest.cpp index 695b6837b..2f5cf3c0b 100644 --- a/src/test/storm/parser/SparseItemLabelingParserTest.cpp +++ b/src/test/storm/parser/SparseItemLabelingParserTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" #include "storm/models/sparse/StateLabeling.h" -#include "storm/parser/SparseItemLabelingParser.h" +#include "storm-parsers/parser/SparseItemLabelingParser.h" #include "storm/exceptions/FileIoException.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/exceptions/OutOfRangeException.h" diff --git a/src/test/storm/parser/SparseStateRewardParserTest.cpp b/src/test/storm/parser/SparseStateRewardParserTest.cpp index d92203210..53524b3cd 100644 --- a/src/test/storm/parser/SparseStateRewardParserTest.cpp +++ b/src/test/storm/parser/SparseStateRewardParserTest.cpp @@ -10,7 +10,7 @@ #include -#include "storm/parser/SparseStateRewardParser.h" +#include "storm-parsers/parser/SparseStateRewardParser.h" #include "storm/exceptions/FileIoException.h" #include "storm/exceptions/WrongFormatException.h" #include "storm/exceptions/OutOfRangeException.h" diff --git a/src/test/storm/permissiveschedulers/MilpPermissiveSchedulerTest.cpp b/src/test/storm/permissiveschedulers/MilpPermissiveSchedulerTest.cpp index 20707d33e..a6b0a34fd 100644 --- a/src/test/storm/permissiveschedulers/MilpPermissiveSchedulerTest.cpp +++ b/src/test/storm/permissiveschedulers/MilpPermissiveSchedulerTest.cpp @@ -1,8 +1,8 @@ #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/PrismParser.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/PrismParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/storage/expressions/ExpressionManager.h" #include "storm/logic/Formulas.h" #include "storm/permissivesched/PermissiveSchedulers.h" diff --git a/src/test/storm/permissiveschedulers/SmtPermissiveSchedulerTest.cpp b/src/test/storm/permissiveschedulers/SmtPermissiveSchedulerTest.cpp index b19f82618..1b873a935 100644 --- a/src/test/storm/permissiveschedulers/SmtPermissiveSchedulerTest.cpp +++ b/src/test/storm/permissiveschedulers/SmtPermissiveSchedulerTest.cpp @@ -1,8 +1,8 @@ #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/PrismParser.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/PrismParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/permissivesched/PermissiveSchedulers.h" #include "storm/builder/ExplicitModelBuilder.h" diff --git a/src/test/storm/storage/DeterministicModelBisimulationDecompositionTest.cpp b/src/test/storm/storage/DeterministicModelBisimulationDecompositionTest.cpp index 3ebeca7a5..3341b1fe7 100644 --- a/src/test/storm/storage/DeterministicModelBisimulationDecompositionTest.cpp +++ b/src/test/storm/storage/DeterministicModelBisimulationDecompositionTest.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/AutoParser.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/AutoParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/storage/bisimulation/DeterministicModelBisimulationDecomposition.h" #include "storm/models/sparse/Dtmc.h" #include "storm/models/sparse/StandardRewardModel.h" diff --git a/src/test/storm/storage/JaniModelTest.cpp b/src/test/storm/storage/JaniModelTest.cpp index 7c7f393b3..c7a6b5c11 100644 --- a/src/test/storm/storage/JaniModelTest.cpp +++ b/src/test/storm/storage/JaniModelTest.cpp @@ -1,6 +1,6 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/utility/solver.h" diff --git a/src/test/storm/storage/MaximalEndComponentDecompositionTest.cpp b/src/test/storm/storage/MaximalEndComponentDecompositionTest.cpp index a7c23edf3..ced1cd6b0 100644 --- a/src/test/storm/storage/MaximalEndComponentDecompositionTest.cpp +++ b/src/test/storm/storage/MaximalEndComponentDecompositionTest.cpp @@ -1,9 +1,13 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/AutoParser.h" +#include "storm-parsers/parser/AutoParser.h" #include "storm/storage/MaximalEndComponentDecomposition.h" #include "storm/models/sparse/MarkovAutomaton.h" +#include "storm/models/sparse/Mdp.h" #include "storm/models/sparse/StandardRewardModel.h" +#include "storm/builder/ExplicitModelBuilder.h" +#include "storm/storage/SymbolicModelDescription.h" +#include "storm-parsers/parser/PrismParser.h" TEST(MaximalEndComponentDecomposition, FullSystem1) { std::shared_ptr> abstractModel = storm::parser::AutoParser<>::parseModel(STORM_TEST_RESOURCES_DIR "/tra/tiny1.tra", STORM_TEST_RESOURCES_DIR "/lab/tiny1.lab", "", ""); @@ -133,3 +137,42 @@ TEST(MaximalEndComponentDecomposition, Subsystem) { ASSERT_TRUE(false); } } + +TEST(MaximalEndComponentDecomposition, Example1) { + std::string prismModelPath = STORM_TEST_RESOURCES_DIR "/mdp/prism-mec-example1.nm"; + storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(prismModelPath); + storm::prism::Program program = modelDescription.preprocess().asPrismProgram(); + + std::shared_ptr> model = storm::builder::ExplicitModelBuilder(program).build(); + std::shared_ptr> mdp = model->as>(); + + storm::storage::MaximalEndComponentDecomposition mecDecomposition(*mdp); + + EXPECT_EQ(2ull, mecDecomposition.size()); + + ASSERT_TRUE(mecDecomposition[0].getStateSet() == storm::storage::MaximalEndComponent::set_type{2}); + EXPECT_TRUE(mecDecomposition[0].getChoicesForState(2) == storm::storage::MaximalEndComponent::set_type{3}); + + ASSERT_TRUE(mecDecomposition[1].getStateSet() == storm::storage::MaximalEndComponent::set_type{0}); + EXPECT_TRUE(mecDecomposition[1].getChoicesForState(0) == storm::storage::MaximalEndComponent::set_type{0}); +} + +TEST(MaximalEndComponentDecomposition, Example2) { + std::string prismModelPath = STORM_TEST_RESOURCES_DIR "/mdp/prism-mec-example2.nm"; + storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(prismModelPath); + storm::prism::Program program = modelDescription.preprocess().asPrismProgram(); + + std::shared_ptr> model = storm::builder::ExplicitModelBuilder(program).build(); + std::shared_ptr> mdp = model->as>(); + + storm::storage::MaximalEndComponentDecomposition mecDecomposition(*mdp); + + EXPECT_EQ(2ull, mecDecomposition.size()); + + ASSERT_TRUE(mecDecomposition[0].getStateSet() == storm::storage::MaximalEndComponent::set_type{2}); + EXPECT_TRUE(mecDecomposition[0].getChoicesForState(2) == storm::storage::MaximalEndComponent::set_type{4}); + + ASSERT_TRUE((mecDecomposition[1].getStateSet() == storm::storage::MaximalEndComponent::set_type{0, 1})); + EXPECT_TRUE((mecDecomposition[1].getChoicesForState(0) == storm::storage::MaximalEndComponent::set_type{0, 1})); + EXPECT_TRUE((mecDecomposition[1].getChoicesForState(1) == storm::storage::MaximalEndComponent::set_type{3})); +} diff --git a/src/test/storm/storage/NondeterministicModelBisimulationDecompositionTest.cpp b/src/test/storm/storage/NondeterministicModelBisimulationDecompositionTest.cpp index 596938852..d77fa8d64 100644 --- a/src/test/storm/storage/NondeterministicModelBisimulationDecompositionTest.cpp +++ b/src/test/storm/storage/NondeterministicModelBisimulationDecompositionTest.cpp @@ -1,8 +1,8 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/PrismParser.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/PrismParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/builder/ExplicitModelBuilder.h" diff --git a/src/test/storm/storage/PrismProgramTest.cpp b/src/test/storm/storage/PrismProgramTest.cpp index 8525f03e2..e860fa997 100644 --- a/src/test/storm/storage/PrismProgramTest.cpp +++ b/src/test/storm/storage/PrismProgramTest.cpp @@ -1,6 +1,6 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/utility/solver.h" diff --git a/src/test/storm/storage/StronglyConnectedComponentDecompositionTest.cpp b/src/test/storm/storage/StronglyConnectedComponentDecompositionTest.cpp index e447d2ec0..821b721a7 100644 --- a/src/test/storm/storage/StronglyConnectedComponentDecompositionTest.cpp +++ b/src/test/storm/storage/StronglyConnectedComponentDecompositionTest.cpp @@ -1,6 +1,6 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/AutoParser.h" +#include "storm-parsers/parser/AutoParser.h" #include "storm/storage/SparseMatrix.h" #include "storm/storage/StronglyConnectedComponentDecomposition.h" #include "storm/models/sparse/StandardRewardModel.h" diff --git a/src/test/storm/storage/SymbolicBisimulationDecompositionTest.cpp b/src/test/storm/storage/SymbolicBisimulationDecompositionTest.cpp index d339bd027..64d2906d1 100644 --- a/src/test/storm/storage/SymbolicBisimulationDecompositionTest.cpp +++ b/src/test/storm/storage/SymbolicBisimulationDecompositionTest.cpp @@ -1,8 +1,8 @@ #include "gtest/gtest.h" #include "storm-config.h" -#include "storm/parser/PrismParser.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/PrismParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/builder/DdPrismModelBuilder.h" @@ -19,7 +19,7 @@ #include "storm/solver/SymbolicMinMaxLinearEquationSolver.h" #include "storm/logic/Formulas.h" -#include "storm/parser/FormulaParser.h" +#include "storm-parsers/parser/FormulaParser.h" #include "storm/models/sparse/Mdp.h" #include "storm/models/sparse/StandardRewardModel.h" diff --git a/src/test/storm/utility/GraphTest.cpp b/src/test/storm/utility/GraphTest.cpp index ac646d99a..3098297c0 100644 --- a/src/test/storm/utility/GraphTest.cpp +++ b/src/test/storm/utility/GraphTest.cpp @@ -2,7 +2,7 @@ #include "storm-config.h" #include "storm/storage/SymbolicModelDescription.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/models/symbolic/Dtmc.h" #include "storm/models/symbolic/Mdp.h" #include "storm/models/symbolic/StandardRewardModel.h" diff --git a/src/test/storm/utility/KSPTest.cpp b/src/test/storm/utility/KSPTest.cpp index 5ac954314..d584a7660 100644 --- a/src/test/storm/utility/KSPTest.cpp +++ b/src/test/storm/utility/KSPTest.cpp @@ -3,7 +3,7 @@ #include "storm/builder/ExplicitModelBuilder.h" #include "storm/models/sparse/Dtmc.h" -#include "storm/parser/PrismParser.h" +#include "storm-parsers/parser/PrismParser.h" #include "storm/storage/SymbolicModelDescription.h" #include "storm/utility/graph.h" #include "storm/utility/shortestPaths.h" @@ -81,7 +81,7 @@ TEST(KSPTest, kspStateSet) { storm::utility::ksp::ShortestPathsGenerator spg(*model, testState); auto bv = spg.getStates(7); - EXPECT_EQ(50, bv.getNumberOfSetBits()); + EXPECT_EQ(50ull, bv.getNumberOfSetBits()); // The result may sadly depend on the compiler/system, so checking a particular outcome is not feasible. // storm::storage::BitVector referenceBV(model->getNumberOfStates(), false); @@ -97,7 +97,7 @@ TEST(KSPTest, kspPathAsList) { storm::utility::ksp::ShortestPathsGenerator spg(*model, testState); auto list = spg.getPathAsList(7); - EXPECT_EQ(50, list.size()); + EXPECT_EQ(50ull, list.size()); // TODO: use path that actually has a loop or something to make this more interesting // auto reference = storm::utility::ksp::OrderedStateList{296, 288, 281, 272, 266, 260, 253, 245, 238, 230, 224, 218, 211, 203, 196, 188, 182, 176, 169, 161, 154, 146, 140, 134, 127, 119, 112, 104, 98, 92, 85, 77, 70, 81, 74, 65, 58, 52, 45, 37, 30, 22, 17, 12, 9, 6, 4, 2, 1, 0}; diff --git a/storm-config.h.in b/storm-config.h.in index b0290b751..55d2247d3 100644 --- a/storm-config.h.in +++ b/storm-config.h.in @@ -41,6 +41,13 @@ // Whether the optimization feature of Z3 is available and to be used (define/undef) #cmakedefine STORM_HAVE_Z3_OPTIMIZE +// Version of Z3 used by Storm. +#define STORM_Z3_VERSION_MAJOR @STORM_Z3_VERSION_MAJOR@ +#define STORM_Z3_VERSION_MINOR @STORM_Z3_VERSION_MINOR@ +#define STORM_Z3_VERSION_PATCH @STORM_Z3_VERSION_PATCH@ +#define STORM_Z3_VERSION @Z3_VERSION@ +#cmakedefine STORM_Z3_API_USES_STANDARD_INTEGERS + // Whether MathSAT is available and to be used (define/undef) #cmakedefine STORM_HAVE_MSAT diff --git a/storm-version.cpp.in b/storm-version.cpp.in index 6e3706bae..f74b2cbc7 100644 --- a/storm-version.cpp.in +++ b/storm-version.cpp.in @@ -11,7 +11,7 @@ namespace storm { const bool StormVersion::versionDev = @STORM_VERSION_DEV@; const StormVersion::VersionSource StormVersion::versionSource = @STORM_VERSION_SOURCE@; const std::string StormVersion::gitRevisionHash = "@STORM_VERSION_GIT_HASH@"; - const boost::optional StormVersion::commitsAhead = @STORM_VERSION_COMMITS_AHEAD@; + const unsigned StormVersion::commitsAhead = @STORM_VERSION_COMMITS_AHEAD@; const boost::optional StormVersion::dirty = @STORM_VERSION_DIRTY@; const std::string StormVersion::systemName = "@CMAKE_SYSTEM_NAME@"; const std::string StormVersion::systemVersion = "@CMAKE_SYSTEM_VERSION@"; diff --git a/travis/generate_travis.py b/travis/generate_travis.py index ade78cbca..f7565c6bc 100644 --- a/travis/generate_travis.py +++ b/travis/generate_travis.py @@ -5,8 +5,8 @@ configs_linux = [ # OS, compiler, build type ("debian-9", "gcc", "DefaultDebug"), ("debian-9", "gcc", "DefaultRelease"), - ("ubuntu-17.10", "gcc", "DefaultDebugTravis"), - ("ubuntu-17.10", "gcc", "DefaultReleaseTravis"), + ("ubuntu-18.04", "gcc", "DefaultDebugTravis"), + ("ubuntu-18.04", "gcc", "DefaultReleaseTravis"), ("ubuntu-18.04", "gcc", "DefaultDebug"), ("ubuntu-18.04", "gcc", "DefaultRelease"), ] @@ -44,6 +44,9 @@ if __name__ == "__main__": s += "dist: trusty\n" s += "language: cpp\n" s += "\n" + s += "git:\n" + s += " depth: false\n" + s += "\n" s += "# Enable caching\n" s += "cache:\n" s += " timeout: 1000\n" @@ -61,7 +64,7 @@ if __name__ == "__main__": s += " on_failure: always\n" s += " on_success: change\n" s += " recipients:\n" - s += ' secure: "Q9CW/PtyWkZwExDrfFFb9n1STGYsRfI6awv1bZHcGwfrDvhpXoMCuF8CwviIfilm7fFJJEoKizTtdRpY0HrOnY/8KY111xrtcFYosxdndv7xbESodIxQOUoIEFCO0mCNHwWYisoi3dAA7H3Yn661EsxluwHjzX5vY0xSbw0n229hlsFz092HwGLSU33zHl7eXpQk+BOFmBTRGlOq9obtDZ17zbHz1oXFZntHK/JDUIYP0b0uq8NvE2jM6CIVdcuSwmIkOhZJoO2L3Py3rBbPci+L2PSK4Smv2UjCPF8KusnOaFIyDB3LcNM9Jqq5ssJMrK/KaO6BiuYrOZXYWZ7KEg3Y/naC8HjOH1dzty+P7oW46sb9F03pTsufqD4R7wcK+9wROTztO6aJPDG/IPH7EWgrBTxqlOkVRwi2eYuQouZpZUW6EMClKbMHMIxCH2S8aOk/r1w2cNwmPEcunnP0nl413x/ByHr4fTPFykPj8pQxIsllYjpdWBRQfDOauKWGzk6LcrFW0qpWP+/aJ2vYu/IoZQMG5lMHbp6Y1Lg09pYm7Q983v3b7D+JvXhOXMyGq91HyPahA2wwKoG1GA4uoZ2I95/IFYNiKkelDd3WTBoFLNF9YFoEJNdCywm1fO2WY4WkyEFBuQcgDA+YpFMJJMxjTbivYk9jvHk2gji//2w="\n' + s += ' - secure: "VWnsiQkt1xjgRo1hfNiNQqvLSr0fshFmLV7jJlUixhCr094mgD0U2bNKdUfebm28Byg9UyDYPbOFDC0sx7KydKiL1q7FKKXkyZH0k04wUu8XiNw+fYkDpmPnQs7G2n8oJ/GFJnr1Wp/1KI3qX5LX3xot4cJfx1I5iFC2O+p+ng6v/oSX+pewlMv4i7KL16ftHHHMo80N694v3g4B2NByn4GU2/bjVQcqlBp/TiVaUa5Nqu9DxZi/n9CJqGEaRHOblWyMO3EyTZsn45BNSWeQ3DtnMwZ73rlIr9CaEgCeuArc6RGghUAVqRI5ao+N5apekIaILwTgL6AJn+Lw/+NRPa8xclgd0rKqUQJMJCDZKjKz2lmIs3bxfELOizxJ3FJQ5R95FAxeAZ6rb/j40YqVVTw2IMBDnEE0J5ZmpUYNUtPti/Adf6GD9Fb2y8sLo0XDJzkI8OxYhfgjSy5KYmRj8O5MXcP2MAE8LQauNO3MaFnL9VMVOTZePJrPozQUgM021uyahf960+QNI06Uqlmg+PwWkSdllQlxHHplOgW7zClFhtSUpnJxcsUBzgg4kVg80gXUwAQkaDi7A9Wh2bs+TvMlmHzBwg+2SaAfWDgjeJIeOaipDkF1uSGzC+EHAiiKYMLd4Aahoi8SuelJUucoyJyLAq00WdUFQIh/izVhM4Y="\n' s += "\n" s += "#\n" s += "# Configurations\n" diff --git a/version.cmake b/version.cmake index 9a0907e74..b82fc11c5 100644 --- a/version.cmake +++ b/version.cmake @@ -1,4 +1,4 @@ set(STORM_VERSION_MAJOR 1) set(STORM_VERSION_MINOR 2) -set(STORM_VERSION_PATCH 1) +set(STORM_VERSION_PATCH 3)